@zipbul/baker 4.0.0 → 5.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/CHANGELOG.md +31 -0
- package/README.md +53 -47
- package/dist/index.d.ts +2 -8
- package/dist/index.js +1 -1
- package/dist/src/baker.d.ts +34 -18
- package/dist/src/baker.js +1 -1
- package/dist/src/configure.d.ts +2 -13
- package/dist/src/configure.js +1 -1
- package/dist/src/decorators/index.d.ts +0 -1
- package/dist/src/decorators/index.js +1 -1
- 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 +5 -17
- package/dist/src/functions/deserialize.js +1 -1
- package/dist/src/functions/serialize.d.ts +8 -13
- package/dist/src/functions/serialize.js +1 -1
- package/dist/src/functions/validate.d.ts +5 -16
- package/dist/src/functions/validate.js +1 -1
- package/dist/src/meta-access.d.ts +1 -8
- package/dist/src/meta-access.js +1 -1
- package/dist/src/seal/deserialize-builder.d.ts +4 -4
- package/dist/src/seal/deserialize-builder.js +243 -243
- package/dist/src/seal/seal.d.ts +13 -39
- package/dist/src/seal/seal.js +1 -1
- package/dist/src/seal/serialize-builder.d.ts +2 -2
- package/dist/src/seal/serialize-builder.js +48 -48
- package/dist/src/symbols.d.ts +2 -4
- package/dist/src/symbols.js +1 -1
- package/dist/src/types.d.ts +4 -4
- 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
package/dist/src/seal/seal.d.ts
CHANGED
|
@@ -2,40 +2,20 @@ import type { SealOptions } from '../interfaces';
|
|
|
2
2
|
import type { RawClassMeta, SealedExecutors } from '../types';
|
|
3
3
|
/** @internal Placeholder executor for circular dependency detection during seal */
|
|
4
4
|
declare function circularPlaceholder(className: string): SealedExecutors<unknown>;
|
|
5
|
+
/** Canonical fingerprint of a SealOptions — the 5 booleans in fixed order. `{}` and a fully-defaulted
|
|
6
|
+
* object both map to "00000", so `new Baker()` and `new Baker({})` share a cache key. */
|
|
7
|
+
declare function configFingerprint(o: SealOptions): string;
|
|
8
|
+
declare function getCached(cls: Function, fp: string): SealedExecutors<unknown> | undefined;
|
|
9
|
+
/** Test-only: drop a single class's cached executors so a re-seal recompiles it. */
|
|
10
|
+
declare function clearCached(cls: Function): void;
|
|
5
11
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
12
|
+
* Test-only: drop the ENTIRE cache. Used by `unseal()` so a test that re-seals classes starts from a
|
|
13
|
+
* clean slate — a whole-cache reset (vs per-class) is the only way to avoid the partial-clear state
|
|
14
|
+
* where a cached root still references a nested whose entry was dropped (a root + its nested are always
|
|
15
|
+
* compiled together, so they must be invalidated together).
|
|
8
16
|
*/
|
|
9
|
-
declare function
|
|
10
|
-
|
|
11
|
-
* Seal every class in `registry` with `options`. Shared core of both the global default `seal()`
|
|
12
|
-
* and per-scope `createBaker().seal()`. Transactional: on any failure every class sealed by this
|
|
13
|
-
* call is rolled back. Clears `registry` on success.
|
|
14
|
-
*
|
|
15
|
-
* A class already sealed (e.g. a shared value-type DTO reached from another scope'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 scope's options.
|
|
18
|
-
*
|
|
19
|
-
* @param track Optional set recording successfully-sealed classes. The default seal passes the
|
|
20
|
-
* global `sealedClasses` so `unseal()` can restore them; instances pass nothing.
|
|
21
|
-
*/
|
|
22
|
-
declare function sealRegistry(registry: Set<Function>, options: SealOptions, track?: Set<Function>): void;
|
|
23
|
-
/**
|
|
24
|
-
* Seal a single class (and its nested DTOs). Not part of the public API — `seal()` (argless)
|
|
25
|
-
* is the only public entry. Exposed via `__testing__.sealClass` so tests can seal one class in
|
|
26
|
-
* isolation. Class[Symbol.metadata][RAW] must exist; Class[SEALED] must not.
|
|
27
|
-
* Transactional: on failure, every placeholder installed by this call (the class and any
|
|
28
|
-
* nested DTO reached by recursion) is removed so a future seal attempt can re-run cleanly.
|
|
29
|
-
*/
|
|
30
|
-
declare function sealOneClass(Class: Function): void;
|
|
31
|
-
/**
|
|
32
|
-
* Public — call once at app startup. Seals every @Recipe-decorated class (and its nested DTOs)
|
|
33
|
-
* and clears the registry. Idempotent: a second call is a no-op.
|
|
34
|
-
*
|
|
35
|
-
* Baker requires this call before any deserialize/serialize/validate. There is no implicit seal.
|
|
36
|
-
* All DTOs must be imported before this call — baker has no lazy/on-demand sealing.
|
|
37
|
-
*/
|
|
38
|
-
declare function seal(): void;
|
|
17
|
+
declare function clearAllCached(): void;
|
|
18
|
+
declare function sealRegistry(registry: Set<Function>, options: SealOptions, executors: Map<Function, SealedExecutors<unknown>>): void;
|
|
39
19
|
/**
|
|
40
20
|
* Merges RAW metadata child-first along the prototype chain of Class.
|
|
41
21
|
*
|
|
@@ -48,10 +28,4 @@ declare function seal(): void;
|
|
|
48
28
|
* - flags: child takes priority, only missing flags are supplemented from parent
|
|
49
29
|
*/
|
|
50
30
|
declare function mergeInheritance(Class: Function): RawClassMeta;
|
|
51
|
-
|
|
52
|
-
mergeInheritance: typeof mergeInheritance;
|
|
53
|
-
circularPlaceholder: typeof circularPlaceholder;
|
|
54
|
-
sealClass: typeof sealOneClass;
|
|
55
|
-
};
|
|
56
|
-
export { ensureSealed, seal, sealRegistry, mergeInheritance, __testing__ };
|
|
57
|
-
export { sealedClasses, resetForTesting } from './seal-state';
|
|
31
|
+
export { sealRegistry, mergeInheritance, circularPlaceholder, getCached, configFingerprint, clearCached, clearAllCached };
|
package/dist/src/seal/seal.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{CollectionType as R,Direction as P}from"../enums.js";import{BakerError as F}from"../errors.js";import{getRaw as h,hasRawOwn as x}from"../meta-access.js";import{isAsyncFunction as v}from"../utils.js";import{analyzeCircular as g}from"./circular-analyzer.js";import{buildDeserializeCode as f,buildValidateCode as y}from"./deserialize-builder.js";import{validateExposeStacks as u}from"./expose-validator.js";import{buildSerializeCode as C}from"./serialize-builder.js";import{validateMeta as p}from"./validate-meta.js";const n=new Set(["__proto__","constructor","prototype"]);const D=new Set([Number,String,Boolean,Date]);function circularPlaceholder(j){const G=`Circular dependency during seal: ${j} is still being sealed`;return{deserialize(){throw new F(G)},serialize(){throw new F(G)},validate(){throw new F(G)},isAsync:!1,isSerializeAsync:!1}}function O(j,G,H,J){const K=G===P.Deserialize?"isAsync":"isSerializeAsync",W=J??new Set,L=(Q)=>{if(W.has(Q))return!1;W.add(Q);const q=H(Q);if(q?.merged)return q[K]===!0;return O(mergeInheritance(Q),G,H,W)};for(const Q of Object.values(j)){if(G===P.Deserialize&&Q.validation.some((q)=>q.rule.isAsync))return!0;for(const q of Q.transform){if(G===P.Deserialize?q.options?.serializeOnly:q.options?.deserializeOnly)continue;if(q.isAsync??v(q.fn))return!0}if(k(Q).some(L))return!0}return!1}function k(j){const G=j.type;if(!G)return[];const H=[];if(G.resolvedClass)H.push(G.resolvedClass);if(G.resolvedCollectionValue)H.push(G.resolvedCollectionValue);if(G.discriminator)for(const J of G.discriminator.subTypes)H.push(J.value);if(H.length===0&&G.fn){const J=G.fn();if(J===Map||J===Set){const K=G.collectionValue?.();if(typeof K==="function"&&!D.has(K))H.push(K)}else{const K=Array.isArray(J)?J[0]:J;if(typeof K==="function"&&!D.has(K))H.push(K)}}return H}let _=new WeakMap;function configFingerprint(j){return(j.enableImplicitConversion?"1":"0")+(j.exposeDefaultValues?"1":"0")+(j.stopAtFirstError?"1":"0")+(j.whitelist?"1":"0")+(j.debug?"1":"0")}function getCached(j,G){return _.get(j)?.get(G)}function clearCached(j){_.delete(j)}function clearAllCached(){_=new WeakMap}function d(j,G,H){let J=_.get(j);if(J===void 0){J=new Map;_.set(j,J)}J.set(G,H)}function sealRegistry(j,G,H){const J=configFingerprint(G),K=new Set;try{for(const W of j)M(W,H,J,G,K)}catch(W){H.clear();throw W}for(const W of K)d(W,J,H.get(W));j.clear()}function M(j,G,H,J,K){if(G.has(j))return;const W=getCached(j,H);if(W!==void 0){G.set(j,W);if(W.merged)for(const q of Object.values(W.merged))for(const Y of k(q))M(Y,G,H,J,K);return}const L=circularPlaceholder(j.name);G.set(j,L);const Q=(q)=>G.get(q);try{const q=mergeInheritance(j);for(const $ of Object.keys(q))if(n.has($))throw new F(`${j.name}: field name '${$}' is not allowed (reserved property name)`);for(const[$,U]of Object.entries(q)){if(!U.type?.fn)continue;let w;try{w=U.type.fn()}catch(B){throw new F(`${j.name}.${$}: type function threw: ${B.message}`,{cause:B})}if(w===Map||w===Set){const B=w===Map?R.Map:R.Set,I={...U.type,collection:B,isArray:!1};if(U.type.collectionValue){let V;try{V=U.type.collectionValue()}catch(E){throw new F(`${j.name}.${$}: collectionValue function threw: ${E.message}`,{cause:E})}if(V!=null&&typeof V==="function"&&!D.has(V))I.resolvedCollectionValue=V}q[$]={...U,type:I};continue}const T=Array.isArray(w),N=T?w[0]:w;if(N==null||typeof N!=="function")throw new F(`${j.name}: @Type/@Field type must return a constructor or [constructor], got ${String(N)}`);const z={...U.type,isArray:T};if(!D.has(N)){z.resolvedClass=N;if(!U.flags.validateNested||!U.flags.validateNestedEach){U.flags={...U.flags};if(!U.flags.validateNested)U.flags.validateNested=!0;if(T&&!U.flags.validateNestedEach)U.flags.validateNestedEach=!0}}q[$]={...U,type:z}}u(q,j.name);p(j,q);const Y=g(j);for(const $ of Object.values(q)){if($.type?.resolvedClass)M($.type.resolvedClass,G,H,J,K);if($.type?.resolvedCollectionValue)M($.type.resolvedCollectionValue,G,H,J,K);if($.type?.discriminator)for(const U of $.type.discriminator.subTypes)M(U.value,G,H,J,K)}const X=O(q,P.Deserialize,Q),Z=O(q,P.Serialize,Q),b=f(j,q,J,Y,X,Q),S=y(j,q,J,Y,X,Q),A=C(j,q,J,Z,Q);Object.assign(L,{deserialize:b,serialize:A,validate:S,isAsync:X,isSerializeAsync:Z,merged:q})}catch(q){G.delete(j);throw q}K?.add(j)}function mergeInheritance(j){const G=[];let H=j;while(H&&H!==Object){if(x(H))G.push(H);const K=Object.getPrototypeOf(H);H=K===H?null:K}const J=Object.create(null);for(const K of G){const W=h(K);for(const[L,Q]of Object.entries(W))if(!J[L])J[L]={...Q,validation:[...Q.validation],transform:[...Q.transform],expose:[...Q.expose],exclude:Q.exclude,type:Q.type,flags:{...Q.flags}};else{const q=J[L],Y=Q;for(const b of Y.validation)if(!q.validation.some((S)=>S.rule.ruleName===b.rule.ruleName))q.validation.push(b);if(q.transform.length===0&&Y.transform.length>0)q.transform=[...Y.transform];if(q.expose.length===0&&Y.expose.length>0)q.expose=[...Y.expose];if(q.exclude===null&&Y.exclude!==null)q.exclude=Y.exclude;if(q.type===null&&Y.type!==null)q.type=Y.type;const X=q.flags,Z=Y.flags;if(Z.isOptional!==void 0&&X.isOptional===void 0)X.isOptional=Z.isOptional;if(Z.isDefined!==void 0&&X.isDefined===void 0)X.isDefined=Z.isDefined;if(Z.validateIf!==void 0&&X.validateIf===void 0)X.validateIf=Z.validateIf;if(Z.isNullable!==void 0&&X.isNullable===void 0)X.isNullable=Z.isNullable;if(Z.validateNested!==void 0&&X.validateNested===void 0)X.validateNested=Z.validateNested;if(Z.validateNestedEach!==void 0&&X.validateNestedEach===void 0)X.validateNestedEach=Z.validateNestedEach}}return J}export{sealRegistry,mergeInheritance,circularPlaceholder,getCached,configFingerprint,clearCached,clearAllCached};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { SealOptions, RuntimeOptions } from '../interfaces';
|
|
2
|
-
import type { RawClassMeta } from '../types';
|
|
2
|
+
import type { RawClassMeta, SealedExecutors } from '../types';
|
|
3
3
|
/**
|
|
4
4
|
* Generate serialize executor code.
|
|
5
5
|
* Assumes no validation — always returns Record<string, unknown> (§4.3).
|
|
6
6
|
*/
|
|
7
|
-
declare function buildSerializeCode<T>(Class: Function, merged: RawClassMeta, options: SealOptions | undefined, isAsync: boolean): (instance: T, opts?: RuntimeOptions) => Record<string, unknown> | Promise<Record<string, unknown>>;
|
|
7
|
+
declare function buildSerializeCode<T>(Class: Function, merged: RawClassMeta, options: SealOptions | undefined, isAsync: boolean, resolve: (cls: Function) => SealedExecutors<unknown> | undefined): (instance: T, opts?: RuntimeOptions) => Record<string, unknown> | Promise<Record<string, unknown>>;
|
|
8
8
|
export { buildSerializeCode };
|
|
@@ -1,70 +1,70 @@
|
|
|
1
|
-
import{CollectionType as c}from"../enums.js";import{BakerError as p}from"../errors.js";import{
|
|
2
|
-
${U} = ${R};`:""}function buildSerializeCode(U,q,
|
|
1
|
+
import{CollectionType as c}from"../enums.js";import{BakerError as p}from"../errors.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 M=q.find((R)=>R.serializeOnly&&R.name);if(M)return M.name;const Q=q.find((R)=>!R.deserializeOnly&&!R.serializeOnly&&R.name);if(Q)return Q.name;return U}function y(U){let q=null;for(const M of U){if(M.deserializeOnly)continue;if(!M.groups||M.groups.length===0)return;if(q===null)q=new Set;for(const Q of M.groups)q.add(Q)}return q===null?void 0:[...q]}function x(U,q,M,Q){if(M.length===0)return null;if(M.length===1){const X=M[0],Z=Q.length;Q.push(X.fn);const W=`refs[${Z}]({value:${U},key:${JSON.stringify(q)},obj:instance})`;return X.isAsync?`(await ${W})`:W}if(M.length===2){const X=M[1],Z=M[0],W=Q.length;Q.push(X.fn);const B=Q.length;Q.push(Z.fn);const D=`refs[${W}]({value:${U},key:${JSON.stringify(q)},obj:instance})`,I=X.isAsync?`(await ${D})`:D,L=`refs[${B}]({value:${I},key:${JSON.stringify(q)},obj:instance})`;return Z.isAsync?`(await ${L})`:L}let R=U;for(let X=M.length-1;X>=0;X-=1){const Z=M[X],W=Q.length;Q.push(Z.fn);const B=`refs[${W}]({value:${R},key:${JSON.stringify(q)},obj:instance})`;R=Z.isAsync?`(await ${B})`:B}return R}function T(U,q,M,Q){const R=x(U,q,M,Q);return R?`
|
|
2
|
+
${U} = ${R};`:""}function buildSerializeCode(U,q,M,Q,R){const X=[],Z=[];let W=`'use strict';
|
|
3
3
|
`;W+=`var ${j.out} = {};
|
|
4
|
-
`;if(Object.values(q).some((
|
|
4
|
+
`;if(Object.values(q).some((P)=>{const O=y(P.expose);return O&&O.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[
|
|
8
|
-
`;const
|
|
9
|
-
`;return Function("refs","execs","BakerError",`return ${
|
|
10
|
-
`}return""}}if(q.expose.length>0&&q.expose.every((
|
|
11
|
-
`;return""}const
|
|
12
|
-
`;let
|
|
7
|
+
`}for(const[P,O]of Object.entries(q))W+=f(P,O,X,Z,Q,R,M,U.name);W+=`return ${j.out};
|
|
8
|
+
`;const D=U.name.replace(/[^\w$.-]/g,"_");W+=`//# sourceURL=baker://${D}/serialize
|
|
9
|
+
`;return Function("refs","execs","BakerError",`return ${Q?"async function":"function"}(instance, opts) { `+W+" }")(X,Z,p)}function f(U,q,M,Q,R,X,Z,W=""){if(q.exclude){if(!q.exclude.deserializeOnly){if(Z?.debug){const Y=q.exclude.serializeOnly?"serializeOnly":"bidirectional";return`// [baker] field ${JSON.stringify(U)} excluded (${Y} @Exclude)
|
|
10
|
+
`}return""}}if(q.expose.length>0&&q.expose.every((Y)=>Y.deserializeOnly)){if(Z?.debug)return`// [baker] field ${JSON.stringify(U)} excluded (all @Expose entries are deserializeOnly)
|
|
11
|
+
`;return""}const B=u(U,q.expose),D=y(q.expose),I=E(U),L=`${j.fieldVal}${I}`;let P="";P+=`var ${L} = instance[${JSON.stringify(U)}];
|
|
12
|
+
`;let O="",S="";if(D&&D.length>0){O=`if ((${j.group0} !== null || ${j.groupsSet}) && (${m(j.group0,j.groupsSet,D)})) {
|
|
13
13
|
`;S=`}
|
|
14
|
-
`}let
|
|
15
|
-
`;J+=` for (var ${j.setItem} of ${
|
|
16
|
-
`;J+=` ${j.setArr}.push(${j.setItem} == null ? ${j.setItem} : execs[${
|
|
14
|
+
`}let $="";const b=q.flags.isOptional,V=q.transform.filter((Y)=>!Y.options?.deserializeOnly);if(q.type?.collection){const Y=`${j.out}[${JSON.stringify(B)}]`,_=q.type.collection;let J;if(_===c.Set)if(q.type.resolvedCollectionValue){const F=X(q.type.resolvedCollectionValue),h=Q.length;Q.push(F);if(R)J=`{ var __ser_ps = []; for (var __ser_item of ${L}) { __ser_ps.push(__ser_item == null ? __ser_item : execs[${h}].serialize(__ser_item, opts)); } ${Y} = await Promise.all(__ser_ps); }`;else{J=`var ${j.setArr} = [];
|
|
15
|
+
`;J+=` for (var ${j.setItem} of ${L}) {
|
|
16
|
+
`;J+=` ${j.setArr}.push(${j.setItem} == null ? ${j.setItem} : execs[${h}].serialize(${j.setItem}, opts));
|
|
17
17
|
`;J+=` }
|
|
18
|
-
`;J+=` ${
|
|
19
|
-
`;if(q.type.resolvedCollectionValue){const
|
|
20
|
-
`;J+=` for (var ${j.mapEntry} of ${
|
|
21
|
-
`;J+=` ${
|
|
18
|
+
`;J+=` ${Y} = ${j.setArr};`}}else J=`${Y} = [...${L}];`;else{const F=`if (typeof ${j.mapEntry}[0] !== 'string') { throw new BakerError(${JSON.stringify(W)} + ': Map field ' + ${JSON.stringify(U)} + ' has non-string key (' + typeof ${j.mapEntry}[0] + '). Map serialization requires string keys.'); }
|
|
19
|
+
`;if(q.type.resolvedCollectionValue){const h=X(q.type.resolvedCollectionValue),z=Q.length;Q.push(h);const v=R?"await ":"";J=`var ${j.mapObj} = Object.create(null);
|
|
20
|
+
`;J+=` for (var ${j.mapEntry} of ${L}) {
|
|
21
|
+
`;J+=` ${F}`;J+=`${j.mapObj}[${j.mapEntry}[0]] = ${j.mapEntry}[1] == null ? ${j.mapEntry}[1] : ${v}execs[${z}].serialize(${j.mapEntry}[1], opts);
|
|
22
22
|
`;J+=` }
|
|
23
|
-
`;J+=` ${
|
|
24
|
-
`;J+=` for (var ${j.mapEntry} of ${
|
|
25
|
-
`;J+=` ${
|
|
23
|
+
`;J+=` ${Y} = ${j.mapObj};`}else{J=`var ${j.mapObj} = Object.create(null);
|
|
24
|
+
`;J+=` for (var ${j.mapEntry} of ${L}) {
|
|
25
|
+
`;J+=` ${F}`;J+=`${j.mapObj}[${j.mapEntry}[0]] = ${j.mapEntry}[1];
|
|
26
26
|
`;J+=` }
|
|
27
|
-
`;J+=` ${
|
|
27
|
+
`;J+=` ${Y} = ${j.mapObj};`}}J+=T(Y,U,V,M);if(b)$=`if (${L} !== undefined && ${L} !== null) {
|
|
28
28
|
${J}
|
|
29
|
-
} else if (${
|
|
30
|
-
${
|
|
29
|
+
} else if (${L} === null) {
|
|
30
|
+
${Y} = null;
|
|
31
31
|
}
|
|
32
|
-
`;else
|
|
32
|
+
`;else $=`if (${L} != null) {
|
|
33
33
|
${J}
|
|
34
34
|
} else {
|
|
35
|
-
${
|
|
35
|
+
${Y} = ${L};
|
|
36
36
|
}
|
|
37
|
-
`;
|
|
38
|
-
`;
|
|
39
|
-
`;if(
|
|
40
|
-
`;
|
|
41
|
-
`}
|
|
42
|
-
`;return
|
|
37
|
+
`;P+=O+$+S;return P}if(q.type?.resolvedClass||q.type?.discriminator||q.type?.fn&&q.flags.validateNested){const Y=q.type?.isArray||q.flags.validateNestedEach||q.validation.some((F)=>F.each),_=`${j.out}[${JSON.stringify(B)}]`;let J;if(q.type.discriminator){const{property:F,subTypes:h}=q.type.discriminator,z=q.type.keepDiscriminatorProperty!==!1,v=[...h].sort((H,k)=>{if(H.value.prototype instanceof k.value)return-1;if(k.value.prototype instanceof H.value)return 1;return 0}),K=(H,k)=>{let w="";for(let A=0;A<v.length;A++){const g=v[A],N=X(g.value),C=Q.length;Q.push(N);const G=M.length;M.push(g.value);w+=`${A===0?"if":"} else if"} (${H} instanceof refs[${G}]) {
|
|
38
|
+
`;w+=` var ${j.serResult} = ${k}execs[${C}].serialize(${H}, opts);
|
|
39
|
+
`;if(z)w+=` ${j.serResult}[${JSON.stringify(F)}] = ${JSON.stringify(g.name)};
|
|
40
|
+
`;w+=` ${j.outItem} = ${j.serResult};
|
|
41
|
+
`}w+=`} else { ${j.outItem} = `+H+`; }
|
|
42
|
+
`;return w};if(Y){const H=R?"await ":"";if(R)J=`${_} = await Promise.all(${L}.map(async function(__ser_item) {
|
|
43
43
|
`;else{J=`var ${j.discArr} = [];
|
|
44
|
-
`;J+=` for (var ${j.discIdx}=0; ${j.discIdx}<${
|
|
45
|
-
`;J+=` var __ser_item = ${
|
|
44
|
+
`;J+=` for (var ${j.discIdx}=0; ${j.discIdx}<${L}.length; ${j.discIdx}++) {
|
|
45
|
+
`;J+=` var __ser_item = ${L}[${j.discIdx}];
|
|
46
46
|
`}J+=` var ${j.outItem};
|
|
47
|
-
`;J+=K("__ser_item",
|
|
47
|
+
`;J+=K("__ser_item",H);if(R){J+=` return ${j.outItem};
|
|
48
48
|
`;J+="}));"}else{J+=` ${j.discArr}.push(${j.outItem});
|
|
49
49
|
`;J+=` }
|
|
50
|
-
`;J+=` ${
|
|
51
|
-
`;J+=K(
|
|
52
|
-
`;J+=` for (var ${j.nestedIdx}=0; ${j.nestedIdx}<${
|
|
53
|
-
`;J+=` var ${j.nestedItem} = ${
|
|
54
|
-
`;J+=` ${j.nestedArr}.push(${j.nestedItem} == null ? ${j.nestedItem} : execs[${
|
|
50
|
+
`;J+=` ${_} = ${j.discArr};`}}else{const H=R?"await ":"";J=`var ${j.outItem};
|
|
51
|
+
`;J+=K(L,H);J+=`${_} = ${j.outItem};`}}else{const F=q.type.resolvedClass??q.type.fn(),h=X(F),z=Q.length;Q.push(h);if(Y)if(R)J=`${_} = await Promise.all(${L}.map(async function(__ser_item) { return __ser_item == null ? __ser_item : await execs[${z}].serialize(__ser_item, opts); }));`;else{J=`var ${j.nestedArr} = [];
|
|
52
|
+
`;J+=` for (var ${j.nestedIdx}=0; ${j.nestedIdx}<${L}.length; ${j.nestedIdx}++) {
|
|
53
|
+
`;J+=` var ${j.nestedItem} = ${L}[${j.nestedIdx}];
|
|
54
|
+
`;J+=` ${j.nestedArr}.push(${j.nestedItem} == null ? ${j.nestedItem} : execs[${z}].serialize(${j.nestedItem}, opts));
|
|
55
55
|
`;J+=` }
|
|
56
|
-
`;J+=` ${
|
|
56
|
+
`;J+=` ${_} = ${j.nestedArr};`}else J=`${_} = ${R?"await ":""}execs[${z}].serialize(${L}, opts);`}J+=T(_,U,V,M);if(b)$=`if (${L} !== undefined && ${L} !== null) {
|
|
57
57
|
${J}
|
|
58
|
-
} else if (${
|
|
59
|
-
${
|
|
58
|
+
} else if (${L} === null) {
|
|
59
|
+
${_} = null;
|
|
60
60
|
}
|
|
61
|
-
`;else
|
|
61
|
+
`;else $=`if (${L} != null) {
|
|
62
62
|
${J}
|
|
63
63
|
} else {
|
|
64
|
-
${
|
|
64
|
+
${_} = ${L};
|
|
65
65
|
}
|
|
66
|
-
`}else{const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
`}else
|
|
70
|
-
`}
|
|
66
|
+
`}else{const Y=n(U,B,L,q,M);if(b){$+=`if (${L} !== undefined) {
|
|
67
|
+
`;$+=" "+Y+`
|
|
68
|
+
`;$+=`}
|
|
69
|
+
`}else $+=Y+`
|
|
70
|
+
`}P+=O+$+S;return P}function n(U,q,M,Q,R){const X=`${j.out}[${JSON.stringify(q)}]`,Z=Q.transform.filter((W)=>!W.options?.deserializeOnly);if(Z.length>0){const W=x(M,U,Z,R);return`${X} = ${W};`}return`${X} = ${M};`}export{buildSerializeCode};
|
package/dist/src/symbols.d.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* Uses Symbol.for: allows AOT code and runtime code to share the same Symbol via the global registry
|
|
2
|
+
* RAW symbol — zero external storage, zero global pollution.
|
|
3
|
+
* Uses Symbol.for: allows AOT code and runtime code to share the same Symbol via the global registry.
|
|
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 by seal()) */
|
|
8
|
-
export declare const SEALED: unique symbol;
|
package/dist/src/symbols.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Symbol.metadata??=Symbol.for("Symbol.metadata");export const RAW=Symbol.for("baker:raw");
|
|
1
|
+
Symbol.metadata??=Symbol.for("Symbol.metadata");export const RAW=Symbol.for("baker:raw");
|
package/dist/src/types.d.ts
CHANGED
|
@@ -121,15 +121,15 @@ export interface TypeDef {
|
|
|
121
121
|
}[];
|
|
122
122
|
};
|
|
123
123
|
keepDiscriminatorProperty?: boolean;
|
|
124
|
-
/** seal
|
|
124
|
+
/** seal-time normalization result — true if fn() returns an array */
|
|
125
125
|
isArray?: boolean;
|
|
126
|
-
/** seal
|
|
126
|
+
/** seal-time normalization result — cached class after resolving fn() (DTOs only, excluding primitives) */
|
|
127
127
|
resolvedClass?: ClassCtor;
|
|
128
|
-
/** seal
|
|
128
|
+
/** seal-time normalization result — Map or Set collection type */
|
|
129
129
|
collection?: CollectionType;
|
|
130
130
|
/** Nested DTO class thunk for Map value / Set element */
|
|
131
131
|
collectionValue?: () => ClassCtor;
|
|
132
|
-
/** seal
|
|
132
|
+
/** seal-time normalization result — cached class after resolving collectionValue */
|
|
133
133
|
resolvedCollectionValue?: ClassCtor;
|
|
134
134
|
}
|
|
135
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()}
|