@oxog/vld 1.1.0 → 1.2.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.
- package/CHANGELOG.md +107 -2
- package/README.md +274 -3
- package/dist/codecs/index.d.ts +89 -0
- package/dist/codecs/index.d.ts.map +1 -0
- package/dist/codecs/index.js +256 -0
- package/dist/codecs/index.js.map +1 -0
- package/dist/coercion/string.js +1 -1
- package/dist/coercion/string.js.map +1 -1
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +27 -1
- package/dist/index.js.map +1 -1
- package/dist/locales/af.d.ts.map +1 -1
- package/dist/locales/af.js +18 -4
- package/dist/locales/af.js.map +1 -1
- package/dist/locales/ar.d.ts.map +1 -1
- package/dist/locales/ar.js +15 -1
- package/dist/locales/ar.js.map +1 -1
- package/dist/locales/bn.d.ts.map +1 -1
- package/dist/locales/bn.js +15 -1
- package/dist/locales/bn.js.map +1 -1
- package/dist/locales/da.d.ts.map +1 -1
- package/dist/locales/da.js +15 -1
- package/dist/locales/da.js.map +1 -1
- package/dist/locales/de.d.ts.map +1 -1
- package/dist/locales/de.js +15 -1
- package/dist/locales/de.js.map +1 -1
- package/dist/locales/en.d.ts.map +1 -1
- package/dist/locales/en.js +15 -1
- package/dist/locales/en.js.map +1 -1
- package/dist/locales/es-MX.d.ts.map +1 -1
- package/dist/locales/es-MX.js +15 -1
- package/dist/locales/es-MX.js.map +1 -1
- package/dist/locales/es.d.ts.map +1 -1
- package/dist/locales/es.js +15 -1
- package/dist/locales/es.js.map +1 -1
- package/dist/locales/fi.d.ts.map +1 -1
- package/dist/locales/fi.js +15 -1
- package/dist/locales/fi.js.map +1 -1
- package/dist/locales/fr.d.ts.map +1 -1
- package/dist/locales/fr.js +15 -1
- package/dist/locales/fr.js.map +1 -1
- package/dist/locales/hi.d.ts.map +1 -1
- package/dist/locales/hi.js +15 -1
- package/dist/locales/hi.js.map +1 -1
- package/dist/locales/id.d.ts.map +1 -1
- package/dist/locales/id.js +15 -1
- package/dist/locales/id.js.map +1 -1
- package/dist/locales/it.d.ts.map +1 -1
- package/dist/locales/it.js +15 -1
- package/dist/locales/it.js.map +1 -1
- package/dist/locales/ja.d.ts.map +1 -1
- package/dist/locales/ja.js +15 -1
- package/dist/locales/ja.js.map +1 -1
- package/dist/locales/ko.d.ts.map +1 -1
- package/dist/locales/ko.js +15 -1
- package/dist/locales/ko.js.map +1 -1
- package/dist/locales/nl.d.ts.map +1 -1
- package/dist/locales/nl.js +15 -1
- package/dist/locales/nl.js.map +1 -1
- package/dist/locales/no.d.ts.map +1 -1
- package/dist/locales/no.js +15 -1
- package/dist/locales/no.js.map +1 -1
- package/dist/locales/pl.d.ts.map +1 -1
- package/dist/locales/pl.js +15 -1
- package/dist/locales/pl.js.map +1 -1
- package/dist/locales/pt-BR.d.ts.map +1 -1
- package/dist/locales/pt-BR.js +15 -1
- package/dist/locales/pt-BR.js.map +1 -1
- package/dist/locales/pt.d.ts.map +1 -1
- package/dist/locales/pt.js +15 -1
- package/dist/locales/pt.js.map +1 -1
- package/dist/locales/ru.d.ts.map +1 -1
- package/dist/locales/ru.js +15 -1
- package/dist/locales/ru.js.map +1 -1
- package/dist/locales/sv.d.ts.map +1 -1
- package/dist/locales/sv.js +15 -1
- package/dist/locales/sv.js.map +1 -1
- package/dist/locales/sw.d.ts.map +1 -1
- package/dist/locales/sw.js +15 -1
- package/dist/locales/sw.js.map +1 -1
- package/dist/locales/th.d.ts.map +1 -1
- package/dist/locales/th.js +15 -1
- package/dist/locales/th.js.map +1 -1
- package/dist/locales/tr.d.ts.map +1 -1
- package/dist/locales/tr.js +15 -1
- package/dist/locales/tr.js.map +1 -1
- package/dist/locales/types.d.ts +10 -0
- package/dist/locales/types.d.ts.map +1 -1
- package/dist/locales/types.js.map +1 -1
- package/dist/locales/vi.d.ts.map +1 -1
- package/dist/locales/vi.js +15 -1
- package/dist/locales/vi.js.map +1 -1
- package/dist/locales/zh.d.ts.map +1 -1
- package/dist/locales/zh.js +15 -1
- package/dist/locales/zh.js.map +1 -1
- package/dist/utils/codec-utils.d.ts +44 -0
- package/dist/utils/codec-utils.d.ts.map +1 -0
- package/dist/utils/codec-utils.js +118 -0
- package/dist/utils/codec-utils.js.map +1 -0
- package/dist/validators/base64.d.ts +18 -0
- package/dist/validators/base64.d.ts.map +1 -0
- package/dist/validators/base64.js +62 -0
- package/dist/validators/base64.js.map +1 -0
- package/dist/validators/codec.d.ts +55 -0
- package/dist/validators/codec.d.ts.map +1 -0
- package/dist/validators/codec.js +182 -0
- package/dist/validators/codec.js.map +1 -0
- package/dist/validators/hex.d.ts +17 -0
- package/dist/validators/hex.d.ts.map +1 -0
- package/dist/validators/hex.js +61 -0
- package/dist/validators/hex.js.map +1 -0
- package/dist/validators/object.d.ts.map +1 -1
- package/dist/validators/object.js +8 -4
- package/dist/validators/object.js.map +1 -1
- package/dist/validators/uint8array.d.ts +25 -0
- package/dist/validators/uint8array.d.ts.map +1 -0
- package/dist/validators/uint8array.js +87 -0
- package/dist/validators/uint8array.js.map +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,7 +5,112 @@ All notable changes to VLD will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
-
## [1.
|
|
8
|
+
## [1.2.0] - 2025-08-24
|
|
9
|
+
|
|
10
|
+
### 🎯 **100% Test Success Rate Achieved**
|
|
11
|
+
- **569 tests passing** with 0 failures across all test suites
|
|
12
|
+
- **97.3% statement coverage** (up from 97.18%)
|
|
13
|
+
- **93.5% branch coverage**
|
|
14
|
+
- **96.78% function coverage**
|
|
15
|
+
- **97.6% line coverage**
|
|
16
|
+
- All Zod-compatible codec tests now fully passing
|
|
17
|
+
|
|
18
|
+
### 🚀 Major Features Added
|
|
19
|
+
|
|
20
|
+
#### **Codec System - Bidirectional Transformations**
|
|
21
|
+
- **NEW**: Complete codec system for bidirectional data transformations
|
|
22
|
+
- `v.codec()` factory method for creating custom codecs
|
|
23
|
+
- Full encode/decode support with type safety
|
|
24
|
+
- Async codec support with `parseAsync()` and `encodeAsync()` methods
|
|
25
|
+
- Comprehensive error handling for both directions
|
|
26
|
+
|
|
27
|
+
#### **19 Built-in Zod-Compatible Codecs**
|
|
28
|
+
|
|
29
|
+
**String Conversion Codecs:**
|
|
30
|
+
- `stringToNumber` - String ↔ Number with validation
|
|
31
|
+
- `stringToInt` - String ↔ Integer with validation
|
|
32
|
+
- `stringToBigInt` - String ↔ BigInt conversion
|
|
33
|
+
- `numberToBigInt` - Number ↔ BigInt conversion
|
|
34
|
+
- `stringToBoolean` - Flexible string ↔ boolean (`'true'`, `'1'`, `'yes'`, `'on'` → `true`)
|
|
35
|
+
|
|
36
|
+
**Date Conversion Codecs:**
|
|
37
|
+
- `isoDatetimeToDate` - ISO 8601 string ↔ Date object
|
|
38
|
+
- `epochSecondsToDate` - Unix seconds ↔ Date object
|
|
39
|
+
- `epochMillisToDate` - Unix milliseconds ↔ Date object
|
|
40
|
+
|
|
41
|
+
**JSON and Complex Data:**
|
|
42
|
+
- `jsonCodec()` - Generic JSON string ↔ any type
|
|
43
|
+
- `base64Json()` - Base64-encoded JSON with schema validation
|
|
44
|
+
- `jwtPayload()` - JWT payload decoder (read-only)
|
|
45
|
+
|
|
46
|
+
**URL and Web:**
|
|
47
|
+
- `stringToURL` - String ↔ URL object
|
|
48
|
+
- `stringToHttpURL` - HTTP/HTTPS URL validation and conversion
|
|
49
|
+
- `uriComponent` - URI component encode/decode
|
|
50
|
+
|
|
51
|
+
**Binary Data:**
|
|
52
|
+
- `base64ToBytes` - Base64 ↔ Uint8Array
|
|
53
|
+
- `base64urlToBytes` - URL-safe Base64 ↔ Uint8Array
|
|
54
|
+
- `hexToBytes` - Hexadecimal ↔ Uint8Array
|
|
55
|
+
- `utf8ToBytes` - UTF-8 string ↔ Uint8Array
|
|
56
|
+
- `bytesToUtf8` - Uint8Array ↔ UTF-8 string
|
|
57
|
+
|
|
58
|
+
#### **New Validator Types**
|
|
59
|
+
- `v.base64()` - Base64 string validation with URL-safe mode
|
|
60
|
+
- `v.hex()` - Hexadecimal string validation with lowercase mode
|
|
61
|
+
- `v.uint8Array()` - Uint8Array validation with length constraints
|
|
62
|
+
|
|
63
|
+
#### **Enhanced Utilities**
|
|
64
|
+
- Comprehensive codec utility functions in `codec-utils.ts`
|
|
65
|
+
- Cross-platform Base64 encoding/decoding (Node.js + Browser)
|
|
66
|
+
- Secure error handling for all codec operations
|
|
67
|
+
|
|
68
|
+
### 📚 Documentation Updates
|
|
69
|
+
- **README.md**: Comprehensive codec documentation with examples
|
|
70
|
+
- **API.md**: Complete codec API reference with TypeScript examples
|
|
71
|
+
- **New Examples**:
|
|
72
|
+
- `examples/codecs.js` - JavaScript codec examples
|
|
73
|
+
- `examples/codecs.ts` - TypeScript codec examples with full type safety
|
|
74
|
+
- Updated CLAUDE.md with codec development guidance
|
|
75
|
+
|
|
76
|
+
### 🔧 Technical Improvements
|
|
77
|
+
- **Zero Circular Dependencies**: Refactored codec architecture
|
|
78
|
+
- **Full Type Safety**: Complete TypeScript support with inference
|
|
79
|
+
- **97.3% Test Coverage**: Comprehensive test suite with 569 passing tests
|
|
80
|
+
- **Error Message Localization**: All codec errors support 27+ languages
|
|
81
|
+
|
|
82
|
+
### 🎯 Zod Compatibility
|
|
83
|
+
- **100% Zod Codec Parity**: All Zod codecs implemented and compatible
|
|
84
|
+
- **Beyond Zod**: Additional codecs not available in Zod
|
|
85
|
+
- **Drop-in Replacement**: Seamless migration path from Zod codecs
|
|
86
|
+
|
|
87
|
+
### Performance
|
|
88
|
+
- **Optimized Transformations**: Efficient bidirectional conversions
|
|
89
|
+
- **Memory Efficient**: Immutable codec architecture prevents leaks
|
|
90
|
+
- **Async Support**: Non-blocking operations for I/O-bound transformations
|
|
91
|
+
|
|
92
|
+
## [1.1.1] - 2025-08-18
|
|
93
|
+
|
|
94
|
+
### Security
|
|
95
|
+
- **CRITICAL**: Fixed prototype pollution vulnerability in VldObject passthrough mode
|
|
96
|
+
- Added protection against `__proto__`, `constructor`, and `prototype` key pollution
|
|
97
|
+
- Comprehensive security test suite added
|
|
98
|
+
|
|
99
|
+
### Fixed
|
|
100
|
+
- Removed unnecessary escape characters in regex patterns (URL validation)
|
|
101
|
+
- Fixed escape characters in locale files (Afrikaans)
|
|
102
|
+
- Added ESLint configuration for code quality
|
|
103
|
+
|
|
104
|
+
### Added
|
|
105
|
+
- Security test suite with prototype pollution prevention tests
|
|
106
|
+
- Coverage improvement tests for better code quality
|
|
107
|
+
- ESLint configuration with TypeScript support
|
|
108
|
+
|
|
109
|
+
### Changed
|
|
110
|
+
- Improved test coverage to 97.1% statements
|
|
111
|
+
- All linting issues resolved
|
|
112
|
+
|
|
113
|
+
## [1.1.0] - 2025-08-12
|
|
9
114
|
|
|
10
115
|
### Added
|
|
11
116
|
- Professional benchmark suite with real-world performance testing
|
|
@@ -46,7 +151,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
46
151
|
- Schema creation: 8.22x faster
|
|
47
152
|
- Overall performance: 2.07x faster average
|
|
48
153
|
|
|
49
|
-
## [1.0.0] -
|
|
154
|
+
## [1.0.0] - 2025-08-11
|
|
50
155
|
|
|
51
156
|
### Initial Release
|
|
52
157
|
|
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
[](https://www.typescriptlang.org/)
|
|
6
6
|
[](package.json)
|
|
7
|
-
[](package.json)
|
|
8
8
|
|
|
9
9
|
VLD is a blazing-fast, type-safe validation library for TypeScript and JavaScript with **full Zod feature parity**. Built with performance in mind, it provides a simple and intuitive API while maintaining excellent type inference and 27+ language internationalization support.
|
|
10
10
|
|
|
@@ -18,7 +18,7 @@ VLD is a blazing-fast, type-safe validation library for TypeScript and JavaScrip
|
|
|
18
18
|
- **🔧 Composable**: Chain validations for complex schemas
|
|
19
19
|
- **⚠️ Advanced Error Formatting**: Tree, pretty, and flatten error utilities
|
|
20
20
|
- **🌍 Multi-language**: Built-in support for 27+ languages
|
|
21
|
-
- **✅
|
|
21
|
+
- **✅ 97.3% Test Coverage**: Rigorously tested with 569 passing tests
|
|
22
22
|
- **🏆 Industry Leading Performance**: 2.07x faster than Zod on average
|
|
23
23
|
|
|
24
24
|
### Advanced Zod-Compatible Features
|
|
@@ -31,6 +31,14 @@ VLD is a blazing-fast, type-safe validation library for TypeScript and JavaScrip
|
|
|
31
31
|
- **🛡️ Fallback Handling**: `catch()` for graceful error recovery
|
|
32
32
|
- **🎯 Object Utilities**: `pick()`, `omit()`, `extend()` for flexible object schemas
|
|
33
33
|
|
|
34
|
+
### 🚀 **NEW** Codec System - Beyond Zod
|
|
35
|
+
- **↔️ Bidirectional Transformations**: Full encode/decode support for data conversion
|
|
36
|
+
- **📦 19 Built-in Codecs**: String conversions, date parsing, JSON, URL, binary data
|
|
37
|
+
- **🔗 Zod-Compatible**: All `stringToNumber`, `jsonCodec`, `base64ToBytes`, etc.
|
|
38
|
+
- **⚡ Async Support**: Both sync and async codec operations
|
|
39
|
+
- **🛠 Custom Codecs**: Create your own bidirectional transformations
|
|
40
|
+
- **🎯 Type-Safe**: Full TypeScript support with perfect type inference
|
|
41
|
+
|
|
34
42
|
## 📊 Performance
|
|
35
43
|
|
|
36
44
|
VLD is designed for speed and efficiency with recent optimizations delivering exceptional performance:
|
|
@@ -768,6 +776,269 @@ schema.extend({ email: v.string().email() })
|
|
|
768
776
|
.catch({ name: 'Unknown', email: 'no-email@example.com' });
|
|
769
777
|
```
|
|
770
778
|
|
|
779
|
+
## 🔄 Codecs - Bidirectional Transformations
|
|
780
|
+
|
|
781
|
+
VLD introduces **codecs** - powerful bidirectional transformations that can convert data between different representations. Unlike simple transformations, codecs can both **decode** (input → output) and **encode** (output → input).
|
|
782
|
+
|
|
783
|
+
### 🎯 What are Codecs?
|
|
784
|
+
|
|
785
|
+
Codecs enable safe, type-checked conversions between different data formats. They're perfect for:
|
|
786
|
+
- **API boundaries**: Convert strings to structured data
|
|
787
|
+
- **Database serialization**: Transform objects to/from storage formats
|
|
788
|
+
- **Network protocols**: Handle data encoding/decoding
|
|
789
|
+
- **Configuration parsing**: Convert config strings to typed values
|
|
790
|
+
|
|
791
|
+
### 📦 Built-in Codecs
|
|
792
|
+
|
|
793
|
+
VLD provides all Zod-compatible codecs plus additional utilities:
|
|
794
|
+
|
|
795
|
+
#### **String Conversion Codecs**
|
|
796
|
+
|
|
797
|
+
```typescript
|
|
798
|
+
import { stringToNumber, stringToInt, stringToBigInt, stringToBoolean } from '@oxog/vld';
|
|
799
|
+
|
|
800
|
+
// String to number conversion
|
|
801
|
+
const age = stringToNumber.parse('25'); // 25
|
|
802
|
+
const price = stringToNumber.encode(99.99); // "99.99"
|
|
803
|
+
|
|
804
|
+
// String to integer (validates integer constraint)
|
|
805
|
+
const count = stringToInt.parse('42'); // 42
|
|
806
|
+
stringToInt.parse('42.5'); // ❌ Validation error: must be integer
|
|
807
|
+
|
|
808
|
+
// String to BigInt for large numbers
|
|
809
|
+
const bigNum = stringToBigInt.parse('123456789012345678901234567890'); // 123456789012345678901234567890n
|
|
810
|
+
|
|
811
|
+
// String to boolean (flexible parsing)
|
|
812
|
+
stringToBoolean.parse('true'); // true
|
|
813
|
+
stringToBoolean.parse('1'); // true
|
|
814
|
+
stringToBoolean.parse('yes'); // true
|
|
815
|
+
stringToBoolean.parse('on'); // true
|
|
816
|
+
stringToBoolean.parse('false'); // false
|
|
817
|
+
stringToBoolean.parse('0'); // false
|
|
818
|
+
```
|
|
819
|
+
|
|
820
|
+
#### **Date Conversion Codecs**
|
|
821
|
+
|
|
822
|
+
```typescript
|
|
823
|
+
import { isoDatetimeToDate, epochSecondsToDate, epochMillisToDate } from '@oxog/vld';
|
|
824
|
+
|
|
825
|
+
// ISO datetime string to Date
|
|
826
|
+
const date1 = isoDatetimeToDate.parse('2023-12-25T10:30:00.000Z');
|
|
827
|
+
console.log(date1.toISOString()); // "2023-12-25T10:30:00.000Z"
|
|
828
|
+
|
|
829
|
+
// Unix epoch seconds to Date
|
|
830
|
+
const date2 = epochSecondsToDate.parse(1703505000);
|
|
831
|
+
console.log(date2.getFullYear()); // 2023
|
|
832
|
+
|
|
833
|
+
// Unix epoch milliseconds to Date
|
|
834
|
+
const date3 = epochMillisToDate.parse(1703505000000);
|
|
835
|
+
console.log(date3.getMonth()); // 11 (December)
|
|
836
|
+
|
|
837
|
+
// All support bidirectional conversion
|
|
838
|
+
const backToEpoch = epochSecondsToDate.encode(new Date()); // Unix timestamp
|
|
839
|
+
```
|
|
840
|
+
|
|
841
|
+
#### **JSON and Complex Data Codecs**
|
|
842
|
+
|
|
843
|
+
```typescript
|
|
844
|
+
import { jsonCodec, base64Json } from '@oxog/vld';
|
|
845
|
+
|
|
846
|
+
// Generic JSON codec
|
|
847
|
+
const userJson = jsonCodec();
|
|
848
|
+
const user = userJson.parse('{"name":"John","age":30}'); // { name: "John", age: 30 }
|
|
849
|
+
const jsonString = userJson.encode(user); // '{"name":"John","age":30}'
|
|
850
|
+
|
|
851
|
+
// JSON codec with schema validation
|
|
852
|
+
const userSchema = v.object({
|
|
853
|
+
name: v.string(),
|
|
854
|
+
age: v.number()
|
|
855
|
+
});
|
|
856
|
+
const typedJsonCodec = jsonCodec(userSchema);
|
|
857
|
+
const validatedUser = typedJsonCodec.parse('{"name":"John","age":30}'); // Fully typed!
|
|
858
|
+
|
|
859
|
+
// Base64-encoded JSON
|
|
860
|
+
const b64JsonCodec = base64Json(userSchema);
|
|
861
|
+
const encoded = b64JsonCodec.encode({ name: "Alice", age: 25 }); // Base64 string
|
|
862
|
+
const decoded = b64JsonCodec.parse(encoded); // { name: "Alice", age: 25 }
|
|
863
|
+
```
|
|
864
|
+
|
|
865
|
+
#### **URL and Web Codecs**
|
|
866
|
+
|
|
867
|
+
```typescript
|
|
868
|
+
import { stringToURL, stringToHttpURL, uriComponent } from '@oxog/vld';
|
|
869
|
+
|
|
870
|
+
// String to URL object
|
|
871
|
+
const url = stringToURL.parse('https://example.com/path?param=value');
|
|
872
|
+
console.log(url.hostname); // "example.com"
|
|
873
|
+
console.log(url.searchParams.get('param')); // "value"
|
|
874
|
+
|
|
875
|
+
// Restrict to HTTP/HTTPS only
|
|
876
|
+
const httpUrl = stringToHttpURL.parse('https://api.example.com');
|
|
877
|
+
stringToHttpURL.parse('ftp://files.example.com'); // ❌ Error: Must be HTTP/HTTPS
|
|
878
|
+
|
|
879
|
+
// URI component encoding/decoding
|
|
880
|
+
const encoded = uriComponent.parse('Hello World! 🚀'); // "Hello%20World!%20%F0%9F%9A%80"
|
|
881
|
+
const decoded = uriComponent.encode(encoded); // "Hello World! 🚀"
|
|
882
|
+
```
|
|
883
|
+
|
|
884
|
+
#### **Binary Data Codecs**
|
|
885
|
+
|
|
886
|
+
```typescript
|
|
887
|
+
import { base64ToBytes, hexToBytes, utf8ToBytes, bytesToUtf8 } from '@oxog/vld';
|
|
888
|
+
|
|
889
|
+
// Base64 to byte array
|
|
890
|
+
const bytes1 = base64ToBytes.parse('SGVsbG8gV29ybGQ='); // Uint8Array([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100])
|
|
891
|
+
|
|
892
|
+
// Hex to byte array
|
|
893
|
+
const bytes2 = hexToBytes.parse('48656c6c6f'); // Uint8Array([72, 101, 108, 108, 111])
|
|
894
|
+
|
|
895
|
+
// UTF-8 string to bytes
|
|
896
|
+
const bytes3 = utf8ToBytes.parse('Hello! 👋'); // Uint8Array([...])
|
|
897
|
+
|
|
898
|
+
// Bytes to UTF-8 string
|
|
899
|
+
const text = bytesToUtf8.parse(bytes3); // "Hello! 👋"
|
|
900
|
+
|
|
901
|
+
// All support round-trip conversion
|
|
902
|
+
const original = 'Hello World!';
|
|
903
|
+
const roundTrip = bytesToUtf8.parse(utf8ToBytes.parse(original)); // "Hello World!"
|
|
904
|
+
```
|
|
905
|
+
|
|
906
|
+
### 🛠 Custom Codecs
|
|
907
|
+
|
|
908
|
+
Create your own codecs for specific use cases:
|
|
909
|
+
|
|
910
|
+
```typescript
|
|
911
|
+
import { v } from '@oxog/vld';
|
|
912
|
+
|
|
913
|
+
// Custom CSV to array codec
|
|
914
|
+
const csvToArray = v.codec(
|
|
915
|
+
v.string(), // Input: CSV string
|
|
916
|
+
v.array(v.string()), // Output: Array of strings
|
|
917
|
+
{
|
|
918
|
+
decode: (csv: string) => csv.split(',').map(s => s.trim()),
|
|
919
|
+
encode: (arr: string[]) => arr.join(', ')
|
|
920
|
+
}
|
|
921
|
+
);
|
|
922
|
+
|
|
923
|
+
const tags = csvToArray.parse('react, typescript, vld'); // ["react", "typescript", "vld"]
|
|
924
|
+
const csvString = csvToArray.encode(['node', 'express', 'api']); // "node, express, api"
|
|
925
|
+
|
|
926
|
+
// Complex: Environment config codec
|
|
927
|
+
const envConfigCodec = v.codec(
|
|
928
|
+
v.string(),
|
|
929
|
+
v.object({
|
|
930
|
+
port: v.number(),
|
|
931
|
+
debug: v.boolean(),
|
|
932
|
+
dbUrl: v.string()
|
|
933
|
+
}),
|
|
934
|
+
{
|
|
935
|
+
decode: (envString: string) => {
|
|
936
|
+
const config = {};
|
|
937
|
+
envString.split('\n').forEach(line => {
|
|
938
|
+
const [key, value] = line.split('=');
|
|
939
|
+
if (key === 'PORT') config.port = parseInt(value, 10);
|
|
940
|
+
if (key === 'DEBUG') config.debug = value === 'true';
|
|
941
|
+
if (key === 'DB_URL') config.dbUrl = value;
|
|
942
|
+
});
|
|
943
|
+
return config;
|
|
944
|
+
},
|
|
945
|
+
encode: (config) => [
|
|
946
|
+
`PORT=${config.port}`,
|
|
947
|
+
`DEBUG=${config.debug}`,
|
|
948
|
+
`DB_URL=${config.dbUrl}`
|
|
949
|
+
].join('\n')
|
|
950
|
+
}
|
|
951
|
+
);
|
|
952
|
+
```
|
|
953
|
+
|
|
954
|
+
### 🚀 Advanced Codec Features
|
|
955
|
+
|
|
956
|
+
#### **Async Codecs**
|
|
957
|
+
```typescript
|
|
958
|
+
const asyncCodec = v.codec(
|
|
959
|
+
v.string(),
|
|
960
|
+
v.object({ data: v.string() }),
|
|
961
|
+
{
|
|
962
|
+
decode: async (str: string) => {
|
|
963
|
+
// Simulate API call
|
|
964
|
+
const response = await fetch(`/api/decode?data=${str}`);
|
|
965
|
+
return response.json();
|
|
966
|
+
},
|
|
967
|
+
encode: async (obj) => {
|
|
968
|
+
const response = await fetch('/api/encode', {
|
|
969
|
+
method: 'POST',
|
|
970
|
+
body: JSON.stringify(obj)
|
|
971
|
+
});
|
|
972
|
+
return response.text();
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
);
|
|
976
|
+
|
|
977
|
+
// Use async methods
|
|
978
|
+
const result = await asyncCodec.parseAsync('input-data');
|
|
979
|
+
const encoded = await asyncCodec.encodeAsync({ data: 'output' });
|
|
980
|
+
```
|
|
981
|
+
|
|
982
|
+
#### **Error Handling**
|
|
983
|
+
```typescript
|
|
984
|
+
const safeParseResult = stringToNumber.safeParse('not-a-number');
|
|
985
|
+
if (!safeParseResult.success) {
|
|
986
|
+
console.error('Parse failed:', safeParseResult.error.message);
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
const safeEncodeResult = stringToNumber.safeEncode('invalid-input');
|
|
990
|
+
if (!safeEncodeResult.success) {
|
|
991
|
+
console.error('Encode failed:', safeEncodeResult.error.message);
|
|
992
|
+
}
|
|
993
|
+
```
|
|
994
|
+
|
|
995
|
+
#### **JWT Payload Decoder**
|
|
996
|
+
```typescript
|
|
997
|
+
import { jwtPayload } from '@oxog/vld';
|
|
998
|
+
|
|
999
|
+
// Decode JWT payload (read-only)
|
|
1000
|
+
const payloadSchema = v.object({
|
|
1001
|
+
sub: v.string(),
|
|
1002
|
+
name: v.string(),
|
|
1003
|
+
iat: v.number()
|
|
1004
|
+
});
|
|
1005
|
+
|
|
1006
|
+
const decoder = jwtPayload(payloadSchema);
|
|
1007
|
+
const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c';
|
|
1008
|
+
|
|
1009
|
+
const payload = decoder.parse(token);
|
|
1010
|
+
console.log(payload.name); // "John Doe"
|
|
1011
|
+
console.log(payload.sub); // "1234567890"
|
|
1012
|
+
```
|
|
1013
|
+
|
|
1014
|
+
### 🎯 Codec vs Transform
|
|
1015
|
+
|
|
1016
|
+
| Feature | Codec | Transform |
|
|
1017
|
+
|---------|--------|-----------|
|
|
1018
|
+
| **Direction** | Bidirectional (encode/decode) | Unidirectional (transform only) |
|
|
1019
|
+
| **Type Safety** | Input and output validation | Output validation only |
|
|
1020
|
+
| **Use Case** | Data serialization, API boundaries | Data cleaning, formatting |
|
|
1021
|
+
| **Performance** | Optimized for round-trips | Optimized for single direction |
|
|
1022
|
+
|
|
1023
|
+
```typescript
|
|
1024
|
+
// Transform: One-way conversion
|
|
1025
|
+
const upperCase = v.string().transform(s => s.toUpperCase());
|
|
1026
|
+
const result = upperCase.parse('hello'); // "HELLO"
|
|
1027
|
+
// No way to get back to "hello"
|
|
1028
|
+
|
|
1029
|
+
// Codec: Two-way conversion
|
|
1030
|
+
const upperCaseCodec = v.codec(
|
|
1031
|
+
v.string(),
|
|
1032
|
+
v.string(),
|
|
1033
|
+
{
|
|
1034
|
+
decode: s => s.toUpperCase(),
|
|
1035
|
+
encode: s => s.toLowerCase()
|
|
1036
|
+
}
|
|
1037
|
+
);
|
|
1038
|
+
const encoded = upperCaseCodec.parse('hello'); // "HELLO"
|
|
1039
|
+
const original = upperCaseCodec.encode('HELLO'); // "hello"
|
|
1040
|
+
```
|
|
1041
|
+
|
|
771
1042
|
## 🔄 Migrating from Zod
|
|
772
1043
|
|
|
773
1044
|
VLD provides 100% feature parity with Zod, making migration seamless:
|
|
@@ -789,7 +1060,7 @@ const schema = v.string().email();
|
|
|
789
1060
|
- **🌍 Internationalization**: Built-in 27+ language support
|
|
790
1061
|
- **📦 Bundle Size**: Smaller with zero dependencies
|
|
791
1062
|
- **🔒 Security**: Immutable validators prevent memory leaks
|
|
792
|
-
- **✅ Testing**:
|
|
1063
|
+
- **✅ Testing**: 97.3% test coverage with 569 tests
|
|
793
1064
|
|
|
794
1065
|
## 📈 Benchmarks
|
|
795
1066
|
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { VldCodec } from '../validators/codec';
|
|
2
|
+
/**
|
|
3
|
+
* Predefined codecs for common transformations (Zod-compatible)
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* String to number codec
|
|
7
|
+
*/
|
|
8
|
+
export declare const stringToNumber: VldCodec<string, number>;
|
|
9
|
+
/**
|
|
10
|
+
* String to integer codec
|
|
11
|
+
*/
|
|
12
|
+
export declare const stringToInt: VldCodec<string, number>;
|
|
13
|
+
/**
|
|
14
|
+
* String to BigInt codec
|
|
15
|
+
*/
|
|
16
|
+
export declare const stringToBigInt: VldCodec<string, bigint>;
|
|
17
|
+
/**
|
|
18
|
+
* Number to BigInt codec
|
|
19
|
+
*/
|
|
20
|
+
export declare const numberToBigInt: VldCodec<number, bigint>;
|
|
21
|
+
/**
|
|
22
|
+
* ISO datetime string to Date codec
|
|
23
|
+
*/
|
|
24
|
+
export declare const isoDatetimeToDate: VldCodec<string, Date>;
|
|
25
|
+
/**
|
|
26
|
+
* Epoch seconds to Date codec
|
|
27
|
+
*/
|
|
28
|
+
export declare const epochSecondsToDate: VldCodec<number, Date>;
|
|
29
|
+
/**
|
|
30
|
+
* Epoch milliseconds to Date codec
|
|
31
|
+
*/
|
|
32
|
+
export declare const epochMillisToDate: VldCodec<number, Date>;
|
|
33
|
+
/**
|
|
34
|
+
* JSON string codec for any type
|
|
35
|
+
*/
|
|
36
|
+
export declare const jsonCodec: <T = any>(schema?: any) => VldCodec<string, T>;
|
|
37
|
+
/**
|
|
38
|
+
* String to URL codec
|
|
39
|
+
*/
|
|
40
|
+
export declare const stringToURL: VldCodec<string, import("url").URL>;
|
|
41
|
+
/**
|
|
42
|
+
* String to HTTP/HTTPS URL codec
|
|
43
|
+
*/
|
|
44
|
+
export declare const stringToHttpURL: VldCodec<string, import("url").URL>;
|
|
45
|
+
/**
|
|
46
|
+
* URI component encode/decode codec
|
|
47
|
+
*/
|
|
48
|
+
export declare const uriComponent: VldCodec<string, string>;
|
|
49
|
+
/**
|
|
50
|
+
* String to boolean codec
|
|
51
|
+
*/
|
|
52
|
+
export declare const stringToBoolean: VldCodec<string, boolean>;
|
|
53
|
+
/**
|
|
54
|
+
* Base64 to Uint8Array codec
|
|
55
|
+
*/
|
|
56
|
+
export declare const base64ToBytes: VldCodec<string, Uint8Array<ArrayBufferLike>>;
|
|
57
|
+
/**
|
|
58
|
+
* URL-safe Base64 to Uint8Array codec
|
|
59
|
+
*/
|
|
60
|
+
export declare const base64UrlToBytes: VldCodec<string, Uint8Array<ArrayBufferLike>>;
|
|
61
|
+
/**
|
|
62
|
+
* Hexadecimal to Uint8Array codec
|
|
63
|
+
*/
|
|
64
|
+
export declare const hexToBytes: VldCodec<string, Uint8Array<ArrayBufferLike>>;
|
|
65
|
+
/**
|
|
66
|
+
* Lowercase hexadecimal to Uint8Array codec
|
|
67
|
+
*/
|
|
68
|
+
export declare const hexLowerToBytes: VldCodec<string, Uint8Array<ArrayBufferLike>>;
|
|
69
|
+
/**
|
|
70
|
+
* UTF-8 string to Uint8Array codec
|
|
71
|
+
*/
|
|
72
|
+
export declare const utf8ToBytes: VldCodec<string, Uint8Array<ArrayBufferLike>>;
|
|
73
|
+
/**
|
|
74
|
+
* Uint8Array to UTF-8 string codec (reverse of utf8ToBytes)
|
|
75
|
+
*/
|
|
76
|
+
export declare const bytesToUtf8: VldCodec<Uint8Array<ArrayBufferLike>, string>;
|
|
77
|
+
/**
|
|
78
|
+
* Base64URL to Uint8Array codec (updated)
|
|
79
|
+
*/
|
|
80
|
+
export declare const base64urlToBytes: VldCodec<string, Uint8Array<ArrayBufferLike>>;
|
|
81
|
+
/**
|
|
82
|
+
* Base64 encoded JSON codec (updated)
|
|
83
|
+
*/
|
|
84
|
+
export declare const base64Json: <T = any>(schema?: any) => VldCodec<string, T>;
|
|
85
|
+
/**
|
|
86
|
+
* JWT payload decoder (base64url encoded JSON) - updated
|
|
87
|
+
*/
|
|
88
|
+
export declare const jwtPayload: (schema?: any) => VldCodec<string, any>;
|
|
89
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/codecs/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAoB/C;;GAEG;AAIH;;GAEG;AACH,eAAO,MAAM,cAAc,0BAW1B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,WAAW,0BAWvB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,cAAc,0BAa1B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,cAAc,0BAO1B,CAAC;AAIF;;GAEG;AACH,eAAO,MAAM,iBAAiB,wBAW7B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kBAAkB,wBAO9B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iBAAiB,wBAO7B,CAAC;AAIF;;GAEG;AACH,eAAO,MAAM,SAAS,GAAI,CAAC,GAAG,GAAG,EAAE,SAAS,GAAG,wBAiB9C,CAAC;AAIF;;GAEG;AACH,eAAO,MAAM,WAAW,qCAavB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,eAAe,qCAiB3B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,YAAY,0BAaxB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,eAAe,2BAY3B,CAAC;AAIF;;GAEG;AACH,eAAO,MAAM,aAAa,+CAOzB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,gBAAgB,+CAO5B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,UAAU,+CAOtB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,eAAe,+CAO3B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,WAAW,+CAOvB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,WAAW,+CAOvB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,gBAAgB,+CAO5B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,UAAU,GAAI,CAAC,GAAG,GAAG,EAAE,SAAS,GAAG,wBAiB/C,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,UAAU,GAAI,SAAS,GAAG,0BAuBtC,CAAC"}
|