@zipbul/baker 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 CHANGED
@@ -39,7 +39,7 @@ Baker gives you a **single `@Field()` decorator** that combines validation, tran
39
39
  - **Single decorator** — `@Field()` replaces 30+ individual decorators
40
40
  - **80+ built-in rules** — `isString`, `min()`, `isEmail()` and more, composed as arguments
41
41
  - **Inline code generation** — auto-seal compiles validators at first `deserialize()`/`serialize()` call
42
- - **Unified validate + transform** — `deserialize()` and `serialize()` in one async call
42
+ - **Unified validate + transform** — `deserialize()` and `serialize()` in one call (sync DTOs skip async overhead)
43
43
  - **Zero reflect-metadata** — no `reflect-metadata` import needed
44
44
  - **Circular reference detection** — automatic static analysis at seal time
45
45
  - **Group-based validation** — apply different rules per request with `groups`
@@ -49,6 +49,8 @@ Baker gives you a **single `@Field()` decorator** that combines validation, tran
49
49
  - **Whitelist mode** — reject undeclared fields with `configure({ forbidUnknown: true })`
50
50
  - **Class inheritance** — child DTOs inherit parent `@Field()` decorators automatically
51
51
  - **Async transforms** — transform functions can be async
52
+ - **Map/Set support** — auto-convert between `Map`/`Set` and JSON-compatible types
53
+ - **Per-field error messages** — `message` and `context` options on `@Field()` for custom errors
52
54
 
53
55
  ---
54
56
 
@@ -161,16 +163,29 @@ interface FieldOptions {
161
163
  schema?: JsonSchemaOverride; // JSON Schema metadata
162
164
  transform?: (params: FieldTransformParams) => unknown;
163
165
  transformDirection?: 'deserializeOnly' | 'serializeOnly';
166
+ message?: string | ((args: MessageArgs) => string); // Error message for all rules
167
+ context?: unknown; // Error context for all rules
168
+ mapValue?: () => Constructor; // Map value DTO type
169
+ setValue?: () => Constructor; // Set element DTO type
164
170
  }
165
171
  ```
166
172
 
167
- ### Per-rule Options (message, groups)
173
+ ### Per-field Error Messages
168
174
 
169
- Per-rule options like `message`, `groups`, and `context` are **not** passed as arguments to individual rule functions. Instead, they are controlled at the `@Field()` level:
175
+ Use `message` and `context` to customize validation error output:
170
176
 
171
- - **`groups`** — set via `FieldOptions.groups` (applies to all rules on that field)
172
- - **`message`** / **`context`** — use `createRule()` for custom error messages, or handle via `BakerError.code`
173
- - **`each` (array element validation)** — use `arrayOf()` (see below)
177
+ ```typescript
178
+ @Field(isString, minLength(3), { message: 'Name is invalid' })
179
+ name!: string;
180
+
181
+ @Field(isEmail(), {
182
+ message: ({ property, value }) => `${property} got bad value: ${value}`,
183
+ context: { severity: 'error' },
184
+ })
185
+ email!: string;
186
+ ```
187
+
188
+ The `message` and `context` are applied to all rules on the field. They appear in `BakerError.message` and `BakerError.context` on validation failure.
174
189
 
175
190
  ### `arrayOf()` — Array Element Validation
176
191
 
@@ -446,6 +461,30 @@ class PetOwnerDto {
446
461
 
447
462
  Discriminator works in both directions — `deserialize()` switches on the property value, `serialize()` dispatches via `instanceof`.
448
463
 
464
+ ### Map / Set Collections
465
+
466
+ Baker auto-converts between `Map`/`Set` and JSON-compatible types:
467
+
468
+ ```typescript
469
+ // Set<primitive>: JSON array ↔ Set
470
+ @Field({ type: () => Set })
471
+ tags!: Set<string>;
472
+
473
+ // Set<DTO>: JSON array of objects ↔ Set of DTO instances
474
+ @Field({ type: () => Set, setValue: () => TagDto })
475
+ tags!: Set<TagDto>;
476
+
477
+ // Map<string, primitive>: JSON object ↔ Map
478
+ @Field({ type: () => Map })
479
+ config!: Map<string, unknown>;
480
+
481
+ // Map<string, DTO>: JSON object ↔ Map of DTO instances
482
+ @Field({ type: () => Map, mapValue: () => PriceDto })
483
+ prices!: Map<string, PriceDto>;
484
+ ```
485
+
486
+ Map keys are always strings (JSON constraint). JSON Schema maps `Set` to `{ type: 'array', uniqueItems: true }` and `Map` to `{ type: 'object', additionalProperties: ... }`.
487
+
449
488
  ---
450
489
 
451
490
  ## Inheritance
@@ -0,0 +1,6 @@
1
+ // @bun
2
+ var b=Symbol.for("baker:raw"),f=Symbol.for("baker:sealed"),g=Symbol.for("baker:rawClassSchema");
3
+ export{b as e,f,g};
4
+
5
+ //# debugId=725867D5C005BD2264756E2164756E21
6
+ //# sourceMappingURL=index-70ggmxsa.js.map
@@ -0,0 +1,6 @@
1
+ // @bun
2
+ import{e as Q}from"./index-70ggmxsa.js";var T=new Set;function U(b,G){if(!Object.prototype.hasOwnProperty.call(b,Q))b[Q]=Object.create(null),T.add(b);let j=b[Q];return j[G]??={validation:[],transform:[],expose:[],exclude:null,type:null,flags:{},schema:null}}function X(b){return b[Symbol.toStringTag]==="AsyncFunction"}var $=Symbol.for("baker:arrayOf");function W(...b){let G={rules:b};return G[$]=!0,G}function v(b){return typeof b==="object"&&b!==null&&b[$]===!0}var N=new Set(["type","discriminator","keepDiscriminatorProperty","rules","optional","nullable","name","deserializeName","serializeName","exclude","groups","when","schema","transform","transformDirection","message","context","mapValue","setValue"]);function Z(b){if(typeof b==="function")return!1;if(typeof b!=="object"||b===null)return!1;if(v(b))return!1;let G=Object.keys(b);if(G.length===0)return!0;return G.some((j)=>N.has(j))}function P(b){if(b.length===0)return{rules:[],options:{}};if(b.length===1&&Z(b[0])){let j=b[0];return{rules:j.rules??[],options:j}}let G=b[b.length-1];if(Z(G)){let j=G,B=b.slice(0,-1);if(j.rules)B=[...B,...j.rules];return{rules:B,options:j}}return{rules:b,options:{}}}function S(b,G,j){for(let B of G)if(v(B))for(let J of B.rules){let q={rule:J,each:!0,groups:j.groups};if(j.message!==void 0)q.message=j.message;if(j.context!==void 0)q.context=j.context;b.validation.push(q)}else{let J={rule:B,groups:j.groups};if(j.message!==void 0)J.message=j.message;if(j.context!==void 0)J.context=j.context;b.validation.push(J)}}function V(b,G){if(G.name)b.expose.push({name:G.name,groups:G.groups});else if(G.deserializeName||G.serializeName){if(G.deserializeName)b.expose.push({name:G.deserializeName,deserializeOnly:!0,groups:G.groups});if(G.serializeName)b.expose.push({name:G.serializeName,serializeOnly:!0,groups:G.groups})}else if(G.groups)b.expose.push({groups:G.groups});else b.expose.push({})}function H(b,G){if(!G.transform)return;let j=G.transform,J=X(j)?async(z)=>j({value:z.value,key:z.key,obj:z.obj,direction:z.type}):(z)=>j({value:z.value,key:z.key,obj:z.obj,direction:z.type});if(G.transformDirection&&G.transformDirection!=="deserializeOnly"&&G.transformDirection!=="serializeOnly")throw Error(`Invalid transformDirection: "${G.transformDirection}". Expected 'deserializeOnly' or 'serializeOnly'.`);let q={};if(G.transformDirection==="deserializeOnly")q.deserializeOnly=!0;if(G.transformDirection==="serializeOnly")q.serializeOnly=!0;b.transform.push({fn:J,options:Object.keys(q).length>0?q:void 0})}function d(...b){return(G,j)=>{let B=G.constructor,q=U(B,j),{rules:z,options:w}=P(b);if(S(q,z,w),w.optional)q.flags.isOptional=!0;if(w.nullable)q.flags.isNullable=!0;if(w.when)q.flags.validateIf=w.when;if(w.type)q.type={fn:w.type,discriminator:w.discriminator,keepDiscriminatorProperty:w.keepDiscriminatorProperty,collectionValue:w.mapValue??w.setValue};if(V(q,w),w.exclude){if(w.exclude===!0)q.exclude={};else if(w.exclude==="deserializeOnly")q.exclude={deserializeOnly:!0};else if(w.exclude==="serializeOnly")q.exclude={serializeOnly:!0}}if(H(q,w),w.schema)if(typeof q.schema==="function");else q.schema={...q.schema??{},...w.schema}}}
3
+ export{T as a,X as b,W as c,d};
4
+
5
+ //# debugId=31CC953F620CB89C64756E2164756E21
6
+ //# sourceMappingURL=index-gcptd79v.js.map