@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 CHANGED
@@ -1,12 +1,12 @@
1
1
  # @zipbul/baker
2
2
 
3
- The fastest decorator-based DTO validation library for TypeScript. Generates optimized validation code at class definition time (AOT), delivering **42ns per validation** up to 163x faster than class-validator, 16x faster than Zod.
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`. Zero runtime overhead. 1,890 tests. 99%+ line coverage.
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 validation functions **once** at class definition time, then executes them on every call — no interpretation, no schema traversal, no runtime compilation cost after the first seal.
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) | **42ns** | 6,852ns | 675ns |
41
- | Invalid path (5 fields) | **93ns** | 10,109ns | 7,948ns |
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 6 libraries on a simple 5-field DTO (valid + invalid input):
49
+ Benchmarked against multiple libraries on simple, nested, array, and error-collection scenarios. Exact numbers vary by machine and runtime.
50
50
 
51
- | Library | Valid | Invalid | vs baker (valid) | vs baker (invalid) |
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 | Promise<T | BakerErrors>`. Sync DTOs return directly — no Promise wrapping. Never throws on validation failure.
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> | Promise<Record<string, unknown>>`. No validation. Sync DTOs return directly.
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 with optional AOT `emit()` for maximum performance.
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};
@@ -1,3 +1,3 @@
1
1
  // @bun
2
2
  var f=Symbol.for("baker:raw"),g=Symbol.for("baker:sealed");
3
- export{f as d,g as e};
3
+ export{f as o,g as p};
@@ -1,3 +1,3 @@
1
1
  // @bun
2
2
  var a=import.meta.require;
3
- export{a as f};
3
+ export{a as q};
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, ConfigureResult } from './src/configure';
11
+ export type { BakerConfig } from './src/configure';
12
12
  export type { RuntimeOptions } from './src/interfaces';