@zipbul/baker 3.3.1 → 3.4.1
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 +33 -0
- package/dist/index.js +1 -10
- package/dist/src/collect.js +1 -26
- package/dist/src/configure.js +1 -43
- package/dist/src/create-rule.js +1 -41
- package/dist/src/decorators/field.js +1 -277
- package/dist/src/decorators/index.js +1 -2
- package/dist/src/decorators/recipe.js +1 -23
- package/dist/src/errors.js +1 -52
- package/dist/src/functions/check-call-options.js +1 -51
- package/dist/src/functions/deserialize.js +1 -57
- package/dist/src/functions/serialize.js +1 -52
- package/dist/src/functions/validate.js +1 -49
- package/dist/src/interfaces.js +0 -4
- package/dist/src/meta-access.js +1 -75
- package/dist/src/registry.js +1 -8
- package/dist/src/rule-metadata.js +1 -17
- package/dist/src/rule-plan.js +1 -117
- package/dist/src/rules/array.js +1 -96
- package/dist/src/rules/binary.d.ts +3 -0
- package/dist/src/rules/binary.js +3 -0
- package/dist/src/rules/combinators.js +1 -111
- package/dist/src/rules/common.js +1 -77
- package/dist/src/rules/date.js +1 -35
- package/dist/src/rules/index.d.ts +1 -0
- package/dist/src/rules/index.js +1 -9
- package/dist/src/rules/locales.js +1 -249
- package/dist/src/rules/number.js +1 -79
- package/dist/src/rules/object.js +1 -49
- package/dist/src/rules/string.js +10 -2033
- package/dist/src/rules/typechecker.js +5 -171
- package/dist/src/seal/circular-analyzer.js +1 -63
- package/dist/src/seal/codegen-utils.js +1 -18
- package/dist/src/seal/deserialize-builder.js +265 -1564
- package/dist/src/seal/expose-validator.js +1 -65
- package/dist/src/seal/seal-state.js +1 -18
- package/dist/src/seal/seal.js +1 -431
- package/dist/src/seal/serialize-builder.js +66 -370
- package/dist/src/seal/validate-meta.js +1 -61
- package/dist/src/symbols.js +1 -13
- package/dist/src/transformers/collection.transformer.js +1 -25
- package/dist/src/transformers/date.transformer.js +1 -18
- package/dist/src/transformers/index.js +1 -6
- package/dist/src/transformers/luxon.transformer.js +1 -34
- package/dist/src/transformers/moment.transformer.js +1 -32
- package/dist/src/transformers/number.transformer.js +1 -8
- package/dist/src/transformers/string.transformer.js +1 -12
- package/dist/src/types.js +0 -1
- package/dist/src/utils.js +1 -10
- package/package.json +2 -2
|
@@ -1,374 +1,70 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const serDef = exposeStack.find(e => e.serializeOnly && e.name);
|
|
32
|
-
if (serDef) {
|
|
33
|
-
return serDef.name;
|
|
34
|
-
}
|
|
35
|
-
// Non-directional @Expose with name → use for both directions
|
|
36
|
-
const biDef = exposeStack.find(e => !e.deserializeOnly && !e.serializeOnly && e.name);
|
|
37
|
-
if (biDef) {
|
|
38
|
-
return biDef.name;
|
|
39
|
-
}
|
|
40
|
-
return fieldKey;
|
|
1
|
+
import{BakerError as c}from"../errors.js";import{getSealed as A}from"../meta-access.js";import{sanitizeKey as p,buildGroupsHasExpr as E}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 m(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
|
+
${U} = ${R};`:""}function buildSerializeCode(U,q,L,M){const R=[],Y=[];let W=`'use strict';
|
|
3
|
+
`;W+=`var ${j.out} = {};
|
|
4
|
+
`;if(Object.values(q).some((_)=>{const v=y(_.expose);return v&&v.length>0})){W+=`var ${j.groups} = opts && opts.groups;
|
|
5
|
+
`;W+=`var ${j.group0} = ${j.groups} && ${j.groups}.length === 1 ? ${j.groups}[0] : null;
|
|
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+=u(_,v,R,Y,M,L,U.name);W+=`return ${j.out};
|
|
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,c)}function u(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
|
+
`}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=m(U,q.expose),F=y(q.expose),z=p(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}) && (${E(j.group0,j.groupsSet,F)})) {
|
|
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($==="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
|
+
`;J+=` for (var ${j.setItem} of ${Q}) {
|
|
16
|
+
`;J+=` ${j.setArr}.push(${j.setItem} == null ? ${j.setItem} : execs[${D}].serialize(${j.setItem}, opts));
|
|
17
|
+
`;J+=` }
|
|
18
|
+
`;J+=` ${X} = ${j.setArr};`}}else J=`${X} = [...${Q}];`;else{const B=`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 D=A(q.type.resolvedCollectionValue),O=M.length;M.push(D);const w=R?"await ":"";J=`var ${j.mapObj} = Object.create(null);
|
|
20
|
+
`;J+=` for (var ${j.mapEntry} of ${Q}) {
|
|
21
|
+
`;J+=` ${B}`;J+=`${j.mapObj}[${j.mapEntry}[0]] = ${j.mapEntry}[1] == null ? ${j.mapEntry}[1] : ${w}execs[${O}].serialize(${j.mapEntry}[1], opts);
|
|
22
|
+
`;J+=` }
|
|
23
|
+
`;J+=` ${X} = ${j.mapObj};`}else{J=`var ${j.mapObj} = Object.create(null);
|
|
24
|
+
`;J+=` for (var ${j.mapEntry} of ${Q}) {
|
|
25
|
+
`;J+=` ${B}`;J+=`${j.mapObj}[${j.mapEntry}[0]] = ${j.mapEntry}[1];
|
|
26
|
+
`;J+=` }
|
|
27
|
+
`;J+=` ${X} = ${j.mapObj};`}}J+=T(X,U,g,L);if(b)H=`if (${Q} !== undefined && ${Q} !== null) {
|
|
28
|
+
${J}
|
|
29
|
+
} else if (${Q} === null) {
|
|
30
|
+
${X} = null;
|
|
41
31
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
for (const e of exposeStack) {
|
|
47
|
-
if (e.deserializeOnly) {
|
|
48
|
-
continue;
|
|
49
|
-
}
|
|
50
|
-
if (!e.groups || e.groups.length === 0) {
|
|
51
|
-
return undefined;
|
|
52
|
-
}
|
|
53
|
-
if (all === null) {
|
|
54
|
-
all = new Set();
|
|
55
|
-
}
|
|
56
|
-
for (const g of e.groups) {
|
|
57
|
-
all.add(g);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
return all === null ? undefined : [...all];
|
|
32
|
+
`;else H=`if (${Q} != null) {
|
|
33
|
+
${J}
|
|
34
|
+
} else {
|
|
35
|
+
${X} = ${Q};
|
|
61
36
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const expr1 = td1.isAsync ? `(await ${call1})` : call1;
|
|
86
|
-
const call0 = `refs[${refIdx0}]({value:${expr1},key:${JSON.stringify(fieldKey)},obj:instance})`;
|
|
87
|
-
return td0.isAsync ? `(await ${call0})` : call0;
|
|
88
|
-
}
|
|
89
|
-
// Walk serTransforms backwards in place — avoids [...arr].reverse() clone allocation
|
|
90
|
-
let valueExpr = inputExpr;
|
|
91
|
-
for (let k = serTransforms.length - 1; k >= 0; k -= 1) {
|
|
92
|
-
const td = serTransforms[k];
|
|
93
|
-
const refIdx = refs.length;
|
|
94
|
-
refs.push(td.fn);
|
|
95
|
-
const callExpr = `refs[${refIdx}]({value:${valueExpr},key:${JSON.stringify(fieldKey)},obj:instance})`;
|
|
96
|
-
valueExpr = td.isAsync ? `(await ${callExpr})` : callExpr;
|
|
97
|
-
}
|
|
98
|
-
return valueExpr;
|
|
37
|
+
`;_+=v+H+S;return _}if(q.type?.resolvedClass||q.type?.discriminator||q.type?.fn&&q.flags.validateNested){const X=q.type?.isArray||q.flags.validateNestedEach||q.validation.some((B)=>B.each),$=`${j.out}[${JSON.stringify(Z)}]`;let J;if(q.type.discriminator){const{property:B,subTypes:D}=q.type.discriminator,O=q.type.keepDiscriminatorProperty!==!1,w=[...D].sort((P,I)=>{if(P.value.prototype instanceof I.value)return-1;if(I.value.prototype instanceof P.value)return 1;return 0}),K=(P,I)=>{let h="";for(let k=0;k<w.length;k++){const V=w[k],N=A(V.value),C=M.length;M.push(N);const G=L.length;L.push(V.value);h+=`${k===0?"if":"} else if"} (${P} instanceof refs[${G}]) {
|
|
38
|
+
`;h+=` var ${j.serResult} = ${I}execs[${C}].serialize(${P}, opts);
|
|
39
|
+
`;if(O)h+=` ${j.serResult}[${JSON.stringify(B)}] = ${JSON.stringify(V.name)};
|
|
40
|
+
`;h+=` ${j.outItem} = ${j.serResult};
|
|
41
|
+
`}h+=`} else { ${j.outItem} = `+P+`; }
|
|
42
|
+
`;return h};if(X){const P=R?"await ":"";if(R)J=`${$} = await Promise.all(${Q}.map(async function(__ser_item) {
|
|
43
|
+
`;else{J=`var ${j.discArr} = [];
|
|
44
|
+
`;J+=` for (var ${j.discIdx}=0; ${j.discIdx}<${Q}.length; ${j.discIdx}++) {
|
|
45
|
+
`;J+=` var __ser_item = ${Q}[${j.discIdx}];
|
|
46
|
+
`}J+=` var ${j.outItem};
|
|
47
|
+
`;J+=K("__ser_item",P);if(R){J+=` return ${j.outItem};
|
|
48
|
+
`;J+="}));"}else{J+=` ${j.discArr}.push(${j.outItem});
|
|
49
|
+
`;J+=` }
|
|
50
|
+
`;J+=` ${$} = ${j.discArr};`}}else{const P=R?"await ":"";J=`var ${j.outItem};
|
|
51
|
+
`;J+=K(Q,P);J+=`${$} = ${j.outItem};`}}else{const B=q.type.resolvedClass??q.type.fn(),D=A(B),O=M.length;M.push(D);if(X)if(R)J=`${$} = await Promise.all(${Q}.map(async function(__ser_item) { return __ser_item == null ? __ser_item : await execs[${O}].serialize(__ser_item, opts); }));`;else{J=`var ${j.nestedArr} = [];
|
|
52
|
+
`;J+=` for (var ${j.nestedIdx}=0; ${j.nestedIdx}<${Q}.length; ${j.nestedIdx}++) {
|
|
53
|
+
`;J+=` var ${j.nestedItem} = ${Q}[${j.nestedIdx}];
|
|
54
|
+
`;J+=` ${j.nestedArr}.push(${j.nestedItem} == null ? ${j.nestedItem} : execs[${O}].serialize(${j.nestedItem}, opts));
|
|
55
|
+
`;J+=` }
|
|
56
|
+
`;J+=` ${$} = ${j.nestedArr};`}else J=`${$} = ${R?"await ":""}execs[${O}].serialize(${Q}, opts);`}J+=T($,U,g,L);if(b)H=`if (${Q} !== undefined && ${Q} !== null) {
|
|
57
|
+
${J}
|
|
58
|
+
} else if (${Q} === null) {
|
|
59
|
+
${$} = null;
|
|
99
60
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
function buildPostNestedTransformCode(outputTarget, fieldKey, serTransforms, refs) {
|
|
105
|
-
const transformed = buildSerializeTransformExpr(outputTarget, fieldKey, serTransforms, refs);
|
|
106
|
-
return transformed ? `\n${outputTarget} = ${transformed};` : '';
|
|
61
|
+
`;else H=`if (${Q} != null) {
|
|
62
|
+
${J}
|
|
63
|
+
} else {
|
|
64
|
+
${$} = ${Q};
|
|
107
65
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
* Assumes no validation — always returns Record<string, unknown> (§4.3).
|
|
114
|
-
*/
|
|
115
|
-
function buildSerializeCode(Class, merged, options, isAsync) {
|
|
116
|
-
const refs = [];
|
|
117
|
-
const execs = [];
|
|
118
|
-
// ── Code generation ────────────────────────────────────────────────────────
|
|
119
|
-
let body = "'use strict';\n";
|
|
120
|
-
body += `var ${GEN.out} = {};\n`;
|
|
121
|
-
// Groups variable — only when fields referencing groups exist
|
|
122
|
-
const hasGroupsField = Object.values(merged).some(meta => {
|
|
123
|
-
const groups = getSerializeExposeGroups(meta.expose);
|
|
124
|
-
return groups && groups.length > 0;
|
|
125
|
-
});
|
|
126
|
-
if (hasGroupsField) {
|
|
127
|
-
body += `var ${GEN.groups} = opts && opts.groups;\n`;
|
|
128
|
-
body += `var ${GEN.group0} = ${GEN.groups} && ${GEN.groups}.length === 1 ? ${GEN.groups}[0] : null;\n`;
|
|
129
|
-
body += `var ${GEN.groupsSet} = ${GEN.groups} && ${GEN.groups}.length > 1 ? new Set(${GEN.groups}) : null;\n`;
|
|
130
|
-
}
|
|
131
|
-
for (const [fieldKey, meta] of Object.entries(merged)) {
|
|
132
|
-
body += generateSerializeFieldCode(fieldKey, meta, refs, execs, isAsync, options, Class.name);
|
|
133
|
-
}
|
|
134
|
-
body += `return ${GEN.out};\n`;
|
|
135
|
-
// sourceURL (§4.9)
|
|
136
|
-
// Sanitize class name so it cannot inject newlines / */ that would break out of the comment.
|
|
137
|
-
const safeClsName = Class.name.replace(/[^\w$.-]/g, '_');
|
|
138
|
-
body += `//# sourceURL=baker://${safeClsName}/serialize\n`;
|
|
139
|
-
// ── Execute new Function ───────────────────────────────────────────────────
|
|
140
|
-
const fnKeyword = isAsync ? 'async function' : 'function';
|
|
141
|
-
const executor = new Function('refs', 'execs', 'BakerError', `return ${fnKeyword}(instance, opts) { ` + body + ' }')(refs, execs, BakerError);
|
|
142
|
-
return executor;
|
|
143
|
-
}
|
|
144
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
145
|
-
// Per-field serialize code generation
|
|
146
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
147
|
-
function generateSerializeFieldCode(fieldKey, meta, refs, execs, isAsync, options, className = '') {
|
|
148
|
-
// ⓪ Exclude serializeOnly / bidirectional → skip
|
|
149
|
-
if (meta.exclude) {
|
|
150
|
-
if (!meta.exclude.deserializeOnly) {
|
|
151
|
-
if (options?.debug) {
|
|
152
|
-
const reason = meta.exclude.serializeOnly ? 'serializeOnly' : 'bidirectional';
|
|
153
|
-
return `// [baker] field ${JSON.stringify(fieldKey)} excluded (${reason} @Exclude)\n`;
|
|
154
|
-
}
|
|
155
|
-
return '';
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
// Expose: if all @Expose entries are deserializeOnly, skip for serialize
|
|
159
|
-
if (meta.expose.length > 0 && meta.expose.every(e => e.deserializeOnly)) {
|
|
160
|
-
if (options?.debug) {
|
|
161
|
-
return `// [baker] field ${JSON.stringify(fieldKey)} excluded (all @Expose entries are deserializeOnly)\n`;
|
|
162
|
-
}
|
|
163
|
-
return '';
|
|
164
|
-
}
|
|
165
|
-
const outputKey = getSerializeOutputKey(fieldKey, meta.expose);
|
|
166
|
-
const exposeGroups = getSerializeExposeGroups(meta.expose);
|
|
167
|
-
const sk = sanitizeKey(fieldKey);
|
|
168
|
-
const fieldVal = `${GEN.fieldVal}${sk}`;
|
|
169
|
-
let fieldCode = '';
|
|
170
|
-
fieldCode += `var ${fieldVal} = instance[${JSON.stringify(fieldKey)}];\n`;
|
|
171
|
-
// groups check wrap (§4.5)
|
|
172
|
-
let fieldStart = '';
|
|
173
|
-
let fieldEnd = '';
|
|
174
|
-
if (exposeGroups && exposeGroups.length > 0) {
|
|
175
|
-
fieldStart = `if ((${GEN.group0} !== null || ${GEN.groupsSet}) && (${buildGroupsHasExpr(GEN.group0, GEN.groupsSet, exposeGroups)})) {\n`;
|
|
176
|
-
fieldEnd = '}\n';
|
|
177
|
-
}
|
|
178
|
-
let innerCode = '';
|
|
179
|
-
// ② @IsOptional → skip output if undefined (§4.3 serialize step 2)
|
|
180
|
-
const useOptionalGuard = meta.flags.isOptional;
|
|
181
|
-
// Collect serialize-direction transforms once
|
|
182
|
-
const serTransforms = meta.transform.filter(td => !td.options?.deserializeOnly);
|
|
183
|
-
// ③a Collection (Map/Set) serialize — Set → Array, Map → plain object
|
|
184
|
-
if (meta.type?.collection) {
|
|
185
|
-
const outputTarget = `${GEN.out}[${JSON.stringify(outputKey)}]`;
|
|
186
|
-
const collection = meta.type.collection;
|
|
187
|
-
let nestedCode;
|
|
188
|
-
if (collection === 'Set') {
|
|
189
|
-
if (meta.type.resolvedCollectionValue) {
|
|
190
|
-
const nestedSealed = getSealed(meta.type.resolvedCollectionValue);
|
|
191
|
-
const execIdx = execs.length;
|
|
192
|
-
execs.push(nestedSealed);
|
|
193
|
-
if (isAsync) {
|
|
194
|
-
nestedCode = `{ var __ser_ps = []; for (var __ser_item of ${fieldVal}) { __ser_ps.push(__ser_item == null ? __ser_item : execs[${execIdx}].serialize(__ser_item, opts)); } ${outputTarget} = await Promise.all(__ser_ps); }`;
|
|
195
|
-
}
|
|
196
|
-
else {
|
|
197
|
-
nestedCode = `var ${GEN.setArr} = [];\n`;
|
|
198
|
-
nestedCode += ` for (var ${GEN.setItem} of ${fieldVal}) {\n`;
|
|
199
|
-
nestedCode += ` ${GEN.setArr}.push(${GEN.setItem} == null ? ${GEN.setItem} : execs[${execIdx}].serialize(${GEN.setItem}, opts));\n`;
|
|
200
|
-
nestedCode += ` }\n`;
|
|
201
|
-
nestedCode += ` ${outputTarget} = ${GEN.setArr};`;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
else {
|
|
205
|
-
nestedCode = `${outputTarget} = [...${fieldVal}];`;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
else {
|
|
209
|
-
// Map → plain object (W8: keys must be strings — throw otherwise)
|
|
210
|
-
const keyCheck = `if (typeof ${GEN.mapEntry}[0] !== 'string') { throw new BakerError(${JSON.stringify(className)} + ': Map field ' + ${JSON.stringify(fieldKey)} + ' has non-string key (' + typeof ${GEN.mapEntry}[0] + '). Map serialization requires string keys.'); }\n `;
|
|
211
|
-
if (meta.type.resolvedCollectionValue) {
|
|
212
|
-
const nestedSealed = getSealed(meta.type.resolvedCollectionValue);
|
|
213
|
-
const execIdx = execs.length;
|
|
214
|
-
execs.push(nestedSealed);
|
|
215
|
-
const awaitKw = isAsync ? 'await ' : '';
|
|
216
|
-
nestedCode = `var ${GEN.mapObj} = Object.create(null);\n`;
|
|
217
|
-
nestedCode += ` for (var ${GEN.mapEntry} of ${fieldVal}) {\n`;
|
|
218
|
-
nestedCode += ` ${keyCheck}`;
|
|
219
|
-
nestedCode += `${GEN.mapObj}[${GEN.mapEntry}[0]] = ${GEN.mapEntry}[1] == null ? ${GEN.mapEntry}[1] : ${awaitKw}execs[${execIdx}].serialize(${GEN.mapEntry}[1], opts);\n`;
|
|
220
|
-
nestedCode += ` }\n`;
|
|
221
|
-
nestedCode += ` ${outputTarget} = ${GEN.mapObj};`;
|
|
222
|
-
}
|
|
223
|
-
else {
|
|
224
|
-
nestedCode = `var ${GEN.mapObj} = Object.create(null);\n`;
|
|
225
|
-
nestedCode += ` for (var ${GEN.mapEntry} of ${fieldVal}) {\n`;
|
|
226
|
-
nestedCode += ` ${keyCheck}`;
|
|
227
|
-
nestedCode += `${GEN.mapObj}[${GEN.mapEntry}[0]] = ${GEN.mapEntry}[1];\n`;
|
|
228
|
-
nestedCode += ` }\n`;
|
|
229
|
-
nestedCode += ` ${outputTarget} = ${GEN.mapObj};`;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
// Apply serialize transforms after collection serialize (nested → transform)
|
|
233
|
-
nestedCode += buildPostNestedTransformCode(outputTarget, fieldKey, serTransforms, refs);
|
|
234
|
-
if (useOptionalGuard) {
|
|
235
|
-
innerCode = `if (${fieldVal} !== undefined && ${fieldVal} !== null) {\n ${nestedCode}\n} else if (${fieldVal} === null) {\n ${outputTarget} = null;\n}\n`;
|
|
236
|
-
}
|
|
237
|
-
else {
|
|
238
|
-
innerCode = `if (${fieldVal} != null) {\n ${nestedCode}\n} else {\n ${outputTarget} = ${fieldVal};\n}\n`;
|
|
239
|
-
}
|
|
240
|
-
fieldCode += fieldStart + innerCode + fieldEnd;
|
|
241
|
-
return fieldCode;
|
|
242
|
-
}
|
|
243
|
-
// ③b nested @Type handling (H4) — supports type + transform combination (nested serialize → transform)
|
|
244
|
-
if (meta.type?.resolvedClass || meta.type?.discriminator || (meta.type?.fn && meta.flags.validateNested)) {
|
|
245
|
-
// Determine array/each mode
|
|
246
|
-
const hasEach = meta.type?.isArray || meta.flags.validateNestedEach || meta.validation.some(rd => rd.each);
|
|
247
|
-
const outputTarget = `${GEN.out}[${JSON.stringify(outputKey)}]`;
|
|
248
|
-
let nestedCode;
|
|
249
|
-
if (meta.type.discriminator) {
|
|
250
|
-
// §C-8 discriminator serialize — instanceof dispatch
|
|
251
|
-
const { property, subTypes } = meta.type.discriminator;
|
|
252
|
-
const keepDisc = meta.type.keepDiscriminatorProperty !== false; // default true for round-trip
|
|
253
|
-
// Sort most-specific-first (subclasses take priority in inheritance relationships)
|
|
254
|
-
const sorted = [...subTypes].sort((a, b) => {
|
|
255
|
-
if (a.value.prototype instanceof b.value) {
|
|
256
|
-
return -1;
|
|
257
|
-
}
|
|
258
|
-
if (b.value.prototype instanceof a.value) {
|
|
259
|
-
return 1;
|
|
260
|
-
}
|
|
261
|
-
return 0;
|
|
262
|
-
});
|
|
263
|
-
// Helper for generating instanceof branch code
|
|
264
|
-
const buildInstanceofChain = (itemVar, awaitKw) => {
|
|
265
|
-
let code = '';
|
|
266
|
-
for (let i = 0; i < sorted.length; i++) {
|
|
267
|
-
const sub = sorted[i];
|
|
268
|
-
const nestedSealed = getSealed(sub.value);
|
|
269
|
-
const execIdx = execs.length;
|
|
270
|
-
execs.push(nestedSealed);
|
|
271
|
-
const refIdx = refs.length;
|
|
272
|
-
refs.push(sub.value);
|
|
273
|
-
const prefix = i === 0 ? 'if' : '} else if';
|
|
274
|
-
code += `${prefix} (${itemVar} instanceof refs[${refIdx}]) {\n`;
|
|
275
|
-
code += ` var ${GEN.serResult} = ${awaitKw}execs[${execIdx}].serialize(${itemVar}, opts);\n`;
|
|
276
|
-
if (keepDisc) {
|
|
277
|
-
code += ` ${GEN.serResult}[${JSON.stringify(property)}] = ${JSON.stringify(sub.name)};\n`;
|
|
278
|
-
}
|
|
279
|
-
code += ` ${GEN.outItem} = ${GEN.serResult};\n`;
|
|
280
|
-
}
|
|
281
|
-
code += `} else { ${GEN.outItem} = ` + itemVar + '; }\n';
|
|
282
|
-
return code;
|
|
283
|
-
};
|
|
284
|
-
if (hasEach) {
|
|
285
|
-
const awaitKw = isAsync ? 'await ' : '';
|
|
286
|
-
if (isAsync) {
|
|
287
|
-
nestedCode = `${outputTarget} = await Promise.all(${fieldVal}.map(async function(__ser_item) {\n`;
|
|
288
|
-
}
|
|
289
|
-
else {
|
|
290
|
-
nestedCode = `var ${GEN.discArr} = [];\n`;
|
|
291
|
-
nestedCode += ` for (var ${GEN.discIdx}=0; ${GEN.discIdx}<${fieldVal}.length; ${GEN.discIdx}++) {\n`;
|
|
292
|
-
nestedCode += ` var __ser_item = ${fieldVal}[${GEN.discIdx}];\n`;
|
|
293
|
-
}
|
|
294
|
-
nestedCode += ` var ${GEN.outItem};\n`;
|
|
295
|
-
nestedCode += buildInstanceofChain('__ser_item', awaitKw);
|
|
296
|
-
if (isAsync) {
|
|
297
|
-
nestedCode += ` return ${GEN.outItem};\n`;
|
|
298
|
-
nestedCode += `}));`;
|
|
299
|
-
}
|
|
300
|
-
else {
|
|
301
|
-
nestedCode += ` ${GEN.discArr}.push(${GEN.outItem});\n`;
|
|
302
|
-
nestedCode += ` }\n`;
|
|
303
|
-
nestedCode += ` ${outputTarget} = ${GEN.discArr};`;
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
else {
|
|
307
|
-
const awaitKw = isAsync ? 'await ' : '';
|
|
308
|
-
nestedCode = `var ${GEN.outItem};\n`;
|
|
309
|
-
nestedCode += buildInstanceofChain(fieldVal, awaitKw);
|
|
310
|
-
nestedCode += `${outputTarget} = ${GEN.outItem};`;
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
else {
|
|
314
|
-
// Existing simple nested logic
|
|
315
|
-
const nestedCls = meta.type.resolvedClass ?? meta.type.fn();
|
|
316
|
-
const nestedSealed = getSealed(nestedCls);
|
|
317
|
-
const execIdx = execs.length;
|
|
318
|
-
execs.push(nestedSealed);
|
|
319
|
-
if (hasEach) {
|
|
320
|
-
if (isAsync) {
|
|
321
|
-
nestedCode = `${outputTarget} = await Promise.all(${fieldVal}.map(async function(__ser_item) { return __ser_item == null ? __ser_item : await execs[${execIdx}].serialize(__ser_item, opts); }));`;
|
|
322
|
-
}
|
|
323
|
-
else {
|
|
324
|
-
nestedCode = `var ${GEN.nestedArr} = [];\n`;
|
|
325
|
-
nestedCode += ` for (var ${GEN.nestedIdx}=0; ${GEN.nestedIdx}<${fieldVal}.length; ${GEN.nestedIdx}++) {\n`;
|
|
326
|
-
nestedCode += ` var ${GEN.nestedItem} = ${fieldVal}[${GEN.nestedIdx}];\n`;
|
|
327
|
-
nestedCode += ` ${GEN.nestedArr}.push(${GEN.nestedItem} == null ? ${GEN.nestedItem} : execs[${execIdx}].serialize(${GEN.nestedItem}, opts));\n`;
|
|
328
|
-
nestedCode += ` }\n`;
|
|
329
|
-
nestedCode += ` ${outputTarget} = ${GEN.nestedArr};`;
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
else {
|
|
333
|
-
const awaitKw = isAsync ? 'await ' : '';
|
|
334
|
-
nestedCode = `${outputTarget} = ${awaitKw}execs[${execIdx}].serialize(${fieldVal}, opts);`;
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
// Apply serialize transforms after nested serialize (nested serialize → transform)
|
|
338
|
-
nestedCode += buildPostNestedTransformCode(outputTarget, fieldKey, serTransforms, refs);
|
|
339
|
-
if (useOptionalGuard) {
|
|
340
|
-
innerCode = `if (${fieldVal} !== undefined && ${fieldVal} !== null) {\n ${nestedCode}\n} else if (${fieldVal} === null) {\n ${outputTarget} = null;\n}\n`;
|
|
341
|
-
}
|
|
342
|
-
else {
|
|
343
|
-
innerCode = `if (${fieldVal} != null) {\n ${nestedCode}\n} else {\n ${outputTarget} = ${fieldVal};\n}\n`;
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
else {
|
|
347
|
-
// Existing @Transform or direct assign handling
|
|
348
|
-
const outputExpr = buildSerializeOutputExpr(fieldKey, outputKey, fieldVal, meta, refs);
|
|
349
|
-
if (useOptionalGuard) {
|
|
350
|
-
innerCode += `if (${fieldVal} !== undefined) {\n`;
|
|
351
|
-
innerCode += ' ' + outputExpr + '\n';
|
|
352
|
-
innerCode += '}\n';
|
|
353
|
-
}
|
|
354
|
-
else {
|
|
355
|
-
innerCode += outputExpr + '\n';
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
fieldCode += fieldStart + innerCode + fieldEnd;
|
|
359
|
-
return fieldCode;
|
|
360
|
-
}
|
|
361
|
-
/**
|
|
362
|
-
* Build field output expression.
|
|
363
|
-
* If @Transform exists, call refs[i](params); otherwise, direct assignment.
|
|
364
|
-
*/
|
|
365
|
-
function buildSerializeOutputExpr(fieldKey, outputKey, fieldValueExpr, meta, refs) {
|
|
366
|
-
const outputTarget = `${GEN.out}[${JSON.stringify(outputKey)}]`;
|
|
367
|
-
const serTransforms = meta.transform.filter(td => !td.options?.deserializeOnly);
|
|
368
|
-
if (serTransforms.length > 0) {
|
|
369
|
-
const transformed = buildSerializeTransformExpr(fieldValueExpr, fieldKey, serTransforms, refs);
|
|
370
|
-
return `${outputTarget} = ${transformed};`;
|
|
371
|
-
}
|
|
372
|
-
return `${outputTarget} = ${fieldValueExpr};`;
|
|
373
|
-
}
|
|
374
|
-
export { buildSerializeCode };
|
|
66
|
+
`}else{const X=f(U,Z,Q,q,L);if(b){H+=`if (${Q} !== undefined) {
|
|
67
|
+
`;H+=" "+X+`
|
|
68
|
+
`;H+=`}
|
|
69
|
+
`}else H+=X+`
|
|
70
|
+
`}_+=v+H+S;return _}function f(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,61 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { hasRawOwn } from '../meta-access.js';
|
|
3
|
-
/**
|
|
4
|
-
* @internal — seal-time invariant checks invoked from sealOne after merge + type normalization,
|
|
5
|
-
* before codegen. Throws BakerError on the first violation.
|
|
6
|
-
*
|
|
7
|
-
* Covers W2 (D7 + D9):
|
|
8
|
-
* - Discriminator shape: empty subTypes / invalid subType entry / name collision / missing property
|
|
9
|
-
* - Set/Map pairing: Set without setValue, Map without mapValue, setValue/mapValue target missing @Field metadata
|
|
10
|
-
* - async-in-sync: a DTO that mixes async rules/transforms with sync rules/transforms in such a way
|
|
11
|
-
* that the caller cannot easily tell — this throws BakerError so the user makes the intent explicit.
|
|
12
|
-
* (Per W2 decision: throw, not warn.)
|
|
13
|
-
*/
|
|
14
|
-
export function validateMeta(Class, merged) {
|
|
15
|
-
const className = Class.name;
|
|
16
|
-
for (const [key, meta] of Object.entries(merged)) {
|
|
17
|
-
// ─── Discriminator shape ─────────────────────────────────────────────
|
|
18
|
-
if (meta.type?.discriminator) {
|
|
19
|
-
const disc = meta.type.discriminator;
|
|
20
|
-
if (typeof disc.property !== 'string' || disc.property.length === 0) {
|
|
21
|
-
throw new BakerError(`${className}.${key}: discriminator.property must be a non-empty string.`);
|
|
22
|
-
}
|
|
23
|
-
if (!Array.isArray(disc.subTypes) || disc.subTypes.length === 0) {
|
|
24
|
-
throw new BakerError(`${className}.${key}: discriminator.subTypes must be a non-empty array of { value, name } entries.`);
|
|
25
|
-
}
|
|
26
|
-
const seenNames = new Set();
|
|
27
|
-
for (let i = 0; i < disc.subTypes.length; i++) {
|
|
28
|
-
const sub = disc.subTypes[i];
|
|
29
|
-
if (typeof sub.name !== 'string' || sub.name.length === 0) {
|
|
30
|
-
throw new BakerError(`${className}.${key}: discriminator.subTypes[${i}].name must be a non-empty string.`);
|
|
31
|
-
}
|
|
32
|
-
if (typeof sub.value !== 'function') {
|
|
33
|
-
throw new BakerError(`${className}.${key}: discriminator.subTypes[${i}].value must be a class constructor (got ${typeof sub.value}).`);
|
|
34
|
-
}
|
|
35
|
-
if (seenNames.has(sub.name)) {
|
|
36
|
-
throw new BakerError(`${className}.${key}: discriminator.subTypes has duplicate name '${sub.name}'. Each subType must have a unique name.`);
|
|
37
|
-
}
|
|
38
|
-
seenNames.add(sub.name);
|
|
39
|
-
// subType class must have @Field metadata (RAW) — otherwise codegen will fail with a less clear error
|
|
40
|
-
if (!hasRawOwn(sub.value)) {
|
|
41
|
-
throw new BakerError(`${className}.${key}: discriminator.subTypes[${i}].value (${sub.value.name}) has no @Field decorators.`);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
// ─── Set/Map collection pairing — unified single-pass check ──────────
|
|
46
|
-
const collection = meta.type?.collection;
|
|
47
|
-
if (collection !== undefined && meta.type?.resolvedCollectionValue) {
|
|
48
|
-
const target = meta.type.resolvedCollectionValue;
|
|
49
|
-
if (!hasRawOwn(target)) {
|
|
50
|
-
const accessor = collection === 'Set' ? 'setValue' : 'mapValue';
|
|
51
|
-
throw new BakerError(`${className}.${key}: ${accessor} target (${target.name}) has no @Field decorators.`);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
// ─── async-in-sync: D9 ────────────────────────────────────────────────
|
|
56
|
-
// Seal-time strict check for "mixed sync/async rules" was attempted but produces too many
|
|
57
|
-
// false positives — sync rules + async transform is a common, valid baker pattern. The
|
|
58
|
-
// remediation for D9 lives in W14's strict API: `validateSync(AsyncDto, x)` and the other
|
|
59
|
-
// `*Sync` variants throw BakerError at the call site after consulting `isAsync`/`isSerializeAsync`.
|
|
60
|
-
// No seal-time invariant added here.
|
|
61
|
-
}
|
|
1
|
+
import{BakerError as x}from"../errors.js";import{hasRawOwn as G}from"../meta-access.js";export function validateMeta(H,I){const p=H.name;for(const[q,A]of Object.entries(I)){if(A.type?.discriminator){const f=A.type.discriminator;if(typeof f.property!=="string"||f.property.length===0)throw new x(`${p}.${q}: discriminator.property must be a non-empty string.`);if(!Array.isArray(f.subTypes)||f.subTypes.length===0)throw new x(`${p}.${q}: discriminator.subTypes must be a non-empty array of { value, name } entries.`);const D=new Set;for(let z=0;z<f.subTypes.length;z++){const j=f.subTypes[z];if(typeof j.name!=="string"||j.name.length===0)throw new x(`${p}.${q}: discriminator.subTypes[${z}].name must be a non-empty string.`);if(typeof j.value!=="function")throw new x(`${p}.${q}: discriminator.subTypes[${z}].value must be a class constructor (got ${typeof j.value}).`);if(D.has(j.name))throw new x(`${p}.${q}: discriminator.subTypes has duplicate name '${j.name}'. Each subType must have a unique name.`);D.add(j.name);if(!G(j.value))throw new x(`${p}.${q}: discriminator.subTypes[${z}].value (${j.value.name}) has no @Field decorators.`)}}const F=A.type?.collection;if(F!==void 0&&A.type?.resolvedCollectionValue){const f=A.type.resolvedCollectionValue;if(!G(f))throw new x(`${p}.${q}: ${F==="Set"?"setValue":"mapValue"} target (${f.name}) has no @Field decorators.`)}}}
|
package/dist/src/symbols.js
CHANGED
|
@@ -1,13 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* 2 Symbols — 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
|
-
*/
|
|
5
|
-
// TC39 decorator metadata polyfill. Bun 1.3.13 does not yet expose Symbol.metadata natively,
|
|
6
|
-
// so this defines it; the `??=` guard yields to a native value if a future runtime provides one.
|
|
7
|
-
// Must run before any decorated class is evaluated — symbols.ts sits at the root of the
|
|
8
|
-
// metadata import graph, so it always does.
|
|
9
|
-
Symbol.metadata ??= Symbol.for('Symbol.metadata');
|
|
10
|
-
/** Tier 1 collection metadata (stored on Class[Symbol.metadata] by decorators) */
|
|
11
|
-
export const RAW = Symbol.for('baker:raw');
|
|
12
|
-
/** Tier 2 seal result (dual executor stored on Class by seal()) */
|
|
13
|
-
export const SEALED = Symbol.for('baker:sealed');
|
|
1
|
+
Symbol.metadata??=Symbol.for("Symbol.metadata");export const RAW=Symbol.for("baker:raw");export const SEALED=Symbol.for("baker:sealed");
|
|
@@ -1,25 +1 @@
|
|
|
1
|
-
export function csvTransformer(
|
|
2
|
-
return {
|
|
3
|
-
deserialize: ({ value }) => (typeof value === 'string' ? value.split(separator) : value),
|
|
4
|
-
serialize: ({ value }) => (Array.isArray(value) ? value.join(separator) : value),
|
|
5
|
-
};
|
|
6
|
-
}
|
|
7
|
-
export const jsonTransformer = {
|
|
8
|
-
deserialize: ({ value }) => {
|
|
9
|
-
if (typeof value !== 'string') {
|
|
10
|
-
return value;
|
|
11
|
-
}
|
|
12
|
-
try {
|
|
13
|
-
return JSON.parse(value);
|
|
14
|
-
}
|
|
15
|
-
catch {
|
|
16
|
-
return value;
|
|
17
|
-
}
|
|
18
|
-
},
|
|
19
|
-
serialize: ({ value }) => {
|
|
20
|
-
if (value != null && typeof value === 'object') {
|
|
21
|
-
return JSON.stringify(value);
|
|
22
|
-
}
|
|
23
|
-
return value;
|
|
24
|
-
},
|
|
25
|
-
};
|
|
1
|
+
export function csvTransformer(r=","){return{deserialize:({value:t})=>typeof t==="string"?t.split(r):t,serialize:({value:t})=>Array.isArray(t)?t.join(r):t}}export const jsonTransformer={deserialize:({value:r})=>{if(typeof r!=="string")return r;try{return JSON.parse(r)}catch{return r}},serialize:({value:r})=>{if(r!=null&&typeof r==="object")return JSON.stringify(r);return r}};
|
|
@@ -1,18 +1 @@
|
|
|
1
|
-
export const unixSecondsTransformer = {
|
|
2
|
-
deserialize: ({ value }) => (typeof value === 'number' ? new Date(value * 1000) : value),
|
|
3
|
-
serialize: ({ value }) => (value instanceof Date ? Math.floor(value.getTime() / 1000) : value),
|
|
4
|
-
};
|
|
5
|
-
export const unixMillisTransformer = {
|
|
6
|
-
deserialize: ({ value }) => (typeof value === 'number' ? new Date(value) : value),
|
|
7
|
-
serialize: ({ value }) => (value instanceof Date ? value.getTime() : value),
|
|
8
|
-
};
|
|
9
|
-
export const isoStringTransformer = {
|
|
10
|
-
deserialize: ({ value }) => {
|
|
11
|
-
if (typeof value !== 'string') {
|
|
12
|
-
return value;
|
|
13
|
-
}
|
|
14
|
-
const d = new Date(value);
|
|
15
|
-
return Number.isNaN(d.getTime()) ? value : d;
|
|
16
|
-
},
|
|
17
|
-
serialize: ({ value }) => (value instanceof Date ? value.toISOString() : value),
|
|
18
|
-
};
|
|
1
|
+
export const unixSecondsTransformer={deserialize:({value:r})=>typeof r==="number"?new Date(r*1000):r,serialize:({value:r})=>r instanceof Date?Math.floor(r.getTime()/1000):r};export const unixMillisTransformer={deserialize:({value:r})=>typeof r==="number"?new Date(r):r,serialize:({value:r})=>r instanceof Date?r.getTime():r};export const isoStringTransformer={deserialize:({value:r})=>{if(typeof r!=="string")return r;const e=new Date(r);return Number.isNaN(e.getTime())?r:e},serialize:({value:r})=>r instanceof Date?r.toISOString():r};
|
|
@@ -1,6 +1 @@
|
|
|
1
|
-
export
|
|
2
|
-
export { roundTransformer } from './number.transformer.js';
|
|
3
|
-
export { unixSecondsTransformer, unixMillisTransformer, isoStringTransformer } from './date.transformer.js';
|
|
4
|
-
export { csvTransformer, jsonTransformer } from './collection.transformer.js';
|
|
5
|
-
export { luxonTransformer } from './luxon.transformer.js';
|
|
6
|
-
export { momentTransformer } from './moment.transformer.js';
|
|
1
|
+
export{trimTransformer,toLowerCaseTransformer,toUpperCaseTransformer}from"./string.transformer.js";export{roundTransformer}from"./number.transformer.js";export{unixSecondsTransformer,unixMillisTransformer,isoStringTransformer}from"./date.transformer.js";export{csvTransformer,jsonTransformer}from"./collection.transformer.js";export{luxonTransformer}from"./luxon.transformer.js";export{momentTransformer}from"./moment.transformer.js";
|
|
@@ -1,34 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
const LUXON_MISSING = "luxonTransformer requires the optional peer dependency 'luxon'. Install it with: bun add luxon";
|
|
3
|
-
async function luxonTransformer(opts) {
|
|
4
|
-
let luxon;
|
|
5
|
-
try {
|
|
6
|
-
luxon = await import('luxon');
|
|
7
|
-
}
|
|
8
|
-
catch (e) {
|
|
9
|
-
throw new BakerError(LUXON_MISSING, { cause: e });
|
|
10
|
-
}
|
|
11
|
-
const { DateTime } = luxon;
|
|
12
|
-
const zone = opts?.zone ?? 'utc';
|
|
13
|
-
// Hoist format option once so the serialize closure doesn't re-read opts per call
|
|
14
|
-
const format = opts?.format;
|
|
15
|
-
return {
|
|
16
|
-
deserialize: ({ value }) => {
|
|
17
|
-
if (typeof value === 'string') {
|
|
18
|
-
return DateTime.fromISO(value, { zone });
|
|
19
|
-
}
|
|
20
|
-
if (value instanceof Date) {
|
|
21
|
-
return DateTime.fromJSDate(value, { zone });
|
|
22
|
-
}
|
|
23
|
-
return value;
|
|
24
|
-
},
|
|
25
|
-
serialize: ({ value }) => {
|
|
26
|
-
if (value && typeof value === 'object' && typeof value.toISO === 'function') {
|
|
27
|
-
const v = value;
|
|
28
|
-
return format ? v.toFormat(format) : v.toISO();
|
|
29
|
-
}
|
|
30
|
-
return value;
|
|
31
|
-
},
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
export { luxonTransformer };
|
|
1
|
+
import{BakerError as C}from"../errors.js";const F="luxonTransformer requires the optional peer dependency 'luxon'. Install it with: bun add luxon";async function luxonTransformer(g){let j;try{j=await import("luxon")}catch(b){throw new C(F,{cause:b})}const{DateTime:q}=j,w=g?.zone??"utc",y=g?.format;return{deserialize:({value:b})=>{if(typeof b==="string")return q.fromISO(b,{zone:w});if(b instanceof Date)return q.fromJSDate(b,{zone:w});return b},serialize:({value:b})=>{if(b&&typeof b==="object"&&typeof b.toISO==="function"){const A=b;return y?A.toFormat(y):A.toISO()}return b}}}export{luxonTransformer};
|