@oxog/vld 2.1.0 → 2.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (220) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/README.md +1296 -1235
  3. package/dist/chunks/errors-core-Cqi4-9ew.js +1 -0
  4. package/dist/chunks/index-CkbfQQ4n.js +1 -0
  5. package/dist/chunks/index-yjpbvFgw.js +1 -0
  6. package/dist/chunks/ip-validation-g5LWgy9t.js +1 -0
  7. package/dist/chunks/security-Dy24T8N7.js +1 -0
  8. package/dist/cjs/chunks/errors-core-DKPohm9I.cjs +1 -0
  9. package/dist/cjs/chunks/index-BEV9nG0N.cjs +1 -0
  10. package/dist/cjs/chunks/index-ZjHAxYdA.cjs +1 -0
  11. package/dist/cjs/chunks/ip-validation-BZqPamPC.cjs +1 -0
  12. package/dist/cjs/chunks/security-aXZVI7qW.cjs +1 -0
  13. package/dist/cjs/cli/bin.cjs +1 -505
  14. package/dist/cjs/cli/index.cjs +1 -209
  15. package/dist/cjs/codecs/index.cjs +1 -45
  16. package/dist/cjs/coercion/index.cjs +1 -588
  17. package/dist/cjs/compat/emitter.cjs +1 -184
  18. package/dist/cjs/compat/result.cjs +1 -189
  19. package/dist/cjs/errors.cjs +1 -180
  20. package/dist/cjs/index.cjs +1 -1894
  21. package/dist/cjs/kernel.cjs +1 -415
  22. package/dist/cjs/locales/af.cjs +1 -108
  23. package/dist/cjs/locales/ar.cjs +1 -108
  24. package/dist/cjs/locales/bn.cjs +1 -108
  25. package/dist/cjs/locales/da.cjs +1 -108
  26. package/dist/cjs/locales/de.cjs +1 -108
  27. package/dist/cjs/locales/en.cjs +1 -108
  28. package/dist/cjs/locales/es-MX.cjs +1 -107
  29. package/dist/cjs/locales/es.cjs +1 -96
  30. package/dist/cjs/locales/fi.cjs +1 -108
  31. package/dist/cjs/locales/fr.cjs +1 -96
  32. package/dist/cjs/locales/hi.cjs +1 -108
  33. package/dist/cjs/locales/id.cjs +1 -108
  34. package/dist/cjs/locales/index.cjs +1 -43
  35. package/dist/cjs/locales/it.cjs +1 -108
  36. package/dist/cjs/locales/ja.cjs +1 -108
  37. package/dist/cjs/locales/ko.cjs +1 -108
  38. package/dist/cjs/locales/lazy.cjs +1 -188
  39. package/dist/cjs/locales/nl.cjs +1 -108
  40. package/dist/cjs/locales/no.cjs +1 -108
  41. package/dist/cjs/locales/pl.cjs +1 -108
  42. package/dist/cjs/locales/pt-BR.cjs +1 -108
  43. package/dist/cjs/locales/pt.cjs +1 -108
  44. package/dist/cjs/locales/ru.cjs +1 -108
  45. package/dist/cjs/locales/runtime.cjs +1 -51
  46. package/dist/cjs/locales/sv.cjs +1 -108
  47. package/dist/cjs/locales/sw.cjs +1 -108
  48. package/dist/cjs/locales/th.cjs +1 -108
  49. package/dist/cjs/locales/tr.cjs +1 -108
  50. package/dist/cjs/locales/vi.cjs +1 -108
  51. package/dist/cjs/locales/zh.cjs +1 -108
  52. package/dist/cjs/mini.cjs +1 -242
  53. package/dist/cjs/pigment.cjs +1 -207
  54. package/dist/cjs/plugins/index.cjs +1 -12
  55. package/dist/cjs/registry.cjs +1 -37
  56. package/dist/cjs/v3/index.cjs +1 -425
  57. package/dist/cjs/v4/core/index.cjs +1 -894
  58. package/dist/cjs/v4/index.cjs +1 -425
  59. package/dist/cjs/v4/locales/index.cjs +1 -130
  60. package/dist/cjs/v4/mini/index.cjs +1 -509
  61. package/dist/cjs/v4-mini/index.cjs +1 -532
  62. package/dist/cjs/validators/any.cjs +1 -40
  63. package/dist/cjs/validators/array.cjs +1 -283
  64. package/dist/cjs/validators/base.cjs +1 -887
  65. package/dist/cjs/validators/base64.cjs +1 -73
  66. package/dist/cjs/validators/bigint.cjs +1 -232
  67. package/dist/cjs/validators/boolean.cjs +1 -146
  68. package/dist/cjs/validators/codec.cjs +1 -205
  69. package/dist/cjs/validators/custom.cjs +1 -71
  70. package/dist/cjs/validators/date.cjs +1 -278
  71. package/dist/cjs/validators/discriminated-union.cjs +1 -130
  72. package/dist/cjs/validators/enum.cjs +1 -154
  73. package/dist/cjs/validators/file.cjs +1 -130
  74. package/dist/cjs/validators/function.cjs +1 -63
  75. package/dist/cjs/validators/hex.cjs +1 -70
  76. package/dist/cjs/validators/index.cjs +1 -123
  77. package/dist/cjs/validators/intersection.cjs +1 -120
  78. package/dist/cjs/validators/json.cjs +1 -70
  79. package/dist/cjs/validators/lazy.cjs +1 -53
  80. package/dist/cjs/validators/literal.cjs +1 -64
  81. package/dist/cjs/validators/map.cjs +1 -218
  82. package/dist/cjs/validators/nan.cjs +1 -41
  83. package/dist/cjs/validators/never.cjs +1 -36
  84. package/dist/cjs/validators/null.cjs +1 -43
  85. package/dist/cjs/validators/number.cjs +1 -427
  86. package/dist/cjs/validators/object.cjs +1 -868
  87. package/dist/cjs/validators/promise.cjs +1 -115
  88. package/dist/cjs/validators/record.cjs +1 -262
  89. package/dist/cjs/validators/set.cjs +1 -203
  90. package/dist/cjs/validators/string-bool.cjs +1 -124
  91. package/dist/cjs/validators/string-formats.cjs +1 -168
  92. package/dist/cjs/validators/string.cjs +1 -12
  93. package/dist/cjs/validators/symbol.cjs +1 -62
  94. package/dist/cjs/validators/template-literal.cjs +1 -108
  95. package/dist/cjs/validators/tuple.cjs +1 -187
  96. package/dist/cjs/validators/uint8array.cjs +1 -106
  97. package/dist/cjs/validators/undefined.cjs +1 -43
  98. package/dist/cjs/validators/union.cjs +1 -191
  99. package/dist/cjs/validators/unknown.cjs +1 -40
  100. package/dist/cjs/validators/void.cjs +1 -48
  101. package/dist/cjs/validators/xor.cjs +1 -69
  102. package/dist/cli/bin.js +1 -406
  103. package/dist/cli/index.js +1 -205
  104. package/dist/codecs/index.js +1 -11
  105. package/dist/coercion/index.js +1 -579
  106. package/dist/compat/emitter.js +1 -180
  107. package/dist/compat/result.js +1 -170
  108. package/dist/errors-core.d.ts +20 -1
  109. package/dist/errors.js +1 -171
  110. package/dist/index.d.ts +248 -357
  111. package/dist/index.js +1 -1534
  112. package/dist/kernel.js +1 -409
  113. package/dist/locales/af.js +1 -106
  114. package/dist/locales/ar.js +1 -106
  115. package/dist/locales/bn.js +1 -106
  116. package/dist/locales/da.js +1 -106
  117. package/dist/locales/de.js +1 -106
  118. package/dist/locales/en.js +1 -106
  119. package/dist/locales/es-MX.js +1 -105
  120. package/dist/locales/es.js +1 -94
  121. package/dist/locales/fi.js +1 -106
  122. package/dist/locales/fr.js +1 -94
  123. package/dist/locales/hi.js +1 -106
  124. package/dist/locales/id.js +1 -106
  125. package/dist/locales/index.js +1 -29
  126. package/dist/locales/it.js +1 -106
  127. package/dist/locales/ja.js +1 -106
  128. package/dist/locales/ko.js +1 -106
  129. package/dist/locales/lazy.js +1 -177
  130. package/dist/locales/nl.js +1 -106
  131. package/dist/locales/no.js +1 -106
  132. package/dist/locales/pl.js +1 -106
  133. package/dist/locales/pt-BR.js +1 -106
  134. package/dist/locales/pt.js +1 -106
  135. package/dist/locales/ru.js +1 -106
  136. package/dist/locales/runtime.js +1 -41
  137. package/dist/locales/sv.js +1 -106
  138. package/dist/locales/sw.js +1 -106
  139. package/dist/locales/th.js +1 -106
  140. package/dist/locales/tr.js +1 -106
  141. package/dist/locales/vi.js +1 -106
  142. package/dist/locales/zh.js +1 -106
  143. package/dist/mini.d.ts +3 -3
  144. package/dist/mini.js +1 -156
  145. package/dist/pigment.js +1 -180
  146. package/dist/plugins/index.js +1 -1
  147. package/dist/registry.js +1 -34
  148. package/dist/utils/json-schema.d.ts +10 -1
  149. package/dist/v3/index.js +1 -46
  150. package/dist/v4/core/index.d.ts +28 -16
  151. package/dist/v4/core/index.js +1 -277
  152. package/dist/v4/index.js +1 -50
  153. package/dist/v4/locales/index.d.ts +4 -0
  154. package/dist/v4/locales/index.js +1 -77
  155. package/dist/v4/mini/index.js +1 -47
  156. package/dist/v4-mini/index.d.ts +1 -1
  157. package/dist/v4-mini/index.js +1 -75
  158. package/dist/validators/any.js +1 -37
  159. package/dist/validators/array.d.ts +7 -0
  160. package/dist/validators/array.js +1 -279
  161. package/dist/validators/base.d.ts +63 -8
  162. package/dist/validators/base.js +1 -868
  163. package/dist/validators/base64.js +1 -68
  164. package/dist/validators/bigint.js +1 -228
  165. package/dist/validators/boolean.js +1 -142
  166. package/dist/validators/codec.js +1 -200
  167. package/dist/validators/custom.js +1 -67
  168. package/dist/validators/date.js +1 -274
  169. package/dist/validators/discriminated-union.d.ts +2 -2
  170. package/dist/validators/discriminated-union.js +1 -123
  171. package/dist/validators/enum.js +1 -150
  172. package/dist/validators/file.js +1 -125
  173. package/dist/validators/function.js +1 -58
  174. package/dist/validators/hex.js +1 -65
  175. package/dist/validators/index.d.ts +1 -1
  176. package/dist/validators/index.js +1 -35
  177. package/dist/validators/intersection.js +1 -116
  178. package/dist/validators/json.js +1 -66
  179. package/dist/validators/lazy.js +1 -50
  180. package/dist/validators/literal.d.ts +7 -3
  181. package/dist/validators/literal.js +1 -60
  182. package/dist/validators/map.js +1 -214
  183. package/dist/validators/nan.js +1 -38
  184. package/dist/validators/never.js +1 -32
  185. package/dist/validators/null.js +1 -40
  186. package/dist/validators/number.d.ts +20 -1
  187. package/dist/validators/number.js +1 -423
  188. package/dist/validators/object.d.ts +12 -2
  189. package/dist/validators/object.js +1 -864
  190. package/dist/validators/promise.js +1 -111
  191. package/dist/validators/record.d.ts +11 -8
  192. package/dist/validators/record.js +1 -258
  193. package/dist/validators/set.js +1 -199
  194. package/dist/validators/string-bool.js +1 -120
  195. package/dist/validators/string-formats.d.ts +58 -62
  196. package/dist/validators/string-formats.js +1 -135
  197. package/dist/validators/string.d.ts +47 -10
  198. package/dist/validators/string.js +1 -4
  199. package/dist/validators/symbol.js +1 -58
  200. package/dist/validators/template-literal.js +1 -103
  201. package/dist/validators/tuple.d.ts +17 -13
  202. package/dist/validators/tuple.js +1 -183
  203. package/dist/validators/uint8array.js +1 -101
  204. package/dist/validators/undefined.js +1 -40
  205. package/dist/validators/union.js +1 -187
  206. package/dist/validators/unknown.js +1 -37
  207. package/dist/validators/void.js +1 -44
  208. package/dist/validators/xor.d.ts +2 -2
  209. package/dist/validators/xor.js +1 -66
  210. package/package.json +239 -238
  211. package/dist/chunks/errors-core-BeiFUtZM.js +0 -81
  212. package/dist/chunks/index-0R2ntSud.js +0 -508
  213. package/dist/chunks/index-BvH403Yy.js +0 -88
  214. package/dist/chunks/security-B0Pd_vSY.js +0 -35
  215. package/dist/chunks/string-BJRQod3t.js +0 -530
  216. package/dist/cjs/chunks/errors-core-DWxLFs2f.cjs +0 -83
  217. package/dist/cjs/chunks/index-BkI37108.cjs +0 -538
  218. package/dist/cjs/chunks/index-Cc3bjUMX.cjs +0 -91
  219. package/dist/cjs/chunks/security-Dc3Jh13T.cjs +0 -37
  220. package/dist/cjs/chunks/string-BKZrc4hs.cjs +0 -533
package/README.md CHANGED
@@ -1,1235 +1,1296 @@
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-100%25-brightgreen.svg)](package.json)
4
-
5
- VLD is a blazing-fast, type-safe validation library for TypeScript and JavaScript with **Zod-compatible root and subpath APIs**. 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
- - **100% Statement / Branch / Line Coverage**: Rigorously tested with 2160 passing tests
38
- - **Release-Gated Performance**: CI guards require VLD to stay faster than the latest stable Zod across runtime, startup, and memory benchmarks
39
- - **Drop-in App Verification**: A real TypeScript fixture is compiled and run once with `zod` and once with built VLD, then normalized runtime output is compared
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 in v2.0.0 - Modular Architecture
52
-
53
- #### Tree-Shakable Mini API
54
- ```typescript
55
- import { string, number, object, optional } from '@oxog/vld/mini';
56
-
57
- const schema = object({
58
- name: string().min(1),
59
- age: optional(number().positive()),
60
- });
61
- ```
62
- - **82% smaller bundles** when using only needed validators
63
- - Individual factory functions for optimal tree-shaking
64
- - Full TypeScript support with identical type inference
65
-
66
- #### Lazy Locale Loading
67
- ```typescript
68
- import { setLocaleAsync } from '@oxog/vld/locales';
69
- await setLocaleAsync('tr'); // Loads Turkish on demand
70
- ```
71
- - **92% bundle reduction** - Only English bundled by default
72
- - `preloadLocales()` for SSR/batch loading
73
- - Full backwards compatibility with `setLocale()`
74
-
75
- #### Dual ESM/CJS Build
76
- - ESM builds for modern bundlers (Vite, esbuild)
77
- - CJS builds for Node.js and legacy environments
78
- - Proper `exports` field with conditional exports
79
-
80
- ### NEW in v1.5.0 - Major Platform Release
81
-
82
- #### Plugin System
83
- - **`definePlugin()`**: Create custom plugins with validators, transforms, and codecs
84
- - **`usePlugin()`**: Register plugins globally
85
- - **Plugin Hooks**: Lifecycle hooks for validation events
86
- - **Custom Validators**: Extend VLD with your own validators
87
-
88
- #### Result Pattern
89
- - **`Ok()`/`Err()`**: Functional error handling
90
- - **`match()`**: Pattern matching on results
91
- - **`map()`/`flatMap()`**: Transform results
92
- - **`tryCatch()`**: Safe function execution
93
- - **`all()`**: Combine multiple results
94
-
95
- #### Event System
96
- - **`createEmitter()`**: Type-safe event emitter
97
- - **`createEventBus()`**: Global event bus
98
- - **Validation Events**: Parse start, success, error, field validation
99
-
100
- #### CLI Tools
101
- - **`vld validate`**: Validate data from command line
102
- - **`vld benchmark`**: Run performance benchmarks
103
- - **Colored Output**: Beautiful terminal output with pigment
104
-
105
- #### New Validators
106
- - **`v.discriminatedUnion()`**: Discriminated union types
107
- - **`v.xor()`**: Exclusive OR validation
108
- - **`v.file()`**: File upload validation
109
- - **`v.function()`**: Function validation
110
- - **`v.custom()`**: Type-safe custom validators
111
- - **`v.json()`**: JSON string validation with schema
112
- - **`v.lazy()`**: Recursive schema support
113
- - **`v.nan()`**: NaN validation
114
- - **`v.null()`**: Null validation
115
- - **`v.undefined()`**: Undefined validation
116
- - **`v.templateLiteral()`**: Template literal types
117
-
118
- #### New String Format Validators
119
- - **`v.hostname()`**: Hostname validation
120
- - **`v.emoji()`**: Emoji validation
121
- - **`v.base64()`/`v.base64url()`**: Base64 format validation
122
- - **`v.hex()`**: Hex string validation
123
- - **`v.jwt()`**: JWT format validation
124
- - **`v.nanoid()`/`v.cuid()`/`v.cuid2()`/`v.ulid()`**: ID format validation
125
- - **`v.mac()`**: MAC address validation
126
- - **`v.cidrv4()`/`v.cidrv6()`**: CIDR block validation
127
- - **`v.e164()`**: E.164 phone number validation
128
- - **`v.hash()`**: Hash validation (md5, sha1, sha256, sha384, sha512)
129
- - **`v.iso.date()`/`v.iso.time()`/`v.iso.dateTime()`/`v.iso.duration()`**: ISO format validation
130
-
131
- #### Enhanced Object Utilities
132
- - **`v.strictObject()`**: Strict mode object validation
133
- - **`v.looseObject()`**: Passthrough object validation
134
- - **`v.partialRecord()`**: Partial record validation
135
- - **`v.looseRecord()`**: Loose record validation
136
- - **`v.int()`**: Integer shortcut
137
- - **`v.int32()`**: 32-bit integer shortcut
138
- - **`v.nullish()`**: Null or undefined
139
- - **`v.NEVER`**: NEVER constant for transforms (Zod 4 parity)
140
-
141
- ### Codec System - Beyond Zod
142
- - **Bidirectional Transformations**: Full encode/decode support for data conversion
143
- - **19 Built-in Codecs**: String conversions, date parsing, JSON, URL, binary data
144
- - **Zod-Compatible**: All `stringToNumber`, `jsonCodec`, `base64ToBytes`, etc.
145
- - **Async Support**: Both sync and async codec operations
146
- - **Custom Codecs**: Create your own bidirectional transformations
147
- - **Type-Safe**: Full TypeScript support with perfect type inference
148
-
149
- ## Performance
150
-
151
- VLD is designed for speed and efficiency with recent optimizations delivering exceptional performance:
152
-
153
- ### Release-Gated Speed Benchmarks (v2.1.0 vs Zod 4.4.3)
154
- - Runtime guard: VLD must stay at least 1.2x faster on every guarded hot path and keep at least a 3x average ratio
155
- - Startup guard: VLD must keep at least 1.1x faster import, 1.25x faster total startup, and 1.25x faster warm parse ratios than Zod
156
- - Memory guard: VLD must keep at least 2x lower total retained heap, 1.5x higher aggregate throughput, and no guarded case below 1.1x speed
157
-
158
- ### Recent Local Benchmark Snapshot
159
- - **52.92x faster** for nullish validation
160
- - **46.58x faster** for catch validation
161
- - **27.62x faster** for number validation
162
- - **14.88x faster** for bigint validation
163
- - **10.71x faster** for nullable validation
164
- - **Over 10x faster** average guarded runtime ratio in recent release checks
165
-
166
- ### Optimizations
167
- - **110x improvement** in union type validation
168
- - **Simplified email regex** for maximum performance
169
- - **Inline type checks** in object validation
170
- - **Optimized loops** with direct array assignment
171
- - **SafeParse optimization** to avoid try-catch overhead
172
- - **Pre-computed keys** with Set for O(1) lookups
173
-
174
- ### Memory Efficiency
175
- - **18.13x less memory** for schema creation
176
- - **6.20x less memory** for union validation
177
- - **2.81x less memory** for simple string validation
178
- - **4.85x less memory** overall
179
-
180
- ### A Note on Real-World Benchmarking
181
-
182
- Many validation library benchmarks can be misleading because they often test with **reused schema instances**:
183
-
184
- ```javascript
185
- // What benchmarks typically test (unrealistic):
186
- const schema = z.string();
187
- for (let i = 0; i < 1000000; i++) {
188
- schema.parse(data); // Same instance reused
189
- }
190
-
191
- // What happens in real applications:
192
- app.post('/api/user', (req, res) => {
193
- // New schema created for each request
194
- const schema = z.object({
195
- email: z.string().email(),
196
- age: z.number().min(18)
197
- });
198
- schema.parse(req.body);
199
- });
200
- ```
201
-
202
- When testing real-world patterns:
203
- - **Creating new instances**: VLD is **2000x faster** than Zod
204
- - **Reused instances**: Zod benefits from V8's singleton optimization
205
- - **Real applications**: Schemas are often created dynamically, where VLD excels
206
-
207
- Run `npm run benchmark:truth` to see the real performance difference.
208
-
209
- ## Installation
210
-
211
- ```bash
212
- npm install @oxog/vld
213
- # or
214
- yarn add @oxog/vld
215
- # or
216
- pnpm add @oxog/vld
217
- ```
218
-
219
- ## Quick Start
220
-
221
- ```typescript
222
- import { v } from '@oxog/vld';
223
-
224
- // It is recommended to import as `v` for consistency with Zod's `z`
225
- // and for a more concise syntax.
226
-
227
- // Define a schema
228
- const userSchema = v.object({
229
- name: v.string().min(2),
230
- email: v.string().email(),
231
- age: v.number().min(18).max(100),
232
- isActive: v.boolean()
233
- });
234
-
235
- // Validate data
236
- const result = userSchema.safeParse({
237
- name: 'John Doe',
238
- email: 'john@example.com',
239
- age: 25,
240
- isActive: true
241
- });
242
-
243
- if (result.success) {
244
- console.log('Valid user:', result.data);
245
- } else {
246
- console.log('Validation error:', result.error);
247
- }
248
- ```
249
-
250
- For advanced error formatting:
251
- ```typescript
252
- import { v, VldError, treeifyError, prettifyError, flattenError } from '@oxog/vld';
253
- ```
254
-
255
- ## API Reference
256
-
257
- ### Basic Types
258
-
259
- ```typescript
260
- v.string() // String validation
261
- v.number() // Number validation
262
- v.int() // Integer validation (shortcut)
263
- v.int32() // 32-bit integer validation
264
- v.boolean() // Boolean validation
265
- v.bigint() // BigInt validation
266
- v.symbol() // Symbol validation
267
- v.date() // Date validation
268
- v.uint8array()// Uint8Array validation
269
- v.literal() // Literal values
270
- v.enum() // Enum values (supports TypeScript enums)
271
- v.any() // Any type
272
- v.unknown() // Unknown type
273
- v.void() // Void type
274
- v.never() // Never type
275
- v.null() // Null type
276
- v.undefined() // Undefined type
277
- v.nan() // NaN type
278
- ```
279
-
280
- ### Advanced Types
281
-
282
- ```typescript
283
- // Collections
284
- v.array(v.string()) // Array validation
285
- v.tuple(v.string(), v.number()) // Fixed-length tuple
286
- v.record(v.number()) // Record/dictionary validation
287
- v.set(v.string()) // Set validation
288
- v.map(v.string(), v.number()) // Map validation
289
-
290
- // Objects
291
- v.object({ // Object schema
292
- name: v.string(),
293
- age: v.number()
294
- })
295
- v.strictObject({...}) // No extra fields allowed
296
- v.looseObject({...}) // Extra fields passed through
297
-
298
- // Composition
299
- v.union(v.string(), v.number()) // Union types
300
- v.intersection(schemaA, schemaB) // Intersection types
301
- v.discriminatedUnion('type', ...) // Discriminated union
302
- v.xor(schemaA, schemaB) // Exclusive OR
303
- v.optional(v.string()) // Optional fields
304
- v.nullable(v.string()) // Nullable fields
305
- v.nullish(v.string()) // Null or undefined
306
- ```
307
-
308
- ### String Validators
309
-
310
- ```typescript
311
- v.string()
312
- .min(5) // Minimum length
313
- .max(10) // Maximum length
314
- .length(8) // Exact length
315
- .email() // Email format
316
- .url() // URL format
317
- .uuid() // UUID format
318
- .regex(/pattern/) // Custom regex
319
- .startsWith('prefix') // String prefix
320
- .endsWith('suffix') // String suffix
321
- .includes('substring') // Contains substring
322
- .ip() // IP address (v4 or v6)
323
- .trim() // Trim whitespace
324
- .toLowerCase() // Convert to lowercase
325
- .toUpperCase() // Convert to uppercase
326
- .nonempty() // Non-empty string
327
- ```
328
-
329
- ### String Format Validators (Top-Level)
330
-
331
- ```typescript
332
- v.email() // Email validation
333
- v.uuid() // UUID validation
334
- v.uuid({ version: 'v4' }) // UUID v4 validation
335
- v.uuidv4() // UUID v4 shortcut
336
- v.hostname() // Hostname validation
337
- v.emoji() // Emoji validation
338
- v.base64() // Base64 format
339
- v.base64url() // Base64 URL-safe format
340
- v.hex() // Hex string
341
- v.jwt() // JWT format
342
- v.nanoid() // NanoID format
343
- v.cuid() // CUID format
344
- v.cuid2() // CUID2 format
345
- v.ulid() // ULID format
346
- v.ipv4() // IPv4 address
347
- v.ipv6() // IPv6 address
348
- v.mac() // MAC address
349
- v.cidrv4() // IPv4 CIDR block
350
- v.cidrv6() // IPv6 CIDR block
351
- v.e164() // E.164 phone number
352
- v.hash('sha256') // Hash validation
353
- v.iso.date() // ISO date format
354
- v.iso.time() // ISO time format
355
- v.iso.dateTime() // ISO datetime format
356
- v.iso.duration() // ISO duration format
357
- ```
358
-
359
- ### Number Validators
360
-
361
- ```typescript
362
- v.number()
363
- .min(0) // Minimum value
364
- .max(100) // Maximum value
365
- .int() // Integer only
366
- .positive() // Positive numbers
367
- .negative() // Negative numbers
368
- .nonnegative() // >= 0
369
- .nonpositive() // <= 0
370
- .finite() // Finite numbers
371
- .safe() // Safe integers
372
- .multipleOf(5) // Multiple of value
373
- ```
374
-
375
- ### Arrays and Objects
376
-
377
- ```typescript
378
- // Arrays
379
- v.array(v.string()) // Array of strings
380
- .min(1) // Minimum length
381
- .max(10) // Maximum length
382
- .length(5) // Exact length
383
- .nonempty() // Non-empty array
384
-
385
- // Objects
386
- v.object({
387
- name: v.string(),
388
- age: v.number()
389
- })
390
- .partial() // All fields optional
391
- .strict() // No extra fields
392
- .passthrough() // Allow extra fields
393
- ```
394
-
395
- ### Composite Types
396
-
397
- ```typescript
398
- // Optional
399
- v.optional(v.string()) // string | undefined
400
-
401
- // Nullable
402
- v.nullable(v.string()) // string | null
403
-
404
- // Nullish
405
- v.nullish(v.string()) // string | null | undefined
406
-
407
- // Union
408
- v.union(v.string(), v.number()) // string | number
409
-
410
- // Discriminated Union
411
- v.discriminatedUnion('type',
412
- v.object({ type: v.literal('a'), a: v.string() }),
413
- v.object({ type: v.literal('b'), b: v.number() })
414
- )
415
-
416
- // Literal
417
- v.literal('active') // 'active'
418
-
419
- // Enum
420
- v.enum('red', 'green', 'blue') // 'red' | 'green' | 'blue'
421
- ```
422
-
423
- ### Type Coercion
424
-
425
- ```typescript
426
- // Coerce strings from various types
427
- v.coerce.string().parse(123) // "123"
428
- v.coerce.string().parse(true) // "true"
429
-
430
- // Coerce numbers from strings/booleans
431
- v.coerce.number().parse("123") // 123
432
- v.coerce.number().parse(true) // 1
433
-
434
- // Coerce booleans from strings/numbers
435
- v.coerce.boolean().parse("true") // true
436
- v.coerce.boolean().parse(1) // true
437
-
438
- // Coerce BigInt from strings/numbers
439
- v.coerce.bigint().parse("123") // 123n
440
- v.coerce.bigint().parse(456) // 456n
441
-
442
- // Coerce Date from strings/timestamps
443
- v.coerce.date().parse("2023-01-01") // Date object
444
- v.coerce.date().parse(1672531200000) // Date object
445
- ```
446
-
447
- ### Object Schema Methods
448
-
449
- ```typescript
450
- const userSchema = v.object({
451
- name: v.string(),
452
- age: v.number(),
453
- email: v.string(),
454
- role: v.string()
455
- });
456
-
457
- // Pick specific fields
458
- const publicSchema = userSchema.pick('name', 'age');
459
- // Type: { name: string; age: number }
460
-
461
- // Omit sensitive fields
462
- const safeSchema = userSchema.omit('email', 'role');
463
- // Type: { name: string; age: number }
464
-
465
- // Extend with new fields
466
- const extendedSchema = userSchema.extend({
467
- isActive: v.boolean(),
468
- lastLogin: v.date()
469
- });
470
- // Type: { name: string; age: number; email: string; role: string; isActive: boolean; lastLogin: Date }
471
- ```
472
-
473
- ### Advanced Validation Methods
474
-
475
- ```typescript
476
- // Custom validation with refine()
477
- const positiveNumber = v.number()
478
- .refine(n => n > 0, "Number must be positive");
479
-
480
- // Data transformation with transform()
481
- const uppercaseString = v.string()
482
- .transform(s => s.toUpperCase());
483
-
484
- // Default values for undefined
485
- const withDefault = v.string().default("fallback");
486
- withDefault.parse(undefined); // "fallback"
487
-
488
- // Catch errors and provide fallback
489
- const withCatch = v.number().catch(-1);
490
- withCatch.parse("invalid"); // -1
491
-
492
- // Method chaining
493
- const complexSchema = v.string()
494
- .min(3)
495
- .transform(s => s.trim())
496
- .refine(s => s.includes('@'), 'Must contain @')
497
- .default('user@example.com');
498
- ```
499
-
500
- ### Special Validators
501
-
502
- ```typescript
503
- // JSON validator with optional schema
504
- v.json() // Any valid JSON
505
- v.json(v.object({ name: v.string() })) // Typed JSON
506
-
507
- // Lazy for recursive schemas
508
- const categorySchema = v.lazy(() =>
509
- v.object({
510
- name: v.string(),
511
- children: v.array(categorySchema).optional()
512
- })
513
- );
514
-
515
- // Custom validator
516
- v.custom({
517
- check: (val) => typeof val === 'string' && val.length > 0,
518
- message: 'Must be a non-empty string'
519
- });
520
-
521
- // File validator
522
- v.file()
523
- .maxSize(5 * 1024 * 1024) // 5MB
524
- .type(['image/png', 'image/jpeg']);
525
-
526
- // Function validator
527
- v.function()
528
- .args(v.string(), v.number())
529
- .returns(v.boolean());
530
- ```
531
-
532
- ### Type Inference
533
-
534
- ```typescript
535
- import { v } from '@oxog/vld';
536
- import type { Infer } from '@oxog/vld';
537
-
538
- const schema = v.object({
539
- name: v.string(),
540
- age: v.number()
541
- });
542
-
543
- // Automatically infer the type
544
- type User = Infer<typeof schema>;
545
- // { name: string; age: number }
546
- ```
547
-
548
- ### Error Formatting Types
549
-
550
- ```typescript
551
- import { VldError } from '@oxog/vld';
552
- import type {
553
- VldIssue, // Individual validation issue
554
- VldErrorTree, // Nested error structure
555
- VldFlattenedError // Flattened error structure
556
- } from '@oxog/vld';
557
- ```
558
-
559
- ### Custom Error Messages
560
-
561
- ```typescript
562
- const schema = v.string().min(8, 'Password must be at least 8 characters');
563
-
564
- const result = schema.safeParse('short');
565
- if (!result.success) {
566
- console.log(result.error.message); // 'Password must be at least 8 characters'
567
- }
568
- ```
569
-
570
- ## Internationalization (i18n)
571
-
572
- VLD supports 27+ languages out of the box with comprehensive error messages:
573
-
574
- ```typescript
575
- import { v, setLocale } from '@oxog/vld';
576
-
577
- // Default is English
578
- const schema = v.string().min(5);
579
- schema.safeParse('Hi'); // Error: "String must be at least 5 characters"
580
-
581
- // Switch to Turkish
582
- setLocale('tr');
583
- schema.safeParse('Hi'); // Error: "Metin en az 5 karakter olmali"
584
-
585
- // Switch to Spanish
586
- setLocale('es');
587
- schema.safeParse('Hi'); // Error: "La cadena debe tener al menos 5 caracteres"
588
-
589
- // Switch to Japanese
590
- setLocale('ja');
591
- schema.safeParse('Hi'); // Error: "..."
592
- ```
593
-
594
- ### Supported Languages
595
-
596
- #### Base Languages (15):
597
- - English (`en`) - Turkish (`tr`) - Spanish (`es`) - French (`fr`) - German (`de`)
598
- - Italian (`it`) - Portuguese (`pt`) - Russian (`ru`) - Japanese (`ja`) - Korean (`ko`)
599
- - Chinese (`zh`) - Arabic (`ar`) - Hindi (`hi`) - Dutch (`nl`) - Polish (`pl`)
600
-
601
- #### European Languages (4):
602
- - Danish (`da`) - Swedish (`sv`) - Norwegian (`no`) - Finnish (`fi`)
603
-
604
- #### Asian Languages (4):
605
- - Thai (`th`) - Vietnamese (`vi`) - Indonesian (`id`) - Bengali (`bn`)
606
-
607
- #### African Languages (2):
608
- - Swahili (`sw`) - Afrikaans (`af`)
609
-
610
- #### American Languages (2):
611
- - Portuguese Brazil (`pt-BR`) - Spanish Mexico (`es-MX`)
612
-
613
- **Plus 75+ additional languages** supported through comprehensive type definitions with English fallback.
614
-
615
- ## Error Handling & Formatting
616
-
617
- VLD provides advanced error formatting utilities similar to Zod's error handling system.
618
-
619
- ### Error Formatting Utilities
620
-
621
- ```typescript
622
- import { v, VldError, treeifyError, prettifyError, flattenError } from '@oxog/vld';
623
-
624
- const userSchema = v.object({
625
- username: v.string().min(3),
626
- favoriteNumbers: v.array(v.number()),
627
- profile: v.object({
628
- name: v.string(),
629
- email: v.string().email()
630
- })
631
- });
632
-
633
- const result = userSchema.safeParse({
634
- username: 'ab',
635
- favoriteNumbers: [1, 'two', 3],
636
- profile: {
637
- name: '',
638
- email: 'invalid-email'
639
- },
640
- extraField: 'not allowed'
641
- });
642
-
643
- if (!result.success) {
644
- const error = result.error as VldError;
645
-
646
- // 1. Tree Format - Nested structure for complex UIs
647
- const tree = treeifyError(error);
648
-
649
- // 2. Pretty Format - Human-readable console output
650
- const pretty = prettifyError(error);
651
-
652
- // 3. Flatten Format - Simple form validation
653
- const flattened = flattenError(error);
654
- }
655
- ```
656
-
657
- ### Using Error Formats in Practice
658
-
659
- #### React Form Validation
660
- ```typescript
661
- function UserForm() {
662
- const [errors, setErrors] = useState<VldFlattenedError | null>(null);
663
-
664
- const handleSubmit = (data: unknown) => {
665
- const result = userSchema.safeParse(data);
666
-
667
- if (!result.success) {
668
- setErrors(flattenError(result.error as VldError));
669
- } else {
670
- setErrors(null);
671
- }
672
- };
673
-
674
- return (
675
- <form onSubmit={handleSubmit}>
676
- {errors?.formErrors.map(error => (
677
- <div key={error} className="form-error">{error}</div>
678
- ))}
679
-
680
- <input name="username" />
681
- {errors?.fieldErrors.username?.map(error => (
682
- <div key={error} className="field-error">{error}</div>
683
- ))}
684
- </form>
685
- );
686
- }
687
- ```
688
-
689
- #### API Error Responses
690
- ```typescript
691
- app.post('/api/users', (req, res) => {
692
- const result = userSchema.safeParse(req.body);
693
-
694
- if (!result.success) {
695
- const tree = treeifyError(result.error as VldError);
696
- res.status(400).json({
697
- error: 'Validation failed',
698
- details: tree
699
- });
700
- }
701
- });
702
- ```
703
-
704
- ## Advanced Examples
705
-
706
- ### Complex Validation with New Features
707
-
708
- ```typescript
709
- const postSchema = v.object({
710
- id: v.union(v.string().uuid(), v.number()),
711
- title: v.string().min(5).max(100),
712
- content: v.string().min(10),
713
- author: v.object({
714
- name: v.string(),
715
- email: v.string().email(),
716
- age: v.coerce.number(),
717
- }),
718
- tags: v.set(v.string()).default(new Set()),
719
- metadata: v.record(v.any()),
720
- coordinates: v.tuple(v.number(), v.number()),
721
- publishedAt: v.date().default(() => new Date()),
722
- status: v.enum('draft', 'published', 'archived')
723
- });
724
-
725
- // Extend with additional fields
726
- const blogPostSchema = postSchema.extend({
727
- viewCount: v.bigint().default(0n),
728
- categories: v.array(v.string()).min(1),
729
- featured: v.boolean().default(false)
730
- });
731
- ```
732
-
733
- ### Discriminated Union
734
-
735
- ```typescript
736
- const eventSchema = v.discriminatedUnion('type',
737
- v.object({
738
- type: v.literal('click'),
739
- x: v.number(),
740
- y: v.number()
741
- }),
742
- v.object({
743
- type: v.literal('scroll'),
744
- direction: v.enum('up', 'down'),
745
- distance: v.number()
746
- }),
747
- v.object({
748
- type: v.literal('keypress'),
749
- key: v.string(),
750
- modifiers: v.array(v.enum('ctrl', 'alt', 'shift'))
751
- })
752
- );
753
-
754
- // Type-safe parsing
755
- const event = eventSchema.parse({
756
- type: 'click',
757
- x: 100,
758
- y: 200
759
- });
760
- ```
761
-
762
- ### Recursive Schemas
763
-
764
- ```typescript
765
- const categorySchema: ReturnType<typeof v.lazy> = v.lazy(() =>
766
- v.object({
767
- name: v.string(),
768
- slug: v.string().regex(/^[a-z0-9-]+$/),
769
- children: v.array(categorySchema).optional()
770
- })
771
- );
772
-
773
- const category = categorySchema.parse({
774
- name: 'Electronics',
775
- slug: 'electronics',
776
- children: [
777
- {
778
- name: 'Phones',
779
- slug: 'phones',
780
- children: [
781
- { name: 'Smartphones', slug: 'smartphones' }
782
- ]
783
- }
784
- ]
785
- });
786
- ```
787
-
788
- ### Type-Safe Forms
789
-
790
- ```typescript
791
- const loginSchema = v.object({
792
- username: v.string().min(3),
793
- password: v.string().min(8),
794
- rememberMe: v.optional(v.boolean())
795
- });
796
-
797
- type LoginForm = Infer<typeof loginSchema>;
798
-
799
- function handleLogin(data: unknown) {
800
- const result = loginSchema.safeParse(data);
801
-
802
- if (result.success) {
803
- const { username, password, rememberMe } = result.data;
804
- // ... handle login
805
- }
806
- }
807
- ```
808
-
809
- ## Codecs - Bidirectional Transformations
810
-
811
- VLD introduces **codecs** - powerful bidirectional transformations that can convert data between different representations.
812
-
813
- ### What are Codecs?
814
-
815
- Codecs enable safe, type-checked conversions between different data formats:
816
-
817
- ```typescript
818
- import { stringToNumber, jsonCodec, base64ToBytes } from '@oxog/vld';
819
-
820
- // String to number conversion
821
- const age = stringToNumber.parse('25'); // 25
822
- const price = stringToNumber.encode(99.99); // "99.99"
823
-
824
- // JSON codec
825
- const userJson = jsonCodec();
826
- const user = userJson.parse('{"name":"John","age":30}');
827
- const jsonString = userJson.encode(user);
828
-
829
- // Binary data
830
- const bytes = base64ToBytes.parse('SGVsbG8gV29ybGQ=');
831
- ```
832
-
833
- ### Built-in Codecs
834
-
835
- #### String Conversion Codecs
836
- ```typescript
837
- import { stringToNumber, stringToInt, stringToBigInt, stringToBoolean } from '@oxog/vld';
838
-
839
- stringToNumber.parse('42.5'); // 42.5
840
- stringToInt.parse('42'); // 42
841
- stringToBigInt.parse('123n'); // 123n
842
- stringToBoolean.parse('true'); // true
843
- ```
844
-
845
- #### Date Conversion Codecs
846
- ```typescript
847
- import { isoDatetimeToDate, epochSecondsToDate, epochMillisToDate } from '@oxog/vld';
848
-
849
- isoDatetimeToDate.parse('2023-12-25T10:30:00.000Z'); // Date
850
- epochSecondsToDate.parse(1703505000); // Date
851
- epochMillisToDate.parse(1703505000000); // Date
852
- ```
853
-
854
- #### URL Codecs
855
- ```typescript
856
- import { stringToURL, stringToHttpURL, uriComponent } from '@oxog/vld';
857
-
858
- stringToURL.parse('https://example.com/path?q=1');
859
- stringToHttpURL.parse('https://api.example.com');
860
- uriComponent.parse('Hello World!'); // "Hello%20World!"
861
- ```
862
-
863
- #### Binary Data Codecs
864
- ```typescript
865
- import { base64ToBytes, hexToBytes, utf8ToBytes, bytesToUtf8 } from '@oxog/vld';
866
-
867
- base64ToBytes.parse('SGVsbG8='); // Uint8Array
868
- hexToBytes.parse('48656c6c6f'); // Uint8Array
869
- utf8ToBytes.parse('Hello'); // Uint8Array
870
- bytesToUtf8.parse(new Uint8Array([72, 101, 108, 108, 111])); // "Hello"
871
- ```
872
-
873
- ### Custom Codecs
874
-
875
- ```typescript
876
- const csvToArray = v.codec(
877
- v.string(),
878
- v.array(v.string()),
879
- {
880
- decode: (csv: string) => csv.split(',').map(s => s.trim()),
881
- encode: (arr: string[]) => arr.join(', ')
882
- }
883
- );
884
-
885
- const tags = csvToArray.parse('react, typescript, vld');
886
- // ["react", "typescript", "vld"]
887
-
888
- const csvString = csvToArray.encode(['node', 'express', 'api']);
889
- // "node, express, api"
890
- ```
891
-
892
- ## Plugin System
893
-
894
- VLD v1.5.0 introduces a powerful plugin system for extending functionality.
895
-
896
- ### Creating a Plugin
897
-
898
- ```typescript
899
- import { definePlugin, usePlugin, v } from '@oxog/vld';
900
-
901
- // Define a custom plugin
902
- const myPlugin = definePlugin({
903
- name: 'my-plugin',
904
- version: '1.0.0',
905
-
906
- // Custom validators
907
- validators: {
908
- phoneNumber: () => v.string().regex(/^\+?[1-9]\d{1,14}$/),
909
- postalCode: () => v.string().regex(/^\d{5}(-\d{4})?$/)
910
- },
911
-
912
- // Custom transforms
913
- transforms: {
914
- normalizePhone: (phone: string) => phone.replace(/[^\d+]/g, '')
915
- },
916
-
917
- // Lifecycle hooks
918
- install(kernel) {
919
- console.log('Plugin installed!');
920
- }
921
- });
922
-
923
- // Register the plugin
924
- usePlugin(myPlugin);
925
- ```
926
-
927
- ### Using Plugin Validators
928
-
929
- ```typescript
930
- import { createVldKernel, usePlugin } from '@oxog/vld';
931
-
932
- const kernel = createVldKernel({ debug: true });
933
-
934
- kernel.use(myPlugin);
935
-
936
- // Access custom validators
937
- const phoneSchema = kernel.validator('phoneNumber');
938
- phoneSchema.parse('+1234567890');
939
- ```
940
-
941
- ## Result Pattern
942
-
943
- VLD v1.5.0 includes a functional Result pattern for error handling.
944
-
945
- ### Basic Usage
946
-
947
- ```typescript
948
- import { Ok, Err, match, map, flatMap, tryCatch } from '@oxog/vld';
949
-
950
- // Create results
951
- const success = Ok(42);
952
- const failure = Err(new Error('Something went wrong'));
953
-
954
- // Pattern matching
955
- const message = match(success, {
956
- ok: (value) => `Got: ${value}`,
957
- err: (error) => `Error: ${error.message}`
958
- });
959
-
960
- // Transform results
961
- const doubled = map(success, (n) => n * 2); // Ok(84)
962
-
963
- // Chain operations
964
- const result = flatMap(success, (n) =>
965
- n > 0 ? Ok(n * 2) : Err(new Error('Must be positive'))
966
- );
967
-
968
- // Safe function execution
969
- const parsed = tryCatch(() => JSON.parse('{"a":1}'));
970
- ```
971
-
972
- ### With Validation
973
-
974
- ```typescript
975
- import { v, isOk, isErr, unwrapOr } from '@oxog/vld';
976
-
977
- const schema = v.object({
978
- name: v.string(),
979
- age: v.number().min(0)
980
- });
981
-
982
- const result = schema.safeParse(data);
983
-
984
- if (isOk(result)) {
985
- console.log('Valid:', result.data);
986
- } else {
987
- console.log('Invalid:', result.error);
988
- }
989
-
990
- // With default value
991
- const user = unwrapOr(result, { name: 'Guest', age: 0 });
992
- ```
993
-
994
- ### Combining Results
995
-
996
- ```typescript
997
- import { all, fromNullable } from '@oxog/vld';
998
-
999
- // Combine multiple results
1000
- const results = [Ok(1), Ok(2), Ok(3)];
1001
- const combined = all(results); // Ok([1, 2, 3])
1002
-
1003
- // Convert nullable to Result
1004
- const maybeValue: string | null = getValue();
1005
- const result = fromNullable(maybeValue, new Error('Value is null'));
1006
- ```
1007
-
1008
- ## CLI Tools
1009
-
1010
- VLD includes command-line tools for validation and benchmarking.
1011
-
1012
- ### Installation
1013
-
1014
- ```bash
1015
- npm install -g @oxog/vld
1016
- # or use npx
1017
- npx vld --help
1018
- ```
1019
-
1020
- ### Commands
1021
-
1022
- ```bash
1023
- # Show help
1024
- vld --help
1025
-
1026
- # Validate data
1027
- vld validate schema.json data.json
1028
-
1029
- # Run benchmarks
1030
- vld benchmark
1031
-
1032
- # Show version
1033
- vld --version
1034
- ```
1035
-
1036
- ### Programmatic CLI
1037
-
1038
- ```typescript
1039
- import { createCli, vldCli } from '@oxog/vld/cli';
1040
-
1041
- // Use the built-in CLI
1042
- vldCli.run(process.argv.slice(2));
1043
-
1044
- // Or create a custom CLI
1045
- const cli = createCli('my-app', '1.0.0', 'My validation app')
1046
- .command({
1047
- name: 'validate',
1048
- description: 'Validate data',
1049
- action: async (args, options) => {
1050
- // Custom validation logic
1051
- }
1052
- });
1053
-
1054
- cli.run(process.argv.slice(2));
1055
- ```
1056
-
1057
- ## Logger & Colored Output
1058
-
1059
- VLD includes a logging system and colored terminal output.
1060
-
1061
- ### Logger
1062
-
1063
- ```typescript
1064
- import { createLogger, setLogLevel, enableDebug } from '@oxog/vld';
1065
-
1066
- // Create a logger
1067
- const logger = createLogger({ prefix: 'VLD' });
1068
-
1069
- logger.info('Processing...');
1070
- logger.warn('Deprecated feature');
1071
- logger.error('Validation failed');
1072
- logger.debug('Debug info');
1073
-
1074
- // Set log level globally
1075
- setLogLevel('debug');
1076
-
1077
- // Enable debug mode
1078
- enableDebug();
1079
- ```
1080
-
1081
- ### Colored Output (Pigment)
1082
-
1083
- ```typescript
1084
- import { pigment, red, green, blue, bold, dim } from '@oxog/vld';
1085
-
1086
- console.log(red('Error!'));
1087
- console.log(green('Success!'));
1088
- console.log(bold(blue('Important')));
1089
- console.log(dim('Less important'));
1090
-
1091
- // Or use the pigment object
1092
- console.log(pigment.red('Error!'));
1093
- console.log(pigment.bold(pigment.green('Success!')));
1094
- ```
1095
-
1096
- ## VLD vs. Zod
1097
-
1098
- VLD is designed as a compelling alternative to Zod, offering full feature parity while delivering significant improvements.
1099
-
1100
- ### Feature Comparison
1101
-
1102
- | Feature | VLD | Zod |
1103
- | ----------------------- | ---------------------------------- | ------------------------------------ |
1104
- | **Performance** | **Release-gated faster runtime, startup, and memory paths** | Baseline |
1105
- | **Memory Usage** | **~4.85x less** overall | Baseline |
1106
- | **Internationalization**| **Built-in (27+ languages)** | Requires third-party library |
1107
- | **Dependencies** | **Zero** | `zod-i18n` for locales |
1108
- | **Bundle Size** | Smaller | Larger |
1109
- | **API** | 100% Zod-compatible | Standard Zod API |
1110
- | **Plugin System** | **Built-in** | Not available |
1111
- | **Result Pattern** | **Built-in** | Not available |
1112
- | **CLI Tools** | **Built-in** | Not available |
1113
- | **Codecs** | Built-in, bidirectional | Built-in |
1114
- | **Type Inference** | Excellent | Excellent |
1115
-
1116
- ### Seamless Migration from Zod
1117
-
1118
- ```javascript
1119
- // Before (Zod)
1120
- import { z } from 'zod';
1121
- const schema = z.string().email();
1122
-
1123
- // After (VLD) - Exact same syntax!
1124
- import { v } from '@oxog/vld';
1125
- const schema = v.string().email();
1126
- ```
1127
-
1128
- ### Drop-in Package Subpaths
1129
-
1130
- VLD also exposes Zod-compatible package subpaths so applications that import Zod 4 entry points can migrate by changing the package name:
1131
-
1132
- ```typescript
1133
- // Before
1134
- import { z } from 'zod';
1135
- import * as core from 'zod/v4/core';
1136
- import * as mini from 'zod/v4-mini';
1137
- import * as locales from 'zod/v4/locales';
1138
-
1139
- // After
1140
- import { z } from '@oxog/vld';
1141
- import * as core from '@oxog/vld/v4/core';
1142
- import * as mini from '@oxog/vld/v4-mini';
1143
- import * as locales from '@oxog/vld/v4/locales';
1144
- ```
1145
-
1146
- Release checks enforce zero missing exports and zero `typeof` mismatches for `zod/v4`, `zod/v4-mini`, `zod/v4/mini`, `zod/v4/core`, and `zod/v4/locales` against the installed latest Zod.
1147
-
1148
- ### Real App Drop-in Verification
1149
-
1150
- `npm run verify:drop-in` creates two temporary TypeScript applications from the same fixture:
1151
-
1152
- - One imports and runs against real `zod`.
1153
- - One imports and runs against the locally built `@oxog/vld` package.
1154
- - The fixture exercises root APIs, `v4`, `v4-mini`, `v4/core`, `v4/locales`, parsing, error formatting, JSON Schema output, mini helpers, and core factories.
1155
- - The normalized runtime outputs must match exactly.
1156
-
1157
- ## Benchmarks
1158
-
1159
- ### Performance Results
1160
-
1161
- Median of 11 samples per case against Zod 4.4.3, the latest stable release at the time of measurement. The release gate also runs focused runtime, startup, memory, package, install, security, Zod parity, and real drop-in app checks before publishing.
1162
-
1163
- | Test Case | VLD Median Performance | Improvement |
1164
- |-----------|----------------|-------------|
1165
- | Simple String | 719.2M ops/sec | **3.40x faster** |
1166
- | Email Validation | 22.1M ops/sec | **2.87x faster** |
1167
- | Top-level Email Format | 22.8M ops/sec | **3.66x faster** |
1168
- | StringBool Validation | 32.7M ops/sec | **3.44x faster** |
1169
- | Number Validation | 226.2M ops/sec | **27.62x faster** |
1170
- | Simple Object | 37.7M ops/sec | **1.40x faster** |
1171
- | Complex Object | 2.3M ops/sec | **1.02x faster** |
1172
- | Array Validation | 39.8M ops/sec | **6.74x faster** |
1173
- | Union Types | 38.1M ops/sec | **4.10x faster** |
1174
- | Optional Values | 236.9M ops/sec | **7.57x faster** |
1175
- | Nullable Values | 224.7M ops/sec | **10.71x faster** |
1176
- | Nullish Values | 228.4M ops/sec | **52.92x faster** |
1177
- | Default Values | 207.8M ops/sec | **9.71x faster** |
1178
- | Catch Values | 232.1M ops/sec | **46.58x faster** |
1179
- | SafeParse | 162.0M ops/sec | **1.80x faster** |
1180
- | Type Coercion | 204.5M ops/sec | **8.72x faster** |
1181
- | Enum Validation | 180.3M ops/sec | **1.32x faster** |
1182
- | Discriminated Union | 19.2M ops/sec | **1.79x faster** |
1183
- | Tuple Validation | 50.0M ops/sec | **7.99x faster** |
1184
- | Record Validation | 10.7M ops/sec | **5.63x faster** |
1185
- | Set Validation | 18.9M ops/sec | **1.84x faster** |
1186
- | Map Validation | 14.8M ops/sec | **2.37x faster** |
1187
- | BigInt Validation | 164.8M ops/sec | **14.88x faster** |
1188
- | Date Validation | 227.2M ops/sec | **2.57x faster** |
1189
- | Symbol Validation | 230.0M ops/sec | **1.01x faster** |
1190
- | Any Validation | 232.5M ops/sec | **1.01x faster** |
1191
- | Unknown Validation | 234.9M ops/sec | **1.04x faster** |
1192
- | Function Validation | 233.6M ops/sec | **3.52x faster** |
1193
- | Template Literal Validation | 39.3M ops/sec | **1.15x faster** |
1194
- | Promise Async Validation | 6.3M ops/sec | **1.38x faster** |
1195
-
1196
- **VLD won 30/30 snapshot tests. Release checks additionally enforce runtime, startup, memory, Zod subpath parity, and real drop-in app guard thresholds against Zod.**
1197
-
1198
- ### Run Benchmarks
1199
-
1200
- ```bash
1201
- # Quick performance comparison
1202
- npm run benchmark
1203
-
1204
- # Median-based stable comparison
1205
- npm run benchmark:stable
1206
-
1207
- # Fast CI-friendly performance regression guard
1208
- npm run benchmark:guard
1209
-
1210
- # Full release gate: lint, source and published types, tests, exports, package and install checks, security audit, Zod parity, real drop-in app verification, and performance guards
1211
- npm run release:check
1212
-
1213
- # Memory usage comparison
1214
- npm run benchmark:memory
1215
-
1216
- # Startup time comparison
1217
- npm run benchmark:startup
1218
-
1219
- # Run all benchmarks
1220
- npm run benchmark:all
1221
- ```
1222
-
1223
- ## Contributing
1224
-
1225
- Contributions are welcome! Please feel free to submit a Pull Request.
1226
-
1227
- ## Links
1228
-
1229
- - [Documentation](https://vld.oxog.dev)
1230
- - [NPM Package](https://www.npmjs.com/package/@oxog/vld)
1231
- - [GitHub Repository](https://github.com/ersinkoc/vld)
1232
-
1233
- ---
1234
-
1235
- Made with Love by [Ersin KOC](https://github.com/ersinkoc)
1
+ # VLD - 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-100%25-brightgreen.svg)](package.json)
4
+
5
+ VLD is a blazing-fast, type-safe validation library for TypeScript and JavaScript with **Zod-compatible root and subpath APIs**. 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
+ - [Codecs - Bidirectional Transformations](#codecs---bidirectional-transformations)
18
+ - [Plugin System](#plugin-system)
19
+ - [Result Pattern](#result-pattern)
20
+ - [CLI Tools](#cli-tools)
21
+ - [Logger & Colored Output](#logger--colored-output)
22
+ - [VLD vs. Zod](#vld-vs-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
+ - **100% Statement / Branch / Function / Line Coverage**: Rigorously tested with 2502 passing tests
38
+ - **Release-Gated Performance**: CI guards require VLD to stay faster than the latest stable Zod across runtime, startup, and memory benchmarks
39
+ - **Drop-in App Verification**: A real TypeScript fixture is compiled and run once with `zod` and once with built VLD, then normalized runtime output is compared
40
+ - **Audited Compatibility Contract**: Modern Zod 4.4.3 factory signatures, schema direction methods, release-fix behaviors, and JSON Schema defaults are differential-tested; see [the compatibility policy](docs/ZOD_COMPATIBILITY.md)
41
+
42
+ ### Advanced Zod-Compatible Features
43
+ - **Type Coercion**: `v.coerce.string()`, `v.coerce.number()`, `v.coerce.boolean()`, etc.
44
+ - **Advanced Types**: BigInt, Symbol, Tuple, Record, Set, Map validation
45
+ - **Intersection Types**: Combine multiple schemas with intelligent merging
46
+ - **Custom Validation**: `refine()` for custom predicates and validation logic
47
+ - **Data Transformation**: `transform()` for post-validation data transformation
48
+ - **Default Values**: `default()` for handling undefined inputs elegantly
49
+ - **Fallback Handling**: `catch()` for graceful error recovery
50
+ - **Object Utilities**: `pick()`, `omit()`, `extend()` for flexible object schemas
51
+
52
+ ### New in v2.2.x - Error Parity, Canary Parity, and Minified Builds
53
+
54
+ #### Zod 4-Compatible Error Issues
55
+ Validation failures now carry Zod 4's structured issue shape, so error-handling code ports across unchanged:
56
+ - `invalid_type` issues expose `expected` and `received` and use the `"Invalid input: expected X, received Y"` message format
57
+ - `too_small` / `too_big` issues expose `minimum`, `maximum`, `origin`, and `inclusive`
58
+ - `invalid_format` issues expose `format`, `origin`, and `pattern`; `invalid_value` issues expose a `values` array
59
+ - `parse()` throws `VldError` (which extends `Error`) for every validator, matching Zod's throw behavior
60
+
61
+ #### New Parity Helpers
62
+ ```typescript
63
+ import { v, deepPartial, input, output } from '@oxog/vld';
64
+
65
+ v.creditCard().parse('4242424242424242'); // regex plus Luhn checksum
66
+
67
+ const schema = v.object({
68
+ profile: v.object({ name: v.string(), age: v.number() })
69
+ });
70
+
71
+ const draft = deepPartial(schema); // every nested field optional
72
+ const inputSide = input(schema); // input-side view of a pipe
73
+ const outputSide = output(schema); // output-side view of a pipe
74
+ ```
75
+ - `deepPartial()` walks children first, mirroring Zod's `visit.js` ordering, and stays cycle-safe through lazy deferral
76
+ - `input()` / `output()` resolve pipe direction the way Zod's root helpers do
77
+ - `@oxog/vld/v4/core` gained the matching `_creditCard`, `isValidCreditCard`, `standardProps`, `handleUnrepresentable`, `$ZodCyclicError`, `attachMemoizer`, and `isBackEdge` shims
78
+
79
+ #### Minified Release Builds
80
+ - Release artifacts ship minified by default (opt out with `VLD_MINIFY=0`)
81
+ - Mangling preserves `/^Vld/` class names, because JSON Schema conversion dispatches on `schema.constructor.name`
82
+ - A built-CJS guard in `verify:drop-in` asserts name-dispatched composites against the minified bundle, so this can never silently regress again
83
+
84
+ ### NEW in v2.0.0 - Modular Architecture
85
+
86
+ #### Tree-Shakable Mini API
87
+ ```typescript
88
+ import { string, number, object, optional } from '@oxog/vld/mini';
89
+
90
+ const schema = object({
91
+ name: string().min(1),
92
+ age: optional(number().positive()),
93
+ });
94
+ ```
95
+ - **82% smaller bundles** when using only needed validators
96
+ - Individual factory functions for optimal tree-shaking
97
+ - Full TypeScript support with identical type inference
98
+
99
+ #### Lazy Locale Loading
100
+
101
+ > **Bundle-size contract:** the lazy entrypoint is published as
102
+ > `@oxog/vld/locales/lazy` (`src/locales/lazy.ts`). It uses dynamic `import()`
103
+ > for each locale on first use, so tree-shaken bundles ship **only English by
104
+ > default**. Importing the plain `@oxog/vld/locales` subpath (or the root
105
+ > `@oxog/vld` package) eagerly pulls every locale into the graph; pick the
106
+ > `lazy` subpath if you only need one or two languages in production.
107
+
108
+ ```typescript
109
+ import { setLocaleAsync, preloadLocales } from '@oxog/vld/locales/lazy';
110
+
111
+ // First call loads the locale on demand; subsequent calls hit the cache.
112
+ await setLocaleAsync('tr');
113
+
114
+ // SSR / batch warm-up: preload what you need before serving traffic.
115
+ await preloadLocales(['en', 'de', 'ja']);
116
+ ```
117
+ - **92% bundle reduction** when you only ship English plus the locales you pre-load
118
+ - `setLocaleAsync()` is true async dynamic-import, not a sync wrapper
119
+ - `preloadLocales()` for SSR/batch loading
120
+ - Backwards-compatible `setLocale()` is still available on the eager entrypoint
121
+
122
+ #### Dual ESM/CJS Build
123
+ - ESM builds for modern bundlers (Vite, esbuild)
124
+ - CJS builds for Node.js and legacy environments
125
+ - Proper `exports` field with conditional exports
126
+
127
+ ### NEW in v1.5.0 - Major Platform Release
128
+
129
+ #### Plugin System
130
+ - **`definePlugin()`**: Create custom plugins with validators, transforms, and codecs
131
+ - **`usePlugin()`**: Register plugins globally
132
+ - **Plugin Hooks**: Lifecycle hooks for validation events
133
+ - **Custom Validators**: Extend VLD with your own validators
134
+
135
+ #### Result Pattern
136
+ - **`Ok()`/`Err()`**: Functional error handling
137
+ - **`match()`**: Pattern matching on results
138
+ - **`map()`/`flatMap()`**: Transform results
139
+ - **`tryCatch()`**: Safe function execution
140
+ - **`all()`**: Combine multiple results
141
+
142
+ #### Event System
143
+ - **`createEmitter()`**: Type-safe event emitter
144
+ - **`createEventBus()`**: Global event bus
145
+ - **Validation Events**: Parse start, success, error, field validation
146
+
147
+ #### CLI Tools
148
+ - **`vld validate`**: Validate data from command line
149
+ - **`vld benchmark`**: Run performance benchmarks
150
+ - **Colored Output**: Beautiful terminal output with pigment
151
+
152
+ #### New Validators
153
+ - **`v.discriminatedUnion()`**: Discriminated union types
154
+ - **`v.xor()`**: Exclusive OR validation
155
+ - **`v.file()`**: File upload validation
156
+ - **`v.function()`**: Function validation
157
+ - **`v.custom()`**: Type-safe custom validators
158
+ - **`v.json()`**: JSON string validation with schema
159
+ - **`v.lazy()`**: Recursive schema support
160
+ - **`v.nan()`**: NaN validation
161
+ - **`v.null()`**: Null validation
162
+ - **`v.undefined()`**: Undefined validation
163
+ - **`v.templateLiteral()`**: Template literal types
164
+
165
+ #### New String Format Validators
166
+ - **`v.hostname()`**: Hostname validation
167
+ - **`v.emoji()`**: Emoji validation
168
+ - **`v.base64()`/`v.base64url()`**: Base64 format validation
169
+ - **`v.hex()`**: Hex string validation
170
+ - **`v.jwt()`**: JWT format validation
171
+ - **`v.nanoid()`/`v.cuid()`/`v.cuid2()`/`v.ulid()`**: ID format validation
172
+ - **`v.mac()`**: MAC address validation
173
+ - **`v.cidrv4()`/`v.cidrv6()`**: CIDR block validation
174
+ - **`v.e164()`**: E.164 phone number validation
175
+ - **`v.hash()`**: Hash validation (md5, sha1, sha256, sha384, sha512)
176
+ - **`v.iso.date()`/`v.iso.time()`/`v.iso.dateTime()`/`v.iso.duration()`**: ISO format validation
177
+
178
+ #### Enhanced Object Utilities
179
+ - **`v.strictObject()`**: Strict mode object validation
180
+ - **`v.looseObject()`**: Passthrough object validation
181
+ - **`v.partialRecord()`**: Partial record validation
182
+ - **`v.looseRecord()`**: Loose record validation
183
+ - **`v.int()`**: Integer shortcut
184
+ - **`v.int32()`**: 32-bit integer shortcut
185
+ - **`v.nullish()`**: Null or undefined
186
+ - **`v.NEVER`**: NEVER constant for transforms (Zod 4 parity)
187
+
188
+ ### Codec System - Beyond Zod
189
+ - **Bidirectional Transformations**: Full encode/decode support for data conversion
190
+ - **19 Built-in Codecs**: String conversions, date parsing, JSON, URL, binary data
191
+ - **Zod-Compatible**: All `stringToNumber`, `jsonCodec`, `base64ToBytes`, etc.
192
+ - **Async Support**: Both sync and async codec operations
193
+ - **Custom Codecs**: Create your own bidirectional transformations
194
+ - **Type-Safe**: Full TypeScript support with perfect type inference
195
+
196
+ ## Performance
197
+
198
+ VLD is designed for speed and efficiency with recent optimizations delivering exceptional performance:
199
+
200
+ ### Release-Gated Speed Benchmarks (v2.2.1 vs Zod 4.4.3)
201
+ - Runtime guard: VLD must stay at least 1.2x faster on every guarded hot path and keep at least a 3x average ratio
202
+ - Startup guard: cross-platform floors are 0.85x import, 0.9x total startup, and 1.25x warm parse versus Zod
203
+ - Memory guard: VLD must keep at least 2x lower total retained heap, 1.5x higher aggregate throughput, and no guarded case below 1.1x speed
204
+
205
+ ### Latest Runtime Guard Snapshot
206
+
207
+ Measured with `npm run benchmark:guard` (5 samples per case) against Zod 4.4.3:
208
+
209
+ | Guarded case | VLD | Zod | Ratio |
210
+ |--------------|-----|-----|-------|
211
+ | Nullish parse | 214.1M ops/sec | 7.0M ops/sec | **30.70x** |
212
+ | Number positive int parse | 252.9M ops/sec | 27.8M ops/sec | **9.10x** |
213
+ | Discriminated union parse | 35.0M ops/sec | 8.6M ops/sec | **4.06x** |
214
+ | Optional parse | 212.9M ops/sec | 56.6M ops/sec | **3.76x** |
215
+ | Union string and number parse | 39.1M ops/sec | 11.9M ops/sec | **3.30x** |
216
+ | Simple string parse | 620.7M ops/sec | 208.8M ops/sec | **2.97x** |
217
+ | Array number parse | 49.2M ops/sec | 28.3M ops/sec | **1.74x** |
218
+ | Simple object parse | 45.2M ops/sec | 27.7M ops/sec | **1.63x** |
219
+
220
+ 8/8 guarded cases pass with a **7.16x average ratio**. Absolute ops/sec numbers are hardware-dependent; the ratios are what the release gate enforces.
221
+
222
+ ### Optimizations
223
+ - **110x improvement** in union type validation
224
+ - **Simplified email regex** for maximum performance
225
+ - **Inline type checks** in object validation
226
+ - **Optimized loops** with direct array assignment
227
+ - **SafeParse optimization** to avoid try-catch overhead
228
+ - **Pre-computed keys** with Set for O(1) lookups
229
+
230
+ ### Memory Efficiency
231
+
232
+ Measured with `npm run benchmark:memory` against Zod 4.4.3. Heap deltas move a few percent between runs, so treat these as approximate:
233
+
234
+ - **~12.7x less memory** for schema creation
235
+ - **~7.3x less memory** for union validation
236
+ - **~2.9x less memory** for simple string validation
237
+ - **~2.7x less memory** for complex object validation
238
+ - **~5x less memory** overall
239
+
240
+ ### A Note on Real-World Benchmarking
241
+
242
+ Many validation library benchmarks can be misleading because they often test with **reused schema instances**:
243
+
244
+ ```javascript
245
+ // What benchmarks typically test (unrealistic):
246
+ const schema = z.string();
247
+ for (let i = 0; i < 1000000; i++) {
248
+ schema.parse(data); // Same instance reused
249
+ }
250
+
251
+ // What happens in real applications:
252
+ app.post('/api/user', (req, res) => {
253
+ // New schema created for each request
254
+ const schema = z.object({
255
+ email: z.string().email(),
256
+ age: z.number().min(18)
257
+ });
258
+ schema.parse(req.body);
259
+ });
260
+ ```
261
+
262
+ When testing real-world patterns:
263
+ - **Creating new instances**: VLD is **2000x faster** than Zod
264
+ - **Reused instances**: Zod benefits from V8's singleton optimization
265
+ - **Real applications**: Schemas are often created dynamically, where VLD excels
266
+
267
+ Run `npm run benchmark:truth` to see the real performance difference.
268
+
269
+ ## Installation
270
+
271
+ ```bash
272
+ npm install @oxog/vld
273
+ # or
274
+ yarn add @oxog/vld
275
+ # or
276
+ pnpm add @oxog/vld
277
+ ```
278
+
279
+ ## Quick Start
280
+
281
+ ```typescript
282
+ import { v } from '@oxog/vld';
283
+
284
+ // It is recommended to import as `v` for consistency with Zod's `z`
285
+ // and for a more concise syntax.
286
+
287
+ // Define a schema
288
+ const userSchema = v.object({
289
+ name: v.string().min(2),
290
+ email: v.string().email(),
291
+ age: v.number().min(18).max(100),
292
+ isActive: v.boolean()
293
+ });
294
+
295
+ // Validate data
296
+ const result = userSchema.safeParse({
297
+ name: 'John Doe',
298
+ email: 'john@example.com',
299
+ age: 25,
300
+ isActive: true
301
+ });
302
+
303
+ if (result.success) {
304
+ console.log('Valid user:', result.data);
305
+ } else {
306
+ console.log('Validation error:', result.error);
307
+ }
308
+ ```
309
+
310
+ For advanced error formatting:
311
+ ```typescript
312
+ import { v, VldError, treeifyError, prettifyError, flattenError } from '@oxog/vld';
313
+ ```
314
+
315
+ ## API Reference
316
+
317
+ ### Basic Types
318
+
319
+ ```typescript
320
+ v.string() // String validation
321
+ v.number() // Number validation
322
+ v.int() // Integer validation (shortcut)
323
+ v.int32() // 32-bit integer validation
324
+ v.boolean() // Boolean validation
325
+ v.bigint() // BigInt validation
326
+ v.symbol() // Symbol validation
327
+ v.date() // Date validation
328
+ v.uint8array()// Uint8Array validation
329
+ v.literal() // Literal values
330
+ v.enum() // Enum values (supports TypeScript enums)
331
+ v.any() // Any type
332
+ v.unknown() // Unknown type
333
+ v.void() // Void type
334
+ v.never() // Never type
335
+ v.null() // Null type
336
+ v.undefined() // Undefined type
337
+ v.nan() // NaN type
338
+ ```
339
+
340
+ ### Advanced Types
341
+
342
+ ```typescript
343
+ // Collections
344
+ v.array(v.string()) // Array validation
345
+ v.tuple(v.string(), v.number()) // Fixed-length tuple
346
+ v.record(v.number()) // Record/dictionary validation
347
+ v.set(v.string()) // Set validation
348
+ v.map(v.string(), v.number()) // Map validation
349
+
350
+ // Objects
351
+ v.object({ // Object schema
352
+ name: v.string(),
353
+ age: v.number()
354
+ })
355
+ v.strictObject({...}) // No extra fields allowed
356
+ v.looseObject({...}) // Extra fields passed through
357
+
358
+ // Composition
359
+ v.union(v.string(), v.number()) // Union types
360
+ v.intersection(schemaA, schemaB) // Intersection types
361
+ v.discriminatedUnion('type', ...) // Discriminated union
362
+ v.xor(schemaA, schemaB) // Exclusive OR
363
+ v.optional(v.string()) // Optional fields
364
+ v.nullable(v.string()) // Nullable fields
365
+ v.nullish(v.string()) // Null or undefined
366
+ ```
367
+
368
+ ### String Validators
369
+
370
+ ```typescript
371
+ v.string()
372
+ .min(5) // Minimum length
373
+ .max(10) // Maximum length
374
+ .length(8) // Exact length
375
+ .email() // Email format
376
+ .url() // URL format
377
+ .uuid() // UUID format
378
+ .regex(/pattern/) // Custom regex
379
+ .startsWith('prefix') // String prefix
380
+ .endsWith('suffix') // String suffix
381
+ .includes('substring') // Contains substring
382
+ .ip() // IP address (v4 or v6)
383
+ .trim() // Trim whitespace
384
+ .toLowerCase() // Convert to lowercase
385
+ .toUpperCase() // Convert to uppercase
386
+ .nonempty() // Non-empty string
387
+ ```
388
+
389
+ ### String Format Validators (Top-Level)
390
+
391
+ ```typescript
392
+ v.email() // Email validation
393
+ v.uuid() // UUID validation
394
+ v.uuid({ version: 'v4' }) // UUID v4 validation
395
+ v.uuidv4() // UUID v4 shortcut
396
+ v.hostname() // Hostname validation
397
+ v.emoji() // Emoji validation
398
+ v.base64() // Base64 format
399
+ v.base64url() // Base64 URL-safe format
400
+ v.hex() // Hex string
401
+ v.jwt() // JWT format
402
+ v.nanoid() // NanoID format
403
+ v.cuid() // CUID format
404
+ v.cuid2() // CUID2 format
405
+ v.ulid() // ULID format
406
+ v.ipv4() // IPv4 address
407
+ v.ipv6() // IPv6 address
408
+ v.mac() // MAC address
409
+ v.cidrv4() // IPv4 CIDR block
410
+ v.cidrv6() // IPv6 CIDR block
411
+ v.e164() // E.164 phone number
412
+ v.creditCard() // Credit card number (regex plus Luhn checksum)
413
+ v.hash('sha256') // Hash validation
414
+ v.iso.date() // ISO date format
415
+ v.iso.time() // ISO time format
416
+ v.iso.dateTime() // ISO datetime format
417
+ v.iso.duration() // ISO duration format
418
+ ```
419
+
420
+ ### Number Validators
421
+
422
+ ```typescript
423
+ v.number()
424
+ .min(0) // Minimum value
425
+ .max(100) // Maximum value
426
+ .int() // Integer only
427
+ .positive() // Positive numbers
428
+ .negative() // Negative numbers
429
+ .nonnegative() // >= 0
430
+ .nonpositive() // <= 0
431
+ .finite() // Finite numbers
432
+ .safe() // Safe integers
433
+ .multipleOf(5) // Multiple of value
434
+ ```
435
+
436
+ ### Arrays and Objects
437
+
438
+ ```typescript
439
+ // Arrays
440
+ v.array(v.string()) // Array of strings
441
+ .min(1) // Minimum length
442
+ .max(10) // Maximum length
443
+ .length(5) // Exact length
444
+ .nonempty() // Non-empty array
445
+
446
+ // Objects
447
+ v.object({
448
+ name: v.string(),
449
+ age: v.number()
450
+ })
451
+ .partial() // All fields optional
452
+ .strict() // No extra fields
453
+ .passthrough() // Allow extra fields
454
+ ```
455
+
456
+ ### Composite Types
457
+
458
+ ```typescript
459
+ // Optional
460
+ v.optional(v.string()) // string | undefined
461
+
462
+ // Nullable
463
+ v.nullable(v.string()) // string | null
464
+
465
+ // Nullish
466
+ v.nullish(v.string()) // string | null | undefined
467
+
468
+ // Union
469
+ v.union(v.string(), v.number()) // string | number
470
+
471
+ // Discriminated Union
472
+ v.discriminatedUnion('type',
473
+ v.object({ type: v.literal('a'), a: v.string() }),
474
+ v.object({ type: v.literal('b'), b: v.number() })
475
+ )
476
+
477
+ // Literal
478
+ v.literal('active') // 'active'
479
+
480
+ // Enum
481
+ v.enum('red', 'green', 'blue') // 'red' | 'green' | 'blue'
482
+ ```
483
+
484
+ ### Type Coercion
485
+
486
+ ```typescript
487
+ // Coerce strings from various types
488
+ v.coerce.string().parse(123) // "123"
489
+ v.coerce.string().parse(true) // "true"
490
+
491
+ // Coerce numbers from strings/booleans
492
+ v.coerce.number().parse("123") // 123
493
+ v.coerce.number().parse(true) // 1
494
+
495
+ // Coerce booleans from strings/numbers
496
+ v.coerce.boolean().parse("true") // true
497
+ v.coerce.boolean().parse(1) // true
498
+
499
+ // Coerce BigInt from strings/numbers
500
+ v.coerce.bigint().parse("123") // 123n
501
+ v.coerce.bigint().parse(456) // 456n
502
+
503
+ // Coerce Date from strings/timestamps
504
+ v.coerce.date().parse("2023-01-01") // Date object
505
+ v.coerce.date().parse(1672531200000) // Date object
506
+ ```
507
+
508
+ ### Object Schema Methods
509
+
510
+ ```typescript
511
+ const userSchema = v.object({
512
+ name: v.string(),
513
+ age: v.number(),
514
+ email: v.string(),
515
+ role: v.string()
516
+ });
517
+
518
+ // Pick specific fields
519
+ const publicSchema = userSchema.pick('name', 'age');
520
+ // Type: { name: string; age: number }
521
+
522
+ // Omit sensitive fields
523
+ const safeSchema = userSchema.omit('email', 'role');
524
+ // Type: { name: string; age: number }
525
+
526
+ // Extend with new fields
527
+ const extendedSchema = userSchema.extend({
528
+ isActive: v.boolean(),
529
+ lastLogin: v.date()
530
+ });
531
+ // Type: { name: string; age: number; email: string; role: string; isActive: boolean; lastLogin: Date }
532
+ ```
533
+
534
+ ### Advanced Validation Methods
535
+
536
+ ```typescript
537
+ // Custom validation with refine()
538
+ const positiveNumber = v.number()
539
+ .refine(n => n > 0, "Number must be positive");
540
+
541
+ // Data transformation with transform()
542
+ const uppercaseString = v.string()
543
+ .transform(s => s.toUpperCase());
544
+
545
+ // Default values for undefined
546
+ const withDefault = v.string().default("fallback");
547
+ withDefault.parse(undefined); // "fallback"
548
+
549
+ // Catch errors and provide fallback
550
+ const withCatch = v.number().catch(-1);
551
+ withCatch.parse("invalid"); // -1
552
+
553
+ // Method chaining
554
+ const complexSchema = v.string()
555
+ .min(3)
556
+ .transform(s => s.trim())
557
+ .refine(s => s.includes('@'), 'Must contain @')
558
+ .default('user@example.com');
559
+ ```
560
+
561
+ ### Special Validators
562
+
563
+ ```typescript
564
+ // JSON validator with optional schema
565
+ v.json() // Any valid JSON
566
+ v.json(v.object({ name: v.string() })) // Typed JSON
567
+
568
+ // Lazy for recursive schemas
569
+ const categorySchema = v.lazy(() =>
570
+ v.object({
571
+ name: v.string(),
572
+ children: v.array(categorySchema).optional()
573
+ })
574
+ );
575
+
576
+ // Custom validator
577
+ v.custom({
578
+ check: (val) => typeof val === 'string' && val.length > 0,
579
+ message: 'Must be a non-empty string'
580
+ });
581
+
582
+ // File validator
583
+ v.file()
584
+ .maxSize(5 * 1024 * 1024) // 5MB
585
+ .type(['image/png', 'image/jpeg']);
586
+
587
+ // Function validator
588
+ v.function()
589
+ .args(v.string(), v.number())
590
+ .returns(v.boolean());
591
+ ```
592
+
593
+ ### Type Inference
594
+
595
+ ```typescript
596
+ import { v } from '@oxog/vld';
597
+ import type { Infer } from '@oxog/vld';
598
+
599
+ const schema = v.object({
600
+ name: v.string(),
601
+ age: v.number()
602
+ });
603
+
604
+ // Automatically infer the type
605
+ type User = Infer<typeof schema>;
606
+ // { name: string; age: number }
607
+ ```
608
+
609
+ ### Error Formatting Types
610
+
611
+ ```typescript
612
+ import { VldError } from '@oxog/vld';
613
+ import type {
614
+ VldIssue, // Individual validation issue
615
+ VldErrorTree, // Nested error structure
616
+ VldFlattenedError // Flattened error structure
617
+ } from '@oxog/vld';
618
+ ```
619
+
620
+ ### Custom Error Messages
621
+
622
+ ```typescript
623
+ const schema = v.string().min(8, 'Password must be at least 8 characters');
624
+
625
+ const result = schema.safeParse('short');
626
+ if (!result.success) {
627
+ console.log(result.error.message); // 'Password must be at least 8 characters'
628
+ }
629
+ ```
630
+
631
+ ## Internationalization (i18n)
632
+
633
+ VLD supports 27+ languages out of the box with comprehensive error messages:
634
+
635
+ ```typescript
636
+ import { v, setLocale } from '@oxog/vld';
637
+
638
+ // Default is English
639
+ const schema = v.string().min(5);
640
+ schema.safeParse('Hi'); // Error: "String must be at least 5 characters"
641
+
642
+ // Switch to Turkish
643
+ setLocale('tr');
644
+ schema.safeParse('Hi'); // Error: "Metin en az 5 karakter olmali"
645
+
646
+ // Switch to Spanish
647
+ setLocale('es');
648
+ schema.safeParse('Hi'); // Error: "La cadena debe tener al menos 5 caracteres"
649
+
650
+ // Switch to Japanese
651
+ setLocale('ja');
652
+ schema.safeParse('Hi'); // Error: "..."
653
+ ```
654
+
655
+ ### Supported Languages
656
+
657
+ #### Base Languages (15):
658
+ - English (`en`) - Turkish (`tr`) - Spanish (`es`) - French (`fr`) - German (`de`)
659
+ - Italian (`it`) - Portuguese (`pt`) - Russian (`ru`) - Japanese (`ja`) - Korean (`ko`)
660
+ - Chinese (`zh`) - Arabic (`ar`) - Hindi (`hi`) - Dutch (`nl`) - Polish (`pl`)
661
+
662
+ #### European Languages (4):
663
+ - Danish (`da`) - Swedish (`sv`) - Norwegian (`no`) - Finnish (`fi`)
664
+
665
+ #### Asian Languages (4):
666
+ - Thai (`th`) - Vietnamese (`vi`) - Indonesian (`id`) - Bengali (`bn`)
667
+
668
+ #### African Languages (2):
669
+ - Swahili (`sw`) - Afrikaans (`af`)
670
+
671
+ #### American Languages (2):
672
+ - Portuguese Brazil (`pt-BR`) - Spanish Mexico (`es-MX`)
673
+
674
+ **Plus 75+ additional languages** supported through comprehensive type definitions with English fallback.
675
+
676
+ ## Error Handling & Formatting
677
+
678
+ VLD provides advanced error formatting utilities similar to Zod's error handling system.
679
+
680
+ ### Error Formatting Utilities
681
+
682
+ ```typescript
683
+ import { v, VldError, treeifyError, prettifyError, flattenError } from '@oxog/vld';
684
+
685
+ const userSchema = v.object({
686
+ username: v.string().min(3),
687
+ favoriteNumbers: v.array(v.number()),
688
+ profile: v.object({
689
+ name: v.string(),
690
+ email: v.string().email()
691
+ })
692
+ });
693
+
694
+ const result = userSchema.safeParse({
695
+ username: 'ab',
696
+ favoriteNumbers: [1, 'two', 3],
697
+ profile: {
698
+ name: '',
699
+ email: 'invalid-email'
700
+ },
701
+ extraField: 'not allowed'
702
+ });
703
+
704
+ if (!result.success) {
705
+ const error = result.error as VldError;
706
+
707
+ // 1. Tree Format - Nested structure for complex UIs
708
+ const tree = treeifyError(error);
709
+
710
+ // 2. Pretty Format - Human-readable console output
711
+ const pretty = prettifyError(error);
712
+
713
+ // 3. Flatten Format - Simple form validation
714
+ const flattened = flattenError(error);
715
+ }
716
+ ```
717
+
718
+ ### Using Error Formats in Practice
719
+
720
+ #### React Form Validation
721
+ ```typescript
722
+ function UserForm() {
723
+ const [errors, setErrors] = useState<VldFlattenedError | null>(null);
724
+
725
+ const handleSubmit = (data: unknown) => {
726
+ const result = userSchema.safeParse(data);
727
+
728
+ if (!result.success) {
729
+ setErrors(flattenError(result.error as VldError));
730
+ } else {
731
+ setErrors(null);
732
+ }
733
+ };
734
+
735
+ return (
736
+ <form onSubmit={handleSubmit}>
737
+ {errors?.formErrors.map(error => (
738
+ <div key={error} className="form-error">{error}</div>
739
+ ))}
740
+
741
+ <input name="username" />
742
+ {errors?.fieldErrors.username?.map(error => (
743
+ <div key={error} className="field-error">{error}</div>
744
+ ))}
745
+ </form>
746
+ );
747
+ }
748
+ ```
749
+
750
+ #### API Error Responses
751
+ ```typescript
752
+ app.post('/api/users', (req, res) => {
753
+ const result = userSchema.safeParse(req.body);
754
+
755
+ if (!result.success) {
756
+ const tree = treeifyError(result.error as VldError);
757
+ res.status(400).json({
758
+ error: 'Validation failed',
759
+ details: tree
760
+ });
761
+ }
762
+ });
763
+ ```
764
+
765
+ ## Advanced Examples
766
+
767
+ ### Complex Validation with New Features
768
+
769
+ ```typescript
770
+ const postSchema = v.object({
771
+ id: v.union(v.string().uuid(), v.number()),
772
+ title: v.string().min(5).max(100),
773
+ content: v.string().min(10),
774
+ author: v.object({
775
+ name: v.string(),
776
+ email: v.string().email(),
777
+ age: v.coerce.number(),
778
+ }),
779
+ tags: v.set(v.string()).default(new Set()),
780
+ metadata: v.record(v.any()),
781
+ coordinates: v.tuple(v.number(), v.number()),
782
+ publishedAt: v.date().default(() => new Date()),
783
+ status: v.enum('draft', 'published', 'archived')
784
+ });
785
+
786
+ // Extend with additional fields
787
+ const blogPostSchema = postSchema.extend({
788
+ viewCount: v.bigint().default(0n),
789
+ categories: v.array(v.string()).min(1),
790
+ featured: v.boolean().default(false)
791
+ });
792
+ ```
793
+
794
+ ### Discriminated Union
795
+
796
+ ```typescript
797
+ const eventSchema = v.discriminatedUnion('type',
798
+ v.object({
799
+ type: v.literal('click'),
800
+ x: v.number(),
801
+ y: v.number()
802
+ }),
803
+ v.object({
804
+ type: v.literal('scroll'),
805
+ direction: v.enum('up', 'down'),
806
+ distance: v.number()
807
+ }),
808
+ v.object({
809
+ type: v.literal('keypress'),
810
+ key: v.string(),
811
+ modifiers: v.array(v.enum('ctrl', 'alt', 'shift'))
812
+ })
813
+ );
814
+
815
+ // Type-safe parsing
816
+ const event = eventSchema.parse({
817
+ type: 'click',
818
+ x: 100,
819
+ y: 200
820
+ });
821
+ ```
822
+
823
+ ### Recursive Schemas
824
+
825
+ ```typescript
826
+ const categorySchema: ReturnType<typeof v.lazy> = v.lazy(() =>
827
+ v.object({
828
+ name: v.string(),
829
+ slug: v.string().regex(/^[a-z0-9-]+$/),
830
+ children: v.array(categorySchema).optional()
831
+ })
832
+ );
833
+
834
+ const category = categorySchema.parse({
835
+ name: 'Electronics',
836
+ slug: 'electronics',
837
+ children: [
838
+ {
839
+ name: 'Phones',
840
+ slug: 'phones',
841
+ children: [
842
+ { name: 'Smartphones', slug: 'smartphones' }
843
+ ]
844
+ }
845
+ ]
846
+ });
847
+ ```
848
+
849
+ ### Type-Safe Forms
850
+
851
+ ```typescript
852
+ const loginSchema = v.object({
853
+ username: v.string().min(3),
854
+ password: v.string().min(8),
855
+ rememberMe: v.optional(v.boolean())
856
+ });
857
+
858
+ type LoginForm = Infer<typeof loginSchema>;
859
+
860
+ function handleLogin(data: unknown) {
861
+ const result = loginSchema.safeParse(data);
862
+
863
+ if (result.success) {
864
+ const { username, password, rememberMe } = result.data;
865
+ // ... handle login
866
+ }
867
+ }
868
+ ```
869
+
870
+ ## Codecs - Bidirectional Transformations
871
+
872
+ VLD introduces **codecs** - powerful bidirectional transformations that can convert data between different representations.
873
+
874
+ ### What are Codecs?
875
+
876
+ Codecs enable safe, type-checked conversions between different data formats:
877
+
878
+ ```typescript
879
+ import { stringToNumber, jsonCodec, base64ToBytes } from '@oxog/vld';
880
+
881
+ // String to number conversion
882
+ const age = stringToNumber.parse('25'); // 25
883
+ const price = stringToNumber.encode(99.99); // "99.99"
884
+
885
+ // JSON codec
886
+ const userJson = jsonCodec();
887
+ const user = userJson.parse('{"name":"John","age":30}');
888
+ const jsonString = userJson.encode(user);
889
+
890
+ // Binary data
891
+ const bytes = base64ToBytes.parse('SGVsbG8gV29ybGQ=');
892
+ ```
893
+
894
+ ### Built-in Codecs
895
+
896
+ #### String Conversion Codecs
897
+ ```typescript
898
+ import { stringToNumber, stringToInt, stringToBigInt, stringToBoolean } from '@oxog/vld';
899
+
900
+ stringToNumber.parse('42.5'); // 42.5
901
+ stringToInt.parse('42'); // 42
902
+ stringToBigInt.parse('123n'); // 123n
903
+ stringToBoolean.parse('true'); // true
904
+ ```
905
+
906
+ #### Date Conversion Codecs
907
+ ```typescript
908
+ import { isoDatetimeToDate, epochSecondsToDate, epochMillisToDate } from '@oxog/vld';
909
+
910
+ isoDatetimeToDate.parse('2023-12-25T10:30:00.000Z'); // Date
911
+ epochSecondsToDate.parse(1703505000); // Date
912
+ epochMillisToDate.parse(1703505000000); // Date
913
+ ```
914
+
915
+ #### URL Codecs
916
+ ```typescript
917
+ import { stringToURL, stringToHttpURL, uriComponent } from '@oxog/vld';
918
+
919
+ stringToURL.parse('https://example.com/path?q=1');
920
+ stringToHttpURL.parse('https://api.example.com');
921
+ uriComponent.parse('Hello World!'); // "Hello%20World!"
922
+ ```
923
+
924
+ #### Binary Data Codecs
925
+ ```typescript
926
+ import { base64ToBytes, hexToBytes, utf8ToBytes, bytesToUtf8 } from '@oxog/vld';
927
+
928
+ base64ToBytes.parse('SGVsbG8='); // Uint8Array
929
+ hexToBytes.parse('48656c6c6f'); // Uint8Array
930
+ utf8ToBytes.parse('Hello'); // Uint8Array
931
+ bytesToUtf8.parse(new Uint8Array([72, 101, 108, 108, 111])); // "Hello"
932
+ ```
933
+
934
+ ### Custom Codecs
935
+
936
+ ```typescript
937
+ const csvToArray = v.codec(
938
+ v.string(),
939
+ v.array(v.string()),
940
+ {
941
+ decode: (csv: string) => csv.split(',').map(s => s.trim()),
942
+ encode: (arr: string[]) => arr.join(', ')
943
+ }
944
+ );
945
+
946
+ const tags = csvToArray.parse('react, typescript, vld');
947
+ // ["react", "typescript", "vld"]
948
+
949
+ const csvString = csvToArray.encode(['node', 'express', 'api']);
950
+ // "node, express, api"
951
+ ```
952
+
953
+ ## Plugin System
954
+
955
+ VLD v1.5.0 introduces a powerful plugin system for extending functionality.
956
+
957
+ ### Creating a Plugin
958
+
959
+ ```typescript
960
+ import { definePlugin, usePlugin, v } from '@oxog/vld';
961
+
962
+ // Define a custom plugin
963
+ const myPlugin = definePlugin({
964
+ name: 'my-plugin',
965
+ version: '1.0.0',
966
+
967
+ // Custom validators
968
+ validators: {
969
+ phoneNumber: () => v.string().regex(/^\+?[1-9]\d{1,14}$/),
970
+ postalCode: () => v.string().regex(/^\d{5}(-\d{4})?$/)
971
+ },
972
+
973
+ // Custom transforms
974
+ transforms: {
975
+ normalizePhone: (phone: string) => phone.replace(/[^\d+]/g, '')
976
+ },
977
+
978
+ // Lifecycle hooks
979
+ install(kernel) {
980
+ console.log('Plugin installed!');
981
+ }
982
+ });
983
+
984
+ // Register the plugin
985
+ usePlugin(myPlugin);
986
+ ```
987
+
988
+ ### Using Plugin Validators
989
+
990
+ ```typescript
991
+ import { createVldKernel, usePlugin } from '@oxog/vld';
992
+
993
+ const kernel = createVldKernel({ debug: true });
994
+
995
+ kernel.use(myPlugin);
996
+
997
+ // Access custom validators
998
+ const phoneSchema = kernel.validator('phoneNumber');
999
+ phoneSchema.parse('+1234567890');
1000
+ ```
1001
+
1002
+ ## Result Pattern
1003
+
1004
+ VLD v1.5.0 includes a functional Result pattern for error handling.
1005
+
1006
+ ### Basic Usage
1007
+
1008
+ ```typescript
1009
+ import { Ok, Err, match, map, flatMap, tryCatch } from '@oxog/vld';
1010
+
1011
+ // Create results
1012
+ const success = Ok(42);
1013
+ const failure = Err(new Error('Something went wrong'));
1014
+
1015
+ // Pattern matching
1016
+ const message = match(success, {
1017
+ ok: (value) => `Got: ${value}`,
1018
+ err: (error) => `Error: ${error.message}`
1019
+ });
1020
+
1021
+ // Transform results
1022
+ const doubled = map(success, (n) => n * 2); // Ok(84)
1023
+
1024
+ // Chain operations
1025
+ const result = flatMap(success, (n) =>
1026
+ n > 0 ? Ok(n * 2) : Err(new Error('Must be positive'))
1027
+ );
1028
+
1029
+ // Safe function execution
1030
+ const parsed = tryCatch(() => JSON.parse('{"a":1}'));
1031
+ ```
1032
+
1033
+ ### With Validation
1034
+
1035
+ ```typescript
1036
+ import { v, isOk, isErr, unwrapOr } from '@oxog/vld';
1037
+
1038
+ const schema = v.object({
1039
+ name: v.string(),
1040
+ age: v.number().min(0)
1041
+ });
1042
+
1043
+ const result = schema.safeParse(data);
1044
+
1045
+ if (isOk(result)) {
1046
+ console.log('Valid:', result.data);
1047
+ } else {
1048
+ console.log('Invalid:', result.error);
1049
+ }
1050
+
1051
+ // With default value
1052
+ const user = unwrapOr(result, { name: 'Guest', age: 0 });
1053
+ ```
1054
+
1055
+ ### Combining Results
1056
+
1057
+ ```typescript
1058
+ import { all, fromNullable } from '@oxog/vld';
1059
+
1060
+ // Combine multiple results
1061
+ const results = [Ok(1), Ok(2), Ok(3)];
1062
+ const combined = all(results); // Ok([1, 2, 3])
1063
+
1064
+ // Convert nullable to Result
1065
+ const maybeValue: string | null = getValue();
1066
+ const result = fromNullable(maybeValue, new Error('Value is null'));
1067
+ ```
1068
+
1069
+ ## CLI Tools
1070
+
1071
+ VLD includes command-line tools for validation and benchmarking.
1072
+
1073
+ ### Installation
1074
+
1075
+ ```bash
1076
+ npm install -g @oxog/vld
1077
+ # or use npx
1078
+ npx vld --help
1079
+ ```
1080
+
1081
+ ### Commands
1082
+
1083
+ ```bash
1084
+ # Show help
1085
+ vld --help
1086
+
1087
+ # Validate data
1088
+ vld validate schema.json data.json
1089
+
1090
+ # Run benchmarks
1091
+ vld benchmark
1092
+
1093
+ # Show version
1094
+ vld --version
1095
+ ```
1096
+
1097
+ ### Programmatic CLI
1098
+
1099
+ ```typescript
1100
+ import { createCli, vldCli } from '@oxog/vld/cli';
1101
+
1102
+ // Use the built-in CLI
1103
+ vldCli.run(process.argv.slice(2));
1104
+
1105
+ // Or create a custom CLI
1106
+ const cli = createCli('my-app', '1.0.0', 'My validation app')
1107
+ .command({
1108
+ name: 'validate',
1109
+ description: 'Validate data',
1110
+ action: async (args, options) => {
1111
+ // Custom validation logic
1112
+ }
1113
+ });
1114
+
1115
+ cli.run(process.argv.slice(2));
1116
+ ```
1117
+
1118
+ ## Logger & Colored Output
1119
+
1120
+ VLD includes a logging system and colored terminal output.
1121
+
1122
+ ### Logger
1123
+
1124
+ ```typescript
1125
+ import { createLogger, setLogLevel, enableDebug } from '@oxog/vld';
1126
+
1127
+ // Create a logger
1128
+ const logger = createLogger({ prefix: 'VLD' });
1129
+
1130
+ logger.info('Processing...');
1131
+ logger.warn('Deprecated feature');
1132
+ logger.error('Validation failed');
1133
+ logger.debug('Debug info');
1134
+
1135
+ // Set log level globally
1136
+ setLogLevel('debug');
1137
+
1138
+ // Enable debug mode
1139
+ enableDebug();
1140
+ ```
1141
+
1142
+ ### Colored Output (Pigment)
1143
+
1144
+ ```typescript
1145
+ import { pigment, red, green, blue, bold, dim } from '@oxog/vld';
1146
+
1147
+ console.log(red('Error!'));
1148
+ console.log(green('Success!'));
1149
+ console.log(bold(blue('Important')));
1150
+ console.log(dim('Less important'));
1151
+
1152
+ // Or use the pigment object
1153
+ console.log(pigment.red('Error!'));
1154
+ console.log(pigment.bold(pigment.green('Success!')));
1155
+ ```
1156
+
1157
+ ## VLD vs. Zod
1158
+
1159
+ VLD is designed as a compelling alternative to Zod, maintaining an audited compatibility baseline while delivering additional platform features.
1160
+
1161
+ ### Feature Comparison
1162
+
1163
+ | Feature | VLD | Zod |
1164
+ | ----------------------- | ---------------------------------- | ------------------------------------ |
1165
+ | **Performance** | **Release-gated faster runtime, startup, and memory paths** | Baseline |
1166
+ | **Memory Usage** | **~5x less** overall | Baseline |
1167
+ | **Internationalization**| **Built-in 27+ locales with lazy loading** | Built-in locales |
1168
+ | **Dependencies** | **Zero runtime dependencies** | Zero runtime dependencies |
1169
+ | **Bundle Size** | Smaller | Larger |
1170
+ | **API** | Zod 4.4.3 compatibility gate + extensions | Standard Zod API |
1171
+ | **Plugin System** | **Built-in** | Not available |
1172
+ | **Result Pattern** | **Built-in** | Not available |
1173
+ | **CLI Tools** | **Built-in** | Not available |
1174
+ | **Codecs** | Built-in system plus reusable presets | Built-in primitives; recipes documented |
1175
+ | **Type Inference** | Excellent | Excellent |
1176
+
1177
+ ### Seamless Migration from Zod
1178
+
1179
+ ```javascript
1180
+ // Before (Zod)
1181
+ import { z } from 'zod';
1182
+ const schema = z.string().email();
1183
+
1184
+ // After (VLD) - Exact same syntax!
1185
+ import { v } from '@oxog/vld';
1186
+ const schema = v.string().email();
1187
+ ```
1188
+
1189
+ ### Drop-in Package Subpaths
1190
+
1191
+ VLD also exposes Zod-compatible package subpaths so applications that import Zod 4 entry points can migrate by changing the package name:
1192
+
1193
+ ```typescript
1194
+ // Before
1195
+ import { z } from 'zod';
1196
+ import * as core from 'zod/v4/core';
1197
+ import * as mini from 'zod/v4-mini';
1198
+ import * as locales from 'zod/v4/locales';
1199
+
1200
+ // After
1201
+ import { z } from '@oxog/vld';
1202
+ import * as core from '@oxog/vld/v4/core';
1203
+ import * as mini from '@oxog/vld/v4-mini';
1204
+ import * as locales from '@oxog/vld/v4/locales';
1205
+ ```
1206
+
1207
+ Release checks enforce zero missing exports and zero `typeof` mismatches for `zod/v4`, `zod/v4-mini`, `zod/v4/mini`, `zod/v4/core`, and `zod/v4/locales` against the installed latest Zod.
1208
+
1209
+ ### Real App Drop-in Verification
1210
+
1211
+ `npm run verify:drop-in` creates two temporary TypeScript applications from the same fixture:
1212
+
1213
+ - One imports and runs against real `zod`.
1214
+ - One imports and runs against the locally built `@oxog/vld` package.
1215
+ - The fixture exercises root APIs, `v4`, `v4-mini`, `v4/core`, `v4/locales`, parsing, error formatting, JSON Schema output, mini helpers, and core factories.
1216
+ - The normalized runtime outputs must match exactly.
1217
+
1218
+ ## Benchmarks
1219
+
1220
+ ### Performance Results
1221
+
1222
+ Median of 11 samples per case against Zod 4.4.3, the latest stable release at the time of measurement. The release gate also runs focused runtime, startup, memory, package, install, security, Zod parity, and real drop-in app checks before publishing.
1223
+
1224
+ | Test Case | VLD Median Performance | Improvement |
1225
+ |-----------|----------------|-------------|
1226
+ | Simple String | 719.2M ops/sec | **3.40x faster** |
1227
+ | Email Validation | 22.1M ops/sec | **2.87x faster** |
1228
+ | Top-level Email Format | 22.8M ops/sec | **3.66x faster** |
1229
+ | StringBool Validation | 32.7M ops/sec | **3.44x faster** |
1230
+ | Number Validation | 226.2M ops/sec | **27.62x faster** |
1231
+ | Simple Object | 37.7M ops/sec | **1.40x faster** |
1232
+ | Complex Object | 2.3M ops/sec | **1.02x faster** |
1233
+ | Array Validation | 39.8M ops/sec | **6.74x faster** |
1234
+ | Union Types | 38.1M ops/sec | **4.10x faster** |
1235
+ | Optional Values | 236.9M ops/sec | **7.57x faster** |
1236
+ | Nullable Values | 224.7M ops/sec | **10.71x faster** |
1237
+ | Nullish Values | 228.4M ops/sec | **52.92x faster** |
1238
+ | Default Values | 207.8M ops/sec | **9.71x faster** |
1239
+ | Catch Values | 232.1M ops/sec | **46.58x faster** |
1240
+ | SafeParse | 162.0M ops/sec | **1.80x faster** |
1241
+ | Type Coercion | 204.5M ops/sec | **8.72x faster** |
1242
+ | Enum Validation | 180.3M ops/sec | **1.32x faster** |
1243
+ | Discriminated Union | 19.2M ops/sec | **1.79x faster** |
1244
+ | Tuple Validation | 50.0M ops/sec | **7.99x faster** |
1245
+ | Record Validation | 10.7M ops/sec | **5.63x faster** |
1246
+ | Set Validation | 18.9M ops/sec | **1.84x faster** |
1247
+ | Map Validation | 14.8M ops/sec | **2.37x faster** |
1248
+ | BigInt Validation | 164.8M ops/sec | **14.88x faster** |
1249
+ | Date Validation | 227.2M ops/sec | **2.57x faster** |
1250
+ | Symbol Validation | 230.0M ops/sec | **1.01x faster** |
1251
+ | Any Validation | 232.5M ops/sec | **1.01x faster** |
1252
+ | Unknown Validation | 234.9M ops/sec | **1.04x faster** |
1253
+ | Function Validation | 233.6M ops/sec | **3.52x faster** |
1254
+ | Template Literal Validation | 39.3M ops/sec | **1.15x faster** |
1255
+ | Promise Async Validation | 6.3M ops/sec | **1.38x faster** |
1256
+
1257
+ **VLD won 30/30 snapshot tests. Release checks additionally enforce runtime, startup, memory, Zod subpath parity, and real drop-in app guard thresholds against Zod.**
1258
+
1259
+ ### Run Benchmarks
1260
+
1261
+ ```bash
1262
+ # Quick performance comparison
1263
+ npm run benchmark
1264
+
1265
+ # Median-based stable comparison
1266
+ npm run benchmark:stable
1267
+
1268
+ # Fast CI-friendly performance regression guard
1269
+ npm run benchmark:guard
1270
+
1271
+ # Full release gate: lint, source and published types, tests, exports, package and install checks, security audit, Zod parity, real drop-in app verification, and performance guards
1272
+ npm run release:check
1273
+
1274
+ # Memory usage comparison
1275
+ npm run benchmark:memory
1276
+
1277
+ # Startup time comparison
1278
+ npm run benchmark:startup
1279
+
1280
+ # Run all benchmarks
1281
+ npm run benchmark:all
1282
+ ```
1283
+
1284
+ ## Contributing
1285
+
1286
+ Contributions are welcome! Please feel free to submit a Pull Request.
1287
+
1288
+ ## Links
1289
+
1290
+ - [Documentation](https://vld.oxog.dev)
1291
+ - [NPM Package](https://www.npmjs.com/package/@oxog/vld)
1292
+ - [GitHub Repository](https://github.com/ersinkoc/vld)
1293
+
1294
+ ---
1295
+
1296
+ Made with Love by [Ersin KOC](https://github.com/ersinkoc)