@esmj/schema 0.3.6 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -8
- package/dist/index.d.mts +8 -3
- package/dist/index.d.ts +8 -3
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -2
package/README.md
CHANGED
|
@@ -12,12 +12,13 @@ npm install @esmj/schema
|
|
|
12
12
|
|
|
13
13
|
`@esmj/schema` is a lightweight and flexible schema validation library designed for developers who need a simple yet powerful way to validate and transform data. Here are some reasons to choose this package:
|
|
14
14
|
|
|
15
|
-
1. **TypeScript First**: Built with TypeScript in mind, it provides strong type inference
|
|
16
|
-
2. **Extensibility**: Easily extend the library with custom logic using the `extend` function.
|
|
17
|
-
3. **Rich Features**: Includes advanced features like preprocessing, transformations, piping,
|
|
18
|
-
4. **
|
|
19
|
-
5. **
|
|
20
|
-
6. **
|
|
15
|
+
1. **TypeScript First**: Built with TypeScript in mind, it provides strong type inference—even for deeply nested and complex schemas.
|
|
16
|
+
2. **Extensibility**: Easily extend the library with custom logic, refinements, and preprocessors using the `extend` function.
|
|
17
|
+
3. **Rich Features**: Includes advanced features like preprocessing, transformations, piping, refinements, and robust error collection (`abortEarly`).
|
|
18
|
+
4. **Actionable Error Handling**: Collect all validation errors at once for better debugging and user experience, with clear and consistent error structures (`error` and `errors`).
|
|
19
|
+
5. **Lightweight**: No dependencies and a small footprint make it ideal for projects where performance and simplicity are key.
|
|
20
|
+
6. **Customizable**: Offers fine-grained control over validation, error handling, and schema composition.
|
|
21
|
+
7. **Performance**: Optimized for speed, making it one of the fastest schema validation libraries available.
|
|
21
22
|
|
|
22
23
|
### Performance Highlights
|
|
23
24
|
|
|
@@ -306,7 +307,7 @@ const preprocessSchema = s.preprocess((value) => new Date(value), s.date());
|
|
|
306
307
|
|
|
307
308
|
### Schema Methods
|
|
308
309
|
|
|
309
|
-
#### `parse(value)`
|
|
310
|
+
#### `parse(value, parseOptions?)`
|
|
310
311
|
|
|
311
312
|
Parses the given value according to the schema.
|
|
312
313
|
|
|
@@ -314,7 +315,7 @@ Parses the given value according to the schema.
|
|
|
314
315
|
const result = stringSchema.parse('hello');
|
|
315
316
|
```
|
|
316
317
|
|
|
317
|
-
#### `safeParse(value)`
|
|
318
|
+
#### `safeParse(value, parseOptions?)`
|
|
318
319
|
|
|
319
320
|
Safely parses the given value according to the schema, returning a success or error result.
|
|
320
321
|
|
|
@@ -324,6 +325,10 @@ const result = stringSchema.safeParse('hello');
|
|
|
324
325
|
|
|
325
326
|
const errorResult = stringSchema.safeParse(123);
|
|
326
327
|
// { success: false, error: { message: 'The value "123" must be type of string but is type of "number".' } }
|
|
328
|
+
|
|
329
|
+
// Collect all errors (not just the first)
|
|
330
|
+
const allErrorsResult = stringSchema.safeParse(123, { abortEarly: false });
|
|
331
|
+
console.log(allErrorsResult.errors); // Array of all errors
|
|
327
332
|
```
|
|
328
333
|
|
|
329
334
|
**Note:** The `error` returned by `safeParse` is not a native `Error` instance. Instead, it is a plain object with the following structure:
|
|
@@ -397,6 +402,69 @@ const refinedSchema = s.string().refine((val) => val.length <= 255, {
|
|
|
397
402
|
});
|
|
398
403
|
```
|
|
399
404
|
|
|
405
|
+
#### Error Collection with `abortEarly` Option
|
|
406
|
+
|
|
407
|
+
Both `parse` and `safeParse` accept an optional second argument:
|
|
408
|
+
`parseOptions: { abortEarly?: boolean }`
|
|
409
|
+
|
|
410
|
+
- **`abortEarly`** (default: `true`):
|
|
411
|
+
If `true`, validation stops at the first error (previous behavior).
|
|
412
|
+
If `false`, all validation errors are collected and returned in the `errors` array.
|
|
413
|
+
|
|
414
|
+
**Example:**
|
|
415
|
+
|
|
416
|
+
```typescript
|
|
417
|
+
const schema = s.object({
|
|
418
|
+
name: s.string(),
|
|
419
|
+
age: s.number(),
|
|
420
|
+
email: s.string()
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
// Default behavior (abortEarly: true)
|
|
424
|
+
const result1 = schema.safeParse({
|
|
425
|
+
name: 123,
|
|
426
|
+
age: 'not a number',
|
|
427
|
+
email: 42
|
|
428
|
+
});
|
|
429
|
+
console.log(result1.success); // false
|
|
430
|
+
console.log(result1.errors.length); // 1
|
|
431
|
+
|
|
432
|
+
// Collect all errors (abortEarly: false)
|
|
433
|
+
const result2 = schema.safeParse({
|
|
434
|
+
name: 123,
|
|
435
|
+
age: 'not a number',
|
|
436
|
+
email: 42
|
|
437
|
+
}, { abortEarly: false });
|
|
438
|
+
console.log(result2.success); // false
|
|
439
|
+
console.log(result2.errors.length); // 3
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
**Error Result Structure:**
|
|
443
|
+
|
|
444
|
+
- `error`: The first error encountered (for compatibility)
|
|
445
|
+
- `errors`: Array of all errors (when `abortEarly: false`)
|
|
446
|
+
|
|
447
|
+
**Note:**
|
|
448
|
+
The `abortEarly` option is propagated through nested schemas, arrays, unions, and refinements.
|
|
449
|
+
This means you get all errors from deeply nested structures when using `{ abortEarly: false }`.
|
|
450
|
+
|
|
451
|
+
**Example Output:**
|
|
452
|
+
|
|
453
|
+
```json
|
|
454
|
+
{
|
|
455
|
+
"success": false,
|
|
456
|
+
"error": {
|
|
457
|
+
"message": "Error parsing key \"name\": The value \"123\" must be type of string but is type of \"number\".",
|
|
458
|
+
"cause": { "key": "name" }
|
|
459
|
+
},
|
|
460
|
+
"errors": [
|
|
461
|
+
{ "message": "Error parsing key \"name\": ...", "cause": { "key": "name" } },
|
|
462
|
+
{ "message": "Error parsing key \"age\": ...", "cause": { "key": "age" } },
|
|
463
|
+
{ "message": "Error parsing key \"email\": ...", "cause": { "key": "email" } }
|
|
464
|
+
]
|
|
465
|
+
}
|
|
466
|
+
```
|
|
467
|
+
|
|
400
468
|
### Extending Schemas
|
|
401
469
|
|
|
402
470
|
You can extend the schema system with custom logic.
|
package/dist/index.d.mts
CHANGED
|
@@ -11,13 +11,18 @@ type Valid<Output> = {
|
|
|
11
11
|
type Invalid = {
|
|
12
12
|
success: false;
|
|
13
13
|
error: ErrorStructure;
|
|
14
|
+
errors?: ErrorStructure[];
|
|
14
15
|
};
|
|
15
16
|
type InternalParseOutput<Output> = Valid<Output> | Invalid;
|
|
17
|
+
interface ParseOptions {
|
|
18
|
+
abortEarly?: boolean;
|
|
19
|
+
}
|
|
16
20
|
interface SchemaInterface<Input, Output> {
|
|
17
21
|
_getType(): string;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
_getDescription(): string;
|
|
23
|
+
_parse(value: Input | Partial<Input>, options?: ParseOptions): InternalParseOutput<Output>;
|
|
24
|
+
parse(value: Input | Partial<Input>, options?: ParseOptions): Output;
|
|
25
|
+
safeParse(value: Input | Partial<Input>, options?: ParseOptions): InternalParseOutput<Output>;
|
|
21
26
|
optional(): SchemaInterface<Input, Partial<Output> | undefined>;
|
|
22
27
|
transform<NewOutput>(callback: (value: Input) => NewOutput): SchemaInterface<Input, NewOutput>;
|
|
23
28
|
nullable(): SchemaInterface<Input, Output | null>;
|
package/dist/index.d.ts
CHANGED
|
@@ -11,13 +11,18 @@ type Valid<Output> = {
|
|
|
11
11
|
type Invalid = {
|
|
12
12
|
success: false;
|
|
13
13
|
error: ErrorStructure;
|
|
14
|
+
errors?: ErrorStructure[];
|
|
14
15
|
};
|
|
15
16
|
type InternalParseOutput<Output> = Valid<Output> | Invalid;
|
|
17
|
+
interface ParseOptions {
|
|
18
|
+
abortEarly?: boolean;
|
|
19
|
+
}
|
|
16
20
|
interface SchemaInterface<Input, Output> {
|
|
17
21
|
_getType(): string;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
_getDescription(): string;
|
|
23
|
+
_parse(value: Input | Partial<Input>, options?: ParseOptions): InternalParseOutput<Output>;
|
|
24
|
+
parse(value: Input | Partial<Input>, options?: ParseOptions): Output;
|
|
25
|
+
safeParse(value: Input | Partial<Input>, options?: ParseOptions): InternalParseOutput<Output>;
|
|
21
26
|
optional(): SchemaInterface<Input, Partial<Output> | undefined>;
|
|
22
27
|
transform<NewOutput>(callback: (value: Input) => NewOutput): SchemaInterface<Input, NewOutput>;
|
|
23
28
|
nullable(): SchemaInterface<Input, Output | null>;
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
'use strict';var
|
|
1
|
+
'use strict';var g={abortEarly:true};function I(e,s){if(!s)return e;let c=e?.cause?.key?`${s}.${e.cause.key}`:`${s}`;return {message:`Error parsing key "${c}": ${e.message}`,cause:{key:c}}}function y(e,s,c){!e.errors||e.errors.length===0||e.errors.forEach(i=>{let o=I(i,c);s.push(o);});}function S(e){return {...g,...e}}var b=e=>typeof e=="string"||e instanceof String,O=e=>typeof e=="number"||e instanceof Number,P=e=>e===true||e===false,k=e=>e instanceof Date&&!Number.isNaN(e.getTime()),x=e=>Array.isArray(e),T=e=>typeof e=="object"&&e!==null&&!Array.isArray(e),E={object(e,s){let c=h(T,{...s,type:"object"});return c._getDescription=()=>`object({ ${Object.entries(e).map(([o,t])=>`${o}: ${t._getDescription()}`).join(", ")} })`,m(c,"_parse",(i,o,t)=>{let r=i(o,t),{abortEarly:a}=S(t);if(r.success===false)return r;let n={},u=[];for(let p in e){let f=e[p]._parse(r.data[p],t);if(f.success)n[p]=f.data;else {if(f=f,a!==false){let l=I(f.error,p);return {success:false,error:l,errors:[l]}}y(f,u,p);}}return u.length>0?{success:false,error:u[0],errors:u}:{success:true,data:n}}),c},string(e){return h(b,{...e,type:"string"})},number(e){return h(O,{...e,type:"number"})},boolean(e){return h(P,{...e,type:"boolean"})},date(e){return h(k,{...e,type:"date"})},enum(e,s){let c=r=>e.includes(r),i=r=>`Invalid ${o} value. Expected ${e.map(a=>`"${a}"`).join(" | ")}, received "${r}".`,o="enum",t=h(c,{message:i,...s,type:o});return t._getDescription=()=>`enum(${e.map(r=>`"${r}"`).join(" | ")})`,t},array(e,s){let c=h(x,{...s,type:"array"});return c._getDescription=()=>`array(${e._getDescription()})`,m(c,"_parse",(i,o,t)=>{let r=i(o,t),{abortEarly:a}=S(t);if(r.success===false)return r;let n=[],u=[];for(let p=0;p<r.data.length;p++){let f=e._parse(r.data[p],t);if(f.success)n.push(f.data);else {if(f=f,a!==false){let l=I(f.error,p);return {success:false,error:l,errors:[l]}}y(f,u,p);}}return u.length>0?{success:false,error:u[0],errors:u}:{success:true,data:n}}),c},any(){return h(()=>true)},preprocess(e,s){return m(s,"_parse",(c,i)=>(i=e(i),c(i))),s},union(e,s){return h(t=>{for(let r=0;r<e.length;r++)if(e[r]._parse(t).success)return true;return false},{message:t=>`Invalid union value. Expected the value to match one of the schemas:${e.map((r,a)=>` ${a+1}. ${r._getDescription()}`).join(",")} but received "${typeof t}" with value: ${T(t)?JSON.stringify(t):`"${t}"`}`,...s,type:"union"})}};function w(e){return s=>`The value "${s}" must be type of ${e} but is type of "${typeof s}".`}function m(e,s,c){let i=e[s];e[s]=(...o)=>c(i,...o);}function h(e,{type:s="any",message:c}={}){c=c||w(s);let i={message:c,type:s},o={_getType(){return s},_getDescription(){return this._getType()},_parse(t,r){if(e(t))return {success:true,data:t};let n={message:typeof c=="function"?c(t):c};return {success:false,error:n,errors:[n]}},parse(t,r){let a=o._parse(t,r);if(!a.success)throw a=a,new Error(a.error.message,{cause:a.error.cause});return a.data},safeParse(t,r){return o._parse(t,r)},transform(t){return m(this,"_parse",(r,a,n)=>{let u=r(a,n);return u.success&&(u.data=t(u.data)),u}),this},optional(){return m(this,"_parse",(t,r,a)=>{let n=t(r,a);return n.success||(n.data=void 0,n.success=true),n}),this},nullable(){return m(this,"_parse",(t,r,a)=>{let n=t(r,a);return n.success||(n.data=null,n.success=true),n}),this},nullish(){return m(this,"_parse",(t,r,a)=>{let n=t(r,a);return !n.success&&r==null&&(n.success=true,n.data=r),n}),this},default(t){return m(this,"_parse",(r,a,n)=>(a===void 0&&(a=t,a=typeof t=="function"?t():t),r(a,n))),this},pipe(t){return m(this,"_parse",(r,a,n)=>{let u=r(a,n);return u.success?t._parse(u.data,n):u}),this},refine(t,{message:r}={}){return m(this,"_parse",(a,n,u)=>{let p=a(n,u);if(!p.success)return p;if(!t(p.data)){let f=typeof r=="function"?r(n):r;return {success:false,error:{message:f},errors:[{message:f}]}}return p}),this}};return d.length>0?d.reduce((t,r)=>r(t,e,i)??t,o):o}var d=[];function _(e){d.push(e);}exports.extend=_;exports.s=E;
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var g={abortEarly:true};function I(e,s){if(!s)return e;let c=e?.cause?.key?`${s}.${e.cause.key}`:`${s}`;return {message:`Error parsing key "${c}": ${e.message}`,cause:{key:c}}}function y(e,s,c){!e.errors||e.errors.length===0||e.errors.forEach(i=>{let o=I(i,c);s.push(o);});}function S(e){return {...g,...e}}var b=e=>typeof e=="string"||e instanceof String,O=e=>typeof e=="number"||e instanceof Number,P=e=>e===true||e===false,k=e=>e instanceof Date&&!Number.isNaN(e.getTime()),x=e=>Array.isArray(e),T=e=>typeof e=="object"&&e!==null&&!Array.isArray(e),E={object(e,s){let c=h(T,{...s,type:"object"});return c._getDescription=()=>`object({ ${Object.entries(e).map(([o,t])=>`${o}: ${t._getDescription()}`).join(", ")} })`,m(c,"_parse",(i,o,t)=>{let r=i(o,t),{abortEarly:a}=S(t);if(r.success===false)return r;let n={},u=[];for(let p in e){let f=e[p]._parse(r.data[p],t);if(f.success)n[p]=f.data;else {if(f=f,a!==false){let l=I(f.error,p);return {success:false,error:l,errors:[l]}}y(f,u,p);}}return u.length>0?{success:false,error:u[0],errors:u}:{success:true,data:n}}),c},string(e){return h(b,{...e,type:"string"})},number(e){return h(O,{...e,type:"number"})},boolean(e){return h(P,{...e,type:"boolean"})},date(e){return h(k,{...e,type:"date"})},enum(e,s){let c=r=>e.includes(r),i=r=>`Invalid ${o} value. Expected ${e.map(a=>`"${a}"`).join(" | ")}, received "${r}".`,o="enum",t=h(c,{message:i,...s,type:o});return t._getDescription=()=>`enum(${e.map(r=>`"${r}"`).join(" | ")})`,t},array(e,s){let c=h(x,{...s,type:"array"});return c._getDescription=()=>`array(${e._getDescription()})`,m(c,"_parse",(i,o,t)=>{let r=i(o,t),{abortEarly:a}=S(t);if(r.success===false)return r;let n=[],u=[];for(let p=0;p<r.data.length;p++){let f=e._parse(r.data[p],t);if(f.success)n.push(f.data);else {if(f=f,a!==false){let l=I(f.error,p);return {success:false,error:l,errors:[l]}}y(f,u,p);}}return u.length>0?{success:false,error:u[0],errors:u}:{success:true,data:n}}),c},any(){return h(()=>true)},preprocess(e,s){return m(s,"_parse",(c,i)=>(i=e(i),c(i))),s},union(e,s){return h(t=>{for(let r=0;r<e.length;r++)if(e[r]._parse(t).success)return true;return false},{message:t=>`Invalid union value. Expected the value to match one of the schemas:${e.map((r,a)=>` ${a+1}. ${r._getDescription()}`).join(",")} but received "${typeof t}" with value: ${T(t)?JSON.stringify(t):`"${t}"`}`,...s,type:"union"})}};function w(e){return s=>`The value "${s}" must be type of ${e} but is type of "${typeof s}".`}function m(e,s,c){let i=e[s];e[s]=(...o)=>c(i,...o);}function h(e,{type:s="any",message:c}={}){c=c||w(s);let i={message:c,type:s},o={_getType(){return s},_getDescription(){return this._getType()},_parse(t,r){if(e(t))return {success:true,data:t};let n={message:typeof c=="function"?c(t):c};return {success:false,error:n,errors:[n]}},parse(t,r){let a=o._parse(t,r);if(!a.success)throw a=a,new Error(a.error.message,{cause:a.error.cause});return a.data},safeParse(t,r){return o._parse(t,r)},transform(t){return m(this,"_parse",(r,a,n)=>{let u=r(a,n);return u.success&&(u.data=t(u.data)),u}),this},optional(){return m(this,"_parse",(t,r,a)=>{let n=t(r,a);return n.success||(n.data=void 0,n.success=true),n}),this},nullable(){return m(this,"_parse",(t,r,a)=>{let n=t(r,a);return n.success||(n.data=null,n.success=true),n}),this},nullish(){return m(this,"_parse",(t,r,a)=>{let n=t(r,a);return !n.success&&r==null&&(n.success=true,n.data=r),n}),this},default(t){return m(this,"_parse",(r,a,n)=>(a===void 0&&(a=t,a=typeof t=="function"?t():t),r(a,n))),this},pipe(t){return m(this,"_parse",(r,a,n)=>{let u=r(a,n);return u.success?t._parse(u.data,n):u}),this},refine(t,{message:r}={}){return m(this,"_parse",(a,n,u)=>{let p=a(n,u);if(!p.success)return p;if(!t(p.data)){let f=typeof r=="function"?r(n):r;return {success:false,error:{message:f},errors:[{message:f}]}}return p}),this}};return d.length>0?d.reduce((t,r)=>r(t,e,i)??t,o):o}var d=[];function _(e){d.push(e);}export{_ as extend,E as s};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@esmj/schema",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Tiny extendable package for schema validation.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"schema",
|
|
@@ -83,7 +83,6 @@
|
|
|
83
83
|
"husky": "^9.1.7",
|
|
84
84
|
"joi": "^17.13.3",
|
|
85
85
|
"lint-staged": "^16.1.0",
|
|
86
|
-
"prettier": "^3.5.3",
|
|
87
86
|
"superstruct": "^2.0.2",
|
|
88
87
|
"tsup": "^8.5.0",
|
|
89
88
|
"yup": "^1.6.1",
|