@zipbul/baker 5.1.0 → 5.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/CHANGELOG.md +84 -0
- package/dist/index.d.ts +11 -9
- package/dist/index.js +1 -1
- package/dist/src/baker.d.ts +8 -9
- package/dist/src/baker.js +1 -1
- package/dist/src/common/enums.d.ts +10 -0
- package/dist/src/common/enums.js +1 -0
- package/dist/src/common/index.d.ts +6 -0
- package/dist/src/common/index.js +1 -0
- package/dist/src/common/interfaces.d.ts +4 -0
- package/dist/src/common/types.d.ts +2 -0
- package/dist/src/config/config-normalizer.d.ts +7 -0
- package/dist/src/config/config-normalizer.js +1 -0
- package/dist/src/config/constants.d.ts +6 -0
- package/dist/src/config/constants.js +1 -0
- package/dist/src/config/index.d.ts +3 -0
- package/dist/src/config/index.js +1 -0
- package/dist/src/{configure.d.ts → config/interfaces.d.ts} +1 -8
- package/dist/src/config/interfaces.js +0 -0
- package/dist/src/decorators/constants.d.ts +2 -0
- package/dist/src/decorators/constants.js +1 -0
- package/dist/src/decorators/enums.d.ts +5 -0
- package/dist/src/decorators/enums.js +1 -0
- package/dist/src/decorators/field.d.ts +3 -54
- package/dist/src/decorators/field.js +1 -1
- package/dist/src/decorators/index.d.ts +2 -2
- package/dist/src/decorators/index.js +1 -1
- package/dist/src/decorators/interfaces.d.ts +50 -0
- package/dist/src/decorators/interfaces.js +0 -0
- package/dist/src/decorators/public.d.ts +2 -0
- package/dist/src/decorators/public.js +1 -0
- package/dist/src/decorators/types.d.ts +6 -0
- package/dist/src/decorators/types.js +0 -0
- package/dist/src/metadata/enums.d.ts +5 -0
- package/dist/src/metadata/enums.js +1 -0
- package/dist/src/metadata/index.d.ts +3 -0
- package/dist/src/metadata/index.js +1 -0
- package/dist/src/metadata/interfaces.d.ts +90 -0
- package/dist/src/metadata/interfaces.js +0 -0
- package/dist/src/metadata/meta-store.d.ts +49 -0
- package/dist/src/metadata/meta-store.js +1 -0
- package/dist/src/metadata/types.d.ts +10 -0
- package/dist/src/metadata/types.js +0 -0
- package/dist/src/rules/array.d.ts +2 -2
- package/dist/src/rules/array.js +1 -1
- package/dist/src/rules/binary.d.ts +2 -2
- package/dist/src/rules/binary.js +1 -1
- package/dist/src/rules/combinators.d.ts +1 -1
- package/dist/src/rules/combinators.js +1 -1
- package/dist/src/rules/common.d.ts +3 -3
- package/dist/src/rules/common.js +1 -1
- package/dist/src/rules/constants.d.ts +10 -0
- package/dist/src/rules/constants.js +1 -0
- package/dist/src/{create-rule.d.ts → rules/create-rule.d.ts} +1 -1
- package/dist/src/rules/create-rule.js +1 -0
- package/dist/src/rules/date.d.ts +1 -1
- package/dist/src/rules/date.js +1 -1
- package/dist/src/{enums.d.ts → rules/enums.d.ts} +0 -20
- package/dist/src/rules/enums.js +1 -0
- package/dist/src/rules/index.d.ts +5 -13
- package/dist/src/rules/index.js +1 -1
- package/dist/src/rules/interfaces.d.ts +43 -0
- package/dist/src/rules/interfaces.js +0 -0
- package/dist/src/rules/locales.d.ts +1 -1
- package/dist/src/rules/locales.js +1 -1
- package/dist/src/rules/number.d.ts +3 -3
- package/dist/src/rules/number.js +1 -1
- package/dist/src/rules/object.d.ts +1 -1
- package/dist/src/rules/object.js +1 -1
- package/dist/src/rules/public.d.ts +14 -0
- package/dist/src/rules/public.js +1 -0
- package/dist/src/{rule-metadata.d.ts → rules/rule-metadata.d.ts} +1 -1
- package/dist/src/{rule-metadata.js → rules/rule-metadata.js} +1 -1
- package/dist/src/{rule-plan.d.ts → rules/rule-plan.d.ts} +4 -7
- package/dist/src/rules/rule-plan.js +1 -0
- package/dist/src/rules/string-basic.d.ts +23 -0
- package/dist/src/rules/string-basic.js +1 -0
- package/dist/src/rules/string-crypto.d.ts +5 -0
- package/dist/src/rules/string-crypto.js +1 -0
- package/dist/src/rules/string-datetime.d.ts +3 -0
- package/dist/src/rules/string-datetime.js +1 -0
- package/dist/src/rules/string-encoding.d.ts +14 -0
- package/dist/src/rules/string-encoding.js +1 -0
- package/dist/src/rules/string-finance.d.ts +18 -0
- package/dist/src/rules/string-finance.js +10 -0
- package/dist/src/rules/string-format.d.ts +38 -0
- package/dist/src/rules/string-format.js +1 -0
- package/dist/src/rules/string-geo.d.ts +5 -0
- package/dist/src/rules/string-geo.js +1 -0
- package/dist/src/rules/string-identifier.d.ts +16 -0
- package/dist/src/rules/string-identifier.js +3 -0
- package/dist/src/rules/string-shared.d.ts +3 -0
- package/dist/src/rules/string-shared.js +1 -0
- package/dist/src/rules/string-width.d.ts +6 -0
- package/dist/src/rules/string-width.js +1 -0
- package/dist/src/rules/string.d.ts +14 -110
- package/dist/src/rules/string.js +1 -12
- package/dist/src/rules/typechecker.d.ts +10 -10
- package/dist/src/rules/typechecker.js +5 -5
- package/dist/src/rules/types.d.ts +26 -0
- package/dist/src/rules/types.js +0 -0
- package/dist/src/{functions → runtime}/check-call-options.d.ts +1 -1
- package/dist/src/runtime/check-call-options.js +1 -0
- package/dist/src/runtime/constants.d.ts +3 -0
- package/dist/src/runtime/constants.js +1 -0
- package/dist/src/{functions → runtime}/deserialize.d.ts +2 -3
- package/dist/src/runtime/deserialize.js +1 -0
- package/dist/src/runtime/index.d.ts +3 -0
- package/dist/src/runtime/index.js +1 -0
- package/dist/src/{functions → runtime}/serialize.d.ts +2 -2
- package/dist/src/runtime/serialize.js +1 -0
- package/dist/src/{functions → runtime}/validate.d.ts +2 -3
- package/dist/src/runtime/validate.js +1 -0
- package/dist/src/seal/async-analyzer.d.ts +20 -0
- package/dist/src/seal/async-analyzer.js +1 -0
- package/dist/src/seal/circular-analyzer.d.ts +9 -6
- package/dist/src/seal/circular-analyzer.js +1 -1
- package/dist/src/seal/circular-placeholder.d.ts +20 -0
- package/dist/src/seal/circular-placeholder.js +1 -0
- package/dist/src/seal/codegen-utils.d.ts +15 -0
- package/dist/src/seal/codegen-utils.js +1 -1
- package/dist/src/seal/compile-cache.d.ts +38 -0
- package/dist/src/seal/compile-cache.js +1 -0
- package/dist/src/seal/constants.d.ts +62 -0
- package/dist/src/seal/constants.js +1 -0
- package/dist/src/seal/deserialize-builder.d.ts +3 -6
- package/dist/src/seal/deserialize-builder.js +200 -263
- package/dist/src/seal/deserialize-codegen.d.ts +58 -0
- package/dist/src/seal/deserialize-codegen.js +64 -0
- package/dist/src/seal/enums.d.ts +1 -2
- package/dist/src/seal/enums.js +1 -1
- package/dist/src/seal/expose-validator.d.ts +2 -2
- package/dist/src/seal/expose-validator.js +1 -1
- package/dist/src/seal/index.d.ts +3 -0
- package/dist/src/seal/index.js +1 -0
- package/dist/src/seal/inheritance-merger.d.ts +18 -0
- package/dist/src/seal/inheritance-merger.js +1 -0
- package/dist/src/seal/interfaces.d.ts +89 -0
- package/dist/src/seal/interfaces.js +0 -0
- package/dist/src/seal/meta-validator.d.ts +16 -0
- package/dist/src/seal/meta-validator.js +1 -0
- package/dist/src/seal/seal.d.ts +4 -28
- package/dist/src/seal/seal.js +1 -1
- package/dist/src/seal/serialize-builder.d.ts +5 -3
- package/dist/src/seal/serialize-builder.js +64 -64
- package/dist/src/seal/type-normalizer.d.ts +9 -0
- package/dist/src/seal/type-normalizer.js +1 -0
- package/dist/src/seal/type-resolver.d.ts +2 -0
- package/dist/src/seal/type-resolver.js +1 -0
- package/dist/src/seal/types.d.ts +6 -0
- package/dist/src/seal/types.js +0 -0
- package/dist/src/transformers/{collection.transformer.d.ts → collection.d.ts} +1 -1
- package/dist/src/transformers/constants.d.ts +2 -0
- package/dist/src/transformers/constants.js +1 -0
- package/dist/src/transformers/{date.transformer.d.ts → date.d.ts} +1 -1
- package/dist/src/transformers/date.js +1 -0
- package/dist/src/transformers/index.d.ts +3 -8
- package/dist/src/transformers/index.js +1 -1
- package/dist/src/transformers/interfaces.d.ts +26 -0
- package/dist/src/transformers/interfaces.js +0 -0
- package/dist/src/transformers/luxon.d.ts +3 -0
- package/dist/src/transformers/luxon.js +1 -0
- package/dist/src/transformers/moment.d.ts +3 -0
- package/dist/src/transformers/moment.js +1 -0
- package/dist/src/transformers/{number.transformer.d.ts → number.d.ts} +1 -1
- package/dist/src/transformers/public.d.ts +7 -0
- package/dist/src/transformers/public.js +1 -0
- package/dist/src/transformers/{string.transformer.d.ts → string.d.ts} +1 -1
- package/dist/src/transformers/types.d.ts +3 -0
- package/dist/src/transformers/types.js +0 -0
- package/package.json +7 -7
- package/dist/src/collect.d.ts +0 -15
- package/dist/src/collect.js +0 -1
- package/dist/src/configure.js +0 -1
- package/dist/src/create-rule.js +0 -1
- package/dist/src/enums.js +0 -1
- package/dist/src/functions/check-call-options.js +0 -1
- package/dist/src/functions/deserialize.js +0 -1
- package/dist/src/functions/serialize.js +0 -1
- package/dist/src/functions/validate.js +0 -1
- package/dist/src/interfaces.d.ts +0 -32
- package/dist/src/meta-access.d.ts +0 -12
- package/dist/src/meta-access.js +0 -1
- package/dist/src/rule-plan.js +0 -1
- package/dist/src/seal/validate-meta.d.ts +0 -13
- package/dist/src/seal/validate-meta.js +0 -1
- package/dist/src/transformers/date.transformer.js +0 -1
- package/dist/src/transformers/luxon.transformer.d.ts +0 -8
- package/dist/src/transformers/luxon.transformer.js +0 -1
- package/dist/src/transformers/moment.transformer.d.ts +0 -7
- package/dist/src/transformers/moment.transformer.js +0 -1
- package/dist/src/types.d.ts +0 -177
- /package/dist/src/{errors.d.ts → common/errors.d.ts} +0 -0
- /package/dist/src/{errors.js → common/errors.js} +0 -0
- /package/dist/src/{interfaces.js → common/interfaces.js} +0 -0
- /package/dist/src/{types.js → common/types.js} +0 -0
- /package/dist/src/{utils.d.ts → common/utils.d.ts} +0 -0
- /package/dist/src/{utils.js → common/utils.js} +0 -0
- /package/dist/src/transformers/{collection.transformer.js → collection.js} +0 -0
- /package/dist/src/transformers/{number.transformer.js → number.js} +0 -0
- /package/dist/src/transformers/{string.transformer.js → string.js} +0 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { RawPropertyMeta, RuleDef } from '../metadata';
|
|
2
|
+
import type { EmitContext } from '../rules';
|
|
3
|
+
import type { CategorizedRules } from './interfaces';
|
|
4
|
+
import { GuardKey } from './enums';
|
|
5
|
+
/** Generate nested error push code that propagates message/context fields */
|
|
6
|
+
export declare function nestedErrPush(errList: string, pathExpr: string, errItemExpr: string, tmpVar: string): string;
|
|
7
|
+
/** Generate nested error return code that propagates message/context fields */
|
|
8
|
+
export declare function nestedErrReturn(pathExpr: string, errItemExpr: string, tmpVar: string, validateOnly?: boolean): string;
|
|
9
|
+
/** Convert field name to a safe JS variable name (includes prefix to prevent internal variable collisions) */
|
|
10
|
+
export declare function toVarName(key: string, prefix?: string): string;
|
|
11
|
+
export declare function resolveGuardKey(isNullable: boolean, useOptionalGuard: boolean): GuardKey;
|
|
12
|
+
export interface GuardParams {
|
|
13
|
+
varName: string;
|
|
14
|
+
emitCtx: EmitContext;
|
|
15
|
+
assignNull: string;
|
|
16
|
+
validationCode: string;
|
|
17
|
+
}
|
|
18
|
+
export declare const GUARD_STRATEGIES: Record<GuardKey, (p: GuardParams) => string>;
|
|
19
|
+
/**
|
|
20
|
+
* When rd.groups is set, only execute code if there is an intersection with runtime __bk$groups.
|
|
21
|
+
* Rules without groups always execute (preserves existing behavior).
|
|
22
|
+
*/
|
|
23
|
+
export declare function wrapGroupsGuard(rd: RuleDef, code: string): string;
|
|
24
|
+
export declare function sameGroups(a?: string[], b?: string[]): boolean;
|
|
25
|
+
export declare function generateConversionCode(targetType: string, varName: string, fieldKey: string, skipVar: string | null, // null = stopAtFirstError
|
|
26
|
+
collectErrors: boolean, emitCtx: EmitContext): string;
|
|
27
|
+
/** Result of categorizeRules — each/nonEach split and typed dependency classification */
|
|
28
|
+
/** categorizeRules — separate each/nonEach rules, detect mixed gate conflicts (pure) */
|
|
29
|
+
export declare function categorizeRules(fieldKey: string, validation: RawPropertyMeta['validation']): CategorizedRules;
|
|
30
|
+
/** Config object for emitTypedRules — bundles closure-captured vars into explicit parameter */
|
|
31
|
+
export interface TypeGateConfig {
|
|
32
|
+
effectiveGateType: string;
|
|
33
|
+
gateCondition: string;
|
|
34
|
+
gateErrorCode: string;
|
|
35
|
+
gateEmitCtx: EmitContext;
|
|
36
|
+
otherGeneral: RuleDef[];
|
|
37
|
+
gateDeps: RuleDef[];
|
|
38
|
+
typeAsserter: RuleDef | undefined;
|
|
39
|
+
enableConversion: boolean;
|
|
40
|
+
}
|
|
41
|
+
/** Generate nested-result handling for deserialize mode (pure) */
|
|
42
|
+
export declare function generateNestedResultCode(fieldKey: string, resultVar: string, collectErrors: boolean, pathPrefix?: string): string;
|
|
43
|
+
/**
|
|
44
|
+
* Nested-executor result handling inside a per-element loop (Set / Map / array / discriminator-each).
|
|
45
|
+
* Single source for the `if (isErr(result)) { …re-path nested errors… } else { <success> }` block that
|
|
46
|
+
* every collection loop repeats — only the element path expression (`ppExpr`), the success statement
|
|
47
|
+
* (`arr.push` / `map.set` / `set.add`), and the base indent differ. The single-object case keeps using
|
|
48
|
+
* {@link generateNestedResultCode} (it writes straight to `out[field]`).
|
|
49
|
+
*/
|
|
50
|
+
export declare function generateNestedEachResultCode(resultVar: string, ppExpr: string, sk: string, collectErrors: boolean, successStmt: string, indent: string): string;
|
|
51
|
+
/** Generate validate-mode nested result handling (null check instead of isErr) (pure) */
|
|
52
|
+
export declare function generateValidateNestedResult(fieldKey: string, resultVar: string, collectErrors: boolean, pathPrefix?: string): string;
|
|
53
|
+
/**
|
|
54
|
+
* Validate-mode counterpart of {@link generateNestedEachResultCode}: the per-element `if (result !==
|
|
55
|
+
* null) { …re-path the returned issue array… }` block shared by the Set / Map / array / discriminator
|
|
56
|
+
* validate-each loops. The element path expression and base indent are the only per-site differences.
|
|
57
|
+
*/
|
|
58
|
+
export declare function generateValidateNestedEachResultCode(resultVar: string, ppExpr: string, sk: string, collectErrors: boolean, indent: string): string;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import{BakerError as M}from"../common/index.js";import{sanitizeKey as H,buildGroupsHasExpr as q}from"./codegen-utils.js";import{DES_GEN as W}from"./constants.js";import{GuardKey as Z}from"./enums.js";export function nestedErrPush(w,j,D,Q){const A=`${Q}_e`;return`var ${A}=${D};
|
|
2
|
+
if(${A}.message===undefined&&${A}.context===undefined){${w}.push({path:${j},code:${A}.code});}
|
|
3
|
+
else{var ${Q}={path:${j},code:${A}.code};
|
|
4
|
+
if(${A}.message!==undefined)${Q}.message=${A}.message;
|
|
5
|
+
if(${A}.context!==undefined)${Q}.context=${A}.context;
|
|
6
|
+
${w}.push(${Q});}
|
|
7
|
+
`}export function nestedErrReturn(w,j,D,Q){const A=(U)=>Q?`return ${U};
|
|
8
|
+
`:`return err(${U});
|
|
9
|
+
`,J=`${D}_e`;return`var ${J}=${j};
|
|
10
|
+
if(${J}.message===undefined&&${J}.context===undefined)${A(`[{path:${w},code:${J}.code}]`)} var ${D}={path:${w},code:${J}.code};
|
|
11
|
+
if(${J}.message!==undefined)${D}.message=${J}.message;
|
|
12
|
+
if(${J}.context!==undefined)${D}.context=${J}.context;
|
|
13
|
+
${A(`[${D}]`)}`}export function toVarName(w,j){return W.field+(j||"")+H(w)}export function resolveGuardKey(w,j){if(w&&j)return Z.NullableOptional;if(w)return Z.Nullable;if(j)return Z.Optional;return Z.Default}export const GUARD_STRATEGIES={[Z.NullableOptional]({varName:w,assignNull:j,validationCode:D}){let Q=`if (${w} === null) { ${j}}
|
|
14
|
+
`;Q+=`else if (${w} !== undefined) {
|
|
15
|
+
`;Q+=D;Q+=`}
|
|
16
|
+
`;return Q},[Z.Nullable]({varName:w,emitCtx:j,assignNull:D,validationCode:Q}){let A=`if (${w} === undefined) ${j.fail("isDefined")};
|
|
17
|
+
`;A+=`else if (${w} !== null) {
|
|
18
|
+
`;A+=Q;A+=`} else { ${D}}
|
|
19
|
+
`;return A},[Z.Optional]({varName:w,validationCode:j}){let D=`if (${w} !== undefined && ${w} !== null) {
|
|
20
|
+
`;D+=j;D+=`}
|
|
21
|
+
`;return D},[Z.Default]({varName:w,emitCtx:j,validationCode:D}){let Q=`if (${w} === undefined || ${w} === null) ${j.fail("isDefined")};
|
|
22
|
+
`;Q+=`else {
|
|
23
|
+
`;Q+=D;Q+=`}
|
|
24
|
+
`;return Q}};export function wrapGroupsGuard(w,j){if(!w.groups||w.groups.length===0)return j;return`if ((${W.group0} === null && !${W.groupsSet}) || ${q(W.group0,W.groupsSet,w.groups)}) {
|
|
25
|
+
${j}
|
|
26
|
+
}
|
|
27
|
+
`}export function sameGroups(w,j){if(!w||w.length===0)return!j||j.length===0;if(!j||w.length!==j.length)return!1;for(let D=0;D<w.length;D++)if(w[D]!==j[D])return!1;return!0}export function generateConversionCode(w,j,D,Q,A,J){const U=A?`${J.fail("conversionFailed")}; ${Q} = true;`:J.fail("conversionFailed")+";";switch(w){case"string":return` ${j} = String(${j});
|
|
28
|
+
`;case"number":return` ${j} = Number(${j});
|
|
29
|
+
if (isNaN(${j})) { ${U} }
|
|
30
|
+
`;case"boolean":return` if (${j} === 'true' || ${j} === '1' || ${j} === 1) ${j} = true;
|
|
31
|
+
else if (${j} === 'false' || ${j} === '0' || ${j} === 0) ${j} = false;
|
|
32
|
+
else { ${U} }
|
|
33
|
+
`;case"date":return` ${j} = new Date(${j});
|
|
34
|
+
if (isNaN(${j}.getTime())) { ${U} }
|
|
35
|
+
`;default:throw new M(`Unknown implicit conversion type: "${w}" for field "${D}"`)}}export function categorizeRules(w,j){const D=[],Q=[],A={string:[],number:[],boolean:[],date:[],array:[],object:[]};for(const X of j){if(X.each){D.push(X);continue}const Y=X.rule.requiresType;if(Y!==void 0)A[Y].push(X);else Q.push(X)}let J=void 0,U=null;for(const X of["string","number","boolean","date","array","object"]){const Y=A[X];if(Y.length===0)continue;if(J){if(U===null)U=[J.type];U.push(X)}else J={type:X,deps:Y}}if(U)throw new M(`Field "${w}" has conflicting requiresType: ${U.join(", ")}`);return{each:D,generalRules:Q,typedDeps:J}}export function generateNestedResultCode(w,j,D,Q){const A=H(w),J=Q?`${Q}+${JSON.stringify(w+".")}`:JSON.stringify(w+".");if(D){const X=`${W.errors}${A}[${W.nestedIdx}${A}]`;return` if (isErr(${j})) {
|
|
36
|
+
var ${W.errors}${A} = ${j}.data;
|
|
37
|
+
var __bk$pp${A} = ${J};
|
|
38
|
+
for (var ${W.nestedIdx}${A}=0; ${W.nestedIdx}${A}<${W.errors}${A}.length; ${W.nestedIdx}${A}++) {
|
|
39
|
+
`+nestedErrPush(W.errList,`__bk$pp${A}+${X}.path`,X,`__ne${A}`)+` }
|
|
40
|
+
} else { ${W.out}[${JSON.stringify(w)}] = ${j}; }
|
|
41
|
+
`}const U=`${W.errors}${A}[0]`;return` if (isErr(${j})) {
|
|
42
|
+
var ${W.errors}${A} = ${j}.data;
|
|
43
|
+
var __bk$pp${A} = ${J};
|
|
44
|
+
`+nestedErrReturn(`__bk$pp${A}+${U}.path`,U,`__ne${A}`)+` } else { ${W.out}[${JSON.stringify(w)}] = ${j}; }
|
|
45
|
+
`}export function generateNestedEachResultCode(w,j,D,Q,A,J){const U=`${W.errors}${D}`,X=`__bk$pp${D}`,Y=`${J} var ${U} = ${w}.data;
|
|
46
|
+
${J} var ${X} = ${j};
|
|
47
|
+
`;let $;if(Q){const _=`${W.nestedIdx}${D}`;$=`${J} for (var ${_}=0; ${_}<${U}.length; ${_}++) {
|
|
48
|
+
${J} `+nestedErrPush(W.errList,`${X}+${U}[${_}].path`,`${U}[${_}]`,`__ne${D}`)+`${J} }
|
|
49
|
+
`}else $=`${J} `+nestedErrReturn(`${X}+${U}[0].path`,`${U}[0]`,`__ne${D}`);return`${J}if (isErr(${w})) {
|
|
50
|
+
${Y}${$}${J}} else { ${A} }
|
|
51
|
+
`}export function generateValidateNestedResult(w,j,D,Q){const A=H(w),J=`__bk$pp${A}`,U=Q?`${Q}+${JSON.stringify(w+".")}`:JSON.stringify(w+".");if(D){const Y=`${j}[${W.nestedIdx}${A}]`;return` if (${j} !== null) {
|
|
52
|
+
var ${J} = ${U};
|
|
53
|
+
for (var ${W.nestedIdx}${A}=0; ${W.nestedIdx}${A}<${j}.length; ${W.nestedIdx}${A}++) {
|
|
54
|
+
`+nestedErrPush(W.errList,`${J}+${Y}.path`,Y,`__ne${A}`)+` }
|
|
55
|
+
}
|
|
56
|
+
`}const X=`${j}[0]`;return` if (${j} !== null) {
|
|
57
|
+
var ${J} = ${U};
|
|
58
|
+
`+nestedErrReturn(`${J}+${X}.path`,X,`__ne${A}`,!0)+` }
|
|
59
|
+
`}export function generateValidateNestedEachResultCode(w,j,D,Q,A){const J=`__bk$pp${D}`;let U=`${A}if (${w} !== null) {
|
|
60
|
+
${A} var ${J} = ${j};
|
|
61
|
+
`;if(Q){const X=`${W.nestedIdx}${D}`;U+=`${A} for (var ${X}=0; ${X}<${w}.length; ${X}++) {
|
|
62
|
+
${A} `+nestedErrPush(W.errList,`${J}+${w}[${X}].path`,`${w}[${X}]`,`__ne${D}`)+`${A} }
|
|
63
|
+
`}else U+=`${A} `+nestedErrReturn(`${J}+${w}[0].path`,`${w}[0]`,`__ne${D}`,!0);U+=`${A}}
|
|
64
|
+
`;return U}
|
package/dist/src/seal/enums.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
/** Null/undefined guard strategy selected per field from its optional/nullable
|
|
1
|
+
/** Null/undefined guard strategy selected per field from its optional/nullable flags. */
|
|
2
2
|
export declare enum GuardKey {
|
|
3
3
|
NullableOptional = "nullable+optional",
|
|
4
4
|
Nullable = "nullable",
|
|
5
|
-
Defined = "defined",
|
|
6
5
|
Optional = "optional",
|
|
7
6
|
Default = "default"
|
|
8
7
|
}
|
package/dist/src/seal/enums.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export var GuardKey;((l)=>{l.NullableOptional="nullable+optional";l.Nullable="nullable";l.
|
|
1
|
+
export var GuardKey;((l)=>{l.NullableOptional="nullable+optional";l.Nullable="nullable";l.Optional="optional";l.Default="default"})(GuardKey||={});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { RawClassMeta } from '../
|
|
1
|
+
import type { RawClassMeta } from '../metadata';
|
|
2
2
|
/**
|
|
3
|
-
* Static validation of @Expose stacks
|
|
3
|
+
* Static validation of @Expose stacks
|
|
4
4
|
*
|
|
5
5
|
* Check 1: same @Expose entry has deserializeOnly: true + serializeOnly: true → excluded from both directions
|
|
6
6
|
* Check 2: if 2+ @Expose entries in the same direction have overlapping groups → BakerError
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Direction as L}from"../
|
|
1
|
+
import{Direction as Q,BakerError as L}from"../common/index.js";import{RESERVED_PROPERTY_NAMES as Z}from"./constants.js";function validateExposeStacks(H,q){const F=q?`${q}.`:"";for(const[C,I]of Object.entries(H)){for(const z of I.expose){if(z.deserializeOnly&&z.serializeOnly)throw new L(`Invalid @Expose on field '${F}${C}': cannot have both deserializeOnly:true and serializeOnly:true on the same @Expose entry. Use separate @Expose decorators for each direction.`);if(z.name!==void 0&&Z.has(z.name))throw new L(`Invalid @Expose name on '${F}${C}': '${z.name}' is a reserved property name and cannot be used as a serialized key.`)}const J=I.expose.filter((z)=>!z.serializeOnly),K=I.expose.filter((z)=>!z.deserializeOnly);U(F+C,J,Q.Deserialize);U(F+C,K,Q.Serialize)}}function U(H,q,F){for(let C=0;C<q.length;C++)for(let I=C+1;I<q.length;I++){const J=q[C].groups??[],K=q[I].groups??[];if($(J,K)){const z=new Set(K),W=J.filter((X)=>z.has(X));throw new L(`@Expose conflict on '${H}': 2 @Expose stacks with '${F}' direction and overlapping groups [${W.join(", ")}]. Each direction must have at most one @Expose per group set.`)}}}function $(H,q){if(H.length===0&&q.length===0)return!0;if(H.length===0||q.length===0)return!1;return H.some((F)=>q.includes(F))}export{validateExposeStacks};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{sealRegistry}from"./seal.js";export{SEAL_OPTION_KEYS}from"./constants.js";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { RawClassMeta, MetaStore } from '../metadata';
|
|
2
|
+
/**
|
|
3
|
+
* Merges RAW metadata child-first along the prototype chain of a class. Holds the {@link MetaStore} it
|
|
4
|
+
* reads RAW through as an injected collaborator.
|
|
5
|
+
*
|
|
6
|
+
* Merge rules:
|
|
7
|
+
* - validation: union by ruleName — child wins on a same-ruleName collision; otherwise parent rules are appended
|
|
8
|
+
* - transform: child takes priority, inherits from parent if absent in child
|
|
9
|
+
* - expose: child takes priority, inherits from parent if absent in child
|
|
10
|
+
* - exclude: child takes priority, inherits from parent if absent in child
|
|
11
|
+
* - type: child takes priority, inherits from parent if absent in child
|
|
12
|
+
* - flags: child takes priority, only missing flags are supplemented from parent
|
|
13
|
+
*/
|
|
14
|
+
export declare class InheritanceMerger {
|
|
15
|
+
#private;
|
|
16
|
+
constructor(meta: MetaStore);
|
|
17
|
+
merge(Class: Function): RawClassMeta;
|
|
18
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export class InheritanceMerger{#b;constructor(D){this.#b=D}merge(D){const F=[];let z=D;while(z&&z!==Object){if(this.#b.hasOwn(z))F.push(z);const B=Object.getPrototypeOf(z);z=B===z?null:B}const A=Object.create(null);for(const B of F){const H=this.#b.get(B);for(const[E,x]of Object.entries(H))if(!A[E])A[E]={...x,validation:[...x.validation],transform:[...x.transform],expose:[...x.expose],exclude:x.exclude,type:x.type,flags:{...x.flags}};else{const b=A[E],j=x;for(const G of j.validation)if(!b.validation.some((I)=>I.rule.ruleName===G.rule.ruleName))b.validation.push(G);if(b.transform.length===0&&j.transform.length>0)b.transform=[...j.transform];if(b.expose.length===0&&j.expose.length>0)b.expose=[...j.expose];if(b.exclude===null&&j.exclude!==null)b.exclude=j.exclude;if(b.type===null&&j.type!==null)b.type=j.type;if(b.message===void 0&&j.message!==void 0)b.message=j.message;if(b.context===void 0&&j.context!==void 0)b.context=j.context;const q=b.flags,v=j.flags;if(v.isOptional!==void 0&&q.isOptional===void 0)q.isOptional=v.isOptional;if(v.validateIf!==void 0&&q.validateIf===void 0)q.validateIf=v.validateIf;if(v.isNullable!==void 0&&q.isNullable===void 0)q.isNullable=v.isNullable;if(v.validateNested!==void 0&&q.validateNested===void 0)q.validateNested=v.validateNested;if(v.validateNestedEach!==void 0&&q.validateNestedEach===void 0)q.validateNestedEach=v.validateNestedEach}}return A}}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { Result, ResultAsync } from '@zipbul/result';
|
|
2
|
+
import type { BakerIssue, RuntimeOptions } from '../common';
|
|
3
|
+
import type { CollectionType, RawClassMeta, RuleDef } from '../metadata';
|
|
4
|
+
export interface SealOptions {
|
|
5
|
+
/** Automatic conversion using validation decorators as type hints. @default false */
|
|
6
|
+
enableImplicitConversion?: boolean;
|
|
7
|
+
/** Use class default values when the key is missing from input. @default false */
|
|
8
|
+
exposeDefaultValues?: boolean;
|
|
9
|
+
/** true: return immediately on first error. false (default): collect all errors. @default false */
|
|
10
|
+
stopAtFirstError?: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* true: reject undeclared fields. Uses the key set from mergeInheritance(Class) as the allowlist.
|
|
13
|
+
* `@Exclude` fields are also included in the whitelist — present but excluded from the result.
|
|
14
|
+
* @default false
|
|
15
|
+
*/
|
|
16
|
+
whitelist?: boolean;
|
|
17
|
+
/** true: include field exclusion reasons as comments in generated code. @default false */
|
|
18
|
+
debug?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface SealedExecutors<T> {
|
|
21
|
+
/** Internal executor — Result pattern. deserialize() wraps and converts to throw */
|
|
22
|
+
deserialize(input: unknown, options?: RuntimeOptions): Result<T, BakerIssue[]> | ResultAsync<T, BakerIssue[]>;
|
|
23
|
+
/** Internal executor — always succeeds. serialize assumes no validation */
|
|
24
|
+
serialize(instance: T, options?: RuntimeOptions): Record<string, unknown> | Promise<Record<string, unknown>>;
|
|
25
|
+
/** Internal executor — validate-only (no object creation). Returns null on success, BakerIssue[] on failure */
|
|
26
|
+
validate(input: unknown, options?: RuntimeOptions): BakerIssue[] | null | Promise<BakerIssue[] | null>;
|
|
27
|
+
/** true if the deserialize direction has async rules/transforms/nested */
|
|
28
|
+
isAsync: boolean;
|
|
29
|
+
/** true if the serialize direction has async transforms/nested */
|
|
30
|
+
isSerializeAsync: boolean;
|
|
31
|
+
/** Inheritance-resolved metadata — read during codegen to wire nested DTO fields and async analysis */
|
|
32
|
+
merged?: RawClassMeta;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Classification of a `@Type`/`@Field` `type` thunk's return value. The single reading of the
|
|
36
|
+
* Map/Set marker + array-unwrap that seal normalization, circular analysis, and async analysis all
|
|
37
|
+
* share — each caller then applies its OWN primitive-exclusion and error policy to `resolved` (seal
|
|
38
|
+
* throws on a non-constructor; the analyzers skip it), so only the classification lives here.
|
|
39
|
+
*/
|
|
40
|
+
export interface ClassifiedType {
|
|
41
|
+
/** Set when the thunk returned the `Map` or `Set` constructor (a collection field). */
|
|
42
|
+
collection?: CollectionType;
|
|
43
|
+
/** True when the thunk returned the array form `[Element]`. */
|
|
44
|
+
isArray: boolean;
|
|
45
|
+
/** The element value (array-unwrapped), or `undefined` for a Map/Set collection. */
|
|
46
|
+
resolved: unknown;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Inline-nested scope a parent builder hands to a child: the shared mutable accumulator (reference
|
|
50
|
+
* arrays + circular-tracking set) plus the child's own path/var/input expression overrides.
|
|
51
|
+
*/
|
|
52
|
+
export interface ChildScope {
|
|
53
|
+
regexes: RegExp[];
|
|
54
|
+
refs: unknown[];
|
|
55
|
+
execs: SealedExecutors<unknown>[];
|
|
56
|
+
inlineCounter: {
|
|
57
|
+
n: number;
|
|
58
|
+
};
|
|
59
|
+
inlineNestedClasses: Set<Function> | undefined;
|
|
60
|
+
pathPrefix: string;
|
|
61
|
+
varPrefix: string;
|
|
62
|
+
inputExpr: string;
|
|
63
|
+
}
|
|
64
|
+
/** Partitioned validation rules for a field — produced by categorizeRules. */
|
|
65
|
+
export interface CategorizedRules {
|
|
66
|
+
each: RuleDef[];
|
|
67
|
+
generalRules: RuleDef[];
|
|
68
|
+
/** The single typed dependency group (if any) after conflict check */
|
|
69
|
+
typedDeps: {
|
|
70
|
+
type: 'string' | 'number' | 'boolean' | 'date' | 'array' | 'object';
|
|
71
|
+
deps: RuleDef[];
|
|
72
|
+
} | undefined;
|
|
73
|
+
}
|
|
74
|
+
/** Result of resolveTypeGate — effective gate type and related metadata. */
|
|
75
|
+
export interface ResolvedTypeGate {
|
|
76
|
+
effectiveGateType: string | null;
|
|
77
|
+
/** The typed dependency rules (from requiresType) */
|
|
78
|
+
gateDeps: RuleDef[];
|
|
79
|
+
/** Index of the type asserter within generalRules (-1 if none) */
|
|
80
|
+
typeAsserterIdx: number;
|
|
81
|
+
/** The type asserter rule def (if found) */
|
|
82
|
+
typeAsserter: RuleDef | undefined;
|
|
83
|
+
/** Whether conversion is enabled for this field */
|
|
84
|
+
enableConversion: boolean;
|
|
85
|
+
/** Whether this gate was inferred from asserter only (no typed deps) */
|
|
86
|
+
asserterInferredGate: string | null;
|
|
87
|
+
/** Whether this gate was inferred from @Type hint */
|
|
88
|
+
typeHintGate: string | null;
|
|
89
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { RawClassMeta, MetaStore } from '../metadata';
|
|
2
|
+
/**
|
|
3
|
+
* Seal-time invariant checks on the merged metadata, run from sealOne after merge + type normalization
|
|
4
|
+
* and before codegen. Holds the {@link MetaStore} (for @Field-presence checks) as an injected collaborator.
|
|
5
|
+
* Throws BakerError on the first violation.
|
|
6
|
+
*
|
|
7
|
+
* Covers W2 (D7 + D9):
|
|
8
|
+
* - Discriminator shape: empty subTypes / invalid subType entry / name collision / missing/reserved property
|
|
9
|
+
* - Set/Map pairing: when a setValue/mapValue thunk is present, its target class must have @Field metadata
|
|
10
|
+
* (a primitive Set/Map with no value thunk is valid and intentionally not flagged)
|
|
11
|
+
*/
|
|
12
|
+
export declare class MetaValidator {
|
|
13
|
+
#private;
|
|
14
|
+
constructor(meta: MetaStore);
|
|
15
|
+
validateShape(Class: Function, merged: RawClassMeta): void;
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export class MetaValidator{#j;constructor(H){this.#j=H}validateShape(H,J){const v=H.name;for(const[w,F]of Object.entries(J)){if(F.type?.discriminator){const j=F.type.discriminator;if(typeof j.property!=="string"||j.property.length===0)throw new x(`${v}.${w}: discriminator.property must be a non-empty string.`);if(L.has(j.property))throw new x(`${v}.${w}: discriminator.property '${j.property}' is a reserved property name and cannot be used.`);if(!Array.isArray(j.subTypes)||j.subTypes.length===0)throw new x(`${v}.${w}: discriminator.subTypes must be a non-empty array of { value, name } entries.`);const G=new Set;for(let z=0;z<j.subTypes.length;z++){const q=j.subTypes[z];if(typeof q.name!=="string"||q.name.length===0)throw new x(`${v}.${w}: discriminator.subTypes[${z}].name must be a non-empty string.`);if(typeof q.value!=="function")throw new x(`${v}.${w}: discriminator.subTypes[${z}].value must be a class constructor (got ${typeof q.value}).`);if(G.has(q.name))throw new x(`${v}.${w}: discriminator.subTypes has duplicate name '${q.name}'. Each subType must have a unique name.`);G.add(q.name);if(!this.#j.hasOwn(q.value))throw new x(`${v}.${w}: discriminator.subTypes[${z}].value (${q.value.name}) has no @Field decorators.`)}}const I=F.type?.collection;if(I!==void 0&&F.type?.resolvedCollectionValue){const j=F.type.resolvedCollectionValue;if(!this.#j.hasOwn(j)){const G=I===K.Set?"setValue":"mapValue";throw new x(`${v}.${w}: ${G} target (${j.name}) has no @Field decorators.`)}}}}}import{BakerError as x}from"../common/index.js";import{CollectionType as K}from"../metadata/index.js";import{RESERVED_PROPERTY_NAMES as L}from"./constants.js";
|
package/dist/src/seal/seal.d.ts
CHANGED
|
@@ -1,31 +1,7 @@
|
|
|
1
|
-
import type { SealOptions } from '
|
|
2
|
-
import type { RawClassMeta, SealedExecutors } from '../types';
|
|
3
|
-
/** @internal Placeholder executor for circular dependency detection during seal */
|
|
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;
|
|
1
|
+
import type { SealOptions, SealedExecutors } from './interfaces';
|
|
11
2
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
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).
|
|
3
|
+
* Seal every class in `registry` with `options`, writing executors into `executors`. The core used by
|
|
4
|
+
* `new Baker().seal()` — a thin entry point over one {@link SealRun}.
|
|
16
5
|
*/
|
|
17
|
-
declare function clearAllCached(): void;
|
|
18
6
|
declare function sealRegistry(registry: Set<Function>, options: SealOptions, executors: Map<Function, SealedExecutors<unknown>>): void;
|
|
19
|
-
|
|
20
|
-
* Merges RAW metadata child-first along the prototype chain of Class.
|
|
21
|
-
*
|
|
22
|
-
* Merge rules:
|
|
23
|
-
* - validation: union merge (both parent and child apply, duplicate rules removed)
|
|
24
|
-
* - transform: child takes priority, inherits from parent if absent in child
|
|
25
|
-
* - expose: child takes priority, inherits from parent if absent in child
|
|
26
|
-
* - exclude: child takes priority, inherits from parent if absent in child
|
|
27
|
-
* - type: child takes priority, inherits from parent if absent in child
|
|
28
|
-
* - flags: child takes priority, only missing flags are supplemented from parent
|
|
29
|
-
*/
|
|
30
|
-
declare function mergeInheritance(Class: Function): RawClassMeta;
|
|
31
|
-
export { sealRegistry, mergeInheritance, circularPlaceholder, getCached, configFingerprint, clearCached, clearAllCached };
|
|
7
|
+
export { sealRegistry };
|
package/dist/src/seal/seal.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
class W{executors;options;fp;sealed=new Map;inserted=new Set;resolve=(j)=>this.executors.get(j);#q;#F;#j;#G;constructor(j,F,G=N){this.executors=j;this.options=F;this.fp=L.fingerprint(F);this.#q=new T(G);this.#F=new Y(this.#q);this.#j=new O(this.resolve,this.#q);this.#G=new V(G)}run(j){try{for(const F of j)this.sealOne(F)}catch(F){for(const G of this.inserted)this.executors.delete(G);throw F}for(const[F,G]of this.sealed)L.set(F,this.fp,G);j.clear()}sealOne(j){if(this.executors.has(j))return;const F=L.get(j,this.fp);if(F!==void 0){this.executors.set(j,F);this.inserted.add(j);if(F.merged)for(const q of Object.values(F.merged))for(const H of this.#j.nestedClassesOf(q))this.sealOne(H);return}const G=new B(j.name);this.executors.set(j,G);this.inserted.add(j);try{const q=this.#q.merge(j);for(const J of Object.keys(q))if(_.has(J))throw new I(`${j.name}: field name '${J}' is not allowed (reserved property name)`);b(q,j.name);P(q,j.name);this.#G.validateShape(j,q);const H=this.#F.analyze(j);for(const J of Object.values(q))for(const f of this.#j.nestedClassesOf(J))this.sealOne(f);const K=this.#j.analyze(q,U.Deserialize),Q=this.#j.analyze(q,U.Serialize),X=w(j,q,this.options,H,K,this.resolve),Z=M(j,q,this.options,H,K,this.resolve),$=D(j,q,this.options,Q,this.resolve);Object.assign(G,{deserialize:X,serialize:$,validate:Z,isAsync:K,isSerializeAsync:Q,merged:q})}catch(q){this.executors.delete(j);throw q}this.sealed.set(j,G)}}import{Direction as U,BakerError as I}from"../common/index.js";import{metaStore as N}from"../metadata/index.js";import{AsyncAnalyzer as O}from"./async-analyzer.js";import{CircularAnalyzer as Y}from"./circular-analyzer.js";import{CircularPlaceholder as B}from"./circular-placeholder.js";import{compileCache as L}from"./compile-cache.js";import{RESERVED_PROPERTY_NAMES as _}from"./constants.js";import{buildDeserializeCode as w,buildValidateCode as M}from"./deserialize-builder.js";import{validateExposeStacks as P}from"./expose-validator.js";import{InheritanceMerger as T}from"./inheritance-merger.js";import{MetaValidator as V}from"./meta-validator.js";import{buildSerializeCode as D}from"./serialize-builder.js";import{normalizeTypeDefs as b}from"./type-normalizer.js";function sealRegistry(j,F,G){new W(G,F).run(j)}export{sealRegistry};
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { RawClassMeta
|
|
1
|
+
import type { RuntimeOptions } from '../common';
|
|
2
|
+
import type { RawClassMeta } from '../metadata';
|
|
3
|
+
import type { SealOptions, SealedExecutors } from './interfaces';
|
|
3
4
|
/**
|
|
4
5
|
* Generate serialize executor code.
|
|
5
|
-
*
|
|
6
|
+
* Thin wrapper preserving the historical free-function entry point: instantiates
|
|
7
|
+
* SerializeBuilder and returns its built executor.
|
|
6
8
|
*/
|
|
7
9
|
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
10
|
export { buildSerializeCode };
|
|
@@ -1,70 +1,70 @@
|
|
|
1
|
-
|
|
2
|
-
${
|
|
3
|
-
`;
|
|
4
|
-
`;
|
|
5
|
-
`;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
`;const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
`;
|
|
12
|
-
`;
|
|
13
|
-
`;
|
|
14
|
-
|
|
15
|
-
`;
|
|
16
|
-
`;
|
|
17
|
-
`;
|
|
18
|
-
`;
|
|
19
|
-
|
|
20
|
-
`;
|
|
21
|
-
`;
|
|
22
|
-
`;
|
|
23
|
-
`;
|
|
24
|
-
`;
|
|
25
|
-
`;
|
|
26
|
-
`;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
${Y} = null;
|
|
1
|
+
class x{refs=[];execs=[];Class;merged;options;isAsync;resolve;constructor(Q,M,U,Y,Z){this.Class=Q;this.merged=M;this.options=U;this.isAsync=Y;this.resolve=Z}build(){let Q=`'use strict';
|
|
2
|
+
`;Q+=`var ${q.out} = {};
|
|
3
|
+
`;let M=!1;for(const _ in this.merged){const j=this.merged[_];if(j===void 0)continue;const R=V(j.expose,b.Serialize);if(R&&R.length>0){M=!0;break}}if(M){Q+=`var ${q.groups} = opts && opts.groups;
|
|
4
|
+
`;Q+=`var ${q.group0} = ${q.groups} && ${q.groups}.length === 1 ? ${q.groups}[0] : null;
|
|
5
|
+
`;Q+=`var ${q.groupsSet} = ${q.groups} && ${q.groups}.length > 1 ? new Set(${q.groups}) : null;
|
|
6
|
+
`}for(const[_,j]of Object.entries(this.merged))Q+=this.generateFieldCode(_,j);Q+=`return ${q.out};
|
|
7
|
+
`;const U=this.Class.name.replace(/[^\w$.-]/g,"_");Q+=`//# sourceURL=baker://${U}/serialize
|
|
8
|
+
`;const Y=this.isAsync?"async function":"function";return Function("refs","execs","BakerError",`return ${Y}(instance, opts) { `+Q+" }")(this.refs,this.execs,K)}resolveExecutor(Q){const M=this.resolve(Q);if(M===void 0)throw new K(`${this.Class.name}: nested class '${Q.name}' was not sealed before serialize codegen.`);return M}generateFieldCode(Q,M){const U=this.Class.name,Y=this.options;if(M.exclude){if(!M.exclude.deserializeOnly){if(Y?.debug){const W=M.exclude.serializeOnly?"serializeOnly":"bidirectional";return`// [baker] field ${JSON.stringify(Q)} excluded (${W} @Exclude)
|
|
9
|
+
`}return""}}if(M.expose.length>0&&M.expose.every((W)=>W.deserializeOnly)){if(Y?.debug)return`// [baker] field ${JSON.stringify(Q)} excluded (all @Expose entries are deserializeOnly)
|
|
10
|
+
`;return""}const Z=p(Q,M.expose,b.Serialize),_=V(M.expose,b.Serialize),j=E(Q),R=`${q.fieldVal}${j}`;let A="";A+=`var ${R} = instance[${JSON.stringify(Q)}];
|
|
11
|
+
`;let v="",w="";if(_&&_.length>0){v=`if ((${q.group0} !== null || ${q.groupsSet}) && (${f(q.group0,q.groupsSet,_)})) {
|
|
12
|
+
`;w=`}
|
|
13
|
+
`}let J="";const I=M.flags.isOptional,g=M.transform.filter((W)=>!W.options?.deserializeOnly);if(M.type?.collection){const W=`${q.out}[${JSON.stringify(Z)}]`,$=M.type.collection;let L;if($===m.Set)if(M.type.resolvedCollectionValue){const H=this.resolveExecutor(M.type.resolvedCollectionValue),O=this.execs.length;this.execs.push(H);if(this.isAsync)L=`{ var __ser_ps${j} = []; for (var __ser_item${j} of ${R}) { __ser_ps${j}.push(__ser_item${j} == null ? __ser_item${j} : execs[${O}].serialize(__ser_item${j}, opts)); } ${W} = await Promise.all(__ser_ps${j}); }`;else{L=`var ${q.setArr}${j} = [];
|
|
14
|
+
`;L+=` for (var ${q.setItem}${j} of ${R}) {
|
|
15
|
+
`;L+=` ${q.setArr}${j}.push(${q.setItem}${j} == null ? ${q.setItem}${j} : execs[${O}].serialize(${q.setItem}${j}, opts));
|
|
16
|
+
`;L+=` }
|
|
17
|
+
`;L+=` ${W} = ${q.setArr}${j};`}}else L=`${W} = [...${R}];`;else{const H=`if (typeof ${q.mapEntry}${j}[0] !== 'string') { throw new BakerError(${JSON.stringify(U)} + ': Map field ' + ${JSON.stringify(Q)} + ' has non-string key (' + typeof ${q.mapEntry}${j}[0] + '). Map serialization requires string keys.'); }
|
|
18
|
+
`;if(M.type.resolvedCollectionValue){const O=this.resolveExecutor(M.type.resolvedCollectionValue),B=this.execs.length;this.execs.push(O);const D=this.isAsync?"await ":"";L=`var ${q.mapObj}${j} = Object.create(null);
|
|
19
|
+
`;L+=` for (var ${q.mapEntry}${j} of ${R}) {
|
|
20
|
+
`;L+=` ${H}`;L+=`${q.mapObj}${j}[${q.mapEntry}${j}[0]] = ${q.mapEntry}${j}[1] == null ? ${q.mapEntry}${j}[1] : ${D}execs[${B}].serialize(${q.mapEntry}${j}[1], opts);
|
|
21
|
+
`;L+=` }
|
|
22
|
+
`;L+=` ${W} = ${q.mapObj}${j};`}else{L=`var ${q.mapObj}${j} = Object.create(null);
|
|
23
|
+
`;L+=` for (var ${q.mapEntry}${j} of ${R}) {
|
|
24
|
+
`;L+=` ${H}`;L+=`${q.mapObj}${j}[${q.mapEntry}${j}[0]] = ${q.mapEntry}${j}[1];
|
|
25
|
+
`;L+=` }
|
|
26
|
+
`;L+=` ${W} = ${q.mapObj}${j};`}}L+=this.buildPostNestedTransformCode(W,Q,g);if(I)J=`if (${R} !== undefined && ${R} !== null) {
|
|
27
|
+
${L}
|
|
28
|
+
} else if (${R} === null) {
|
|
29
|
+
${W} = null;
|
|
31
30
|
}
|
|
32
|
-
`;else
|
|
33
|
-
${
|
|
31
|
+
`;else J=`if (${R} != null) {
|
|
32
|
+
${L}
|
|
34
33
|
} else {
|
|
35
|
-
${
|
|
34
|
+
${W} = ${R};
|
|
36
35
|
}
|
|
37
|
-
`;
|
|
38
|
-
`;
|
|
39
|
-
`;if(z
|
|
40
|
-
`;
|
|
41
|
-
`}
|
|
42
|
-
`;return
|
|
43
|
-
`;else{
|
|
44
|
-
`;
|
|
45
|
-
`;
|
|
46
|
-
`}
|
|
47
|
-
`;
|
|
48
|
-
`;
|
|
49
|
-
`;
|
|
50
|
-
`;
|
|
51
|
-
`;
|
|
52
|
-
`;
|
|
53
|
-
`;
|
|
54
|
-
`;
|
|
55
|
-
`;
|
|
56
|
-
`;
|
|
57
|
-
${
|
|
58
|
-
} else if (${
|
|
59
|
-
${
|
|
36
|
+
`;A+=v+J+w;return A}const F=M.type;if(F&&(F.resolvedClass||F.discriminator||F.fn!==void 0&&M.flags.validateNested)){const W=F.isArray||M.flags.validateNestedEach||M.validation.some((H)=>H.each),$=`${q.out}[${JSON.stringify(Z)}]`;let L;if(F.discriminator){const{property:H,subTypes:O}=F.discriminator,B=F.keepDiscriminatorProperty===!0,D=O.map((X,P)=>({sub:X,index:P,depth:u(X.value)})).sort((X,P)=>P.depth-X.depth||X.index-P.index).map((X)=>X.sub),T=(X,P)=>{let z="";for(let S=0;S<D.length;S++){const h=D[S],C=this.resolveExecutor(h.value),N=this.execs.length;this.execs.push(C);const G=this.refs.length;this.refs.push(h.value);z+=`${S===0?"if":"} else if"} (${X} instanceof refs[${G}]) {
|
|
37
|
+
`;z+=` var ${q.serResult}${j} = ${P}execs[${N}].serialize(${X}, opts);
|
|
38
|
+
`;if(B)z+=` ${q.serResult}${j}[${JSON.stringify(H)}] = ${JSON.stringify(h.name)};
|
|
39
|
+
`;z+=` ${q.outItem}${j} = ${q.serResult}${j};
|
|
40
|
+
`}const c=JSON.stringify(JSON.stringify(O.map((S)=>S.name))),y=`(${X} == null ? ${X} : ${X}[${JSON.stringify(H)}])`,k=JSON.stringify(`${U}.${Q}: value matches no discriminator subtype (received discriminator=`);z+=`} else { throw new BakerError(${k} + JSON.stringify(${y}) + ${JSON.stringify(", expected one of ")} + ${c} + ${JSON.stringify(")")}); }
|
|
41
|
+
`;return z};if(W){const X=this.isAsync?"await ":"",P=`__ser_item${j}`;if(this.isAsync)L=`${$} = await Promise.all(${R}.map(async function(${P}) {
|
|
42
|
+
`;else{L=`var ${q.discArr}${j} = [];
|
|
43
|
+
`;L+=` for (var ${q.discIdx}${j}=0; ${q.discIdx}${j}<${R}.length; ${q.discIdx}${j}++) {
|
|
44
|
+
`;L+=` var ${P} = ${R}[${q.discIdx}${j}];
|
|
45
|
+
`}L+=` var ${q.outItem}${j};
|
|
46
|
+
`;L+=T(P,X);if(this.isAsync){L+=` return ${q.outItem}${j};
|
|
47
|
+
`;L+="}));"}else{L+=` ${q.discArr}${j}.push(${q.outItem}${j});
|
|
48
|
+
`;L+=` }
|
|
49
|
+
`;L+=` ${$} = ${q.discArr}${j};`}}else{const X=this.isAsync?"await ":"";L=`var ${q.outItem}${j};
|
|
50
|
+
`;L+=T(R,X);L+=`${$} = ${q.outItem}${j};`}}else{const H=F.resolvedClass,O=this.resolveExecutor(H),B=this.execs.length;this.execs.push(O);if(W)if(this.isAsync)L=`${$} = await Promise.all(${R}.map(async function(__ser_item) { return __ser_item == null ? __ser_item : await execs[${B}].serialize(__ser_item, opts); }));`;else{L=`var ${q.nestedArr}${j} = [];
|
|
51
|
+
`;L+=` for (var ${q.nestedIdx}${j}=0; ${q.nestedIdx}${j}<${R}.length; ${q.nestedIdx}${j}++) {
|
|
52
|
+
`;L+=` var ${q.nestedItem}${j} = ${R}[${q.nestedIdx}${j}];
|
|
53
|
+
`;L+=` ${q.nestedArr}${j}.push(${q.nestedItem}${j} == null ? ${q.nestedItem}${j} : execs[${B}].serialize(${q.nestedItem}${j}, opts));
|
|
54
|
+
`;L+=` }
|
|
55
|
+
`;L+=` ${$} = ${q.nestedArr}${j};`}else{const D=this.isAsync?"await ":"";L=`${$} = ${D}execs[${B}].serialize(${R}, opts);`}}L+=this.buildPostNestedTransformCode($,Q,g);if(I)J=`if (${R} !== undefined && ${R} !== null) {
|
|
56
|
+
${L}
|
|
57
|
+
} else if (${R} === null) {
|
|
58
|
+
${$} = null;
|
|
60
59
|
}
|
|
61
|
-
`;else
|
|
62
|
-
${
|
|
60
|
+
`;else J=`if (${R} != null) {
|
|
61
|
+
${L}
|
|
63
62
|
} else {
|
|
64
|
-
${
|
|
63
|
+
${$} = ${R};
|
|
65
64
|
}
|
|
66
|
-
`}else{const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
`}else
|
|
70
|
-
`}
|
|
65
|
+
`}else{const W=this.buildOutputExpr(Q,Z,R,M);if(I){J+=`if (${R} !== undefined) {
|
|
66
|
+
`;J+=" "+W+`
|
|
67
|
+
`;J+=`}
|
|
68
|
+
`}else J+=W+`
|
|
69
|
+
`}A+=v+J+w;return A}buildTransformExpr(Q,M,U){if(U.length===0)return null;const Y=this.refs;let Z=Q;for(let _=U.length-1;_>=0;_-=1){const j=U[_],R=Y.length;Y.push(j.fn);const A=`refs[${R}]({value:${Z},key:${JSON.stringify(M)},obj:instance})`;Z=j.isAsync?`(await ${A})`:A}return Z}buildPostNestedTransformCode(Q,M,U){const Y=this.buildTransformExpr(Q,M,U);return Y?`
|
|
70
|
+
${Q} = ${Y};`:""}buildOutputExpr(Q,M,U,Y){const Z=`${q.out}[${JSON.stringify(M)}]`,_=Y.transform.filter((j)=>!j.options?.deserializeOnly);if(_.length>0){const j=this.buildTransformExpr(U,Q,_);return`${Z} = ${j};`}return`${Z} = ${U};`}}import{BakerError as K,Direction as b}from"../common/index.js";import{CollectionType as m}from"../metadata/index.js";import{sanitizeKey as E,buildGroupsHasExpr as f,resolveExposeName as p,resolveExposeGroups as V}from"./codegen-utils.js";import{SER_GEN as q}from"./constants.js";function u(Q){let M=0,U=Object.getPrototypeOf(Q);while(typeof U==="function"){M+=1;U=Object.getPrototypeOf(U)}return M}function buildSerializeCode(Q,M,U,Y,Z){return new x(Q,M,U,Y,Z).build()}export{buildSerializeCode};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { RawClassMeta } from '../metadata';
|
|
2
|
+
/**
|
|
3
|
+
* Seal-time normalization of each field's `@Type`/`@Field` type thunk: resolve `type.fn()`, detect
|
|
4
|
+
* Map/Set collections and the `[Element]` array form, exclude primitive constructors, and auto-infer the
|
|
5
|
+
* `validateNested`/`validateNestedEach` flags for DTO classes. Mutates `merged` in place — it reassigns
|
|
6
|
+
* `merged[key]` with a copy-on-write `type` (never mutating the shared RAW `type`) and mutates the
|
|
7
|
+
* already-per-seal-cloned `meta.flags` directly. Stateless — a plain function (no instance needed).
|
|
8
|
+
*/
|
|
9
|
+
export declare function normalizeTypeDefs(merged: RawClassMeta, className: string): void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{BakerError as H}from"../common/index.js";import{PRIMITIVE_CTORS as U}from"./constants.js";import{classifyTypeResult as W}from"./type-resolver.js";export function normalizeTypeDefs(F,G){for(const[z,b]of Object.entries(F)){if(!b.type?.fn)continue;let J;try{J=b.type.fn()}catch(j){throw new H(`${G}.${z}: type function threw: ${j instanceof Error?j.message:String(j)}`,{cause:j})}const{collection:K,isArray:L,resolved:q}=W(J);if(K!==void 0){const j={...b.type,collection:K,isArray:!1};if(b.type.collectionValue){let x;try{x=b.type.collectionValue()}catch(D){throw new H(`${G}.${z}: collectionValue function threw: ${D instanceof Error?D.message:String(D)}`,{cause:D})}if(x!=null&&typeof x==="function"&&!U.has(x))j.resolvedCollectionValue=x}F[z]={...b,type:j};continue}if(q==null||typeof q!=="function")throw new H(`${G}: @Type/@Field type must return a constructor or [constructor], got ${String(q)}`);const Q={...b.type,isArray:L};if(!U.has(q)){Q.resolvedClass=q;if(!b.flags.validateNested)b.flags.validateNested=!0;if(L&&!b.flags.validateNestedEach)b.flags.validateNestedEach=!0}F[z]={...b,type:Q}}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{CollectionType as i}from"../metadata/index.js";export function classifyTypeResult(e){if(e===Map)return{collection:i.Map,isArray:!1,resolved:void 0};if(e===Set)return{collection:i.Set,isArray:!1,resolved:void 0};const r=Array.isArray(e);return{isArray:r,resolved:r?e[0]:e}}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Result, ResultAsync } from '@zipbul/result';
|
|
2
|
+
import type { RuntimeOptions, BakerIssue } from '../common';
|
|
3
|
+
/** Compiled deserialize executor — Result pattern (or its async variant), produced by the builder. */
|
|
4
|
+
export type DeserializeExecutor<T> = (input: unknown, opts?: RuntimeOptions) => Result<T, BakerIssue[]> | ResultAsync<T, BakerIssue[]>;
|
|
5
|
+
/** Compiled validate-only executor — null on success, BakerIssue[] on failure (or its async variant). */
|
|
6
|
+
export type ValidateExecutor = (input: unknown, opts?: RuntimeOptions) => BakerIssue[] | null | Promise<BakerIssue[] | null>;
|
|
File without changes
|