@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,1564 +1,265 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}
|
|
220
|
-
function
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
},
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
default({ varName, emitCtx, validationCode }) {
|
|
267
|
-
let code = `if (${varName} === undefined || ${varName} === null) ${emitCtx.fail('isDefined')};\n`;
|
|
268
|
-
code += `else {\n`;
|
|
269
|
-
code += validationCode;
|
|
270
|
-
code += '}\n';
|
|
271
|
-
return code;
|
|
272
|
-
},
|
|
273
|
-
};
|
|
274
|
-
function generateFieldCode(fieldKey, meta, ctx) {
|
|
275
|
-
const { exposeDefaultValues } = ctx;
|
|
276
|
-
// ⓪ Exclude deserializeOnly / bidirectional → skip
|
|
277
|
-
if (meta.exclude) {
|
|
278
|
-
if (!meta.exclude.serializeOnly) {
|
|
279
|
-
if (ctx.options?.debug) {
|
|
280
|
-
const reason = meta.exclude.deserializeOnly ? 'deserializeOnly' : 'bidirectional';
|
|
281
|
-
return `// [baker] field ${JSON.stringify(fieldKey)} excluded (${reason} @Exclude)\n`;
|
|
282
|
-
}
|
|
283
|
-
return '';
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
// Expose: check if this field is exposed to deserialize
|
|
287
|
-
// If all @Expose entries are serializeOnly, skip field
|
|
288
|
-
if (meta.expose.length > 0 && meta.expose.every(e => e.serializeOnly)) {
|
|
289
|
-
if (ctx.options?.debug) {
|
|
290
|
-
return `// [baker] field ${JSON.stringify(fieldKey)} excluded (all @Expose entries are serializeOnly)\n`;
|
|
291
|
-
}
|
|
292
|
-
return '';
|
|
293
|
-
}
|
|
294
|
-
const varName = toVarName(fieldKey, ctx.varPrefix);
|
|
295
|
-
const extractKey = getDeserializeExtractKey(fieldKey, meta.expose);
|
|
296
|
-
const exposeGroups = getDeserializeExposeGroups(meta.expose);
|
|
297
|
-
const inputObj = ctx.inputExpr || 'input';
|
|
298
|
-
// Create EmitContext — bake field-level message/context so EVERY field-own-path failure
|
|
299
|
-
// (gate, required-missing, conversion, structural gates) carries them, not just rule bodies.
|
|
300
|
-
const fieldExtras = computeFieldExtras(meta, fieldKey, varName, ctx);
|
|
301
|
-
const emitCtx = makeEmitCtx(fieldKey, ctx, fieldExtras);
|
|
302
|
-
let fieldCode = '';
|
|
303
|
-
// ① @ValidateIf guard
|
|
304
|
-
let validateIfIdx = null;
|
|
305
|
-
if (meta.flags.validateIf) {
|
|
306
|
-
validateIfIdx = ctx.refs.length;
|
|
307
|
-
ctx.refs.push(meta.flags.validateIf);
|
|
308
|
-
}
|
|
309
|
-
// ③ Extract + exposeDefaultValues — W7 (N-4): use Object.hasOwn to block prototype-inherited values
|
|
310
|
-
let extractCode;
|
|
311
|
-
const extractKeyJson = JSON.stringify(extractKey);
|
|
312
|
-
if (exposeDefaultValues && !meta.flags.isOptional) {
|
|
313
|
-
// exposeDefaultValues still needs hasOwn — must distinguish "missing key" (use default)
|
|
314
|
-
// from "explicit undefined" (no default). Prototype-only keys are treated as missing.
|
|
315
|
-
const defaultsSource = ctx.validateOnly ? '__bk$defs' : GEN.out;
|
|
316
|
-
extractCode = `var ${varName} = Object.hasOwn(${inputObj}, ${extractKeyJson}) ? ${inputObj}[${extractKeyJson}] : ${defaultsSource}[${JSON.stringify(fieldKey)}];\n`;
|
|
317
|
-
}
|
|
318
|
-
else {
|
|
319
|
-
// Direct property access (own or inherited), matching the fast-validator norm (e.g. ajv).
|
|
320
|
-
// A per-field `Object.hasOwn` guard would read own-only but cost ~10 ns per 5-field DTO
|
|
321
|
-
// (Bun 1.3.13 / i7-13700K) — a ~30% regression on the hot path. The only case it would change
|
|
322
|
-
// is an input whose prototype chain carries a declared field name, which requires a global
|
|
323
|
-
// `Object.prototype` pollution introduced elsewhere (a separate, pre-existing app vulnerability
|
|
324
|
-
// — baker's own input gate rejects `__proto__` payloads). Normal inputs (JSON.parse, framework
|
|
325
|
-
// request bodies) are always own-keyed, so this never triggers in practice.
|
|
326
|
-
extractCode = `var ${varName} = ${inputObj}[${extractKeyJson}];\n`;
|
|
327
|
-
}
|
|
328
|
-
// groups check wrap (§4.5)
|
|
329
|
-
let fieldStart = '';
|
|
330
|
-
let fieldEnd = '';
|
|
331
|
-
if (exposeGroups && exposeGroups.length > 0) {
|
|
332
|
-
fieldStart = `if ((${GEN.group0} !== null || ${GEN.groupsSet}) && (${buildGroupsHasExpr(GEN.group0, GEN.groupsSet, exposeGroups)})) {\n`;
|
|
333
|
-
fieldEnd = '}\n';
|
|
334
|
-
}
|
|
335
|
-
// inner content (extract + optional guard + validation + assign)
|
|
336
|
-
let innerCode = extractCode;
|
|
337
|
-
// ② null/undefined guard — @IsOptional, @IsNullable, @IsDefined combinations (§4.3, Phase5)
|
|
338
|
-
const useOptionalGuard = !!(meta.flags.isOptional && !meta.flags.isDefined);
|
|
339
|
-
const isNullable = meta.flags.isNullable === true;
|
|
340
|
-
const validationCode = generateValidationCode(fieldKey, varName, meta, ctx, emitCtx, exposeGroups);
|
|
341
|
-
const assignNull = ctx.validateOnly ? '' : `${GEN.out}[${JSON.stringify(fieldKey)}] = null;\n`;
|
|
342
|
-
const guardKey = resolveGuardKey(isNullable, useOptionalGuard, meta.flags.isDefined ?? false);
|
|
343
|
-
innerCode += GUARD_STRATEGIES[guardKey]({ varName, emitCtx, assignNull, validationCode });
|
|
344
|
-
// ① @ValidateIf outer wrap
|
|
345
|
-
if (validateIfIdx !== null) {
|
|
346
|
-
fieldCode += fieldStart + `if (refs[${validateIfIdx}](${inputObj})) {\n` + innerCode + '}\n' + fieldEnd;
|
|
347
|
-
}
|
|
348
|
-
else {
|
|
349
|
-
fieldCode += fieldStart + innerCode + fieldEnd;
|
|
350
|
-
}
|
|
351
|
-
return fieldCode;
|
|
352
|
-
}
|
|
353
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
354
|
-
// Validation code generation — type guard + transform + validate + assign
|
|
355
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
356
|
-
function generateValidationCode(fieldKey, varName, meta, ctx, emitCtx, fieldGroups) {
|
|
357
|
-
const { collectErrors } = ctx;
|
|
358
|
-
let code = '';
|
|
359
|
-
// @Transform (deserialize direction) — before validation (§4.3 ⑤)
|
|
360
|
-
const dsTransforms = meta.transform.filter(td => !td.options?.serializeOnly);
|
|
361
|
-
if (dsTransforms.length > 0) {
|
|
362
|
-
const fkJson = JSON.stringify(fieldKey);
|
|
363
|
-
const objExpr = ctx.inputExpr || 'input';
|
|
364
|
-
if (dsTransforms.length === 1) {
|
|
365
|
-
const td = dsTransforms[0];
|
|
366
|
-
const refIdx = ctx.refs.length;
|
|
367
|
-
ctx.refs.push(td.fn);
|
|
368
|
-
const callExpr = `refs[${refIdx}]({value:${varName},key:${fkJson},obj:${objExpr}})`;
|
|
369
|
-
code += `${varName} = ${td.isAsync ? 'await ' : ''}${callExpr};\n`;
|
|
370
|
-
}
|
|
371
|
-
else if (dsTransforms.length === 2) {
|
|
372
|
-
const td0 = dsTransforms[0];
|
|
373
|
-
const td1 = dsTransforms[1];
|
|
374
|
-
const refIdx0 = ctx.refs.length;
|
|
375
|
-
ctx.refs.push(td0.fn);
|
|
376
|
-
const refIdx1 = ctx.refs.length;
|
|
377
|
-
ctx.refs.push(td1.fn);
|
|
378
|
-
const call0 = `refs[${refIdx0}]({value:${varName},key:${fkJson},obj:${objExpr}})`;
|
|
379
|
-
const expr0 = td0.isAsync ? `await ${call0}` : call0;
|
|
380
|
-
const call1 = `refs[${refIdx1}]({value:${expr0},key:${fkJson},obj:${objExpr}})`;
|
|
381
|
-
code += `${varName} = ${td1.isAsync ? 'await ' : ''}${call1};\n`;
|
|
382
|
-
}
|
|
383
|
-
else {
|
|
384
|
-
for (const td of dsTransforms) {
|
|
385
|
-
const refIdx = ctx.refs.length;
|
|
386
|
-
ctx.refs.push(td.fn);
|
|
387
|
-
const callExpr = `refs[${refIdx}]({value:${varName},key:${fkJson},obj:${objExpr}})`;
|
|
388
|
-
code += `${varName} = ${td.isAsync ? 'await ' : ''}${callExpr};\n`;
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
// Collection (Map/Set) auto conversion
|
|
393
|
-
if (meta.type?.collection) {
|
|
394
|
-
code += ctx.validateOnly
|
|
395
|
-
? generateCollectionCodeValidateOnly(fieldKey, varName, meta, ctx, emitCtx)
|
|
396
|
-
: generateCollectionCode(fieldKey, varName, meta, ctx, emitCtx);
|
|
397
|
-
return code;
|
|
398
|
-
}
|
|
399
|
-
// @ValidateNested + @Type (§8.1)
|
|
400
|
-
if (meta.flags.validateNested && meta.type?.fn) {
|
|
401
|
-
code += ctx.validateOnly
|
|
402
|
-
? generateNestedCodeValidateOnly(fieldKey, varName, meta, ctx, emitCtx)
|
|
403
|
-
: generateNestedCode(fieldKey, varName, meta, ctx, emitCtx);
|
|
404
|
-
return code;
|
|
405
|
-
}
|
|
406
|
-
// No validation rules → direct assign (skip in validate mode)
|
|
407
|
-
if (meta.validation.length === 0) {
|
|
408
|
-
if (!ctx.validateOnly) {
|
|
409
|
-
code += `${GEN.out}[${JSON.stringify(fieldKey)}] = ${varName};\n`;
|
|
410
|
-
}
|
|
411
|
-
return code;
|
|
412
|
-
}
|
|
413
|
-
// Build validation with type gate
|
|
414
|
-
code += buildRulesCode(fieldKey, varName, meta.validation, collectErrors, emitCtx, ctx, meta, fieldGroups);
|
|
415
|
-
return code;
|
|
416
|
-
}
|
|
417
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
418
|
-
// Helpers for computing message/context extra fields in generated issue objects
|
|
419
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
420
|
-
/** Build the `,message:...,context:...` extras string for a generated issue object.
|
|
421
|
-
* `getConstraintsArg` produces the JS expression for a message function's `constraints`
|
|
422
|
-
* field; it runs AFTER the message ref is pushed, preserving ref-array order. */
|
|
423
|
-
function buildIssueExtras(message, context, getConstraintsArg, fieldKey, varName, ctx) {
|
|
424
|
-
let extra = '';
|
|
425
|
-
if (typeof message === 'string') {
|
|
426
|
-
extra += `,message:${JSON.stringify(message)}`;
|
|
427
|
-
}
|
|
428
|
-
else if (typeof message === 'function') {
|
|
429
|
-
const msgIdx = ctx.refs.length;
|
|
430
|
-
ctx.refs.push(message);
|
|
431
|
-
const constraintsArg = getConstraintsArg();
|
|
432
|
-
extra += `,message:refs[${msgIdx}]({property:${JSON.stringify(fieldKey)},value:${varName},constraints:${constraintsArg}})`;
|
|
433
|
-
}
|
|
434
|
-
if (context !== undefined) {
|
|
435
|
-
const ctxIdx = ctx.refs.length;
|
|
436
|
-
ctx.refs.push(context);
|
|
437
|
-
extra += `,context:refs[${ctxIdx}]`;
|
|
438
|
-
}
|
|
439
|
-
return extra;
|
|
440
|
-
}
|
|
441
|
-
/** Per-rule extras — a message function receives the failing rule's `constraints`. */
|
|
442
|
-
function computeRuleExtras(rd, fieldKey, varName, ctx) {
|
|
443
|
-
return buildIssueExtras(rd.message, rd.context, () => {
|
|
444
|
-
const constraintsIdx = ctx.refs.length;
|
|
445
|
-
ctx.refs.push(rd.rule.constraints ?? {});
|
|
446
|
-
return `refs[${constraintsIdx}]`;
|
|
447
|
-
}, fieldKey, varName, ctx);
|
|
448
|
-
}
|
|
449
|
-
/** Field-level extras appended to EVERY failure of a field — including non-rule failures
|
|
450
|
-
* (type gate, required-missing, conversion, structural gates) and type-only fields. No
|
|
451
|
-
* specific rule applies, so a message function gets `constraints:{}`. */
|
|
452
|
-
function computeFieldExtras(meta, fieldKey, varName, ctx) {
|
|
453
|
-
return buildIssueExtras(meta.message, meta.context, () => '{}', fieldKey, varName, ctx);
|
|
454
|
-
}
|
|
455
|
-
/** Create per-rule EmitContext (with message/context overrides) */
|
|
456
|
-
function makeRuleEmitCtx(baseEmitCtx, fieldKey, varName, rd, ctx) {
|
|
457
|
-
const extra = computeRuleExtras(rd, fieldKey, varName, ctx);
|
|
458
|
-
if (!extra) {
|
|
459
|
-
return baseEmitCtx;
|
|
460
|
-
}
|
|
461
|
-
const pathExpr = baseEmitCtx.pathExpr ?? JSON.stringify(fieldKey);
|
|
462
|
-
return {
|
|
463
|
-
...baseEmitCtx,
|
|
464
|
-
fail(code) {
|
|
465
|
-
if (baseEmitCtx.collectErrors) {
|
|
466
|
-
return `${GEN.errList}.push({path:${pathExpr},code:${JSON.stringify(code)}${extra}})`;
|
|
467
|
-
}
|
|
468
|
-
else if (ctx.validateOnly) {
|
|
469
|
-
return `return [{path:${pathExpr},code:${JSON.stringify(code)}${extra}}]`;
|
|
470
|
-
}
|
|
471
|
-
return `return err([{path:${pathExpr},code:${JSON.stringify(code)}${extra}}])`;
|
|
472
|
-
},
|
|
473
|
-
};
|
|
474
|
-
}
|
|
475
|
-
function emitRuleList(fieldKey, varName, rules, emitCtx, ctx, indent, fieldGroups, insideTypeGate) {
|
|
476
|
-
let code = '';
|
|
477
|
-
// Single-pass partition over rules, counting both cacheable categories without a filter[] alloc.
|
|
478
|
-
let lengthCount = 0;
|
|
479
|
-
let timeCount = 0;
|
|
480
|
-
for (const rd of rules) {
|
|
481
|
-
if (!sameGroups(rd.groups, fieldGroups)) {
|
|
482
|
-
continue;
|
|
483
|
-
}
|
|
484
|
-
if (rd.rule.plan?.cacheKey === 'length') {
|
|
485
|
-
lengthCount += 1;
|
|
486
|
-
}
|
|
487
|
-
else if (rd.rule.plan?.cacheKey === 'time') {
|
|
488
|
-
timeCount += 1;
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
const sk = sanitizeKey(fieldKey);
|
|
492
|
-
const lengthVar = lengthCount > 1 ? `${GEN.arr}${sk}len` : null;
|
|
493
|
-
const timeVar = timeCount > 1 ? `${GEN.arr}${sk}time` : null;
|
|
494
|
-
if (lengthVar) {
|
|
495
|
-
code += `${indent}var ${lengthVar} = ${varName}.length;\n`;
|
|
496
|
-
}
|
|
497
|
-
if (timeVar) {
|
|
498
|
-
code += `${indent}var ${timeVar} = ${varName}.getTime();\n`;
|
|
499
|
-
}
|
|
500
|
-
for (const rd of rules) {
|
|
501
|
-
const sg = sameGroups(rd.groups, fieldGroups); // cache once — was called 3× per rule
|
|
502
|
-
const ruleEmitCtx = makeRuleEmitCtx(emitCtx, fieldKey, varName, rd, ctx);
|
|
503
|
-
const gatedCtx = insideTypeGate ? { ...ruleEmitCtx, insideTypeGate: true } : ruleEmitCtx;
|
|
504
|
-
let emitted;
|
|
505
|
-
if (sg && rd.rule.plan && (lengthVar || timeVar)) {
|
|
506
|
-
const cache = {};
|
|
507
|
-
if (rd.rule.plan.cacheKey === 'length' && lengthVar) {
|
|
508
|
-
cache.length = lengthVar;
|
|
509
|
-
}
|
|
510
|
-
if (rd.rule.plan.cacheKey === 'time' && timeVar) {
|
|
511
|
-
cache.time = timeVar;
|
|
512
|
-
}
|
|
513
|
-
emitted = emitRulePlan(varName, gatedCtx, rd.rule.ruleName, rd.rule.plan, cache, insideTypeGate);
|
|
514
|
-
}
|
|
515
|
-
else {
|
|
516
|
-
emitted = rd.rule.emit(varName, gatedCtx);
|
|
517
|
-
}
|
|
518
|
-
if (!emitted) {
|
|
519
|
-
continue;
|
|
520
|
-
} // empty emit (e.g., asserter fully subsumed by gate)
|
|
521
|
-
const ruleCode = sg ? emitted : wrapGroupsGuard(rd, emitted);
|
|
522
|
-
code += indent + ruleCode.replace(/\n/g, '\n' + indent) + '\n';
|
|
523
|
-
}
|
|
524
|
-
return code;
|
|
525
|
-
}
|
|
526
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
527
|
-
// wrapGroupsGuard — per-rule validation groups check wrapper (§M4)
|
|
528
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
529
|
-
/**
|
|
530
|
-
* When rd.groups is set, only execute code if there is an intersection with runtime __bk$groups.
|
|
531
|
-
* Rules without groups always execute (preserves existing behavior).
|
|
532
|
-
*/
|
|
533
|
-
function wrapGroupsGuard(rd, code) {
|
|
534
|
-
if (!rd.groups || rd.groups.length === 0) {
|
|
535
|
-
return code;
|
|
536
|
-
}
|
|
537
|
-
return `if ((${GEN.group0} === null && !${GEN.groupsSet}) || ${buildGroupsHasExpr(GEN.group0, GEN.groupsSet, rd.groups)}) {\n${code}\n}\n`;
|
|
538
|
-
}
|
|
539
|
-
function sameGroups(a, b) {
|
|
540
|
-
if (!a || a.length === 0) {
|
|
541
|
-
return !b || b.length === 0;
|
|
542
|
-
}
|
|
543
|
-
if (!b || a.length !== b.length) {
|
|
544
|
-
return false;
|
|
545
|
-
}
|
|
546
|
-
for (let i = 0; i < a.length; i++) {
|
|
547
|
-
if (a[i] !== b[i]) {
|
|
548
|
-
return false;
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
return true;
|
|
552
|
-
}
|
|
553
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
554
|
-
// generateConversionCode — enableImplicitConversion conversion code generation
|
|
555
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
556
|
-
function generateConversionCode(targetType, varName, fieldKey, skipVar, // null = stopAtFirstError
|
|
557
|
-
collectErrors, emitCtx) {
|
|
558
|
-
const failCode = collectErrors
|
|
559
|
-
? `${emitCtx.fail('conversionFailed')}; ${skipVar} = true;`
|
|
560
|
-
: emitCtx.fail('conversionFailed') + ';';
|
|
561
|
-
switch (targetType) {
|
|
562
|
-
case 'string':
|
|
563
|
-
return ` ${varName} = String(${varName});\n`;
|
|
564
|
-
case 'number':
|
|
565
|
-
return ` ${varName} = Number(${varName});\n if (isNaN(${varName})) { ${failCode} }\n`;
|
|
566
|
-
case 'boolean':
|
|
567
|
-
return (` if (${varName} === 'true' || ${varName} === '1' || ${varName} === 1) ${varName} = true;\n` +
|
|
568
|
-
` else if (${varName} === 'false' || ${varName} === '0' || ${varName} === 0) ${varName} = false;\n` +
|
|
569
|
-
` else { ${failCode} }\n`);
|
|
570
|
-
case 'date':
|
|
571
|
-
return ` ${varName} = new Date(${varName});\n if (isNaN(${varName}.getTime())) { ${failCode} }\n`;
|
|
572
|
-
default:
|
|
573
|
-
throw new BakerError(`Unknown implicit conversion type: "${targetType}" for field "${fieldKey}"`);
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
/** `@Type`() primitive builtin → target type mapping */
|
|
577
|
-
const PRIMITIVE_TYPE_HINTS = {
|
|
578
|
-
Number: 'number',
|
|
579
|
-
Boolean: 'boolean',
|
|
580
|
-
String: 'string',
|
|
581
|
-
Date: 'date',
|
|
582
|
-
};
|
|
583
|
-
/** Asserter rule name → gate type mapping */
|
|
584
|
-
const ASSERTER_TO_GATE = {
|
|
585
|
-
isString: 'string',
|
|
586
|
-
isNumber: 'number',
|
|
587
|
-
isBoolean: 'boolean',
|
|
588
|
-
isDate: 'date',
|
|
589
|
-
isInt: 'number',
|
|
590
|
-
isArray: 'array',
|
|
591
|
-
isObject: 'object',
|
|
592
|
-
};
|
|
593
|
-
/** Asserters whose gate check fully subsumes the rule (skip emit inside gate) */
|
|
594
|
-
const GATE_ONLY_ASSERTERS = new Set(['isString', 'isBoolean', 'isDate', 'isArray', 'isObject']);
|
|
595
|
-
/** categorizeRules — separate each/nonEach rules, detect mixed gate conflicts */
|
|
596
|
-
function categorizeRules(fieldKey, validation) {
|
|
597
|
-
// Single-pass partition — was 9 separate .filter() passes over the same array, each allocating
|
|
598
|
-
// a fresh intermediate. For a field with N rules, runs at seal time only but adds up across DTOs.
|
|
599
|
-
const each = [];
|
|
600
|
-
const generalRules = [];
|
|
601
|
-
const typedBuckets = {
|
|
602
|
-
string: [],
|
|
603
|
-
number: [],
|
|
604
|
-
boolean: [],
|
|
605
|
-
date: [],
|
|
606
|
-
array: [],
|
|
607
|
-
object: [],
|
|
608
|
-
};
|
|
609
|
-
for (const rd of validation) {
|
|
610
|
-
if (rd.each) {
|
|
611
|
-
each.push(rd);
|
|
612
|
-
continue;
|
|
613
|
-
}
|
|
614
|
-
const reqType = rd.rule.requiresType;
|
|
615
|
-
if (reqType !== undefined) {
|
|
616
|
-
typedBuckets[reqType].push(rd);
|
|
617
|
-
}
|
|
618
|
-
else {
|
|
619
|
-
generalRules.push(rd);
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
// Mixed gate conflict detection — at most one bucket should be non-empty
|
|
623
|
-
let chosen = undefined;
|
|
624
|
-
let activeTypes = null;
|
|
625
|
-
for (const t of ['string', 'number', 'boolean', 'date', 'array', 'object']) {
|
|
626
|
-
const deps = typedBuckets[t];
|
|
627
|
-
if (deps.length === 0) {
|
|
628
|
-
continue;
|
|
629
|
-
}
|
|
630
|
-
if (chosen) {
|
|
631
|
-
// Late allocation: only build the array when we actually need to report a conflict
|
|
632
|
-
if (activeTypes === null) {
|
|
633
|
-
activeTypes = [chosen.type];
|
|
634
|
-
}
|
|
635
|
-
activeTypes.push(t);
|
|
636
|
-
}
|
|
637
|
-
else {
|
|
638
|
-
chosen = { type: t, deps };
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
if (activeTypes) {
|
|
642
|
-
throw new BakerError(`Field "${fieldKey}" has conflicting requiresType: ${activeTypes.join(', ')}`);
|
|
643
|
-
}
|
|
644
|
-
return { each, generalRules, typedDeps: chosen };
|
|
645
|
-
}
|
|
646
|
-
/** resolveTypeGate — determine effective gate type from asserters/conversion/type hints */
|
|
647
|
-
function resolveTypeGate(fieldKey, categorized, meta, ctx) {
|
|
648
|
-
const { generalRules, typedDeps } = categorized;
|
|
649
|
-
const hasTypedDeps = !!typedDeps;
|
|
650
|
-
const gateType = typedDeps?.type ?? null;
|
|
651
|
-
const gateDeps = typedDeps?.deps ?? [];
|
|
652
|
-
// Find type asserter in generalRules matching gate type
|
|
653
|
-
let typeAsserterIdx = -1;
|
|
654
|
-
if (gateType) {
|
|
655
|
-
typeAsserterIdx = generalRules.findIndex(rd => ASSERTER_TO_GATE[rd.rule.ruleName] === gateType);
|
|
656
|
-
}
|
|
657
|
-
// enableImplicitConversion check — skip if explicit @Transform for deserialize direction
|
|
658
|
-
const enableConversion = !!ctx.options?.enableImplicitConversion && !meta?.transform.some(td => !td.options?.serializeOnly);
|
|
659
|
-
// enableImplicitConversion: asserter-only gate inference — generate conversion gate even for standalone @IsNumber() usage
|
|
660
|
-
let asserterInferredGate = null;
|
|
661
|
-
if (!hasTypedDeps && enableConversion && typeAsserterIdx < 0) {
|
|
662
|
-
for (let i = 0; i < generalRules.length; i++) {
|
|
663
|
-
const gate = ASSERTER_TO_GATE[generalRules[i].rule.ruleName];
|
|
664
|
-
if (gate) {
|
|
665
|
-
typeAsserterIdx = i;
|
|
666
|
-
asserterInferredGate = gate;
|
|
667
|
-
break;
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
const typeAsserter = typeAsserterIdx >= 0 ? generalRules[typeAsserterIdx] : undefined;
|
|
672
|
-
// @Type() primitive hint — infer conversion target when no typed deps exist
|
|
673
|
-
let typeHintGate = null;
|
|
674
|
-
if (!hasTypedDeps && !asserterInferredGate && enableConversion && meta?.type?.fn) {
|
|
675
|
-
try {
|
|
676
|
-
const raw = meta.type.fn();
|
|
677
|
-
const typeCtor = Array.isArray(raw) ? raw[0] : raw;
|
|
678
|
-
typeHintGate = typeCtor ? (PRIMITIVE_TYPE_HINTS[typeCtor.name] ?? null) : null;
|
|
679
|
-
}
|
|
680
|
-
catch (e) {
|
|
681
|
-
throw new BakerError(`field "${fieldKey}": @Field type function threw: ${e.message}`, { cause: e });
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
return {
|
|
685
|
-
effectiveGateType: gateType ?? asserterInferredGate ?? typeHintGate,
|
|
686
|
-
gateDeps,
|
|
687
|
-
typeAsserterIdx,
|
|
688
|
-
typeAsserter,
|
|
689
|
-
enableConversion,
|
|
690
|
-
asserterInferredGate,
|
|
691
|
-
typeHintGate,
|
|
692
|
-
};
|
|
693
|
-
}
|
|
694
|
-
/** emitTypedRules — generate type gate + inner validation code */
|
|
695
|
-
function emitTypedRules(fieldKey, varName, collectErrors, emitCtx, ctx, config, fieldGroups) {
|
|
696
|
-
let code = '';
|
|
697
|
-
const sk = sanitizeKey(fieldKey); // cached — was called up to 4× in this function before
|
|
698
|
-
const { effectiveGateType, gateCondition, gateErrorCode, gateEmitCtx, otherGeneral, gateDeps, typeAsserter, enableConversion } = config;
|
|
699
|
-
// Helper: emit inner validation rules
|
|
700
|
-
const emitInnerRules = (indent) => {
|
|
701
|
-
const rules = [];
|
|
702
|
-
// typeAsserter emit — skip GATE_ONLY_ASSERTERS (isString, isBoolean) as they fully overlap with the gate
|
|
703
|
-
if (typeAsserter && !GATE_ONLY_ASSERTERS.has(typeAsserter.rule.ruleName)) {
|
|
704
|
-
rules.push(typeAsserter);
|
|
705
|
-
}
|
|
706
|
-
rules.push(...otherGeneral, ...gateDeps);
|
|
707
|
-
return emitRuleList(fieldKey, varName, rules, emitCtx, ctx, indent, fieldGroups, true);
|
|
708
|
-
};
|
|
709
|
-
if (collectErrors) {
|
|
710
|
-
const canConvert = enableConversion &&
|
|
711
|
-
(effectiveGateType === 'string' ||
|
|
712
|
-
effectiveGateType === 'number' ||
|
|
713
|
-
effectiveGateType === 'boolean' ||
|
|
714
|
-
effectiveGateType === 'date');
|
|
715
|
-
if (canConvert) {
|
|
716
|
-
// Conversion mode: try convert on gate failure, skip field if conversion fails
|
|
717
|
-
const skipVar = `${GEN.skip}${sk}`;
|
|
718
|
-
code += `var ${skipVar} = false;\n`;
|
|
719
|
-
code += `if (${gateCondition}) {\n`;
|
|
720
|
-
code += generateConversionCode(effectiveGateType, varName, fieldKey, skipVar, true, emitCtx);
|
|
721
|
-
code += `}\n`;
|
|
722
|
-
code += `if (!${skipVar}) {\n`;
|
|
723
|
-
if (ctx.validateOnly) {
|
|
724
|
-
code += emitInnerRules(' ');
|
|
725
|
-
}
|
|
726
|
-
else {
|
|
727
|
-
const markVar = `${GEN.mark}${sk}`;
|
|
728
|
-
code += ` var ${markVar} = ${GEN.errList}.length;\n`;
|
|
729
|
-
code += emitInnerRules(' ');
|
|
730
|
-
code += ` if (${GEN.errList}.length === ${markVar}) ${GEN.out}[${JSON.stringify(fieldKey)}] = ${varName};\n`;
|
|
731
|
-
}
|
|
732
|
-
code += `}\n`;
|
|
733
|
-
}
|
|
734
|
-
else {
|
|
735
|
-
code += `if (${gateCondition}) ${gateEmitCtx.fail(gateErrorCode)};\n`;
|
|
736
|
-
code += `else {\n`;
|
|
737
|
-
if (ctx.validateOnly) {
|
|
738
|
-
code += emitInnerRules(' ');
|
|
739
|
-
}
|
|
740
|
-
else {
|
|
741
|
-
const markVar = `${GEN.mark}${sk}`;
|
|
742
|
-
code += ` var ${markVar} = ${GEN.errList}.length;\n`;
|
|
743
|
-
code += emitInnerRules(' ');
|
|
744
|
-
code += ` if (${GEN.errList}.length === ${markVar}) ${GEN.out}[${JSON.stringify(fieldKey)}] = ${varName};\n`;
|
|
745
|
-
}
|
|
746
|
-
code += `}\n`;
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
else {
|
|
750
|
-
const canConvert = enableConversion &&
|
|
751
|
-
(effectiveGateType === 'string' ||
|
|
752
|
-
effectiveGateType === 'number' ||
|
|
753
|
-
effectiveGateType === 'boolean' ||
|
|
754
|
-
effectiveGateType === 'date');
|
|
755
|
-
if (canConvert) {
|
|
756
|
-
code += `if (${gateCondition}) {\n`;
|
|
757
|
-
code += generateConversionCode(effectiveGateType, varName, fieldKey, null, false, emitCtx);
|
|
758
|
-
code += `}\n`;
|
|
759
|
-
code += emitInnerRules('');
|
|
760
|
-
if (!ctx.validateOnly) {
|
|
761
|
-
code += `${GEN.out}[${JSON.stringify(fieldKey)}] = ${varName};\n`;
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
else {
|
|
765
|
-
code += `if (${gateCondition}) ${gateEmitCtx.fail(gateErrorCode)};\n`;
|
|
766
|
-
code += emitInnerRules('');
|
|
767
|
-
if (!ctx.validateOnly) {
|
|
768
|
-
code += `${GEN.out}[${JSON.stringify(fieldKey)}] = ${varName};\n`;
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
return code;
|
|
773
|
-
}
|
|
774
|
-
/** emitGeneralRules — generate type-agnostic rule code */
|
|
775
|
-
function emitGeneralRules(fieldKey, varName, generalRules, collectErrors, emitCtx, ctx, fieldGroups) {
|
|
776
|
-
let code = '';
|
|
777
|
-
if (collectErrors) {
|
|
778
|
-
if (generalRules.length === 0) {
|
|
779
|
-
if (!ctx.validateOnly) {
|
|
780
|
-
code += `${GEN.out}[${JSON.stringify(fieldKey)}] = ${varName};\n`;
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
else if (ctx.validateOnly) {
|
|
784
|
-
code += emitRuleList(fieldKey, varName, generalRules, emitCtx, ctx, '', fieldGroups);
|
|
785
|
-
}
|
|
786
|
-
else {
|
|
787
|
-
const markVar = `${GEN.mark}${sanitizeKey(fieldKey)}`;
|
|
788
|
-
code += `var ${markVar} = ${GEN.errList}.length;\n`;
|
|
789
|
-
code += emitRuleList(fieldKey, varName, generalRules, emitCtx, ctx, '', fieldGroups);
|
|
790
|
-
code += `if (${GEN.errList}.length === ${markVar}) ${GEN.out}[${JSON.stringify(fieldKey)}] = ${varName};\n`;
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
else {
|
|
794
|
-
code += emitRuleList(fieldKey, varName, generalRules, emitCtx, ctx, '', fieldGroups);
|
|
795
|
-
if (!ctx.validateOnly) {
|
|
796
|
-
code += `${GEN.out}[${JSON.stringify(fieldKey)}] = ${varName};\n`;
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
|
-
return code;
|
|
800
|
-
}
|
|
801
|
-
/** emitEachRules — generate Array/Set/Map each code */
|
|
802
|
-
function emitEachRules(fieldKey, varName, eachRules, collectErrors, emitCtx, ctx, fieldGroups) {
|
|
803
|
-
let code = '';
|
|
804
|
-
for (const rd of eachRules) {
|
|
805
|
-
// pathKey must honor ctx.pathPrefix so inlined nested DTOs report full path.
|
|
806
|
-
// Without this, validate(Parent, ...) returned `tags[1]` while deserialize returned `nested.tags[1]`.
|
|
807
|
-
const pathKey = ctx.pathPrefix ? `${ctx.pathPrefix}+${JSON.stringify(fieldKey)}` : JSON.stringify(fieldKey);
|
|
808
|
-
const sk = sanitizeKey(fieldKey);
|
|
809
|
-
const iVar = `${GEN.index}${sk}`;
|
|
810
|
-
const siVar = `${GEN.setIdx}${sk}`;
|
|
811
|
-
const svVar = `${GEN.setVal}${sk}`;
|
|
812
|
-
const miVar = `${GEN.mapIdx}${sk}`;
|
|
813
|
-
const mvVar = `${GEN.mapVal}${sk}`;
|
|
814
|
-
const extra = computeRuleExtras(rd, fieldKey, varName, ctx);
|
|
815
|
-
// Cache the groups-guard predicate once — was previously evaluated twice (open + close)
|
|
816
|
-
const rdGroups = rd.groups && rd.groups.length > 0 && !sameGroups(rd.groups, fieldGroups) ? rd.groups : null;
|
|
817
|
-
const eachGuardOpen = rdGroups
|
|
818
|
-
? `if ((${GEN.group0} === null && !${GEN.groupsSet}) || ${buildGroupsHasExpr(GEN.group0, GEN.groupsSet, rdGroups)}) {\n`
|
|
819
|
-
: '';
|
|
820
|
-
const eachGuardClose = rdGroups ? '}\n' : '';
|
|
821
|
-
// Collection descriptors: [idxVar, elemExpr, loopHeader, counterDecl, counterInc]
|
|
822
|
-
const collections = [
|
|
823
|
-
{
|
|
824
|
-
guard: `Array.isArray(${varName})`,
|
|
825
|
-
idxVar: iVar,
|
|
826
|
-
elemExpr: `${varName}[${iVar}]`,
|
|
827
|
-
loopHeader: `for (var ${iVar}=0; ${iVar}<${varName}.length; ${iVar}++)`,
|
|
828
|
-
counterDecl: '',
|
|
829
|
-
counterInc: '',
|
|
830
|
-
},
|
|
831
|
-
{
|
|
832
|
-
guard: `${varName} instanceof Set`,
|
|
833
|
-
idxVar: siVar,
|
|
834
|
-
elemExpr: svVar,
|
|
835
|
-
loopHeader: `for (var ${svVar} of ${varName})`,
|
|
836
|
-
counterDecl: `var ${siVar} = 0;\n`,
|
|
837
|
-
counterInc: `${siVar}++;\n`,
|
|
838
|
-
},
|
|
839
|
-
{
|
|
840
|
-
guard: `${varName} instanceof Map`,
|
|
841
|
-
idxVar: miVar,
|
|
842
|
-
elemExpr: mvVar,
|
|
843
|
-
loopHeader: `for (var ${mvVar} of ${varName}.values())`,
|
|
844
|
-
counterDecl: `var ${miVar} = 0;\n`,
|
|
845
|
-
counterInc: `${miVar}++;\n`,
|
|
846
|
-
},
|
|
847
|
-
];
|
|
848
|
-
// Single prefix var shared across all collection branches — only one branch executes
|
|
849
|
-
// at runtime, so reusing the same identifier avoids 3 hoisted-but-dead var declarations.
|
|
850
|
-
const prefixVar = `__bk$ep_${sk}`;
|
|
851
|
-
const emitCollectionBlock = (col) => {
|
|
852
|
-
const failFn = (c) => collectErrors
|
|
853
|
-
? `${GEN.errList}.push({path:${prefixVar}+${col.idxVar}+']',code:${JSON.stringify(c)}${extra}})`
|
|
854
|
-
: ctx.validateOnly
|
|
855
|
-
? `return [{path:${prefixVar}+${col.idxVar}+']',code:${JSON.stringify(c)}${extra}}]`
|
|
856
|
-
: `return err([{path:${prefixVar}+${col.idxVar}+']',code:${JSON.stringify(c)}${extra}}])`;
|
|
857
|
-
const colEmitCtx = { ...emitCtx, fail: failFn };
|
|
858
|
-
let block = '';
|
|
859
|
-
block += ` ${col.counterDecl}`;
|
|
860
|
-
block += ` ${col.loopHeader} {\n`;
|
|
861
|
-
block += ' ' + rd.rule.emit(col.elemExpr, colEmitCtx) + '\n';
|
|
862
|
-
if (col.counterInc) {
|
|
863
|
-
block += ` ${col.counterInc}`;
|
|
864
|
-
}
|
|
865
|
-
block += ` }\n`;
|
|
866
|
-
return block;
|
|
867
|
-
};
|
|
868
|
-
// Compute collection kind once via integer dispatch — eliminates 2-3× repeated
|
|
869
|
-
// Array.isArray / instanceof Set / instanceof Map evaluation.
|
|
870
|
-
// prefixVar is declared once here and reused by all three branches.
|
|
871
|
-
const kindVar = `__bk$ck${sk}`;
|
|
872
|
-
code += eachGuardOpen;
|
|
873
|
-
code += `var ${kindVar} = Array.isArray(${varName})?1:(${varName} instanceof Set?2:(${varName} instanceof Map?3:0));\n`;
|
|
874
|
-
code += `var ${prefixVar} = ${pathKey}+'[';\n`;
|
|
875
|
-
if (collectErrors) {
|
|
876
|
-
code += `if (${kindVar} === 1) {\n`;
|
|
877
|
-
code += emitCollectionBlock(collections[0]);
|
|
878
|
-
code += `} else if (${kindVar} === 2) {\n`;
|
|
879
|
-
code += emitCollectionBlock(collections[1]);
|
|
880
|
-
code += `} else if (${kindVar} === 3) {\n`;
|
|
881
|
-
code += emitCollectionBlock(collections[2]);
|
|
882
|
-
code += `} else { ${emitCtx.fail('isArray')}; }\n`;
|
|
883
|
-
}
|
|
884
|
-
else {
|
|
885
|
-
code += `if (${kindVar} === 0) ${emitCtx.fail('isArray')};\n`;
|
|
886
|
-
code += `if (${kindVar} === 1) {\n`;
|
|
887
|
-
code += emitCollectionBlock(collections[0]);
|
|
888
|
-
code += `} else if (${kindVar} === 2) {\n`;
|
|
889
|
-
code += emitCollectionBlock(collections[1]);
|
|
890
|
-
code += `} else if (${kindVar} === 3) {\n`;
|
|
891
|
-
code += emitCollectionBlock(collections[2]);
|
|
892
|
-
code += `}\n`;
|
|
893
|
-
}
|
|
894
|
-
code += eachGuardClose;
|
|
895
|
-
}
|
|
896
|
-
return code;
|
|
897
|
-
}
|
|
898
|
-
/** buildRulesCode — orchestrator that composes categorize → resolve → emit phases */
|
|
899
|
-
function buildRulesCode(fieldKey, varName, validation, collectErrors, emitCtx, ctx, meta, fieldGroups) {
|
|
900
|
-
// Phase 1: Categorize rules
|
|
901
|
-
const categorized = categorizeRules(fieldKey, validation);
|
|
902
|
-
// Phase 2: Resolve type gate
|
|
903
|
-
const resolved = resolveTypeGate(fieldKey, categorized, meta, ctx);
|
|
904
|
-
let code = '';
|
|
905
|
-
// Phase 3: Emit typed or general rules
|
|
906
|
-
const hasTypedDeps = !!categorized.typedDeps;
|
|
907
|
-
if (hasTypedDeps || resolved.asserterInferredGate || resolved.typeHintGate) {
|
|
908
|
-
// Other general rules (excluding the type asserter)
|
|
909
|
-
const otherGeneral = resolved.typeAsserter
|
|
910
|
-
? categorized.generalRules.filter((_, i) => i !== resolved.typeAsserterIdx)
|
|
911
|
-
: categorized.generalRules;
|
|
912
|
-
// Generate type gate condition — date uses instanceof, others use typeof
|
|
913
|
-
let gateCondition;
|
|
914
|
-
let gateErrorCode;
|
|
915
|
-
if (resolved.typeAsserter) {
|
|
916
|
-
gateErrorCode = resolved.typeAsserter.rule.ruleName;
|
|
917
|
-
}
|
|
918
|
-
else if (resolved.gateDeps.length > 0) {
|
|
919
|
-
gateErrorCode = resolved.gateDeps[0].rule.ruleName;
|
|
920
|
-
}
|
|
921
|
-
else {
|
|
922
|
-
gateErrorCode = 'conversionFailed'; // @Type hint only — no asserter or deps
|
|
923
|
-
}
|
|
924
|
-
if (resolved.effectiveGateType === 'date') {
|
|
925
|
-
gateCondition = `!(${varName} instanceof Date) || isNaN(${varName}.getTime())`;
|
|
926
|
-
}
|
|
927
|
-
else if (resolved.effectiveGateType === 'array') {
|
|
928
|
-
gateCondition = `!Array.isArray(${varName})`;
|
|
929
|
-
}
|
|
930
|
-
else if (resolved.effectiveGateType === 'object') {
|
|
931
|
-
gateCondition = `typeof ${varName} !== 'object' || ${varName} === null || Array.isArray(${varName})`;
|
|
932
|
-
}
|
|
933
|
-
else if (resolved.effectiveGateType === 'number') {
|
|
934
|
-
gateCondition = `typeof ${varName} !== 'number' || isNaN(${varName})`;
|
|
935
|
-
}
|
|
936
|
-
else {
|
|
937
|
-
gateCondition = `typeof ${varName} !== '${resolved.effectiveGateType}'`;
|
|
938
|
-
}
|
|
939
|
-
// Type gate fail — reflect message/context if typeAsserter rd exists
|
|
940
|
-
const gateEmitCtx = resolved.typeAsserter ? makeRuleEmitCtx(emitCtx, fieldKey, varName, resolved.typeAsserter, ctx) : emitCtx;
|
|
941
|
-
code += emitTypedRules(fieldKey, varName, collectErrors, emitCtx, ctx, {
|
|
942
|
-
effectiveGateType: resolved.effectiveGateType,
|
|
943
|
-
gateCondition,
|
|
944
|
-
gateErrorCode,
|
|
945
|
-
gateEmitCtx,
|
|
946
|
-
otherGeneral,
|
|
947
|
-
gateDeps: resolved.gateDeps,
|
|
948
|
-
typeAsserter: resolved.typeAsserter,
|
|
949
|
-
enableConversion: resolved.enableConversion,
|
|
950
|
-
}, fieldGroups);
|
|
951
|
-
}
|
|
952
|
-
else {
|
|
953
|
-
code += emitGeneralRules(fieldKey, varName, categorized.generalRules, collectErrors, emitCtx, ctx, fieldGroups);
|
|
954
|
-
}
|
|
955
|
-
// Phase 4: Emit each rules
|
|
956
|
-
code += emitEachRules(fieldKey, varName, categorized.each, collectErrors, emitCtx, ctx, fieldGroups);
|
|
957
|
-
return code;
|
|
958
|
-
}
|
|
959
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
960
|
-
// generateCollectionCode — Map/Set auto conversion
|
|
961
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
962
|
-
function generateCollectionCode(fieldKey, varName, meta, ctx, emitCtx) {
|
|
963
|
-
const { collectErrors, execs } = ctx;
|
|
964
|
-
const sk = sanitizeKey(fieldKey);
|
|
965
|
-
const collection = meta.type.collection;
|
|
966
|
-
const awaitKw = ctx.isAsync ? 'await ' : '';
|
|
967
|
-
// nested DTO executor (if present)
|
|
968
|
-
let execIdx = -1;
|
|
969
|
-
if (meta.type.resolvedCollectionValue) {
|
|
970
|
-
const nestedSealed = getSealed(meta.type.resolvedCollectionValue);
|
|
971
|
-
execIdx = execs.length;
|
|
972
|
-
execs.push(nestedSealed);
|
|
973
|
-
}
|
|
974
|
-
let code = '';
|
|
975
|
-
if (collection === 'Set') {
|
|
976
|
-
// input: array → Set
|
|
977
|
-
code += `if (Array.isArray(${varName})) {\n`;
|
|
978
|
-
// array-level validation rules (e.g. arrayMinSize)
|
|
979
|
-
const nonEachRules = meta.validation.filter(rd => !rd.each);
|
|
980
|
-
code += emitRuleList(fieldKey, varName, nonEachRules, emitCtx, ctx, ' ');
|
|
981
|
-
if (execIdx >= 0) {
|
|
982
|
-
// nested DTO Set
|
|
983
|
-
const iVar = `${GEN.index}${sk}`;
|
|
984
|
-
code += ` var ${GEN.arr}${sk} = new Set();\n`;
|
|
985
|
-
code += ` for (var ${iVar}=0; ${iVar}<${varName}.length; ${iVar}++) {\n`;
|
|
986
|
-
code += ` var ${GEN.result}${sk} = ${awaitKw}execs[${execIdx}].deserialize(${varName}[${iVar}], opts);\n`;
|
|
987
|
-
code += ` if (isErr(${GEN.result}${sk})) {\n`;
|
|
988
|
-
if (collectErrors) {
|
|
989
|
-
code += ` var ${GEN.errors}${sk} = ${GEN.result}${sk}.data;\n`;
|
|
990
|
-
code += ` var __bk$pp${sk} = ${JSON.stringify(fieldKey)}+'['+${iVar}+'].';\n`;
|
|
991
|
-
code += ` for (var ${GEN.nestedIdx}${sk}=0; ${GEN.nestedIdx}${sk}<${GEN.errors}${sk}.length; ${GEN.nestedIdx}${sk}++) {\n`;
|
|
992
|
-
code +=
|
|
993
|
-
` ` +
|
|
994
|
-
nestedErrPush(GEN.errList, `__bk$pp${sk}+${GEN.errors}${sk}[${GEN.nestedIdx}${sk}].path`, `${GEN.errors}${sk}[${GEN.nestedIdx}${sk}]`, `__ne${sk}`);
|
|
995
|
-
code += ` }\n`;
|
|
996
|
-
}
|
|
997
|
-
else {
|
|
998
|
-
code += ` var ${GEN.errors}${sk} = ${GEN.result}${sk}.data;\n`;
|
|
999
|
-
code += ` var __bk$pp${sk} = ${JSON.stringify(fieldKey)}+'['+${iVar}+'].';\n`;
|
|
1000
|
-
code += ` ` + nestedErrReturn(`__bk$pp${sk}+${GEN.errors}${sk}[0].path`, `${GEN.errors}${sk}[0]`, `__ne${sk}`);
|
|
1001
|
-
}
|
|
1002
|
-
code += ` } else { ${GEN.arr}${sk}.add(${GEN.result}${sk}); }\n`;
|
|
1003
|
-
code += ` }\n`;
|
|
1004
|
-
code += ` ${GEN.out}[${JSON.stringify(fieldKey)}] = ${GEN.arr}${sk};\n`;
|
|
1005
|
-
}
|
|
1006
|
-
else {
|
|
1007
|
-
// primitive Set
|
|
1008
|
-
code += ` ${GEN.out}[${JSON.stringify(fieldKey)}] = new Set(${varName});\n`;
|
|
1009
|
-
}
|
|
1010
|
-
// each validation rules (per element)
|
|
1011
|
-
const eachRules = meta.validation.filter(rd => rd.each);
|
|
1012
|
-
if (eachRules.length > 0) {
|
|
1013
|
-
const siVar = `${GEN.setIdx}${sk}`;
|
|
1014
|
-
const svVar = `${GEN.setVal}${sk}`;
|
|
1015
|
-
code += ` var ${siVar} = 0;\n`;
|
|
1016
|
-
code += ` for (var ${svVar} of ${GEN.out}[${JSON.stringify(fieldKey)}]) {\n`;
|
|
1017
|
-
for (const rd of eachRules) {
|
|
1018
|
-
const extra = computeRuleExtras(rd, fieldKey, varName, ctx);
|
|
1019
|
-
const failFn = (c) => collectErrors
|
|
1020
|
-
? `${GEN.errList}.push({path:${JSON.stringify(fieldKey)}+'['+${siVar}+']',code:${JSON.stringify(c)}${extra}})`
|
|
1021
|
-
: `return err([{path:${JSON.stringify(fieldKey)}+'['+${siVar}+']',code:${JSON.stringify(c)}${extra}}])`;
|
|
1022
|
-
const colEmitCtx = { ...emitCtx, fail: failFn };
|
|
1023
|
-
code += ` ${rd.rule.emit(svVar, colEmitCtx)}\n`;
|
|
1024
|
-
}
|
|
1025
|
-
code += ` ${siVar}++;\n`;
|
|
1026
|
-
code += ` }\n`;
|
|
1027
|
-
}
|
|
1028
|
-
code += `} else { ${emitCtx.fail('isArray')}; }\n`;
|
|
1029
|
-
}
|
|
1030
|
-
else {
|
|
1031
|
-
// Map: input plain object → Map
|
|
1032
|
-
code += `if (${varName} != null && typeof ${varName} === 'object' && !Array.isArray(${varName})) {\n`;
|
|
1033
|
-
if (execIdx >= 0) {
|
|
1034
|
-
// nested DTO Map — indexed Object.keys loop (measured 2-30× faster than for-in+hasOwn on Bun/JSC)
|
|
1035
|
-
const kVar = `${GEN.key}${sk}`;
|
|
1036
|
-
const ksVar = `__bk$mk${sk}`;
|
|
1037
|
-
const iVarMap = `__bk$mi${sk}`;
|
|
1038
|
-
code += ` var ${GEN.arr}${sk} = new Map();\n`;
|
|
1039
|
-
code += ` var ${ksVar} = Object.keys(${varName});\n`;
|
|
1040
|
-
code += ` for (var ${iVarMap}=0; ${iVarMap}<${ksVar}.length; ${iVarMap}++) {\n`;
|
|
1041
|
-
code += ` var ${kVar} = ${ksVar}[${iVarMap}];\n`;
|
|
1042
|
-
code += ` var ${GEN.result}${sk} = ${awaitKw}execs[${execIdx}].deserialize(${varName}[${kVar}], opts);\n`;
|
|
1043
|
-
code += ` if (isErr(${GEN.result}${sk})) {\n`;
|
|
1044
|
-
if (collectErrors) {
|
|
1045
|
-
code += ` var ${GEN.errors}${sk} = ${GEN.result}${sk}.data;\n`;
|
|
1046
|
-
code += ` var __bk$pp${sk} = ${JSON.stringify(fieldKey)}+'['+${kVar}+'].';\n`;
|
|
1047
|
-
code += ` for (var ${GEN.nestedIdx}${sk}=0; ${GEN.nestedIdx}${sk}<${GEN.errors}${sk}.length; ${GEN.nestedIdx}${sk}++) {\n`;
|
|
1048
|
-
code +=
|
|
1049
|
-
` ` +
|
|
1050
|
-
nestedErrPush(GEN.errList, `__bk$pp${sk}+${GEN.errors}${sk}[${GEN.nestedIdx}${sk}].path`, `${GEN.errors}${sk}[${GEN.nestedIdx}${sk}]`, `__ne${sk}`);
|
|
1051
|
-
code += ` }\n`;
|
|
1052
|
-
}
|
|
1053
|
-
else {
|
|
1054
|
-
code += ` var ${GEN.errors}${sk} = ${GEN.result}${sk}.data;\n`;
|
|
1055
|
-
code += ` var __bk$pp${sk} = ${JSON.stringify(fieldKey)}+'['+${kVar}+'].';\n`;
|
|
1056
|
-
code += ` ` + nestedErrReturn(`__bk$pp${sk}+${GEN.errors}${sk}[0].path`, `${GEN.errors}${sk}[0]`, `__ne${sk}`);
|
|
1057
|
-
}
|
|
1058
|
-
code += ` } else { ${GEN.arr}${sk}.set(${kVar}, ${GEN.result}${sk}); }\n`;
|
|
1059
|
-
code += ` }\n`;
|
|
1060
|
-
code += ` ${GEN.out}[${JSON.stringify(fieldKey)}] = ${GEN.arr}${sk};\n`;
|
|
1061
|
-
}
|
|
1062
|
-
else {
|
|
1063
|
-
// primitive Map — indexed Object.keys loop
|
|
1064
|
-
const ksVar = `__bk$mk${sk}`;
|
|
1065
|
-
const iVarMap = `__bk$mi${sk}`;
|
|
1066
|
-
code += ` var ${GEN.arr}${sk} = new Map();\n`;
|
|
1067
|
-
code += ` var ${ksVar} = Object.keys(${varName});\n`;
|
|
1068
|
-
code += ` for (var ${iVarMap}=0; ${iVarMap}<${ksVar}.length; ${iVarMap}++) {\n`;
|
|
1069
|
-
code += ` var ${GEN.key}${sk} = ${ksVar}[${iVarMap}];\n`;
|
|
1070
|
-
code += ` ${GEN.arr}${sk}.set(${GEN.key}${sk}, ${varName}[${GEN.key}${sk}]);\n`;
|
|
1071
|
-
code += ` }\n`;
|
|
1072
|
-
code += ` ${GEN.out}[${JSON.stringify(fieldKey)}] = ${GEN.arr}${sk};\n`;
|
|
1073
|
-
}
|
|
1074
|
-
code += `} else { ${emitCtx.fail('isObject')}; }\n`;
|
|
1075
|
-
}
|
|
1076
|
-
return code;
|
|
1077
|
-
}
|
|
1078
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
1079
|
-
// generateNestedCode — @ValidateNested + @Type (§8.1, §8.2)
|
|
1080
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
1081
|
-
function generateNestedCode(fieldKey, varName, meta, ctx, emitCtx) {
|
|
1082
|
-
const { collectErrors, execs } = ctx;
|
|
1083
|
-
if (!meta.type) {
|
|
1084
|
-
return `${GEN.out}[${JSON.stringify(fieldKey)}] = ${varName};\n`;
|
|
1085
|
-
}
|
|
1086
|
-
let code = '';
|
|
1087
|
-
const sk = sanitizeKey(fieldKey);
|
|
1088
|
-
if (meta.type.discriminator) {
|
|
1089
|
-
// §8.3 discriminator
|
|
1090
|
-
const discProp = JSON.stringify(meta.type.discriminator.property);
|
|
1091
|
-
code += `var ${GEN.disc}${sk} = ${varName} && ${varName}[${discProp}];\n`;
|
|
1092
|
-
code += `switch (${GEN.disc}${sk}) {\n`;
|
|
1093
|
-
for (const sub of meta.type.discriminator.subTypes) {
|
|
1094
|
-
const nestedSealed = getSealed(sub.value);
|
|
1095
|
-
const execIdx = execs.length;
|
|
1096
|
-
execs.push(nestedSealed);
|
|
1097
|
-
const awaitKwD = ctx.isAsync ? 'await ' : '';
|
|
1098
|
-
code += ` case ${JSON.stringify(sub.name)}:\n`;
|
|
1099
|
-
code += ` var ${GEN.result}${sk} = ${awaitKwD}execs[${execIdx}].deserialize(${varName}, opts);\n`;
|
|
1100
|
-
code += generateNestedResultCode(fieldKey, `${GEN.result}${sk}`, collectErrors, ctx.pathPrefix);
|
|
1101
|
-
code += ` break;\n`;
|
|
1102
|
-
}
|
|
1103
|
-
const validSubTypeNamesJson = JSON.stringify(meta.type.discriminator.subTypes.map(s => s.name));
|
|
1104
|
-
const discPathExpr = emitCtx.pathExpr ?? JSON.stringify(fieldKey);
|
|
1105
|
-
const discValueExpr = `${GEN.disc}${sk}`;
|
|
1106
|
-
if (collectErrors) {
|
|
1107
|
-
code += ` default: ${GEN.errList}.push({path:${discPathExpr},code:'invalidDiscriminator',context:{received:${discValueExpr},validSubTypes:${validSubTypeNamesJson}}});\n`;
|
|
1108
|
-
}
|
|
1109
|
-
else if (ctx.validateOnly) {
|
|
1110
|
-
code += ` default: return [{path:${discPathExpr},code:'invalidDiscriminator',context:{received:${discValueExpr},validSubTypes:${validSubTypeNamesJson}}}];\n`;
|
|
1111
|
-
}
|
|
1112
|
-
else {
|
|
1113
|
-
code += ` default: return err([{path:${discPathExpr},code:'invalidDiscriminator',context:{received:${discValueExpr},validSubTypes:${validSubTypeNamesJson}}}]);\n`;
|
|
1114
|
-
}
|
|
1115
|
-
code += `}\n`;
|
|
1116
|
-
// keepDiscriminatorProperty: preserve discriminator property in result object (PB-3)
|
|
1117
|
-
if (meta.type.keepDiscriminatorProperty) {
|
|
1118
|
-
const fkJson = JSON.stringify(fieldKey);
|
|
1119
|
-
code += `{var __dh=${GEN.out}[${fkJson}]; if(__dh!=null) __dh[${discProp}]=${GEN.disc}${sk};}\n`;
|
|
1120
|
-
}
|
|
1121
|
-
}
|
|
1122
|
-
else {
|
|
1123
|
-
// §8.1 simple nested or §8.2 each array
|
|
1124
|
-
const nestedCls = meta.type.resolvedClass ?? meta.type.fn();
|
|
1125
|
-
const nestedSealed = getSealed(nestedCls);
|
|
1126
|
-
const execIdx = execs.length;
|
|
1127
|
-
execs.push(nestedSealed);
|
|
1128
|
-
// Check if validateNested each (array) — meta.type is already proven non-null above
|
|
1129
|
-
const hasEach = meta.type.isArray || meta.flags.validateNestedEach || meta.validation.some(rd => rd.each);
|
|
1130
|
-
if (hasEach) {
|
|
1131
|
-
const iVar = `${GEN.index}${sk}`;
|
|
1132
|
-
const awaitKwE = ctx.isAsync ? 'await ' : '';
|
|
1133
|
-
code += `if (Array.isArray(${varName})) {\n`;
|
|
1134
|
-
// Emit non-each array-level validation rules (e.g. @ArrayMinSize, @ArrayMaxSize)
|
|
1135
|
-
const nonEachRules = meta.validation.filter(rd => !rd.each);
|
|
1136
|
-
code += emitRuleList(fieldKey, varName, nonEachRules, emitCtx, ctx, ' ');
|
|
1137
|
-
code += ` var ${GEN.arr}${sk} = [];\n`;
|
|
1138
|
-
code += ` for (var ${iVar}=0; ${iVar}<${varName}.length; ${iVar}++) {\n`;
|
|
1139
|
-
code += ` var ${GEN.result}${sk} = ${awaitKwE}execs[${execIdx}].deserialize(${varName}[${iVar}], opts);\n`;
|
|
1140
|
-
code += ` if (isErr(${GEN.result}${sk})) {\n`;
|
|
1141
|
-
if (collectErrors) {
|
|
1142
|
-
code += ` var ${GEN.errors}${sk} = ${GEN.result}${sk}.data;\n`;
|
|
1143
|
-
code += ` var __bk$pp${sk} = ${JSON.stringify(fieldKey)}+'['+${iVar}+'].';\n`;
|
|
1144
|
-
code += ` for (var ${GEN.nestedIdx}${sk}=0; ${GEN.nestedIdx}${sk}<${GEN.errors}${sk}.length; ${GEN.nestedIdx}${sk}++) {\n`;
|
|
1145
|
-
code +=
|
|
1146
|
-
` ` +
|
|
1147
|
-
nestedErrPush(GEN.errList, `__bk$pp${sk}+${GEN.errors}${sk}[${GEN.nestedIdx}${sk}].path`, `${GEN.errors}${sk}[${GEN.nestedIdx}${sk}]`, `__ne${sk}`);
|
|
1148
|
-
code += ` }\n`;
|
|
1149
|
-
}
|
|
1150
|
-
else {
|
|
1151
|
-
code += ` var ${GEN.errors}${sk} = ${GEN.result}${sk}.data;\n`;
|
|
1152
|
-
code += ` var __bk$pp${sk} = ${JSON.stringify(fieldKey)}+'['+${iVar}+'].';\n`;
|
|
1153
|
-
code += ` ` + nestedErrReturn(`__bk$pp${sk}+${GEN.errors}${sk}[0].path`, `${GEN.errors}${sk}[0]`, `__ne${sk}`);
|
|
1154
|
-
}
|
|
1155
|
-
code += ` } else { ${GEN.arr}${sk}.push(${GEN.result}${sk}); }\n`;
|
|
1156
|
-
code += ` }\n`;
|
|
1157
|
-
code += ` ${GEN.out}[${JSON.stringify(fieldKey)}] = ${GEN.arr}${sk};\n`;
|
|
1158
|
-
code += `} else { ${emitCtx.fail('isArray')}; }\n`;
|
|
1159
|
-
}
|
|
1160
|
-
else {
|
|
1161
|
-
const awaitKwS = ctx.isAsync ? 'await ' : '';
|
|
1162
|
-
code += `if (${varName} != null && typeof ${varName} === 'object' && !Array.isArray(${varName})) {\n`;
|
|
1163
|
-
code += ` var ${GEN.result}${sk} = ${awaitKwS}execs[${execIdx}].deserialize(${varName}, opts);\n`;
|
|
1164
|
-
code += generateNestedResultCode(fieldKey, `${GEN.result}${sk}`, collectErrors, ctx.pathPrefix);
|
|
1165
|
-
code += `} else { ${emitCtx.fail('isObject')}; }\n`;
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
1168
|
-
return code;
|
|
1169
|
-
}
|
|
1170
|
-
function generateNestedResultCode(fieldKey, resultVar, collectErrors, pathPrefix) {
|
|
1171
|
-
const sk = sanitizeKey(fieldKey);
|
|
1172
|
-
// Prepend the current scope's path prefix so an executor reached from inside an inlined block
|
|
1173
|
-
// (e.g. a circular nested DTO) keeps the full path, not just `fieldKey.`.
|
|
1174
|
-
const ppValue = pathPrefix ? `${pathPrefix}+${JSON.stringify(fieldKey + '.')}` : JSON.stringify(fieldKey + '.');
|
|
1175
|
-
if (collectErrors) {
|
|
1176
|
-
const errItem = `${GEN.errors}${sk}[${GEN.nestedIdx}${sk}]`;
|
|
1177
|
-
return (` if (isErr(${resultVar})) {\n` +
|
|
1178
|
-
` var ${GEN.errors}${sk} = ${resultVar}.data;\n` +
|
|
1179
|
-
` var __bk$pp${sk} = ${ppValue};\n` +
|
|
1180
|
-
` for (var ${GEN.nestedIdx}${sk}=0; ${GEN.nestedIdx}${sk}<${GEN.errors}${sk}.length; ${GEN.nestedIdx}${sk}++) {\n` +
|
|
1181
|
-
` ` +
|
|
1182
|
-
nestedErrPush(GEN.errList, `__bk$pp${sk}+${errItem}.path`, errItem, `__ne${sk}`) +
|
|
1183
|
-
` }\n` +
|
|
1184
|
-
` } else { ${GEN.out}[${JSON.stringify(fieldKey)}] = ${resultVar}; }\n`);
|
|
1185
|
-
}
|
|
1186
|
-
const errFirst = `${GEN.errors}${sk}[0]`;
|
|
1187
|
-
return (` if (isErr(${resultVar})) {\n` +
|
|
1188
|
-
` var ${GEN.errors}${sk} = ${resultVar}.data;\n` +
|
|
1189
|
-
` var __bk$pp${sk} = ${ppValue};\n` +
|
|
1190
|
-
` ` +
|
|
1191
|
-
nestedErrReturn(`__bk$pp${sk}+${errFirst}.path`, errFirst, `__ne${sk}`) +
|
|
1192
|
-
` } else { ${GEN.out}[${JSON.stringify(fieldKey)}] = ${resultVar}; }\n`);
|
|
1193
|
-
}
|
|
1194
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
1195
|
-
// generateNestedCodeValidateOnly — validate-only nested (inline when possible)
|
|
1196
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
1197
|
-
// Inline-eligibility predicate: a nested DTO can be inlined unless it is already in the
|
|
1198
|
-
// active inline-set (circular reference). Inlined directly at the three call sites below
|
|
1199
|
-
// — no extra function call at seal time.
|
|
1200
|
-
/**
|
|
1201
|
-
* Emit inline validation code for all fields of a nested DTO.
|
|
1202
|
-
* Reuses generateFieldCode with modified ctx (pathPrefix, varPrefix, inputExpr).
|
|
1203
|
-
*/
|
|
1204
|
-
function emitInlineNestedBlock(nestedMerged, nestedClass, inputExpr, pathPrefixExpr, varPrefix, ctx) {
|
|
1205
|
-
const inlinedSet = ctx.inlineNestedClasses;
|
|
1206
|
-
inlinedSet.add(nestedClass);
|
|
1207
|
-
const inlineCtx = {
|
|
1208
|
-
...ctx,
|
|
1209
|
-
pathPrefix: pathPrefixExpr,
|
|
1210
|
-
varPrefix,
|
|
1211
|
-
inputExpr,
|
|
1212
|
-
exposeDefaultValues: false, // inline nested doesn't use exposeDefaultValues
|
|
1213
|
-
};
|
|
1214
|
-
let code = '';
|
|
1215
|
-
for (const [fieldKey, meta] of Object.entries(nestedMerged)) {
|
|
1216
|
-
code += generateFieldCode(fieldKey, meta, inlineCtx);
|
|
1217
|
-
}
|
|
1218
|
-
inlinedSet.delete(nestedClass);
|
|
1219
|
-
return code;
|
|
1220
|
-
}
|
|
1221
|
-
function generateNestedCodeValidateOnly(fieldKey, varName, meta, ctx, emitCtx) {
|
|
1222
|
-
const { collectErrors, execs } = ctx;
|
|
1223
|
-
if (!meta.type) {
|
|
1224
|
-
return '';
|
|
1225
|
-
}
|
|
1226
|
-
const sk = (ctx.varPrefix || '') + sanitizeKey(fieldKey);
|
|
1227
|
-
let code = '';
|
|
1228
|
-
// Initialize inline tracking set if not present
|
|
1229
|
-
if (!ctx.inlineNestedClasses) {
|
|
1230
|
-
ctx.inlineNestedClasses = new Set();
|
|
1231
|
-
}
|
|
1232
|
-
if (meta.type.discriminator) {
|
|
1233
|
-
// Discriminator — inline each subType's validation
|
|
1234
|
-
const discProp = JSON.stringify(meta.type.discriminator.property);
|
|
1235
|
-
code += `var ${GEN.disc}${sk} = ${varName} && ${varName}[${discProp}];\n`;
|
|
1236
|
-
code += `switch (${GEN.disc}${sk}) {\n`;
|
|
1237
|
-
for (const sub of meta.type.discriminator.subTypes) {
|
|
1238
|
-
const subSealed = getSealed(sub.value);
|
|
1239
|
-
const subMerged = subSealed.merged;
|
|
1240
|
-
const canInline = subMerged && !ctx.inlineNestedClasses.has(sub.value);
|
|
1241
|
-
code += ` case ${JSON.stringify(sub.name)}:\n`;
|
|
1242
|
-
if (canInline) {
|
|
1243
|
-
const ppExpr = ctx.pathPrefix ? `${ctx.pathPrefix}+${JSON.stringify(fieldKey + '.')}` : JSON.stringify(fieldKey + '.');
|
|
1244
|
-
const vpPrefix = `${sk}_d${sanitizeKey(sub.name)}_`;
|
|
1245
|
-
code += emitInlineNestedBlock(subMerged, sub.value, varName, ppExpr, vpPrefix, ctx);
|
|
1246
|
-
}
|
|
1247
|
-
else {
|
|
1248
|
-
const execIdx = execs.length;
|
|
1249
|
-
execs.push(subSealed);
|
|
1250
|
-
const awaitKw = ctx.isAsync ? 'await ' : '';
|
|
1251
|
-
code += ` var ${GEN.result}${sk} = ${awaitKw}execs[${execIdx}].validate(${varName}, opts);\n`;
|
|
1252
|
-
code += generateValidateNestedResult(fieldKey, `${GEN.result}${sk}`, collectErrors, ctx.pathPrefix);
|
|
1253
|
-
}
|
|
1254
|
-
code += ` break;\n`;
|
|
1255
|
-
}
|
|
1256
|
-
const validSubTypeNamesJsonV = JSON.stringify(meta.type.discriminator.subTypes.map(s => s.name));
|
|
1257
|
-
const discPathExprV = emitCtx.pathExpr ?? JSON.stringify(fieldKey);
|
|
1258
|
-
const discValueExprV = `${GEN.disc}${sk}`;
|
|
1259
|
-
if (collectErrors) {
|
|
1260
|
-
code += ` default: ${GEN.errList}.push({path:${discPathExprV},code:'invalidDiscriminator',context:{received:${discValueExprV},validSubTypes:${validSubTypeNamesJsonV}}});\n`;
|
|
1261
|
-
}
|
|
1262
|
-
else {
|
|
1263
|
-
code += ` default: return [{path:${discPathExprV},code:'invalidDiscriminator',context:{received:${discValueExprV},validSubTypes:${validSubTypeNamesJsonV}}}];\n`;
|
|
1264
|
-
}
|
|
1265
|
-
code += `}\n`;
|
|
1266
|
-
}
|
|
1267
|
-
else {
|
|
1268
|
-
const nestedCls = meta.type.resolvedClass ?? meta.type.fn();
|
|
1269
|
-
const nestedSealed = getSealed(nestedCls);
|
|
1270
|
-
const nestedMerged = nestedSealed.merged;
|
|
1271
|
-
const hasEach = meta.type.isArray || meta.flags.validateNestedEach || meta.validation.some(rd => rd.each);
|
|
1272
|
-
// Decide: inline or function call
|
|
1273
|
-
const useInline = nestedMerged && !ctx.inlineNestedClasses.has(nestedCls);
|
|
1274
|
-
if (hasEach) {
|
|
1275
|
-
const iVar = `${GEN.index}${sk}`;
|
|
1276
|
-
code += `if (Array.isArray(${varName})) {\n`;
|
|
1277
|
-
const nonEachRules = meta.validation.filter(rd => !rd.each);
|
|
1278
|
-
code += emitRuleList(fieldKey, varName, nonEachRules, emitCtx, ctx, ' ');
|
|
1279
|
-
code += ` for (var ${iVar}=0; ${iVar}<${varName}.length; ${iVar}++) {\n`;
|
|
1280
|
-
if (useInline) {
|
|
1281
|
-
// INLINE: generate validation code directly in the loop body.
|
|
1282
|
-
// Emit the per-iteration path as a single local var — both the invalidInput error
|
|
1283
|
-
// path and the nested block reference it, avoiding two identical 3-string concats.
|
|
1284
|
-
const itemVar = `__il$${sk}item`;
|
|
1285
|
-
const ppVar = `__bk$pp${sk}`;
|
|
1286
|
-
const ppExpr = ppVar;
|
|
1287
|
-
const ppInit = ctx.pathPrefix
|
|
1288
|
-
? `${ctx.pathPrefix}+${JSON.stringify(fieldKey)}+'['+${iVar}+'].'`
|
|
1289
|
-
: `${JSON.stringify(fieldKey)}+'['+${iVar}+'].'`;
|
|
1290
|
-
const vpPrefix = `${sk}i_`;
|
|
1291
|
-
code += ` var ${itemVar} = ${varName}[${iVar}];\n`;
|
|
1292
|
-
code += ` var ${ppVar} = ${ppInit};\n`;
|
|
1293
|
-
// Input type guard for the item — uses the cached prefix
|
|
1294
|
-
code += ` if (${itemVar} == null || typeof ${itemVar} !== 'object' || Array.isArray(${itemVar})) `;
|
|
1295
|
-
if (collectErrors) {
|
|
1296
|
-
code += `${GEN.errList}.push({path:${ppVar},code:'invalidInput'});\n`;
|
|
1297
|
-
}
|
|
1298
|
-
else {
|
|
1299
|
-
code += `return [{path:${ppVar},code:'invalidInput'}];\n`;
|
|
1300
|
-
}
|
|
1301
|
-
code += ` else {\n`;
|
|
1302
|
-
code += emitInlineNestedBlock(nestedMerged, nestedCls, itemVar, ppExpr, vpPrefix, ctx);
|
|
1303
|
-
code += ` }\n`;
|
|
1304
|
-
}
|
|
1305
|
-
else {
|
|
1306
|
-
// FALLBACK: function call to validate
|
|
1307
|
-
const execIdx = execs.length;
|
|
1308
|
-
execs.push(nestedSealed);
|
|
1309
|
-
const awaitKw = ctx.isAsync ? 'await ' : '';
|
|
1310
|
-
code += ` var ${GEN.result}${sk} = ${awaitKw}execs[${execIdx}].validate(${varName}[${iVar}], opts);\n`;
|
|
1311
|
-
code += ` if (${GEN.result}${sk} !== null) {\n`;
|
|
1312
|
-
const ppVar = `__bk$pp${sk}`;
|
|
1313
|
-
const ppInit = ctx.pathPrefix
|
|
1314
|
-
? `${ctx.pathPrefix}+${JSON.stringify(fieldKey)}+'['+${iVar}+'].'`
|
|
1315
|
-
: `${JSON.stringify(fieldKey)}+'['+${iVar}+'].'`;
|
|
1316
|
-
code += ` var ${ppVar} = ${ppInit};\n`;
|
|
1317
|
-
if (collectErrors) {
|
|
1318
|
-
code += ` for (var ${GEN.nestedIdx}${sk}=0; ${GEN.nestedIdx}${sk}<${GEN.result}${sk}.length; ${GEN.nestedIdx}${sk}++) {\n`;
|
|
1319
|
-
code +=
|
|
1320
|
-
` ` +
|
|
1321
|
-
nestedErrPush(GEN.errList, `${ppVar}+${GEN.result}${sk}[${GEN.nestedIdx}${sk}].path`, `${GEN.result}${sk}[${GEN.nestedIdx}${sk}]`, `__ne${sk}`);
|
|
1322
|
-
code += ` }\n`;
|
|
1323
|
-
}
|
|
1324
|
-
else {
|
|
1325
|
-
code += ` ` + nestedErrReturn(`${ppVar}+${GEN.result}${sk}[0].path`, `${GEN.result}${sk}[0]`, `__ne${sk}`, true);
|
|
1326
|
-
}
|
|
1327
|
-
code += ` }\n`;
|
|
1328
|
-
}
|
|
1329
|
-
code += ` }\n`;
|
|
1330
|
-
code += `} else { ${emitCtx.fail('isArray')}; }\n`;
|
|
1331
|
-
}
|
|
1332
|
-
else {
|
|
1333
|
-
// Single nested object — arrays are objects by `typeof` but are not valid nested DTOs;
|
|
1334
|
-
// reject them here (matching the deserialize path) instead of descending into their fields.
|
|
1335
|
-
code += `if (${varName} != null && typeof ${varName} === 'object' && !Array.isArray(${varName})) {\n`;
|
|
1336
|
-
if (useInline) {
|
|
1337
|
-
const ppExpr = ctx.pathPrefix ? `${ctx.pathPrefix}+${JSON.stringify(fieldKey + '.')}` : JSON.stringify(fieldKey + '.');
|
|
1338
|
-
const vpPrefix = `${sk}_`;
|
|
1339
|
-
code += emitInlineNestedBlock(nestedMerged, nestedCls, varName, ppExpr, vpPrefix, ctx);
|
|
1340
|
-
}
|
|
1341
|
-
else {
|
|
1342
|
-
const execIdx = execs.length;
|
|
1343
|
-
execs.push(nestedSealed);
|
|
1344
|
-
const awaitKw = ctx.isAsync ? 'await ' : '';
|
|
1345
|
-
code += ` var ${GEN.result}${sk} = ${awaitKw}execs[${execIdx}].validate(${varName}, opts);\n`;
|
|
1346
|
-
code += generateValidateNestedResult(fieldKey, `${GEN.result}${sk}`, collectErrors, ctx.pathPrefix);
|
|
1347
|
-
}
|
|
1348
|
-
code += `} else { ${emitCtx.fail('isObject')}; }\n`;
|
|
1349
|
-
}
|
|
1350
|
-
}
|
|
1351
|
-
return code;
|
|
1352
|
-
}
|
|
1353
|
-
/** Generate validate-mode nested result handling (null check instead of isErr) */
|
|
1354
|
-
function generateValidateNestedResult(fieldKey, resultVar, collectErrors, pathPrefix) {
|
|
1355
|
-
const sk = sanitizeKey(fieldKey);
|
|
1356
|
-
const ppVar = `__bk$pp${sk}`;
|
|
1357
|
-
// Prepend the current scope's path prefix (see generateNestedResultCode).
|
|
1358
|
-
const ppValue = pathPrefix ? `${pathPrefix}+${JSON.stringify(fieldKey + '.')}` : JSON.stringify(fieldKey + '.');
|
|
1359
|
-
if (collectErrors) {
|
|
1360
|
-
const errItem = `${resultVar}[${GEN.nestedIdx}${sk}]`;
|
|
1361
|
-
return (` if (${resultVar} !== null) {\n` +
|
|
1362
|
-
` var ${ppVar} = ${ppValue};\n` +
|
|
1363
|
-
` for (var ${GEN.nestedIdx}${sk}=0; ${GEN.nestedIdx}${sk}<${resultVar}.length; ${GEN.nestedIdx}${sk}++) {\n` +
|
|
1364
|
-
` ` +
|
|
1365
|
-
nestedErrPush(GEN.errList, `${ppVar}+${errItem}.path`, errItem, `__ne${sk}`) +
|
|
1366
|
-
` }\n` +
|
|
1367
|
-
` }\n`);
|
|
1368
|
-
}
|
|
1369
|
-
const errFirst = `${resultVar}[0]`;
|
|
1370
|
-
return (` if (${resultVar} !== null) {\n` +
|
|
1371
|
-
` var ${ppVar} = ${ppValue};\n` +
|
|
1372
|
-
` ` +
|
|
1373
|
-
nestedErrReturn(`${ppVar}+${errFirst}.path`, errFirst, `__ne${sk}`, true) +
|
|
1374
|
-
` }\n`);
|
|
1375
|
-
}
|
|
1376
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
1377
|
-
// generateCollectionCodeValidateOnly — validate-only collection (no Set/Map creation)
|
|
1378
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
1379
|
-
function generateCollectionCodeValidateOnly(fieldKey, varName, meta, ctx, emitCtx) {
|
|
1380
|
-
const { collectErrors, execs } = ctx;
|
|
1381
|
-
const sk = (ctx.varPrefix || '') + sanitizeKey(fieldKey);
|
|
1382
|
-
const collection = meta.type.collection;
|
|
1383
|
-
const awaitKw = ctx.isAsync ? 'await ' : '';
|
|
1384
|
-
if (!ctx.inlineNestedClasses) {
|
|
1385
|
-
ctx.inlineNestedClasses = new Set();
|
|
1386
|
-
}
|
|
1387
|
-
// Resolve nested DTO for collection values
|
|
1388
|
-
let nestedCls;
|
|
1389
|
-
let nestedSealed;
|
|
1390
|
-
let nestedMerged;
|
|
1391
|
-
if (meta.type.resolvedCollectionValue) {
|
|
1392
|
-
nestedCls = meta.type.resolvedCollectionValue;
|
|
1393
|
-
nestedSealed = getSealed(nestedCls);
|
|
1394
|
-
nestedMerged = nestedSealed.merged;
|
|
1395
|
-
}
|
|
1396
|
-
const useInline = nestedCls && nestedMerged && !ctx.inlineNestedClasses.has(nestedCls);
|
|
1397
|
-
let code = '';
|
|
1398
|
-
if (collection === 'Set') {
|
|
1399
|
-
code += `if (Array.isArray(${varName})) {\n`;
|
|
1400
|
-
const nonEachRules = meta.validation.filter(rd => !rd.each);
|
|
1401
|
-
code += emitRuleList(fieldKey, varName, nonEachRules, emitCtx, ctx, ' ');
|
|
1402
|
-
if (nestedSealed) {
|
|
1403
|
-
const iVar = `${GEN.index}${sk}`;
|
|
1404
|
-
code += ` for (var ${iVar}=0; ${iVar}<${varName}.length; ${iVar}++) {\n`;
|
|
1405
|
-
if (useInline) {
|
|
1406
|
-
// Cache per-iteration path prefix into a single local var — itemInvalidPathExpr was
|
|
1407
|
-
// identical to ppExpr (two copies of the same 3-string concat in the emitted body).
|
|
1408
|
-
const itemVar = `__il$${sk}ci`;
|
|
1409
|
-
const ppVar = `__bk$pp${sk}`;
|
|
1410
|
-
const ppInit = ctx.pathPrefix
|
|
1411
|
-
? `${ctx.pathPrefix}+${JSON.stringify(fieldKey)}+'['+${iVar}+'].'`
|
|
1412
|
-
: `${JSON.stringify(fieldKey)}+'['+${iVar}+'].'`;
|
|
1413
|
-
const vpPrefix = `${sk}c_`;
|
|
1414
|
-
code += ` var ${itemVar} = ${varName}[${iVar}];\n`;
|
|
1415
|
-
code += ` var ${ppVar} = ${ppInit};\n`;
|
|
1416
|
-
code += ` if (${itemVar} == null || typeof ${itemVar} !== 'object' || Array.isArray(${itemVar})) `;
|
|
1417
|
-
if (collectErrors) {
|
|
1418
|
-
code += `${GEN.errList}.push({path:${ppVar},code:'invalidInput'});\n`;
|
|
1419
|
-
}
|
|
1420
|
-
else {
|
|
1421
|
-
code += `return [{path:${ppVar},code:'invalidInput'}];\n`;
|
|
1422
|
-
}
|
|
1423
|
-
code += ` else {\n`;
|
|
1424
|
-
code += emitInlineNestedBlock(nestedMerged, nestedCls, itemVar, ppVar, vpPrefix, ctx);
|
|
1425
|
-
code += ` }\n`;
|
|
1426
|
-
}
|
|
1427
|
-
else {
|
|
1428
|
-
const execIdx = execs.length;
|
|
1429
|
-
execs.push(nestedSealed);
|
|
1430
|
-
code += ` var ${GEN.result}${sk} = ${awaitKw}execs[${execIdx}].validate(${varName}[${iVar}], opts);\n`;
|
|
1431
|
-
code += ` if (${GEN.result}${sk} !== null) {\n`;
|
|
1432
|
-
const ppVar = `__bk$pp${sk}`;
|
|
1433
|
-
const ppInit = ctx.pathPrefix
|
|
1434
|
-
? `${ctx.pathPrefix}+${JSON.stringify(fieldKey)}+'['+${iVar}+'].'`
|
|
1435
|
-
: `${JSON.stringify(fieldKey)}+'['+${iVar}+'].'`;
|
|
1436
|
-
code += ` var ${ppVar} = ${ppInit};\n`;
|
|
1437
|
-
if (collectErrors) {
|
|
1438
|
-
code += ` for (var ${GEN.nestedIdx}${sk}=0; ${GEN.nestedIdx}${sk}<${GEN.result}${sk}.length; ${GEN.nestedIdx}${sk}++) {\n`;
|
|
1439
|
-
code +=
|
|
1440
|
-
` ` +
|
|
1441
|
-
nestedErrPush(GEN.errList, `${ppVar}+${GEN.result}${sk}[${GEN.nestedIdx}${sk}].path`, `${GEN.result}${sk}[${GEN.nestedIdx}${sk}]`, `__ne${sk}`);
|
|
1442
|
-
code += ` }\n`;
|
|
1443
|
-
}
|
|
1444
|
-
else {
|
|
1445
|
-
code += ` ` + nestedErrReturn(`${ppVar}+${GEN.result}${sk}[0].path`, `${GEN.result}${sk}[0]`, `__ne${sk}`, true);
|
|
1446
|
-
}
|
|
1447
|
-
code += ` }\n`;
|
|
1448
|
-
}
|
|
1449
|
-
code += ` }\n`;
|
|
1450
|
-
}
|
|
1451
|
-
// each validation — iterate input array directly
|
|
1452
|
-
const eachRules = meta.validation.filter(rd => rd.each);
|
|
1453
|
-
if (eachRules.length > 0) {
|
|
1454
|
-
const eiVar = `${GEN.index}${sk}e`;
|
|
1455
|
-
code += ` for (var ${eiVar}=0; ${eiVar}<${varName}.length; ${eiVar}++) {\n`;
|
|
1456
|
-
for (const rd of eachRules) {
|
|
1457
|
-
const prefixVar = `__bk$ep_${sk}`;
|
|
1458
|
-
const extra = computeRuleExtras(rd, fieldKey, varName, ctx);
|
|
1459
|
-
const failFn = (c) => collectErrors
|
|
1460
|
-
? `${GEN.errList}.push({path:${prefixVar}+${eiVar}+']',code:${JSON.stringify(c)}${extra}})`
|
|
1461
|
-
: `return [{path:${prefixVar}+${eiVar}+']',code:${JSON.stringify(c)}${extra}}]`;
|
|
1462
|
-
const colEmitCtx = { ...emitCtx, fail: failFn };
|
|
1463
|
-
if (!code.includes(`var ${prefixVar}`)) {
|
|
1464
|
-
const prefixInit = ctx.pathPrefix
|
|
1465
|
-
? `${ctx.pathPrefix}+${JSON.stringify(fieldKey)}+'['`
|
|
1466
|
-
: `${JSON.stringify(fieldKey)}+'['`;
|
|
1467
|
-
code += ` var ${prefixVar} = ${prefixInit};\n`;
|
|
1468
|
-
}
|
|
1469
|
-
code += ` ${rd.rule.emit(`${varName}[${eiVar}]`, colEmitCtx)}\n`;
|
|
1470
|
-
}
|
|
1471
|
-
code += ` }\n`;
|
|
1472
|
-
}
|
|
1473
|
-
code += `} else { ${emitCtx.fail('isArray')}; }\n`;
|
|
1474
|
-
}
|
|
1475
|
-
else {
|
|
1476
|
-
// Map: validate object values
|
|
1477
|
-
code += `if (${varName} != null && typeof ${varName} === 'object' && !Array.isArray(${varName})) {\n`;
|
|
1478
|
-
if (nestedSealed) {
|
|
1479
|
-
const kVar = `${GEN.key}${sk}`;
|
|
1480
|
-
const ksVar = `__bk$vk${sk}`;
|
|
1481
|
-
const iVar = `__bk$vi${sk}`;
|
|
1482
|
-
code += ` var ${ksVar} = Object.keys(${varName});\n`;
|
|
1483
|
-
code += ` for (var ${iVar}=0; ${iVar}<${ksVar}.length; ${iVar}++) {\n`;
|
|
1484
|
-
code += ` var ${kVar} = ${ksVar}[${iVar}];\n`;
|
|
1485
|
-
if (useInline) {
|
|
1486
|
-
const itemVar = `__il$${sk}mi`;
|
|
1487
|
-
const ppExpr = ctx.pathPrefix
|
|
1488
|
-
? `${ctx.pathPrefix}+${JSON.stringify(fieldKey)}+'['+${kVar}+'].'`
|
|
1489
|
-
: `${JSON.stringify(fieldKey)}+'['+${kVar}+'].'`;
|
|
1490
|
-
const vpPrefix = `${sk}m_`;
|
|
1491
|
-
const itemInvalidPathExpr = ppExpr;
|
|
1492
|
-
code += ` var ${itemVar} = ${varName}[${kVar}];\n`;
|
|
1493
|
-
code += ` if (${itemVar} == null || typeof ${itemVar} !== 'object' || Array.isArray(${itemVar})) `;
|
|
1494
|
-
if (collectErrors) {
|
|
1495
|
-
code += `${GEN.errList}.push({path:${itemInvalidPathExpr},code:'invalidInput'});\n`;
|
|
1496
|
-
}
|
|
1497
|
-
else {
|
|
1498
|
-
code += `return [{path:${itemInvalidPathExpr},code:'invalidInput'}];\n`;
|
|
1499
|
-
}
|
|
1500
|
-
code += ` else {\n`;
|
|
1501
|
-
code += emitInlineNestedBlock(nestedMerged, nestedCls, itemVar, ppExpr, vpPrefix, ctx);
|
|
1502
|
-
code += ` }\n`;
|
|
1503
|
-
}
|
|
1504
|
-
else {
|
|
1505
|
-
const execIdx = execs.length;
|
|
1506
|
-
execs.push(nestedSealed);
|
|
1507
|
-
code += ` var ${GEN.result}${sk} = ${awaitKw}execs[${execIdx}].validate(${varName}[${kVar}], opts);\n`;
|
|
1508
|
-
code += ` if (${GEN.result}${sk} !== null) {\n`;
|
|
1509
|
-
const ppVar = `__bk$pp${sk}`;
|
|
1510
|
-
const ppInit = ctx.pathPrefix
|
|
1511
|
-
? `${ctx.pathPrefix}+${JSON.stringify(fieldKey)}+'['+${kVar}+'].'`
|
|
1512
|
-
: `${JSON.stringify(fieldKey)}+'['+${kVar}+'].'`;
|
|
1513
|
-
code += ` var ${ppVar} = ${ppInit};\n`;
|
|
1514
|
-
if (collectErrors) {
|
|
1515
|
-
code += ` for (var ${GEN.nestedIdx}${sk}=0; ${GEN.nestedIdx}${sk}<${GEN.result}${sk}.length; ${GEN.nestedIdx}${sk}++) {\n`;
|
|
1516
|
-
code +=
|
|
1517
|
-
` ` +
|
|
1518
|
-
nestedErrPush(GEN.errList, `${ppVar}+${GEN.result}${sk}[${GEN.nestedIdx}${sk}].path`, `${GEN.result}${sk}[${GEN.nestedIdx}${sk}]`, `__ne${sk}`);
|
|
1519
|
-
code += ` }\n`;
|
|
1520
|
-
}
|
|
1521
|
-
else {
|
|
1522
|
-
code += ` ` + nestedErrReturn(`${ppVar}+${GEN.result}${sk}[0].path`, `${GEN.result}${sk}[0]`, `__ne${sk}`, true);
|
|
1523
|
-
}
|
|
1524
|
-
code += ` }\n`;
|
|
1525
|
-
}
|
|
1526
|
-
code += ` }\n`;
|
|
1527
|
-
}
|
|
1528
|
-
code += `} else { ${emitCtx.fail('isObject')}; }\n`;
|
|
1529
|
-
}
|
|
1530
|
-
return code;
|
|
1531
|
-
}
|
|
1532
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
1533
|
-
// makeEmitCtx — create per-field EmitContext
|
|
1534
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
1535
|
-
function makeEmitCtx(fieldKey, ctx, fieldExtras = '') {
|
|
1536
|
-
const { collectErrors, regexes, refs, execs, validateOnly, pathPrefix } = ctx;
|
|
1537
|
-
const pathExpr = pathPrefix ? `${pathPrefix}+${JSON.stringify(fieldKey)}` : JSON.stringify(fieldKey);
|
|
1538
|
-
return {
|
|
1539
|
-
addRegex(re) {
|
|
1540
|
-
regexes.push(re);
|
|
1541
|
-
return regexes.length - 1;
|
|
1542
|
-
},
|
|
1543
|
-
addRef(fn) {
|
|
1544
|
-
refs.push(fn);
|
|
1545
|
-
return refs.length - 1;
|
|
1546
|
-
},
|
|
1547
|
-
addExecutor(executor) {
|
|
1548
|
-
execs.push(executor);
|
|
1549
|
-
return execs.length - 1;
|
|
1550
|
-
},
|
|
1551
|
-
fail(code) {
|
|
1552
|
-
if (collectErrors) {
|
|
1553
|
-
return `${GEN.errList}.push({path:${pathExpr},code:${JSON.stringify(code)}${fieldExtras}})`;
|
|
1554
|
-
}
|
|
1555
|
-
else if (validateOnly) {
|
|
1556
|
-
return `return [{path:${pathExpr},code:${JSON.stringify(code)}${fieldExtras}}]`;
|
|
1557
|
-
}
|
|
1558
|
-
return `return err([{path:${pathExpr},code:${JSON.stringify(code)}${fieldExtras}}])`;
|
|
1559
|
-
},
|
|
1560
|
-
collectErrors,
|
|
1561
|
-
pathExpr: pathExpr,
|
|
1562
|
-
};
|
|
1563
|
-
}
|
|
1564
|
-
export { buildDeserializeCode, buildValidateCode };
|
|
1
|
+
import{err as c,isErr as d}from"@zipbul/result";import{BakerError as N}from"../errors.js";import{getSealed as u}from"../meta-access.js";import{emitRulePlan as t}from"../rule-plan.js";import{sanitizeKey as G,buildGroupsHasExpr as n}from"./codegen-utils.js";const X={field:"__bk$f_",index:"__bk$i_",setIdx:"__bk$si_",setVal:"__bk$sv_",mapIdx:"__bk$mi_",mapVal:"__bk$mv_",mark:"__bk$mark_",skip:"__bk$skip_",result:"__bk$r_",errors:"__bk$re_",arr:"__bk$arr_",disc:"__bk$dt_",nestedIdx:"__bk$j_",out:"__bk$out",errList:"__bk$errors",groups:"__bk$groups",group0:"__bk$group0",groupsSet:"__bk$groupsSet",key:"__bk$k"};function I(Z,W,U,$){const _=`${$}_e`;return`var ${_}=${U};
|
|
2
|
+
if(${_}.message===undefined&&${_}.context===undefined){${Z}.push({path:${W},code:${_}.code});}
|
|
3
|
+
else{var ${$}={path:${W},code:${_}.code};
|
|
4
|
+
if(${_}.message!==undefined)${$}.message=${_}.message;
|
|
5
|
+
if(${_}.context!==undefined)${$}.context=${_}.context;
|
|
6
|
+
${Z}.push(${$});}
|
|
7
|
+
`}function g(Z,W,U,$){const _=(j)=>$?`return ${j};
|
|
8
|
+
`:`return err(${j});
|
|
9
|
+
`;return`if(${W}.message===undefined&&${W}.context===undefined)${_(`[{path:${Z},code:${W}.code}]`)} var ${U}={path:${Z},code:${W}.code};
|
|
10
|
+
if(${W}.message!==undefined)${U}.message=${W}.message;
|
|
11
|
+
if(${W}.context!==undefined)${U}.context=${W}.context;
|
|
12
|
+
${_(`[${U}]`)}`}function r(Z,W){return X.field+(W||"")+G(Z)}function x(Z,W){const U=W.find((_)=>_.deserializeOnly&&_.name);if(U)return U.name;const $=W.find((_)=>!_.deserializeOnly&&!_.serializeOnly&&_.name);if($)return $.name;return Z}function m(Z){let W=null;for(const U of Z){if(U.serializeOnly)continue;if(!U.groups||U.groups.length===0)return;if(W===null)W=new Set;for(const $ of U.groups)W.add($)}return W===null?void 0:[...W]}function buildDeserializeCode(Z,W,U,$,_,j=!1){const B=U?.stopAtFirstError??!1,Q=!B,q=U?.exposeDefaultValues??!1,z=[],F=[],H=[],D=j?(M)=>M:(M)=>`err(${M})`;let L=`'use strict';
|
|
13
|
+
`;if(j){if(q)L+=`var __bk$defs = new _Cls();
|
|
14
|
+
`}else L+=q?`var ${X.out} = new _Cls();
|
|
15
|
+
`:`var ${X.out} = Object.create(_Cls.prototype);
|
|
16
|
+
`;if(Q)L+=`var ${X.errList} = [];
|
|
17
|
+
`;L+=`if (input == null || typeof input !== 'object' || Array.isArray(input)) return ${D("[{path:'',code:'invalidInput'}]")};
|
|
18
|
+
`;if($){L+=`var __seen = (opts && opts[__SEEN_KEY]) || null;
|
|
19
|
+
`;L+=`if (__seen === null) { __seen = new WeakSet(); opts = opts ? { ...opts, [__SEEN_KEY]: __seen } : { [__SEEN_KEY]: __seen }; }
|
|
20
|
+
`;L+=`if (__seen.has(input)) return ${D("[{path:'',code:'circular'}]")};
|
|
21
|
+
`;L+=`__seen.add(input);
|
|
22
|
+
`;L+=`try {
|
|
23
|
+
`}if(U?.whitelist){const M=new Set;for(const[S,R]of Object.entries(W)){const T=x(S,R.expose);M.add(T)}const w=F.length;F.push(M);if(Q)L+=`{var __wlk=Object.keys(input);for(var __wli=0;__wli<__wlk.length;__wli++){var ${X.key}=__wlk[__wli];if(!refs[${w}].has(${X.key}))${X.errList}.push({path:${X.key},code:'whitelistViolation'});}}
|
|
24
|
+
`;else L+=`{var __wlk=Object.keys(input);for(var __wli=0;__wli<__wlk.length;__wli++){var ${X.key}=__wlk[__wli];if(!refs[${w}].has(${X.key}))return ${D(`[{path:${X.key},code:'whitelistViolation'}]`)};}}
|
|
25
|
+
`}let Y=!1;for(const M in W){const w=W[M],S=m(w.expose);if(S&&S.length>0){Y=!0;break}let R=!1;for(const T of w.validation)if(T.groups&&T.groups.length>0){R=!0;break}if(R){Y=!0;break}}if(Y){L+=`var ${X.groups} = opts && opts.groups;
|
|
26
|
+
`;L+=`var ${X.group0} = ${X.groups} && ${X.groups}.length === 1 ? ${X.groups}[0] : null;
|
|
27
|
+
`;L+=`var ${X.groupsSet} = ${X.groups} && ${X.groups}.length > 1 ? new Set(${X.groups}) : null;
|
|
28
|
+
`}for(const[M,w]of Object.entries(W)){const S=o(M,w,{stopAtFirstError:B,collectErrors:Q,exposeDefaultValues:q,isAsync:_,regexes:z,refs:F,execs:H,options:U,validateOnly:j});L+=S}if(Q)L+=`if (${X.errList}.length) return ${j?X.errList:`err(${X.errList})`};
|
|
29
|
+
`;L+=`return ${j?"null":X.out};
|
|
30
|
+
`;if($)L+=`} finally { __seen.delete(input); }
|
|
31
|
+
`;const A=Z.name.replace(/[^\w$.-]/g,"_");L+=`//# sourceURL=baker://${A}/${j?"validate":"deserialize"}
|
|
32
|
+
`;const O=_?"async function":"function",J=Symbol.for("baker:circular-seen");return Function("_Cls","re","refs","execs","err","isErr","__SEEN_KEY",`return ${O}(input, opts) { `+L+" }")(Z,z,F,H,c,d,J)}function buildValidateCode(Z,W,U,$,_){return buildDeserializeCode(Z,W,U,$,_,!0)}function a(Z,W,U){if(Z&&W)return"nullable+optional";if(Z)return"nullable";if(U)return"defined";if(W)return"optional";return"default"}const e={"nullable+optional"({varName:Z,assignNull:W,validationCode:U}){let $=`if (${Z} === null) { ${W}}
|
|
33
|
+
`;$+=`else if (${Z} !== undefined) {
|
|
34
|
+
`;$+=U;$+=`}
|
|
35
|
+
`;return $},nullable({varName:Z,emitCtx:W,assignNull:U,validationCode:$}){let _=`if (${Z} === undefined) ${W.fail("isDefined")};
|
|
36
|
+
`;_+=`else if (${Z} !== null) {
|
|
37
|
+
`;_+=$;_+=`} else { ${U}}
|
|
38
|
+
`;return _},defined({varName:Z,emitCtx:W,validationCode:U}){let $=`if (${Z} === undefined) ${W.fail("isDefined")};
|
|
39
|
+
`;$+=U;return $},optional({varName:Z,validationCode:W}){let U=`if (${Z} !== undefined && ${Z} !== null) {
|
|
40
|
+
`;U+=W;U+=`}
|
|
41
|
+
`;return U},default({varName:Z,emitCtx:W,validationCode:U}){let $=`if (${Z} === undefined || ${Z} === null) ${W.fail("isDefined")};
|
|
42
|
+
`;$+=`else {
|
|
43
|
+
`;$+=U;$+=`}
|
|
44
|
+
`;return $}};function o(Z,W,U){const{exposeDefaultValues:$}=U;if(W.exclude){if(!W.exclude.serializeOnly){if(U.options?.debug){const R=W.exclude.deserializeOnly?"deserializeOnly":"bidirectional";return`// [baker] field ${JSON.stringify(Z)} excluded (${R} @Exclude)
|
|
45
|
+
`}return""}}if(W.expose.length>0&&W.expose.every((R)=>R.serializeOnly)){if(U.options?.debug)return`// [baker] field ${JSON.stringify(Z)} excluded (all @Expose entries are serializeOnly)
|
|
46
|
+
`;return""}const _=r(Z,U.varPrefix),j=x(Z,W.expose),B=m(W.expose),Q=U.inputExpr||"input",q=WQ(W,Z,_,U),z=JQ(Z,U,q);let F="",H=null;if(W.flags.validateIf){H=U.refs.length;U.refs.push(W.flags.validateIf)}let D;const L=JSON.stringify(j);if($&&!W.flags.isOptional){const R=U.validateOnly?"__bk$defs":X.out;D=`var ${_} = Object.hasOwn(${Q}, ${L}) ? ${Q}[${L}] : ${R}[${JSON.stringify(Z)}];
|
|
47
|
+
`}else D=`var ${_} = ${Q}[${L}];
|
|
48
|
+
`;let Y="",A="";if(B&&B.length>0){Y=`if ((${X.group0} !== null || ${X.groupsSet}) && (${n(X.group0,X.groupsSet,B)})) {
|
|
49
|
+
`;A=`}
|
|
50
|
+
`}let O=D;const J=!!(W.flags.isOptional&&!W.flags.isDefined),P=W.flags.isNullable===!0,M=QQ(Z,_,W,U,z,B),w=U.validateOnly?"":`${X.out}[${JSON.stringify(Z)}] = null;
|
|
51
|
+
`,S=a(P,J,W.flags.isDefined??!1);O+=e[S]({varName:_,emitCtx:z,assignNull:w,validationCode:M});if(H!==null)F+=Y+`if (refs[${H}](${Q})) {
|
|
52
|
+
`+O+`}
|
|
53
|
+
`+A;else F+=Y+O+A;return F}function QQ(Z,W,U,$,_,j){const{collectErrors:B}=$;let Q="";const q=U.transform.filter((z)=>!z.options?.serializeOnly);if(q.length>0){const z=JSON.stringify(Z),F=$.inputExpr||"input";if(q.length===1){const H=q[0],D=$.refs.length;$.refs.push(H.fn);const L=`refs[${D}]({value:${W},key:${z},obj:${F}})`;Q+=`${W} = ${H.isAsync?"await ":""}${L};
|
|
54
|
+
`}else if(q.length===2){const H=q[0],D=q[1],L=$.refs.length;$.refs.push(H.fn);const Y=$.refs.length;$.refs.push(D.fn);const A=`refs[${L}]({value:${W},key:${z},obj:${F}})`,O=H.isAsync?`await ${A}`:A,J=`refs[${Y}]({value:${O},key:${z},obj:${F}})`;Q+=`${W} = ${D.isAsync?"await ":""}${J};
|
|
55
|
+
`}else for(const H of q){const D=$.refs.length;$.refs.push(H.fn);const L=`refs[${D}]({value:${W},key:${z},obj:${F}})`;Q+=`${W} = ${H.isAsync?"await ":""}${L};
|
|
56
|
+
`}}if(U.type?.collection){Q+=$.validateOnly?FQ(Z,W,U,$,_):jQ(Z,W,U,$,_);return Q}if(U.flags.validateNested&&U.type?.fn){Q+=$.validateOnly?BQ(Z,W,U,$,_):zQ(Z,W,U,$,_);return Q}if(U.validation.length===0){if(!$.validateOnly)Q+=`${X.out}[${JSON.stringify(Z)}] = ${W};
|
|
57
|
+
`;return Q}Q+=LQ(Z,W,U.validation,B,_,$,U,j);return Q}function l(Z,W,U,$,_,j){let B="";if(typeof Z==="string")B+=`,message:${JSON.stringify(Z)}`;else if(typeof Z==="function"){const Q=j.refs.length;j.refs.push(Z);const q=U();B+=`,message:refs[${Q}]({property:${JSON.stringify($)},value:${_},constraints:${q}})`}if(W!==void 0){const Q=j.refs.length;j.refs.push(W);B+=`,context:refs[${Q}]`}return B}function V(Z,W,U,$){return l(Z.message,Z.context,()=>{const _=$.refs.length;$.refs.push(Z.rule.constraints??{});return`refs[${_}]`},W,U,$)}function WQ(Z,W,U,$){return l(Z.message,Z.context,()=>"{}",W,U,$)}function s(Z,W,U,$,_){const j=V($,W,U,_);if(!j)return Z;const B=Z.pathExpr??JSON.stringify(W);return{...Z,fail(Q){if(Z.collectErrors)return`${X.errList}.push({path:${B},code:${JSON.stringify(Q)}${j}})`;else if(_.validateOnly)return`return [{path:${B},code:${JSON.stringify(Q)}${j}}]`;return`return err([{path:${B},code:${JSON.stringify(Q)}${j}}])`}}}function h(Z,W,U,$,_,j,B,Q){let q="",z=0,F=0;for(const Y of U){if(!k(Y.groups,B))continue;if(Y.rule.plan?.cacheKey==="length")z+=1;else if(Y.rule.plan?.cacheKey==="time")F+=1}const H=G(Z),D=z>1?`${X.arr}${H}len`:null,L=F>1?`${X.arr}${H}time`:null;if(D)q+=`${j}var ${D} = ${W}.length;
|
|
58
|
+
`;if(L)q+=`${j}var ${L} = ${W}.getTime();
|
|
59
|
+
`;for(const Y of U){const A=k(Y.groups,B),O=s($,Z,W,Y,_),J=Q?{...O,insideTypeGate:!0}:O;let P;if(A&&Y.rule.plan&&(D||L)){const w={};if(Y.rule.plan.cacheKey==="length"&&D)w.length=D;if(Y.rule.plan.cacheKey==="time"&&L)w.time=L;P=t(W,J,Y.rule.ruleName,Y.rule.plan,w,Q)}else P=Y.rule.emit(W,J);if(!P)continue;const M=A?P:XQ(Y,P);q+=j+M.replace(/\n/g,`
|
|
60
|
+
`+j)+`
|
|
61
|
+
`}return q}function XQ(Z,W){if(!Z.groups||Z.groups.length===0)return W;return`if ((${X.group0} === null && !${X.groupsSet}) || ${n(X.group0,X.groupsSet,Z.groups)}) {
|
|
62
|
+
${W}
|
|
63
|
+
}
|
|
64
|
+
`}function k(Z,W){if(!Z||Z.length===0)return!W||W.length===0;if(!W||Z.length!==W.length)return!1;for(let U=0;U<Z.length;U++)if(Z[U]!==W[U])return!1;return!0}function y(Z,W,U,$,_,j){const B=_?`${j.fail("conversionFailed")}; ${$} = true;`:j.fail("conversionFailed")+";";switch(Z){case"string":return` ${W} = String(${W});
|
|
65
|
+
`;case"number":return` ${W} = Number(${W});
|
|
66
|
+
if (isNaN(${W})) { ${B} }
|
|
67
|
+
`;case"boolean":return` if (${W} === 'true' || ${W} === '1' || ${W} === 1) ${W} = true;
|
|
68
|
+
else if (${W} === 'false' || ${W} === '0' || ${W} === 0) ${W} = false;
|
|
69
|
+
else { ${B} }
|
|
70
|
+
`;case"date":return` ${W} = new Date(${W});
|
|
71
|
+
if (isNaN(${W}.getTime())) { ${B} }
|
|
72
|
+
`;default:throw new N(`Unknown implicit conversion type: "${Z}" for field "${U}"`)}}const ZQ={Number:"number",Boolean:"boolean",String:"string",Date:"date"};const p={isString:"string",isNumber:"number",isBoolean:"boolean",isDate:"date",isInt:"number",isArray:"array",isObject:"object"};const $Q=new Set(["isString","isBoolean","isDate","isArray","isObject"]);function UQ(Z,W){const U=[],$=[],_={string:[],number:[],boolean:[],date:[],array:[],object:[]};for(const Q of W){if(Q.each){U.push(Q);continue}const q=Q.rule.requiresType;if(q!==void 0)_[q].push(Q);else $.push(Q)}let j=void 0,B=null;for(const Q of["string","number","boolean","date","array","object"]){const q=_[Q];if(q.length===0)continue;if(j){if(B===null)B=[j.type];B.push(Q)}else j={type:Q,deps:q}}if(B)throw new N(`Field "${Z}" has conflicting requiresType: ${B.join(", ")}`);return{each:U,generalRules:$,typedDeps:j}}function YQ(Z,W,U,$){const{generalRules:_,typedDeps:j}=W,B=!!j,Q=j?.type??null,q=j?.deps??[];let z=-1;if(Q)z=_.findIndex((Y)=>p[Y.rule.ruleName]===Q);const F=!!$.options?.enableImplicitConversion&&!U?.transform.some((Y)=>!Y.options?.serializeOnly);let H=null;if(!B&&F&&z<0)for(let Y=0;Y<_.length;Y++){const A=p[_[Y].rule.ruleName];if(A){z=Y;H=A;break}}const D=z>=0?_[z]:void 0;let L=null;if(!B&&!H&&F&&U?.type?.fn)try{const Y=U.type.fn(),A=Array.isArray(Y)?Y[0]:Y;L=A?ZQ[A.name]??null:null}catch(Y){throw new N(`field "${Z}": @Field type function threw: ${Y.message}`,{cause:Y})}return{effectiveGateType:Q??H??L,gateDeps:q,typeAsserterIdx:z,typeAsserter:D,enableConversion:F,asserterInferredGate:H,typeHintGate:L}}function qQ(Z,W,U,$,_,j,B){let Q="";const q=G(Z),{effectiveGateType:z,gateCondition:F,gateErrorCode:H,gateEmitCtx:D,otherGeneral:L,gateDeps:Y,typeAsserter:A,enableConversion:O}=j,J=(P)=>{const M=[];if(A&&!$Q.has(A.rule.ruleName))M.push(A);M.push(...L,...Y);return h(Z,W,M,$,_,P,B,!0)};if(U)if(O&&(z==="string"||z==="number"||z==="boolean"||z==="date")){const M=`${X.skip}${q}`;Q+=`var ${M} = false;
|
|
73
|
+
`;Q+=`if (${F}) {
|
|
74
|
+
`;Q+=y(z,W,Z,M,!0,$);Q+=`}
|
|
75
|
+
`;Q+=`if (!${M}) {
|
|
76
|
+
`;if(_.validateOnly)Q+=J(" ");else{const w=`${X.mark}${q}`;Q+=` var ${w} = ${X.errList}.length;
|
|
77
|
+
`;Q+=J(" ");Q+=` if (${X.errList}.length === ${w}) ${X.out}[${JSON.stringify(Z)}] = ${W};
|
|
78
|
+
`}Q+=`}
|
|
79
|
+
`}else{Q+=`if (${F}) ${D.fail(H)};
|
|
80
|
+
`;Q+=`else {
|
|
81
|
+
`;if(_.validateOnly)Q+=J(" ");else{const M=`${X.mark}${q}`;Q+=` var ${M} = ${X.errList}.length;
|
|
82
|
+
`;Q+=J(" ");Q+=` if (${X.errList}.length === ${M}) ${X.out}[${JSON.stringify(Z)}] = ${W};
|
|
83
|
+
`}Q+=`}
|
|
84
|
+
`}else if(O&&(z==="string"||z==="number"||z==="boolean"||z==="date")){Q+=`if (${F}) {
|
|
85
|
+
`;Q+=y(z,W,Z,null,!1,$);Q+=`}
|
|
86
|
+
`;Q+=J("");if(!_.validateOnly)Q+=`${X.out}[${JSON.stringify(Z)}] = ${W};
|
|
87
|
+
`}else{Q+=`if (${F}) ${D.fail(H)};
|
|
88
|
+
`;Q+=J("");if(!_.validateOnly)Q+=`${X.out}[${JSON.stringify(Z)}] = ${W};
|
|
89
|
+
`}return Q}function _Q(Z,W,U,$,_,j,B){let Q="";if($)if(U.length===0){if(!j.validateOnly)Q+=`${X.out}[${JSON.stringify(Z)}] = ${W};
|
|
90
|
+
`}else if(j.validateOnly)Q+=h(Z,W,U,_,j,"",B);else{const q=`${X.mark}${G(Z)}`;Q+=`var ${q} = ${X.errList}.length;
|
|
91
|
+
`;Q+=h(Z,W,U,_,j,"",B);Q+=`if (${X.errList}.length === ${q}) ${X.out}[${JSON.stringify(Z)}] = ${W};
|
|
92
|
+
`}else{Q+=h(Z,W,U,_,j,"",B);if(!j.validateOnly)Q+=`${X.out}[${JSON.stringify(Z)}] = ${W};
|
|
93
|
+
`}return Q}function HQ(Z,W,U,$,_,j,B){let Q="";if(U.length===0)return Q;const q=j.pathPrefix?`${j.pathPrefix}+${JSON.stringify(Z)}`:JSON.stringify(Z),z=G(Z),F=`${X.index}${z}`,H=`${X.setIdx}${z}`,D=`${X.setVal}${z}`,L=`${X.mapIdx}${z}`,Y=`${X.mapVal}${z}`,A=`__bk$ep_${z}`,O=`__bk$ck${z}`;Q+=`var ${O} = Array.isArray(${W})?1:(${W} instanceof Set?2:(${W} instanceof Map?3:0));
|
|
94
|
+
`;Q+=`var ${A} = ${q}+'[';
|
|
95
|
+
`;Q+=`if (${O} === 0) ${_.fail("isArray")};
|
|
96
|
+
`;for(const J of U){const P=V(J,Z,W,j),M=J.groups&&J.groups.length>0&&!k(J.groups,B)?J.groups:null,w=M?`if ((${X.group0} === null && !${X.groupsSet}) || ${n(X.group0,X.groupsSet,M)}) {
|
|
97
|
+
`:"",S=M?`}
|
|
98
|
+
`:"",R=[{guard:`Array.isArray(${W})`,idxVar:F,elemExpr:`${W}[${F}]`,loopHeader:`for (var ${F}=0; ${F}<${W}.length; ${F}++)`,counterDecl:"",counterInc:""},{guard:`${W} instanceof Set`,idxVar:H,elemExpr:D,loopHeader:`for (var ${D} of ${W})`,counterDecl:`var ${H} = 0;
|
|
99
|
+
`,counterInc:`${H}++;
|
|
100
|
+
`},{guard:`${W} instanceof Map`,idxVar:L,elemExpr:Y,loopHeader:`for (var ${Y} of ${W}.values())`,counterDecl:`var ${L} = 0;
|
|
101
|
+
`,counterInc:`${L}++;
|
|
102
|
+
`}],T=(b)=>{const i={..._,fail:(K)=>$?`${X.errList}.push({path:${A}+${b.idxVar}+']',code:${JSON.stringify(K)}${P}})`:j.validateOnly?`return [{path:${A}+${b.idxVar}+']',code:${JSON.stringify(K)}${P}}]`:`return err([{path:${A}+${b.idxVar}+']',code:${JSON.stringify(K)}${P}}])`};let E="";E+=` ${b.counterDecl}`;E+=` ${b.loopHeader} {
|
|
103
|
+
`;E+=" "+J.rule.emit(b.elemExpr,i)+`
|
|
104
|
+
`;if(b.counterInc)E+=` ${b.counterInc}`;E+=` }
|
|
105
|
+
`;return E};Q+=w;Q+=`if (${O} === 1) {
|
|
106
|
+
`;Q+=T(R[0]);Q+=`} else if (${O} === 2) {
|
|
107
|
+
`;Q+=T(R[1]);Q+=`} else if (${O} === 3) {
|
|
108
|
+
`;Q+=T(R[2]);Q+=`}
|
|
109
|
+
`;Q+=S}return Q}function LQ(Z,W,U,$,_,j,B,Q){const q=UQ(Z,U),z=YQ(Z,q,B,j);let F="";if(!!q.typedDeps||z.asserterInferredGate||z.typeHintGate){const D=z.typeAsserter?q.generalRules.filter((O,J)=>J!==z.typeAsserterIdx):q.generalRules;let L,Y;if(z.typeAsserter)Y=z.typeAsserter.rule.ruleName;else if(z.gateDeps.length>0)Y=z.gateDeps[0].rule.ruleName;else Y="conversionFailed";if(z.effectiveGateType==="date")L=`!(${W} instanceof Date) || isNaN(${W}.getTime())`;else if(z.effectiveGateType==="array")L=`!Array.isArray(${W})`;else if(z.effectiveGateType==="object")L=`typeof ${W} !== 'object' || ${W} === null || Array.isArray(${W})`;else if(z.effectiveGateType==="number")L=`typeof ${W} !== 'number' || isNaN(${W})`;else L=`typeof ${W} !== '${z.effectiveGateType}'`;const A=z.typeAsserter?s(_,Z,W,z.typeAsserter,j):_;F+=qQ(Z,W,$,_,j,{effectiveGateType:z.effectiveGateType,gateCondition:L,gateErrorCode:Y,gateEmitCtx:A,otherGeneral:D,gateDeps:z.gateDeps,typeAsserter:z.typeAsserter,enableConversion:z.enableConversion},Q)}else F+=_Q(Z,W,q.generalRules,$,_,j,Q);F+=HQ(Z,W,q.each,$,_,j,Q);return F}function jQ(Z,W,U,$,_){const{collectErrors:j,execs:B}=$,Q=G(Z),q=U.type.collection,z=$.isAsync?"await ":"";let F=-1;if(U.type.resolvedCollectionValue){const D=u(U.type.resolvedCollectionValue);F=B.length;B.push(D)}let H="";if(q==="Set"){H+=`if (Array.isArray(${W})) {
|
|
110
|
+
`;const D=U.validation.filter((Y)=>!Y.each);H+=h(Z,W,D,_,$," ");if(F>=0){const Y=`${X.index}${Q}`;H+=` var ${X.arr}${Q} = new Set();
|
|
111
|
+
`;H+=` for (var ${Y}=0; ${Y}<${W}.length; ${Y}++) {
|
|
112
|
+
`;H+=` var ${X.result}${Q} = ${z}execs[${F}].deserialize(${W}[${Y}], opts);
|
|
113
|
+
`;H+=` if (isErr(${X.result}${Q})) {
|
|
114
|
+
`;if(j){H+=` var ${X.errors}${Q} = ${X.result}${Q}.data;
|
|
115
|
+
`;H+=` var __bk$pp${Q} = ${JSON.stringify(Z)}+'['+${Y}+'].';
|
|
116
|
+
`;H+=` for (var ${X.nestedIdx}${Q}=0; ${X.nestedIdx}${Q}<${X.errors}${Q}.length; ${X.nestedIdx}${Q}++) {
|
|
117
|
+
`;H+=" "+I(X.errList,`__bk$pp${Q}+${X.errors}${Q}[${X.nestedIdx}${Q}].path`,`${X.errors}${Q}[${X.nestedIdx}${Q}]`,`__ne${Q}`);H+=` }
|
|
118
|
+
`}else{H+=` var ${X.errors}${Q} = ${X.result}${Q}.data;
|
|
119
|
+
`;H+=` var __bk$pp${Q} = ${JSON.stringify(Z)}+'['+${Y}+'].';
|
|
120
|
+
`;H+=" "+g(`__bk$pp${Q}+${X.errors}${Q}[0].path`,`${X.errors}${Q}[0]`,`__ne${Q}`)}H+=` } else { ${X.arr}${Q}.add(${X.result}${Q}); }
|
|
121
|
+
`;H+=` }
|
|
122
|
+
`;H+=` ${X.out}[${JSON.stringify(Z)}] = ${X.arr}${Q};
|
|
123
|
+
`}else H+=` ${X.out}[${JSON.stringify(Z)}] = new Set(${W});
|
|
124
|
+
`;const L=U.validation.filter((Y)=>Y.each);if(L.length>0){const Y=`${X.setIdx}${Q}`,A=`${X.setVal}${Q}`;H+=` var ${Y} = 0;
|
|
125
|
+
`;H+=` for (var ${A} of ${X.out}[${JSON.stringify(Z)}]) {
|
|
126
|
+
`;for(const O of L){const J=V(O,Z,W,$),M={..._,fail:(w)=>j?`${X.errList}.push({path:${JSON.stringify(Z)}+'['+${Y}+']',code:${JSON.stringify(w)}${J}})`:`return err([{path:${JSON.stringify(Z)}+'['+${Y}+']',code:${JSON.stringify(w)}${J}}])`};H+=` ${O.rule.emit(A,M)}
|
|
127
|
+
`}H+=` ${Y}++;
|
|
128
|
+
`;H+=` }
|
|
129
|
+
`}H+=`} else { ${_.fail("isArray")}; }
|
|
130
|
+
`}else{H+=`if (${W} != null && typeof ${W} === 'object' && !Array.isArray(${W})) {
|
|
131
|
+
`;if(F>=0){const D=`${X.key}${Q}`,L=`__bk$mk${Q}`,Y=`__bk$mi${Q}`;H+=` var ${X.arr}${Q} = new Map();
|
|
132
|
+
`;H+=` var ${L} = Object.keys(${W});
|
|
133
|
+
`;H+=` for (var ${Y}=0; ${Y}<${L}.length; ${Y}++) {
|
|
134
|
+
`;H+=` var ${D} = ${L}[${Y}];
|
|
135
|
+
`;H+=` var ${X.result}${Q} = ${z}execs[${F}].deserialize(${W}[${D}], opts);
|
|
136
|
+
`;H+=` if (isErr(${X.result}${Q})) {
|
|
137
|
+
`;if(j){H+=` var ${X.errors}${Q} = ${X.result}${Q}.data;
|
|
138
|
+
`;H+=` var __bk$pp${Q} = ${JSON.stringify(Z)}+'['+${D}+'].';
|
|
139
|
+
`;H+=` for (var ${X.nestedIdx}${Q}=0; ${X.nestedIdx}${Q}<${X.errors}${Q}.length; ${X.nestedIdx}${Q}++) {
|
|
140
|
+
`;H+=" "+I(X.errList,`__bk$pp${Q}+${X.errors}${Q}[${X.nestedIdx}${Q}].path`,`${X.errors}${Q}[${X.nestedIdx}${Q}]`,`__ne${Q}`);H+=` }
|
|
141
|
+
`}else{H+=` var ${X.errors}${Q} = ${X.result}${Q}.data;
|
|
142
|
+
`;H+=` var __bk$pp${Q} = ${JSON.stringify(Z)}+'['+${D}+'].';
|
|
143
|
+
`;H+=" "+g(`__bk$pp${Q}+${X.errors}${Q}[0].path`,`${X.errors}${Q}[0]`,`__ne${Q}`)}H+=` } else { ${X.arr}${Q}.set(${D}, ${X.result}${Q}); }
|
|
144
|
+
`;H+=` }
|
|
145
|
+
`;H+=` ${X.out}[${JSON.stringify(Z)}] = ${X.arr}${Q};
|
|
146
|
+
`}else{const D=`__bk$mk${Q}`,L=`__bk$mi${Q}`;H+=` var ${X.arr}${Q} = new Map();
|
|
147
|
+
`;H+=` var ${D} = Object.keys(${W});
|
|
148
|
+
`;H+=` for (var ${L}=0; ${L}<${D}.length; ${L}++) {
|
|
149
|
+
`;H+=` var ${X.key}${Q} = ${D}[${L}];
|
|
150
|
+
`;H+=` ${X.arr}${Q}.set(${X.key}${Q}, ${W}[${X.key}${Q}]);
|
|
151
|
+
`;H+=` }
|
|
152
|
+
`;H+=` ${X.out}[${JSON.stringify(Z)}] = ${X.arr}${Q};
|
|
153
|
+
`}H+=`} else { ${_.fail("isObject")}; }
|
|
154
|
+
`}return H}function zQ(Z,W,U,$,_){const{collectErrors:j,execs:B}=$;if(!U.type)return`${X.out}[${JSON.stringify(Z)}] = ${W};
|
|
155
|
+
`;let Q="";const q=G(Z);if(U.type.discriminator){const z=JSON.stringify(U.type.discriminator.property);Q+=`var ${X.disc}${q} = ${W} && ${W}[${z}];
|
|
156
|
+
`;Q+=`switch (${X.disc}${q}) {
|
|
157
|
+
`;for(const L of U.type.discriminator.subTypes){const Y=u(L.value),A=B.length;B.push(Y);const O=$.isAsync?"await ":"";Q+=` case ${JSON.stringify(L.name)}:
|
|
158
|
+
`;Q+=` var ${X.result}${q} = ${O}execs[${A}].deserialize(${W}, opts);
|
|
159
|
+
`;Q+=f(Z,`${X.result}${q}`,j,$.pathPrefix);Q+=` break;
|
|
160
|
+
`}const F=JSON.stringify(U.type.discriminator.subTypes.map((L)=>L.name)),H=_.pathExpr??JSON.stringify(Z),D=`${X.disc}${q}`;if(j)Q+=` default: ${X.errList}.push({path:${H},code:'invalidDiscriminator',context:{received:${D},validSubTypes:${F}}});
|
|
161
|
+
`;else if($.validateOnly)Q+=` default: return [{path:${H},code:'invalidDiscriminator',context:{received:${D},validSubTypes:${F}}}];
|
|
162
|
+
`;else Q+=` default: return err([{path:${H},code:'invalidDiscriminator',context:{received:${D},validSubTypes:${F}}}]);
|
|
163
|
+
`;Q+=`}
|
|
164
|
+
`;if(U.type.keepDiscriminatorProperty){const L=JSON.stringify(Z);Q+=`{var __dh=${X.out}[${L}]; if(__dh!=null) __dh[${z}]=${X.disc}${q};}
|
|
165
|
+
`}}else{const z=U.type.resolvedClass??U.type.fn(),F=u(z),H=B.length;B.push(F);if(U.type.isArray||U.flags.validateNestedEach||U.validation.some((L)=>L.each)){const L=`${X.index}${q}`,Y=$.isAsync?"await ":"";Q+=`if (Array.isArray(${W})) {
|
|
166
|
+
`;const A=U.validation.filter((O)=>!O.each);Q+=h(Z,W,A,_,$," ");Q+=` var ${X.arr}${q} = [];
|
|
167
|
+
`;Q+=` for (var ${L}=0; ${L}<${W}.length; ${L}++) {
|
|
168
|
+
`;Q+=` var ${X.result}${q} = ${Y}execs[${H}].deserialize(${W}[${L}], opts);
|
|
169
|
+
`;Q+=` if (isErr(${X.result}${q})) {
|
|
170
|
+
`;if(j){Q+=` var ${X.errors}${q} = ${X.result}${q}.data;
|
|
171
|
+
`;Q+=` var __bk$pp${q} = ${JSON.stringify(Z)}+'['+${L}+'].';
|
|
172
|
+
`;Q+=` for (var ${X.nestedIdx}${q}=0; ${X.nestedIdx}${q}<${X.errors}${q}.length; ${X.nestedIdx}${q}++) {
|
|
173
|
+
`;Q+=" "+I(X.errList,`__bk$pp${q}+${X.errors}${q}[${X.nestedIdx}${q}].path`,`${X.errors}${q}[${X.nestedIdx}${q}]`,`__ne${q}`);Q+=` }
|
|
174
|
+
`}else{Q+=` var ${X.errors}${q} = ${X.result}${q}.data;
|
|
175
|
+
`;Q+=` var __bk$pp${q} = ${JSON.stringify(Z)}+'['+${L}+'].';
|
|
176
|
+
`;Q+=" "+g(`__bk$pp${q}+${X.errors}${q}[0].path`,`${X.errors}${q}[0]`,`__ne${q}`)}Q+=` } else { ${X.arr}${q}.push(${X.result}${q}); }
|
|
177
|
+
`;Q+=` }
|
|
178
|
+
`;Q+=` ${X.out}[${JSON.stringify(Z)}] = ${X.arr}${q};
|
|
179
|
+
`;Q+=`} else { ${_.fail("isArray")}; }
|
|
180
|
+
`}else{const L=$.isAsync?"await ":"";Q+=`if (${W} != null && typeof ${W} === 'object' && !Array.isArray(${W})) {
|
|
181
|
+
`;Q+=` var ${X.result}${q} = ${L}execs[${H}].deserialize(${W}, opts);
|
|
182
|
+
`;Q+=f(Z,`${X.result}${q}`,j,$.pathPrefix);Q+=`} else { ${_.fail("isObject")}; }
|
|
183
|
+
`}}return Q}function f(Z,W,U,$){const _=G(Z),j=$?`${$}+${JSON.stringify(Z+".")}`:JSON.stringify(Z+".");if(U){const Q=`${X.errors}${_}[${X.nestedIdx}${_}]`;return` if (isErr(${W})) {
|
|
184
|
+
var ${X.errors}${_} = ${W}.data;
|
|
185
|
+
var __bk$pp${_} = ${j};
|
|
186
|
+
for (var ${X.nestedIdx}${_}=0; ${X.nestedIdx}${_}<${X.errors}${_}.length; ${X.nestedIdx}${_}++) {
|
|
187
|
+
`+I(X.errList,`__bk$pp${_}+${Q}.path`,Q,`__ne${_}`)+` }
|
|
188
|
+
} else { ${X.out}[${JSON.stringify(Z)}] = ${W}; }
|
|
189
|
+
`}const B=`${X.errors}${_}[0]`;return` if (isErr(${W})) {
|
|
190
|
+
var ${X.errors}${_} = ${W}.data;
|
|
191
|
+
var __bk$pp${_} = ${j};
|
|
192
|
+
`+g(`__bk$pp${_}+${B}.path`,B,`__ne${_}`)+` } else { ${X.out}[${JSON.stringify(Z)}] = ${W}; }
|
|
193
|
+
`}function C(Z,W,U,$,_,j){const B=j.inlineNestedClasses;B.add(W);const Q={...j,pathPrefix:$,varPrefix:_,inputExpr:U,exposeDefaultValues:!1};let q="";for(const[z,F]of Object.entries(Z))q+=o(z,F,Q);B.delete(W);return q}function BQ(Z,W,U,$,_){const{collectErrors:j,execs:B}=$;if(!U.type)return"";const Q=($.varPrefix||"")+G(Z);let q="";if(!$.inlineNestedClasses)$.inlineNestedClasses=new Set;if(U.type.discriminator){const z=JSON.stringify(U.type.discriminator.property);q+=`var ${X.disc}${Q} = ${W} && ${W}[${z}];
|
|
194
|
+
`;q+=`switch (${X.disc}${Q}) {
|
|
195
|
+
`;for(const L of U.type.discriminator.subTypes){const Y=u(L.value),A=Y.merged,O=A&&!$.inlineNestedClasses.has(L.value);q+=` case ${JSON.stringify(L.name)}:
|
|
196
|
+
`;if(O){const J=$.pathPrefix?`${$.pathPrefix}+${JSON.stringify(Z+".")}`:JSON.stringify(Z+"."),P=`${Q}_d${G(L.name)}_`;q+=C(A,L.value,W,J,P,$)}else{const J=B.length;B.push(Y);const P=$.isAsync?"await ":"";q+=` var ${X.result}${Q} = ${P}execs[${J}].validate(${W}, opts);
|
|
197
|
+
`;q+=v(Z,`${X.result}${Q}`,j,$.pathPrefix)}q+=` break;
|
|
198
|
+
`}const F=JSON.stringify(U.type.discriminator.subTypes.map((L)=>L.name)),H=_.pathExpr??JSON.stringify(Z),D=`${X.disc}${Q}`;if(j)q+=` default: ${X.errList}.push({path:${H},code:'invalidDiscriminator',context:{received:${D},validSubTypes:${F}}});
|
|
199
|
+
`;else q+=` default: return [{path:${H},code:'invalidDiscriminator',context:{received:${D},validSubTypes:${F}}}];
|
|
200
|
+
`;q+=`}
|
|
201
|
+
`}else{const z=U.type.resolvedClass??U.type.fn(),F=u(z),H=F.merged,D=U.type.isArray||U.flags.validateNestedEach||U.validation.some((Y)=>Y.each),L=H&&!$.inlineNestedClasses.has(z);if(D){const Y=`${X.index}${Q}`;q+=`if (Array.isArray(${W})) {
|
|
202
|
+
`;const A=U.validation.filter((O)=>!O.each);q+=h(Z,W,A,_,$," ");q+=` for (var ${Y}=0; ${Y}<${W}.length; ${Y}++) {
|
|
203
|
+
`;if(L){const O=`__il$${Q}item`,J=`__bk$pp${Q}`,P=J,M=$.pathPrefix?`${$.pathPrefix}+${JSON.stringify(Z)}+'['+${Y}+'].'`:`${JSON.stringify(Z)}+'['+${Y}+'].'`,w=`${Q}i_`;q+=` var ${O} = ${W}[${Y}];
|
|
204
|
+
`;q+=` var ${J} = ${M};
|
|
205
|
+
`;q+=` if (${O} == null || typeof ${O} !== 'object' || Array.isArray(${O})) `;if(j)q+=`${X.errList}.push({path:${J},code:'invalidInput'});
|
|
206
|
+
`;else q+=`return [{path:${J},code:'invalidInput'}];
|
|
207
|
+
`;q+=` else {
|
|
208
|
+
`;q+=C(H,z,O,P,w,$);q+=` }
|
|
209
|
+
`}else{const O=B.length;B.push(F);const J=$.isAsync?"await ":"";q+=` var ${X.result}${Q} = ${J}execs[${O}].validate(${W}[${Y}], opts);
|
|
210
|
+
`;q+=` if (${X.result}${Q} !== null) {
|
|
211
|
+
`;const P=`__bk$pp${Q}`,M=$.pathPrefix?`${$.pathPrefix}+${JSON.stringify(Z)}+'['+${Y}+'].'`:`${JSON.stringify(Z)}+'['+${Y}+'].'`;q+=` var ${P} = ${M};
|
|
212
|
+
`;if(j){q+=` for (var ${X.nestedIdx}${Q}=0; ${X.nestedIdx}${Q}<${X.result}${Q}.length; ${X.nestedIdx}${Q}++) {
|
|
213
|
+
`;q+=" "+I(X.errList,`${P}+${X.result}${Q}[${X.nestedIdx}${Q}].path`,`${X.result}${Q}[${X.nestedIdx}${Q}]`,`__ne${Q}`);q+=` }
|
|
214
|
+
`}else q+=" "+g(`${P}+${X.result}${Q}[0].path`,`${X.result}${Q}[0]`,`__ne${Q}`,!0);q+=` }
|
|
215
|
+
`}q+=` }
|
|
216
|
+
`;q+=`} else { ${_.fail("isArray")}; }
|
|
217
|
+
`}else{q+=`if (${W} != null && typeof ${W} === 'object' && !Array.isArray(${W})) {
|
|
218
|
+
`;if(L){const Y=$.pathPrefix?`${$.pathPrefix}+${JSON.stringify(Z+".")}`:JSON.stringify(Z+"."),A=`${Q}_`;q+=C(H,z,W,Y,A,$)}else{const Y=B.length;B.push(F);const A=$.isAsync?"await ":"";q+=` var ${X.result}${Q} = ${A}execs[${Y}].validate(${W}, opts);
|
|
219
|
+
`;q+=v(Z,`${X.result}${Q}`,j,$.pathPrefix)}q+=`} else { ${_.fail("isObject")}; }
|
|
220
|
+
`}}return q}function v(Z,W,U,$){const _=G(Z),j=`__bk$pp${_}`,B=$?`${$}+${JSON.stringify(Z+".")}`:JSON.stringify(Z+".");if(U){const q=`${W}[${X.nestedIdx}${_}]`;return` if (${W} !== null) {
|
|
221
|
+
var ${j} = ${B};
|
|
222
|
+
for (var ${X.nestedIdx}${_}=0; ${X.nestedIdx}${_}<${W}.length; ${X.nestedIdx}${_}++) {
|
|
223
|
+
`+I(X.errList,`${j}+${q}.path`,q,`__ne${_}`)+` }
|
|
224
|
+
}
|
|
225
|
+
`}const Q=`${W}[0]`;return` if (${W} !== null) {
|
|
226
|
+
var ${j} = ${B};
|
|
227
|
+
`+g(`${j}+${Q}.path`,Q,`__ne${_}`,!0)+` }
|
|
228
|
+
`}function FQ(Z,W,U,$,_){const{collectErrors:j,execs:B}=$,Q=($.varPrefix||"")+G(Z),q=U.type.collection,z=$.isAsync?"await ":"";if(!$.inlineNestedClasses)$.inlineNestedClasses=new Set;let F,H,D;if(U.type.resolvedCollectionValue){F=U.type.resolvedCollectionValue;H=u(F);D=H.merged}const L=F&&D&&!$.inlineNestedClasses.has(F);let Y="";if(q==="Set"){Y+=`if (Array.isArray(${W})) {
|
|
229
|
+
`;const A=U.validation.filter((J)=>!J.each);Y+=h(Z,W,A,_,$," ");if(H){const J=`${X.index}${Q}`;Y+=` for (var ${J}=0; ${J}<${W}.length; ${J}++) {
|
|
230
|
+
`;if(L){const P=`__il$${Q}ci`,M=`__bk$pp${Q}`,w=$.pathPrefix?`${$.pathPrefix}+${JSON.stringify(Z)}+'['+${J}+'].'`:`${JSON.stringify(Z)}+'['+${J}+'].'`,S=`${Q}c_`;Y+=` var ${P} = ${W}[${J}];
|
|
231
|
+
`;Y+=` var ${M} = ${w};
|
|
232
|
+
`;Y+=` if (${P} == null || typeof ${P} !== 'object' || Array.isArray(${P})) `;if(j)Y+=`${X.errList}.push({path:${M},code:'invalidInput'});
|
|
233
|
+
`;else Y+=`return [{path:${M},code:'invalidInput'}];
|
|
234
|
+
`;Y+=` else {
|
|
235
|
+
`;Y+=C(D,F,P,M,S,$);Y+=` }
|
|
236
|
+
`}else{const P=B.length;B.push(H);Y+=` var ${X.result}${Q} = ${z}execs[${P}].validate(${W}[${J}], opts);
|
|
237
|
+
`;Y+=` if (${X.result}${Q} !== null) {
|
|
238
|
+
`;const M=`__bk$pp${Q}`,w=$.pathPrefix?`${$.pathPrefix}+${JSON.stringify(Z)}+'['+${J}+'].'`:`${JSON.stringify(Z)}+'['+${J}+'].'`;Y+=` var ${M} = ${w};
|
|
239
|
+
`;if(j){Y+=` for (var ${X.nestedIdx}${Q}=0; ${X.nestedIdx}${Q}<${X.result}${Q}.length; ${X.nestedIdx}${Q}++) {
|
|
240
|
+
`;Y+=" "+I(X.errList,`${M}+${X.result}${Q}[${X.nestedIdx}${Q}].path`,`${X.result}${Q}[${X.nestedIdx}${Q}]`,`__ne${Q}`);Y+=` }
|
|
241
|
+
`}else Y+=" "+g(`${M}+${X.result}${Q}[0].path`,`${X.result}${Q}[0]`,`__ne${Q}`,!0);Y+=` }
|
|
242
|
+
`}Y+=` }
|
|
243
|
+
`}const O=U.validation.filter((J)=>J.each);if(O.length>0){const J=`${X.index}${Q}e`;Y+=` for (var ${J}=0; ${J}<${W}.length; ${J}++) {
|
|
244
|
+
`;for(const P of O){const M=`__bk$ep_${Q}`,w=V(P,Z,W,$),R={..._,fail:(T)=>j?`${X.errList}.push({path:${M}+${J}+']',code:${JSON.stringify(T)}${w}})`:`return [{path:${M}+${J}+']',code:${JSON.stringify(T)}${w}}]`};if(!Y.includes(`var ${M}`)){const T=$.pathPrefix?`${$.pathPrefix}+${JSON.stringify(Z)}+'['`:`${JSON.stringify(Z)}+'['`;Y+=` var ${M} = ${T};
|
|
245
|
+
`}Y+=` ${P.rule.emit(`${W}[${J}]`,R)}
|
|
246
|
+
`}Y+=` }
|
|
247
|
+
`}Y+=`} else { ${_.fail("isArray")}; }
|
|
248
|
+
`}else{Y+=`if (${W} != null && typeof ${W} === 'object' && !Array.isArray(${W})) {
|
|
249
|
+
`;if(H){const A=`${X.key}${Q}`,O=`__bk$vk${Q}`,J=`__bk$vi${Q}`;Y+=` var ${O} = Object.keys(${W});
|
|
250
|
+
`;Y+=` for (var ${J}=0; ${J}<${O}.length; ${J}++) {
|
|
251
|
+
`;Y+=` var ${A} = ${O}[${J}];
|
|
252
|
+
`;if(L){const P=`__il$${Q}mi`,M=$.pathPrefix?`${$.pathPrefix}+${JSON.stringify(Z)}+'['+${A}+'].'`:`${JSON.stringify(Z)}+'['+${A}+'].'`,w=`${Q}m_`,S=M;Y+=` var ${P} = ${W}[${A}];
|
|
253
|
+
`;Y+=` if (${P} == null || typeof ${P} !== 'object' || Array.isArray(${P})) `;if(j)Y+=`${X.errList}.push({path:${S},code:'invalidInput'});
|
|
254
|
+
`;else Y+=`return [{path:${S},code:'invalidInput'}];
|
|
255
|
+
`;Y+=` else {
|
|
256
|
+
`;Y+=C(D,F,P,M,w,$);Y+=` }
|
|
257
|
+
`}else{const P=B.length;B.push(H);Y+=` var ${X.result}${Q} = ${z}execs[${P}].validate(${W}[${A}], opts);
|
|
258
|
+
`;Y+=` if (${X.result}${Q} !== null) {
|
|
259
|
+
`;const M=`__bk$pp${Q}`,w=$.pathPrefix?`${$.pathPrefix}+${JSON.stringify(Z)}+'['+${A}+'].'`:`${JSON.stringify(Z)}+'['+${A}+'].'`;Y+=` var ${M} = ${w};
|
|
260
|
+
`;if(j){Y+=` for (var ${X.nestedIdx}${Q}=0; ${X.nestedIdx}${Q}<${X.result}${Q}.length; ${X.nestedIdx}${Q}++) {
|
|
261
|
+
`;Y+=" "+I(X.errList,`${M}+${X.result}${Q}[${X.nestedIdx}${Q}].path`,`${X.result}${Q}[${X.nestedIdx}${Q}]`,`__ne${Q}`);Y+=` }
|
|
262
|
+
`}else Y+=" "+g(`${M}+${X.result}${Q}[0].path`,`${X.result}${Q}[0]`,`__ne${Q}`,!0);Y+=` }
|
|
263
|
+
`}Y+=` }
|
|
264
|
+
`}Y+=`} else { ${_.fail("isObject")}; }
|
|
265
|
+
`}return Y}function JQ(Z,W,U=""){const{collectErrors:$,regexes:_,refs:j,execs:B,validateOnly:Q,pathPrefix:q}=W,z=q?`${q}+${JSON.stringify(Z)}`:JSON.stringify(Z);return{addRegex(F){_.push(F);return _.length-1},addRef(F){j.push(F);return j.length-1},addExecutor(F){B.push(F);return B.length-1},fail(F){if($)return`${X.errList}.push({path:${z},code:${JSON.stringify(F)}${U}})`;else if(Q)return`return [{path:${z},code:${JSON.stringify(F)}${U}}]`;return`return err([{path:${z},code:${JSON.stringify(F)}${U}}])`},collectErrors:$,pathExpr:z}}export{buildDeserializeCode,buildValidateCode};
|