zod-compiler 2.0.2 → 2.0.4
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/README.md +8 -6
- package/dist/cli/commands/check.d.ts.map +1 -1
- package/dist/cli/commands/check.js +5 -0
- package/dist/cli/commands/check.js.map +1 -1
- package/dist/core/codegen/build-path.js +18 -6
- package/dist/core/codegen/build-path.js.map +1 -1
- package/dist/core/codegen/context.d.ts +12 -1
- package/dist/core/codegen/context.d.ts.map +1 -1
- package/dist/core/codegen/context.js +15 -1
- package/dist/core/codegen/context.js.map +1 -1
- package/dist/core/codegen/index.js +1 -0
- package/dist/core/codegen/index.js.map +1 -1
- package/dist/core/codegen/issue-decls.d.ts +67 -5
- package/dist/core/codegen/issue-decls.d.ts.map +1 -1
- package/dist/core/codegen/issue-decls.js +72 -7
- package/dist/core/codegen/issue-decls.js.map +1 -1
- package/dist/core/codegen/schemas/array.d.ts.map +1 -1
- package/dist/core/codegen/schemas/array.js +3 -3
- package/dist/core/codegen/schemas/array.js.map +1 -1
- package/dist/core/codegen/schemas/effect.d.ts +13 -1
- package/dist/core/codegen/schemas/effect.d.ts.map +1 -1
- package/dist/core/codegen/schemas/effect.js +26 -3
- package/dist/core/codegen/schemas/effect.js.map +1 -1
- package/dist/core/codegen/schemas/fallback.d.ts +17 -1
- package/dist/core/codegen/schemas/fallback.d.ts.map +1 -1
- package/dist/core/codegen/schemas/fallback.js +31 -15
- package/dist/core/codegen/schemas/fallback.js.map +1 -1
- package/dist/core/codegen/schemas/number.d.ts.map +1 -1
- package/dist/core/codegen/schemas/number.js +35 -35
- package/dist/core/codegen/schemas/number.js.map +1 -1
- package/dist/core/codegen/schemas/object.d.ts.map +1 -1
- package/dist/core/codegen/schemas/object.js +64 -29
- package/dist/core/codegen/schemas/object.js.map +1 -1
- package/dist/core/codegen/schemas/recursive-ref.d.ts.map +1 -1
- package/dist/core/codegen/schemas/recursive-ref.js +1 -0
- package/dist/core/codegen/schemas/recursive-ref.js.map +1 -1
- package/dist/core/codegen/schemas/string.d.ts.map +1 -1
- package/dist/core/codegen/schemas/string.js +3 -3
- package/dist/core/codegen/schemas/string.js.map +1 -1
- package/dist/core/codegen/schemas/template-literal.d.ts.map +1 -1
- package/dist/core/codegen/schemas/template-literal.js +1 -1
- package/dist/core/codegen/schemas/template-literal.js.map +1 -1
- package/dist/core/extract/index.d.ts.map +1 -1
- package/dist/core/extract/index.js +2 -0
- package/dist/core/extract/index.js.map +1 -1
- package/dist/core/extract/zod-version.d.ts +78 -0
- package/dist/core/extract/zod-version.d.ts.map +1 -0
- package/dist/core/extract/zod-version.js +109 -0
- package/dist/core/extract/zod-version.js.map +1 -0
- package/dist/jit.d.ts.map +1 -1
- package/dist/jit.js +9 -1
- package/dist/jit.js.map +1 -1
- package/dist/runtime.d.ts +3 -0
- package/dist/runtime.js +4 -1
- package/dist/unplugin/hoist-compile.d.ts.map +1 -1
- package/dist/unplugin/hoist-compile.js +3 -1
- package/dist/unplugin/hoist-compile.js.map +1 -1
- package/dist/unplugin/transform.d.ts.map +1 -1
- package/dist/unplugin/transform.js +5 -0
- package/dist/unplugin/transform.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,9 +1,21 @@
|
|
|
1
|
-
import { checkPriority,
|
|
1
|
+
import { checkPriority, emitRuntimeHelper } from "../context.js";
|
|
2
2
|
import { emit } from "../emit.js";
|
|
3
3
|
import { invalidType, tooBig, tooSmall } from "../emit-issue.js";
|
|
4
4
|
import { ZC_FSR_DECL } from "../issue-decls.js";
|
|
5
|
-
import { refineCheck, superRefineCheck, superRefineFastTest } from "./effect.js";
|
|
5
|
+
import { fastRefineTest, refineCheck, superRefineCheck, superRefineFastTest } from "./effect.js";
|
|
6
6
|
//#region src/core/codegen/schemas/number.ts
|
|
7
|
+
/**
|
|
8
|
+
* The integer number formats, each with the range zod's `NUMBER_FORMAT_RANGES`
|
|
9
|
+
* gives it. `null` for safeint, whose own range IS the safe-integer range that
|
|
10
|
+
* every integer format reports first (see the number_format case below), so it
|
|
11
|
+
* has no range branch of its own. float32/float64 are absent on purpose: they
|
|
12
|
+
* are pure range checks and never report `invalid_type`.
|
|
13
|
+
*/
|
|
14
|
+
const INT_FORMAT_RANGES = {
|
|
15
|
+
int32: [-2147483648, 2147483647],
|
|
16
|
+
safeint: null,
|
|
17
|
+
uint32: [0, 4294967295]
|
|
18
|
+
};
|
|
7
19
|
function slowNumber(ir, g) {
|
|
8
20
|
let code = "";
|
|
9
21
|
if (ir.coerce) code += emit`try{${g.output}=Number(${g.input});}catch(_){}`;
|
|
@@ -17,7 +29,7 @@ function slowNumber(ir, g) {
|
|
|
17
29
|
}`;
|
|
18
30
|
if (ir.checks.length > 0) {
|
|
19
31
|
code += `else{`;
|
|
20
|
-
const opensIntGuard = (check) => check.kind === "number_format" &&
|
|
32
|
+
const opensIntGuard = (check) => check.kind === "number_format" && INT_FORMAT_RANGES[check.format] !== void 0;
|
|
21
33
|
let seenIntFormat = false;
|
|
22
34
|
let intGuardOpen = false;
|
|
23
35
|
for (const check of ir.checks) {
|
|
@@ -48,46 +60,34 @@ function slowNumber(ir, g) {
|
|
|
48
60
|
break;
|
|
49
61
|
case "number_format": {
|
|
50
62
|
const message = check.message ?? g.typeMsg;
|
|
51
|
-
|
|
63
|
+
const intRange = INT_FORMAT_RANGES[check.format];
|
|
64
|
+
if (intRange !== void 0) {
|
|
52
65
|
const note = `note:"Integers must be within the safe integer range."`;
|
|
53
66
|
const msgProp = message !== void 0 ? `,message:${JSON.stringify(message)}` : "";
|
|
54
67
|
code += emit`
|
|
55
68
|
if(!Number.isInteger(${g.input})){
|
|
56
69
|
${invalidType(g, "int", {
|
|
57
|
-
extra: "format
|
|
70
|
+
extra: `format:"${check.format}"`,
|
|
58
71
|
extraBeforeCode: true,
|
|
59
72
|
message
|
|
60
73
|
})}
|
|
61
|
-
}else if(${g.input}
|
|
62
|
-
${g.
|
|
63
|
-
|
|
64
|
-
|
|
74
|
+
}else if(!Number.isSafeInteger(${g.input})){
|
|
75
|
+
if(${g.input}>0){
|
|
76
|
+
${g.issues}.push({code:"too_big",maximum:9007199254740991,${note},origin:"int",inclusive:true,input:${g.input},path:${g.path}${msgProp}});
|
|
77
|
+
}else{
|
|
78
|
+
${g.issues}.push({code:"too_small",minimum:-9007199254740991,${note},origin:"int",inclusive:true,input:${g.input},path:${g.path}${msgProp}});
|
|
79
|
+
}
|
|
65
80
|
}`;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
${tooBig(g, 2147483647, "number", true, { message })}
|
|
77
|
-
}`;
|
|
78
|
-
else if (check.format === "uint32") code += emit`
|
|
79
|
-
if(!Number.isInteger(${g.input})){
|
|
80
|
-
${invalidType(g, "int", {
|
|
81
|
-
extra: "format:\"uint32\"",
|
|
82
|
-
extraBeforeCode: true,
|
|
83
|
-
message
|
|
84
|
-
})}
|
|
85
|
-
}else if(${g.input}<0){
|
|
86
|
-
${tooSmall(g, 0, "number", true, { message })}
|
|
87
|
-
}else if(${g.input}>4294967295){
|
|
88
|
-
${tooBig(g, 4294967295, "number", true, { message })}
|
|
89
|
-
}`;
|
|
90
|
-
else if (check.format === "float32") code += emit`
|
|
81
|
+
if (intRange !== null) {
|
|
82
|
+
const [minimum, maximum] = intRange;
|
|
83
|
+
code += emit`
|
|
84
|
+
else if(${g.input}<${minimum}){
|
|
85
|
+
${tooSmall(g, minimum, "number", true, { message })}
|
|
86
|
+
}else if(${g.input}>${maximum}){
|
|
87
|
+
${tooBig(g, maximum, "number", true, { message })}
|
|
88
|
+
}`;
|
|
89
|
+
}
|
|
90
|
+
} else if (check.format === "float32") code += emit`
|
|
91
91
|
if(${g.input}<-3.4028234663852886e+38){
|
|
92
92
|
${tooSmall(g, "-3.4028234663852886e+38", "number", true, { message })}
|
|
93
93
|
}else if(${g.input}>3.4028234663852886e+38){
|
|
@@ -165,7 +165,7 @@ function fastNumber(ir, g) {
|
|
|
165
165
|
case "starts_with":
|
|
166
166
|
case "ends_with": break;
|
|
167
167
|
}
|
|
168
|
-
for (const check of ir.checks) if (check.kind === "refine_effect") parts.push(
|
|
168
|
+
for (const check of ir.checks) if (check.kind === "refine_effect") parts.push(fastRefineTest(check, x, g));
|
|
169
169
|
else if (check.kind === "super_refine_effect") parts.push(superRefineFastTest(check, x, g));
|
|
170
170
|
return parts.join("&&");
|
|
171
171
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"number.js","names":[],"sources":["../../../../src/core/codegen/schemas/number.ts"],"sourcesContent":["import type { CheckIR, NumberIR } from \"../../types.js\";\nimport type { FastGen, SlowGen } from \"../context.js\";\nimport { checkPriority, emitEffectCallable, emitRuntimeHelper } from \"../context.js\";\nimport { emit } from \"../emit.js\";\nimport { invalidType, tooBig, tooSmall } from \"../emit-issue.js\";\nimport { ZC_FSR_DECL } from \"../issue-decls.js\";\nimport { refineCheck, superRefineCheck, superRefineFastTest } from \"./effect.js\";\n\nexport function slowNumber(ir: NumberIR, g: SlowGen): string {\n let code = \"\";\n if (ir.coerce) {\n code += emit`try{${g.output}=Number(${g.input});}catch(_){}`;\n }\n code += emit`\n if(typeof ${g.input}!==\"number\"){\n ${invalidType(g, \"number\")}\n }else if(Number.isNaN(${g.input})){\n ${invalidType(g, \"number\", { extra: 'received:\"NaN\"' })}\n }else if(!Number.isFinite(${g.input})){\n ${invalidType(g, \"number\", { extra: `received:String(${g.input})` })}\n }`;\n\n if (ir.checks.length > 0) {\n code += `else{`;\n // An integer FORMAT check (`.int()`, `.int32()`, `.uint32()`) is the one\n // check here that can report `invalid_type` — non-continuable — and zod's\n // `runChecks` then skips every check after it (`else if (isAborted)\n // continue`). So `z.number().int().min(5)` on 1.5 reports the int failure\n // ALONE, where an ungated chain also volunteers too_small. Its aborting\n // branch is exactly `!Number.isInteger(value)` (the range branches report\n // continuable too_small/too_big, which do NOT stop later checks), so one\n // guard opened after the first such check reproduces zod without any\n // runtime abort bookkeeping. Non-integer formats (float32/float64) never\n // report invalid_type and so never open it.\n const opensIntGuard = (check: (typeof ir.checks)[number]): boolean =>\n check.kind === \"number_format\" &&\n (check.format === \"safeint\" || check.format === \"int32\" || check.format === \"uint32\");\n let seenIntFormat = false;\n let intGuardOpen = false;\n // Insertion order mirrors zod's issue order for multi-failure inputs.\n // The slow path collects ALL issues (no short-circuit), so cost ordering\n // buys nothing here — only the fast path's && chain benefits from it.\n for (const check of ir.checks) {\n // Opened lazily, on the first check that actually needs gating: an integer\n // format check in LAST position has nothing after it, so the guard would\n // be dead code.\n if (seenIntFormat && !intGuardOpen) {\n code += `if(Number.isInteger(${g.input})){`;\n intGuardOpen = true;\n }\n switch (check.kind) {\n case \"greater_than\":\n if (check.inclusive) {\n code += emit`\n if(${g.input}<${check.value}){\n ${tooSmall(g, check.value, \"number\", true, { message: check.message })}\n }`;\n } else {\n code += emit`\n if(${g.input}<=${check.value}){\n ${tooSmall(g, check.value, \"number\", false, { message: check.message })}\n }`;\n }\n break;\n case \"less_than\":\n if (check.inclusive) {\n code += emit`\n if(${g.input}>${check.value}){\n ${tooBig(g, check.value, \"number\", true, { message: check.message })}\n }`;\n } else {\n code += emit`\n if(${g.input}>=${check.value}){\n ${tooBig(g, check.value, \"number\", false, { message: check.message })}\n }`;\n }\n break;\n case \"number_format\": {\n const message = check.message ?? g.typeMsg;\n if (check.format === \"safeint\") {\n // Mirrors $ZodCheckNumberFormat: non-integers → invalid_type;\n // integers outside the safe range → too_small/too_big with\n // origin \"int\" and zod's explanatory note.\n const note = `note:\"Integers must be within the safe integer range.\"`;\n const msgProp = message !== undefined ? `,message:${JSON.stringify(message)}` : \"\";\n code += emit`\n if(!Number.isInteger(${g.input})){\n ${invalidType(g, \"int\", { extra: 'format:\"safeint\"', extraBeforeCode: true, message })}\n }else if(${g.input}<-9007199254740991){\n ${g.issues}.push({code:\"too_small\",minimum:-9007199254740991,${note},origin:\"int\",inclusive:true,input:${g.input},path:${g.path}${msgProp}});\n }else if(${g.input}>9007199254740991){\n ${g.issues}.push({code:\"too_big\",maximum:9007199254740991,${note},origin:\"int\",inclusive:true,input:${g.input},path:${g.path}${msgProp}});\n }`;\n } else if (check.format === \"int32\") {\n code += emit`\n if(!Number.isInteger(${g.input})){\n ${invalidType(g, \"int\", { extra: 'format:\"int32\"', extraBeforeCode: true, message })}\n }else if(${g.input}<-2147483648){\n ${tooSmall(g, -2147483648, \"number\", true, { message })}\n }else if(${g.input}>2147483647){\n ${tooBig(g, 2147483647, \"number\", true, { message })}\n }`;\n } else if (check.format === \"uint32\") {\n code += emit`\n if(!Number.isInteger(${g.input})){\n ${invalidType(g, \"int\", { extra: 'format:\"uint32\"', extraBeforeCode: true, message })}\n }else if(${g.input}<0){\n ${tooSmall(g, 0, \"number\", true, { message })}\n }else if(${g.input}>4294967295){\n ${tooBig(g, 4294967295, \"number\", true, { message })}\n }`;\n } else if (check.format === \"float32\") {\n code += emit`\n if(${g.input}<-3.4028234663852886e+38){\n ${tooSmall(g, \"-3.4028234663852886e+38\", \"number\", true, { message })}\n }else if(${g.input}>3.4028234663852886e+38){\n ${tooBig(g, \"3.4028234663852886e+38\", \"number\", true, { message })}\n }`;\n }\n // float64 range is [-Number.MAX_VALUE, Number.MAX_VALUE], already covered by the isFinite check above\n break;\n }\n case \"multiple_of\": {\n const message = check.message ?? g.typeMsg;\n const msgProp = message !== undefined ? `,message:${JSON.stringify(message)}` : \"\";\n // zod uses a float-safe remainder: raw % mis-rejects 0.3 % 0.1\n const fsr = emitRuntimeHelper(g.ctx, \"__zcFsr\", ZC_FSR_DECL);\n code += emit`\n if(${fsr}(${g.input},${check.value})!==0){\n ${g.issues}.push({origin:\"number\",code:\"not_multiple_of\",divisor:${check.value},input:${g.input},path:${g.path}${msgProp}});\n }`;\n break;\n }\n case \"refine_effect\":\n code += refineCheck(check, g.input, g);\n break;\n case \"super_refine_effect\":\n code += superRefineCheck(check, g.input, g);\n break;\n }\n if (opensIntGuard(check)) seenIntFormat = true;\n }\n if (intGuardOpen) code += `}`;\n code += `}`;\n }\n\n return `${code}\\n`;\n}\n\nexport function fastNumber(ir: NumberIR, g: FastGen): string | null {\n if (ir.coerce) return null;\n\n const x = g.input;\n const checks = ir.checks.filter((c): c is CheckIR => c.kind !== \"refine_effect\");\n\n // Number.isFinite(x) alone implies typeof number && !NaN && finite — zod's\n // entire number type gate in one non-coercing intrinsic. Likewise\n // Number.isSafeInteger covers the safeint format gate. Only the bitwise int\n // formats ((x|0)===x, (x>>>0)===x) still need the typeof guard: applying\n // ToNumber to an arbitrary input would invoke valueOf side effects zod\n // never triggers. Math.fround(Infinity)===Infinity, so float32 keeps the\n // isFinite gate.\n const hasSafeInt = checks.some((c) => c.kind === \"number_format\" && c.format === \"safeint\");\n const hasBitwiseInt = checks.some(\n (c) => c.kind === \"number_format\" && (c.format === \"int32\" || c.format === \"uint32\"),\n );\n const parts: string[] = [];\n if (hasBitwiseInt) {\n parts.push(`typeof ${x}===\"number\"`);\n } else if (!hasSafeInt) {\n parts.push(`Number.isFinite(${x})`);\n }\n\n for (const check of checks.sort(checkPriority)) {\n switch (check.kind) {\n case \"number_format\":\n switch (check.format) {\n case \"safeint\":\n parts.push(`Number.isSafeInteger(${x})`);\n break;\n case \"int32\":\n parts.push(`(${x}|0)===${x}`);\n break;\n case \"uint32\":\n parts.push(`${x}>=0`, `${x}<=4294967295`, `(${x}>>>0)===${x}`);\n break;\n case \"float32\":\n // zod's float32 check is a pure RANGE check (3.14 is accepted\n // even though Math.fround(3.14) !== 3.14) — fround here would\n // make the fast path stricter than the slow path / zod.\n parts.push(`${x}>=-3.4028234663852886e+38`, `${x}<=3.4028234663852886e+38`);\n break;\n case \"float64\":\n // All finite numbers are valid float64\n break;\n }\n break;\n case \"greater_than\":\n parts.push(check.inclusive ? `${x}>=${check.value}` : `${x}>${check.value}`);\n break;\n case \"less_than\":\n parts.push(check.inclusive ? `${x}<=${check.value}` : `${x}<${check.value}`);\n break;\n case \"multiple_of\": {\n const fsr = emitRuntimeHelper(g.ctx, \"__zcFsr\", ZC_FSR_DECL);\n parts.push(`${fsr}(${x},${check.value})===0`);\n break;\n }\n case \"min_length\":\n case \"max_length\":\n case \"length_equals\":\n case \"string_format\":\n case \"includes\":\n case \"starts_with\":\n case \"ends_with\":\n // String-only checks on a number schema — shouldn't happen, skip\n break;\n }\n }\n\n // Refine effect checks (appended last — run after cheap checks short-circuit)\n for (const check of ir.checks) {\n if (check.kind === \"refine_effect\") {\n parts.push(`${emitEffectCallable(g.ctx, check)}(${x})`);\n } else if (check.kind === \"super_refine_effect\") {\n parts.push(superRefineFastTest(check, x, g));\n }\n }\n\n return parts.join(\"&&\");\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"number.js","names":[],"sources":["../../../../src/core/codegen/schemas/number.ts"],"sourcesContent":["import type { CheckIR, NumberIR } from \"../../types.js\";\nimport type { FastGen, SlowGen } from \"../context.js\";\nimport { checkPriority, emitRuntimeHelper } from \"../context.js\";\nimport { emit } from \"../emit.js\";\nimport { invalidType, tooBig, tooSmall } from \"../emit-issue.js\";\nimport { ZC_FSR_DECL } from \"../issue-decls.js\";\nimport { fastRefineTest, refineCheck, superRefineCheck, superRefineFastTest } from \"./effect.js\";\n\n/**\n * The integer number formats, each with the range zod's `NUMBER_FORMAT_RANGES`\n * gives it. `null` for safeint, whose own range IS the safe-integer range that\n * every integer format reports first (see the number_format case below), so it\n * has no range branch of its own. float32/float64 are absent on purpose: they\n * are pure range checks and never report `invalid_type`.\n */\nconst INT_FORMAT_RANGES: { readonly [format: string]: readonly [number, number] | null } = {\n int32: [-2147483648, 2147483647],\n safeint: null,\n uint32: [0, 4294967295],\n};\n\nexport function slowNumber(ir: NumberIR, g: SlowGen): string {\n let code = \"\";\n if (ir.coerce) {\n code += emit`try{${g.output}=Number(${g.input});}catch(_){}`;\n }\n code += emit`\n if(typeof ${g.input}!==\"number\"){\n ${invalidType(g, \"number\")}\n }else if(Number.isNaN(${g.input})){\n ${invalidType(g, \"number\", { extra: 'received:\"NaN\"' })}\n }else if(!Number.isFinite(${g.input})){\n ${invalidType(g, \"number\", { extra: `received:String(${g.input})` })}\n }`;\n\n if (ir.checks.length > 0) {\n code += `else{`;\n // An integer FORMAT check (`.int()`, `.int32()`, `.uint32()`) is the one\n // check here that can report `invalid_type` — non-continuable — and zod's\n // `runChecks` then skips every check after it (`else if (isAborted)\n // continue`). So `z.number().int().min(5)` on 1.5 reports the int failure\n // ALONE, where an ungated chain also volunteers too_small. Its aborting\n // branch is exactly `!Number.isInteger(value)` (the range branches report\n // continuable too_small/too_big, which do NOT stop later checks), so one\n // guard opened after the first such check reproduces zod without any\n // runtime abort bookkeeping. Non-integer formats (float32/float64) never\n // report invalid_type and so never open it.\n const opensIntGuard = (check: (typeof ir.checks)[number]): boolean =>\n check.kind === \"number_format\" && INT_FORMAT_RANGES[check.format] !== undefined;\n let seenIntFormat = false;\n let intGuardOpen = false;\n // Insertion order mirrors zod's issue order for multi-failure inputs.\n // The slow path collects ALL issues (no short-circuit), so cost ordering\n // buys nothing here — only the fast path's && chain benefits from it.\n for (const check of ir.checks) {\n // Opened lazily, on the first check that actually needs gating: an integer\n // format check in LAST position has nothing after it, so the guard would\n // be dead code.\n if (seenIntFormat && !intGuardOpen) {\n code += `if(Number.isInteger(${g.input})){`;\n intGuardOpen = true;\n }\n switch (check.kind) {\n case \"greater_than\":\n if (check.inclusive) {\n code += emit`\n if(${g.input}<${check.value}){\n ${tooSmall(g, check.value, \"number\", true, { message: check.message })}\n }`;\n } else {\n code += emit`\n if(${g.input}<=${check.value}){\n ${tooSmall(g, check.value, \"number\", false, { message: check.message })}\n }`;\n }\n break;\n case \"less_than\":\n if (check.inclusive) {\n code += emit`\n if(${g.input}>${check.value}){\n ${tooBig(g, check.value, \"number\", true, { message: check.message })}\n }`;\n } else {\n code += emit`\n if(${g.input}>=${check.value}){\n ${tooBig(g, check.value, \"number\", false, { message: check.message })}\n }`;\n }\n break;\n case \"number_format\": {\n const message = check.message ?? g.typeMsg;\n const intRange = INT_FORMAT_RANGES[check.format];\n if (intRange !== undefined) {\n // Mirrors $ZodCheckNumberFormat's integer branch, in ITS order: a\n // non-integer is an aborting `invalid_type`; an integer outside the\n // SAFE range is reported against the safe bounds — origin \"int\",\n // zod's explanatory note — and the check returns there, whatever the\n // format's own range says; only a safe integer reaches the format\n // range, reported with origin \"number\". So `z.int32()` on 1e21 is a\n // too_big with `maximum: 2^53-1` and origin \"int\", not one with\n // 2147483647 and origin \"number\" (the format range never runs for it),\n // and the same input against `z.int32().max(10)` reports BOTH the\n // safe-range issue and the max — the safe-range issue is continuable,\n // so later checks still run (see the isInteger guard above). safeint's\n // own range IS the safe range, so its third branch is dead and omitted.\n //\n // Key order is zod's push order minus the fields its finalizer\n // strips: `{code, maximum, note, origin, inclusive}` for the safe\n // range, where a format-range issue leads with `origin` like any\n // check-created size issue.\n const note = `note:\"Integers must be within the safe integer range.\"`;\n const msgProp = message !== undefined ? `,message:${JSON.stringify(message)}` : \"\";\n code += emit`\n if(!Number.isInteger(${g.input})){\n ${invalidType(g, \"int\", { extra: `format:\"${check.format}\"`, extraBeforeCode: true, message })}\n }else if(!Number.isSafeInteger(${g.input})){\n if(${g.input}>0){\n ${g.issues}.push({code:\"too_big\",maximum:9007199254740991,${note},origin:\"int\",inclusive:true,input:${g.input},path:${g.path}${msgProp}});\n }else{\n ${g.issues}.push({code:\"too_small\",minimum:-9007199254740991,${note},origin:\"int\",inclusive:true,input:${g.input},path:${g.path}${msgProp}});\n }\n }`;\n if (intRange !== null) {\n const [minimum, maximum] = intRange;\n code += emit`\n else if(${g.input}<${minimum}){\n ${tooSmall(g, minimum, \"number\", true, { message })}\n }else if(${g.input}>${maximum}){\n ${tooBig(g, maximum, \"number\", true, { message })}\n }`;\n }\n } else if (check.format === \"float32\") {\n code += emit`\n if(${g.input}<-3.4028234663852886e+38){\n ${tooSmall(g, \"-3.4028234663852886e+38\", \"number\", true, { message })}\n }else if(${g.input}>3.4028234663852886e+38){\n ${tooBig(g, \"3.4028234663852886e+38\", \"number\", true, { message })}\n }`;\n }\n // float64 range is [-Number.MAX_VALUE, Number.MAX_VALUE], already covered by the isFinite check above\n break;\n }\n case \"multiple_of\": {\n const message = check.message ?? g.typeMsg;\n const msgProp = message !== undefined ? `,message:${JSON.stringify(message)}` : \"\";\n // zod uses a float-safe remainder: raw % mis-rejects 0.3 % 0.1\n const fsr = emitRuntimeHelper(g.ctx, \"__zcFsr\", ZC_FSR_DECL);\n code += emit`\n if(${fsr}(${g.input},${check.value})!==0){\n ${g.issues}.push({origin:\"number\",code:\"not_multiple_of\",divisor:${check.value},input:${g.input},path:${g.path}${msgProp}});\n }`;\n break;\n }\n case \"refine_effect\":\n code += refineCheck(check, g.input, g);\n break;\n case \"super_refine_effect\":\n code += superRefineCheck(check, g.input, g);\n break;\n }\n if (opensIntGuard(check)) seenIntFormat = true;\n }\n if (intGuardOpen) code += `}`;\n code += `}`;\n }\n\n return `${code}\\n`;\n}\n\nexport function fastNumber(ir: NumberIR, g: FastGen): string | null {\n if (ir.coerce) return null;\n\n const x = g.input;\n const checks = ir.checks.filter((c): c is CheckIR => c.kind !== \"refine_effect\");\n\n // Number.isFinite(x) alone implies typeof number && !NaN && finite — zod's\n // entire number type gate in one non-coercing intrinsic. Likewise\n // Number.isSafeInteger covers the safeint format gate. Only the bitwise int\n // formats ((x|0)===x, (x>>>0)===x) still need the typeof guard: applying\n // ToNumber to an arbitrary input would invoke valueOf side effects zod\n // never triggers. Math.fround(Infinity)===Infinity, so float32 keeps the\n // isFinite gate.\n const hasSafeInt = checks.some((c) => c.kind === \"number_format\" && c.format === \"safeint\");\n const hasBitwiseInt = checks.some(\n (c) => c.kind === \"number_format\" && (c.format === \"int32\" || c.format === \"uint32\"),\n );\n const parts: string[] = [];\n if (hasBitwiseInt) {\n parts.push(`typeof ${x}===\"number\"`);\n } else if (!hasSafeInt) {\n parts.push(`Number.isFinite(${x})`);\n }\n\n for (const check of checks.sort(checkPriority)) {\n switch (check.kind) {\n case \"number_format\":\n switch (check.format) {\n case \"safeint\":\n parts.push(`Number.isSafeInteger(${x})`);\n break;\n case \"int32\":\n parts.push(`(${x}|0)===${x}`);\n break;\n case \"uint32\":\n parts.push(`${x}>=0`, `${x}<=4294967295`, `(${x}>>>0)===${x}`);\n break;\n case \"float32\":\n // zod's float32 check is a pure RANGE check (3.14 is accepted\n // even though Math.fround(3.14) !== 3.14) — fround here would\n // make the fast path stricter than the slow path / zod.\n parts.push(`${x}>=-3.4028234663852886e+38`, `${x}<=3.4028234663852886e+38`);\n break;\n case \"float64\":\n // All finite numbers are valid float64\n break;\n }\n break;\n case \"greater_than\":\n parts.push(check.inclusive ? `${x}>=${check.value}` : `${x}>${check.value}`);\n break;\n case \"less_than\":\n parts.push(check.inclusive ? `${x}<=${check.value}` : `${x}<${check.value}`);\n break;\n case \"multiple_of\": {\n const fsr = emitRuntimeHelper(g.ctx, \"__zcFsr\", ZC_FSR_DECL);\n parts.push(`${fsr}(${x},${check.value})===0`);\n break;\n }\n case \"min_length\":\n case \"max_length\":\n case \"length_equals\":\n case \"string_format\":\n case \"includes\":\n case \"starts_with\":\n case \"ends_with\":\n // String-only checks on a number schema — shouldn't happen, skip\n break;\n }\n }\n\n // Refine effect checks (appended last — run after cheap checks short-circuit)\n for (const check of ir.checks) {\n if (check.kind === \"refine_effect\") {\n parts.push(fastRefineTest(check, x, g));\n } else if (check.kind === \"super_refine_effect\") {\n parts.push(superRefineFastTest(check, x, g));\n }\n }\n\n return parts.join(\"&&\");\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"object.d.ts","names":[],"sources":["../../../../src/core/codegen/schemas/object.ts"],"mappings":";;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"object.d.ts","names":[],"sources":["../../../../src/core/codegen/schemas/object.ts"],"mappings":";;;;;;;;;;;;iBA2BgB,iBAAiB,IAAI,oBAAoB;iBAIzC,WAAW,IAAI;EAAa;GAAkB,GAAG;iBAiXjD,WAAW,IAAI,UAAU,GAAG"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { declareFastTemps,
|
|
1
|
+
import { declareFastTemps, emitRuntimeHelper, escapeString, extendPath, extendStaticPath, hasMutation, keyMembershipTest, outputAlwaysDefined } from "../context.js";
|
|
2
2
|
import { emit } from "../emit.js";
|
|
3
3
|
import { invalidType, unrecognizedKeys } from "../emit-issue.js";
|
|
4
4
|
import { ZC_AB_DECL, ZC_PROTO_SCRUB_DECL } from "../issue-decls.js";
|
|
5
|
-
import { refineCheck, superRefineCheck, superRefineFastTest } from "./effect.js";
|
|
5
|
+
import { fastRefineTest, refineCheck, superRefineCheck, superRefineFastTest } from "./effect.js";
|
|
6
6
|
import { orderByRuntimeCost } from "../fast-size.js";
|
|
7
7
|
//#region src/core/codegen/schemas/object.ts
|
|
8
8
|
/**
|
|
@@ -23,38 +23,55 @@ function slowObject(ir, g) {
|
|
|
23
23
|
${invalidType(g, "object")}
|
|
24
24
|
}else{`;
|
|
25
25
|
const strip = ir.stripUnknownKeys === true;
|
|
26
|
-
const
|
|
26
|
+
const rebuild = strip || ir.catchall !== void 0 && hasMutation(ir.catchall) || Object.values(ir.properties).some(hasMutation);
|
|
27
27
|
const objVar = g.temp("o");
|
|
28
|
-
if (!
|
|
29
|
-
code +=
|
|
28
|
+
if (!rebuild) {
|
|
29
|
+
code += `var ${objVar}=${g.input};`;
|
|
30
30
|
const scrub = emitRuntimeHelper(g.ctx, "__zcPs", ZC_PROTO_SCRUB_DECL);
|
|
31
31
|
code += `${objVar}=${scrub}(${objVar});`;
|
|
32
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Pass-through only: land a nested node's REPLACEMENT value on the output.
|
|
35
|
+
* A pass-through property never rewrites its value (that is what
|
|
36
|
+
* pass-through means), but a nested object or record can still hand back a
|
|
37
|
+
* proto-scrubbed COPY of its input, and that copy has to reach the output
|
|
38
|
+
* object — not the caller's object. Writing it straight into `objVar` did
|
|
39
|
+
* exactly that when `objVar` was the input: the caller's own object was
|
|
40
|
+
* edited in place (its nested value swapped for the copy), and a frozen
|
|
41
|
+
* input threw "Cannot assign to read only property" out of `safeParse`. So
|
|
42
|
+
* the output becomes a copy of its own the first time a replacement lands,
|
|
43
|
+
* and only then. Spread is safe here: `objVar` is still the input only when
|
|
44
|
+
* the scrub above found no own `__proto__` on it.
|
|
45
|
+
*/
|
|
46
|
+
const handoff = (outVar, inVar, keyExpr) => `if(${outVar}!==${inVar}){if(${objVar}===${g.input}){${objVar}={...${g.input}};}${objVar}[${keyExpr}]=${outVar};}`;
|
|
33
47
|
const refineMark = ir.checks && ir.checks.length > 0 ? g.temp("rm") : "";
|
|
34
48
|
if (refineMark) code += `var ${refineMark}=${g.issues}.length;`;
|
|
35
49
|
const skipAbsent = new Set(ir.skipAbsentKeys ?? []);
|
|
36
50
|
const suppressAbsent = new Set(ir.suppressAbsentKeys ?? []);
|
|
37
51
|
const nonoptional = new Set(ir.nonoptionalKeys ?? []);
|
|
38
|
-
/**
|
|
52
|
+
/** Rebuild only: per-property output slot + whether it is always in the result. */
|
|
39
53
|
const slots = [];
|
|
40
54
|
for (const [key, propIR] of parsedProperties(ir)) {
|
|
41
55
|
const keyStr = escapeString(key);
|
|
42
56
|
const propPath = extendStaticPath(g.path, key);
|
|
43
|
-
const propExpr =
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
57
|
+
const propExpr = g.temp("sv");
|
|
58
|
+
code += `var ${propExpr}=${g.input}[${keyStr}];`;
|
|
59
|
+
let outExpr = propExpr;
|
|
60
|
+
if (rebuild) slots.push({
|
|
61
|
+
always: outputAlwaysDefined(propIR),
|
|
62
|
+
keyStr,
|
|
63
|
+
value: propExpr
|
|
64
|
+
});
|
|
65
|
+
else {
|
|
66
|
+
outExpr = g.temp("ov");
|
|
67
|
+
code += `var ${outExpr}=${propExpr};`;
|
|
51
68
|
}
|
|
52
69
|
const propCode = g.visit(propIR, {
|
|
53
70
|
input: propExpr,
|
|
54
|
-
output:
|
|
71
|
+
output: outExpr,
|
|
55
72
|
path: propPath
|
|
56
73
|
});
|
|
57
|
-
const dropValue =
|
|
74
|
+
const dropValue = rebuild ? `${propExpr}=undefined;` : `${outExpr}=${propExpr};`;
|
|
58
75
|
if (skipAbsent.has(key)) code += emit`if(${keyStr} in ${g.input}){${propCode}}`;
|
|
59
76
|
else if (suppressAbsent.has(key)) {
|
|
60
77
|
const beforeVar = g.temp("ob");
|
|
@@ -85,8 +102,9 @@ function slowObject(ir, g) {
|
|
|
85
102
|
}
|
|
86
103
|
}`;
|
|
87
104
|
} else code += propCode;
|
|
105
|
+
if (!rebuild) code += handoff(outExpr, propExpr, keyStr);
|
|
88
106
|
}
|
|
89
|
-
if (
|
|
107
|
+
if (rebuild) {
|
|
90
108
|
const literal = [];
|
|
91
109
|
let appends = "";
|
|
92
110
|
let leading = true;
|
|
@@ -99,6 +117,14 @@ function slowObject(ir, g) {
|
|
|
99
117
|
appends += slot.always ? `${objVar}[${slot.keyStr}]=${slot.value};` : `if(${slot.value}!==undefined||(${slot.keyStr} in ${g.input})){${objVar}[${slot.keyStr}]=${slot.value};}`;
|
|
100
118
|
}
|
|
101
119
|
code += `var ${objVar}={${literal.join(",")}};${appends}`;
|
|
120
|
+
if (!strip && !ir.strict && ir.catchall === void 0) {
|
|
121
|
+
const kVar = g.temp("lk");
|
|
122
|
+
const test = keyMembershipTest(g.ctx, Object.keys(ir.properties), kVar);
|
|
123
|
+
code += emit`
|
|
124
|
+
for(var ${kVar} in ${g.input}){
|
|
125
|
+
if(!(${test})&&${kVar}!=="__proto__"){${objVar}[${kVar}]=${g.input}[${kVar}];}
|
|
126
|
+
}`;
|
|
127
|
+
}
|
|
102
128
|
}
|
|
103
129
|
if (ir.strict) {
|
|
104
130
|
const keys = Object.keys(ir.properties);
|
|
@@ -118,21 +144,30 @@ function slowObject(ir, g) {
|
|
|
118
144
|
const keys = Object.keys(ir.properties);
|
|
119
145
|
const kVar = g.temp("ck");
|
|
120
146
|
const test = keyMembershipTest(g.ctx, keys, kVar);
|
|
121
|
-
const
|
|
122
|
-
|
|
147
|
+
const catchallPath = extendPath(g.path, kVar);
|
|
148
|
+
let body;
|
|
149
|
+
if (rebuild) {
|
|
150
|
+
const slot = `${objVar}[${kVar}]`;
|
|
151
|
+
body = `${slot}=${g.input}[${kVar}];${g.visit(ir.catchall, {
|
|
152
|
+
input: slot,
|
|
153
|
+
output: slot,
|
|
154
|
+
path: catchallPath
|
|
155
|
+
})}`;
|
|
156
|
+
} else {
|
|
157
|
+
const inVar = g.temp("cv");
|
|
158
|
+
const outVar = g.temp("co");
|
|
159
|
+
body = `var ${inVar}=${g.input}[${kVar}];var ${outVar}=${inVar};` + g.visit(ir.catchall, {
|
|
160
|
+
input: inVar,
|
|
161
|
+
output: outVar,
|
|
162
|
+
path: catchallPath
|
|
163
|
+
}) + handoff(outVar, inVar, kVar);
|
|
164
|
+
}
|
|
123
165
|
code += emit`
|
|
124
166
|
for(var ${kVar} in ${g.input}){
|
|
125
|
-
if(!(${test})&&${kVar}!=="__proto__"){
|
|
126
|
-
${seed}
|
|
127
|
-
${g.visit(ir.catchall, {
|
|
128
|
-
input: slot,
|
|
129
|
-
output: slot,
|
|
130
|
-
path: extendPath(g.path, kVar)
|
|
131
|
-
})}
|
|
132
|
-
}
|
|
167
|
+
if(!(${test})&&${kVar}!=="__proto__"){${body}}
|
|
133
168
|
}`;
|
|
134
169
|
}
|
|
135
|
-
|
|
170
|
+
code += `${g.output}=${objVar};`;
|
|
136
171
|
if (ir.checks && ir.checks.length > 0) {
|
|
137
172
|
let refines = "";
|
|
138
173
|
for (const check of ir.checks) refines += check.kind === "super_refine_effect" ? superRefineCheck(check, objVar, g) : refineCheck(check, objVar, g);
|
|
@@ -199,7 +234,7 @@ function fastObjectBody(ir, g, skipKey) {
|
|
|
199
234
|
}
|
|
200
235
|
}
|
|
201
236
|
if (ir.checks) {
|
|
202
|
-
for (const check of ir.checks) if (check.kind === "refine_effect") parts.push(
|
|
237
|
+
for (const check of ir.checks) if (check.kind === "refine_effect") parts.push(fastRefineTest(check, x, g));
|
|
203
238
|
else if (check.kind === "super_refine_effect") parts.push(superRefineFastTest(check, x, g));
|
|
204
239
|
}
|
|
205
240
|
return parts;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"object.js","names":[],"sources":["../../../../src/core/codegen/schemas/object.ts"],"sourcesContent":["import type { ObjectIR, SchemaIR } from \"../../types.js\";\nimport type { FastGen, SlowGen } from \"../context.js\";\nimport {\n declareFastTemps,\n emitEffectCallable,\n emitRuntimeHelper,\n escapeString,\n extendPath,\n extendStaticPath,\n hasMutation,\n keyMembershipTest,\n outputAlwaysDefined,\n} from \"../context.js\";\nimport { emit } from \"../emit.js\";\nimport { invalidType, unrecognizedKeys } from \"../emit-issue.js\";\nimport { ZC_AB_DECL, ZC_PROTO_SCRUB_DECL } from \"../issue-decls.js\";\nimport { orderByRuntimeCost } from \"../fast-size.js\";\nimport { refineCheck, superRefineCheck, superRefineFastTest } from \"./effect.js\";\n\n/**\n * The shape entries whose schemas actually RUN. `$ZodObject` skips a declared\n * `__proto__` key in its shape loop (`if (key === \"__proto__\") continue`), so\n * that property is never validated and never written to the output — it is\n * only a recognized name for the strict/catchall passes, which read\n * `Object.keys(ir.properties)` directly. Writing it would be worse than\n * pointless: `{__proto__: v}` in an object literal, and `o[\"__proto__\"] = v`\n * on a plain object, both SET THE PROTOTYPE rather than define a key.\n */\nexport function parsedProperties(ir: ObjectIR): [string, SchemaIR][] {\n return Object.entries(ir.properties).filter(([key]) => key !== \"__proto__\");\n}\n\nexport function slowObject(ir: SchemaIR & { type: \"object\" }, g: SlowGen): string {\n let code = emit`\n if(typeof ${g.input}!==\"object\"||${g.input}===null||Array.isArray(${g.input})){\n ${invalidType(g, \"object\")}\n }else{`;\n\n // Strip mode (zod's default z.object() output) rebuilds a FRESH object from\n // the declared keys, so it always writes back. Otherwise clone only when a\n // property mutates the value.\n const strip = ir.stripUnknownKeys === true;\n const needsClone =\n strip ||\n (ir.catchall !== undefined && hasMutation(ir.catchall)) ||\n Object.values(ir.properties).some(hasMutation);\n const objVar = g.temp(\"o\");\n if (!strip) {\n // Spread, not Object.assign: V8's CloneObjectIC makes `{...x}` ~25% faster\n // on the whole safeParse call for mutation-bearing schemas.\n code += needsClone ? `var ${objVar}={...${g.input}};` : `var ${objVar}=${g.input};`;\n // A loose or catchall output is the INPUT, or a spread of it — and spread\n // copies an own `__proto__` as a plain data property — where zod's fresh\n // `{}` never receives the key at all. Scrub it (see ZC_PROTO_SCRUB_DECL); a\n // strip object rebuilds and needs nothing here.\n const scrub = emitRuntimeHelper(g.ctx, \"__zcPs\", ZC_PROTO_SCRUB_DECL);\n code += `${objVar}=${scrub}(${objVar});`;\n }\n\n // Object-level refines are gated on zod's ABORT rule, not on \"did anything\n // fail\": zod parses the properties (plus the strict/catchall passes) into the\n // payload and then skips its check chain only when `util.aborted` holds — i.e.\n // when one of those issues is non-continuable. A property that failed its own\n // `min`/format check reports a CONTINUABLE issue, so the outer refine still\n // runs and both messages surface — and so does an unrecognized key, which zod\n // pushes with `continue: true`; a property that failed to parse at all\n // (`invalid_type`, a bad record key) aborts and suppresses it. Snapshot the\n // issue count before the properties so the scan covers exactly this node's\n // own parse, as zod's fresh sub-payload does.\n const refineMark = ir.checks && ir.checks.length > 0 ? g.temp(\"rm\") : \"\";\n if (refineMark) code += `var ${refineMark}=${g.issues}.length;`;\n\n const skipAbsent = new Set(ir.skipAbsentKeys ?? []);\n const suppressAbsent = new Set(ir.suppressAbsentKeys ?? []);\n const nonoptional = new Set(ir.nonoptionalKeys ?? []);\n /** Strip only: per-property output slot + whether it is always in the result. */\n const slots: { always: boolean; keyStr: string; value: string }[] = [];\n\n for (const [key, propIR] of parsedProperties(ir)) {\n const keyStr = escapeString(key);\n const propPath = extendStaticPath(g.path, key);\n // Strip validates the value read from the INPUT, held in a local, and\n // assembles the result afterwards. Reading through the half-built copy (the\n // previous shape) both cost a megamorphic access per key — the copy's map\n // changes with every key added — and diverged from zod, which parses\n // `input[key]` and so accepts a value found on the prototype.\n const propExpr = strip ? g.temp(\"sv\") : `${objVar}[${keyStr}]`;\n if (strip) {\n code += `var ${propExpr}=${g.input}[${keyStr}];`;\n slots.push({ always: outputAlwaysDefined(propIR), keyStr, value: propExpr });\n }\n const propCode = g.visit(propIR, { input: propExpr, output: propExpr, path: propPath });\n // The three absent-key rules of zod's handlePropertyResult (see ObjectIR).\n // Presence is tested on the ORIGINAL input, as zod's `key in input` is —\n // a clone would hide an inherited key.\n //\n // The two rules that test presence AFTER running the property snapshot it\n // FIRST. Running the property can create the very key the test asks about:\n // where nothing mutates, `objVar` IS `g.input`, so the write-back of a\n // property that accepted `undefined` (`o[key]=result`) adds an own `key`\n // and the guard then reads it as present. `z.looseObject({a: z.custom()})`\n // on `{}` lost its `nonoptional` issue entirely that way — the fast path\n // still rejected on `\"a\" in x`, so `safeParse` failed with an EMPTY issue\n // array — and left the caller's object carrying an `a` zod never writes.\n const dropValue = strip ? `${propExpr}=undefined;` : `delete ${objVar}[${keyStr}];`;\n if (skipAbsent.has(key)) {\n code += emit`if(${keyStr} in ${g.input}){${propCode}}`;\n } else if (suppressAbsent.has(key)) {\n // A defaulted optional-out property runs, but a failure on an absent key\n // is discarded whole: issues and whatever value it wrote before failing.\n const beforeVar = g.temp(\"ob\");\n const presentVar = g.temp(\"op\");\n code += emit`\n var ${presentVar}=${keyStr} in ${g.input};\n var ${beforeVar}=${g.issues}.length;\n ${propCode}\n if(!${presentVar}&&${g.issues}.length>${beforeVar}){\n ${g.issues}.length=${beforeVar};${dropValue}\n }`;\n } else if (nonoptional.has(key)) {\n // A required key that is absent fails as such when its schema raised\n // nothing for the `undefined` it saw. zod pushes this one without an\n // `inst`, so no schema-level message reaches it — and it returns without\n // assigning, so whatever the property made of `undefined` is dropped.\n const beforeVar = g.temp(\"ob\");\n const presentVar = g.temp(\"op\");\n code += emit`\n var ${presentVar}=${keyStr} in ${g.input};\n var ${beforeVar}=${g.issues}.length;\n ${propCode}\n if(!${presentVar}){\n ${dropValue}\n if(${g.issues}.length===${beforeVar}){\n ${invalidType(g, \"nonoptional\", { input: \"undefined\", path: propPath, codeFirst: true, useTypeMsg: false })}\n }\n }`;\n } else {\n code += propCode;\n }\n }\n\n if (strip) {\n // Assemble the result in shape order (zod's key order), taking as long a\n // LEADING run of always-present keys as possible into one object literal:\n // V8 stamps a literal out of a cached boilerplate map in a single\n // allocation, where adding keys one at a time walks a transition chain and\n // re-checks the map on every store — measured 8.1x (20 keys) / 2.8x (7\n // keys, one optional) on the whole safeParse.\n //\n // Everything after the first conditional key is appended so insertion order\n // still matches zod. The per-key test is zod's own: keep the key when the\n // parsed value is defined, or when it was present on the input at all\n // (`key in input`, prototype included — an own `k: undefined` survives).\n const literal: string[] = [];\n let appends = \"\";\n let leading = true;\n for (const slot of slots) {\n if (leading && slot.always) {\n literal.push(`${slot.keyStr}:${slot.value}`);\n continue;\n }\n leading = false;\n appends += slot.always\n ? `${objVar}[${slot.keyStr}]=${slot.value};`\n : `if(${slot.value}!==undefined||(${slot.keyStr} in ${g.input})){${objVar}[${slot.keyStr}]=${slot.value};}`;\n }\n code += `var ${objVar}={${literal.join(\",\")}};${appends}`;\n }\n\n // Strict unknown-key pass — zod's handleCatchall, byte-exact: for-in over\n // the ORIGINAL input (inherited enumerable keys count, no hasOwnProperty),\n // ALL unknown keys collected into one issue, pushed AFTER property issues\n // and before object-level refines.\n if (ir.strict) {\n const keys = Object.keys(ir.properties);\n const ukVar = g.temp(\"uk\");\n const kVar = g.temp(\"k\");\n const test = keyMembershipTest(g.ctx, keys, kVar);\n code += emit`\n var ${ukVar}=null;\n for(var ${kVar} in ${g.input}){\n if(!(${test})){(${ukVar}=${ukVar}||[]).push(${kVar});}\n }\n if(${ukVar}!==null){\n ${unrecognizedKeys(g, ukVar)}\n }`;\n }\n\n // .catchall(schema): validate every key NOT in the shape, mirroring zod's\n // handleCatchall — same bare for-in over the ORIGINAL input as the strict\n // pass (inherited enumerable keys count, no hasOwnProperty guard), each\n // issue reported at the key. Runs after the properties, as zod does. An\n // undeclared `__proto__` is skipped like zod skips it: never validated, and\n // never assigned, since `o[\"__proto__\"]=v` on the clone would replace its\n // prototype instead of adding a key. (The strict pass above still REPORTS\n // it — there it is an unknown key like any other.)\n //\n // The key's slot is BOTH the input and the output, exactly as a shape\n // property's is: a value-rewriting catchall (coerce, .trim(), a default)\n // writes through the same expression it later re-reads, so splitting them\n // makes the rewrite invisible to its own checks.\n //\n // When the catchall mutates, objVar is a clone, and each unknown key is\n // copied into it before validation — which also reproduces zod, whose fresh\n // output object gains an OWN key for every for-in key it saw, inherited ones\n // included. A non-mutating catchall writes nothing, so objVar is the input\n // and the object stays pass-through by reference.\n if (ir.catchall) {\n const keys = Object.keys(ir.properties);\n const kVar = g.temp(\"ck\");\n const test = keyMembershipTest(g.ctx, keys, kVar);\n const slot = `${objVar}[${kVar}]`;\n const seed = needsClone ? `${slot}=${g.input}[${kVar}];` : \"\";\n code += emit`\n for(var ${kVar} in ${g.input}){\n if(!(${test})&&${kVar}!==\"__proto__\"){\n ${seed}\n ${g.visit(ir.catchall, {\n input: slot,\n output: slot,\n path: extendPath(g.path, kVar),\n })}\n }\n }`;\n }\n\n // `!strip` also writes back: the scrub above may have re-pointed `objVar` at\n // a copy even where nothing else mutates.\n if (needsClone || !strip) {\n code += `${g.output}=${objVar};`;\n }\n\n // Object-level refine effects: z.object({...}).refine(fn), suppressed when the\n // parse phase aborted (see refineMark). One gate covers every effect: each of\n // them can only add a continuable `custom` issue, so zod's per-check\n // re-evaluation of `isAborted` can never flip between them.\n if (ir.checks && ir.checks.length > 0) {\n let refines = \"\";\n for (const check of ir.checks) {\n refines +=\n check.kind === \"super_refine_effect\"\n ? superRefineCheck(check, objVar, g)\n : refineCheck(check, objVar, g);\n }\n const aborted = emitRuntimeHelper(g.ctx, \"__zcAb\", ZC_AB_DECL);\n code += `if(!${aborted}(${g.issues},${refineMark})){${refines}}`;\n }\n\n code += `}\\n`;\n return code;\n}\n\n/**\n * Property entries in the order their fast-checks should be `&&`-chained:\n * cheapest first, declaration order preserved among equals (Array#sort is\n * stable). Valid input runs every conjunct whatever the order, so this costs\n * nothing on the hot path; a REJECT stops at the first false conjunct, so\n * pricing a `z.email()` behind the `kind` literal that actually discriminates\n * is what makes union probing and `.is()` misses cheap (see\n * estimateRuntimeCost). The SLOW path keeps declaration order — that one's\n * output is the issue list, whose order is part of zod parity.\n */\nfunction orderedProperties(ir: ObjectIR, g: FastGen): [string, SchemaIR][] {\n return orderByRuntimeCost(parsedProperties(ir), ([, propIR]) => propIR, g.ctx);\n}\n\n/**\n * Property/strict/refine fast-checks for an object, WITHOUT the leading\n * `typeof===object && !==null && !Array.isArray` type-guard. Returns the\n * conjunct parts (joinable with `&&`), or null if any child is fast-ineligible.\n *\n * `skipKey`, when given, omits that one property's check. Used by the\n * discriminated-union fast path (via `g.discSkipKey`): the enclosing `switch`\n * has already matched the discriminator's value, so re-checking it is redundant.\n */\nfunction fastObjectBody(ir: ObjectIR, g: FastGen, skipKey?: string): string[] | null {\n const x = g.input;\n const parts: string[] = [];\n const absentAccepts = new Set([...(ir.skipAbsentKeys ?? []), ...(ir.suppressAbsentKeys ?? [])]);\n const nonoptional = new Set(ir.nonoptionalKeys ?? []);\n\n for (const [key, propIR] of orderedProperties(ir, g)) {\n if (key === skipKey) continue;\n const keyStr = escapeString(key);\n const propCheck = g.visit(propIR, { input: `${x}[${keyStr}]` });\n if (propCheck === null) return null; // All-or-nothing\n // Presence rules keyed on the schema's `optin`/`optout` (see ObjectIR): a\n // required key must be there whatever its schema makes of `undefined`, and\n // an optional-out key on the \"optional\"/\"defaulted\" rungs is accepted when\n // it is not, whatever the property would say.\n if (nonoptional.has(key)) {\n parts.push(propCheck === \"true\" ? `${keyStr} in ${x}` : `${keyStr} in ${x}&&${propCheck}`);\n } else if (absentAccepts.has(key)) {\n parts.push(`(!(${keyStr} in ${x})||${propCheck})`);\n } else {\n parts.push(propCheck);\n }\n }\n\n // Strict unknown-key pass: hosted boolean helper (a for-in loop cannot live\n // in the && chain). Same for-in iteration as the slow path — fast/slow\n // agreement is load-bearing under the __zcFinD deferral. The membership set is\n // the FULL key list (the discriminator is a recognized key), independent of\n // skipKey, which only suppresses re-validating the discriminator's value.\n if (ir.strict) {\n const keys = Object.keys(ir.properties);\n const fnName = g.temp(\"so\");\n const test = keyMembershipTest(g.ctx, keys, \"k\");\n g.ctx.preamble.push(\n `function ${fnName}(o){for(var k in o){if(!(${test}))return false;}return true;}`,\n );\n parts.push(`${fnName}(${x})`);\n }\n\n // .catchall(schema): same hosted for-in as strict, but each unrecognized key\n // is checked against the catchall rather than rejected outright. The value is\n // hoisted into a local for the same reason fastRecord does it — repeated\n // `o[k]` computed loads are not eliminated across check boundaries.\n if (ir.catchall) {\n const kv = g.temp(\"cak\");\n const vv = g.temp(\"cav\");\n const catchallGen = g.scoped(vv);\n const valCheck = catchallGen.visit(ir.catchall, { input: vv });\n if (valCheck === null) return null;\n if (valCheck !== \"true\") {\n const fnName = g.temp(\"co\");\n const test = keyMembershipTest(g.ctx, Object.keys(ir.properties), kv);\n g.ctx.preamble.push(\n `function ${fnName}(o){${declareFastTemps(catchallGen.scope)}var ${kv},${vv};for(${kv} in o){if(!(${test})&&${kv}!==\"__proto__\"){${vv}=o[${kv}];if(!(${valCheck}))return false;}}return true;}`,\n );\n parts.push(`${fnName}(${x})`);\n }\n }\n\n // Object-level refine effects (appended last — run after property checks short-circuit)\n if (ir.checks) {\n for (const check of ir.checks) {\n if (check.kind === \"refine_effect\") {\n parts.push(`${emitEffectCallable(g.ctx, check)}(${x})`);\n } else if (check.kind === \"super_refine_effect\") {\n parts.push(superRefineFastTest(check, x, g));\n }\n }\n }\n\n return parts;\n}\n\nexport function fastObject(ir: ObjectIR, g: FastGen): string | null {\n // A strip object still gets an expression. Stripping reshapes the OUTPUT, not\n // the verdict, so this remains an exact acceptance predicate — which is what\n // `.is()` installs, and what the build path reuses to validate the subtrees it\n // passes through by reference. What it must NOT do is stand in for the parse\n // result: generateValidator withholds the `data:input` shortcut whenever the\n // schema rebuilds (see rebuildsOutput).\n const x = g.input;\n const body = fastObjectBody(ir, g, g.discSkipKey);\n if (body === null) return null;\n // Discriminated-union option: the enclosing switch (and the caller's guard)\n // already established object-ness and the discriminator value, so emit only\n // the remaining checks — no leading type-guard. An option with nothing left\n // to check accepts unconditionally (\"true\").\n if (g.discSkipKey !== undefined) {\n return body.length > 0 ? body.join(\"&&\") : \"true\";\n }\n return [`typeof ${x}===\"object\"`, `${x}!==null`, `!Array.isArray(${x})`, ...body].join(\"&&\");\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"object.js","names":[],"sources":["../../../../src/core/codegen/schemas/object.ts"],"sourcesContent":["import type { ObjectIR, SchemaIR } from \"../../types.js\";\nimport type { FastGen, SlowGen } from \"../context.js\";\nimport {\n declareFastTemps,\n emitRuntimeHelper,\n escapeString,\n extendPath,\n extendStaticPath,\n hasMutation,\n keyMembershipTest,\n outputAlwaysDefined,\n} from \"../context.js\";\nimport { emit } from \"../emit.js\";\nimport { invalidType, unrecognizedKeys } from \"../emit-issue.js\";\nimport { ZC_AB_DECL, ZC_PROTO_SCRUB_DECL } from \"../issue-decls.js\";\nimport { orderByRuntimeCost } from \"../fast-size.js\";\nimport { fastRefineTest, refineCheck, superRefineCheck, superRefineFastTest } from \"./effect.js\";\n\n/**\n * The shape entries whose schemas actually RUN. `$ZodObject` skips a declared\n * `__proto__` key in its shape loop (`if (key === \"__proto__\") continue`), so\n * that property is never validated and never written to the output — it is\n * only a recognized name for the strict/catchall passes, which read\n * `Object.keys(ir.properties)` directly. Writing it would be worse than\n * pointless: `{__proto__: v}` in an object literal, and `o[\"__proto__\"] = v`\n * on a plain object, both SET THE PROTOTYPE rather than define a key.\n */\nexport function parsedProperties(ir: ObjectIR): [string, SchemaIR][] {\n return Object.entries(ir.properties).filter(([key]) => key !== \"__proto__\");\n}\n\nexport function slowObject(ir: SchemaIR & { type: \"object\" }, g: SlowGen): string {\n let code = emit`\n if(typeof ${g.input}!==\"object\"||${g.input}===null||Array.isArray(${g.input})){\n ${invalidType(g, \"object\")}\n }else{`;\n\n // zod parses every object into a FRESH `{}`: the shape keys land first, in\n // shape order (handlePropertyResult), and a loose or catchall object then\n // appends the unknown keys in the input's for-in order (handleCatchall).\n //\n // Two modes reproduce that. REBUILD assembles exactly that object: always\n // for strip mode (zod's default z.object() output is only the declared keys),\n // and for a loose, strict or catchall object whenever a property or the\n // catchall rewrites its value — a rewritten value has to land somewhere, and\n // a spread clone of the input (what this once did) put it at the wrong\n // position: the input's key order, with a substituted key appended last.\n // PASS-THROUGH hands the input back by reference when nothing rewrites\n // anything; identity, key order and inherited keys then ride along with it,\n // which is the documented container-identity divergence (see\n // known-divergences.test.ts).\n const strip = ir.stripUnknownKeys === true;\n const rebuild =\n strip ||\n (ir.catchall !== undefined && hasMutation(ir.catchall)) ||\n Object.values(ir.properties).some(hasMutation);\n const objVar = g.temp(\"o\");\n if (!rebuild) {\n code += `var ${objVar}=${g.input};`;\n // The pass-through output is the input, and an own `__proto__` must not\n // ride along on it where zod's fresh `{}` never receives the key. Scrub it\n // (see ZC_PROTO_SCRUB_DECL); a rebuilt object never copies the key in the\n // first place.\n const scrub = emitRuntimeHelper(g.ctx, \"__zcPs\", ZC_PROTO_SCRUB_DECL);\n code += `${objVar}=${scrub}(${objVar});`;\n }\n /**\n * Pass-through only: land a nested node's REPLACEMENT value on the output.\n * A pass-through property never rewrites its value (that is what\n * pass-through means), but a nested object or record can still hand back a\n * proto-scrubbed COPY of its input, and that copy has to reach the output\n * object — not the caller's object. Writing it straight into `objVar` did\n * exactly that when `objVar` was the input: the caller's own object was\n * edited in place (its nested value swapped for the copy), and a frozen\n * input threw \"Cannot assign to read only property\" out of `safeParse`. So\n * the output becomes a copy of its own the first time a replacement lands,\n * and only then. Spread is safe here: `objVar` is still the input only when\n * the scrub above found no own `__proto__` on it.\n */\n const handoff = (outVar: string, inVar: string, keyExpr: string): string =>\n `if(${outVar}!==${inVar}){if(${objVar}===${g.input}){${objVar}={...${g.input}};}${objVar}[${keyExpr}]=${outVar};}`;\n\n // Object-level refines are gated on zod's ABORT rule, not on \"did anything\n // fail\": zod parses the properties (plus the strict/catchall passes) into the\n // payload and then skips its check chain only when `util.aborted` holds — i.e.\n // when one of those issues is non-continuable. A property that failed its own\n // `min`/format check reports a CONTINUABLE issue, so the outer refine still\n // runs and both messages surface — and so does an unrecognized key, which zod\n // pushes with `continue: true`; a property that failed to parse at all\n // (`invalid_type`, a bad record key) aborts and suppresses it. Snapshot the\n // issue count before the properties so the scan covers exactly this node's\n // own parse, as zod's fresh sub-payload does.\n const refineMark = ir.checks && ir.checks.length > 0 ? g.temp(\"rm\") : \"\";\n if (refineMark) code += `var ${refineMark}=${g.issues}.length;`;\n\n const skipAbsent = new Set(ir.skipAbsentKeys ?? []);\n const suppressAbsent = new Set(ir.suppressAbsentKeys ?? []);\n const nonoptional = new Set(ir.nonoptionalKeys ?? []);\n /** Rebuild only: per-property output slot + whether it is always in the result. */\n const slots: { always: boolean; keyStr: string; value: string }[] = [];\n\n for (const [key, propIR] of parsedProperties(ir)) {\n const keyStr = escapeString(key);\n const propPath = extendStaticPath(g.path, key);\n // Every mode validates the value read from the ORIGINAL input, held in a\n // local. zod parses `input[key]` — a prototype-inclusive read — so a value\n // found on the prototype is accepted and, on a rebuild, copied out as an\n // own key. Reading through a spread clone, as the loose/strict/catchall\n // branch once did, turned such a value into `undefined` (spread copies own\n // keys only): the fast and build paths, which read `input[key]`, rejected\n // the input, this walk then found nothing wrong with it, and a strict\n // object came back as a failure with an EMPTY issue list. The local also\n // spares every check the megamorphic re-load of `o[key]` — the clone's map\n // changed with every key added.\n const propExpr = g.temp(\"sv\");\n code += `var ${propExpr}=${g.input}[${keyStr}];`;\n // A rebuild assembles the output from the local afterwards (in shape order,\n // below). A pass-through property is handed a slot of its own to write a\n // replacement into, which `handoff` then lands on the output.\n let outExpr = propExpr;\n if (rebuild) {\n slots.push({ always: outputAlwaysDefined(propIR), keyStr, value: propExpr });\n } else {\n outExpr = g.temp(\"ov\");\n code += `var ${outExpr}=${propExpr};`;\n }\n const propCode = g.visit(propIR, { input: propExpr, output: outExpr, path: propPath });\n // The three absent-key rules of zod's handlePropertyResult (see ObjectIR).\n // Presence is tested on the ORIGINAL input, as zod's `key in input` is —\n // a clone would hide an inherited key.\n //\n // The two rules that test presence AFTER running the property snapshot it\n // FIRST, exactly as zod reads `isPresent` before it looks at the result. A\n // property writes nowhere but its own local, so nothing it makes of\n // `undefined` can create the very key it is asked about (writing straight\n // into the output object once did that: `z.looseObject({a: z.custom()})` on\n // `{}` lost its `nonoptional` issue and left the caller's object carrying\n // an `a` zod never writes); dropping its value is resetting that local.\n const dropValue = rebuild ? `${propExpr}=undefined;` : `${outExpr}=${propExpr};`;\n if (skipAbsent.has(key)) {\n code += emit`if(${keyStr} in ${g.input}){${propCode}}`;\n } else if (suppressAbsent.has(key)) {\n // A defaulted optional-out property runs, but a failure on an absent key\n // is discarded whole: issues and whatever value it wrote before failing.\n const beforeVar = g.temp(\"ob\");\n const presentVar = g.temp(\"op\");\n code += emit`\n var ${presentVar}=${keyStr} in ${g.input};\n var ${beforeVar}=${g.issues}.length;\n ${propCode}\n if(!${presentVar}&&${g.issues}.length>${beforeVar}){\n ${g.issues}.length=${beforeVar};${dropValue}\n }`;\n } else if (nonoptional.has(key)) {\n // A required key that is absent fails as such when its schema raised\n // nothing for the `undefined` it saw. zod pushes this one without an\n // `inst`, so no schema-level message reaches it — and it returns without\n // assigning, so whatever the property made of `undefined` is dropped.\n const beforeVar = g.temp(\"ob\");\n const presentVar = g.temp(\"op\");\n code += emit`\n var ${presentVar}=${keyStr} in ${g.input};\n var ${beforeVar}=${g.issues}.length;\n ${propCode}\n if(!${presentVar}){\n ${dropValue}\n if(${g.issues}.length===${beforeVar}){\n ${invalidType(g, \"nonoptional\", { input: \"undefined\", path: propPath, codeFirst: true, useTypeMsg: false })}\n }\n }`;\n } else {\n code += propCode;\n }\n if (!rebuild) code += handoff(outExpr, propExpr, keyStr);\n }\n\n if (rebuild) {\n // Assemble the result in shape order (zod's key order), taking as long a\n // LEADING run of always-present keys as possible into one object literal:\n // V8 stamps a literal out of a cached boilerplate map in a single\n // allocation, where adding keys one at a time walks a transition chain and\n // re-checks the map on every store — measured 8.1x (20 keys) / 2.8x (7\n // keys, one optional) on the whole safeParse.\n //\n // Everything after the first conditional key is appended so insertion order\n // still matches zod. The per-key test is zod's own: keep the key when the\n // parsed value is defined, or when it was present on the input at all\n // (`key in input`, prototype included — an own `k: undefined` survives).\n const literal: string[] = [];\n let appends = \"\";\n let leading = true;\n for (const slot of slots) {\n if (leading && slot.always) {\n literal.push(`${slot.keyStr}:${slot.value}`);\n continue;\n }\n leading = false;\n appends += slot.always\n ? `${objVar}[${slot.keyStr}]=${slot.value};`\n : `if(${slot.value}!==undefined||(${slot.keyStr} in ${g.input})){${objVar}[${slot.keyStr}]=${slot.value};}`;\n }\n code += `var ${objVar}={${literal.join(\",\")}};${appends}`;\n\n // A rebuilt loose object then takes every unknown key, in the input's\n // for-in order — zod's handleCatchall running its `unknown` catchall, which\n // hands each value back as it is and assigns it as an own key, inherited\n // ones included. (A catchall with a schema does the same in its own pass\n // below; a strict object reports the keys instead.)\n if (!strip && !ir.strict && ir.catchall === undefined) {\n const kVar = g.temp(\"lk\");\n const test = keyMembershipTest(g.ctx, Object.keys(ir.properties), kVar);\n code += emit`\n for(var ${kVar} in ${g.input}){\n if(!(${test})&&${kVar}!==\"__proto__\"){${objVar}[${kVar}]=${g.input}[${kVar}];}\n }`;\n }\n }\n\n // Strict unknown-key pass — zod's handleCatchall, byte-exact: for-in over\n // the ORIGINAL input (inherited enumerable keys count, no hasOwnProperty),\n // ALL unknown keys collected into one issue, pushed AFTER property issues\n // and before object-level refines.\n if (ir.strict) {\n const keys = Object.keys(ir.properties);\n const ukVar = g.temp(\"uk\");\n const kVar = g.temp(\"k\");\n const test = keyMembershipTest(g.ctx, keys, kVar);\n code += emit`\n var ${ukVar}=null;\n for(var ${kVar} in ${g.input}){\n if(!(${test})){(${ukVar}=${ukVar}||[]).push(${kVar});}\n }\n if(${ukVar}!==null){\n ${unrecognizedKeys(g, ukVar)}\n }`;\n }\n\n // .catchall(schema): validate every key NOT in the shape, mirroring zod's\n // handleCatchall — same bare for-in over the ORIGINAL input as the strict\n // pass (inherited enumerable keys count, no hasOwnProperty guard), each\n // issue reported at the key. Runs after the properties, as zod does. An\n // undeclared `__proto__` is skipped like zod skips it: never validated, and\n // never assigned, since `o[\"__proto__\"]=v` on a plain object would replace\n // its prototype instead of adding a key. (The strict pass above still\n // REPORTS it — there it is an unknown key like any other.)\n //\n // On a rebuild each unknown key is copied onto the fresh output before it is\n // validated — which reproduces zod, whose fresh output object gains an OWN\n // key for every for-in key it saw, inherited ones included, whether or not\n // the catchall accepted its value. The key's slot is then BOTH the input and\n // the output, exactly as a shape property's local is: a value-rewriting\n // catchall (coerce, .trim(), a default) writes through the same expression\n // it later re-reads, so splitting them makes the rewrite invisible to its own\n // checks. A pass-through catchall writes nothing, so the object stays by\n // reference; it gets the same replacement slot a pass-through property does,\n // for the same reason.\n if (ir.catchall) {\n const keys = Object.keys(ir.properties);\n const kVar = g.temp(\"ck\");\n const test = keyMembershipTest(g.ctx, keys, kVar);\n const catchallPath = extendPath(g.path, kVar);\n let body: string;\n if (rebuild) {\n const slot = `${objVar}[${kVar}]`;\n body = `${slot}=${g.input}[${kVar}];${g.visit(ir.catchall, { input: slot, output: slot, path: catchallPath })}`;\n } else {\n const inVar = g.temp(\"cv\");\n const outVar = g.temp(\"co\");\n body =\n `var ${inVar}=${g.input}[${kVar}];var ${outVar}=${inVar};` +\n g.visit(ir.catchall, { input: inVar, output: outVar, path: catchallPath }) +\n handoff(outVar, inVar, kVar);\n }\n code += emit`\n for(var ${kVar} in ${g.input}){\n if(!(${test})&&${kVar}!==\"__proto__\"){${body}}\n }`;\n }\n\n // Always written back: a rebuild is a fresh object, and a pass-through may\n // have become a copy (the scrub above, or a handoff) even where nothing\n // rewrote a value.\n code += `${g.output}=${objVar};`;\n\n // Object-level refine effects: z.object({...}).refine(fn), suppressed when the\n // parse phase aborted (see refineMark). One gate covers every effect: each of\n // them can only add a continuable `custom` issue, so zod's per-check\n // re-evaluation of `isAborted` can never flip between them.\n if (ir.checks && ir.checks.length > 0) {\n let refines = \"\";\n for (const check of ir.checks) {\n refines +=\n check.kind === \"super_refine_effect\"\n ? superRefineCheck(check, objVar, g)\n : refineCheck(check, objVar, g);\n }\n const aborted = emitRuntimeHelper(g.ctx, \"__zcAb\", ZC_AB_DECL);\n code += `if(!${aborted}(${g.issues},${refineMark})){${refines}}`;\n }\n\n code += `}\\n`;\n return code;\n}\n\n/**\n * Property entries in the order their fast-checks should be `&&`-chained:\n * cheapest first, declaration order preserved among equals (Array#sort is\n * stable). Valid input runs every conjunct whatever the order, so this costs\n * nothing on the hot path; a REJECT stops at the first false conjunct, so\n * pricing a `z.email()` behind the `kind` literal that actually discriminates\n * is what makes union probing and `.is()` misses cheap (see\n * estimateRuntimeCost). The SLOW path keeps declaration order — that one's\n * output is the issue list, whose order is part of zod parity.\n */\nfunction orderedProperties(ir: ObjectIR, g: FastGen): [string, SchemaIR][] {\n return orderByRuntimeCost(parsedProperties(ir), ([, propIR]) => propIR, g.ctx);\n}\n\n/**\n * Property/strict/refine fast-checks for an object, WITHOUT the leading\n * `typeof===object && !==null && !Array.isArray` type-guard. Returns the\n * conjunct parts (joinable with `&&`), or null if any child is fast-ineligible.\n *\n * `skipKey`, when given, omits that one property's check. Used by the\n * discriminated-union fast path (via `g.discSkipKey`): the enclosing `switch`\n * has already matched the discriminator's value, so re-checking it is redundant.\n */\nfunction fastObjectBody(ir: ObjectIR, g: FastGen, skipKey?: string): string[] | null {\n const x = g.input;\n const parts: string[] = [];\n const absentAccepts = new Set([...(ir.skipAbsentKeys ?? []), ...(ir.suppressAbsentKeys ?? [])]);\n const nonoptional = new Set(ir.nonoptionalKeys ?? []);\n\n for (const [key, propIR] of orderedProperties(ir, g)) {\n if (key === skipKey) continue;\n const keyStr = escapeString(key);\n const propCheck = g.visit(propIR, { input: `${x}[${keyStr}]` });\n if (propCheck === null) return null; // All-or-nothing\n // Presence rules keyed on the schema's `optin`/`optout` (see ObjectIR): a\n // required key must be there whatever its schema makes of `undefined`, and\n // an optional-out key on the \"optional\"/\"defaulted\" rungs is accepted when\n // it is not, whatever the property would say.\n if (nonoptional.has(key)) {\n parts.push(propCheck === \"true\" ? `${keyStr} in ${x}` : `${keyStr} in ${x}&&${propCheck}`);\n } else if (absentAccepts.has(key)) {\n parts.push(`(!(${keyStr} in ${x})||${propCheck})`);\n } else {\n parts.push(propCheck);\n }\n }\n\n // Strict unknown-key pass: hosted boolean helper (a for-in loop cannot live\n // in the && chain). Same for-in iteration as the slow path — fast/slow\n // agreement is load-bearing under the __zcFinD deferral. The membership set is\n // the FULL key list (the discriminator is a recognized key), independent of\n // skipKey, which only suppresses re-validating the discriminator's value.\n if (ir.strict) {\n const keys = Object.keys(ir.properties);\n const fnName = g.temp(\"so\");\n const test = keyMembershipTest(g.ctx, keys, \"k\");\n g.ctx.preamble.push(\n `function ${fnName}(o){for(var k in o){if(!(${test}))return false;}return true;}`,\n );\n parts.push(`${fnName}(${x})`);\n }\n\n // .catchall(schema): same hosted for-in as strict, but each unrecognized key\n // is checked against the catchall rather than rejected outright. The value is\n // hoisted into a local for the same reason fastRecord does it — repeated\n // `o[k]` computed loads are not eliminated across check boundaries.\n if (ir.catchall) {\n const kv = g.temp(\"cak\");\n const vv = g.temp(\"cav\");\n const catchallGen = g.scoped(vv);\n const valCheck = catchallGen.visit(ir.catchall, { input: vv });\n if (valCheck === null) return null;\n if (valCheck !== \"true\") {\n const fnName = g.temp(\"co\");\n const test = keyMembershipTest(g.ctx, Object.keys(ir.properties), kv);\n g.ctx.preamble.push(\n `function ${fnName}(o){${declareFastTemps(catchallGen.scope)}var ${kv},${vv};for(${kv} in o){if(!(${test})&&${kv}!==\"__proto__\"){${vv}=o[${kv}];if(!(${valCheck}))return false;}}return true;}`,\n );\n parts.push(`${fnName}(${x})`);\n }\n }\n\n // Object-level refine effects (appended last — run after property checks short-circuit)\n if (ir.checks) {\n for (const check of ir.checks) {\n if (check.kind === \"refine_effect\") {\n parts.push(fastRefineTest(check, x, g));\n } else if (check.kind === \"super_refine_effect\") {\n parts.push(superRefineFastTest(check, x, g));\n }\n }\n }\n\n return parts;\n}\n\nexport function fastObject(ir: ObjectIR, g: FastGen): string | null {\n // A strip object still gets an expression. Stripping reshapes the OUTPUT, not\n // the verdict, so this remains an exact acceptance predicate — which is what\n // `.is()` installs, and what the build path reuses to validate the subtrees it\n // passes through by reference. What it must NOT do is stand in for the parse\n // result: generateValidator withholds the `data:input` shortcut whenever the\n // schema rebuilds (see rebuildsOutput).\n const x = g.input;\n const body = fastObjectBody(ir, g, g.discSkipKey);\n if (body === null) return null;\n // Discriminated-union option: the enclosing switch (and the caller's guard)\n // already established object-ness and the discriminator value, so emit only\n // the remaining checks — no leading type-guard. An option with nothing left\n // to check accepts unconditionally (\"true\").\n if (g.discSkipKey !== undefined) {\n return body.length > 0 ? body.join(\"&&\") : \"true\";\n }\n return [`typeof ${x}===\"object\"`, `${x}!==null`, `!Array.isArray(${x})`, ...body].join(\"&&\");\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recursive-ref.d.ts","names":[],"sources":["../../../../src/core/codegen/schemas/recursive-ref.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"recursive-ref.d.ts","names":[],"sources":["../../../../src/core/codegen/schemas/recursive-ref.ts"],"mappings":";;;iBA2DgB,iBAAiB,IAAI,gBAAgB,GAAG;iBAIxC,iBAAiB,IAAI,gBAAgB,GAAG;;;;;;iBASxC,oBAAoB,IAAI,mBAAmB,GAAG;iBAI9C,oBAAoB,IAAI,mBAAmB,GAAG"}
|
|
@@ -40,6 +40,7 @@ function slowRecursionCall(refId, g) {
|
|
|
40
40
|
*/
|
|
41
41
|
function fastRecursionCall(refId, g) {
|
|
42
42
|
const ctx = g.ctx;
|
|
43
|
+
if (ctx.fastRecursionDisabled) return null;
|
|
43
44
|
const t = ctx.recTargets?.get(refId ?? 0);
|
|
44
45
|
if (!t || t.isRoot) {
|
|
45
46
|
ctx.recFastName ??= `__fcr_${ctx.counter++}`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recursive-ref.js","names":[],"sources":["../../../../src/core/codegen/schemas/recursive-ref.ts"],"sourcesContent":["import type { RecursionTargetIR, RecursiveRefIR } from \"../../types.js\";\nimport type { CodeGenContext, FastGen, SlowGen } from \"../context.js\";\nimport { emit } from \"../emit.js\";\n\n/**\n * Resolve the slow (safeParse-shaped) validator name for a recursion target.\n * refId 0 / no table ⇒ the root, which is the schema's own `safeParse_<name>`.\n */\nfunction slowTargetName(ctx: CodeGenContext, refId: number | undefined): string {\n const t = ctx.recTargets?.get(refId ?? 0);\n return t && !t.isRoot ? t.slowName : ctx.fnName;\n}\n\n/**\n * Emit a recursion call on the slow (issue-collecting) path: invoke the hosted\n * validator for the target shape, then either merge its issues with the current\n * path prefix or write back its (possibly mutated) data. Shared by both the\n * back-edge (`recursiveRef`) and the in-place target site (`recursionTarget`).\n */\nfunction slowRecursionCall(refId: number | undefined, g: SlowGen): string {\n const fn = slowTargetName(g.ctx, refId);\n const n = g.ctx.counter++;\n const rVar = `__rec_r${n}`;\n const iVar = `__rec_i${n}`;\n const jVar = `__rec_j${n}`;\n return `${emit`\n var ${rVar}=${fn}(${g.input});\n if(!${rVar}.success){\n var ${iVar}=${rVar}.error.issues;\n for(var ${jVar}=0;${jVar}<${iVar}.length;${jVar}++){\n ${g.issues}.push({...${iVar}[${jVar}],\n path:${g.path}.concat(${iVar}[${jVar}].path)});\n }\n }else{\n ${g.output}=${rVar}.data;\n }\n `}\\n`;\n}\n\n/**\n * Resolve the fast (boolean) validator name for a recursion target, allocating\n * the root's lazily (mirrors the historical single-target behavior). Returns a\n * call expression on the target's hosted fast-check.\n */\nfunction fastRecursionCall(refId: number | undefined, g: FastGen): string {\n const ctx = g.ctx;\n const t = ctx.recTargets?.get(refId ?? 0);\n if (!t || t.isRoot) {\n // Root recursion (refId 0, or a standalone generator call with no table):\n // the root fast expression is hosted under this name by generateValidator.\n ctx.recFastName ??= `__fcr_${ctx.counter++}`;\n if (t) t.fastName = ctx.recFastName;\n return `${ctx.recFastName}(${g.input})`;\n }\n // Non-root target: name pre-allocated by generateValidator before the walk.\n return `${t.fastName}(${g.input})`;\n}\n\nexport function slowRecursiveRef(ir: RecursiveRefIR, g: SlowGen): string {\n return slowRecursionCall(ir.refId, g);\n}\n\nexport function fastRecursiveRef(ir: RecursiveRefIR, g: FastGen): string | null {\n return fastRecursionCall(ir.refId, g);\n}\n\n/**\n * In-place site of a non-root recursion target (the recursive sub-schema where\n * it sits in the larger tree). Validating it = calling its hosted validator,\n * exactly like a back-edge, so both reuse the recursion-call emitters.\n */\nexport function slowRecursionTarget(ir: RecursionTargetIR, g: SlowGen): string {\n return slowRecursionCall(ir.refId, g);\n}\n\nexport function fastRecursionTarget(ir: RecursionTargetIR, g: FastGen): string | null {\n return fastRecursionCall(ir.refId, g);\n}\n"],"mappings":";;;;;;AAQA,SAAS,eAAe,KAAqB,OAAmC;CAC9E,MAAM,IAAI,IAAI,YAAY,IAAI,SAAS,CAAC;CACxC,OAAO,KAAK,CAAC,EAAE,SAAS,EAAE,WAAW,IAAI;AAC3C;;;;;;;AAQA,SAAS,kBAAkB,OAA2B,GAAoB;CACxE,MAAM,KAAK,eAAe,EAAE,KAAK,KAAK;CACtC,MAAM,IAAI,EAAE,IAAI;CAChB,MAAM,OAAO,UAAU;CACvB,MAAM,OAAO,UAAU;CACvB,MAAM,OAAO,UAAU;CACvB,OAAO,GAAG,IAAI;UACN,KAAK,GAAG,GAAG,GAAG,EAAE,MAAM;UACtB,KAAK;YACH,KAAK,GAAG,KAAK;gBACT,KAAK,KAAK,KAAK,GAAG,KAAK,UAAU,KAAK;UAC5C,EAAE,OAAO,YAAY,KAAK,GAAG,KAAK;iBAC3B,EAAE,KAAK,UAAU,KAAK,GAAG,KAAK;;;QAGvC,EAAE,OAAO,GAAG,KAAK;;IAErB;AACJ;;;;;;AAOA,SAAS,kBAAkB,OAA2B,
|
|
1
|
+
{"version":3,"file":"recursive-ref.js","names":[],"sources":["../../../../src/core/codegen/schemas/recursive-ref.ts"],"sourcesContent":["import type { RecursionTargetIR, RecursiveRefIR } from \"../../types.js\";\nimport type { CodeGenContext, FastGen, SlowGen } from \"../context.js\";\nimport { emit } from \"../emit.js\";\n\n/**\n * Resolve the slow (safeParse-shaped) validator name for a recursion target.\n * refId 0 / no table ⇒ the root, which is the schema's own `safeParse_<name>`.\n */\nfunction slowTargetName(ctx: CodeGenContext, refId: number | undefined): string {\n const t = ctx.recTargets?.get(refId ?? 0);\n return t && !t.isRoot ? t.slowName : ctx.fnName;\n}\n\n/**\n * Emit a recursion call on the slow (issue-collecting) path: invoke the hosted\n * validator for the target shape, then either merge its issues with the current\n * path prefix or write back its (possibly mutated) data. Shared by both the\n * back-edge (`recursiveRef`) and the in-place target site (`recursionTarget`).\n */\nfunction slowRecursionCall(refId: number | undefined, g: SlowGen): string {\n const fn = slowTargetName(g.ctx, refId);\n const n = g.ctx.counter++;\n const rVar = `__rec_r${n}`;\n const iVar = `__rec_i${n}`;\n const jVar = `__rec_j${n}`;\n return `${emit`\n var ${rVar}=${fn}(${g.input});\n if(!${rVar}.success){\n var ${iVar}=${rVar}.error.issues;\n for(var ${jVar}=0;${jVar}<${iVar}.length;${jVar}++){\n ${g.issues}.push({...${iVar}[${jVar}],\n path:${g.path}.concat(${iVar}[${jVar}].path)});\n }\n }else{\n ${g.output}=${rVar}.data;\n }\n `}\\n`;\n}\n\n/**\n * Resolve the fast (boolean) validator name for a recursion target, allocating\n * the root's lazily (mirrors the historical single-target behavior). Returns a\n * call expression on the target's hosted fast-check.\n */\nfunction fastRecursionCall(refId: number | undefined, g: FastGen): string | null {\n const ctx = g.ctx;\n if (ctx.fastRecursionDisabled) return null;\n const t = ctx.recTargets?.get(refId ?? 0);\n if (!t || t.isRoot) {\n // Root recursion (refId 0, or a standalone generator call with no table):\n // the root fast expression is hosted under this name by generateValidator.\n ctx.recFastName ??= `__fcr_${ctx.counter++}`;\n if (t) t.fastName = ctx.recFastName;\n return `${ctx.recFastName}(${g.input})`;\n }\n // Non-root target: name pre-allocated by generateValidator before the walk.\n return `${t.fastName}(${g.input})`;\n}\n\nexport function slowRecursiveRef(ir: RecursiveRefIR, g: SlowGen): string {\n return slowRecursionCall(ir.refId, g);\n}\n\nexport function fastRecursiveRef(ir: RecursiveRefIR, g: FastGen): string | null {\n return fastRecursionCall(ir.refId, g);\n}\n\n/**\n * In-place site of a non-root recursion target (the recursive sub-schema where\n * it sits in the larger tree). Validating it = calling its hosted validator,\n * exactly like a back-edge, so both reuse the recursion-call emitters.\n */\nexport function slowRecursionTarget(ir: RecursionTargetIR, g: SlowGen): string {\n return slowRecursionCall(ir.refId, g);\n}\n\nexport function fastRecursionTarget(ir: RecursionTargetIR, g: FastGen): string | null {\n return fastRecursionCall(ir.refId, g);\n}\n"],"mappings":";;;;;;AAQA,SAAS,eAAe,KAAqB,OAAmC;CAC9E,MAAM,IAAI,IAAI,YAAY,IAAI,SAAS,CAAC;CACxC,OAAO,KAAK,CAAC,EAAE,SAAS,EAAE,WAAW,IAAI;AAC3C;;;;;;;AAQA,SAAS,kBAAkB,OAA2B,GAAoB;CACxE,MAAM,KAAK,eAAe,EAAE,KAAK,KAAK;CACtC,MAAM,IAAI,EAAE,IAAI;CAChB,MAAM,OAAO,UAAU;CACvB,MAAM,OAAO,UAAU;CACvB,MAAM,OAAO,UAAU;CACvB,OAAO,GAAG,IAAI;UACN,KAAK,GAAG,GAAG,GAAG,EAAE,MAAM;UACtB,KAAK;YACH,KAAK,GAAG,KAAK;gBACT,KAAK,KAAK,KAAK,GAAG,KAAK,UAAU,KAAK;UAC5C,EAAE,OAAO,YAAY,KAAK,GAAG,KAAK;iBAC3B,EAAE,KAAK,UAAU,KAAK,GAAG,KAAK;;;QAGvC,EAAE,OAAO,GAAG,KAAK;;IAErB;AACJ;;;;;;AAOA,SAAS,kBAAkB,OAA2B,GAA2B;CAC/E,MAAM,MAAM,EAAE;CACd,IAAI,IAAI,uBAAuB,OAAO;CACtC,MAAM,IAAI,IAAI,YAAY,IAAI,SAAS,CAAC;CACxC,IAAI,CAAC,KAAK,EAAE,QAAQ;EAGlB,IAAI,gBAAgB,SAAS,IAAI;EACjC,IAAI,GAAG,EAAE,WAAW,IAAI;EACxB,OAAO,GAAG,IAAI,YAAY,GAAG,EAAE,MAAM;CACvC;CAEA,OAAO,GAAG,EAAE,SAAS,GAAG,EAAE,MAAM;AAClC;AAEA,SAAgB,iBAAiB,IAAoB,GAAoB;CACvE,OAAO,kBAAkB,GAAG,OAAO,CAAC;AACtC;AAEA,SAAgB,iBAAiB,IAAoB,GAA2B;CAC9E,OAAO,kBAAkB,GAAG,OAAO,CAAC;AACtC;;;;;;AAOA,SAAgB,oBAAoB,IAAuB,GAAoB;CAC7E,OAAO,kBAAkB,GAAG,OAAO,CAAC;AACtC;AAEA,SAAgB,oBAAoB,IAAuB,GAA2B;CACpF,OAAO,kBAAkB,GAAG,OAAO,CAAC;AACtC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"string.d.ts","names":[],"sources":["../../../../src/core/codegen/schemas/string.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"string.d.ts","names":[],"sources":["../../../../src/core/codegen/schemas/string.ts"],"mappings":";;;iBA0GgB,WAAW,IAAI,UAAU,GAAG;;;;;;;;;;;iBAmK5B,gBAAgB,OAAO,SAAS,WAAW,KAAK;iBAuDhD,WAAW,IAAI,UAAU,GAAG"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { EMAIL_REGEX_SOURCE, fastTestSource, isDefaultEmailPattern } from "../well-known-regex.js";
|
|
2
|
-
import { checkPriority,
|
|
2
|
+
import { checkPriority, emitEffectFn, emitRegex, emitRegexSourceString, emitRuntimeHelper, escapeString } from "../context.js";
|
|
3
3
|
import { emit } from "../emit.js";
|
|
4
4
|
import { invalidFormat, invalidType, tooBig, tooSmall } from "../emit-issue.js";
|
|
5
5
|
import { ZC_EMAIL_DECL } from "../issue-decls.js";
|
|
6
|
-
import { refineCheck, superRefineCheck, superRefineFastTest } from "./effect.js";
|
|
6
|
+
import { fastRefineTest, refineCheck, superRefineCheck, superRefineFastTest } from "./effect.js";
|
|
7
7
|
import { stringLengthTests, whenGatedSizeChecks } from "./sizeable.js";
|
|
8
8
|
//#region src/core/codegen/schemas/string.ts
|
|
9
9
|
/** `re.lastIndex=0;` reset statement for stateful (g/y-flagged) regexes. */
|
|
@@ -254,7 +254,7 @@ function fastString(ir, g) {
|
|
|
254
254
|
if (expr === null) return null;
|
|
255
255
|
parts.push(expr);
|
|
256
256
|
}
|
|
257
|
-
for (const check of ir.checks) if (check.kind === "refine_effect") parts.push(
|
|
257
|
+
for (const check of ir.checks) if (check.kind === "refine_effect") parts.push(fastRefineTest(check, x, g));
|
|
258
258
|
else if (check.kind === "super_refine_effect") parts.push(superRefineFastTest(check, x, g));
|
|
259
259
|
return parts.join("&&");
|
|
260
260
|
}
|