@esmj/schema 0.1.0 → 0.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/README.md CHANGED
@@ -18,6 +18,55 @@ npm install @esmj/schema
18
18
  4. **Lightweight**: None dependencies and a small footprint make it ideal for projects where performance and simplicity are key.
19
19
  5. **Customizable**: Offers fine-grained control over validation and error handling.
20
20
 
21
+ ## Comparison with Similar Libraries
22
+
23
+ When choosing a schema validation library, bundle size can be an important factor, especially for frontend applications where minimizing JavaScript size is critical. Here's how `@esmj/schema` compares to other popular libraries:
24
+
25
+ | Library | Bundle Size (minified + gzipped) |
26
+ |------------------|---------------------------------|
27
+ | `@esmj/schema` | ~1 KB |
28
+ | Superstruct | ~3.2 KB |
29
+ | Yup | ~12.2 KB |
30
+ | Zod@3 | ~13 KB |
31
+ | @zod/mini | ~20,5 KB |
32
+ | Joi | ~40,4 KB |
33
+ | Zod@4 | ~40,8 KB |
34
+
35
+ ### Performance Comparison
36
+
37
+ #### Schema Creation Performance
38
+
39
+ | Library | 1 Iteration | 1,000 Iterations | 1,000,000 Iterations |
40
+ |-----------------|-------------|------------------|----------------------|
41
+ | `@esmj/schema` | 0.02 ms | 4.93 ms | 399.62 ms |
42
+ | zod@3 | 0.08 ms | 9.68 ms | 8.53 s |
43
+ | @zod/mini | 0.22 ms | 39.77 ms | 34.51 s |
44
+ | Yup | 0.54 ms | 14.03 ms | 12.34 s |
45
+ | Superstruct | 0.13 ms | 1.04 ms | 487.94 ms |
46
+ | Joi | 0.62 ms | 31.60 ms | 23.06 s |
47
+
48
+ #### Parsing Performance
49
+
50
+ | Library | 1 Iteration | 1,000 Iterations | 1,000,000 Iterations |
51
+ |-----------------|-------------|------------------|----------------------|
52
+ | `@esmj/schema` | 0.05 ms | 0.46 ms | 267.93 ms |
53
+ | zod@3 | 0.14 ms | 1.44 ms | 897.89 ms |
54
+ | @zod/mini | 0.23 ms | 0.42 ms | 199.08 ms |
55
+ | Yup | 0.30 ms | 9.49 ms | 8.69 s |
56
+ | Superstruct | 0.08 ms | 4.18 ms | 3.71 s |
57
+ | Joi | 0.33 ms | 3.35 ms | 2.69 s |
58
+
59
+ #### Error Handling Performance
60
+
61
+ | Library | 1 Iteration | 1,000 Iterations | 1,000,000 Iterations |
62
+ |-----------------|-------------|------------------|----------------------|
63
+ | `@esmj/schema` | 0.03 ms | 0.59 ms | 365.32 ms |
64
+ | zod3 | 0.05 ms | 2.09 ms | 1.26 s |
65
+ | @zod/mini | 0.07 ms | 0.99 ms | 545.12 ms |
66
+ | Yup | 0.27 ms | 19.28 ms | 18.87 s |
67
+ | Superstruct | 0.04 ms | 8.62 ms | 6.24 s |
68
+ | Joi | 0.15 ms | 4.13 ms | 2.57 s |
69
+
21
70
  ## Usage
22
71
 
23
72
  ### Basic Usage
@@ -125,6 +174,26 @@ Creates a schema that accepts any value.
125
174
  const anySchema = s.any();
126
175
  ```
127
176
 
177
+ #### `s.enum(values)`
178
+
179
+ Creates an enum schema that validates against a predefined set of string values.
180
+
181
+ - **`values`**: An array of strings representing the allowed values for the enum. Each value must be a string.
182
+
183
+ ```typescript
184
+ const enumSchema = s.enum(['admin', 'user', 'guest']);
185
+
186
+ const validResult = enumSchema.parse('admin');
187
+ console.log(validResult);
188
+ // 'admin'
189
+
190
+ const invalidResult = enumSchema.safeParse('invalidRole');
191
+ console.log(invalidResult.success);
192
+ // false
193
+ console.log(invalidResult.error.message);
194
+ // Error: Invalid enum value. Expected "admin" | "user" | "guest", received "invalidRole".
195
+ ```
196
+
128
197
  #### `s.preprocess(callback, schema)`
129
198
 
130
199
  Creates a schema that preprocesses the input value using the provided callback before validating it with the given schema.
@@ -148,9 +217,26 @@ const result = stringSchema.parse('hello');
148
217
  Safely parses the given value according to the schema, returning a success or error result.
149
218
 
150
219
  ```typescript
151
- const result = stringSchema.safeParse('hello'); // { success: true, data: 'hello' }
220
+ const result = stringSchema.safeParse('hello');
221
+ // { success: true, data: 'hello' }
222
+
223
+ const errorResult = stringSchema.safeParse(123);
224
+ // { success: false, error: { message: 'The value "123" must be type of string but is type of "number".' } }
225
+ ```
226
+
227
+ **Note:** The `error` returned by `safeParse` is not a native `Error` instance. Instead, it is a plain object with the following structure:
228
+
229
+ ```typescript
230
+ type ErrorStructure = {
231
+ message: string;
232
+ cause?: {
233
+ key?: string;
234
+ };
235
+ };
152
236
  ```
153
237
 
238
+ This allows for easier serialization and debugging but may require additional handling if you expect a native `Error` instance.
239
+
154
240
  #### `optional()`
155
241
 
156
242
  Makes the schema optional.
@@ -0,0 +1,585 @@
1
+ import { performance } from 'node:perf_hooks';
2
+ import { z as zodMini } from '@zod/mini'; // Import Zod Mini
3
+ import Joi from 'joi'; // Import Joi
4
+ import superStruct from 'superstruct'; // Import Superstruct
5
+ import * as yup from 'yup'; // Import Yup
6
+ import { z } from 'zod'; // Import Zod
7
+ import { s } from '../src/index.ts'; // Import @esmj/schema
8
+
9
+ function formatTime(time: number) {
10
+ // Convert time to milliseconds
11
+ if (time < 1000) {
12
+ return `${time.toFixed(2)} ms`;
13
+ }
14
+ // Convert to seconds
15
+ const seconds = time / 1000;
16
+ return `${seconds.toFixed(2)} s`;
17
+ }
18
+
19
+ function benchmark(label: string, fn: () => void) {
20
+ const REPEAT = 10;
21
+ let sum = 0;
22
+ let min = Number.POSITIVE_INFINITY;
23
+ let max = Number.NEGATIVE_INFINITY;
24
+ for (let i = 0; i < REPEAT; i++) {
25
+ const start = performance.now();
26
+ fn();
27
+ const end = performance.now();
28
+
29
+ const time = end - start;
30
+
31
+ min = Math.min(min, time);
32
+ max = Math.max(max, time);
33
+ sum += time;
34
+ }
35
+
36
+ const average = sum / REPEAT;
37
+
38
+ console.log(
39
+ `${label} took an average of ${formatTime(average)} over ${REPEAT} iterations. Min: ${formatTime(min)}, Max: ${formatTime(max)}`,
40
+ );
41
+
42
+ return formatTime(average);
43
+ }
44
+
45
+ function scenarioParseSchema(testData) {
46
+ // Define a schema using @esmj/schema
47
+ const esmjSchema = s.object({
48
+ username: s.string(),
49
+ password: s.string(),
50
+ age: s.number().optional(),
51
+ address: s.object({
52
+ street: s.string().nullish(),
53
+ city: s.string().nullable(),
54
+ date: s.date().optional(), // Optional date field
55
+ }),
56
+ tags: s.array(s.string()),
57
+ });
58
+
59
+ // Define a schema using Zod
60
+ const zodSchema = z.object({
61
+ username: z.string(),
62
+ password: z.string(),
63
+ age: z.number().optional(),
64
+ address: z.object({
65
+ street: z.string().nullish(),
66
+ city: z.string().nullable(),
67
+ date: z.date().optional(), // Optional date field
68
+ }),
69
+ tags: z.array(z.string()),
70
+ });
71
+
72
+ // Define a schema using Zod Mini
73
+ const zodMiniSchema = zodMini.object({
74
+ username: zodMini.string(),
75
+ password: zodMini.string(),
76
+ age: zodMini.optional(zodMini.number()),
77
+ address: zodMini.object({
78
+ street: zodMini.nullish(zodMini.string()),
79
+ city: zodMini.nullable(zodMini.string()),
80
+ date: zodMini.optional(zodMini.date()), // Optional date field
81
+ }),
82
+ tags: zodMini.array(zodMini.string()),
83
+ });
84
+
85
+ // Define a schema using Yup
86
+ const yupSchema = yup.object({
87
+ username: yup.string().required(),
88
+ password: yup.string().required(),
89
+ age: yup.number().optional(),
90
+ address: yup.object({
91
+ street: yup.string().nullable(),
92
+ city: yup.string().nullable(),
93
+ date: yup.date().optional(), // Optional date field
94
+ }),
95
+ tags: yup.array().of(yup.string().required()).required(),
96
+ });
97
+
98
+ // Define a schema using Superstruct
99
+ const superStructSchema = superStruct.object({
100
+ username: superStruct.string(),
101
+ password: superStruct.string(),
102
+ age: superStruct.number(),
103
+ address: superStruct.object({
104
+ street: superStruct.optional(superStruct.string()),
105
+ city: superStruct.optional(superStruct.string()),
106
+ date: superStruct.optional(superStruct.date()), // Optional date field
107
+ }),
108
+ tags: superStruct.array(superStruct.string()),
109
+ });
110
+
111
+ // Define a schema using Joi
112
+ const joiSchema = Joi.object({
113
+ username: Joi.string().required(),
114
+ password: Joi.string().required(),
115
+ age: Joi.number().required(),
116
+ address: Joi.object({
117
+ street: Joi.string().optional(), // Allow null for street
118
+ city: Joi.string().optional(),
119
+ date: Joi.date().optional(), // Optional date field
120
+ }),
121
+ tags: Joi.array().items(Joi.string().required()).required(),
122
+ });
123
+
124
+ const result = {} as Record<string, object>;
125
+
126
+ const esmjSchemaResult = {};
127
+ esmjSchemaResult['1'] = benchmark('@esmj/schema 1', () => {
128
+ for (let i = 0; i < 1; i++) {
129
+ esmjSchema.safeParse(testData);
130
+ }
131
+ });
132
+
133
+ esmjSchemaResult['1_000'] = benchmark('@esmj/schema 1_000', () => {
134
+ for (let i = 0; i < 1_000; i++) {
135
+ esmjSchema.safeParse(testData);
136
+ }
137
+ });
138
+
139
+ esmjSchemaResult['1_000_000'] = benchmark('@esmj/schema 1_000_000', () => {
140
+ for (let i = 0; i < 1_000_000; i++) {
141
+ esmjSchema.safeParse(testData);
142
+ }
143
+ });
144
+ result.esmjSchema = esmjSchemaResult;
145
+
146
+ const zod = {};
147
+ zod['1'] = benchmark('zod 1', () => {
148
+ for (let i = 0; i < 1; i++) {
149
+ zodSchema.safeParse(testData);
150
+ }
151
+ });
152
+
153
+ zod['1_000'] = benchmark('zod 1_000', () => {
154
+ for (let i = 0; i < 1_000; i++) {
155
+ zodSchema.safeParse(testData);
156
+ }
157
+ });
158
+
159
+ zod['1_000_000'] = benchmark('zod 1_000_000', () => {
160
+ for (let i = 0; i < 1_000_000; i++) {
161
+ zodSchema.safeParse(testData);
162
+ }
163
+ });
164
+ result.zod = zod;
165
+
166
+ const zodMiniResult = {};
167
+ zodMiniResult['1'] = benchmark('@zod/mini 1', () => {
168
+ for (let i = 0; i < 1; i++) {
169
+ zodMiniSchema.safeParse(testData);
170
+ }
171
+ });
172
+
173
+ zodMiniResult['1_000'] = benchmark('@zod/mini 1_000', () => {
174
+ for (let i = 0; i < 1_000; i++) {
175
+ zodMiniSchema.safeParse(testData);
176
+ }
177
+ });
178
+
179
+ zodMiniResult['1_000_000'] = benchmark('@zod/mini 1_000_000', () => {
180
+ for (let i = 0; i < 1_000_000; i++) {
181
+ zodMiniSchema.safeParse(testData);
182
+ }
183
+ });
184
+ result.zodMini = zodMiniResult;
185
+
186
+ const yupResult = {};
187
+ yupResult['1'] = benchmark('yup 1', () => {
188
+ for (let i = 0; i < 1; i++) {
189
+ yupSchema.isValidSync(testData);
190
+ }
191
+ });
192
+
193
+ yupResult['1_000'] = benchmark('yup 1_000', () => {
194
+ for (let i = 0; i < 1_000; i++) {
195
+ yupSchema.isValidSync(testData);
196
+ }
197
+ });
198
+
199
+ yupResult['1_000_000'] = benchmark('yup 1_000_000', () => {
200
+ for (let i = 0; i < 1_000_000; i++) {
201
+ yupSchema.isValidSync(testData);
202
+ }
203
+ });
204
+ result.yup = yupResult;
205
+
206
+ const superStructResult = {};
207
+ superStructResult['1'] = benchmark('superStruct 1', () => {
208
+ for (let i = 0; i < 1; i++) {
209
+ superStruct.is(testData, superStructSchema);
210
+ }
211
+ });
212
+
213
+ superStructResult['1_000'] = benchmark('superStruct 1_000', () => {
214
+ for (let i = 0; i < 1_000; i++) {
215
+ superStruct.is(testData, superStructSchema);
216
+ }
217
+ });
218
+
219
+ superStructResult['1_000_000'] = benchmark('superStruct 1_000_000', () => {
220
+ for (let i = 0; i < 1_000_000; i++) {
221
+ superStruct.is(testData, superStructSchema);
222
+ }
223
+ });
224
+ result.superStruct = superStructResult;
225
+
226
+ const joiResult = {};
227
+ joiResult['1'] = benchmark('joi 1', () => {
228
+ for (let i = 0; i < 1; i++) {
229
+ try {
230
+ joiSchema.validate(testData);
231
+ } catch (e) {}
232
+ }
233
+ });
234
+
235
+ joiResult['1_000'] = benchmark('joi 1_000', () => {
236
+ for (let i = 0; i < 1_000; i++) {
237
+ try {
238
+ joiSchema.validate(testData);
239
+ } catch (e) {}
240
+ }
241
+ });
242
+
243
+ joiResult['1_000_000'] = benchmark('joi 1_000_000', () => {
244
+ for (let i = 0; i < 1_000_000; i++) {
245
+ try {
246
+ joiSchema.validate(testData);
247
+ } catch (e) {}
248
+ }
249
+ });
250
+ result.joi = joiResult;
251
+
252
+ return result;
253
+ }
254
+
255
+ function scenarioCreatingSchema() {
256
+ const result = {} as Record<string, object>;
257
+ const esmjSchemaResult = {};
258
+ esmjSchemaResult['1'] = benchmark('@esmj/schema 1', () => {
259
+ for (let i = 0; i < 1; i++) {
260
+ const esmjSchema = s.object({
261
+ username: s.string(),
262
+ password: s.string(),
263
+ age: s.number().optional(),
264
+ address: s.object({
265
+ street: s.string().nullish(),
266
+ city: s.string().nullable(),
267
+ date: s.date().optional(), // Optional date field
268
+ }),
269
+ tags: s.array(s.string()),
270
+ });
271
+ }
272
+ });
273
+
274
+ esmjSchemaResult['1_000'] = benchmark('@esmj/schema 1_000', () => {
275
+ for (let i = 0; i < 1_000; i++) {
276
+ const esmjSchema = s.object({
277
+ username: s.string(),
278
+ password: s.string(),
279
+ age: s.number().optional(),
280
+ address: s.object({
281
+ street: s.string().nullish(),
282
+ city: s.string().nullable(),
283
+ date: s.date().optional(), // Optional date field
284
+ }),
285
+ tags: s.array(s.string()),
286
+ });
287
+ }
288
+ });
289
+
290
+ esmjSchemaResult['1_000_000'] = benchmark('@esmj/schema 1_000_000', () => {
291
+ for (let i = 0; i < 1_000_000; i++) {
292
+ const esmjSchema = s.object({
293
+ username: s.string(),
294
+ password: s.string(),
295
+ age: s.number().optional(),
296
+ address: s.object({
297
+ street: s.string().nullish(),
298
+ city: s.string().nullable(),
299
+ date: s.date().optional(), // Optional date field
300
+ }),
301
+ tags: s.array(s.string()),
302
+ });
303
+ }
304
+ });
305
+ result.esmjSchema = esmjSchemaResult;
306
+
307
+ const zod = {};
308
+ zod['1'] = benchmark('zod 1', () => {
309
+ for (let i = 0; i < 1; i++) {
310
+ const zodSchema = z.object({
311
+ username: z.string(),
312
+ password: z.string(),
313
+ age: z.number().optional(),
314
+ address: z.object({
315
+ street: z.string().nullish(),
316
+ city: z.string().nullable(),
317
+ date: z.date().optional(), // Optional date field
318
+ }),
319
+ tags: z.array(z.string()),
320
+ });
321
+ }
322
+ });
323
+
324
+ zod['1_000'] = benchmark('zod 1_000', () => {
325
+ for (let i = 0; i < 1_000; i++) {
326
+ const zodSchema = z.object({
327
+ username: z.string(),
328
+ password: z.string(),
329
+ age: z.number().optional(),
330
+ address: z.object({
331
+ street: z.string().nullish(),
332
+ city: z.string().nullable(),
333
+ date: z.date().optional(), // Optional date field
334
+ }),
335
+ tags: z.array(z.string()),
336
+ });
337
+ }
338
+ });
339
+
340
+ zod['1_000_000'] = benchmark('zod 1_000_000', () => {
341
+ for (let i = 0; i < 1_000_000; i++) {
342
+ const zodSchema = z.object({
343
+ username: z.string(),
344
+ password: z.string(),
345
+ age: z.number().optional(),
346
+ address: z.object({
347
+ street: z.string().nullish(),
348
+ city: z.string().nullable(),
349
+ date: z.date().optional(), // Optional date field
350
+ }),
351
+ tags: z.array(z.string()),
352
+ });
353
+ }
354
+ });
355
+ result.zod = zod;
356
+
357
+ const zodMiniResult = {};
358
+ zodMiniResult['1'] = benchmark('@zod/mini 1', () => {
359
+ for (let i = 0; i < 1; i++) {
360
+ const zodMiniSchema = zodMini.object({
361
+ username: zodMini.string(),
362
+ password: zodMini.string(),
363
+ age: zodMini.optional(zodMini.number()),
364
+ address: zodMini.object({
365
+ street: zodMini.nullish(zodMini.string()),
366
+ city: zodMini.nullable(zodMini.string()),
367
+ date: zodMini.optional(zodMini.date()), // Optional date field
368
+ }),
369
+ tags: zodMini.array(zodMini.string()),
370
+ });
371
+ }
372
+ });
373
+
374
+ zodMiniResult['1_000'] = benchmark('@zod/mini 1_000', () => {
375
+ for (let i = 0; i < 1_000; i++) {
376
+ const zodMiniSchema = zodMini.object({
377
+ username: zodMini.string(),
378
+ password: zodMini.string(),
379
+ age: zodMini.optional(zodMini.number()),
380
+ address: zodMini.object({
381
+ street: zodMini.nullish(zodMini.string()),
382
+ city: zodMini.nullable(zodMini.string()),
383
+ date: zodMini.optional(zodMini.date()), // Optional date field
384
+ }),
385
+ tags: zodMini.array(zodMini.string()),
386
+ });
387
+ }
388
+ });
389
+
390
+ zodMiniResult['1_000_000'] = benchmark('@zod/mini 1_000_000', () => {
391
+ for (let i = 0; i < 1_000_000; i++) {
392
+ const zodMiniSchema = zodMini.object({
393
+ username: zodMini.string(),
394
+ password: zodMini.string(),
395
+ age: zodMini.optional(zodMini.number()),
396
+ address: zodMini.object({
397
+ street: zodMini.nullish(zodMini.string()),
398
+ city: zodMini.nullable(zodMini.string()),
399
+ date: zodMini.optional(zodMini.date()), // Optional date field
400
+ }),
401
+ tags: zodMini.array(zodMini.string()),
402
+ });
403
+ }
404
+ });
405
+ result.zodMini = zodMiniResult;
406
+
407
+ const yupResult = {};
408
+ yupResult['1'] = benchmark('yup 1', () => {
409
+ for (let i = 0; i < 1; i++) {
410
+ const yupSchema = yup.object({
411
+ username: yup.string().required(),
412
+ password: yup.string().required(),
413
+ age: yup.number().optional(),
414
+ address: yup.object({
415
+ street: yup.string().nullable(),
416
+ city: yup.string().nullable(),
417
+ date: yup.date().optional(), // Optional date field
418
+ }),
419
+ tags: yup.array().of(yup.string().required()).required(),
420
+ });
421
+ }
422
+ });
423
+
424
+ yupResult['1_000'] = benchmark('yup 1_000', () => {
425
+ for (let i = 0; i < 1_000; i++) {
426
+ const yupSchema = yup.object({
427
+ username: yup.string().required(),
428
+ password: yup.string().required(),
429
+ age: yup.number().optional(),
430
+ address: yup.object({
431
+ street: yup.string().nullable(),
432
+ city: yup.string().nullable(),
433
+ date: yup.date().optional(), // Optional date field
434
+ }),
435
+ tags: yup.array().of(yup.string().required()).required(),
436
+ });
437
+ }
438
+ });
439
+
440
+ yupResult['1_000_000'] = benchmark('yup 1_000_000', () => {
441
+ for (let i = 0; i < 1_000_000; i++) {
442
+ const yupSchema = yup.object({
443
+ username: yup.string().required(),
444
+ password: yup.string().required(),
445
+ age: yup.number().optional(),
446
+ address: yup.object({
447
+ street: yup.string().nullable(),
448
+ city: yup.string().nullable(),
449
+ date: yup.date().optional(), // Optional date field
450
+ }),
451
+ tags: yup.array().of(yup.string().required()).required(),
452
+ });
453
+ }
454
+ });
455
+ result.yup = yupResult;
456
+
457
+ const superStructResult = {};
458
+ superStructResult['1'] = benchmark('superStruct 1', () => {
459
+ for (let i = 0; i < 1; i++) {
460
+ const superStructSchema = superStruct.object({
461
+ username: superStruct.string(),
462
+ password: superStruct.string(),
463
+ age: superStruct.number(),
464
+ address: superStruct.object({
465
+ street: superStruct.optional(superStruct.string()),
466
+ city: superStruct.optional(superStruct.string()),
467
+ date: superStruct.optional(superStruct.date()), // Optional date field
468
+ }),
469
+ tags: superStruct.array(superStruct.string()),
470
+ });
471
+ }
472
+ });
473
+
474
+ superStructResult['1_000'] = benchmark('superStruct 1_000', () => {
475
+ for (let i = 0; i < 1_000; i++) {
476
+ const superStructSchema = superStruct.object({
477
+ username: superStruct.string(),
478
+ password: superStruct.string(),
479
+ age: superStruct.number(),
480
+ address: superStruct.object({
481
+ street: superStruct.optional(superStruct.string()),
482
+ city: superStruct.optional(superStruct.string()),
483
+ date: superStruct.optional(superStruct.date()), // Optional date field
484
+ }),
485
+ tags: superStruct.array(superStruct.string()),
486
+ });
487
+ }
488
+ });
489
+
490
+ superStructResult['1_000_000'] = benchmark('superStruct 1_000_000', () => {
491
+ for (let i = 0; i < 1_000_000; i++) {
492
+ const superStructSchema = superStruct.object({
493
+ username: superStruct.string(),
494
+ password: superStruct.string(),
495
+ age: superStruct.number(),
496
+ address: superStruct.object({
497
+ street: superStruct.optional(superStruct.string()),
498
+ city: superStruct.optional(superStruct.string()),
499
+ date: superStruct.optional(superStruct.date()), // Optional date field
500
+ }),
501
+ tags: superStruct.array(superStruct.string()),
502
+ });
503
+ }
504
+ });
505
+ result.superStruct = superStructResult;
506
+
507
+ const joiResult = {};
508
+ joiResult['1'] = benchmark('joi 1', () => {
509
+ for (let i = 0; i < 1; i++) {
510
+ const joiSchema = Joi.object({
511
+ username: Joi.string().required(),
512
+ password: Joi.string().required(),
513
+ age: Joi.number().required(),
514
+ address: Joi.object({
515
+ street: Joi.string().optional(), // Allow null for street
516
+ city: Joi.string().optional(),
517
+ date: Joi.date().optional(), // Optional date field
518
+ }),
519
+ tags: Joi.array().items(Joi.string().required()).required(),
520
+ });
521
+ }
522
+ });
523
+
524
+ joiResult['1_000'] = benchmark('joi 1_000', () => {
525
+ for (let i = 0; i < 1_000; i++) {
526
+ const joiSchema = Joi.object({
527
+ username: Joi.string().required(),
528
+ password: Joi.string().required(),
529
+ age: Joi.number().required(),
530
+ address: Joi.object({
531
+ street: Joi.string().optional(), // Allow null for street
532
+ city: Joi.string().optional(),
533
+ date: Joi.date().optional(), // Optional date field
534
+ }),
535
+ tags: Joi.array().items(Joi.string().required()).required(),
536
+ });
537
+ }
538
+ });
539
+
540
+ joiResult['1_000_000'] = benchmark('joi 1_000_000', () => {
541
+ for (let i = 0; i < 1_000_000; i++) {
542
+ const joiSchema = Joi.object({
543
+ username: Joi.string().required(),
544
+ password: Joi.string().required(),
545
+ age: Joi.number().required(),
546
+ address: Joi.object({
547
+ street: Joi.string().optional(), // Allow null for street
548
+ city: Joi.string().optional(),
549
+ date: Joi.date().optional(), // Optional date field
550
+ }),
551
+ tags: Joi.array().items(Joi.string().required()).required(),
552
+ });
553
+ }
554
+ });
555
+ result.joi = joiResult;
556
+
557
+ return result;
558
+ }
559
+
560
+ const testDataGood = {
561
+ username: 'john_doe',
562
+ password: 'securepassword',
563
+ age: 30,
564
+ address: {
565
+ street: '123 Main St',
566
+ city: 'New York',
567
+ date: new Date(),
568
+ },
569
+ tags: ['developer', 'typescript'],
570
+ };
571
+
572
+ const testDataBad = {
573
+ username: 'john_doe',
574
+ password: 'securepassword',
575
+ age: 30,
576
+ address: {
577
+ street: '123 Main St',
578
+ city: 'New York',
579
+ date: new Date(),
580
+ },
581
+ };
582
+
583
+ console.table(scenarioCreatingSchema());
584
+ console.table(scenarioParseSchema(testDataGood));
585
+ console.table(scenarioParseSchema(testDataBad));
package/dist/index.d.mts CHANGED
@@ -1,12 +1,22 @@
1
+ type ErrorStructure = {
2
+ message: string;
3
+ cause?: {
4
+ key?: string;
5
+ };
6
+ };
7
+ type Valid<Output> = {
8
+ success: true;
9
+ data: Output;
10
+ };
11
+ type Invalid = {
12
+ success: false;
13
+ error: ErrorStructure;
14
+ };
15
+ type InternalParseOutput<Output> = Valid<Output> | Invalid;
1
16
  interface SchemaInterface<Input, Output> {
17
+ _parse(value: Input | Partial<Input> | unknown): InternalParseOutput<Output>;
2
18
  parse(value: Input | Partial<Input> | unknown): Output;
3
- safeParse(value: Input | Partial<Input> | unknown): {
4
- success: true;
5
- data: Output;
6
- } | {
7
- success: false;
8
- error: Error;
9
- };
19
+ safeParse(value: Input | Partial<Input> | unknown): InternalParseOutput<Output>;
10
20
  optional(): SchemaInterface<Input, Partial<Output> | undefined>;
11
21
  transform<NewOutput>(callback: (value: Input) => NewOutput): SchemaInterface<Input, NewOutput>;
12
22
  nullable(): SchemaInterface<Input, Output | null>;
@@ -17,6 +27,8 @@ interface SchemaInterface<Input, Output> {
17
27
  message: string;
18
28
  }): SchemaInterface<Input, Output>;
19
29
  }
30
+ interface EnumSchemaInterface<T extends string> extends SchemaInterface<string, T> {
31
+ }
20
32
  interface StringSchemaInterface extends SchemaInterface<string, string> {
21
33
  }
22
34
  interface NumberSchemaInterface extends SchemaInterface<number, number> {
@@ -33,7 +45,7 @@ interface ObjectSchemaInterface<T extends Record<string, SchemaType>> extends Sc
33
45
  [Property in keyof T]: ReturnType<T[Property]['parse']>;
34
46
  }> {
35
47
  }
36
- type SchemaType = StringSchemaInterface | SchemaInterface<string, string> | SchemaInterface<string, string | undefined> | SchemaInterface<string, string | null> | SchemaInterface<string, string | undefined | null> | ObjectSchemaInterface<Record<string, SchemaType>> | SchemaInterface<object, object> | SchemaInterface<object, object | undefined> | SchemaInterface<object, object | null> | SchemaInterface<object, object | undefined | null> | NumberSchemaInterface | SchemaInterface<number, number> | SchemaInterface<number, number | undefined> | SchemaInterface<number, number | null> | SchemaInterface<number, number | undefined | null> | BooleanSchemaInterface | SchemaInterface<boolean, boolean> | SchemaInterface<boolean, boolean | undefined> | SchemaInterface<boolean, boolean | null> | SchemaInterface<boolean, boolean | undefined | null> | DateSchemaInterface | SchemaInterface<Date, Date> | SchemaInterface<Date, Date | undefined> | SchemaInterface<Date, Date | null> | SchemaInterface<Date, Date | undefined | null> | ArraySchemaInterface<StringSchemaInterface | ObjectSchemaInterface<Record<string, SchemaType>> | NumberSchemaInterface | BooleanSchemaInterface> | SchemaInterface<Array<unknown>, Array<unknown>> | SchemaInterface<Array<unknown>, Array<unknown> | undefined> | SchemaInterface<Array<unknown>, Array<unknown> | null> | SchemaInterface<Array<unknown>, Array<unknown> | undefined | null>;
48
+ type SchemaType = StringSchemaInterface | SchemaInterface<string, string> | SchemaInterface<string, string | undefined> | SchemaInterface<string, string | null> | SchemaInterface<string, string | undefined | null> | ObjectSchemaInterface<Record<string, SchemaType>> | SchemaInterface<object, object> | SchemaInterface<object, object | undefined> | SchemaInterface<object, object | null> | SchemaInterface<object, object | undefined | null> | NumberSchemaInterface | SchemaInterface<number, number> | SchemaInterface<number, number | undefined> | SchemaInterface<number, number | null> | SchemaInterface<number, number | undefined | null> | BooleanSchemaInterface | SchemaInterface<boolean, boolean> | SchemaInterface<boolean, boolean | undefined> | SchemaInterface<boolean, boolean | null> | SchemaInterface<boolean, boolean | undefined | null> | DateSchemaInterface | SchemaInterface<Date, Date> | SchemaInterface<Date, Date | undefined> | SchemaInterface<Date, Date | null> | SchemaInterface<Date, Date | undefined | null> | EnumSchemaInterface<string> | ArraySchemaInterface<StringSchemaInterface | ObjectSchemaInterface<Record<string, SchemaType>> | NumberSchemaInterface | BooleanSchemaInterface | DateSchemaInterface | EnumSchemaInterface<string>> | SchemaInterface<Array<unknown>, Array<unknown>> | SchemaInterface<Array<unknown>, Array<unknown> | undefined> | SchemaInterface<Array<unknown>, Array<unknown> | null> | SchemaInterface<Array<unknown>, Array<unknown> | undefined | null>;
37
49
  type ExtenderType = (inter: SchemaType, validation: Function, options: {
38
50
  message: string;
39
51
  type: string;
@@ -44,6 +56,7 @@ declare const s: {
44
56
  number(): NumberSchemaInterface;
45
57
  boolean(): BooleanSchemaInterface;
46
58
  date(): DateSchemaInterface;
59
+ enum(definition: Readonly<Array<string>>): EnumSchemaInterface<(typeof definition)[number]>;
47
60
  array<T extends SchemaType>(definition: T): ArraySchemaInterface<T>;
48
61
  any(): any;
49
62
  preprocess<T extends SchemaType>(callback: Function, schema: T): T;
@@ -51,4 +64,4 @@ declare const s: {
51
64
  declare function extend(callback: ExtenderType): void;
52
65
  type Infer<T> = T extends SchemaType ? ReturnType<T['parse']> : unknown;
53
66
 
54
- export { type ArraySchemaInterface, type BooleanSchemaInterface, type DateSchemaInterface, type ExtenderType, type Infer, type NumberSchemaInterface, type ObjectSchemaInterface, type SchemaInterface, type SchemaType, type StringSchemaInterface, extend, s };
67
+ export { type ArraySchemaInterface, type BooleanSchemaInterface, type DateSchemaInterface, type EnumSchemaInterface, type ExtenderType, type Infer, type NumberSchemaInterface, type ObjectSchemaInterface, type SchemaInterface, type SchemaType, type StringSchemaInterface, extend, s };
package/dist/index.d.ts CHANGED
@@ -1,12 +1,22 @@
1
+ type ErrorStructure = {
2
+ message: string;
3
+ cause?: {
4
+ key?: string;
5
+ };
6
+ };
7
+ type Valid<Output> = {
8
+ success: true;
9
+ data: Output;
10
+ };
11
+ type Invalid = {
12
+ success: false;
13
+ error: ErrorStructure;
14
+ };
15
+ type InternalParseOutput<Output> = Valid<Output> | Invalid;
1
16
  interface SchemaInterface<Input, Output> {
17
+ _parse(value: Input | Partial<Input> | unknown): InternalParseOutput<Output>;
2
18
  parse(value: Input | Partial<Input> | unknown): Output;
3
- safeParse(value: Input | Partial<Input> | unknown): {
4
- success: true;
5
- data: Output;
6
- } | {
7
- success: false;
8
- error: Error;
9
- };
19
+ safeParse(value: Input | Partial<Input> | unknown): InternalParseOutput<Output>;
10
20
  optional(): SchemaInterface<Input, Partial<Output> | undefined>;
11
21
  transform<NewOutput>(callback: (value: Input) => NewOutput): SchemaInterface<Input, NewOutput>;
12
22
  nullable(): SchemaInterface<Input, Output | null>;
@@ -17,6 +27,8 @@ interface SchemaInterface<Input, Output> {
17
27
  message: string;
18
28
  }): SchemaInterface<Input, Output>;
19
29
  }
30
+ interface EnumSchemaInterface<T extends string> extends SchemaInterface<string, T> {
31
+ }
20
32
  interface StringSchemaInterface extends SchemaInterface<string, string> {
21
33
  }
22
34
  interface NumberSchemaInterface extends SchemaInterface<number, number> {
@@ -33,7 +45,7 @@ interface ObjectSchemaInterface<T extends Record<string, SchemaType>> extends Sc
33
45
  [Property in keyof T]: ReturnType<T[Property]['parse']>;
34
46
  }> {
35
47
  }
36
- type SchemaType = StringSchemaInterface | SchemaInterface<string, string> | SchemaInterface<string, string | undefined> | SchemaInterface<string, string | null> | SchemaInterface<string, string | undefined | null> | ObjectSchemaInterface<Record<string, SchemaType>> | SchemaInterface<object, object> | SchemaInterface<object, object | undefined> | SchemaInterface<object, object | null> | SchemaInterface<object, object | undefined | null> | NumberSchemaInterface | SchemaInterface<number, number> | SchemaInterface<number, number | undefined> | SchemaInterface<number, number | null> | SchemaInterface<number, number | undefined | null> | BooleanSchemaInterface | SchemaInterface<boolean, boolean> | SchemaInterface<boolean, boolean | undefined> | SchemaInterface<boolean, boolean | null> | SchemaInterface<boolean, boolean | undefined | null> | DateSchemaInterface | SchemaInterface<Date, Date> | SchemaInterface<Date, Date | undefined> | SchemaInterface<Date, Date | null> | SchemaInterface<Date, Date | undefined | null> | ArraySchemaInterface<StringSchemaInterface | ObjectSchemaInterface<Record<string, SchemaType>> | NumberSchemaInterface | BooleanSchemaInterface> | SchemaInterface<Array<unknown>, Array<unknown>> | SchemaInterface<Array<unknown>, Array<unknown> | undefined> | SchemaInterface<Array<unknown>, Array<unknown> | null> | SchemaInterface<Array<unknown>, Array<unknown> | undefined | null>;
48
+ type SchemaType = StringSchemaInterface | SchemaInterface<string, string> | SchemaInterface<string, string | undefined> | SchemaInterface<string, string | null> | SchemaInterface<string, string | undefined | null> | ObjectSchemaInterface<Record<string, SchemaType>> | SchemaInterface<object, object> | SchemaInterface<object, object | undefined> | SchemaInterface<object, object | null> | SchemaInterface<object, object | undefined | null> | NumberSchemaInterface | SchemaInterface<number, number> | SchemaInterface<number, number | undefined> | SchemaInterface<number, number | null> | SchemaInterface<number, number | undefined | null> | BooleanSchemaInterface | SchemaInterface<boolean, boolean> | SchemaInterface<boolean, boolean | undefined> | SchemaInterface<boolean, boolean | null> | SchemaInterface<boolean, boolean | undefined | null> | DateSchemaInterface | SchemaInterface<Date, Date> | SchemaInterface<Date, Date | undefined> | SchemaInterface<Date, Date | null> | SchemaInterface<Date, Date | undefined | null> | EnumSchemaInterface<string> | ArraySchemaInterface<StringSchemaInterface | ObjectSchemaInterface<Record<string, SchemaType>> | NumberSchemaInterface | BooleanSchemaInterface | DateSchemaInterface | EnumSchemaInterface<string>> | SchemaInterface<Array<unknown>, Array<unknown>> | SchemaInterface<Array<unknown>, Array<unknown> | undefined> | SchemaInterface<Array<unknown>, Array<unknown> | null> | SchemaInterface<Array<unknown>, Array<unknown> | undefined | null>;
37
49
  type ExtenderType = (inter: SchemaType, validation: Function, options: {
38
50
  message: string;
39
51
  type: string;
@@ -44,6 +56,7 @@ declare const s: {
44
56
  number(): NumberSchemaInterface;
45
57
  boolean(): BooleanSchemaInterface;
46
58
  date(): DateSchemaInterface;
59
+ enum(definition: Readonly<Array<string>>): EnumSchemaInterface<(typeof definition)[number]>;
47
60
  array<T extends SchemaType>(definition: T): ArraySchemaInterface<T>;
48
61
  any(): any;
49
62
  preprocess<T extends SchemaType>(callback: Function, schema: T): T;
@@ -51,4 +64,4 @@ declare const s: {
51
64
  declare function extend(callback: ExtenderType): void;
52
65
  type Infer<T> = T extends SchemaType ? ReturnType<T['parse']> : unknown;
53
66
 
54
- export { type ArraySchemaInterface, type BooleanSchemaInterface, type DateSchemaInterface, type ExtenderType, type Infer, type NumberSchemaInterface, type ObjectSchemaInterface, type SchemaInterface, type SchemaType, type StringSchemaInterface, extend, s };
67
+ export { type ArraySchemaInterface, type BooleanSchemaInterface, type DateSchemaInterface, type EnumSchemaInterface, type ExtenderType, type Infer, type NumberSchemaInterface, type ObjectSchemaInterface, type SchemaInterface, type SchemaType, type StringSchemaInterface, extend, s };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- 'use strict';var y={object(n){let u=i(c=>typeof c=="object"&&c!==null&&!Array.isArray(c),{type:"object"});return s(u,"parse",(c,...o)=>{let e=c(...o);return n&&typeof e=="object"?Object.keys(n).reduce((t,r)=>{if(typeof n[r]?.parse=="function")try{t[r]=n[r].parse(e[r]);}catch(p){throw r=p.cause?.key?`${r}.${p.cause.key}`:r,new Error(`Error parsing key "${r}": ${p.message}`,{cause:{key:r}})}return t},{}):e}),u},string(){return i(a=>typeof a=="string",{type:"string"})},number(){return i(a=>typeof a=="number",{type:"number"})},boolean(){return i(a=>a===true||a===false,{type:"boolean"})},date(){return i(a=>a instanceof Date&&!Number.isNaN(a.getTime()),{type:"date"})},array(n){let u=i(c=>Array.isArray(c),{type:"array"});return s(u,"parse",(c,o)=>(o=c(o),n&&Array.isArray(o)?o.map((e,t)=>{try{return n.parse(e)}catch(r){let p=r.cause?.key?`${t}.${r.cause.key}`:t;throw new Error(`Error parsing index "${t}": ${r.message}`,{cause:{key:p}})}}):o)),u},any(){return i(()=>true)},preprocess(n,a){return s(a,"parse",(u,c)=>(c=n(c),u(c))),a}};function h(n){return a=>`The value "${a}" must be type of ${n} but is type of "${typeof a}".`}function s(n,a,u){let c=n[a];n[a]=(...o)=>u(c,...o);}function i(n,{type:a="any"}={}){let u=h(a),c={message:u,type:a},o={parse(e){if(!n(e))throw new Error(u(e));return e},safeParse(e){try{return {success:!0,data:this.parse(e)}}catch(t){return {success:false,error:t}}},transform(e){return s(this,"parse",(t,r)=>e(t(r))),this},optional(){return s(this,"parse",(e,t)=>{try{return e(t)}catch{return}}),this},nullable(){return s(this,"parse",(e,t)=>{try{return e(t)}catch{return null}}),this},nullish(){return s(this,"parse",(e,t)=>{try{return e(t)}catch{return t==null?t:null}}),this},default(e){return s(this,"parse",(t,r)=>(r===void 0&&(r=e,r=typeof e=="function"?e():e),r=t(r),r)),this},pipe(e){return s(this,"parse",(t,r)=>e.parse(t(r))),this},refine(e,{message:t}){return s(this,"parse",(r,p)=>{let f=r(p);if(!e(f))throw new Error(t);return f}),this}};return m.reduce((e,t)=>t(e,n,c)??e,o)}var m=[];function I(n){m.push(n);}exports.extend=I;exports.s=y;
1
+ 'use strict';var h=r=>typeof r=="string"||r instanceof String,I=r=>typeof r=="number"||r instanceof Number,l=r=>r===true||r===false,y=r=>r instanceof Date&&!Number.isNaN(r.getTime()),d=r=>Array.isArray(r),S=r=>typeof r=="object"&&r!==null&&!Array.isArray(r),g={object(r){let a=p(S,{type:"object"});return i(a,"_parse",(u,...o)=>{let c=u(...o);if(c.success===false)return c;let n={};for(let t in r){let e=r[t]._parse(c.data[t]);if(e.success)n[t]=e.data;else {e=e;let s=e?.error?.cause?.key?`${t}.${e?.error?.cause?.key}`:t;return e.error.message=`Error parsing key "${s}": ${e.error.message}`,e.error.cause={key:s},e}}return {success:true,data:n}}),a},string(){return p(h,{type:"string"})},number(){return p(I,{type:"number"})},boolean(){return p(l,{type:"boolean"})},date(){return p(y,{type:"date"})},enum(r){let a=n=>r.includes(n),u=n=>`Invalid ${o} value. Expected ${r.map(t=>`"${t}"`).join(" | ")}, received "${n}".`,o="enum";return p(a,{type:o,message:u})},array(r){let a=p(d,{type:"array"});return i(a,"_parse",(u,...o)=>{let c=u(...o);if(c.success===false)return c;let n=[];for(let t=0;t<c.data.length;t++){let e=r._parse(c.data[t]);if(e.success)n.push(e.data);else {e=e;let s=e.error?.cause?.key?`${t}.${e.error.cause.key}`:`${t}`;return e.error.message=`Error parsing index "${t}": ${e.error.message}`,e.error.cause={key:s},e}}return {success:true,data:n}}),a},any(){return p(()=>true)},preprocess(r,a){return i(a,"_parse",(u,o)=>(o=r(o),u(o))),a}};function b(r){return a=>`The value "${a}" must be type of ${r} but is type of "${typeof a}".`}function i(r,a,u){let o=r[a];r[a]=(...c)=>u(o,...c);}function p(r,{type:a="any",message:u}={}){u=u||b(a);let o={message:u,type:a},c={_parse(n){return r(n)?{success:true,data:n}:{success:false,error:{message:u(n)}}},parse(n){let t=c._parse(n);if(!t.success)throw t=t,new Error(t.error.message,{cause:t.error.cause});return t.data},safeParse(n){return c._parse(n)},transform(n){return i(this,"_parse",(t,e)=>{let s=t(e);return s.success&&(s.data=n(s.data)),s}),this},optional(){return i(this,"_parse",(n,t)=>{let e=n(t);return e.success||(e.data=void 0,e.success=true),e}),this},nullable(){return i(this,"_parse",(n,t)=>{let e=n(t);return e.success||(e.data=null,e.success=true),e}),this},nullish(){return i(this,"_parse",(n,t)=>{let e=n(t);return !e.success&&t==null&&(e.success=true,e.data=t),e}),this},default(n){return i(this,"_parse",(t,e)=>(e===void 0&&(e=n,e=typeof n=="function"?n():n),t(e))),this},pipe(n){return i(this,"_parse",(t,e)=>{let s=t(e);return s.success?n._parse(s.data):s}),this},refine(n,{message:t}){return i(this,"_parse",(e,s)=>{let m=e(s);return n(m.data)?m:{success:false,error:{message:t}}}),this}};return f.length>0?f.reduce((n,t)=>t(n,r,o)??n,c):c}var f=[];function T(r){f.push(r);}exports.extend=T;exports.s=g;
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- var y={object(n){let u=i(c=>typeof c=="object"&&c!==null&&!Array.isArray(c),{type:"object"});return s(u,"parse",(c,...o)=>{let e=c(...o);return n&&typeof e=="object"?Object.keys(n).reduce((t,r)=>{if(typeof n[r]?.parse=="function")try{t[r]=n[r].parse(e[r]);}catch(p){throw r=p.cause?.key?`${r}.${p.cause.key}`:r,new Error(`Error parsing key "${r}": ${p.message}`,{cause:{key:r}})}return t},{}):e}),u},string(){return i(a=>typeof a=="string",{type:"string"})},number(){return i(a=>typeof a=="number",{type:"number"})},boolean(){return i(a=>a===true||a===false,{type:"boolean"})},date(){return i(a=>a instanceof Date&&!Number.isNaN(a.getTime()),{type:"date"})},array(n){let u=i(c=>Array.isArray(c),{type:"array"});return s(u,"parse",(c,o)=>(o=c(o),n&&Array.isArray(o)?o.map((e,t)=>{try{return n.parse(e)}catch(r){let p=r.cause?.key?`${t}.${r.cause.key}`:t;throw new Error(`Error parsing index "${t}": ${r.message}`,{cause:{key:p}})}}):o)),u},any(){return i(()=>true)},preprocess(n,a){return s(a,"parse",(u,c)=>(c=n(c),u(c))),a}};function h(n){return a=>`The value "${a}" must be type of ${n} but is type of "${typeof a}".`}function s(n,a,u){let c=n[a];n[a]=(...o)=>u(c,...o);}function i(n,{type:a="any"}={}){let u=h(a),c={message:u,type:a},o={parse(e){if(!n(e))throw new Error(u(e));return e},safeParse(e){try{return {success:!0,data:this.parse(e)}}catch(t){return {success:false,error:t}}},transform(e){return s(this,"parse",(t,r)=>e(t(r))),this},optional(){return s(this,"parse",(e,t)=>{try{return e(t)}catch{return}}),this},nullable(){return s(this,"parse",(e,t)=>{try{return e(t)}catch{return null}}),this},nullish(){return s(this,"parse",(e,t)=>{try{return e(t)}catch{return t==null?t:null}}),this},default(e){return s(this,"parse",(t,r)=>(r===void 0&&(r=e,r=typeof e=="function"?e():e),r=t(r),r)),this},pipe(e){return s(this,"parse",(t,r)=>e.parse(t(r))),this},refine(e,{message:t}){return s(this,"parse",(r,p)=>{let f=r(p);if(!e(f))throw new Error(t);return f}),this}};return m.reduce((e,t)=>t(e,n,c)??e,o)}var m=[];function I(n){m.push(n);}export{I as extend,y as s};
1
+ var h=r=>typeof r=="string"||r instanceof String,I=r=>typeof r=="number"||r instanceof Number,l=r=>r===true||r===false,y=r=>r instanceof Date&&!Number.isNaN(r.getTime()),d=r=>Array.isArray(r),S=r=>typeof r=="object"&&r!==null&&!Array.isArray(r),g={object(r){let a=p(S,{type:"object"});return i(a,"_parse",(u,...o)=>{let c=u(...o);if(c.success===false)return c;let n={};for(let t in r){let e=r[t]._parse(c.data[t]);if(e.success)n[t]=e.data;else {e=e;let s=e?.error?.cause?.key?`${t}.${e?.error?.cause?.key}`:t;return e.error.message=`Error parsing key "${s}": ${e.error.message}`,e.error.cause={key:s},e}}return {success:true,data:n}}),a},string(){return p(h,{type:"string"})},number(){return p(I,{type:"number"})},boolean(){return p(l,{type:"boolean"})},date(){return p(y,{type:"date"})},enum(r){let a=n=>r.includes(n),u=n=>`Invalid ${o} value. Expected ${r.map(t=>`"${t}"`).join(" | ")}, received "${n}".`,o="enum";return p(a,{type:o,message:u})},array(r){let a=p(d,{type:"array"});return i(a,"_parse",(u,...o)=>{let c=u(...o);if(c.success===false)return c;let n=[];for(let t=0;t<c.data.length;t++){let e=r._parse(c.data[t]);if(e.success)n.push(e.data);else {e=e;let s=e.error?.cause?.key?`${t}.${e.error.cause.key}`:`${t}`;return e.error.message=`Error parsing index "${t}": ${e.error.message}`,e.error.cause={key:s},e}}return {success:true,data:n}}),a},any(){return p(()=>true)},preprocess(r,a){return i(a,"_parse",(u,o)=>(o=r(o),u(o))),a}};function b(r){return a=>`The value "${a}" must be type of ${r} but is type of "${typeof a}".`}function i(r,a,u){let o=r[a];r[a]=(...c)=>u(o,...c);}function p(r,{type:a="any",message:u}={}){u=u||b(a);let o={message:u,type:a},c={_parse(n){return r(n)?{success:true,data:n}:{success:false,error:{message:u(n)}}},parse(n){let t=c._parse(n);if(!t.success)throw t=t,new Error(t.error.message,{cause:t.error.cause});return t.data},safeParse(n){return c._parse(n)},transform(n){return i(this,"_parse",(t,e)=>{let s=t(e);return s.success&&(s.data=n(s.data)),s}),this},optional(){return i(this,"_parse",(n,t)=>{let e=n(t);return e.success||(e.data=void 0,e.success=true),e}),this},nullable(){return i(this,"_parse",(n,t)=>{let e=n(t);return e.success||(e.data=null,e.success=true),e}),this},nullish(){return i(this,"_parse",(n,t)=>{let e=n(t);return !e.success&&t==null&&(e.success=true,e.data=t),e}),this},default(n){return i(this,"_parse",(t,e)=>(e===void 0&&(e=n,e=typeof n=="function"?n():n),t(e))),this},pipe(n){return i(this,"_parse",(t,e)=>{let s=t(e);return s.success?n._parse(s.data):s}),this},refine(n,{message:t}){return i(this,"_parse",(e,s)=>{let m=e(s);return n(m.data)?m:{success:false,error:{message:t}}}),this}};return f.length>0?f.reduce((n,t)=>t(n,r,o)??n,c):c}var f=[];function T(r){f.push(r);}export{T as extend,g as s};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@esmj/schema",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Tiny extendable package for schema validation.",
5
5
  "keywords": [
6
6
  "schema",
@@ -22,6 +22,7 @@
22
22
  "scripts": {
23
23
  "lint": "biome check --no-errors-on-unmatched",
24
24
  "lint:fix": "npm run lint -- --fix --unsafe",
25
+ "benchmark": "node benchmark/index.ts",
25
26
  "dev": "node_modules/.bin/tsup --dts --watch --onSuccess 'node ./dist/index.mjs'",
26
27
  "test": "node --test --experimental-strip-types",
27
28
  "test:watch": "npm run test -- --watch",
@@ -62,17 +63,22 @@
62
63
  "homepage": "https://github.com/mjancarik/esmj-schema#readme",
63
64
  "devDependencies": {
64
65
  "@biomejs/biome": "1.9.4",
65
- "@commitlint/cli": "^19.7.1",
66
- "@commitlint/config-conventional": "^19.7.1",
67
- "@typescript-eslint/eslint-plugin": "^8.24.0",
68
- "@typescript-eslint/parser": "^8.24.0",
66
+ "@commitlint/cli": "^19.8.1",
67
+ "@commitlint/config-conventional": "^19.8.1",
68
+ "@typescript-eslint/eslint-plugin": "^8.33.0",
69
+ "@typescript-eslint/parser": "^8.33.0",
70
+ "@zod/mini": "^4.0.0-beta.0",
69
71
  "commitizen": "^4.3.1",
70
72
  "conventional-changelog-cli": "^5.0.0",
71
73
  "cz-conventional-changelog": "^3.3.0",
72
74
  "git-cz": "^4.9.0",
73
75
  "husky": "^9.1.7",
74
- "lint-staged": "^15.4.3",
75
- "prettier": "^3.5.0",
76
- "tsup": "^8.3.6"
76
+ "joi": "^17.13.3",
77
+ "lint-staged": "^16.1.0",
78
+ "prettier": "^3.5.3",
79
+ "superstruct": "^2.0.2",
80
+ "tsup": "^8.5.0",
81
+ "yup": "^1.6.1",
82
+ "zod": "^3.25.42"
77
83
  }
78
84
  }
package/biome.json DELETED
@@ -1,50 +0,0 @@
1
- {
2
- "files": {
3
- "include": ["src/**/*.mjs", "src/**/*.ts", "utils/**/*.mjs"],
4
- "ignore": [".history/**/*"]
5
- },
6
- "formatter": {
7
- "enabled": true,
8
- "formatWithErrors": true,
9
- "indentStyle": "space",
10
- "indentWidth": 2,
11
- "lineEnding": "lf",
12
- "lineWidth": 80,
13
- "attributePosition": "auto"
14
- },
15
- "organizeImports": { "enabled": true },
16
- "linter": {
17
- "enabled": true,
18
- "rules": {
19
- "recommended": true,
20
- "complexity": {
21
- "noForEach": "off",
22
- "noBannedTypes": "off"
23
- },
24
- "style": {
25
- "noParameterAssign": "off"
26
- }
27
- }
28
- },
29
- "javascript": {
30
- "formatter": {
31
- "jsxQuoteStyle": "double",
32
- "quoteProperties": "asNeeded",
33
- "trailingCommas": "all",
34
- "semicolons": "always",
35
- "arrowParentheses": "always",
36
- "bracketSpacing": true,
37
- "bracketSameLine": false,
38
- "quoteStyle": "single",
39
- "attributePosition": "auto"
40
- }
41
- },
42
- "overrides": [
43
- {
44
- "include": ["*.json"],
45
- "formatter": {
46
- "indentWidth": 2
47
- }
48
- }
49
- ]
50
- }