@zipbul/baker 2.1.0 → 2.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 +20 -20
- package/dist/index-03cysbck.js +3 -0
- package/dist/index-dcbd798a.js +3 -0
- package/dist/{index-fnv35wrf.js → index-jp2yjd6g.js} +1 -1
- package/dist/{index-s0n74vx1.js → index-mw7met6r.js} +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +301 -209
- package/dist/src/configure.d.ts +1 -6
- package/dist/src/create-rule.d.ts +1 -1
- package/dist/src/decorators/index.js +1 -1
- package/dist/src/errors.d.ts +4 -3
- package/dist/src/functions/deserialize.d.ts +4 -3
- package/dist/src/functions/serialize.d.ts +3 -2
- package/dist/src/functions/validate.d.ts +6 -4
- package/dist/src/rule-plan.d.ts +30 -0
- package/dist/src/rules/array.d.ts +1 -1
- package/dist/src/rules/common.d.ts +2 -2
- package/dist/src/rules/index.js +18 -8
- package/dist/src/rules/number.d.ts +2 -2
- package/dist/src/rules/string.d.ts +9 -9
- package/dist/src/rules/typechecker.d.ts +6 -6
- package/dist/src/seal/codegen-utils.d.ts +7 -0
- package/dist/src/seal/deserialize-builder.d.ts +2 -1
- package/dist/src/seal/seal.d.ts +1 -1
- package/dist/src/symbols.js +1 -1
- package/dist/src/transformers/index.js +1 -1
- package/dist/src/types.d.ts +44 -6
- package/dist/src/utils.d.ts +2 -0
- package/package.json +1 -1
- package/dist/index-k3d659ad.js +0 -3
package/README.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# @zipbul/baker
|
|
2
2
|
|
|
3
|
-
The fastest decorator-based DTO validation library for TypeScript. Generates optimized validation
|
|
3
|
+
The fastest decorator-based DTO validation library for TypeScript. Generates optimized validation and serialization code on first seal, then reuses the sealed executors on every call.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
bun add @zipbul/baker
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
Zero `reflect-metadata`.
|
|
9
|
+
Zero `reflect-metadata`. Sealed codegen. 1,975 tests. 99%+ line coverage.
|
|
10
10
|
|
|
11
11
|
## Quick Start
|
|
12
12
|
|
|
@@ -33,12 +33,12 @@ if (isBakerError(result)) {
|
|
|
33
33
|
|
|
34
34
|
## Why Baker?
|
|
35
35
|
|
|
36
|
-
Baker generates optimized JavaScript
|
|
36
|
+
Baker generates optimized JavaScript functions once on first seal, then executes them on every call.
|
|
37
37
|
|
|
38
38
|
| Feature | baker | class-validator | Zod |
|
|
39
39
|
|---|---|---|---|
|
|
40
|
-
| Valid path (5 fields) | **
|
|
41
|
-
| Invalid path (5 fields) | **
|
|
40
|
+
| Valid path (5 fields) | **fast sealed path** | slower | slower |
|
|
41
|
+
| Invalid path (5 fields) | **fast sealed path** | slower | slower |
|
|
42
42
|
| Approach | AOT code generation | Runtime interpretation | Schema method chain |
|
|
43
43
|
| Decorators | `@Field` (unified) | 30+ individual | N/A |
|
|
44
44
|
| `reflect-metadata` | Not needed | Required | N/A |
|
|
@@ -46,31 +46,23 @@ Baker generates optimized JavaScript validation functions **once** at class defi
|
|
|
46
46
|
|
|
47
47
|
## Performance
|
|
48
48
|
|
|
49
|
-
Benchmarked against
|
|
49
|
+
Benchmarked against multiple libraries on simple, nested, array, and error-collection scenarios. Exact numbers vary by machine and runtime.
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|---|---|---|---|---|
|
|
53
|
-
| **baker** | **42ns** | **93ns** | — | — |
|
|
54
|
-
| TypeBox | 123ns | 112ns | 2.9x slower | 1.2x slower |
|
|
55
|
-
| AJV | 142ns | 201ns | 3.4x slower | 2.2x slower |
|
|
56
|
-
| ArkType | 145ns | 8,591ns | 3.4x slower | 92x slower |
|
|
57
|
-
| Valibot | 281ns | 1,070ns | 6.7x slower | 12x slower |
|
|
58
|
-
| Zod | 675ns | 7,948ns | 16x slower | 85x slower |
|
|
59
|
-
| class-validator | 6,852ns | 10,109ns | 163x slower | 109x slower |
|
|
51
|
+
See [`bench/`](./bench) for the current benchmark suite and exact scenarios.
|
|
60
52
|
|
|
61
53
|
## API
|
|
62
54
|
|
|
63
55
|
### `deserialize<T>(Class, input, options?)`
|
|
64
56
|
|
|
65
|
-
Returns `T | BakerErrors
|
|
57
|
+
Returns `T | BakerErrors` for sync DTOs, `Promise<T | BakerErrors>` for async DTOs. Never throws on validation failure.
|
|
66
58
|
|
|
67
59
|
### `serialize<T>(instance, options?)`
|
|
68
60
|
|
|
69
|
-
Returns `Record<string, unknown
|
|
61
|
+
Returns `Record<string, unknown>` for sync DTOs, `Promise<Record<string, unknown>>` for async DTOs. No validation.
|
|
70
62
|
|
|
71
63
|
### `validate(Class, input, options?)` / `validate(input, ...rules)`
|
|
72
64
|
|
|
73
|
-
DTO-level or ad-hoc single-value validation. Returns `true | BakerErrors
|
|
65
|
+
DTO-level or ad-hoc single-value validation. Returns `true | BakerErrors` for sync paths, `Promise<true | BakerErrors>` for async paths.
|
|
74
66
|
|
|
75
67
|
### `isBakerError(value)`
|
|
76
68
|
|
|
@@ -78,7 +70,7 @@ Type guard. Narrows result to `BakerErrors` containing `{ path, code, message?,
|
|
|
78
70
|
|
|
79
71
|
### `configure(config)`
|
|
80
72
|
|
|
81
|
-
Global configuration. Call before first deserialize/serialize/validate.
|
|
73
|
+
Global configuration. Call before first deserialize/serialize/validate. Calling it after auto-seal throws `SealError`.
|
|
82
74
|
|
|
83
75
|
```typescript
|
|
84
76
|
configure({
|
|
@@ -91,7 +83,15 @@ configure({
|
|
|
91
83
|
|
|
92
84
|
### `createRule(name, validate)`
|
|
93
85
|
|
|
94
|
-
Custom validation rule
|
|
86
|
+
Custom validation rule.
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
const isEven = createRule({
|
|
90
|
+
name: 'isEven',
|
|
91
|
+
validate: (v) => typeof v === 'number' && v % 2 === 0,
|
|
92
|
+
requiresType: 'number',
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
95
|
|
|
96
96
|
## @Field Decorator
|
|
97
97
|
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{o as C}from"./index-jp2yjd6g.js";var H=new Set;function J(h,G){if(!Object.prototype.hasOwnProperty.call(h,C))h[C]=Object.create(null),H.add(h);let S=h[C];return S[G]??={validation:[],transform:[],expose:[],exclude:null,type:null,flags:{}}}function Q(h){return h[Symbol.toStringTag]==="AsyncFunction"}function U(h){return(typeof h==="object"||typeof h==="function")&&h!==null&&typeof h.then==="function"}var $=Symbol.for("baker:arrayOf");function M(...h){let G={rules:h};return G[$]=!0,G}function N(h){return typeof h==="object"&&h!==null&&h[$]===!0}var V=new Set(["type","discriminator","keepDiscriminatorProperty","rules","optional","nullable","name","deserializeName","serializeName","exclude","groups","when","transform","message","context","mapValue","setValue"]);function X(h){if(typeof h==="function")return!1;if(typeof h!=="object"||h===null)return!1;if(N(h))return!1;let G=Object.keys(h);if(G.length===0)return!0;return G.some((S)=>V.has(S))}function I(h){if(h.length===0)return{rules:[],options:{}};if(h.length===1&&X(h[0])){let S=h[0];return{rules:S.rules??[],options:S}}let G=h[h.length-1];if(X(G)){let S=G,q=h.slice(0,-1);if(S.rules)q=[...q,...S.rules];return{rules:q,options:S}}return{rules:h,options:{}}}function L(h,G,S){for(let q of G)if(N(q))for(let w of q.rules){let b={rule:w,each:!0,groups:S.groups};if(S.message!==void 0)b.message=S.message;if(S.context!==void 0)b.context=S.context;h.validation.push(b)}else{let w={rule:q,groups:S.groups};if(S.message!==void 0)w.message=S.message;if(S.context!==void 0)w.context=S.context;h.validation.push(w)}}function E(h,G){if(G.name)h.expose.push({name:G.name,groups:G.groups});else if(G.deserializeName||G.serializeName){if(G.deserializeName)h.expose.push({name:G.deserializeName,deserializeOnly:!0,groups:G.groups});if(G.serializeName)h.expose.push({name:G.serializeName,serializeOnly:!0,groups:G.groups})}else if(G.groups)h.expose.push({groups:G.groups});else h.expose.push({})}function Z(h,G,S){let q=Q(S);return{fn:(b)=>{let B=S(b);if(!q&&U(B))throw Error(`@Field(${h}) ${G} transform returned Promise. Declare the transform with async if it is asynchronous.`);return B},isAsync:q}}function P(h,G,S){if(!S.transform)return;let q=Array.isArray(S.transform)?S.transform:[S.transform];for(let w of q){let b=Z(G,"deserialize",w.deserialize),B=Z(G,"serialize",w.serialize);h.transform.push({fn:b.fn,isAsync:b.isAsync,options:{deserializeOnly:!0}},{fn:B.fn,isAsync:B.isAsync,options:{serializeOnly:!0}})}}function v(...h){return(G,S)=>{let q=G.constructor,w=S,b=J(q,w),{rules:B,options:j}=I(h);if(L(b,B,j),j.optional)b.flags.isOptional=!0;if(j.nullable)b.flags.isNullable=!0;if(j.when)b.flags.validateIf=j.when;if(j.type)b.type={fn:j.type,discriminator:j.discriminator,keepDiscriminatorProperty:j.keepDiscriminatorProperty,collectionValue:j.mapValue??j.setValue};if(E(b,j),j.exclude){if(j.exclude===!0)b.exclude={};else if(j.exclude==="deserializeOnly")b.exclude={deserializeOnly:!0};else if(j.exclude==="serializeOnly")b.exclude={serializeOnly:!0}}P(b,w,j)}}
|
|
3
|
+
export{H as j,Q as k,U as l,M as m,v as n};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
var I=()=>({kind:"value"}),Y=(w=I())=>({kind:"member",object:w,property:"length"}),Z=(w=I())=>({kind:"call0",object:w,method:"getTime"}),O=(w)=>({kind:"literal",value:w}),_=(w,z,A)=>({kind:"compare",left:w,op:z,right:typeof A==="number"?O(A):A});var $=(...w)=>({kind:"or",checks:w});function L(w){return Q({name:w.name,requiresType:w.requiresType,constraints:w.constraints,plan:w.plan,validate:w.validate,emit:(z,A)=>U(z,A,w.name,w.plan)})}function Q(w){let z=(A)=>w.validate(A);if(z.emit=w.emit,z.ruleName=w.name,w.requiresType!==void 0)z.requiresType=w.requiresType;if(z.constraints=w.constraints??{},w.isAsync)z.isAsync=!0;if(w.plan)z.plan=w.plan;return z}function U(w,z,A,B,F,K){let M=K?W(B.failure):B.failure;return`if (${J(M,w,F)}) ${z.fail(A)};`}function W(w){if(w.kind==="compare")return w;let z=w.checks.filter((A)=>!X(A));if(z.length===0)return w;if(z.length===1)return z[0];return{kind:w.kind,checks:z}}function X(w){if(w.kind!=="compare"||w.op!=="!==")return!1;return H(w.left,w.right)}function H(w,z){if(w.kind!==z.kind)return!1;if(w.kind==="value")return!0;if(w.kind==="literal")return w.value===z.value;if(w.kind==="member")return H(w.object,z.object);if(w.kind==="call0")return w.method===z.method&&H(w.object,z.object);return!1}function J(w,z,A){if(w.kind==="compare")return`${D(w.left,z,A)} ${w.op} ${D(w.right,z,A)}`;let B=w.kind==="and"?" && ":" || ";return`(${w.checks.map((F)=>J(F,z,A)).join(B)})`}function D(w,z,A){switch(w.kind){case"value":return z;case"literal":return String(w.value);case"member":return A?.length??`${D(w.object,z,A)}.length`;case"call0":return A?.time??`${D(w.object,z,A)}.getTime()`}}
|
|
3
|
+
export{I as a,Y as b,Z as c,O as d,_ as e,$ as f,L as g,Q as h,U as i};
|
package/dist/index.d.ts
CHANGED
|
@@ -8,5 +8,5 @@ export type { FieldOptions, ArrayOfMarker } from './src/decorators/index';
|
|
|
8
8
|
export type { BakerError, BakerErrors } from './src/errors';
|
|
9
9
|
export { isBakerError, SealError } from './src/errors';
|
|
10
10
|
export type { EmittableRule, Transformer, TransformParams } from './src/types';
|
|
11
|
-
export type { BakerConfig
|
|
11
|
+
export type { BakerConfig } from './src/configure';
|
|
12
12
|
export type { RuntimeOptions } from './src/interfaces';
|