@oxog/vld 2.2.5 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/README.md +242 -1125
  3. package/dist/chunks/errors-core-CerMg2rq.js +1 -0
  4. package/dist/chunks/index-CKPStM3V.js +1 -0
  5. package/dist/cjs/chunks/errors-core-DrydF46s.cjs +1 -0
  6. package/dist/cjs/chunks/index-lejEpLfv.cjs +1 -0
  7. package/dist/cjs/cli/bin.cjs +1 -1
  8. package/dist/cjs/codecs/index.cjs +1 -1
  9. package/dist/cjs/coercion/index.cjs +1 -1
  10. package/dist/cjs/compile.cjs +1 -0
  11. package/dist/cjs/errors.cjs +1 -1
  12. package/dist/cjs/index.cjs +1 -1
  13. package/dist/cjs/locales/gu.cjs +1 -0
  14. package/dist/cjs/locales/index.cjs +1 -1
  15. package/dist/cjs/locales/kn.cjs +1 -0
  16. package/dist/cjs/locales/ne.cjs +1 -0
  17. package/dist/cjs/locales/pt-BR-v4.cjs +1 -0
  18. package/dist/cjs/locales/sk.cjs +1 -0
  19. package/dist/cjs/mini.cjs +1 -1
  20. package/dist/cjs/v3/index.cjs +1 -1
  21. package/dist/cjs/v4/core/index.cjs +1 -1
  22. package/dist/cjs/v4/index.cjs +1 -1
  23. package/dist/cjs/v4/locales/index.cjs +1 -1
  24. package/dist/cjs/v4/mini/index.cjs +1 -1
  25. package/dist/cjs/v4-mini/index.cjs +1 -1
  26. package/dist/cjs/validators/any.cjs +1 -1
  27. package/dist/cjs/validators/array.cjs +1 -1
  28. package/dist/cjs/validators/base.cjs +1 -1
  29. package/dist/cjs/validators/base64.cjs +1 -1
  30. package/dist/cjs/validators/bigint.cjs +1 -1
  31. package/dist/cjs/validators/boolean.cjs +1 -1
  32. package/dist/cjs/validators/codec.cjs +1 -1
  33. package/dist/cjs/validators/custom.cjs +1 -1
  34. package/dist/cjs/validators/date.cjs +1 -1
  35. package/dist/cjs/validators/discriminated-union.cjs +1 -1
  36. package/dist/cjs/validators/enum.cjs +1 -1
  37. package/dist/cjs/validators/file.cjs +1 -1
  38. package/dist/cjs/validators/function.cjs +1 -1
  39. package/dist/cjs/validators/hex.cjs +1 -1
  40. package/dist/cjs/validators/index.cjs +1 -1
  41. package/dist/cjs/validators/intersection.cjs +1 -1
  42. package/dist/cjs/validators/json.cjs +1 -1
  43. package/dist/cjs/validators/lazy.cjs +1 -1
  44. package/dist/cjs/validators/literal.cjs +1 -1
  45. package/dist/cjs/validators/map.cjs +1 -1
  46. package/dist/cjs/validators/nan.cjs +1 -1
  47. package/dist/cjs/validators/never.cjs +1 -1
  48. package/dist/cjs/validators/null.cjs +1 -1
  49. package/dist/cjs/validators/number.cjs +1 -1
  50. package/dist/cjs/validators/object.cjs +1 -1
  51. package/dist/cjs/validators/promise.cjs +1 -1
  52. package/dist/cjs/validators/record.cjs +1 -1
  53. package/dist/cjs/validators/set.cjs +1 -1
  54. package/dist/cjs/validators/string-bool.cjs +1 -1
  55. package/dist/cjs/validators/string-formats.cjs +1 -1
  56. package/dist/cjs/validators/string.cjs +1 -1
  57. package/dist/cjs/validators/symbol.cjs +1 -1
  58. package/dist/cjs/validators/template-literal.cjs +1 -1
  59. package/dist/cjs/validators/tuple.cjs +1 -1
  60. package/dist/cjs/validators/uint8array.cjs +1 -1
  61. package/dist/cjs/validators/undefined.cjs +1 -1
  62. package/dist/cjs/validators/union.cjs +1 -1
  63. package/dist/cjs/validators/unknown.cjs +1 -1
  64. package/dist/cjs/validators/void.cjs +1 -1
  65. package/dist/cjs/validators/xor.cjs +1 -1
  66. package/dist/coercion/index.js +1 -1
  67. package/dist/compile.d.ts +81 -0
  68. package/dist/compile.js +1 -0
  69. package/dist/errors-core.d.ts +12 -1
  70. package/dist/errors.js +1 -1
  71. package/dist/index.d.ts +384 -14
  72. package/dist/index.js +1 -1
  73. package/dist/locales/gu.d.ts +2 -0
  74. package/dist/locales/gu.js +1 -0
  75. package/dist/locales/index.js +1 -1
  76. package/dist/locales/kn.d.ts +2 -0
  77. package/dist/locales/kn.js +1 -0
  78. package/dist/locales/ne.d.ts +2 -0
  79. package/dist/locales/ne.js +1 -0
  80. package/dist/locales/pt-BR-v4.d.ts +2 -0
  81. package/dist/locales/pt-BR-v4.js +1 -0
  82. package/dist/locales/sk.d.ts +2 -0
  83. package/dist/locales/sk.js +1 -0
  84. package/dist/v3/index.js +1 -1
  85. package/dist/v4/core/index.d.ts +13 -0
  86. package/dist/v4/core/index.js +1 -1
  87. package/dist/v4/index.d.ts +1 -0
  88. package/dist/v4/index.js +1 -1
  89. package/dist/v4/locales/index.d.ts +6 -0
  90. package/dist/v4/locales/index.js +1 -1
  91. package/dist/v4/mini/index.js +1 -1
  92. package/dist/v4-mini/index.d.ts +2 -0
  93. package/dist/v4-mini/index.js +1 -1
  94. package/dist/validators/array.js +1 -1
  95. package/dist/validators/base.d.ts +147 -9
  96. package/dist/validators/base.js +1 -1
  97. package/dist/validators/base64.js +1 -1
  98. package/dist/validators/bigint.d.ts +7 -0
  99. package/dist/validators/bigint.js +1 -1
  100. package/dist/validators/boolean.js +1 -1
  101. package/dist/validators/codec.js +1 -1
  102. package/dist/validators/custom.d.ts +20 -10
  103. package/dist/validators/custom.js +1 -1
  104. package/dist/validators/date.d.ts +2 -0
  105. package/dist/validators/date.js +1 -1
  106. package/dist/validators/discriminated-union.d.ts +2 -0
  107. package/dist/validators/discriminated-union.js +1 -1
  108. package/dist/validators/enum.d.ts +8 -2
  109. package/dist/validators/enum.js +1 -1
  110. package/dist/validators/file.js +1 -1
  111. package/dist/validators/function.js +1 -1
  112. package/dist/validators/hex.js +1 -1
  113. package/dist/validators/intersection.js +1 -1
  114. package/dist/validators/json.js +1 -1
  115. package/dist/validators/literal.d.ts +1 -0
  116. package/dist/validators/literal.js +1 -1
  117. package/dist/validators/map.d.ts +7 -0
  118. package/dist/validators/map.js +1 -1
  119. package/dist/validators/nan.js +1 -1
  120. package/dist/validators/never.js +1 -1
  121. package/dist/validators/null.js +1 -1
  122. package/dist/validators/number.d.ts +5 -0
  123. package/dist/validators/number.js +1 -1
  124. package/dist/validators/object.d.ts +15 -0
  125. package/dist/validators/object.js +1 -1
  126. package/dist/validators/promise.d.ts +6 -7
  127. package/dist/validators/promise.js +1 -1
  128. package/dist/validators/record.d.ts +2 -0
  129. package/dist/validators/record.js +1 -1
  130. package/dist/validators/set.d.ts +5 -0
  131. package/dist/validators/set.js +1 -1
  132. package/dist/validators/string-bool.js +1 -1
  133. package/dist/validators/string-formats.d.ts +1 -0
  134. package/dist/validators/string-formats.js +1 -1
  135. package/dist/validators/string.d.ts +2 -0
  136. package/dist/validators/string.js +1 -1
  137. package/dist/validators/symbol.js +1 -1
  138. package/dist/validators/template-literal.js +1 -1
  139. package/dist/validators/tuple.js +1 -1
  140. package/dist/validators/uint8array.js +1 -1
  141. package/dist/validators/undefined.js +1 -1
  142. package/dist/validators/union.d.ts +1 -0
  143. package/dist/validators/union.js +1 -1
  144. package/dist/validators/void.js +1 -1
  145. package/dist/validators/xor.js +1 -1
  146. package/package.json +8 -2
  147. package/dist/chunks/errors-core-Cqi4-9ew.js +0 -1
  148. package/dist/chunks/index-yjpbvFgw.js +0 -1
  149. package/dist/cjs/chunks/errors-core-DKPohm9I.cjs +0 -1
  150. package/dist/cjs/chunks/index-BEV9nG0N.cjs +0 -1
package/README.md CHANGED
@@ -1,270 +1,23 @@
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
1
+ # VLD - Ultra-Fast TypeScript Validation Library
66
2
 
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
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) [![Website](https://img.shields.io/badge/Website-vld.oxog.dev-6366f1.svg)](https://vld.oxog.dev)
78
4
 
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
5
+ **Website & Live Playground**: [https://vld.oxog.dev](https://vld.oxog.dev)
83
6
 
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
- }
7
+ ---
250
8
 
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
- ```
9
+ ## Highlights
261
10
 
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
11
+ - **Release-Gated Speed**: 11x+ faster runtime throughput and 4.7x+ less memory consumption compared to Zod.
12
+ - **Zero Dependencies**: Pure TypeScript/JavaScript with zero third-party runtime bloat.
13
+ - **Drop-in Zod Compatibility**: Swap imports directly or use subpaths (`@oxog/vld/v4`, `@oxog/vld/mini`, `@oxog/vld/v4/core`, `@oxog/vld/v4/locales`).
14
+ - **Full Static Inference**: Automatic type extraction using `v.infer<typeof schema>`.
15
+ - **Tree-Shakeable Mini API**: Build hyper-optimized bundles with `@oxog/vld/mini`.
16
+ - **Built-in i18n**: Out-of-the-box error localization for 27+ languages with lazy-loading support (`@oxog/vld/locales/lazy`).
17
+ - **Result Pattern & Codecs**: Functional error handling (`tryCatch`, `match`, `Ok`, `Err`) and bidirectional data transformations.
18
+ - **100% Test Coverage**: Verified across 2500+ tests and drop-in TypeScript application suites.
266
19
 
267
- Run `npm run benchmark:truth` to see the real performance difference.
20
+ ---
268
21
 
269
22
  ## Installation
270
23
 
@@ -274,1022 +27,386 @@ npm install @oxog/vld
274
27
  yarn add @oxog/vld
275
28
  # or
276
29
  pnpm add @oxog/vld
30
+ # or
31
+ bun add @oxog/vld
277
32
  ```
278
33
 
34
+ ---
35
+
279
36
  ## Quick Start
280
37
 
281
38
  ```typescript
282
39
  import { v } from '@oxog/vld';
283
40
 
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
41
+ // Define schema with chainable validations
288
42
  const userSchema = v.object({
289
- name: v.string().min(2),
43
+ name: v.string().min(2).max(100),
290
44
  email: v.string().email(),
291
- age: v.number().min(18).max(100),
292
- isActive: v.boolean()
45
+ age: v.number().int().positive().optional(),
46
+ role: v.enum('admin', 'user', 'guest').default('user'),
47
+ tags: v.array(v.string()).min(1),
293
48
  });
294
49
 
295
- // Validate data
50
+ // Infer TypeScript type
51
+ type User = v.infer<typeof userSchema>;
52
+
53
+ // Safe parsing (no throwing)
296
54
  const result = userSchema.safeParse({
297
55
  name: 'John Doe',
298
56
  email: 'john@example.com',
299
- age: 25,
300
- isActive: true
57
+ age: 28,
58
+ tags: ['developer', 'typescript'],
301
59
  });
302
60
 
303
61
  if (result.success) {
304
- console.log('Valid user:', result.data);
62
+ console.log('Valid data:', result.data); // Typed as User
305
63
  } else {
306
- console.log('Validation error:', result.error);
64
+ console.error('Validation issues:', result.error.issues);
307
65
  }
308
66
  ```
309
67
 
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
68
+ ---
318
69
 
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
- ```
70
+ ## Core Schema API
339
71
 
340
- ### Advanced Types
72
+ ### Primitive Validators
341
73
 
342
74
  ```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
75
+ v.string() // String validation
76
+ v.number() // Number validation
77
+ v.int() // Integer validation
78
+ v.int32() // 32-bit integer validation
79
+ v.boolean() // Boolean validation
80
+ v.bigint() // BigInt validation
81
+ v.date() // Date validation
82
+ v.symbol() // Symbol validation
83
+ v.uint8array() // Uint8Array validation
84
+ v.literal('active') // Literal value
85
+ v.enum('admin', 'user', 'guest') // Enum values
86
+ v.any() // Any type
87
+ v.unknown() // Unknown type
88
+ v.null() // Null
89
+ v.undefined() // Undefined
90
+ v.nullish() // Null or undefined
91
+ v.void() // Void
92
+ v.never() // Never
366
93
  ```
367
94
 
368
- ### String Validators
95
+ ### String Formats
369
96
 
370
97
  ```typescript
371
98
  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
99
+ .min(3)
100
+ .max(100)
101
+ .email()
102
+ .url()
103
+ .uuid()
104
+ .regex(/^[a-z0-9]+$/)
105
+ .startsWith('https://')
106
+ .endsWith('.json')
107
+ .trim()
108
+ .toLowerCase();
109
+
110
+ // Top-level format helpers:
111
+ v.email()
112
+ v.uuid()
113
+ v.creditCard() // Luhn checksum validated
114
+ v.jwt()
115
+ v.cuid()
116
+ v.cuid2()
117
+ v.nanoid()
118
+ v.ulid()
119
+ v.ipv4()
120
+ v.ipv6()
121
+ v.iso.date()
122
+ v.iso.dateTime()
123
+ ```
124
+
125
+ ### Number Constraints
421
126
 
422
127
  ```typescript
423
128
  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
129
+ .min(0)
130
+ .max(100)
131
+ .int()
132
+ .positive()
133
+ .negative()
134
+ .nonnegative()
135
+ .multipleOf(5)
136
+ .finite()
137
+ .safe();
434
138
  ```
435
139
 
436
- ### Arrays and Objects
140
+ ### Objects & Collections
437
141
 
438
142
  ```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
143
  // 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()
144
+ const profileSchema = v.object({
145
+ username: v.string().min(3),
146
+ age: v.number().optional(),
516
147
  });
517
148
 
518
- // Pick specific fields
519
- const publicSchema = userSchema.pick('name', 'age');
520
- // Type: { name: string; age: number }
149
+ // Object transformations
150
+ profileSchema.partial(); // All fields optional
151
+ profileSchema.strict(); // Reject unknown fields
152
+ profileSchema.passthrough(); // Keep unknown fields
153
+ profileSchema.pick('username');
154
+ profileSchema.omit('age');
155
+ profileSchema.extend({ bio: v.string() });
521
156
 
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 }
157
+ // Arrays & Collections
158
+ v.array(v.string()).min(1).max(10);
159
+ v.tuple(v.string(), v.number());
160
+ v.record(v.string(), v.number());
161
+ v.set(v.string());
162
+ v.map(v.string(), v.number());
532
163
  ```
533
164
 
534
- ### Advanced Validation Methods
165
+ ### Unions & Compositions
535
166
 
536
167
  ```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
168
+ // Union
169
+ v.union(v.string(), v.number());
552
170
 
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
- ```
171
+ // Discriminated Union
172
+ const eventSchema = v.discriminatedUnion('type',
173
+ v.object({ type: v.literal('click'), x: v.number(), y: v.number() }),
174
+ v.object({ type: v.literal('scroll'), offset: v.number() })
175
+ );
560
176
 
561
- ### Special Validators
177
+ // Intersections & XOR
178
+ v.intersection(schemaA, schemaB);
179
+ v.xor(schemaA, schemaB);
562
180
 
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(() =>
181
+ // Recursive / Lazy Schemas
182
+ const treeSchema: ReturnType<typeof v.lazy> = v.lazy(() =>
570
183
  v.object({
571
- name: v.string(),
572
- children: v.array(categorySchema).optional()
184
+ id: v.string(),
185
+ children: v.array(treeSchema).optional(),
573
186
  })
574
187
  );
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
188
  ```
592
189
 
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
- });
190
+ ---
603
191
 
604
- // Automatically infer the type
605
- type User = Infer<typeof schema>;
606
- // { name: string; age: number }
607
- ```
192
+ ## Type Coercion & Modifiers
608
193
 
609
- ### Error Formatting Types
194
+ ### Automatic Coercion (`v.coerce`)
610
195
 
611
196
  ```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';
197
+ v.coerce.string().parse(123); // "123"
198
+ v.coerce.number().parse("42"); // 42
199
+ v.coerce.boolean().parse("true"); // true
200
+ v.coerce.bigint().parse("1000"); // 1000n
201
+ v.coerce.date().parse("2026-08-17"); // Date object
618
202
  ```
619
203
 
620
- ### Custom Error Messages
204
+ ### Refinements, Transforms & Defaults
621
205
 
622
206
  ```typescript
623
- const schema = v.string().min(8, 'Password must be at least 8 characters');
207
+ const customSchema = v.string()
208
+ .transform(val => val.trim())
209
+ .refine(val => val.length >= 3, 'Must be at least 3 characters')
210
+ .default('default_value')
211
+ .catch('fallback_on_error');
624
212
 
625
- const result = schema.safeParse('short');
626
- if (!result.success) {
627
- console.log(result.error.message); // 'Password must be at least 8 characters'
628
- }
213
+ // SuperRefine for multi-field cross validation
214
+ const passwordSchema = v.object({
215
+ password: v.string().min(8),
216
+ confirm: v.string(),
217
+ }).superRefine((data, ctx) => {
218
+ if (data.password !== data.confirm) {
219
+ ctx.addIssue({
220
+ code: 'custom',
221
+ path: ['confirm'],
222
+ message: 'Passwords do not match',
223
+ });
224
+ }
225
+ });
629
226
  ```
630
227
 
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';
228
+ ---
637
229
 
638
- // Default is English
639
- const schema = v.string().min(5);
640
- schema.safeParse('Hi'); // Error: "String must be at least 5 characters"
230
+ ## Tree-Shakeable Mini API
641
231
 
642
- // Switch to Turkish
643
- setLocale('tr');
644
- schema.safeParse('Hi'); // Error: "Metin en az 5 karakter olmali"
232
+ For bundle-constrained applications, `@oxog/vld/mini` provides pure standalone functions with zero extra overhead:
645
233
 
646
- // Switch to Spanish
647
- setLocale('es');
648
- schema.safeParse('Hi'); // Error: "La cadena debe tener al menos 5 caracteres"
234
+ ```typescript
235
+ import { string, number, object, optional, array } from '@oxog/vld/mini';
649
236
 
650
- // Switch to Japanese
651
- setLocale('ja');
652
- schema.safeParse('Hi'); // Error: "..."
237
+ const userSchema = object({
238
+ name: string().min(2),
239
+ age: optional(number().positive()),
240
+ roles: array(string()),
241
+ });
653
242
  ```
654
243
 
655
- ### Supported Languages
244
+ ---
656
245
 
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`)
246
+ ## Drop-in Zod Compatibility
661
247
 
662
- #### European Languages (4):
663
- - Danish (`da`) - Swedish (`sv`) - Norwegian (`no`) - Finnish (`fi`)
248
+ VLD provides drop-in subpaths that mirror Zod export structures and error shapes:
664
249
 
665
- #### Asian Languages (4):
666
- - Thai (`th`) - Vietnamese (`vi`) - Indonesian (`id`) - Bengali (`bn`)
250
+ ```typescript
251
+ // Replace Zod imports seamlessly
252
+ import { z } from '@oxog/vld';
253
+ import * as core from '@oxog/vld/v4/core';
254
+ import * as mini from '@oxog/vld/v4-mini';
255
+ import * as locales from '@oxog/vld/v4/locales';
667
256
 
668
- #### African Languages (2):
669
- - Swahili (`sw`) - Afrikaans (`af`)
257
+ import { v, deepPartial, input, output } from '@oxog/vld';
258
+ ```
670
259
 
671
- #### American Languages (2):
672
- - Portuguese Brazil (`pt-BR`) - Spanish Mexico (`es-MX`)
260
+ - Structured error issues with `expected`, `received`, `minimum`, `maximum`, and `path`.
261
+ - Compatibility tested against latest stable Zod releases.
673
262
 
674
- **Plus 75+ additional languages** supported through comprehensive type definitions with English fallback.
263
+ ---
675
264
 
676
265
  ## Error Handling & Formatting
677
266
 
678
- VLD provides advanced error formatting utilities similar to Zod's error handling system.
679
-
680
- ### Error Formatting Utilities
681
-
682
267
  ```typescript
683
268
  import { v, VldError, treeifyError, prettifyError, flattenError } from '@oxog/vld';
684
269
 
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
- });
270
+ const result = userSchema.safeParse(invalidData);
703
271
 
704
272
  if (!result.success) {
705
273
  const error = result.error as VldError;
706
274
 
707
- // 1. Tree Format - Nested structure for complex UIs
708
- const tree = treeifyError(error);
275
+ // Flattened field errors for forms
276
+ const { fieldErrors, formErrors } = flattenError(error);
709
277
 
710
- // 2. Pretty Format - Human-readable console output
278
+ // Human-readable CLI / console output
711
279
  const pretty = prettifyError(error);
712
280
 
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
- );
281
+ // Nested tree structure for UI inspection
282
+ const tree = treeifyError(error);
747
283
  }
748
284
  ```
749
285
 
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
- );
286
+ ---
814
287
 
815
- // Type-safe parsing
816
- const event = eventSchema.parse({
817
- type: 'click',
818
- x: 100,
819
- y: 200
820
- });
821
- ```
288
+ ## Internationalization (i18n)
822
289
 
823
- ### Recursive Schemas
290
+ VLD includes built-in translations for 27+ languages:
824
291
 
825
292
  ```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
- );
293
+ import { v, setLocale } from '@oxog/vld';
833
294
 
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
- });
295
+ setLocale('tr'); // Turkish error messages
296
+ setLocale('es'); // Spanish
297
+ setLocale('de'); // German
298
+ setLocale('ja'); // Japanese
299
+ setLocale('fr'); // French
847
300
  ```
848
301
 
849
- ### Type-Safe Forms
302
+ ### Lazy Loading for Minimal Bundles
850
303
 
851
304
  ```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>;
305
+ import { setLocaleAsync, preloadLocales } from '@oxog/vld/locales/lazy';
859
306
 
860
- function handleLogin(data: unknown) {
861
- const result = loginSchema.safeParse(data);
307
+ // Loads locale on demand via dynamic import()
308
+ await setLocaleAsync('tr');
862
309
 
863
- if (result.success) {
864
- const { username, password, rememberMe } = result.data;
865
- // ... handle login
866
- }
867
- }
310
+ // Preload for SSR / warm start
311
+ await preloadLocales(['en', 'de', 'ja']);
868
312
  ```
869
313
 
870
- ## Codecs - Bidirectional Transformations
871
-
872
- VLD introduces **codecs** - powerful bidirectional transformations that can convert data between different representations.
873
-
874
- ### What are Codecs?
314
+ ---
875
315
 
876
- Codecs enable safe, type-checked conversions between different data formats:
316
+ ## Bidirectional Codecs
877
317
 
878
318
  ```typescript
879
- import { stringToNumber, jsonCodec, base64ToBytes } from '@oxog/vld';
319
+ import { stringToNumber, jsonCodec, base64ToBytes, hexToBytes } from '@oxog/vld';
880
320
 
881
- // String to number conversion
882
- const age = stringToNumber.parse('25'); // 25
883
- const price = stringToNumber.encode(99.99); // "99.99"
321
+ // String to Number decode & encode
322
+ const num = stringToNumber.parse('42'); // 42
323
+ const str = stringToNumber.encode(42); // "42"
884
324
 
885
325
  // JSON codec
886
- const userJson = jsonCodec();
887
- const user = userJson.parse('{"name":"John","age":30}');
888
- const jsonString = userJson.encode(user);
326
+ const json = jsonCodec();
327
+ const parsed = json.parse('{"id":1}');
328
+ const encoded = json.encode(parsed);
889
329
 
890
- // Binary data
891
- const bytes = base64ToBytes.parse('SGVsbG8gV29ybGQ=');
330
+ // Binary conversions
331
+ const bytes = base64ToBytes.parse('SGVsbG8=');
892
332
  ```
893
333
 
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
- ```
334
+ ---
905
335
 
906
- #### Date Conversion Codecs
907
- ```typescript
908
- import { isoDatetimeToDate, epochSecondsToDate, epochMillisToDate } from '@oxog/vld';
336
+ ## Result Pattern
909
337
 
910
- isoDatetimeToDate.parse('2023-12-25T10:30:00.000Z'); // Date
911
- epochSecondsToDate.parse(1703505000); // Date
912
- epochMillisToDate.parse(1703505000000); // Date
913
- ```
338
+ Functional error handling without exceptions:
914
339
 
915
- #### URL Codecs
916
340
  ```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
- ```
341
+ import { Ok, Err, match, map, flatMap, tryCatch, isOk, isErr, unwrapOr } from '@oxog/vld';
923
342
 
924
- #### Binary Data Codecs
925
- ```typescript
926
- import { base64ToBytes, hexToBytes, utf8ToBytes, bytesToUtf8 } from '@oxog/vld';
343
+ const result = tryCatch(() => JSON.parse(rawInput));
927
344
 
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"
345
+ const output = match(result, {
346
+ ok: data => `Success: ${data.id}`,
347
+ err: err => `Failed: ${err.message}`,
348
+ });
932
349
  ```
933
350
 
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
- ```
351
+ ---
952
352
 
953
353
  ## Plugin System
954
354
 
955
- VLD v1.5.0 introduces a powerful plugin system for extending functionality.
956
-
957
- ### Creating a Plugin
958
-
959
355
  ```typescript
960
- import { definePlugin, usePlugin, v } from '@oxog/vld';
356
+ import { definePlugin, usePlugin, createVldKernel, v } from '@oxog/vld';
961
357
 
962
- // Define a custom plugin
963
- const myPlugin = definePlugin({
964
- name: 'my-plugin',
358
+ const phonePlugin = definePlugin({
359
+ name: 'phone-validator',
965
360
  version: '1.0.0',
966
-
967
- // Custom validators
968
361
  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, '')
362
+ phone: () => v.string().regex(/^\+?[1-9]\d{1,14}$/),
976
363
  },
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
364
  });
1020
365
 
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}'));
366
+ usePlugin(phonePlugin);
1031
367
  ```
1032
368
 
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
- ```
369
+ ---
1188
370
 
1189
- ### Drop-in Package Subpaths
371
+ ## Performance
1190
372
 
1191
- VLD also exposes Zod-compatible package subpaths so applications that import Zod 4 entry points can migrate by changing the package name:
373
+ VLD is optimized for modern V8 runtimes. CI gates enforce performance floors on every commit against Zod:
1192
374
 
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';
375
+ | Benchmark Case | VLD Throughput | Relative Speedup |
376
+ |----------------|----------------|------------------|
377
+ | Nullish Parse | ~214M ops/sec | **30.7x faster** |
378
+ | Number / Positive Int | ~253M ops/sec | **9.1x faster** |
379
+ | Discriminated Union | ~35M ops/sec | **4.1x faster** |
380
+ | Optional Parse | ~213M ops/sec | **3.8x faster** |
381
+ | Union Parse | ~39M ops/sec | **3.3x faster** |
382
+ | Simple String | ~620M ops/sec | **3.0x faster** |
383
+ | Array / Object Parse | ~49M ops/sec | **1.7x faster** |
1199
384
 
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
- ```
385
+ Explore full benchmark results and interactive visual comparisons at [vld.oxog.dev/benchmark](https://vld.oxog.dev/benchmark).
1206
386
 
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
387
+ ### Running Benchmarks Locally
1260
388
 
1261
389
  ```bash
1262
- # Quick performance comparison
1263
390
  npm run benchmark
1264
-
1265
- # Median-based stable comparison
1266
- npm run benchmark:stable
1267
-
1268
- # Fast CI-friendly performance regression guard
1269
391
  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
392
  npm run benchmark:memory
1276
-
1277
- # Startup time comparison
1278
393
  npm run benchmark:startup
1279
-
1280
- # Run all benchmarks
1281
- npm run benchmark:all
394
+ npm run release:check
1282
395
  ```
1283
396
 
397
+ ---
398
+
1284
399
  ## Contributing
1285
400
 
1286
- Contributions are welcome! Please feel free to submit a Pull Request.
401
+ Contributions are warmly welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) and [SECURITY.md](SECURITY.md) for details.
402
+
403
+ ---
1287
404
 
1288
405
  ## Links
1289
406
 
1290
- - [Documentation](https://vld.oxog.dev)
1291
- - [NPM Package](https://www.npmjs.com/package/@oxog/vld)
1292
- - [GitHub Repository](https://github.com/ersinkoc/vld)
407
+ - **Documentation & Playground**: [https://vld.oxog.dev](https://vld.oxog.dev)
408
+ - **NPM Package**: [https://www.npmjs.com/package/@oxog/vld](https://www.npmjs.com/package/@oxog/vld)
409
+ - **GitHub Repository**: [https://github.com/ersinkoc/vld](https://github.com/ersinkoc/vld)
1293
410
 
1294
411
  ---
1295
412