@zipbul/baker 3.4.1 → 5.0.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/CHANGELOG.md +45 -0
- package/README.md +256 -159
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -1
- package/dist/src/baker.d.ts +31 -0
- package/dist/src/baker.js +1 -0
- package/dist/src/configure.d.ts +3 -8
- package/dist/src/configure.js +1 -1
- package/dist/src/create-rule.d.ts +2 -1
- package/dist/src/decorators/field.d.ts +2 -1
- package/dist/src/decorators/field.js +1 -1
- package/dist/src/decorators/index.d.ts +0 -1
- package/dist/src/decorators/index.js +1 -1
- package/dist/src/enums.d.ts +51 -0
- package/dist/src/enums.js +1 -0
- package/dist/src/errors.d.ts +1 -1
- package/dist/src/errors.js +1 -1
- package/dist/src/functions/check-call-options.d.ts +1 -1
- package/dist/src/functions/check-call-options.js +1 -1
- package/dist/src/functions/deserialize.d.ts +1 -1
- package/dist/src/functions/serialize.d.ts +1 -1
- package/dist/src/rule-plan.d.ts +5 -3
- package/dist/src/rule-plan.js +1 -1
- package/dist/src/rules/array.js +1 -1
- package/dist/src/rules/date.js +1 -1
- package/dist/src/rules/locales.js +1 -1
- package/dist/src/rules/number.js +1 -1
- package/dist/src/rules/object.js +1 -1
- package/dist/src/rules/string.js +5 -5
- package/dist/src/rules/typechecker.js +5 -5
- package/dist/src/seal/deserialize-builder.js +230 -230
- package/dist/src/seal/enums.d.ts +8 -0
- package/dist/src/seal/enums.js +1 -0
- package/dist/src/seal/expose-validator.js +1 -1
- package/dist/src/seal/seal.d.ts +10 -21
- package/dist/src/seal/seal.js +1 -1
- package/dist/src/seal/serialize-builder.js +8 -8
- package/dist/src/seal/validate-meta.js +1 -1
- package/dist/src/symbols.d.ts +1 -1
- package/dist/src/types.d.ts +15 -14
- package/package.json +1 -1
- package/dist/src/decorators/recipe.d.ts +0 -17
- package/dist/src/decorators/recipe.js +0 -1
- package/dist/src/registry.d.ts +0 -8
- package/dist/src/registry.js +0 -1
- package/dist/src/seal/seal-state.d.ts +0 -10
- package/dist/src/seal/seal-state.js +0 -1
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Null/undefined guard strategy selected per field from its optional/nullable/defined flags. */
|
|
2
|
+
export declare enum GuardKey {
|
|
3
|
+
NullableOptional = "nullable+optional",
|
|
4
|
+
Nullable = "nullable",
|
|
5
|
+
Defined = "defined",
|
|
6
|
+
Optional = "optional",
|
|
7
|
+
Default = "default"
|
|
8
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export var GuardKey;((l)=>{l.NullableOptional="nullable+optional";l.Nullable="nullable";l.Defined="defined";l.Optional="optional";l.Default="default"})(GuardKey||={});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{BakerError as K}from"../errors.js";function validateExposeStacks(F,z){const C=z?`${z}.`:"";for(const[A,H]of Object.entries(F)){for(const q of H.expose){if(q.deserializeOnly&&q.serializeOnly)throw new K(`Invalid @Expose on field '${C}${A}': cannot have both deserializeOnly:true and serializeOnly:true on the same @Expose entry. Use separate @Expose decorators for each direction.`);if(q.name==="__proto__"||q.name==="constructor"||q.name==="prototype")throw new K(`Invalid @Field name on '${C}${A}': '${q.name}' is a reserved property name and cannot be used as a serialized key.`)}const I=H.expose.filter((q)=>!q.serializeOnly),J=H.expose.filter((q)=>!q.deserializeOnly);
|
|
1
|
+
import{Direction as L}from"../enums.js";import{BakerError as K}from"../errors.js";function validateExposeStacks(F,z){const C=z?`${z}.`:"";for(const[A,H]of Object.entries(F)){for(const q of H.expose){if(q.deserializeOnly&&q.serializeOnly)throw new K(`Invalid @Expose on field '${C}${A}': cannot have both deserializeOnly:true and serializeOnly:true on the same @Expose entry. Use separate @Expose decorators for each direction.`);if(q.name==="__proto__"||q.name==="constructor"||q.name==="prototype")throw new K(`Invalid @Field name on '${C}${A}': '${q.name}' is a reserved property name and cannot be used as a serialized key.`)}const I=H.expose.filter((q)=>!q.serializeOnly),J=H.expose.filter((q)=>!q.deserializeOnly);M(C+A,I,L.Deserialize);M(C+A,J,L.Serialize)}}function M(F,z,C){for(let A=0;A<z.length;A++)for(let H=A+1;H<z.length;H++){const I=z[A].groups??[],J=z[H].groups??[];if(R(I,J)){const q=new Set(J),P=I.length===0?[]:I.filter((Q)=>q.has(Q));throw new K(`@Expose conflict on '${F}': 2 @Expose stacks with '${C}' direction and overlapping groups [${P.join(", ")}]. Each direction must have at most one @Expose per group set.`)}}}function R(F,z){if(F.length===0&&z.length===0)return!0;if(F.length===0||z.length===0)return!1;return F.some((C)=>z.includes(C))}export{validateExposeStacks};
|
package/dist/src/seal/seal.d.ts
CHANGED
|
@@ -1,27 +1,22 @@
|
|
|
1
|
+
import type { SealOptions } from '../interfaces';
|
|
1
2
|
import type { RawClassMeta, SealedExecutors } from '../types';
|
|
2
3
|
/** @internal Placeholder executor for circular dependency detection during seal */
|
|
3
4
|
declare function circularPlaceholder(className: string): SealedExecutors<unknown>;
|
|
4
5
|
/**
|
|
5
6
|
* @internal — used by serialize/deserialize. Returns the sealed executor.
|
|
6
|
-
* Throws if the class was never sealed.
|
|
7
|
+
* Throws if the class was never sealed. Seal a class via `new Baker().seal()` first.
|
|
7
8
|
*/
|
|
8
9
|
declare function ensureSealed(Class: Function): SealedExecutors<unknown>;
|
|
9
10
|
/**
|
|
10
|
-
* Seal
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* Transactional: on failure, every placeholder installed by this call (the class and any
|
|
14
|
-
* nested DTO reached by recursion) is removed so a future seal attempt can re-run cleanly.
|
|
15
|
-
*/
|
|
16
|
-
declare function sealOneClass(Class: Function): void;
|
|
17
|
-
/**
|
|
18
|
-
* Public — call once at app startup. Seals every @Recipe-decorated class (and its nested DTOs)
|
|
19
|
-
* and clears the registry. Idempotent: a second call is a no-op.
|
|
11
|
+
* Seal every class in `registry` with `options`. The core used by `new Baker().seal()`.
|
|
12
|
+
* Transactional: on any failure every class sealed by this call is rolled back. Clears `registry`
|
|
13
|
+
* on success.
|
|
20
14
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
15
|
+
* A class already sealed (e.g. a shared value-type DTO reached from another Baker's roots) is
|
|
16
|
+
* reused as-is — class identity is the isolation boundary, so a shared class carries one sealed
|
|
17
|
+
* behaviour. Distinct classes stay fully isolated because each is sealed with its Baker's options.
|
|
23
18
|
*/
|
|
24
|
-
declare function
|
|
19
|
+
declare function sealRegistry(registry: Set<Function>, options: SealOptions): void;
|
|
25
20
|
/**
|
|
26
21
|
* Merges RAW metadata child-first along the prototype chain of Class.
|
|
27
22
|
*
|
|
@@ -34,10 +29,4 @@ declare function seal(): void;
|
|
|
34
29
|
* - flags: child takes priority, only missing flags are supplemented from parent
|
|
35
30
|
*/
|
|
36
31
|
declare function mergeInheritance(Class: Function): RawClassMeta;
|
|
37
|
-
|
|
38
|
-
mergeInheritance: typeof mergeInheritance;
|
|
39
|
-
circularPlaceholder: typeof circularPlaceholder;
|
|
40
|
-
sealClass: typeof sealOneClass;
|
|
41
|
-
};
|
|
42
|
-
export { ensureSealed, seal, mergeInheritance, __testing__ };
|
|
43
|
-
export { sealedClasses, resetForTesting } from './seal-state';
|
|
32
|
+
export { ensureSealed, sealRegistry, mergeInheritance, circularPlaceholder };
|
package/dist/src/seal/seal.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{CollectionType as O,Direction as w}from"../enums.js";import{BakerError as F}from"../errors.js";import{deleteSealed as z,freezeRaw as S,getRaw as I,getSealed as h,hasRawOwn as E,hasSealedOwn as R,setSealed as k}from"../meta-access.js";import{isAsyncFunction as v}from"../utils.js";import{analyzeCircular as A}from"./circular-analyzer.js";import{buildDeserializeCode as f,buildValidateCode as g}from"./deserialize-builder.js";import{validateExposeStacks as u}from"./expose-validator.js";import{buildSerializeCode as y}from"./serialize-builder.js";import{validateMeta as p}from"./validate-meta.js";const C=new Set(["__proto__","constructor","prototype"]);const _=new Set([Number,String,Boolean,Date]);function circularPlaceholder(H){const j=`Circular dependency during seal: ${H} is still being sealed`;return{deserialize(){throw new F(j)},serialize(){throw new F(j)},validate(){throw new F(j)},isAsync:!1,isSerializeAsync:!1}}function b(H,j,J){const Q=j===w.Deserialize?"isAsync":"isSerializeAsync",q=J??new Set,L=(W)=>{if(q.has(W))return!1;q.add(W);const U=h(W);if(U?.merged)return U[Q]===!0;return b(mergeInheritance(W),j,q)};for(const W of Object.values(H)){if(j===w.Deserialize&&W.validation.some((U)=>U.rule.isAsync))return!0;for(const U of W.transform){if(j===w.Deserialize?U.options?.serializeOnly:U.options?.deserializeOnly)continue;if(U.isAsync??v(U.fn))return!0}if(n(W).some(L))return!0}return!1}function n(H){const j=H.type;if(!j)return[];const J=[];if(j.resolvedClass)J.push(j.resolvedClass);if(j.resolvedCollectionValue)J.push(j.resolvedCollectionValue);if(j.discriminator)for(const Q of j.discriminator.subTypes)J.push(Q.value);if(J.length===0&&j.fn){const Q=j.fn();if(Q===Map||Q===Set){const q=j.collectionValue?.();if(typeof q==="function"&&!_.has(q))J.push(q)}else{const q=Array.isArray(Q)?Q[0]:Q;if(typeof q==="function"&&!_.has(q))J.push(q)}}return J}function ensureSealed(H){const j=h(H);if(!j){const J=H.name||"<anonymous class>";throw new F(`${J} is not sealed. Call your baker's seal() (new Baker().seal()) at app startup before deserialize/validate/serialize. (If ${J} has no @Field decorators, decorate at least one property.)`)}return j}function sealRegistry(H,j){const J=new Set;try{for(const Q of H)P(Q,j,J)}catch(Q){for(const q of J)z(q);throw Q}for(const Q of J)S(Q);H.clear()}function P(H,j,J){if(R(H))return;const Q=circularPlaceholder(H.name);k(H,Q);try{const q=mergeInheritance(H);for(const K of Object.keys(q))if(C.has(K))throw new F(`${H.name}: field name '${K}' is not allowed (reserved property name)`);for(const[K,G]of Object.entries(q)){if(!G.type?.fn)continue;let $;try{$=G.type.fn()}catch(M){throw new F(`${H.name}.${K}: type function threw: ${M.message}`,{cause:M})}if($===Map||$===Set){const M=$===Map?O.Map:O.Set,B={...G.type,collection:M,isArray:!1};if(G.type.collectionValue){let V;try{V=G.type.collectionValue()}catch(T){throw new F(`${H.name}.${K}: collectionValue function threw: ${T.message}`,{cause:T})}if(V!=null&&typeof V==="function"&&!_.has(V))B.resolvedCollectionValue=V}q[K]={...G,type:B};continue}const x=Array.isArray($),N=x?$[0]:$;if(N==null||typeof N!=="function")throw new F(`${H.name}: @Type/@Field type must return a constructor or [constructor], got ${String(N)}`);const D={...G.type,isArray:x};if(!_.has(N)){D.resolvedClass=N;if(!G.flags.validateNested||!G.flags.validateNestedEach){G.flags={...G.flags};if(!G.flags.validateNested)G.flags.validateNested=!0;if(x&&!G.flags.validateNestedEach)G.flags.validateNestedEach=!0}}q[K]={...G,type:D}}u(q,H.name);p(H,q);const L=A(H);for(const K of Object.values(q)){if(K.type?.resolvedClass)P(K.type.resolvedClass,j,J);if(K.type?.resolvedCollectionValue)P(K.type.resolvedCollectionValue,j,J);if(K.type?.discriminator)for(const G of K.type.discriminator.subTypes)P(G.value,j,J)}const W=b(q,w.Deserialize),U=b(q,w.Serialize),Z=f(H,q,j,L,W),X=g(H,q,j,L,W),Y=y(H,q,j,U);Object.assign(Q,{deserialize:Z,serialize:Y,validate:X,isAsync:W,isSerializeAsync:U,merged:q})}catch(q){z(H);throw q}J?.add(H)}function mergeInheritance(H){const j=[];let J=H;while(J&&J!==Object){if(E(J))j.push(J);const L=Object.getPrototypeOf(J);J=L===J?null:L}const Q=Object.create(null),q=j.length>1;for(const L of j){const W=I(L);for(const[U,Z]of Object.entries(W))if(!Q[U])Q[U]=q?{validation:[...Z.validation],transform:[...Z.transform],expose:[...Z.expose],exclude:Z.exclude,type:Z.type,flags:{...Z.flags}}:Z;else{const X=Q[U],Y=Z;for(const $ of Y.validation)if(!X.validation.some((x)=>x.rule.ruleName===$.rule.ruleName))X.validation.push($);if(X.transform.length===0&&Y.transform.length>0)X.transform=[...Y.transform];if(X.expose.length===0&&Y.expose.length>0)X.expose=[...Y.expose];if(X.exclude===null&&Y.exclude!==null)X.exclude=Y.exclude;if(X.type===null&&Y.type!==null)X.type=Y.type;const K=X.flags,G=Y.flags;if(G.isOptional!==void 0&&K.isOptional===void 0)K.isOptional=G.isOptional;if(G.isDefined!==void 0&&K.isDefined===void 0)K.isDefined=G.isDefined;if(G.validateIf!==void 0&&K.validateIf===void 0)K.validateIf=G.validateIf;if(G.isNullable!==void 0&&K.isNullable===void 0)K.isNullable=G.isNullable;if(G.validateNested!==void 0&&K.validateNested===void 0)K.validateNested=G.validateNested;if(G.validateNestedEach!==void 0&&K.validateNestedEach===void 0)K.validateNestedEach=G.validateNestedEach}}return Q}export{ensureSealed,sealRegistry,mergeInheritance,circularPlaceholder};
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{CollectionType as c}from"../enums.js";import{BakerError as p}from"../errors.js";import{getSealed as A}from"../meta-access.js";import{sanitizeKey as E,buildGroupsHasExpr as m}from"./codegen-utils.js";const j={out:"__bk$out",fieldVal:"__bk$fv_",groups:"__bk$groups",group0:"__bk$group0",groupsSet:"__bk$groupsSet",setArr:"__bk$sa",setItem:"__bk$si",mapObj:"__bk$m",mapEntry:"__bk$me",serResult:"__bk$sr",outItem:"__bk$out_item",discArr:"__bk$da",discIdx:"__bk$di",nestedArr:"__bk$na",nestedIdx:"__bk$ni",nestedItem:"__bk$nitem"};function u(U,q){const L=q.find((R)=>R.serializeOnly&&R.name);if(L)return L.name;const M=q.find((R)=>!R.deserializeOnly&&!R.serializeOnly&&R.name);if(M)return M.name;return U}function y(U){let q=null;for(const L of U){if(L.deserializeOnly)continue;if(!L.groups||L.groups.length===0)return;if(q===null)q=new Set;for(const M of L.groups)q.add(M)}return q===null?void 0:[...q]}function x(U,q,L,M){if(L.length===0)return null;if(L.length===1){const Y=L[0],W=M.length;M.push(Y.fn);const Z=`refs[${W}]({value:${U},key:${JSON.stringify(q)},obj:instance})`;return Y.isAsync?`(await ${Z})`:Z}if(L.length===2){const Y=L[1],W=L[0],Z=M.length;M.push(Y.fn);const F=M.length;M.push(W.fn);const z=`refs[${Z}]({value:${U},key:${JSON.stringify(q)},obj:instance})`,Q=Y.isAsync?`(await ${z})`:z,_=`refs[${F}]({value:${Q},key:${JSON.stringify(q)},obj:instance})`;return W.isAsync?`(await ${_})`:_}let R=U;for(let Y=L.length-1;Y>=0;Y-=1){const W=L[Y],Z=M.length;M.push(W.fn);const F=`refs[${Z}]({value:${R},key:${JSON.stringify(q)},obj:instance})`;R=W.isAsync?`(await ${F})`:F}return R}function T(U,q,L,M){const R=x(U,q,L,M);return R?`
|
|
2
2
|
${U} = ${R};`:""}function buildSerializeCode(U,q,L,M){const R=[],Y=[];let W=`'use strict';
|
|
3
3
|
`;W+=`var ${j.out} = {};
|
|
4
4
|
`;if(Object.values(q).some((_)=>{const v=y(_.expose);return v&&v.length>0})){W+=`var ${j.groups} = opts && opts.groups;
|
|
5
5
|
`;W+=`var ${j.group0} = ${j.groups} && ${j.groups}.length === 1 ? ${j.groups}[0] : null;
|
|
6
6
|
`;W+=`var ${j.groupsSet} = ${j.groups} && ${j.groups}.length > 1 ? new Set(${j.groups}) : null;
|
|
7
|
-
`}for(const[_,v]of Object.entries(q))W+=
|
|
7
|
+
`}for(const[_,v]of Object.entries(q))W+=f(_,v,R,Y,M,L,U.name);W+=`return ${j.out};
|
|
8
8
|
`;const F=U.name.replace(/[^\w$.-]/g,"_");W+=`//# sourceURL=baker://${F}/serialize
|
|
9
|
-
`;return Function("refs","execs","BakerError",`return ${M?"async function":"function"}(instance, opts) { `+W+" }")(R,Y,
|
|
9
|
+
`;return Function("refs","execs","BakerError",`return ${M?"async function":"function"}(instance, opts) { `+W+" }")(R,Y,p)}function f(U,q,L,M,R,Y,W=""){if(q.exclude){if(!q.exclude.deserializeOnly){if(Y?.debug){const X=q.exclude.serializeOnly?"serializeOnly":"bidirectional";return`// [baker] field ${JSON.stringify(U)} excluded (${X} @Exclude)
|
|
10
10
|
`}return""}}if(q.expose.length>0&&q.expose.every((X)=>X.deserializeOnly)){if(Y?.debug)return`// [baker] field ${JSON.stringify(U)} excluded (all @Expose entries are deserializeOnly)
|
|
11
|
-
`;return""}const Z=
|
|
12
|
-
`;let v="",S="";if(F&&F.length>0){v=`if ((${j.group0} !== null || ${j.groupsSet}) && (${
|
|
11
|
+
`;return""}const Z=u(U,q.expose),F=y(q.expose),z=E(U),Q=`${j.fieldVal}${z}`;let _="";_+=`var ${Q} = instance[${JSON.stringify(U)}];
|
|
12
|
+
`;let v="",S="";if(F&&F.length>0){v=`if ((${j.group0} !== null || ${j.groupsSet}) && (${m(j.group0,j.groupsSet,F)})) {
|
|
13
13
|
`;S=`}
|
|
14
|
-
`}let H="";const b=q.flags.isOptional,g=q.transform.filter((X)=>!X.options?.deserializeOnly);if(q.type?.collection){const X=`${j.out}[${JSON.stringify(Z)}]`,$=q.type.collection;let J;if($===
|
|
14
|
+
`}let H="";const b=q.flags.isOptional,g=q.transform.filter((X)=>!X.options?.deserializeOnly);if(q.type?.collection){const X=`${j.out}[${JSON.stringify(Z)}]`,$=q.type.collection;let J;if($===c.Set)if(q.type.resolvedCollectionValue){const B=A(q.type.resolvedCollectionValue),D=M.length;M.push(B);if(R)J=`{ var __ser_ps = []; for (var __ser_item of ${Q}) { __ser_ps.push(__ser_item == null ? __ser_item : execs[${D}].serialize(__ser_item, opts)); } ${X} = await Promise.all(__ser_ps); }`;else{J=`var ${j.setArr} = [];
|
|
15
15
|
`;J+=` for (var ${j.setItem} of ${Q}) {
|
|
16
16
|
`;J+=` ${j.setArr}.push(${j.setItem} == null ? ${j.setItem} : execs[${D}].serialize(${j.setItem}, opts));
|
|
17
17
|
`;J+=` }
|
|
@@ -63,8 +63,8 @@ ${U} = ${R};`:""}function buildSerializeCode(U,q,L,M){const R=[],Y=[];let W=`'us
|
|
|
63
63
|
} else {
|
|
64
64
|
${$} = ${Q};
|
|
65
65
|
}
|
|
66
|
-
`}else{const X=
|
|
66
|
+
`}else{const X=n(U,Z,Q,q,L);if(b){H+=`if (${Q} !== undefined) {
|
|
67
67
|
`;H+=" "+X+`
|
|
68
68
|
`;H+=`}
|
|
69
69
|
`}else H+=X+`
|
|
70
|
-
`}_+=v+H+S;return _}function
|
|
70
|
+
`}_+=v+H+S;return _}function n(U,q,L,M,R){const Y=`${j.out}[${JSON.stringify(q)}]`,W=M.transform.filter((Z)=>!Z.options?.deserializeOnly);if(W.length>0){const Z=x(L,U,W,R);return`${Y} = ${Z};`}return`${Y} = ${L};`}export{buildSerializeCode};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{BakerError as
|
|
1
|
+
import{CollectionType as K}from"../enums.js";import{BakerError as z}from"../errors.js";import{hasRawOwn as H}from"../meta-access.js";export function validateMeta(I,J){const q=I.name;for(const[x,D]of Object.entries(J)){if(D.type?.discriminator){const f=D.type.discriminator;if(typeof f.property!=="string"||f.property.length===0)throw new z(`${q}.${x}: discriminator.property must be a non-empty string.`);if(!Array.isArray(f.subTypes)||f.subTypes.length===0)throw new z(`${q}.${x}: discriminator.subTypes must be a non-empty array of { value, name } entries.`);const F=new Set;for(let A=0;A<f.subTypes.length;A++){const j=f.subTypes[A];if(typeof j.name!=="string"||j.name.length===0)throw new z(`${q}.${x}: discriminator.subTypes[${A}].name must be a non-empty string.`);if(typeof j.value!=="function")throw new z(`${q}.${x}: discriminator.subTypes[${A}].value must be a class constructor (got ${typeof j.value}).`);if(F.has(j.name))throw new z(`${q}.${x}: discriminator.subTypes has duplicate name '${j.name}'. Each subType must have a unique name.`);F.add(j.name);if(!H(j.value))throw new z(`${q}.${x}: discriminator.subTypes[${A}].value (${j.value.name}) has no @Field decorators.`)}}const G=D.type?.collection;if(G!==void 0&&D.type?.resolvedCollectionValue){const f=D.type.resolvedCollectionValue;if(!H(f)){const F=G===K.Set?"setValue":"mapValue";throw new z(`${q}.${x}: ${F} target (${f.name}) has no @Field decorators.`)}}}}
|
package/dist/src/symbols.d.ts
CHANGED
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
*/
|
|
5
5
|
/** Tier 1 collection metadata (stored on Class[Symbol.metadata] by decorators) */
|
|
6
6
|
export declare const RAW: unique symbol;
|
|
7
|
-
/** Tier 2 seal result (dual executor stored on Class
|
|
7
|
+
/** Tier 2 seal result (dual executor stored on Class at seal time) */
|
|
8
8
|
export declare const SEALED: unique symbol;
|
package/dist/src/types.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Result, ResultAsync } from '@zipbul/result';
|
|
2
|
+
import type { CacheKey, CollectionType, RequiredType, RuleOp, RulePlanCheckKind, RulePlanExprKind } from './enums';
|
|
2
3
|
import type { BakerIssue } from './errors';
|
|
3
4
|
import type { RuntimeOptions } from './interfaces';
|
|
4
5
|
export interface EmitContext {
|
|
@@ -26,7 +27,7 @@ export interface EmittableRule {
|
|
|
26
27
|
* Only set for rules that assume a specific type (e.g., isEmail → 'string').
|
|
27
28
|
* `@IsString` itself is undefined (it includes its own typeof check).
|
|
28
29
|
*/
|
|
29
|
-
readonly requiresType?:
|
|
30
|
+
readonly requiresType?: RequiredType;
|
|
30
31
|
/** Expose rule parameters for external reading */
|
|
31
32
|
readonly constraints?: Record<string, unknown>;
|
|
32
33
|
/** true when the rule is explicitly async and must be awaited */
|
|
@@ -37,30 +38,30 @@ export interface InternalRule extends EmittableRule {
|
|
|
37
38
|
readonly plan?: RulePlan;
|
|
38
39
|
}
|
|
39
40
|
export type RulePlanExpr = {
|
|
40
|
-
kind:
|
|
41
|
+
kind: RulePlanExprKind.Value;
|
|
41
42
|
} | {
|
|
42
|
-
kind:
|
|
43
|
+
kind: RulePlanExprKind.Member;
|
|
43
44
|
object: RulePlanExpr;
|
|
44
45
|
property: 'length';
|
|
45
46
|
} | {
|
|
46
|
-
kind:
|
|
47
|
+
kind: RulePlanExprKind.Call0;
|
|
47
48
|
object: RulePlanExpr;
|
|
48
49
|
method: 'getTime';
|
|
49
50
|
} | {
|
|
50
|
-
kind:
|
|
51
|
+
kind: RulePlanExprKind.Literal;
|
|
51
52
|
value: number;
|
|
52
53
|
};
|
|
53
54
|
export type RulePlanCheck = {
|
|
54
|
-
kind:
|
|
55
|
+
kind: RulePlanCheckKind.Compare;
|
|
55
56
|
left: RulePlanExpr;
|
|
56
|
-
op:
|
|
57
|
+
op: RuleOp;
|
|
57
58
|
right: RulePlanExpr;
|
|
58
59
|
} | {
|
|
59
|
-
kind:
|
|
60
|
+
kind: RulePlanCheckKind.And | RulePlanCheckKind.Or;
|
|
60
61
|
checks: RulePlanCheck[];
|
|
61
62
|
};
|
|
62
63
|
export interface RulePlan {
|
|
63
|
-
cacheKey?:
|
|
64
|
+
cacheKey?: CacheKey;
|
|
64
65
|
failure: RulePlanCheck;
|
|
65
66
|
}
|
|
66
67
|
/** Arguments for user-defined message callback */
|
|
@@ -120,15 +121,15 @@ export interface TypeDef {
|
|
|
120
121
|
}[];
|
|
121
122
|
};
|
|
122
123
|
keepDiscriminatorProperty?: boolean;
|
|
123
|
-
/** seal
|
|
124
|
+
/** seal-time normalization result — true if fn() returns an array */
|
|
124
125
|
isArray?: boolean;
|
|
125
|
-
/** seal
|
|
126
|
+
/** seal-time normalization result — cached class after resolving fn() (DTOs only, excluding primitives) */
|
|
126
127
|
resolvedClass?: ClassCtor;
|
|
127
|
-
/** seal
|
|
128
|
-
collection?:
|
|
128
|
+
/** seal-time normalization result — Map or Set collection type */
|
|
129
|
+
collection?: CollectionType;
|
|
129
130
|
/** Nested DTO class thunk for Map value / Set element */
|
|
130
131
|
collectionValue?: () => ClassCtor;
|
|
131
|
-
/** seal
|
|
132
|
+
/** seal-time normalization result — cached class after resolving collectionValue */
|
|
132
133
|
resolvedCollectionValue?: ClassCtor;
|
|
133
134
|
}
|
|
134
135
|
export interface PropertyFlags {
|
package/package.json
CHANGED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Marks a class as a baker DTO so `seal()` (called with no arguments) discovers and seals it.
|
|
3
|
-
*
|
|
4
|
-
* Modern (TC39) field decorators receive no class reference, so `@Field` alone cannot register
|
|
5
|
-
* the owning class. `@Recipe` runs after the field decorators and registers the class itself.
|
|
6
|
-
*
|
|
7
|
-
* @example
|
|
8
|
-
* ```ts
|
|
9
|
-
* \@Recipe
|
|
10
|
-
* class UserDto {
|
|
11
|
-
* \@Field(isString()) name!: string;
|
|
12
|
-
* }
|
|
13
|
-
* seal();
|
|
14
|
-
* ```
|
|
15
|
-
*/
|
|
16
|
-
declare function Recipe<T extends Function>(value: T, _context: ClassDecoratorContext): void;
|
|
17
|
-
export { Recipe };
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{globalRegistry as q}from"../registry.js";function Recipe(j,z){q.add(j)}export{Recipe};
|
package/dist/src/registry.d.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Global registry — automatically registers classes with at least one decorator attached
|
|
3
|
-
*
|
|
4
|
-
* - Automatically called from ensureMeta()
|
|
5
|
-
* - seal() iterates this Set to seal all DTOs
|
|
6
|
-
* - Metadata is not stored here — used only as an index (which classes are registered)
|
|
7
|
-
*/
|
|
8
|
-
export declare const globalRegistry: Set<Function>;
|
package/dist/src/registry.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const globalRegistry=new Set;
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @internal — shared seal state, extracted so `configure.ts` can read `isSealed()`
|
|
3
|
-
* without importing `seal.ts` (which would create a cycle: seal → configure → seal).
|
|
4
|
-
*/
|
|
5
|
-
/** List of sealed classes — used by unseal to remove SEALED */
|
|
6
|
-
export declare const sealedClasses: Set<Function>;
|
|
7
|
-
export declare function isSealed(): boolean;
|
|
8
|
-
export declare function markSealed(): void;
|
|
9
|
-
/** @internal — used by unseal() in test helpers */
|
|
10
|
-
export declare function resetForTesting(): void;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
let c=!1;export const sealedClasses=new Set;export function isSealed(){return c}export function markSealed(){c=!0}export function resetForTesting(){c=!1;sealedClasses.clear()}
|