@zipbul/baker 2.0.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 +155 -107
- package/dist/index-03cysbck.js +3 -0
- package/dist/index-dcbd798a.js +3 -0
- package/dist/index-jp2yjd6g.js +3 -0
- package/dist/index-mw7met6r.js +3 -0
- package/dist/index-xdn55cz3.js +0 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +296 -206
- package/dist/src/configure.d.ts +1 -6
- package/dist/src/create-rule.d.ts +1 -1
- package/dist/src/decorators/field.d.ts +4 -12
- package/dist/src/decorators/index.d.ts +1 -1
- package/dist/src/decorators/index.js +1 -4
- 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.d.ts +1 -1
- package/dist/src/rules/index.js +18 -11
- package/dist/src/rules/number.d.ts +2 -2
- package/dist/src/rules/string.d.ts +11 -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 -4
- package/dist/src/transformers/collection.transformer.d.ts +3 -0
- package/dist/src/transformers/date.transformer.d.ts +4 -0
- package/dist/src/transformers/index.d.ts +8 -0
- package/dist/src/transformers/index.js +2 -0
- package/dist/src/transformers/luxon.transformer.d.ts +6 -0
- package/dist/src/transformers/moment.transformer.d.ts +5 -0
- package/dist/src/transformers/number.transformer.d.ts +2 -0
- package/dist/src/transformers/string.transformer.d.ts +4 -0
- package/dist/src/types.d.ts +48 -8
- package/dist/src/utils.d.ts +2 -0
- package/package.json +28 -4
- package/dist/index-btgens0c.js +0 -6
- package/dist/index-k369bbht.js +0 -6
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Convert key to a valid JS identifier suffix (encode non-alphanumeric chars via charCode to prevent collisions) */
|
|
2
|
+
export declare function sanitizeKey(key: string): string;
|
|
3
|
+
/**
|
|
4
|
+
* Generate a groups-has expression for the fast-path single-group / Set pattern.
|
|
5
|
+
* Checks if any of the given groups match the runtime groups.
|
|
6
|
+
*/
|
|
7
|
+
export declare function buildGroupsHasExpr(singleGroupVar: string, groupsVar: string, groups: string[]): string;
|
|
@@ -2,4 +2,5 @@ import type { Result, ResultAsync } from '@zipbul/result';
|
|
|
2
2
|
import type { RawClassMeta } from '../types';
|
|
3
3
|
import type { SealOptions, RuntimeOptions } from '../interfaces';
|
|
4
4
|
import { type BakerError } from '../errors';
|
|
5
|
-
export declare function buildDeserializeCode<T>(Class: Function, merged: RawClassMeta, options: SealOptions | undefined, needsCircularCheck: boolean, isAsync: boolean): (input: unknown, opts?: RuntimeOptions) => Result<T, BakerError[]> | ResultAsync<T, BakerError[]>;
|
|
5
|
+
export declare function buildDeserializeCode<T>(Class: Function, merged: RawClassMeta, options: SealOptions | undefined, needsCircularCheck: boolean, isAsync: boolean, validateOnly?: boolean): (input: unknown, opts?: RuntimeOptions) => Result<T, BakerError[]> | ResultAsync<T, BakerError[]>;
|
|
6
|
+
export declare function buildValidateCode(Class: Function, merged: RawClassMeta, options: SealOptions | undefined, needsCircularCheck: boolean, isAsync: boolean): (input: unknown, opts?: RuntimeOptions) => BakerError[] | null | Promise<BakerError[] | null>;
|
package/dist/src/seal/seal.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { RawClassMeta, SealedExecutors } from '../types';
|
|
2
2
|
/** @internal Placeholder executor for circular dependency detection during seal */
|
|
3
3
|
declare function _circularPlaceholder(className: string): SealedExecutors<unknown>;
|
|
4
|
-
/** @internal — used by configure() to
|
|
4
|
+
/** @internal — used by configure() to reject post-seal calls */
|
|
5
5
|
export declare function _isSealed(): boolean;
|
|
6
6
|
/** List of sealed classes — used by unseal to remove SEALED */
|
|
7
7
|
export declare const _sealedClasses: Set<Function>;
|
package/dist/src/symbols.js
CHANGED
|
@@ -1,5 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{
|
|
3
|
-
|
|
4
|
-
//# debugId=90C1D92A7675C52B64756E2164756E21
|
|
5
|
-
//# sourceMappingURL=symbols.js.map
|
|
2
|
+
import{o as a,p as b}from"../index-jp2yjd6g.js";import"../index-mw7met6r.js";export{b as SEALED,a as RAW};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { trimTransformer, toLowerCaseTransformer, toUpperCaseTransformer } from './string.transformer';
|
|
2
|
+
export { roundTransformer } from './number.transformer';
|
|
3
|
+
export { unixSecondsTransformer, unixMillisTransformer, isoStringTransformer } from './date.transformer';
|
|
4
|
+
export { csvTransformer, jsonTransformer } from './collection.transformer';
|
|
5
|
+
export { luxonTransformer } from './luxon.transformer';
|
|
6
|
+
export type { LuxonTransformerOptions } from './luxon.transformer';
|
|
7
|
+
export { momentTransformer } from './moment.transformer';
|
|
8
|
+
export type { MomentTransformerOptions } from './moment.transformer';
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{q as n}from"../../index-mw7met6r.js";var s={deserialize:({value:r})=>typeof r==="string"?r.trim():r,serialize:({value:r})=>typeof r==="string"?r.trim():r},i={deserialize:({value:r})=>typeof r==="string"?r.toLowerCase():r,serialize:({value:r})=>typeof r==="string"?r.toLowerCase():r},f={deserialize:({value:r})=>typeof r==="string"?r.toUpperCase():r,serialize:({value:r})=>typeof r==="string"?r.toUpperCase():r};function m(r=0){let t=Math.pow(10,r),e=(o)=>typeof o==="number"&&Number.isFinite(o)?Math.round(o*t)/t:o;return{deserialize:({value:o})=>e(o),serialize:({value:o})=>e(o)}}var a={deserialize:({value:r})=>typeof r==="number"?new Date(r*1000):r,serialize:({value:r})=>r instanceof Date?Math.floor(r.getTime()/1000):r},p={deserialize:({value:r})=>typeof r==="number"?new Date(r):r,serialize:({value:r})=>r instanceof Date?r.getTime():r},T={deserialize:({value:r})=>{if(typeof r!=="string")return r;let t=new Date(r);return Number.isNaN(t.getTime())?r:t},serialize:({value:r})=>r instanceof Date?r.toISOString():r};function y(r=","){return{deserialize:({value:t})=>typeof t==="string"?t.split(r):t,serialize:({value:t})=>Array.isArray(t)?t.join(r):t}}var c={deserialize:({value:r})=>{if(typeof r!=="string")return r;try{return JSON.parse(r)}catch{return r}},serialize:({value:r})=>{if(r!=null&&typeof r==="object")return JSON.stringify(r);return r}};async function x(r){let{DateTime:t}=await import("luxon"),e=r?.zone??"utc";return{deserialize:({value:o})=>{if(typeof o==="string")return t.fromISO(o,{zone:e});if(o instanceof Date)return t.fromJSDate(o,{zone:e});return o},serialize:({value:o})=>{if(o&&typeof o==="object"&&typeof o.toISO==="function")return r?.format?o.toFormat(r.format):o.toISO();return o}}}async function z(r){let t=(await import("moment")).default;return{deserialize:({value:e})=>{if(typeof e==="string"||e instanceof Date)return t(e);return e},serialize:({value:e})=>{if(e&&typeof e==="object"&&typeof e.toISOString==="function"&&typeof e.format==="function")return r?.format?e.format(r.format):e.toISOString();return e}}}export{a as unixSecondsTransformer,p as unixMillisTransformer,s as trimTransformer,f as toUpperCaseTransformer,i as toLowerCaseTransformer,m as roundTransformer,z as momentTransformer,x as luxonTransformer,c as jsonTransformer,T as isoStringTransformer,y as csvTransformer};
|
package/dist/src/types.d.ts
CHANGED
|
@@ -9,6 +9,10 @@ export interface EmitContext {
|
|
|
9
9
|
fail(code: string): string;
|
|
10
10
|
/** Whether error collection mode is enabled (= !stopAtFirstError) */
|
|
11
11
|
collectErrors: boolean;
|
|
12
|
+
/** Whether this emit runs inside a type gate (typeof/instanceof already verified) */
|
|
13
|
+
insideTypeGate?: boolean;
|
|
14
|
+
/** @internal Path expression for inline nested — used by makeRuleEmitCtx */
|
|
15
|
+
_pathExpr?: string;
|
|
12
16
|
}
|
|
13
17
|
export interface EmittableRule {
|
|
14
18
|
(value: unknown): boolean | Promise<boolean>;
|
|
@@ -19,12 +23,43 @@ export interface EmittableRule {
|
|
|
19
23
|
* Only set for rules that assume a specific type (e.g., isEmail → 'string').
|
|
20
24
|
* @IsString itself is undefined (it includes its own typeof check).
|
|
21
25
|
*/
|
|
22
|
-
readonly requiresType?: 'string' | 'number' | 'boolean' | 'date';
|
|
26
|
+
readonly requiresType?: 'string' | 'number' | 'boolean' | 'date' | 'array' | 'object';
|
|
23
27
|
/** Expose rule parameters for external reading */
|
|
24
28
|
readonly constraints?: Record<string, unknown>;
|
|
25
|
-
/** true when
|
|
29
|
+
/** true when the rule is explicitly async and must be awaited */
|
|
26
30
|
readonly isAsync?: boolean;
|
|
27
31
|
}
|
|
32
|
+
/** @internal internal rule shape used by builders for optimization metadata */
|
|
33
|
+
export interface InternalRule extends EmittableRule {
|
|
34
|
+
readonly plan?: RulePlan;
|
|
35
|
+
}
|
|
36
|
+
export type RulePlanExpr = {
|
|
37
|
+
kind: 'value';
|
|
38
|
+
} | {
|
|
39
|
+
kind: 'member';
|
|
40
|
+
object: RulePlanExpr;
|
|
41
|
+
property: 'length';
|
|
42
|
+
} | {
|
|
43
|
+
kind: 'call0';
|
|
44
|
+
object: RulePlanExpr;
|
|
45
|
+
method: 'getTime';
|
|
46
|
+
} | {
|
|
47
|
+
kind: 'literal';
|
|
48
|
+
value: number;
|
|
49
|
+
};
|
|
50
|
+
export type RulePlanCheck = {
|
|
51
|
+
kind: 'compare';
|
|
52
|
+
left: RulePlanExpr;
|
|
53
|
+
op: '<' | '<=' | '>' | '>=' | '===' | '!==';
|
|
54
|
+
right: RulePlanExpr;
|
|
55
|
+
} | {
|
|
56
|
+
kind: 'and' | 'or';
|
|
57
|
+
checks: RulePlanCheck[];
|
|
58
|
+
};
|
|
59
|
+
export interface RulePlan {
|
|
60
|
+
cacheKey?: 'length' | 'time';
|
|
61
|
+
failure: RulePlanCheck;
|
|
62
|
+
}
|
|
28
63
|
/** Arguments for user-defined message callback */
|
|
29
64
|
export interface MessageArgs {
|
|
30
65
|
property: string;
|
|
@@ -32,7 +67,7 @@ export interface MessageArgs {
|
|
|
32
67
|
constraints: Record<string, unknown>;
|
|
33
68
|
}
|
|
34
69
|
export interface RuleDef {
|
|
35
|
-
rule:
|
|
70
|
+
rule: InternalRule;
|
|
36
71
|
each?: boolean;
|
|
37
72
|
groups?: string[];
|
|
38
73
|
/** Value to include in BakerError.message on validation failure */
|
|
@@ -40,17 +75,20 @@ export interface RuleDef {
|
|
|
40
75
|
/** Arbitrary value to include in BakerError.context on validation failure */
|
|
41
76
|
context?: unknown;
|
|
42
77
|
}
|
|
43
|
-
/** @Transform callback signature */
|
|
44
|
-
export type TransformFunction = (params: TransformParams) => unknown;
|
|
45
78
|
export interface TransformParams {
|
|
46
79
|
value: unknown;
|
|
47
80
|
key: string;
|
|
48
|
-
/** deserialize: original input object, serialize: class instance */
|
|
49
81
|
obj: Record<string, unknown>;
|
|
50
|
-
type: 'deserialize' | 'serialize';
|
|
51
82
|
}
|
|
83
|
+
export interface Transformer {
|
|
84
|
+
deserialize(params: TransformParams): unknown | Promise<unknown>;
|
|
85
|
+
serialize(params: TransformParams): unknown | Promise<unknown>;
|
|
86
|
+
}
|
|
87
|
+
/** Internal — direction-specific transform function stored after @Field processing */
|
|
88
|
+
export type TransformFunction = (params: TransformParams) => unknown | Promise<unknown>;
|
|
52
89
|
export interface TransformDef {
|
|
53
90
|
fn: TransformFunction;
|
|
91
|
+
isAsync?: boolean;
|
|
54
92
|
options?: {
|
|
55
93
|
groups?: string[];
|
|
56
94
|
deserializeOnly?: boolean;
|
|
@@ -96,7 +134,7 @@ export interface PropertyFlags {
|
|
|
96
134
|
/** @IsNullable() — allow and assign null, reject undefined */
|
|
97
135
|
isNullable?: boolean;
|
|
98
136
|
/** @ValidateIf(cond) — skip all field validation when false */
|
|
99
|
-
validateIf?: (obj: any) => boolean;
|
|
137
|
+
validateIf?: (obj: Record<string, any>) => boolean;
|
|
100
138
|
/** @ValidateNested() — trigger recursive validation for nested DTOs. Used with @Type */
|
|
101
139
|
validateNested?: boolean;
|
|
102
140
|
/** @ValidateNested({ each: true }) — validate nested DTOs per array element */
|
|
@@ -121,6 +159,8 @@ export interface SealedExecutors<T> {
|
|
|
121
159
|
_deserialize(input: unknown, options?: RuntimeOptions): Result<T, BakerError[]> | ResultAsync<T, BakerError[]>;
|
|
122
160
|
/** Internal executor — always succeeds. serialize assumes no validation */
|
|
123
161
|
_serialize(instance: T, options?: RuntimeOptions): Record<string, unknown> | Promise<Record<string, unknown>>;
|
|
162
|
+
/** Internal executor — validate-only (no object creation). Returns null on success, BakerError[] on failure */
|
|
163
|
+
_validate(input: unknown, options?: RuntimeOptions): BakerError[] | null | Promise<BakerError[] | null>;
|
|
124
164
|
/** true if the deserialize direction has async rules/transforms/nested */
|
|
125
165
|
_isAsync: boolean;
|
|
126
166
|
/** true if the serialize direction has async transforms/nested */
|
package/dist/src/utils.d.ts
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
/** minification-safe async function detection (uses Symbol.toStringTag, not constructor.name) */
|
|
2
2
|
export declare function isAsyncFunction(fn: Function): boolean;
|
|
3
|
+
/** Promise-like detection used to enforce sync/async contract at runtime */
|
|
4
|
+
export declare function isPromiseLike(value: unknown): value is PromiseLike<unknown>;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zipbul/baker",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.2.0",
|
|
4
|
+
"description": "Fastest decorator-based DTO validation for TypeScript. AOT code generation, 42ns per validation, 163x faster than class-validator. Zero reflect-metadata.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Junhyung Park (https://github.com/parkrevil)",
|
|
7
7
|
"repository": {
|
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
"homepage": "https://github.com/zipbul/baker#readme",
|
|
13
13
|
"keywords": [
|
|
14
14
|
"validation",
|
|
15
|
+
"validator",
|
|
15
16
|
"transform",
|
|
17
|
+
"transformer",
|
|
16
18
|
"decorator",
|
|
17
19
|
"dto",
|
|
18
20
|
"typescript",
|
|
@@ -20,11 +22,15 @@
|
|
|
20
22
|
"type-safe",
|
|
21
23
|
"class-validator",
|
|
22
24
|
"class-transformer",
|
|
25
|
+
"class-validator-alternative",
|
|
26
|
+
"zod-alternative",
|
|
23
27
|
"serialize",
|
|
24
28
|
"deserialize",
|
|
25
29
|
"code-generation",
|
|
30
|
+
"aot",
|
|
26
31
|
"bun",
|
|
27
|
-
"
|
|
32
|
+
"fast-validation",
|
|
33
|
+
"schema-validation"
|
|
28
34
|
],
|
|
29
35
|
"engines": {
|
|
30
36
|
"bun": ">=1.0.0"
|
|
@@ -50,6 +56,10 @@
|
|
|
50
56
|
"./symbols": {
|
|
51
57
|
"import": "./dist/src/symbols.js",
|
|
52
58
|
"types": "./dist/src/symbols.d.ts"
|
|
59
|
+
},
|
|
60
|
+
"./transformers": {
|
|
61
|
+
"import": "./dist/src/transformers/index.js",
|
|
62
|
+
"types": "./dist/src/transformers/index.d.ts"
|
|
53
63
|
}
|
|
54
64
|
},
|
|
55
65
|
"files": [
|
|
@@ -62,11 +72,23 @@
|
|
|
62
72
|
"access": "public",
|
|
63
73
|
"provenance": true
|
|
64
74
|
},
|
|
75
|
+
"peerDependencies": {
|
|
76
|
+
"luxon": ">=3.0.0",
|
|
77
|
+
"moment": ">=2.0.0"
|
|
78
|
+
},
|
|
79
|
+
"peerDependenciesMeta": {
|
|
80
|
+
"luxon": {
|
|
81
|
+
"optional": true
|
|
82
|
+
},
|
|
83
|
+
"moment": {
|
|
84
|
+
"optional": true
|
|
85
|
+
}
|
|
86
|
+
},
|
|
65
87
|
"dependencies": {
|
|
66
88
|
"@zipbul/result": "1.0.0"
|
|
67
89
|
},
|
|
68
90
|
"scripts": {
|
|
69
|
-
"build": "bun build index.ts src/decorators/index.ts src/rules/index.ts src/symbols.ts --outdir dist --target bun --format esm --splitting --packages external --
|
|
91
|
+
"build": "bun build index.ts src/decorators/index.ts src/rules/index.ts src/symbols.ts src/transformers/index.ts --outdir dist --target bun --format esm --splitting --packages external --root . --production && tsc -p tsconfig.build.json",
|
|
70
92
|
"typecheck": "tsc --noEmit",
|
|
71
93
|
"test": "bun test",
|
|
72
94
|
"test:coverage": "bun test --coverage",
|
|
@@ -82,6 +104,8 @@
|
|
|
82
104
|
"@commitlint/config-conventional": "^20.4.2",
|
|
83
105
|
"@sinclair/typebox": "^0.34.48",
|
|
84
106
|
"@types/bun": "^1.3.9",
|
|
107
|
+
"@types/luxon": "^3.7.1",
|
|
108
|
+
"@types/moment": "^2.13.0",
|
|
85
109
|
"ajv": "^8.18.0",
|
|
86
110
|
"arktype": "^2.2.0",
|
|
87
111
|
"class-transformer": "^0.5.1",
|
package/dist/index-btgens0c.js
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
// @bun
|
|
2
|
-
import{e as v}from"./index-k369bbht.js";var z=new Set;function B(h,S){if(!Object.prototype.hasOwnProperty.call(h,v))h[v]=Object.create(null),z.add(h);let b=h[v];return b[S]??={validation:[],transform:[],expose:[],exclude:null,type:null,flags:{}}}function C(h){return h[Symbol.toStringTag]==="AsyncFunction"}var J=Symbol.for("baker:arrayOf");function E(...h){let S={rules:h};return S[J]=!0,S}function Q(h){return typeof h==="object"&&h!==null&&h[J]===!0}var T=new Set(["type","discriminator","keepDiscriminatorProperty","rules","optional","nullable","name","deserializeName","serializeName","exclude","groups","when","transform","transformDirection","message","context","mapValue","setValue"]);function H(h){if(typeof h==="function")return!1;if(typeof h!=="object"||h===null)return!1;if(Q(h))return!1;let S=Object.keys(h);if(S.length===0)return!0;return S.some((b)=>T.has(b))}function U(h){if(h.length===0)return{rules:[],options:{}};if(h.length===1&&H(h[0])){let b=h[0];return{rules:b.rules??[],options:b}}let S=h[h.length-1];if(H(S)){let b=S,j=h.slice(0,-1);if(b.rules)j=[...j,...b.rules];return{rules:j,options:b}}return{rules:h,options:{}}}function X(h,S,b){for(let j of S)if(Q(j))for(let q of j.rules){let c={rule:q,each:!0,groups:b.groups};if(b.message!==void 0)c.message=b.message;if(b.context!==void 0)c.context=b.context;h.validation.push(c)}else{let q={rule:j,groups:b.groups};if(b.message!==void 0)q.message=b.message;if(b.context!==void 0)q.context=b.context;h.validation.push(q)}}function Z(h,S){if(S.name)h.expose.push({name:S.name,groups:S.groups});else if(S.deserializeName||S.serializeName){if(S.deserializeName)h.expose.push({name:S.deserializeName,deserializeOnly:!0,groups:S.groups});if(S.serializeName)h.expose.push({name:S.serializeName,serializeOnly:!0,groups:S.groups})}else if(S.groups)h.expose.push({groups:S.groups});else h.expose.push({})}function $(h,S){if(!S.transform)return;let b=S.transform,q=C(b)?async(G)=>b({value:G.value,key:G.key,obj:G.obj,direction:G.type}):(G)=>b({value:G.value,key:G.key,obj:G.obj,direction:G.type}),c={};if(S.transformDirection==="deserializeOnly")c.deserializeOnly=!0;if(S.transformDirection==="serializeOnly")c.serializeOnly=!0;h.transform.push({fn:q,options:Object.keys(c).length>0?c:void 0})}function W(...h){return(S,b)=>{let j=S.constructor,c=B(j,b),{rules:G,options:w}=U(h);if(X(c,G,w),w.optional)c.flags.isOptional=!0;if(w.nullable)c.flags.isNullable=!0;if(w.when)c.flags.validateIf=w.when;if(w.type)c.type={fn:w.type,discriminator:w.discriminator,keepDiscriminatorProperty:w.keepDiscriminatorProperty,collectionValue:w.mapValue??w.setValue};if(Z(c,w),w.exclude){if(w.exclude===!0)c.exclude={};else if(w.exclude==="deserializeOnly")c.exclude={deserializeOnly:!0};else if(w.exclude==="serializeOnly")c.exclude={serializeOnly:!0}}$(c,w)}}
|
|
3
|
-
export{z as a,C as b,E as c,W as d};
|
|
4
|
-
|
|
5
|
-
//# debugId=97D01B7065BCEE5B64756E2164756E21
|
|
6
|
-
//# sourceMappingURL=index-btgens0c.js.map
|