zod-validate 1.0.0 → 1.1.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/README.md +664 -238
- package/package.json +25 -12
package/README.md
CHANGED
|
@@ -8,8 +8,9 @@ Reusable validation utilities built on top of [Zod](https://zod.dev/) for fronte
|
|
|
8
8
|
- Individual field validation
|
|
9
9
|
- Nested object validation
|
|
10
10
|
- Nested array validation
|
|
11
|
-
-
|
|
12
|
-
-
|
|
11
|
+
- Structured validation errors
|
|
12
|
+
- Optional validation error codes
|
|
13
|
+
- Optional human-readable message resolution
|
|
13
14
|
- Localized validation messages
|
|
14
15
|
|
|
15
16
|
The package does **not** replace Zod. Zod remains the validation engine.
|
|
@@ -19,39 +20,13 @@ The package does **not** replace Zod. Zod remains the validation engine.
|
|
|
19
20
|
## Installation
|
|
20
21
|
|
|
21
22
|
```bash
|
|
22
|
-
npm install zod-validate
|
|
23
|
+
npm install zod-validate
|
|
23
24
|
```
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
## Why zod-validate?
|
|
28
|
-
|
|
29
|
-
Zod already provides excellent schema validation.
|
|
30
|
-
|
|
31
|
-
The problem is usually what happens **around** the schema.
|
|
32
|
-
|
|
33
|
-
For example, an application may need to:
|
|
34
|
-
|
|
35
|
-
- Validate an entire form.
|
|
36
|
-
- Validate one field while the user is typing.
|
|
37
|
-
- Keep validation errors associated with their original fields.
|
|
38
|
-
- Handle deeply nested objects and arrays.
|
|
39
|
-
- Return stable validation codes instead of UI-specific messages.
|
|
40
|
-
- Resolve those codes into different languages on the frontend.
|
|
41
|
-
|
|
42
|
-
`zod-validate` provides a small, consistent API for those tasks.
|
|
43
|
-
|
|
44
|
-
Instead of repeatedly working directly with Zod's `safeParse()` result, create a validator once:
|
|
26
|
+
You continue to use Zod to define your schemas:
|
|
45
27
|
|
|
46
28
|
```ts
|
|
47
|
-
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
Then use it wherever needed:
|
|
51
|
-
|
|
52
|
-
```ts
|
|
53
|
-
signup.validate(data);
|
|
54
|
-
signup.validateField("mobile_no", value);
|
|
29
|
+
import { z } from "zod";
|
|
55
30
|
```
|
|
56
31
|
|
|
57
32
|
---
|
|
@@ -60,39 +35,31 @@ signup.validateField("mobile_no", value);
|
|
|
60
35
|
|
|
61
36
|
## 1. Define a Zod schema
|
|
62
37
|
|
|
63
|
-
You
|
|
38
|
+
You define your validation rules using Zod as usual.
|
|
39
|
+
|
|
40
|
+
For example:
|
|
64
41
|
|
|
65
42
|
```ts
|
|
66
43
|
import { z } from "zod";
|
|
67
44
|
|
|
68
45
|
const signupSchema = z.object({
|
|
69
46
|
mobile_no: z
|
|
70
|
-
.string(
|
|
47
|
+
.string()
|
|
71
48
|
.trim()
|
|
72
|
-
.min(1, "
|
|
73
|
-
.regex(/^[0-9]+$/, "
|
|
74
|
-
.regex(/^(98|97)/, "
|
|
75
|
-
.length(10, "
|
|
49
|
+
.min(1, "Mobile number is required.")
|
|
50
|
+
.regex(/^[0-9]+$/, "Mobile number must contain only digits.")
|
|
51
|
+
.regex(/^(98|97)/, "Mobile number must start with 98 or 97.")
|
|
52
|
+
.length(10, "Mobile number must be exactly 10 digits."),
|
|
76
53
|
|
|
77
|
-
password: z.string(
|
|
54
|
+
password: z.string().min(8, "Password must be at least 8 characters."),
|
|
78
55
|
});
|
|
79
56
|
```
|
|
80
57
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
```ts
|
|
84
|
-
"mobile_no_required";
|
|
85
|
-
"mobile_no_digits";
|
|
86
|
-
"mobile_no_prefix";
|
|
87
|
-
"mobile_no_length";
|
|
88
|
-
"password_min_length";
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
These codes can later be converted into human-readable messages.
|
|
58
|
+
Zod messages are returned by `zod-validate` exactly as provided by the schema.
|
|
92
59
|
|
|
93
60
|
---
|
|
94
61
|
|
|
95
|
-
|
|
62
|
+
## 2. Create a Validator
|
|
96
63
|
|
|
97
64
|
Import `createValidator`:
|
|
98
65
|
|
|
@@ -106,7 +73,7 @@ Create a validator from your schema:
|
|
|
106
73
|
const signup = createValidator(signupSchema);
|
|
107
74
|
```
|
|
108
75
|
|
|
109
|
-
The validator is now tied to that schema.
|
|
76
|
+
The validator is now tied to that schema and can be reused wherever the schema needs to be validated.
|
|
110
77
|
|
|
111
78
|
---
|
|
112
79
|
|
|
@@ -137,9 +104,38 @@ The returned `data` is the data parsed by Zod.
|
|
|
137
104
|
|
|
138
105
|
This means Zod transformations are also preserved.
|
|
139
106
|
|
|
107
|
+
For example:
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
const schema = z.object({
|
|
111
|
+
name: z.string().trim(),
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
If the input is:
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
{
|
|
119
|
+
name: " John Doe ";
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
the successful result contains the transformed value:
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
{
|
|
127
|
+
valid: true,
|
|
128
|
+
data: {
|
|
129
|
+
name: "John Doe"
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
140
134
|
---
|
|
141
135
|
|
|
142
|
-
|
|
136
|
+
# 4. Invalid Data
|
|
137
|
+
|
|
138
|
+
Suppose the data is invalid:
|
|
143
139
|
|
|
144
140
|
```ts
|
|
145
141
|
const result = signup.validate({
|
|
@@ -148,7 +144,105 @@ const result = signup.validate({
|
|
|
148
144
|
});
|
|
149
145
|
```
|
|
150
146
|
|
|
151
|
-
|
|
147
|
+
Because the schema contains human-readable messages, the result is:
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
{
|
|
151
|
+
valid: false,
|
|
152
|
+
errors: {
|
|
153
|
+
mobile_no: "Mobile number is required.",
|
|
154
|
+
password: "Password must be at least 8 characters."
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
The errors remain associated with their original fields.
|
|
160
|
+
|
|
161
|
+
You can use them directly:
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
if (!result.valid) {
|
|
165
|
+
console.log(result.errors.mobile_no);
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Output:
|
|
170
|
+
|
|
171
|
+
```text
|
|
172
|
+
Mobile number is required.
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
No additional message resolution is required.
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
# Validation Messages
|
|
180
|
+
|
|
181
|
+
There are two ways to provide messages in your Zod schemas.
|
|
182
|
+
|
|
183
|
+
## Direct Messages
|
|
184
|
+
|
|
185
|
+
The simplest approach is to put the human-readable message directly in the schema:
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
const signupSchema = z.object({
|
|
189
|
+
mobile_no: z.string().min(1, "Mobile number is required."),
|
|
190
|
+
password: z.string().min(8, "Password must be at least 8 characters."),
|
|
191
|
+
});
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Then:
|
|
195
|
+
|
|
196
|
+
```ts
|
|
197
|
+
const result = signup.validate({
|
|
198
|
+
mobile_no: "",
|
|
199
|
+
password: "123",
|
|
200
|
+
});
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
returns:
|
|
204
|
+
|
|
205
|
+
```ts
|
|
206
|
+
{
|
|
207
|
+
valid: false,
|
|
208
|
+
errors: {
|
|
209
|
+
mobile_no: "Mobile number is required.",
|
|
210
|
+
password: "Password must be at least 8 characters."
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
You can use these messages directly:
|
|
216
|
+
|
|
217
|
+
```ts
|
|
218
|
+
if (!result.valid) {
|
|
219
|
+
setErrors(result.errors);
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
This approach is useful when your validation messages are simple and do not need to be managed separately.
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
# Validation Codes
|
|
228
|
+
|
|
229
|
+
For larger applications, you may prefer to keep validation rules separate from human-readable messages.
|
|
230
|
+
|
|
231
|
+
Instead of putting the final message in the schema, use a stable validation code:
|
|
232
|
+
|
|
233
|
+
```ts
|
|
234
|
+
const signupSchema = z.object({
|
|
235
|
+
mobile_no: z
|
|
236
|
+
.string()
|
|
237
|
+
.min(1, "mobile_no_required")
|
|
238
|
+
.regex(/^[0-9]+$/, "mobile_no_digits")
|
|
239
|
+
.length(10, "mobile_no_length"),
|
|
240
|
+
|
|
241
|
+
password: z.string().min(8, "password_min_length"),
|
|
242
|
+
});
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
The validator then returns those codes:
|
|
152
246
|
|
|
153
247
|
```ts
|
|
154
248
|
{
|
|
@@ -160,13 +254,257 @@ The result is:
|
|
|
160
254
|
}
|
|
161
255
|
```
|
|
162
256
|
|
|
163
|
-
The
|
|
257
|
+
The validation layer does not need to know how those codes should be displayed.
|
|
258
|
+
|
|
259
|
+
Your application can decide what each code means.
|
|
260
|
+
|
|
261
|
+
For example:
|
|
262
|
+
|
|
263
|
+
```ts
|
|
264
|
+
const messages = {
|
|
265
|
+
mobile_no_required: "Mobile number is required.",
|
|
266
|
+
mobile_no_digits: "Mobile number must contain only digits.",
|
|
267
|
+
mobile_no_length: "Mobile number must be exactly 10 digits.",
|
|
268
|
+
password_min_length: "Password must be at least 8 characters.",
|
|
269
|
+
};
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
This approach becomes particularly useful when messages need to be:
|
|
273
|
+
|
|
274
|
+
- Centralized
|
|
275
|
+
- Reused
|
|
276
|
+
- Changed independently from validation rules
|
|
277
|
+
- Localized
|
|
278
|
+
- Shared between different parts of an application
|
|
164
279
|
|
|
165
280
|
---
|
|
166
281
|
|
|
167
|
-
#
|
|
282
|
+
# Resolving Validation Messages
|
|
283
|
+
|
|
284
|
+
`resolveValidationMessages()` is an optional utility for applications that use validation codes.
|
|
285
|
+
|
|
286
|
+
Import it:
|
|
287
|
+
|
|
288
|
+
```ts
|
|
289
|
+
import { resolveValidationMessages } from "zod-validate";
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
Given validation errors:
|
|
293
|
+
|
|
294
|
+
```ts
|
|
295
|
+
const errors = {
|
|
296
|
+
mobile_no: "mobile_no_required",
|
|
297
|
+
password: "password_min_length",
|
|
298
|
+
};
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
and a message map:
|
|
168
302
|
|
|
169
|
-
|
|
303
|
+
```ts
|
|
304
|
+
const messages = {
|
|
305
|
+
mobile_no_required: "Mobile number is required.",
|
|
306
|
+
password_min_length: "Password must be at least 8 characters.",
|
|
307
|
+
};
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
resolve the codes:
|
|
311
|
+
|
|
312
|
+
```ts
|
|
313
|
+
const resolvedErrors = resolveValidationMessages(errors, messages);
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
The result is:
|
|
317
|
+
|
|
318
|
+
```ts
|
|
319
|
+
{
|
|
320
|
+
mobile_no: "Mobile number is required.",
|
|
321
|
+
password: "Password must be at least 8 characters."
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
The structure of the errors is preserved.
|
|
326
|
+
|
|
327
|
+
For example:
|
|
328
|
+
|
|
329
|
+
```ts
|
|
330
|
+
{
|
|
331
|
+
user: {
|
|
332
|
+
email: "email_invalid";
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
can become:
|
|
338
|
+
|
|
339
|
+
```ts
|
|
340
|
+
{
|
|
341
|
+
user: {
|
|
342
|
+
email: "Invalid email address.";
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
You do **not** need to use `resolveValidationMessages()` when your Zod schema already contains human-readable messages.
|
|
348
|
+
|
|
349
|
+
It is an optional layer for applications that choose to use validation codes.
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
# Missing Message Codes
|
|
354
|
+
|
|
355
|
+
A message does not have to exist for every validation code.
|
|
356
|
+
|
|
357
|
+
For example:
|
|
358
|
+
|
|
359
|
+
```ts
|
|
360
|
+
const errors = {
|
|
361
|
+
mobile_no: "mobile_no_required",
|
|
362
|
+
password: "password_min_length",
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
const messages = {
|
|
366
|
+
mobile_no_required: "Mobile number is required.",
|
|
367
|
+
};
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
Resolving the messages produces:
|
|
371
|
+
|
|
372
|
+
```ts
|
|
373
|
+
{
|
|
374
|
+
mobile_no: "Mobile number is required.",
|
|
375
|
+
password: "password_min_length"
|
|
376
|
+
}
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
Known codes are resolved.
|
|
380
|
+
|
|
381
|
+
Unknown codes fall back to the original code.
|
|
382
|
+
|
|
383
|
+
This means missing messages do not silently disappear.
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
# Localized Validation Messages
|
|
388
|
+
|
|
389
|
+
Validation codes can also be resolved using localized message maps.
|
|
390
|
+
|
|
391
|
+
For example:
|
|
392
|
+
|
|
393
|
+
```ts
|
|
394
|
+
const messages = {
|
|
395
|
+
en: {
|
|
396
|
+
mobile_no_required: "Mobile number is required.",
|
|
397
|
+
password_min_length: "Password must be at least 8 characters.",
|
|
398
|
+
},
|
|
399
|
+
|
|
400
|
+
np: {
|
|
401
|
+
mobile_no_required: "मोबाइल नम्बर आवश्यक छ।",
|
|
402
|
+
password_min_length: "पासवर्ड कम्तीमा ८ अक्षरको हुनुपर्छ।",
|
|
403
|
+
},
|
|
404
|
+
};
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
Resolve English messages:
|
|
408
|
+
|
|
409
|
+
```ts
|
|
410
|
+
const errors = resolveValidationMessages(
|
|
411
|
+
validationResult.errors,
|
|
412
|
+
messages,
|
|
413
|
+
"en",
|
|
414
|
+
);
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
Result:
|
|
418
|
+
|
|
419
|
+
```ts
|
|
420
|
+
{
|
|
421
|
+
mobile_no: "Mobile number is required.",
|
|
422
|
+
password: "Password must be at least 8 characters."
|
|
423
|
+
}
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
Resolve Nepali messages:
|
|
427
|
+
|
|
428
|
+
```ts
|
|
429
|
+
const errors = resolveValidationMessages(
|
|
430
|
+
validationResult.errors,
|
|
431
|
+
messages,
|
|
432
|
+
"np",
|
|
433
|
+
);
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
Result:
|
|
437
|
+
|
|
438
|
+
```ts
|
|
439
|
+
{
|
|
440
|
+
mobile_no: "मोबाइल नम्बर आवश्यक छ।",
|
|
441
|
+
password: "पासवर्ड कम्तीमा ८ अक्षरको हुनुपर्छ।"
|
|
442
|
+
}
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
---
|
|
446
|
+
|
|
447
|
+
# Default Locale
|
|
448
|
+
|
|
449
|
+
If no locale is supplied and the message configuration is localized, `en` is preferred when available.
|
|
450
|
+
|
|
451
|
+
For example:
|
|
452
|
+
|
|
453
|
+
```ts
|
|
454
|
+
const messages = {
|
|
455
|
+
en: {
|
|
456
|
+
required: "This field is required.",
|
|
457
|
+
},
|
|
458
|
+
|
|
459
|
+
np: {
|
|
460
|
+
required: "यो field आवश्यक छ।",
|
|
461
|
+
},
|
|
462
|
+
};
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
Then:
|
|
466
|
+
|
|
467
|
+
```ts
|
|
468
|
+
resolveValidationMessages(errors, messages);
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
uses the English messages.
|
|
472
|
+
|
|
473
|
+
If `en` does not exist, the first available locale is used.
|
|
474
|
+
You can always explicitly choose a locale:
|
|
475
|
+
|
|
476
|
+
```ts
|
|
477
|
+
resolveValidationMessages(errors, messages, "np");
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
---
|
|
481
|
+
|
|
482
|
+
# Plain Message Maps
|
|
483
|
+
|
|
484
|
+
Localization is optional.
|
|
485
|
+
|
|
486
|
+
You can provide a normal message map:
|
|
487
|
+
|
|
488
|
+
```ts
|
|
489
|
+
const messages = {
|
|
490
|
+
required: "This field is required.",
|
|
491
|
+
invalid_email: "Invalid email address.",
|
|
492
|
+
};
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
Then:
|
|
496
|
+
|
|
497
|
+
```ts
|
|
498
|
+
resolveValidationMessages(errors, messages);
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
No locale is required.
|
|
502
|
+
|
|
503
|
+
---
|
|
504
|
+
|
|
505
|
+
# 5. Type-Safe Result Handling
|
|
506
|
+
|
|
507
|
+
The result returned by `createValidator()` is a discriminated union.
|
|
170
508
|
|
|
171
509
|
```ts
|
|
172
510
|
const result = signup.validate(data);
|
|
@@ -196,7 +534,7 @@ means `result.errors` is available.
|
|
|
196
534
|
|
|
197
535
|
---
|
|
198
536
|
|
|
199
|
-
#
|
|
537
|
+
# 6. Validate a Single Field
|
|
200
538
|
|
|
201
539
|
For frontend forms, you often don't want to validate the entire form.
|
|
202
540
|
|
|
@@ -227,22 +565,27 @@ You get:
|
|
|
227
565
|
{
|
|
228
566
|
valid: false,
|
|
229
567
|
errors: {
|
|
230
|
-
mobile_no: "
|
|
568
|
+
mobile_no: "Mobile number must contain only digits."
|
|
231
569
|
}
|
|
232
570
|
}
|
|
233
571
|
```
|
|
234
572
|
|
|
235
|
-
|
|
573
|
+
If your schema uses validation codes instead:
|
|
236
574
|
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
575
|
+
```ts
|
|
576
|
+
{
|
|
577
|
+
valid: false,
|
|
578
|
+
errors: {
|
|
579
|
+
mobile_no: "mobile_no_digits"
|
|
580
|
+
}
|
|
581
|
+
}
|
|
241
582
|
```
|
|
242
583
|
|
|
584
|
+
The same schema can therefore be used for both whole-form and individual-field validation.
|
|
585
|
+
|
|
243
586
|
---
|
|
244
587
|
|
|
245
|
-
#
|
|
588
|
+
# 7. Why Field Validation Is Useful
|
|
246
589
|
|
|
247
590
|
A frontend form might use the validator like this:
|
|
248
591
|
|
|
@@ -270,7 +613,7 @@ The same Zod schema is used for both operations.
|
|
|
270
613
|
|
|
271
614
|
---
|
|
272
615
|
|
|
273
|
-
#
|
|
616
|
+
# 8. Nested Objects
|
|
274
617
|
|
|
275
618
|
`zod-validate` preserves the structure of nested validation errors.
|
|
276
619
|
|
|
@@ -297,7 +640,8 @@ Validate:
|
|
|
297
640
|
|
|
298
641
|
```ts
|
|
299
642
|
const result = userValidator.validate({
|
|
300
|
-
name: "
|
|
643
|
+
name: "John Doe",
|
|
644
|
+
|
|
301
645
|
address: {
|
|
302
646
|
city: "",
|
|
303
647
|
street: "",
|
|
@@ -305,15 +649,42 @@ const result = userValidator.validate({
|
|
|
305
649
|
});
|
|
306
650
|
```
|
|
307
651
|
|
|
308
|
-
The errors mirror the input structure:
|
|
652
|
+
The errors mirror the input structure:
|
|
653
|
+
|
|
654
|
+
```ts
|
|
655
|
+
{
|
|
656
|
+
valid: false,
|
|
657
|
+
errors: {
|
|
658
|
+
address: {
|
|
659
|
+
city: "city_required",
|
|
660
|
+
street: "street_required"
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
The same structure is preserved when using direct messages:
|
|
667
|
+
|
|
668
|
+
```ts
|
|
669
|
+
const userSchema = z.object({
|
|
670
|
+
name: z.string("Name is required."),
|
|
671
|
+
|
|
672
|
+
address: z.object({
|
|
673
|
+
city: z.string("City is required."),
|
|
674
|
+
street: z.string("Street is required."),
|
|
675
|
+
}),
|
|
676
|
+
});
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
The resulting errors are:
|
|
309
680
|
|
|
310
681
|
```ts
|
|
311
682
|
{
|
|
312
683
|
valid: false,
|
|
313
684
|
errors: {
|
|
314
685
|
address: {
|
|
315
|
-
city: "
|
|
316
|
-
street: "
|
|
686
|
+
city: "City is required.",
|
|
687
|
+
street: "Street is required."
|
|
317
688
|
}
|
|
318
689
|
}
|
|
319
690
|
}
|
|
@@ -323,7 +694,7 @@ This is useful because the error structure follows the same hierarchy as the dat
|
|
|
323
694
|
|
|
324
695
|
---
|
|
325
696
|
|
|
326
|
-
#
|
|
697
|
+
# 9. Nested Arrays
|
|
327
698
|
|
|
328
699
|
Arrays are also preserved.
|
|
329
700
|
|
|
@@ -342,7 +713,6 @@ const buildingSchema = z.object({
|
|
|
342
713
|
units: z.array(
|
|
343
714
|
z.object({
|
|
344
715
|
unit_number: z.string("unit_number_required"),
|
|
345
|
-
|
|
346
716
|
rent: z.number("rent_required"),
|
|
347
717
|
}),
|
|
348
718
|
),
|
|
@@ -367,6 +737,7 @@ const result = buildingValidator.validate({
|
|
|
367
737
|
{
|
|
368
738
|
floor_number: 1,
|
|
369
739
|
name: "Ground Floor",
|
|
740
|
+
|
|
370
741
|
units: [
|
|
371
742
|
{
|
|
372
743
|
unit_number: "101",
|
|
@@ -378,6 +749,7 @@ const result = buildingValidator.validate({
|
|
|
378
749
|
{
|
|
379
750
|
floor_number: 2,
|
|
380
751
|
name: "",
|
|
752
|
+
|
|
381
753
|
units: [
|
|
382
754
|
{
|
|
383
755
|
unit_number: "",
|
|
@@ -440,7 +812,7 @@ The structure is intentionally aligned with the original data.
|
|
|
440
812
|
|
|
441
813
|
---
|
|
442
814
|
|
|
443
|
-
#
|
|
815
|
+
# 10. Using Nested Errors in React
|
|
444
816
|
|
|
445
817
|
This structure works naturally with component hierarchies.
|
|
446
818
|
|
|
@@ -471,8 +843,6 @@ errors?.rent;
|
|
|
471
843
|
For example:
|
|
472
844
|
|
|
473
845
|
```tsx
|
|
474
|
-
<input />;
|
|
475
|
-
|
|
476
846
|
{
|
|
477
847
|
errors?.rent && <p>{errors.rent}</p>;
|
|
478
848
|
}
|
|
@@ -490,7 +860,7 @@ into a flat string just to find the error.
|
|
|
490
860
|
|
|
491
861
|
---
|
|
492
862
|
|
|
493
|
-
#
|
|
863
|
+
# 11. Sparse Array Errors
|
|
494
864
|
|
|
495
865
|
When only some array items contain errors, the resulting error array can contain empty slots.
|
|
496
866
|
|
|
@@ -499,6 +869,7 @@ For example:
|
|
|
499
869
|
```ts
|
|
500
870
|
[
|
|
501
871
|
<empty>,
|
|
872
|
+
|
|
502
873
|
{
|
|
503
874
|
name: "floor_name_required"
|
|
504
875
|
}
|
|
@@ -531,303 +902,358 @@ This allows the error array to preserve the original data indexes.
|
|
|
531
902
|
|
|
532
903
|
---
|
|
533
904
|
|
|
534
|
-
#
|
|
905
|
+
# 12. Choosing Between Messages and Codes
|
|
535
906
|
|
|
536
|
-
|
|
907
|
+
Both approaches are supported.
|
|
537
908
|
|
|
538
|
-
|
|
909
|
+
### Use direct messages when:
|
|
910
|
+
|
|
911
|
+
- Your messages are simple.
|
|
912
|
+
- You don't need localization.
|
|
913
|
+
- You want the schema to contain the final message.
|
|
914
|
+
- You want to use `result.errors` directly.
|
|
915
|
+
|
|
916
|
+
Example:
|
|
539
917
|
|
|
540
918
|
```ts
|
|
541
|
-
|
|
919
|
+
z.string().min(1, "Name is required.");
|
|
542
920
|
```
|
|
543
921
|
|
|
544
|
-
|
|
922
|
+
Result:
|
|
545
923
|
|
|
546
924
|
```ts
|
|
547
925
|
{
|
|
548
|
-
|
|
549
|
-
errors: {
|
|
550
|
-
mobile_no: "mobile_no_required",
|
|
551
|
-
password: "password_min_length"
|
|
552
|
-
}
|
|
926
|
+
name: "Name is required.";
|
|
553
927
|
}
|
|
554
928
|
```
|
|
555
929
|
|
|
556
|
-
|
|
930
|
+
### Use validation codes when:
|
|
557
931
|
|
|
558
|
-
|
|
932
|
+
- Messages need to be centralized.
|
|
933
|
+
- Multiple parts of the application share the same messages.
|
|
934
|
+
- Messages need to be localized.
|
|
935
|
+
- You want to change wording without changing validation rules.
|
|
936
|
+
- The validation layer should return stable identifiers rather than UI text.
|
|
559
937
|
|
|
560
|
-
|
|
938
|
+
Example:
|
|
561
939
|
|
|
562
940
|
```ts
|
|
563
|
-
|
|
564
|
-
mobile_no_required: "Mobile number is required.",
|
|
565
|
-
password_min_length: "Password must be at least 8 characters.",
|
|
566
|
-
};
|
|
941
|
+
z.string().min(1, "name_required");
|
|
567
942
|
```
|
|
568
943
|
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
# 12. Resolve Validation Messages
|
|
572
|
-
|
|
573
|
-
Import:
|
|
944
|
+
Result:
|
|
574
945
|
|
|
575
946
|
```ts
|
|
576
|
-
|
|
947
|
+
{
|
|
948
|
+
name: "name_required";
|
|
949
|
+
}
|
|
577
950
|
```
|
|
578
951
|
|
|
579
|
-
Then:
|
|
952
|
+
Then resolve it:
|
|
580
953
|
|
|
581
954
|
```ts
|
|
582
|
-
|
|
955
|
+
resolveValidationMessages(errors, messages);
|
|
583
956
|
```
|
|
584
957
|
|
|
585
|
-
|
|
958
|
+
There is no requirement to use validation codes.
|
|
959
|
+
|
|
960
|
+
There is also no requirement to use `resolveValidationMessages()`.
|
|
961
|
+
|
|
962
|
+
---
|
|
963
|
+
|
|
964
|
+
# 13. Locale Configuration Validation
|
|
965
|
+
|
|
966
|
+
`resolveValidationMessages()` validates the message configuration it receives.
|
|
967
|
+
|
|
968
|
+
A plain message map must contain string messages:
|
|
586
969
|
|
|
587
970
|
```ts
|
|
588
|
-
const
|
|
589
|
-
|
|
590
|
-
|
|
971
|
+
const messages = {
|
|
972
|
+
required: "This field is required.",
|
|
973
|
+
invalid: "Invalid value.",
|
|
591
974
|
};
|
|
592
975
|
```
|
|
593
976
|
|
|
594
|
-
|
|
977
|
+
A localized message map must contain message maps:
|
|
595
978
|
|
|
596
979
|
```ts
|
|
597
980
|
const messages = {
|
|
598
|
-
|
|
599
|
-
|
|
981
|
+
en: {
|
|
982
|
+
required: "This field is required.",
|
|
983
|
+
},
|
|
984
|
+
|
|
985
|
+
np: {
|
|
986
|
+
required: "यो field आवश्यक छ।",
|
|
987
|
+
},
|
|
600
988
|
};
|
|
601
989
|
```
|
|
602
990
|
|
|
603
|
-
|
|
991
|
+
Invalid message values are rejected instead of being silently accepted.
|
|
992
|
+
|
|
993
|
+
For example:
|
|
604
994
|
|
|
605
995
|
```ts
|
|
606
|
-
{
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
}
|
|
996
|
+
const messages = {
|
|
997
|
+
en: {
|
|
998
|
+
required: 123,
|
|
999
|
+
},
|
|
1000
|
+
};
|
|
610
1001
|
```
|
|
611
1002
|
|
|
1003
|
+
is invalid because validation messages must be strings.
|
|
1004
|
+
|
|
612
1005
|
---
|
|
613
1006
|
|
|
614
|
-
#
|
|
1007
|
+
# 14. Frontend Use Case
|
|
615
1008
|
|
|
616
|
-
A
|
|
1009
|
+
A React form can use the library without requiring message resolution.
|
|
617
1010
|
|
|
618
|
-
|
|
1011
|
+
## During field interaction
|
|
1012
|
+
|
|
1013
|
+
If your schema uses direct messages:
|
|
619
1014
|
|
|
620
1015
|
```ts
|
|
621
|
-
const
|
|
622
|
-
mobile_no: "mobile_no_required",
|
|
623
|
-
password: "password_min_length",
|
|
624
|
-
};
|
|
1016
|
+
const result = signup.validateField("mobile_no", mobileNo);
|
|
625
1017
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
}
|
|
1018
|
+
if (!result.valid) {
|
|
1019
|
+
setMobileError(result.errors.mobile_no);
|
|
1020
|
+
}
|
|
629
1021
|
```
|
|
630
1022
|
|
|
631
|
-
The
|
|
1023
|
+
The error can be displayed immediately.
|
|
1024
|
+
|
|
1025
|
+
If your schema uses validation codes:
|
|
632
1026
|
|
|
633
1027
|
```ts
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
1028
|
+
const result = signup.validateField("mobile_no", mobileNo);
|
|
1029
|
+
|
|
1030
|
+
if (!result.valid) {
|
|
1031
|
+
setMobileError(result.errors.mobile_no);
|
|
637
1032
|
}
|
|
638
1033
|
```
|
|
639
1034
|
|
|
640
|
-
|
|
1035
|
+
the error will contain the code instead.
|
|
641
1036
|
|
|
642
|
-
|
|
1037
|
+
You can resolve it when appropriate:
|
|
643
1038
|
|
|
644
|
-
|
|
1039
|
+
```ts
|
|
1040
|
+
const errors = resolveValidationMessages(result.errors, messages);
|
|
1041
|
+
```
|
|
645
1042
|
|
|
646
|
-
|
|
1043
|
+
## During submission
|
|
647
1044
|
|
|
648
|
-
|
|
1045
|
+
With direct messages:
|
|
649
1046
|
|
|
650
1047
|
```ts
|
|
651
|
-
const
|
|
652
|
-
en: {
|
|
653
|
-
mobile_no_required: "Mobile number is required.",
|
|
654
|
-
password_min_length: "Password must be at least 8 characters.",
|
|
655
|
-
},
|
|
1048
|
+
const result = signup.validate(formData);
|
|
656
1049
|
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
1050
|
+
if (!result.valid) {
|
|
1051
|
+
setErrors(result.errors);
|
|
1052
|
+
return;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
submitForm(result.data);
|
|
662
1056
|
```
|
|
663
1057
|
|
|
664
|
-
|
|
1058
|
+
With validation codes:
|
|
665
1059
|
|
|
666
1060
|
```ts
|
|
667
|
-
const
|
|
668
|
-
validationResult.errors,
|
|
669
|
-
messages,
|
|
670
|
-
"en",
|
|
671
|
-
);
|
|
672
|
-
```
|
|
1061
|
+
const result = signup.validate(formData);
|
|
673
1062
|
|
|
674
|
-
|
|
1063
|
+
if (!result.valid) {
|
|
1064
|
+
const errors = resolveValidationMessages(result.errors, messages);
|
|
675
1065
|
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
mobile_no: "Mobile number is required.",
|
|
679
|
-
password: "Password must be at least 8 characters."
|
|
1066
|
+
setErrors(errors);
|
|
1067
|
+
return;
|
|
680
1068
|
}
|
|
1069
|
+
|
|
1070
|
+
submitForm(result.data);
|
|
681
1071
|
```
|
|
682
1072
|
|
|
683
|
-
|
|
1073
|
+
This keeps the two approaches independent while allowing them to use the same validator.
|
|
1074
|
+
|
|
1075
|
+
---
|
|
1076
|
+
|
|
1077
|
+
# 15. Backend Use Case
|
|
1078
|
+
|
|
1079
|
+
The same validator can be used on the backend.
|
|
1080
|
+
|
|
1081
|
+
For example:
|
|
684
1082
|
|
|
685
1083
|
```ts
|
|
686
|
-
const
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
1084
|
+
const result = signup.validate(req.body);
|
|
1085
|
+
|
|
1086
|
+
if (!result.valid) {
|
|
1087
|
+
return res.status(400).json({
|
|
1088
|
+
errors: result.errors,
|
|
1089
|
+
});
|
|
1090
|
+
}
|
|
691
1091
|
```
|
|
692
1092
|
|
|
693
|
-
|
|
1093
|
+
When using validation codes, the backend can return stable codes:
|
|
694
1094
|
|
|
695
|
-
```
|
|
1095
|
+
```json
|
|
696
1096
|
{
|
|
697
|
-
|
|
698
|
-
|
|
1097
|
+
"errors": {
|
|
1098
|
+
"mobile_no": "mobile_no_required",
|
|
1099
|
+
"password": "password_min_length"
|
|
1100
|
+
}
|
|
699
1101
|
}
|
|
700
1102
|
```
|
|
701
1103
|
|
|
1104
|
+
The frontend can then decide how those codes should be displayed.
|
|
1105
|
+
|
|
1106
|
+
This allows the backend validation rules and frontend presentation to remain separate.
|
|
1107
|
+
|
|
702
1108
|
---
|
|
703
1109
|
|
|
704
|
-
#
|
|
1110
|
+
# 16. Zod Remains the Validation Engine
|
|
705
1111
|
|
|
706
|
-
|
|
1112
|
+
`zod-validate` does not replace Zod.
|
|
707
1113
|
|
|
708
|
-
|
|
1114
|
+
Your schemas are still Zod schemas:
|
|
709
1115
|
|
|
710
1116
|
```ts
|
|
711
|
-
const
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
},
|
|
715
|
-
|
|
716
|
-
np: {
|
|
717
|
-
required: "यो field आवश्यक छ।",
|
|
718
|
-
},
|
|
719
|
-
};
|
|
1117
|
+
const schema = z.object({
|
|
1118
|
+
name: z.string().min(1, "Name is required."),
|
|
1119
|
+
});
|
|
720
1120
|
```
|
|
721
1121
|
|
|
722
|
-
|
|
1122
|
+
Zod performs the actual validation.
|
|
723
1123
|
|
|
724
|
-
|
|
725
|
-
resolveValidationMessages(errors, messages);
|
|
726
|
-
```
|
|
1124
|
+
`zod-validate` provides a reusable layer around the result:
|
|
727
1125
|
|
|
728
|
-
|
|
1126
|
+
```text
|
|
1127
|
+
Zod schema
|
|
1128
|
+
↓
|
|
1129
|
+
Zod validation
|
|
1130
|
+
↓
|
|
1131
|
+
createValidator()
|
|
1132
|
+
↓
|
|
1133
|
+
Structured validation result
|
|
1134
|
+
```
|
|
729
1135
|
|
|
730
|
-
|
|
1136
|
+
The package does not introduce another validation language or replace Zod's schema API.
|
|
731
1137
|
|
|
732
1138
|
---
|
|
733
1139
|
|
|
734
|
-
#
|
|
1140
|
+
# API Summary
|
|
735
1141
|
|
|
736
|
-
|
|
1142
|
+
## `createValidator(schema)`
|
|
737
1143
|
|
|
738
|
-
|
|
1144
|
+
Creates a reusable validator from a Zod schema.
|
|
739
1145
|
|
|
740
1146
|
```ts
|
|
741
|
-
const
|
|
742
|
-
required: "This field is required.",
|
|
743
|
-
invalid_email: "Invalid email address.",
|
|
744
|
-
};
|
|
1147
|
+
const validator = createValidator(schema);
|
|
745
1148
|
```
|
|
746
1149
|
|
|
747
|
-
|
|
1150
|
+
### `validator.validate(data)`
|
|
1151
|
+
|
|
1152
|
+
Validates the complete value.
|
|
748
1153
|
|
|
749
1154
|
```ts
|
|
750
|
-
|
|
1155
|
+
const result = validator.validate(data);
|
|
751
1156
|
```
|
|
752
1157
|
|
|
753
|
-
|
|
1158
|
+
Returns either:
|
|
754
1159
|
|
|
755
|
-
|
|
1160
|
+
```ts
|
|
1161
|
+
{
|
|
1162
|
+
valid: true,
|
|
1163
|
+
data: parsedData
|
|
1164
|
+
}
|
|
1165
|
+
```
|
|
1166
|
+
|
|
1167
|
+
or:
|
|
756
1168
|
|
|
757
|
-
|
|
1169
|
+
```ts
|
|
1170
|
+
{
|
|
1171
|
+
valid: false,
|
|
1172
|
+
errors: validationErrors
|
|
1173
|
+
}
|
|
1174
|
+
```
|
|
758
1175
|
|
|
759
|
-
|
|
1176
|
+
### `validator.validateField(field, value)`
|
|
760
1177
|
|
|
761
|
-
|
|
1178
|
+
Validates an individual field of a Zod object schema.
|
|
762
1179
|
|
|
763
1180
|
```ts
|
|
764
|
-
const
|
|
765
|
-
required: "This field is required.",
|
|
766
|
-
invalid: "Invalid value.",
|
|
767
|
-
};
|
|
1181
|
+
const result = validator.validateField("mobile_no", value);
|
|
768
1182
|
```
|
|
769
1183
|
|
|
770
|
-
|
|
1184
|
+
Returns either:
|
|
771
1185
|
|
|
772
1186
|
```ts
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
1187
|
+
{
|
|
1188
|
+
valid: true,
|
|
1189
|
+
data: parsedValue
|
|
1190
|
+
}
|
|
1191
|
+
```
|
|
777
1192
|
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
1193
|
+
or:
|
|
1194
|
+
|
|
1195
|
+
```ts
|
|
1196
|
+
{
|
|
1197
|
+
valid: false,
|
|
1198
|
+
errors: {
|
|
1199
|
+
mobile_no: "validation message or code"
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
782
1202
|
```
|
|
783
1203
|
|
|
784
|
-
|
|
1204
|
+
---
|
|
785
1205
|
|
|
786
|
-
|
|
1206
|
+
## `resolveValidationMessages(errors, messages, locale?)`
|
|
1207
|
+
|
|
1208
|
+
Optionally resolves validation codes into human-readable messages.
|
|
787
1209
|
|
|
788
1210
|
```ts
|
|
789
|
-
const
|
|
790
|
-
en: {
|
|
791
|
-
required: 123,
|
|
792
|
-
},
|
|
793
|
-
};
|
|
1211
|
+
const resolvedErrors = resolveValidationMessages(errors, messages);
|
|
794
1212
|
```
|
|
795
1213
|
|
|
796
|
-
|
|
1214
|
+
For localized messages:
|
|
1215
|
+
|
|
1216
|
+
```ts
|
|
1217
|
+
const resolvedErrors = resolveValidationMessages(errors, messages, "np");
|
|
1218
|
+
```
|
|
1219
|
+
|
|
1220
|
+
This function is only needed when you choose to manage validation messages separately from your Zod schemas.
|
|
797
1221
|
|
|
798
1222
|
---
|
|
799
1223
|
|
|
800
|
-
#
|
|
1224
|
+
# Design Philosophy
|
|
801
1225
|
|
|
802
|
-
|
|
1226
|
+
`zod-validate` intentionally stays small.
|
|
803
1227
|
|
|
804
|
-
|
|
1228
|
+
It does not try to replace Zod or create a new validation system.
|
|
805
1229
|
|
|
806
|
-
|
|
807
|
-
const result = signup.validateField("mobile_no", mobileNo);
|
|
1230
|
+
Instead, it focuses on a few useful problems around Zod validation:
|
|
808
1231
|
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
1232
|
+
- Reusable validators
|
|
1233
|
+
- Whole-object validation
|
|
1234
|
+
- Field-level validation
|
|
1235
|
+
- Structured nested errors
|
|
1236
|
+
- Preserving array indexes
|
|
1237
|
+
- Optional validation codes
|
|
1238
|
+
- Optional message resolution
|
|
1239
|
+
- Optional localization
|
|
813
1240
|
|
|
814
|
-
|
|
1241
|
+
You can keep your schemas simple with direct messages:
|
|
815
1242
|
|
|
816
1243
|
```ts
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
if (!result.valid) {
|
|
820
|
-
const errors = resolveValidationMessages(result.errors, messages);
|
|
1244
|
+
z.string().min(1, "Name is required.");
|
|
1245
|
+
```
|
|
821
1246
|
|
|
822
|
-
|
|
823
|
-
return;
|
|
824
|
-
}
|
|
1247
|
+
or use stable validation codes when your application benefits from separating validation rules from presentation:
|
|
825
1248
|
|
|
826
|
-
|
|
1249
|
+
```ts
|
|
1250
|
+
z.string().min(1, "name_required");
|
|
827
1251
|
```
|
|
828
1252
|
|
|
829
|
-
|
|
1253
|
+
Both approaches work with the same validator.
|
|
830
1254
|
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
1255
|
+
---
|
|
1256
|
+
|
|
1257
|
+
# License
|
|
1258
|
+
|
|
1259
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,32 +1,45 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zod-validate",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Reusable validation utilities built on top of Zod for frontend and backend applications.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"zod",
|
|
7
|
+
"validation",
|
|
8
|
+
"typescript",
|
|
9
|
+
"frontend",
|
|
10
|
+
"backend"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"author": "Bibek Bhattarai",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/dmaya0393/zod-validate.git"
|
|
17
|
+
},
|
|
18
|
+
"bugs": {
|
|
19
|
+
"url": "https://github.com/dmaya0393/zod-validate/issues"
|
|
20
|
+
},
|
|
5
21
|
"type": "module",
|
|
6
22
|
"main": "./dist/index.js",
|
|
7
23
|
"types": "./dist/index.d.ts",
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
|
+
"import": "./dist/index.js"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
8
30
|
"files": [
|
|
9
31
|
"dist"
|
|
10
32
|
],
|
|
11
33
|
"scripts": {
|
|
12
34
|
"build": "tsc",
|
|
13
35
|
"test": "vitest",
|
|
36
|
+
"test:run": "vitest run",
|
|
14
37
|
"dev:test": "tsx test.ts"
|
|
15
38
|
},
|
|
16
|
-
"
|
|
17
|
-
"zod",
|
|
18
|
-
"validation",
|
|
19
|
-
"typescript",
|
|
20
|
-
"frontend",
|
|
21
|
-
"backend"
|
|
22
|
-
],
|
|
23
|
-
"author": "Bibek Bhattarai",
|
|
24
|
-
"license": "MIT",
|
|
25
|
-
"peerDependencies": {
|
|
39
|
+
"dependencies": {
|
|
26
40
|
"zod": "^4.6.2"
|
|
27
41
|
},
|
|
28
42
|
"devDependencies": {
|
|
29
|
-
"zod": "^4.6.2",
|
|
30
43
|
"tsx": "^4.23.13",
|
|
31
44
|
"typescript": "^7.0.2",
|
|
32
45
|
"vitest": "^5.0.0"
|