@oxog/vld 1.0.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 +113 -0
- package/LICENSE +21 -0
- package/README.md +776 -0
- package/dist/errors.d.ts +49 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +144 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +275 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1170 -0
- package/dist/index.js.map +1 -0
- package/dist/locales/af.d.ts +3 -0
- package/dist/locales/af.d.ts.map +1 -0
- package/dist/locales/af.js +74 -0
- package/dist/locales/af.js.map +1 -0
- package/dist/locales/ar.d.ts +3 -0
- package/dist/locales/ar.d.ts.map +1 -0
- package/dist/locales/ar.js +74 -0
- package/dist/locales/ar.js.map +1 -0
- package/dist/locales/bn.d.ts +3 -0
- package/dist/locales/bn.d.ts.map +1 -0
- package/dist/locales/bn.js +74 -0
- package/dist/locales/bn.js.map +1 -0
- package/dist/locales/da.d.ts +3 -0
- package/dist/locales/da.d.ts.map +1 -0
- package/dist/locales/da.js +74 -0
- package/dist/locales/da.js.map +1 -0
- package/dist/locales/de.d.ts +3 -0
- package/dist/locales/de.d.ts.map +1 -0
- package/dist/locales/de.js +74 -0
- package/dist/locales/de.js.map +1 -0
- package/dist/locales/en.d.ts +3 -0
- package/dist/locales/en.d.ts.map +1 -0
- package/dist/locales/en.js +74 -0
- package/dist/locales/en.js.map +1 -0
- package/dist/locales/es-MX.d.ts +3 -0
- package/dist/locales/es-MX.d.ts.map +1 -0
- package/dist/locales/es-MX.js +74 -0
- package/dist/locales/es-MX.js.map +1 -0
- package/dist/locales/es.d.ts +3 -0
- package/dist/locales/es.d.ts.map +1 -0
- package/dist/locales/es.js +62 -0
- package/dist/locales/es.js.map +1 -0
- package/dist/locales/fi.d.ts +3 -0
- package/dist/locales/fi.d.ts.map +1 -0
- package/dist/locales/fi.js +74 -0
- package/dist/locales/fi.js.map +1 -0
- package/dist/locales/fr.d.ts +3 -0
- package/dist/locales/fr.d.ts.map +1 -0
- package/dist/locales/fr.js +62 -0
- package/dist/locales/fr.js.map +1 -0
- package/dist/locales/hi.d.ts +3 -0
- package/dist/locales/hi.d.ts.map +1 -0
- package/dist/locales/hi.js +74 -0
- package/dist/locales/hi.js.map +1 -0
- package/dist/locales/id.d.ts +3 -0
- package/dist/locales/id.d.ts.map +1 -0
- package/dist/locales/id.js +74 -0
- package/dist/locales/id.js.map +1 -0
- package/dist/locales/index.d.ts +19 -0
- package/dist/locales/index.d.ts.map +1 -0
- package/dist/locales/index.js +91 -0
- package/dist/locales/index.js.map +1 -0
- package/dist/locales/it.d.ts +3 -0
- package/dist/locales/it.d.ts.map +1 -0
- package/dist/locales/it.js +74 -0
- package/dist/locales/it.js.map +1 -0
- package/dist/locales/ja.d.ts +3 -0
- package/dist/locales/ja.d.ts.map +1 -0
- package/dist/locales/ja.js +74 -0
- package/dist/locales/ja.js.map +1 -0
- package/dist/locales/ko.d.ts +3 -0
- package/dist/locales/ko.d.ts.map +1 -0
- package/dist/locales/ko.js +74 -0
- package/dist/locales/ko.js.map +1 -0
- package/dist/locales/nl.d.ts +3 -0
- package/dist/locales/nl.d.ts.map +1 -0
- package/dist/locales/nl.js +74 -0
- package/dist/locales/nl.js.map +1 -0
- package/dist/locales/no.d.ts +3 -0
- package/dist/locales/no.d.ts.map +1 -0
- package/dist/locales/no.js +74 -0
- package/dist/locales/no.js.map +1 -0
- package/dist/locales/pl.d.ts +3 -0
- package/dist/locales/pl.d.ts.map +1 -0
- package/dist/locales/pl.js +74 -0
- package/dist/locales/pl.js.map +1 -0
- package/dist/locales/pt-BR.d.ts +3 -0
- package/dist/locales/pt-BR.d.ts.map +1 -0
- package/dist/locales/pt-BR.js +74 -0
- package/dist/locales/pt-BR.js.map +1 -0
- package/dist/locales/pt.d.ts +3 -0
- package/dist/locales/pt.d.ts.map +1 -0
- package/dist/locales/pt.js +74 -0
- package/dist/locales/pt.js.map +1 -0
- package/dist/locales/ru.d.ts +3 -0
- package/dist/locales/ru.d.ts.map +1 -0
- package/dist/locales/ru.js +74 -0
- package/dist/locales/ru.js.map +1 -0
- package/dist/locales/sv.d.ts +3 -0
- package/dist/locales/sv.d.ts.map +1 -0
- package/dist/locales/sv.js +74 -0
- package/dist/locales/sv.js.map +1 -0
- package/dist/locales/sw.d.ts +3 -0
- package/dist/locales/sw.d.ts.map +1 -0
- package/dist/locales/sw.js +74 -0
- package/dist/locales/sw.js.map +1 -0
- package/dist/locales/th.d.ts +3 -0
- package/dist/locales/th.d.ts.map +1 -0
- package/dist/locales/th.js +74 -0
- package/dist/locales/th.js.map +1 -0
- package/dist/locales/tr.d.ts +3 -0
- package/dist/locales/tr.d.ts.map +1 -0
- package/dist/locales/tr.js +74 -0
- package/dist/locales/tr.js.map +1 -0
- package/dist/locales/types.d.ts +60 -0
- package/dist/locales/types.d.ts.map +1 -0
- package/dist/locales/types.js +2 -0
- package/dist/locales/types.js.map +1 -0
- package/dist/locales/vi.d.ts +3 -0
- package/dist/locales/vi.d.ts.map +1 -0
- package/dist/locales/vi.js +74 -0
- package/dist/locales/vi.js.map +1 -0
- package/dist/locales/zh.d.ts +3 -0
- package/dist/locales/zh.d.ts.map +1 -0
- package/dist/locales/zh.js +74 -0
- package/dist/locales/zh.js.map +1 -0
- package/dist/locales.d.ts +54 -0
- package/dist/locales.d.ts.map +1 -0
- package/dist/locales.js +732 -0
- package/dist/locales.js.map +1 -0
- package/package.json +73 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1170 @@
|
|
|
1
|
+
// VLD - Fast, Type-Safe Validation Library
|
|
2
|
+
// Zero dependencies, blazing fast performance
|
|
3
|
+
import { getMessages } from './locales/index.js';
|
|
4
|
+
// Base class for all validators
|
|
5
|
+
class VldBase {
|
|
6
|
+
refine(predicate, message) {
|
|
7
|
+
return new VldRefine(this, predicate, message);
|
|
8
|
+
}
|
|
9
|
+
// Transform data after validation
|
|
10
|
+
transform(fn) {
|
|
11
|
+
return new VldTransform(this, fn);
|
|
12
|
+
}
|
|
13
|
+
// Add default value
|
|
14
|
+
default(value) {
|
|
15
|
+
return new VldDefault(this, value);
|
|
16
|
+
}
|
|
17
|
+
// Catch validation errors and provide fallback
|
|
18
|
+
catch(value) {
|
|
19
|
+
return new VldCatch(this, value);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
// Pre-compiled regex patterns
|
|
23
|
+
const EMAIL_REGEX = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+$/;
|
|
24
|
+
const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
25
|
+
const URL_REGEX = /^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)$/;
|
|
26
|
+
const IPV4_REGEX = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
|
|
27
|
+
const IPV6_REGEX = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/;
|
|
28
|
+
// Helper function for deep merging objects in intersection
|
|
29
|
+
function deepMerge(obj1, obj2) {
|
|
30
|
+
const result = { ...obj1 };
|
|
31
|
+
for (const key in obj2) {
|
|
32
|
+
if (obj2.hasOwnProperty(key)) {
|
|
33
|
+
if (typeof obj2[key] === 'object' && obj2[key] !== null && !Array.isArray(obj2[key]) &&
|
|
34
|
+
typeof obj1[key] === 'object' && obj1[key] !== null && !Array.isArray(obj1[key])) {
|
|
35
|
+
result[key] = deepMerge(obj1[key], obj2[key]);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
result[key] = obj2[key];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
// String validator
|
|
45
|
+
export class VldString extends VldBase {
|
|
46
|
+
constructor() {
|
|
47
|
+
super(...arguments);
|
|
48
|
+
this.checks = [];
|
|
49
|
+
this.transforms = [];
|
|
50
|
+
this.errorMessage = getMessages().invalidString;
|
|
51
|
+
}
|
|
52
|
+
parse(value) {
|
|
53
|
+
if (typeof value !== 'string') {
|
|
54
|
+
throw new Error(this.errorMessage || getMessages().invalidString);
|
|
55
|
+
}
|
|
56
|
+
let val = value;
|
|
57
|
+
// Apply transforms
|
|
58
|
+
for (const transform of this.transforms) {
|
|
59
|
+
val = transform(val);
|
|
60
|
+
}
|
|
61
|
+
// Apply checks
|
|
62
|
+
for (const check of this.checks) {
|
|
63
|
+
if (!check(val)) {
|
|
64
|
+
throw new Error(this.errorMessage || getMessages().invalidString);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return val;
|
|
68
|
+
}
|
|
69
|
+
safeParse(value) {
|
|
70
|
+
try {
|
|
71
|
+
return { success: true, data: this.parse(value) };
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
return { success: false, error: error };
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Chainable methods
|
|
78
|
+
min(length, message) {
|
|
79
|
+
const newValidator = new VldString();
|
|
80
|
+
newValidator.checks = [...this.checks, (v) => v.length >= length];
|
|
81
|
+
newValidator.transforms = [...this.transforms];
|
|
82
|
+
newValidator.errorMessage = message || getMessages().stringMin(length);
|
|
83
|
+
return newValidator;
|
|
84
|
+
}
|
|
85
|
+
max(length, message) {
|
|
86
|
+
const newValidator = new VldString();
|
|
87
|
+
newValidator.checks = [...this.checks, (v) => v.length <= length];
|
|
88
|
+
newValidator.transforms = [...this.transforms];
|
|
89
|
+
newValidator.errorMessage = message || getMessages().stringMax(length);
|
|
90
|
+
return newValidator;
|
|
91
|
+
}
|
|
92
|
+
length(length, message) {
|
|
93
|
+
const newValidator = new VldString();
|
|
94
|
+
newValidator.checks = [...this.checks, (v) => v.length === length];
|
|
95
|
+
newValidator.transforms = [...this.transforms];
|
|
96
|
+
newValidator.errorMessage = message || getMessages().stringLength(length);
|
|
97
|
+
return newValidator;
|
|
98
|
+
}
|
|
99
|
+
email(message) {
|
|
100
|
+
const newValidator = new VldString();
|
|
101
|
+
newValidator.checks = [...this.checks, (v) => EMAIL_REGEX.test(v)];
|
|
102
|
+
newValidator.transforms = [...this.transforms];
|
|
103
|
+
newValidator.errorMessage = message || getMessages().stringEmail;
|
|
104
|
+
return newValidator;
|
|
105
|
+
}
|
|
106
|
+
url(message) {
|
|
107
|
+
const newValidator = new VldString();
|
|
108
|
+
newValidator.checks = [...this.checks, (v) => URL_REGEX.test(v)];
|
|
109
|
+
newValidator.transforms = [...this.transforms];
|
|
110
|
+
newValidator.errorMessage = message || getMessages().stringUrl;
|
|
111
|
+
return newValidator;
|
|
112
|
+
}
|
|
113
|
+
uuid(message) {
|
|
114
|
+
const newValidator = new VldString();
|
|
115
|
+
newValidator.checks = [...this.checks, (v) => UUID_REGEX.test(v)];
|
|
116
|
+
newValidator.transforms = [...this.transforms];
|
|
117
|
+
newValidator.errorMessage = message || getMessages().stringUuid;
|
|
118
|
+
return newValidator;
|
|
119
|
+
}
|
|
120
|
+
regex(pattern, message) {
|
|
121
|
+
const newValidator = new VldString();
|
|
122
|
+
newValidator.checks = [...this.checks, (v) => pattern.test(v)];
|
|
123
|
+
newValidator.transforms = [...this.transforms];
|
|
124
|
+
newValidator.errorMessage = message || getMessages().stringRegex;
|
|
125
|
+
return newValidator;
|
|
126
|
+
}
|
|
127
|
+
trim() {
|
|
128
|
+
const newValidator = new VldString();
|
|
129
|
+
newValidator.checks = [...this.checks];
|
|
130
|
+
newValidator.transforms = [...this.transforms, (v) => v.trim()];
|
|
131
|
+
newValidator.errorMessage = this.errorMessage;
|
|
132
|
+
return newValidator;
|
|
133
|
+
}
|
|
134
|
+
toLowerCase() {
|
|
135
|
+
const newValidator = new VldString();
|
|
136
|
+
newValidator.checks = [...this.checks];
|
|
137
|
+
newValidator.transforms = [...this.transforms, (v) => v.toLowerCase()];
|
|
138
|
+
newValidator.errorMessage = this.errorMessage;
|
|
139
|
+
return newValidator;
|
|
140
|
+
}
|
|
141
|
+
toUpperCase() {
|
|
142
|
+
const newValidator = new VldString();
|
|
143
|
+
newValidator.checks = [...this.checks];
|
|
144
|
+
newValidator.transforms = [...this.transforms, (v) => v.toUpperCase()];
|
|
145
|
+
newValidator.errorMessage = this.errorMessage;
|
|
146
|
+
return newValidator;
|
|
147
|
+
}
|
|
148
|
+
startsWith(str, message) {
|
|
149
|
+
const newValidator = new VldString();
|
|
150
|
+
newValidator.checks = [...this.checks, (v) => v.startsWith(str)];
|
|
151
|
+
newValidator.transforms = [...this.transforms];
|
|
152
|
+
newValidator.errorMessage = message || getMessages().stringStartsWith(str);
|
|
153
|
+
return newValidator;
|
|
154
|
+
}
|
|
155
|
+
endsWith(str, message) {
|
|
156
|
+
const newValidator = new VldString();
|
|
157
|
+
newValidator.checks = [...this.checks, (v) => v.endsWith(str)];
|
|
158
|
+
newValidator.transforms = [...this.transforms];
|
|
159
|
+
newValidator.errorMessage = message || getMessages().stringEndsWith(str);
|
|
160
|
+
return newValidator;
|
|
161
|
+
}
|
|
162
|
+
includes(str, message) {
|
|
163
|
+
const newValidator = new VldString();
|
|
164
|
+
newValidator.checks = [...this.checks, (v) => v.includes(str)];
|
|
165
|
+
newValidator.transforms = [...this.transforms];
|
|
166
|
+
newValidator.errorMessage = message || getMessages().stringIncludes(str);
|
|
167
|
+
return newValidator;
|
|
168
|
+
}
|
|
169
|
+
ip(message) {
|
|
170
|
+
const newValidator = new VldString();
|
|
171
|
+
newValidator.checks = [...this.checks, (v) => IPV4_REGEX.test(v) || IPV6_REGEX.test(v)];
|
|
172
|
+
newValidator.transforms = [...this.transforms];
|
|
173
|
+
newValidator.errorMessage = message || getMessages().stringIp;
|
|
174
|
+
return newValidator;
|
|
175
|
+
}
|
|
176
|
+
ipv4(message) {
|
|
177
|
+
const newValidator = new VldString();
|
|
178
|
+
newValidator.checks = [...this.checks, (v) => IPV4_REGEX.test(v)];
|
|
179
|
+
newValidator.transforms = [...this.transforms];
|
|
180
|
+
newValidator.errorMessage = message || getMessages().stringIpv4;
|
|
181
|
+
return newValidator;
|
|
182
|
+
}
|
|
183
|
+
ipv6(message) {
|
|
184
|
+
const newValidator = new VldString();
|
|
185
|
+
newValidator.checks = [...this.checks, (v) => IPV6_REGEX.test(v)];
|
|
186
|
+
newValidator.transforms = [...this.transforms];
|
|
187
|
+
newValidator.errorMessage = message || getMessages().stringIpv6;
|
|
188
|
+
return newValidator;
|
|
189
|
+
}
|
|
190
|
+
nonempty(message) {
|
|
191
|
+
const newValidator = new VldString();
|
|
192
|
+
newValidator.checks = [...this.checks, (v) => v.length > 0];
|
|
193
|
+
newValidator.transforms = [...this.transforms];
|
|
194
|
+
newValidator.errorMessage = message || getMessages().stringEmpty;
|
|
195
|
+
return newValidator;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
// Number validator
|
|
199
|
+
export class VldNumber extends VldBase {
|
|
200
|
+
constructor() {
|
|
201
|
+
super(...arguments);
|
|
202
|
+
this.checks = [];
|
|
203
|
+
this.errorMessage = getMessages().invalidNumber;
|
|
204
|
+
}
|
|
205
|
+
parse(value) {
|
|
206
|
+
if (typeof value !== 'number' || isNaN(value)) {
|
|
207
|
+
throw new Error(this.errorMessage || getMessages().invalidNumber);
|
|
208
|
+
}
|
|
209
|
+
for (const check of this.checks) {
|
|
210
|
+
if (!check(value)) {
|
|
211
|
+
throw new Error(this.errorMessage || getMessages().invalidNumber);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return value;
|
|
215
|
+
}
|
|
216
|
+
safeParse(value) {
|
|
217
|
+
try {
|
|
218
|
+
return { success: true, data: this.parse(value) };
|
|
219
|
+
}
|
|
220
|
+
catch (error) {
|
|
221
|
+
return { success: false, error: error };
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
min(value, message) {
|
|
225
|
+
const newValidator = new VldNumber();
|
|
226
|
+
newValidator.checks = [...this.checks, (v) => v >= value];
|
|
227
|
+
newValidator.errorMessage = message || getMessages().numberMin(value);
|
|
228
|
+
return newValidator;
|
|
229
|
+
}
|
|
230
|
+
max(value, message) {
|
|
231
|
+
const newValidator = new VldNumber();
|
|
232
|
+
newValidator.checks = [...this.checks, (v) => v <= value];
|
|
233
|
+
newValidator.errorMessage = message || getMessages().numberMax(value);
|
|
234
|
+
return newValidator;
|
|
235
|
+
}
|
|
236
|
+
int(message) {
|
|
237
|
+
const newValidator = new VldNumber();
|
|
238
|
+
newValidator.checks = [...this.checks, (v) => Number.isInteger(v)];
|
|
239
|
+
newValidator.errorMessage = message || getMessages().numberInt;
|
|
240
|
+
return newValidator;
|
|
241
|
+
}
|
|
242
|
+
positive(message) {
|
|
243
|
+
const newValidator = new VldNumber();
|
|
244
|
+
newValidator.checks = [...this.checks, (v) => v > 0];
|
|
245
|
+
newValidator.errorMessage = message || getMessages().numberPositive;
|
|
246
|
+
return newValidator;
|
|
247
|
+
}
|
|
248
|
+
negative(message) {
|
|
249
|
+
const newValidator = new VldNumber();
|
|
250
|
+
newValidator.checks = [...this.checks, (v) => v < 0];
|
|
251
|
+
newValidator.errorMessage = message || getMessages().numberNegative;
|
|
252
|
+
return newValidator;
|
|
253
|
+
}
|
|
254
|
+
nonnegative(message) {
|
|
255
|
+
const newValidator = new VldNumber();
|
|
256
|
+
newValidator.checks = [...this.checks, (v) => v >= 0];
|
|
257
|
+
newValidator.errorMessage = message || getMessages().numberNonnegative;
|
|
258
|
+
return newValidator;
|
|
259
|
+
}
|
|
260
|
+
nonpositive(message) {
|
|
261
|
+
const newValidator = new VldNumber();
|
|
262
|
+
newValidator.checks = [...this.checks, (v) => v <= 0];
|
|
263
|
+
newValidator.errorMessage = message || getMessages().numberNonpositive;
|
|
264
|
+
return newValidator;
|
|
265
|
+
}
|
|
266
|
+
finite(message) {
|
|
267
|
+
const newValidator = new VldNumber();
|
|
268
|
+
newValidator.checks = [...this.checks, (v) => Number.isFinite(v)];
|
|
269
|
+
newValidator.errorMessage = message || getMessages().numberFinite;
|
|
270
|
+
return newValidator;
|
|
271
|
+
}
|
|
272
|
+
safe(message) {
|
|
273
|
+
const newValidator = new VldNumber();
|
|
274
|
+
newValidator.checks = [...this.checks, (v) => Number.isSafeInteger(v)];
|
|
275
|
+
newValidator.errorMessage = message || getMessages().numberSafe;
|
|
276
|
+
return newValidator;
|
|
277
|
+
}
|
|
278
|
+
multipleOf(value, message) {
|
|
279
|
+
const newValidator = new VldNumber();
|
|
280
|
+
newValidator.checks = [...this.checks, (v) => v % value === 0];
|
|
281
|
+
newValidator.errorMessage = message || getMessages().numberMultipleOf(value);
|
|
282
|
+
return newValidator;
|
|
283
|
+
}
|
|
284
|
+
step(value, message) {
|
|
285
|
+
return this.multipleOf(value, message);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
// Boolean validator
|
|
289
|
+
export class VldBoolean extends VldBase {
|
|
290
|
+
constructor() {
|
|
291
|
+
super(...arguments);
|
|
292
|
+
this.errorMessage = getMessages().invalidBoolean;
|
|
293
|
+
}
|
|
294
|
+
parse(value) {
|
|
295
|
+
if (typeof value !== 'boolean') {
|
|
296
|
+
throw new Error(this.errorMessage || getMessages().invalidBoolean);
|
|
297
|
+
}
|
|
298
|
+
return value;
|
|
299
|
+
}
|
|
300
|
+
safeParse(value) {
|
|
301
|
+
if (typeof value === 'boolean') {
|
|
302
|
+
return { success: true, data: value };
|
|
303
|
+
}
|
|
304
|
+
return { success: false, error: new Error(this.errorMessage) };
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
// Array validator
|
|
308
|
+
export class VldArray extends VldBase {
|
|
309
|
+
constructor(itemValidator) {
|
|
310
|
+
super();
|
|
311
|
+
this.itemValidator = itemValidator;
|
|
312
|
+
}
|
|
313
|
+
parse(value) {
|
|
314
|
+
if (!Array.isArray(value)) {
|
|
315
|
+
throw new Error(getMessages().invalidArray);
|
|
316
|
+
}
|
|
317
|
+
return value.map((item, index) => {
|
|
318
|
+
try {
|
|
319
|
+
return this.itemValidator.parse(item);
|
|
320
|
+
}
|
|
321
|
+
catch (error) {
|
|
322
|
+
throw new Error(getMessages().arrayItem(index, error.message));
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
safeParse(value) {
|
|
327
|
+
try {
|
|
328
|
+
return { success: true, data: this.parse(value) };
|
|
329
|
+
}
|
|
330
|
+
catch (error) {
|
|
331
|
+
return { success: false, error: error };
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
min(length, message) {
|
|
335
|
+
const originalParse = this.parse.bind(this);
|
|
336
|
+
this.parse = (value) => {
|
|
337
|
+
const arr = originalParse(value);
|
|
338
|
+
if (arr.length < length) {
|
|
339
|
+
throw new Error(message || getMessages().arrayMin(length));
|
|
340
|
+
}
|
|
341
|
+
return arr;
|
|
342
|
+
};
|
|
343
|
+
return this;
|
|
344
|
+
}
|
|
345
|
+
max(length, message) {
|
|
346
|
+
const originalParse = this.parse.bind(this);
|
|
347
|
+
this.parse = (value) => {
|
|
348
|
+
const arr = originalParse(value);
|
|
349
|
+
if (arr.length > length) {
|
|
350
|
+
throw new Error(message || getMessages().arrayMax(length));
|
|
351
|
+
}
|
|
352
|
+
return arr;
|
|
353
|
+
};
|
|
354
|
+
return this;
|
|
355
|
+
}
|
|
356
|
+
length(length, message) {
|
|
357
|
+
const originalParse = this.parse.bind(this);
|
|
358
|
+
this.parse = (value) => {
|
|
359
|
+
const arr = originalParse(value);
|
|
360
|
+
if (arr.length !== length) {
|
|
361
|
+
throw new Error(message || getMessages().arrayLength(length));
|
|
362
|
+
}
|
|
363
|
+
return arr;
|
|
364
|
+
};
|
|
365
|
+
return this;
|
|
366
|
+
}
|
|
367
|
+
nonempty(message) {
|
|
368
|
+
return this.min(1, message || getMessages().arrayEmpty);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
// Object validator
|
|
372
|
+
export class VldObject extends VldBase {
|
|
373
|
+
constructor(shape) {
|
|
374
|
+
super();
|
|
375
|
+
this.shape = shape;
|
|
376
|
+
}
|
|
377
|
+
parse(value) {
|
|
378
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
379
|
+
throw new Error(getMessages().invalidObject);
|
|
380
|
+
}
|
|
381
|
+
const result = {};
|
|
382
|
+
const obj = value;
|
|
383
|
+
for (const [key, validator] of Object.entries(this.shape)) {
|
|
384
|
+
try {
|
|
385
|
+
result[key] = validator.parse(obj[key]);
|
|
386
|
+
}
|
|
387
|
+
catch (error) {
|
|
388
|
+
throw new Error(getMessages().objectField(key, error.message));
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return result;
|
|
392
|
+
}
|
|
393
|
+
safeParse(value) {
|
|
394
|
+
try {
|
|
395
|
+
return { success: true, data: this.parse(value) };
|
|
396
|
+
}
|
|
397
|
+
catch (error) {
|
|
398
|
+
return { success: false, error: error };
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
partial() {
|
|
402
|
+
const partialShape = {};
|
|
403
|
+
for (const [key, validator] of Object.entries(this.shape)) {
|
|
404
|
+
partialShape[key] = new VldOptional(validator);
|
|
405
|
+
}
|
|
406
|
+
return new VldObject(partialShape);
|
|
407
|
+
}
|
|
408
|
+
strict() {
|
|
409
|
+
const originalParse = this.parse.bind(this);
|
|
410
|
+
this.parse = (value) => {
|
|
411
|
+
const result = originalParse(value);
|
|
412
|
+
const obj = value;
|
|
413
|
+
const extraKeys = Object.keys(obj).filter(key => !(key in this.shape));
|
|
414
|
+
if (extraKeys.length > 0) {
|
|
415
|
+
throw new Error(getMessages().unexpectedKeys(extraKeys));
|
|
416
|
+
}
|
|
417
|
+
return result;
|
|
418
|
+
};
|
|
419
|
+
return this;
|
|
420
|
+
}
|
|
421
|
+
pick(...keys) {
|
|
422
|
+
const pickedShape = {};
|
|
423
|
+
for (const key of keys) {
|
|
424
|
+
if (key in this.shape) {
|
|
425
|
+
pickedShape[key] = this.shape[key];
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
return new VldObject(pickedShape);
|
|
429
|
+
}
|
|
430
|
+
omit(...keys) {
|
|
431
|
+
const omittedShape = {};
|
|
432
|
+
for (const [key, validator] of Object.entries(this.shape)) {
|
|
433
|
+
if (!keys.includes(key)) {
|
|
434
|
+
omittedShape[key] = validator;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
return new VldObject(omittedShape);
|
|
438
|
+
}
|
|
439
|
+
extend(extension) {
|
|
440
|
+
const extendedShape = { ...this.shape };
|
|
441
|
+
for (const [key, validator] of Object.entries(extension)) {
|
|
442
|
+
extendedShape[key] = validator;
|
|
443
|
+
}
|
|
444
|
+
return new VldObject(extendedShape);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
// Optional validator
|
|
448
|
+
export class VldOptional extends VldBase {
|
|
449
|
+
constructor(validator) {
|
|
450
|
+
super();
|
|
451
|
+
this.validator = validator;
|
|
452
|
+
}
|
|
453
|
+
parse(value) {
|
|
454
|
+
if (value === undefined) {
|
|
455
|
+
return undefined;
|
|
456
|
+
}
|
|
457
|
+
return this.validator.parse(value);
|
|
458
|
+
}
|
|
459
|
+
safeParse(value) {
|
|
460
|
+
if (value === undefined) {
|
|
461
|
+
return { success: true, data: undefined };
|
|
462
|
+
}
|
|
463
|
+
return this.validator.safeParse(value);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
// Nullable validator
|
|
467
|
+
export class VldNullable extends VldBase {
|
|
468
|
+
constructor(validator) {
|
|
469
|
+
super();
|
|
470
|
+
this.validator = validator;
|
|
471
|
+
}
|
|
472
|
+
parse(value) {
|
|
473
|
+
if (value === null) {
|
|
474
|
+
return null;
|
|
475
|
+
}
|
|
476
|
+
return this.validator.parse(value);
|
|
477
|
+
}
|
|
478
|
+
safeParse(value) {
|
|
479
|
+
if (value === null) {
|
|
480
|
+
return { success: true, data: null };
|
|
481
|
+
}
|
|
482
|
+
return this.validator.safeParse(value);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
// Union validator
|
|
486
|
+
export class VldUnion extends VldBase {
|
|
487
|
+
constructor(validators) {
|
|
488
|
+
super();
|
|
489
|
+
this.validators = validators;
|
|
490
|
+
}
|
|
491
|
+
parse(value) {
|
|
492
|
+
const errors = [];
|
|
493
|
+
for (const validator of this.validators) {
|
|
494
|
+
try {
|
|
495
|
+
return validator.parse(value);
|
|
496
|
+
}
|
|
497
|
+
catch (error) {
|
|
498
|
+
errors.push(error);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
throw new Error(getMessages().unionNoMatch(errors.map(e => e.message)));
|
|
502
|
+
}
|
|
503
|
+
safeParse(value) {
|
|
504
|
+
try {
|
|
505
|
+
return { success: true, data: this.parse(value) };
|
|
506
|
+
}
|
|
507
|
+
catch (error) {
|
|
508
|
+
return { success: false, error: error };
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
// Intersection validator
|
|
513
|
+
export class VldIntersection extends VldBase {
|
|
514
|
+
constructor(validatorA, validatorB) {
|
|
515
|
+
super();
|
|
516
|
+
this.validatorA = validatorA;
|
|
517
|
+
this.validatorB = validatorB;
|
|
518
|
+
}
|
|
519
|
+
parse(value) {
|
|
520
|
+
try {
|
|
521
|
+
// Both validators must pass
|
|
522
|
+
const resultA = this.validatorA.parse(value);
|
|
523
|
+
const resultB = this.validatorB.parse(value);
|
|
524
|
+
// For object types, deep merge the results
|
|
525
|
+
if (typeof resultA === 'object' && resultA !== null &&
|
|
526
|
+
typeof resultB === 'object' && resultB !== null &&
|
|
527
|
+
!Array.isArray(resultA) && !Array.isArray(resultB)) {
|
|
528
|
+
return deepMerge(resultA, resultB);
|
|
529
|
+
}
|
|
530
|
+
// For primitive types, both must be the same value
|
|
531
|
+
if (resultA === resultB) {
|
|
532
|
+
return resultA;
|
|
533
|
+
}
|
|
534
|
+
// If they're different primitive values, this is an error
|
|
535
|
+
throw new Error(getMessages().intersectionError('Values must be identical for intersection of primitive types'));
|
|
536
|
+
}
|
|
537
|
+
catch (error) {
|
|
538
|
+
throw new Error(getMessages().intersectionError(error.message));
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
safeParse(value) {
|
|
542
|
+
try {
|
|
543
|
+
return { success: true, data: this.parse(value) };
|
|
544
|
+
}
|
|
545
|
+
catch (error) {
|
|
546
|
+
return { success: false, error: error };
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
// Literal validator
|
|
551
|
+
export class VldLiteral extends VldBase {
|
|
552
|
+
constructor(literal) {
|
|
553
|
+
super();
|
|
554
|
+
this.literal = literal;
|
|
555
|
+
}
|
|
556
|
+
parse(value) {
|
|
557
|
+
if (value !== this.literal) {
|
|
558
|
+
throw new Error(getMessages().literalExpected(JSON.stringify(this.literal), JSON.stringify(value)));
|
|
559
|
+
}
|
|
560
|
+
return this.literal;
|
|
561
|
+
}
|
|
562
|
+
safeParse(value) {
|
|
563
|
+
if (value === this.literal) {
|
|
564
|
+
return { success: true, data: this.literal };
|
|
565
|
+
}
|
|
566
|
+
return { success: false, error: new Error(getMessages().literalExpected(JSON.stringify(this.literal), 'received')) };
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
// Enum validator
|
|
570
|
+
export class VldEnum extends VldBase {
|
|
571
|
+
constructor(values) {
|
|
572
|
+
super();
|
|
573
|
+
this.values = values;
|
|
574
|
+
}
|
|
575
|
+
parse(value) {
|
|
576
|
+
if (!this.values.includes(value)) {
|
|
577
|
+
throw new Error(getMessages().enumExpected([...this.values], JSON.stringify(value)));
|
|
578
|
+
}
|
|
579
|
+
return value;
|
|
580
|
+
}
|
|
581
|
+
safeParse(value) {
|
|
582
|
+
if (this.values.includes(value)) {
|
|
583
|
+
return { success: true, data: value };
|
|
584
|
+
}
|
|
585
|
+
return { success: false, error: new Error(getMessages().enumExpected([...this.values], 'received')) };
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
// Date validator
|
|
589
|
+
export class VldDate extends VldBase {
|
|
590
|
+
constructor() {
|
|
591
|
+
super(...arguments);
|
|
592
|
+
this.checks = [];
|
|
593
|
+
this.errorMessage = getMessages().invalidDate;
|
|
594
|
+
}
|
|
595
|
+
parse(value) {
|
|
596
|
+
let date;
|
|
597
|
+
if (value instanceof Date) {
|
|
598
|
+
date = value;
|
|
599
|
+
}
|
|
600
|
+
else if (typeof value === 'string' || typeof value === 'number') {
|
|
601
|
+
date = new Date(value);
|
|
602
|
+
}
|
|
603
|
+
else {
|
|
604
|
+
throw new Error(this.errorMessage || getMessages().invalidDate);
|
|
605
|
+
}
|
|
606
|
+
if (isNaN(date.getTime())) {
|
|
607
|
+
throw new Error(this.errorMessage || getMessages().invalidDate);
|
|
608
|
+
}
|
|
609
|
+
for (const check of this.checks) {
|
|
610
|
+
if (!check(date)) {
|
|
611
|
+
throw new Error(this.errorMessage || getMessages().invalidDate);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
return date;
|
|
615
|
+
}
|
|
616
|
+
safeParse(value) {
|
|
617
|
+
try {
|
|
618
|
+
return { success: true, data: this.parse(value) };
|
|
619
|
+
}
|
|
620
|
+
catch (error) {
|
|
621
|
+
return { success: false, error: error };
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
min(date, message) {
|
|
625
|
+
const newValidator = new VldDate();
|
|
626
|
+
newValidator.checks = [...this.checks, (v) => v >= date];
|
|
627
|
+
newValidator.errorMessage = message || getMessages().dateMin(date);
|
|
628
|
+
return newValidator;
|
|
629
|
+
}
|
|
630
|
+
max(date, message) {
|
|
631
|
+
const newValidator = new VldDate();
|
|
632
|
+
newValidator.checks = [...this.checks, (v) => v <= date];
|
|
633
|
+
newValidator.errorMessage = message || getMessages().dateMax(date);
|
|
634
|
+
return newValidator;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
// Any validator
|
|
638
|
+
export class VldAny extends VldBase {
|
|
639
|
+
parse(value) {
|
|
640
|
+
return value;
|
|
641
|
+
}
|
|
642
|
+
safeParse(value) {
|
|
643
|
+
return { success: true, data: value };
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
// Unknown validator
|
|
647
|
+
export class VldUnknown extends VldBase {
|
|
648
|
+
parse(value) {
|
|
649
|
+
return value;
|
|
650
|
+
}
|
|
651
|
+
safeParse(value) {
|
|
652
|
+
return { success: true, data: value };
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
// Void validator
|
|
656
|
+
export class VldVoid extends VldBase {
|
|
657
|
+
parse(value) {
|
|
658
|
+
if (value !== undefined) {
|
|
659
|
+
throw new Error(getMessages().expectedUndefined);
|
|
660
|
+
}
|
|
661
|
+
return undefined;
|
|
662
|
+
}
|
|
663
|
+
safeParse(value) {
|
|
664
|
+
if (value === undefined) {
|
|
665
|
+
return { success: true, data: undefined };
|
|
666
|
+
}
|
|
667
|
+
return { success: false, error: new Error(getMessages().expectedUndefined) };
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
// Never validator
|
|
671
|
+
export class VldNever extends VldBase {
|
|
672
|
+
parse(_value) {
|
|
673
|
+
throw new Error(getMessages().neverType);
|
|
674
|
+
}
|
|
675
|
+
safeParse(_value) {
|
|
676
|
+
return { success: false, error: new Error(getMessages().neverType) };
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
// BigInt validator
|
|
680
|
+
export class VldBigInt extends VldBase {
|
|
681
|
+
constructor() {
|
|
682
|
+
super(...arguments);
|
|
683
|
+
this.checks = [];
|
|
684
|
+
this.errorMessage = getMessages().invalidBigint;
|
|
685
|
+
}
|
|
686
|
+
parse(value) {
|
|
687
|
+
if (typeof value !== 'bigint') {
|
|
688
|
+
throw new Error(this.errorMessage || getMessages().invalidBigint);
|
|
689
|
+
}
|
|
690
|
+
for (const check of this.checks) {
|
|
691
|
+
if (!check(value)) {
|
|
692
|
+
throw new Error(this.errorMessage || getMessages().invalidBigint);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
return value;
|
|
696
|
+
}
|
|
697
|
+
safeParse(value) {
|
|
698
|
+
try {
|
|
699
|
+
return { success: true, data: this.parse(value) };
|
|
700
|
+
}
|
|
701
|
+
catch (error) {
|
|
702
|
+
return { success: false, error: error };
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
// Symbol validator
|
|
707
|
+
export class VldSymbol extends VldBase {
|
|
708
|
+
constructor() {
|
|
709
|
+
super(...arguments);
|
|
710
|
+
this.errorMessage = getMessages().invalidSymbol;
|
|
711
|
+
}
|
|
712
|
+
parse(value) {
|
|
713
|
+
if (typeof value !== 'symbol') {
|
|
714
|
+
throw new Error(this.errorMessage || getMessages().invalidSymbol);
|
|
715
|
+
}
|
|
716
|
+
return value;
|
|
717
|
+
}
|
|
718
|
+
safeParse(value) {
|
|
719
|
+
if (typeof value === 'symbol') {
|
|
720
|
+
return { success: true, data: value };
|
|
721
|
+
}
|
|
722
|
+
return { success: false, error: new Error(this.errorMessage) };
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
// Tuple validator
|
|
726
|
+
export class VldTuple extends VldBase {
|
|
727
|
+
constructor(validators) {
|
|
728
|
+
super();
|
|
729
|
+
this.validators = validators;
|
|
730
|
+
}
|
|
731
|
+
parse(value) {
|
|
732
|
+
if (!Array.isArray(value)) {
|
|
733
|
+
throw new Error(getMessages().invalidTuple);
|
|
734
|
+
}
|
|
735
|
+
if (value.length !== this.validators.length) {
|
|
736
|
+
throw new Error(getMessages().tupleLength(this.validators.length, value.length));
|
|
737
|
+
}
|
|
738
|
+
const result = [];
|
|
739
|
+
for (let i = 0; i < this.validators.length; i++) {
|
|
740
|
+
try {
|
|
741
|
+
result[i] = this.validators[i].parse(value[i]);
|
|
742
|
+
}
|
|
743
|
+
catch (error) {
|
|
744
|
+
throw new Error(getMessages().arrayItem(i, error.message));
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
return result;
|
|
748
|
+
}
|
|
749
|
+
safeParse(value) {
|
|
750
|
+
try {
|
|
751
|
+
return { success: true, data: this.parse(value) };
|
|
752
|
+
}
|
|
753
|
+
catch (error) {
|
|
754
|
+
return { success: false, error: error };
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
// Record validator
|
|
759
|
+
export class VldRecord extends VldBase {
|
|
760
|
+
constructor(valueValidator) {
|
|
761
|
+
super();
|
|
762
|
+
this.valueValidator = valueValidator;
|
|
763
|
+
}
|
|
764
|
+
parse(value) {
|
|
765
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
766
|
+
throw new Error(getMessages().invalidRecord);
|
|
767
|
+
}
|
|
768
|
+
const result = {};
|
|
769
|
+
const obj = value;
|
|
770
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
771
|
+
try {
|
|
772
|
+
result[key] = this.valueValidator.parse(val);
|
|
773
|
+
}
|
|
774
|
+
catch (error) {
|
|
775
|
+
throw new Error(getMessages().objectField(key, error.message));
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
return result;
|
|
779
|
+
}
|
|
780
|
+
safeParse(value) {
|
|
781
|
+
try {
|
|
782
|
+
return { success: true, data: this.parse(value) };
|
|
783
|
+
}
|
|
784
|
+
catch (error) {
|
|
785
|
+
return { success: false, error: error };
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
// Set validator
|
|
790
|
+
export class VldSet extends VldBase {
|
|
791
|
+
constructor(itemValidator) {
|
|
792
|
+
super();
|
|
793
|
+
this.itemValidator = itemValidator;
|
|
794
|
+
}
|
|
795
|
+
parse(value) {
|
|
796
|
+
if (!(value instanceof Set)) {
|
|
797
|
+
throw new Error(getMessages().invalidSet);
|
|
798
|
+
}
|
|
799
|
+
const result = new Set();
|
|
800
|
+
for (const item of value) {
|
|
801
|
+
try {
|
|
802
|
+
result.add(this.itemValidator.parse(item));
|
|
803
|
+
}
|
|
804
|
+
catch (error) {
|
|
805
|
+
throw new Error(error.message);
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
return result;
|
|
809
|
+
}
|
|
810
|
+
safeParse(value) {
|
|
811
|
+
try {
|
|
812
|
+
return { success: true, data: this.parse(value) };
|
|
813
|
+
}
|
|
814
|
+
catch (error) {
|
|
815
|
+
return { success: false, error: error };
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
// Map validator
|
|
820
|
+
export class VldMap extends VldBase {
|
|
821
|
+
constructor(keyValidator, valueValidator) {
|
|
822
|
+
super();
|
|
823
|
+
this.keyValidator = keyValidator;
|
|
824
|
+
this.valueValidator = valueValidator;
|
|
825
|
+
}
|
|
826
|
+
parse(value) {
|
|
827
|
+
if (!(value instanceof Map)) {
|
|
828
|
+
throw new Error(getMessages().invalidMap);
|
|
829
|
+
}
|
|
830
|
+
const result = new Map();
|
|
831
|
+
for (const [key, val] of value) {
|
|
832
|
+
try {
|
|
833
|
+
const validKey = this.keyValidator.parse(key);
|
|
834
|
+
const validValue = this.valueValidator.parse(val);
|
|
835
|
+
result.set(validKey, validValue);
|
|
836
|
+
}
|
|
837
|
+
catch (error) {
|
|
838
|
+
throw new Error(error.message);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
return result;
|
|
842
|
+
}
|
|
843
|
+
safeParse(value) {
|
|
844
|
+
try {
|
|
845
|
+
return { success: true, data: this.parse(value) };
|
|
846
|
+
}
|
|
847
|
+
catch (error) {
|
|
848
|
+
return { success: false, error: error };
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
// Coercion classes
|
|
853
|
+
export class VldCoerceString extends VldString {
|
|
854
|
+
parse(value) {
|
|
855
|
+
try {
|
|
856
|
+
// Attempt coercion to string
|
|
857
|
+
if (value === null || value === undefined) {
|
|
858
|
+
throw new Error(getMessages().coercionFailed('string', value));
|
|
859
|
+
}
|
|
860
|
+
const coerced = String(value);
|
|
861
|
+
// Call parent parse with coerced value
|
|
862
|
+
return super.parse(coerced);
|
|
863
|
+
}
|
|
864
|
+
catch (error) {
|
|
865
|
+
const errorMessage = error.message;
|
|
866
|
+
// If it's a coercion error, re-throw as is
|
|
867
|
+
if (errorMessage.includes('Cannot coerce')) {
|
|
868
|
+
throw error;
|
|
869
|
+
}
|
|
870
|
+
// If it's a validation error from parent, re-throw as is
|
|
871
|
+
if (errorMessage.includes('String must') || errorMessage.includes('Invalid')) {
|
|
872
|
+
throw error;
|
|
873
|
+
}
|
|
874
|
+
// Otherwise, it's a coercion failure
|
|
875
|
+
throw new Error(getMessages().coercionFailed('string', value));
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
safeParse(value) {
|
|
879
|
+
try {
|
|
880
|
+
return { success: true, data: this.parse(value) };
|
|
881
|
+
}
|
|
882
|
+
catch (error) {
|
|
883
|
+
return { success: false, error: error };
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
export class VldCoerceNumber extends VldNumber {
|
|
888
|
+
parse(value) {
|
|
889
|
+
try {
|
|
890
|
+
// Attempt coercion to number
|
|
891
|
+
if (typeof value === 'string') {
|
|
892
|
+
const trimmed = value.trim();
|
|
893
|
+
if (trimmed === '') {
|
|
894
|
+
throw new Error(getMessages().coercionFailed('number', value));
|
|
895
|
+
}
|
|
896
|
+
const coerced = Number(trimmed);
|
|
897
|
+
if (isNaN(coerced)) {
|
|
898
|
+
throw new Error(getMessages().coercionFailed('number', value));
|
|
899
|
+
}
|
|
900
|
+
return super.parse(coerced);
|
|
901
|
+
}
|
|
902
|
+
else if (typeof value === 'boolean') {
|
|
903
|
+
return super.parse(value ? 1 : 0);
|
|
904
|
+
}
|
|
905
|
+
else if (value === null || value === undefined) {
|
|
906
|
+
throw new Error(getMessages().coercionFailed('number', value));
|
|
907
|
+
}
|
|
908
|
+
return super.parse(Number(value));
|
|
909
|
+
}
|
|
910
|
+
catch (error) {
|
|
911
|
+
if (error.message.includes('coercionFailed')) {
|
|
912
|
+
throw error;
|
|
913
|
+
}
|
|
914
|
+
throw new Error(getMessages().coercionFailed('number', value));
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
safeParse(value) {
|
|
918
|
+
try {
|
|
919
|
+
return { success: true, data: this.parse(value) };
|
|
920
|
+
}
|
|
921
|
+
catch (error) {
|
|
922
|
+
return { success: false, error: error };
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
export class VldCoerceBoolean extends VldBoolean {
|
|
927
|
+
parse(value) {
|
|
928
|
+
try {
|
|
929
|
+
// Attempt coercion to boolean
|
|
930
|
+
if (typeof value === 'string') {
|
|
931
|
+
const lower = value.toLowerCase().trim();
|
|
932
|
+
if (lower === 'true' || lower === '1' || lower === 'yes')
|
|
933
|
+
return true;
|
|
934
|
+
if (lower === 'false' || lower === '0' || lower === 'no')
|
|
935
|
+
return false;
|
|
936
|
+
throw new Error(getMessages().coercionFailed('boolean', value));
|
|
937
|
+
}
|
|
938
|
+
else if (typeof value === 'number') {
|
|
939
|
+
if (value === 1)
|
|
940
|
+
return true;
|
|
941
|
+
if (value === 0)
|
|
942
|
+
return false;
|
|
943
|
+
throw new Error(getMessages().coercionFailed('boolean', value));
|
|
944
|
+
}
|
|
945
|
+
else if (value === null || value === undefined) {
|
|
946
|
+
throw new Error(getMessages().coercionFailed('boolean', value));
|
|
947
|
+
}
|
|
948
|
+
return super.parse(Boolean(value));
|
|
949
|
+
}
|
|
950
|
+
catch (error) {
|
|
951
|
+
if (error.message.includes('coercionFailed')) {
|
|
952
|
+
throw error;
|
|
953
|
+
}
|
|
954
|
+
throw new Error(getMessages().coercionFailed('boolean', value));
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
safeParse(value) {
|
|
958
|
+
try {
|
|
959
|
+
return { success: true, data: this.parse(value) };
|
|
960
|
+
}
|
|
961
|
+
catch (error) {
|
|
962
|
+
return { success: false, error: error };
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
export class VldCoerceBigInt extends VldBigInt {
|
|
967
|
+
parse(value) {
|
|
968
|
+
try {
|
|
969
|
+
// Attempt coercion to bigint
|
|
970
|
+
if (typeof value === 'string') {
|
|
971
|
+
const trimmed = value.trim();
|
|
972
|
+
if (trimmed === '') {
|
|
973
|
+
throw new Error(getMessages().coercionFailed('bigint', value));
|
|
974
|
+
}
|
|
975
|
+
return super.parse(BigInt(trimmed));
|
|
976
|
+
}
|
|
977
|
+
else if (typeof value === 'number') {
|
|
978
|
+
if (!Number.isInteger(value)) {
|
|
979
|
+
throw new Error(getMessages().coercionFailed('bigint', value));
|
|
980
|
+
}
|
|
981
|
+
return super.parse(BigInt(value));
|
|
982
|
+
}
|
|
983
|
+
else if (value === null || value === undefined) {
|
|
984
|
+
throw new Error(getMessages().coercionFailed('bigint', value));
|
|
985
|
+
}
|
|
986
|
+
return super.parse(BigInt(value));
|
|
987
|
+
}
|
|
988
|
+
catch (error) {
|
|
989
|
+
if (error.message.includes('coercionFailed')) {
|
|
990
|
+
throw error;
|
|
991
|
+
}
|
|
992
|
+
throw new Error(getMessages().coercionFailed('bigint', value));
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
safeParse(value) {
|
|
996
|
+
try {
|
|
997
|
+
return { success: true, data: this.parse(value) };
|
|
998
|
+
}
|
|
999
|
+
catch (error) {
|
|
1000
|
+
return { success: false, error: error };
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
export class VldCoerceDate extends VldDate {
|
|
1005
|
+
parse(value) {
|
|
1006
|
+
try {
|
|
1007
|
+
// Attempt coercion to date
|
|
1008
|
+
if (typeof value === 'string' || typeof value === 'number') {
|
|
1009
|
+
return super.parse(new Date(value));
|
|
1010
|
+
}
|
|
1011
|
+
else if (value === null || value === undefined) {
|
|
1012
|
+
throw new Error(getMessages().coercionFailed('date', value));
|
|
1013
|
+
}
|
|
1014
|
+
return super.parse(value);
|
|
1015
|
+
}
|
|
1016
|
+
catch (error) {
|
|
1017
|
+
if (error.message.includes('coercionFailed')) {
|
|
1018
|
+
throw error;
|
|
1019
|
+
}
|
|
1020
|
+
throw new Error(getMessages().coercionFailed('date', value));
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
safeParse(value) {
|
|
1024
|
+
try {
|
|
1025
|
+
return { success: true, data: this.parse(value) };
|
|
1026
|
+
}
|
|
1027
|
+
catch (error) {
|
|
1028
|
+
return { success: false, error: error };
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
// Refine validator - adds custom validation
|
|
1033
|
+
export class VldRefine extends VldBase {
|
|
1034
|
+
constructor(baseValidator, predicate, customMessage) {
|
|
1035
|
+
super();
|
|
1036
|
+
this.baseValidator = baseValidator;
|
|
1037
|
+
this.predicate = predicate;
|
|
1038
|
+
this.customMessage = customMessage;
|
|
1039
|
+
}
|
|
1040
|
+
parse(value) {
|
|
1041
|
+
const baseResult = this.baseValidator.parse(value);
|
|
1042
|
+
try {
|
|
1043
|
+
if (!this.predicate(baseResult)) {
|
|
1044
|
+
throw new Error(this.customMessage || 'Refinement condition not met');
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
catch (error) {
|
|
1048
|
+
throw new Error(getMessages().customValidationError(error.message));
|
|
1049
|
+
}
|
|
1050
|
+
return baseResult;
|
|
1051
|
+
}
|
|
1052
|
+
safeParse(value) {
|
|
1053
|
+
try {
|
|
1054
|
+
return { success: true, data: this.parse(value) };
|
|
1055
|
+
}
|
|
1056
|
+
catch (error) {
|
|
1057
|
+
return { success: false, error: error };
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
// Transform validator - transforms data after validation
|
|
1062
|
+
export class VldTransform extends VldBase {
|
|
1063
|
+
constructor(baseValidator, transformer) {
|
|
1064
|
+
super();
|
|
1065
|
+
this.baseValidator = baseValidator;
|
|
1066
|
+
this.transformer = transformer;
|
|
1067
|
+
}
|
|
1068
|
+
parse(value) {
|
|
1069
|
+
const baseResult = this.baseValidator.parse(value);
|
|
1070
|
+
try {
|
|
1071
|
+
return this.transformer(baseResult);
|
|
1072
|
+
}
|
|
1073
|
+
catch (error) {
|
|
1074
|
+
throw new Error(getMessages().transformError(error.message));
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
safeParse(value) {
|
|
1078
|
+
try {
|
|
1079
|
+
return { success: true, data: this.parse(value) };
|
|
1080
|
+
}
|
|
1081
|
+
catch (error) {
|
|
1082
|
+
return { success: false, error: error };
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
// Default validator - provides default value for undefined
|
|
1087
|
+
export class VldDefault extends VldBase {
|
|
1088
|
+
constructor(baseValidator, defaultValue) {
|
|
1089
|
+
super();
|
|
1090
|
+
this.baseValidator = baseValidator;
|
|
1091
|
+
this.defaultValue = defaultValue;
|
|
1092
|
+
}
|
|
1093
|
+
parse(value) {
|
|
1094
|
+
if (value === undefined) {
|
|
1095
|
+
return this.defaultValue;
|
|
1096
|
+
}
|
|
1097
|
+
return this.baseValidator.parse(value);
|
|
1098
|
+
}
|
|
1099
|
+
safeParse(value) {
|
|
1100
|
+
if (value === undefined) {
|
|
1101
|
+
return { success: true, data: this.defaultValue };
|
|
1102
|
+
}
|
|
1103
|
+
return this.baseValidator.safeParse(value);
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
// Catch validator - provides fallback value on validation error
|
|
1107
|
+
export class VldCatch extends VldBase {
|
|
1108
|
+
constructor(baseValidator, fallbackValue) {
|
|
1109
|
+
super();
|
|
1110
|
+
this.baseValidator = baseValidator;
|
|
1111
|
+
this.fallbackValue = fallbackValue;
|
|
1112
|
+
}
|
|
1113
|
+
parse(value) {
|
|
1114
|
+
try {
|
|
1115
|
+
return this.baseValidator.parse(value);
|
|
1116
|
+
}
|
|
1117
|
+
catch {
|
|
1118
|
+
return this.fallbackValue;
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
safeParse(value) {
|
|
1122
|
+
const result = this.baseValidator.safeParse(value);
|
|
1123
|
+
if (result.success) {
|
|
1124
|
+
return result;
|
|
1125
|
+
}
|
|
1126
|
+
else {
|
|
1127
|
+
return { success: true, data: this.fallbackValue };
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
// Main API
|
|
1132
|
+
export const v = {
|
|
1133
|
+
string: () => new VldString(),
|
|
1134
|
+
number: () => new VldNumber(),
|
|
1135
|
+
boolean: () => new VldBoolean(),
|
|
1136
|
+
bigint: () => new VldBigInt(),
|
|
1137
|
+
symbol: () => new VldSymbol(),
|
|
1138
|
+
array: (item) => new VldArray(item),
|
|
1139
|
+
tuple: (...validators) => new VldTuple(validators),
|
|
1140
|
+
object: (shape) => new VldObject(shape),
|
|
1141
|
+
record: (valueValidator) => new VldRecord(valueValidator),
|
|
1142
|
+
set: (item) => new VldSet(item),
|
|
1143
|
+
map: (key, value) => new VldMap(key, value),
|
|
1144
|
+
optional: (validator) => new VldOptional(validator),
|
|
1145
|
+
nullable: (validator) => new VldNullable(validator),
|
|
1146
|
+
union: (...validators) => new VldUnion(validators),
|
|
1147
|
+
intersection: (a, b) => new VldIntersection(a, b),
|
|
1148
|
+
literal: (value) => new VldLiteral(value),
|
|
1149
|
+
enum: (...values) => new VldEnum(values),
|
|
1150
|
+
date: () => new VldDate(),
|
|
1151
|
+
any: () => new VldAny(),
|
|
1152
|
+
unknown: () => new VldUnknown(),
|
|
1153
|
+
void: () => new VldVoid(),
|
|
1154
|
+
never: () => new VldNever(),
|
|
1155
|
+
// Coercion API
|
|
1156
|
+
coerce: {
|
|
1157
|
+
string: () => new VldCoerceString(),
|
|
1158
|
+
number: () => new VldCoerceNumber(),
|
|
1159
|
+
boolean: () => new VldCoerceBoolean(),
|
|
1160
|
+
bigint: () => new VldCoerceBigInt(),
|
|
1161
|
+
date: () => new VldCoerceDate(),
|
|
1162
|
+
}
|
|
1163
|
+
};
|
|
1164
|
+
// Export all validators for direct use
|
|
1165
|
+
export { VldBase, };
|
|
1166
|
+
// Export locale functionality
|
|
1167
|
+
export { setLocale, getLocale, getMessages } from './locales/index.js';
|
|
1168
|
+
// Export error formatting utilities
|
|
1169
|
+
export { VldError, treeifyError, prettifyError, flattenError } from './errors.js';
|
|
1170
|
+
//# sourceMappingURL=index.js.map
|