zod-compiler 2.0.5 → 2.0.7
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 +97 -118
- package/dist/core/codegen/build-path.d.ts.map +1 -1
- package/dist/core/codegen/build-path.js +16 -7
- package/dist/core/codegen/build-path.js.map +1 -1
- package/dist/core/codegen/context.d.ts +10 -7
- package/dist/core/codegen/context.d.ts.map +1 -1
- package/dist/core/codegen/context.js +10 -8
- package/dist/core/codegen/context.js.map +1 -1
- package/dist/core/codegen/index.js +3 -2
- package/dist/core/codegen/index.js.map +1 -1
- package/dist/core/codegen/issue-decls.d.ts +28 -1
- package/dist/core/codegen/issue-decls.d.ts.map +1 -1
- package/dist/core/codegen/issue-decls.js +30 -1
- package/dist/core/codegen/issue-decls.js.map +1 -1
- package/dist/core/codegen/schemas/array.js +2 -2
- package/dist/core/codegen/schemas/array.js.map +1 -1
- package/dist/core/codegen/schemas/custom.js +1 -1
- package/dist/core/codegen/schemas/discriminated-union.js +1 -1
- package/dist/core/codegen/schemas/discriminated-union.js.map +1 -1
- package/dist/core/codegen/schemas/effect.js +2 -2
- package/dist/core/codegen/schemas/effect.js.map +1 -1
- package/dist/core/codegen/schemas/fallback.js +1 -1
- package/dist/core/codegen/schemas/map.js +1 -1
- package/dist/core/codegen/schemas/number.js +1 -1
- package/dist/core/codegen/schemas/object.js +3 -3
- package/dist/core/codegen/schemas/object.js.map +1 -1
- package/dist/core/codegen/schemas/record.js +2 -2
- package/dist/core/codegen/schemas/record.js.map +1 -1
- package/dist/core/codegen/schemas/sizeable.js +1 -1
- package/dist/core/codegen/schemas/string.d.ts +15 -2
- package/dist/core/codegen/schemas/string.d.ts.map +1 -1
- package/dist/core/codegen/schemas/string.js +97 -35
- package/dist/core/codegen/schemas/string.js.map +1 -1
- package/dist/core/codegen/schemas/tuple.js +2 -2
- package/dist/core/codegen/schemas/tuple.js.map +1 -1
- package/dist/core/codegen/schemas/union.js +1 -1
- package/dist/core/iife.d.ts +11 -12
- package/dist/core/iife.d.ts.map +1 -1
- package/dist/core/iife.js +12 -13
- package/dist/core/iife.js.map +1 -1
- package/dist/runtime.d.ts +2 -0
- package/dist/runtime.js +3 -1
- package/dist/static-filter.d.ts +19 -1
- package/dist/static-filter.d.ts.map +1 -1
- package/dist/static-filter.js +27 -1
- package/dist/static-filter.js.map +1 -1
- package/dist/unplugin/virtual.js +1 -1
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { ZC_FZ_DECL, ZC_HOP_DECL, ZC_PLAIN_DECL, ZC_PROTO_SCRUB_DECL } from "../issue-decls.js";
|
|
1
2
|
import { declareFastTemps, emitRuntimeHelper, extendPath, hasMutation, visitMember } from "../context.js";
|
|
2
3
|
import { emit } from "../emit.js";
|
|
3
4
|
import { invalidType, unrecognizedKeys } from "../emit-issue.js";
|
|
4
|
-
import { ZC_FZ_DECL, ZC_HOP_DECL, ZC_PLAIN_DECL, ZC_PROTO_SCRUB_DECL } from "../issue-decls.js";
|
|
5
5
|
//#region src/core/codegen/schemas/record.ts
|
|
6
6
|
/**
|
|
7
7
|
* `$ZodRecord` gates on `util.isPlainObject`, NOT the looser `util.isObject`
|
|
@@ -30,7 +30,7 @@ function slowRecord(ir, g) {
|
|
|
30
30
|
if (hasMutation(ir.valueType)) code += `if(${rec}===${g.input}){${rec}={...${g.input}};}`;
|
|
31
31
|
const keyVar = g.temp("rkey");
|
|
32
32
|
const keyIssuesVar = g.temp("rki");
|
|
33
|
-
const keyPath = extendPath(g.path, keyVar);
|
|
33
|
+
const keyPath = extendPath(g.ctx, g.path, keyVar);
|
|
34
34
|
const hop = emitRuntimeHelper(g.ctx, "__zcHop", ZC_HOP_DECL);
|
|
35
35
|
const fz = emitRuntimeHelper(g.ctx, "__zcFz", ZC_FZ_DECL);
|
|
36
36
|
const unrecognizedVar = ir.enumerableKeys ? g.temp("ruk") : null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"record.js","names":[],"sources":["../../../../src/core/codegen/schemas/record.ts"],"sourcesContent":["import type { RecordIR, SchemaIR } from \"../../types.js\";\nimport type { FastGen, SlowGen } from \"../context.js\";\nimport {\n declareFastTemps,\n emitRuntimeHelper,\n extendPath,\n hasMutation,\n visitMember,\n} from \"../context.js\";\nimport { emit } from \"../emit.js\";\nimport { invalidType, unrecognizedKeys } from \"../emit-issue.js\";\nimport { ZC_FZ_DECL, ZC_HOP_DECL, ZC_PLAIN_DECL, ZC_PROTO_SCRUB_DECL } from \"../issue-decls.js\";\n\n/**\n * `$ZodRecord` gates on `util.isPlainObject`, NOT the looser `util.isObject`\n * that `$ZodObject` uses — see {@link ZC_PLAIN_DECL} for why the two differ and\n * what accepting the wrong one lets through.\n */\nfunction plainObjectTest(g: SlowGen | FastGen, input: string): string {\n return `${emitRuntimeHelper(g.ctx, \"__zcPlain\", ZC_PLAIN_DECL)}(${input})`;\n}\n\n/**\n * Zod's record walk opens with `if (key === \"__proto__\") continue`, so an own\n * `__proto__` data property (which `JSON.parse('{\"__proto__\":…}')` creates) is\n * neither key-validated nor value-validated nor copied. Without this the\n * compiled walk reported issues zod does not: `z.record(z.string(), z.number())`\n * over such an object raised a SECOND `invalid_type`, and a constrained key\n * schema a second `invalid_key`, for a key zod never looks at.\n */\nconst PROTO_SKIP = (keyVar: string): string => `if(${keyVar}===\"__proto__\")continue;`;\n\nexport function slowRecord(ir: SchemaIR & { type: \"record\" }, g: SlowGen): string {\n let code = emit`\n if(!${plainObjectTest(g, g.input)}){\n ${invalidType(g, \"record\")}\n }else{`;\n\n // One binding for every read and write, handed back as the output after the\n // walk: a parent can hand this node `g.input` and `g.output` as two different\n // bindings (see slowTuple).\n //\n // PROTO_SKIP keeps `__proto__` out of the WALK; the scrub keeps it out of the\n // OUTPUT, which zod also does — it copies into a fresh `{}` and skips the key\n // (see ZC_PROTO_SCRUB_DECL). Runs before the walk so the loop iterates the\n // same container the caller gets back. A mutating value schema rewrites values\n // as a matter of course, so it takes a copy up front unless the scrub already\n // made one; anything else copies only once a value hands back a replacement\n // (see visitMember).\n const rec = g.temp(\"rc\");\n code += `var ${rec}=${emitRuntimeHelper(g.ctx, \"__zcPs\", ZC_PROTO_SCRUB_DECL)}(${g.input});`;\n if (hasMutation(ir.valueType)) {\n code += `if(${rec}===${g.input}){${rec}={...${g.input}};}`;\n }\n\n const keyVar = g.temp(\"rkey\");\n const keyIssuesVar = g.temp(\"rki\");\n const keyPath = extendPath(g.path, keyVar);\n // for-in + hasOwnProperty guard instead of Object.keys(): identical\n // own-enumerable string-key set and iteration order, no keys-array\n // allocation. The loop iterates the binding as the walk found it — the clone,\n // when one was taken above — and a copy taken mid-walk for a replacement has\n // the same keys, so the key set is stable (values change, keys don't).\n // Records whose values mutate run this path eagerly, so they get the same\n // speedup the fast path does.\n const hop = emitRuntimeHelper(g.ctx, \"__zcHop\", ZC_HOP_DECL);\n\n // The key is validated at a RELATIVE path and its issues are finalized before\n // being nested, mirroring zod: `issues: keyResult.issues.map(finalizeIssue)`\n // over a payload zod ran fresh. Nested issues never reach the top-level\n // finalization loop (it walks only the outer array), so an unfinalized nest\n // leaked absolute paths and the raw `input` into the reported error.\n const fz = emitRuntimeHelper(g.ctx, \"__zcFz\", ZC_FZ_DECL);\n\n // A key the enumerable key schema rejects is unrecognized, not invalid: zod\n // collects those and pushes ONE `unrecognized_keys` after the walk, so it\n // trails every value issue (see RecordIR.enumerableKeys).\n const unrecognizedVar = ir.enumerableKeys ? g.temp(\"ruk\") : null;\n const onKeyFailure =\n unrecognizedVar === null\n ? `${g.issues}.push({code:\"invalid_key\",origin:\"record\",issues:${fz}(${keyIssuesVar}),input:${keyVar},path:${keyPath}${g.typeMsg === undefined ? \"\" : `,message:${JSON.stringify(g.typeMsg)}`}});`\n : `(${unrecognizedVar}=${unrecognizedVar}||[]).push(${keyVar});`;\n\n const keyCode = g.visit(ir.keyType, {\n input: keyVar,\n output: keyVar,\n path: \"[]\",\n issues: keyIssuesVar,\n });\n const valueCode = visitMember(g, ir.valueType, {\n container: rec,\n original: g.input,\n copy: `{...${g.input}}`,\n key: keyVar,\n path: keyPath,\n issues: g.issues,\n });\n code += emit`\n ${unrecognizedVar === null ? \"\" : `var ${unrecognizedVar}=null;`}\n for(var ${keyVar} in ${rec}){\n if(!${hop}.call(${rec},${keyVar}))continue;\n ${PROTO_SKIP(keyVar)}\n var ${keyIssuesVar}=[];\n ${keyCode}\n if(${keyIssuesVar}.length>0){\n ${onKeyFailure}\n }else{\n ${valueCode}\n }\n }\n ${unrecognizedVar === null ? \"\" : `if(${unrecognizedVar}!==null){${unrecognizedKeys(g, unrecognizedVar)}}`}\n ${g.output}=${rec};\n }`;\n return `${code}\\n`;\n}\n\nexport function fastRecord(ir: RecordIR, g: FastGen): string | null {\n const x = g.input;\n const parts: string[] = [plainObjectTest(g, x)];\n\n // Object.keys only yields strings — a plain unconstrained string key schema\n // is always satisfied, so skip generating its check entirely.\n const plainStringKey =\n ir.keyType.type === \"string\" && ir.keyType.checks.length === 0 && ir.keyType.coerce !== true;\n\n const kv = g.temp(\"rk\");\n const vv = g.temp(\"rv\");\n // Key + value checks share one fresh scope — they live in the same emitted\n // helper function, size-gated independently of the caller.\n const body = g.scoped(kv);\n const keyCheck = plainStringKey ? \"true\" : body.visit(ir.keyType, { input: kv });\n // Hoist o[k] into a loop variable: computed-key lookups don't get V8's load\n // elimination across check boundaries, so each repeated o[k] would re-walk\n // the (often dictionary-mode) object.\n const valCheck = body.visit(ir.valueType, { input: vv });\n if (keyCheck === null || valCheck === null) return null;\n\n const conditions: string[] = [];\n if (keyCheck !== \"true\") conditions.push(keyCheck);\n if (valCheck !== \"true\") conditions.push(valCheck);\n\n if (conditions.length > 0) {\n const helperName = g.temp(\"rf\");\n const valAssign = valCheck !== \"true\" ? `${vv}=o[${kv}];` : \"\";\n // for-in (no Object.keys array allocation) + hasOwnProperty guard. The\n // guard restricts iteration to own enumerable string keys — the exact set\n // Object.keys/the slow path produce — so inherited enumerable props can't\n // make the fast check disagree with the deferred slow walk. Measured 2.9x\n // (5 keys) to 5.8x (20 keys) faster than the Object.keys form; the hoisted\n // __zcHop.call inlines, making the guard ~free vs an unguarded for-in.\n const hop = emitRuntimeHelper(g.ctx, \"__zcHop\", ZC_HOP_DECL);\n g.ctx.preamble.push(\n `function ${helperName}(o){${declareFastTemps(body.scope)}var ${kv},${vv};for(${kv} in o){${PROTO_SKIP(kv)}if(${hop}.call(o,${kv})){${valAssign}if(!(${conditions.join(\"&&\")})){return false;}}}return true;}`,\n );\n parts.push(`${helperName}(${x})`);\n }\n\n return parts.join(\"&&\");\n}\n"],"mappings":";;;;;;;;;;AAkBA,SAAS,gBAAgB,GAAsB,OAAuB;CACpE,OAAO,GAAG,kBAAkB,EAAE,KAAK,aAAa,aAAa,EAAE,GAAG,MAAM;AAC1E;;;;;;;;;AAUA,MAAM,cAAc,WAA2B,MAAM,OAAO;AAE5D,SAAgB,WAAW,IAAmC,GAAoB;CAChF,IAAI,OAAO,IAAI;UACP,gBAAgB,GAAG,EAAE,KAAK,EAAE;QAC9B,YAAY,GAAG,QAAQ,EAAE;;CAc/B,MAAM,MAAM,EAAE,KAAK,IAAI;CACvB,QAAQ,OAAO,IAAI,GAAG,kBAAkB,EAAE,KAAK,UAAU,mBAAmB,EAAE,GAAG,EAAE,MAAM;CACzF,IAAI,YAAY,GAAG,SAAS,GAC1B,QAAQ,MAAM,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,OAAO,EAAE,MAAM;CAGxD,MAAM,SAAS,EAAE,KAAK,MAAM;CAC5B,MAAM,eAAe,EAAE,KAAK,KAAK;CACjC,MAAM,UAAU,WAAW,EAAE,MAAM,MAAM;
|
|
1
|
+
{"version":3,"file":"record.js","names":[],"sources":["../../../../src/core/codegen/schemas/record.ts"],"sourcesContent":["import type { RecordIR, SchemaIR } from \"../../types.js\";\nimport type { FastGen, SlowGen } from \"../context.js\";\nimport {\n declareFastTemps,\n emitRuntimeHelper,\n extendPath,\n hasMutation,\n visitMember,\n} from \"../context.js\";\nimport { emit } from \"../emit.js\";\nimport { invalidType, unrecognizedKeys } from \"../emit-issue.js\";\nimport { ZC_FZ_DECL, ZC_HOP_DECL, ZC_PLAIN_DECL, ZC_PROTO_SCRUB_DECL } from \"../issue-decls.js\";\n\n/**\n * `$ZodRecord` gates on `util.isPlainObject`, NOT the looser `util.isObject`\n * that `$ZodObject` uses — see {@link ZC_PLAIN_DECL} for why the two differ and\n * what accepting the wrong one lets through.\n */\nfunction plainObjectTest(g: SlowGen | FastGen, input: string): string {\n return `${emitRuntimeHelper(g.ctx, \"__zcPlain\", ZC_PLAIN_DECL)}(${input})`;\n}\n\n/**\n * Zod's record walk opens with `if (key === \"__proto__\") continue`, so an own\n * `__proto__` data property (which `JSON.parse('{\"__proto__\":…}')` creates) is\n * neither key-validated nor value-validated nor copied. Without this the\n * compiled walk reported issues zod does not: `z.record(z.string(), z.number())`\n * over such an object raised a SECOND `invalid_type`, and a constrained key\n * schema a second `invalid_key`, for a key zod never looks at.\n */\nconst PROTO_SKIP = (keyVar: string): string => `if(${keyVar}===\"__proto__\")continue;`;\n\nexport function slowRecord(ir: SchemaIR & { type: \"record\" }, g: SlowGen): string {\n let code = emit`\n if(!${plainObjectTest(g, g.input)}){\n ${invalidType(g, \"record\")}\n }else{`;\n\n // One binding for every read and write, handed back as the output after the\n // walk: a parent can hand this node `g.input` and `g.output` as two different\n // bindings (see slowTuple).\n //\n // PROTO_SKIP keeps `__proto__` out of the WALK; the scrub keeps it out of the\n // OUTPUT, which zod also does — it copies into a fresh `{}` and skips the key\n // (see ZC_PROTO_SCRUB_DECL). Runs before the walk so the loop iterates the\n // same container the caller gets back. A mutating value schema rewrites values\n // as a matter of course, so it takes a copy up front unless the scrub already\n // made one; anything else copies only once a value hands back a replacement\n // (see visitMember).\n const rec = g.temp(\"rc\");\n code += `var ${rec}=${emitRuntimeHelper(g.ctx, \"__zcPs\", ZC_PROTO_SCRUB_DECL)}(${g.input});`;\n if (hasMutation(ir.valueType)) {\n code += `if(${rec}===${g.input}){${rec}={...${g.input}};}`;\n }\n\n const keyVar = g.temp(\"rkey\");\n const keyIssuesVar = g.temp(\"rki\");\n const keyPath = extendPath(g.ctx, g.path, keyVar);\n // for-in + hasOwnProperty guard instead of Object.keys(): identical\n // own-enumerable string-key set and iteration order, no keys-array\n // allocation. The loop iterates the binding as the walk found it — the clone,\n // when one was taken above — and a copy taken mid-walk for a replacement has\n // the same keys, so the key set is stable (values change, keys don't).\n // Records whose values mutate run this path eagerly, so they get the same\n // speedup the fast path does.\n const hop = emitRuntimeHelper(g.ctx, \"__zcHop\", ZC_HOP_DECL);\n\n // The key is validated at a RELATIVE path and its issues are finalized before\n // being nested, mirroring zod: `issues: keyResult.issues.map(finalizeIssue)`\n // over a payload zod ran fresh. Nested issues never reach the top-level\n // finalization loop (it walks only the outer array), so an unfinalized nest\n // leaked absolute paths and the raw `input` into the reported error.\n const fz = emitRuntimeHelper(g.ctx, \"__zcFz\", ZC_FZ_DECL);\n\n // A key the enumerable key schema rejects is unrecognized, not invalid: zod\n // collects those and pushes ONE `unrecognized_keys` after the walk, so it\n // trails every value issue (see RecordIR.enumerableKeys).\n const unrecognizedVar = ir.enumerableKeys ? g.temp(\"ruk\") : null;\n const onKeyFailure =\n unrecognizedVar === null\n ? `${g.issues}.push({code:\"invalid_key\",origin:\"record\",issues:${fz}(${keyIssuesVar}),input:${keyVar},path:${keyPath}${g.typeMsg === undefined ? \"\" : `,message:${JSON.stringify(g.typeMsg)}`}});`\n : `(${unrecognizedVar}=${unrecognizedVar}||[]).push(${keyVar});`;\n\n const keyCode = g.visit(ir.keyType, {\n input: keyVar,\n output: keyVar,\n path: \"[]\",\n issues: keyIssuesVar,\n });\n const valueCode = visitMember(g, ir.valueType, {\n container: rec,\n original: g.input,\n copy: `{...${g.input}}`,\n key: keyVar,\n path: keyPath,\n issues: g.issues,\n });\n code += emit`\n ${unrecognizedVar === null ? \"\" : `var ${unrecognizedVar}=null;`}\n for(var ${keyVar} in ${rec}){\n if(!${hop}.call(${rec},${keyVar}))continue;\n ${PROTO_SKIP(keyVar)}\n var ${keyIssuesVar}=[];\n ${keyCode}\n if(${keyIssuesVar}.length>0){\n ${onKeyFailure}\n }else{\n ${valueCode}\n }\n }\n ${unrecognizedVar === null ? \"\" : `if(${unrecognizedVar}!==null){${unrecognizedKeys(g, unrecognizedVar)}}`}\n ${g.output}=${rec};\n }`;\n return `${code}\\n`;\n}\n\nexport function fastRecord(ir: RecordIR, g: FastGen): string | null {\n const x = g.input;\n const parts: string[] = [plainObjectTest(g, x)];\n\n // Object.keys only yields strings — a plain unconstrained string key schema\n // is always satisfied, so skip generating its check entirely.\n const plainStringKey =\n ir.keyType.type === \"string\" && ir.keyType.checks.length === 0 && ir.keyType.coerce !== true;\n\n const kv = g.temp(\"rk\");\n const vv = g.temp(\"rv\");\n // Key + value checks share one fresh scope — they live in the same emitted\n // helper function, size-gated independently of the caller.\n const body = g.scoped(kv);\n const keyCheck = plainStringKey ? \"true\" : body.visit(ir.keyType, { input: kv });\n // Hoist o[k] into a loop variable: computed-key lookups don't get V8's load\n // elimination across check boundaries, so each repeated o[k] would re-walk\n // the (often dictionary-mode) object.\n const valCheck = body.visit(ir.valueType, { input: vv });\n if (keyCheck === null || valCheck === null) return null;\n\n const conditions: string[] = [];\n if (keyCheck !== \"true\") conditions.push(keyCheck);\n if (valCheck !== \"true\") conditions.push(valCheck);\n\n if (conditions.length > 0) {\n const helperName = g.temp(\"rf\");\n const valAssign = valCheck !== \"true\" ? `${vv}=o[${kv}];` : \"\";\n // for-in (no Object.keys array allocation) + hasOwnProperty guard. The\n // guard restricts iteration to own enumerable string keys — the exact set\n // Object.keys/the slow path produce — so inherited enumerable props can't\n // make the fast check disagree with the deferred slow walk. Measured 2.9x\n // (5 keys) to 5.8x (20 keys) faster than the Object.keys form; the hoisted\n // __zcHop.call inlines, making the guard ~free vs an unguarded for-in.\n const hop = emitRuntimeHelper(g.ctx, \"__zcHop\", ZC_HOP_DECL);\n g.ctx.preamble.push(\n `function ${helperName}(o){${declareFastTemps(body.scope)}var ${kv},${vv};for(${kv} in o){${PROTO_SKIP(kv)}if(${hop}.call(o,${kv})){${valAssign}if(!(${conditions.join(\"&&\")})){return false;}}}return true;}`,\n );\n parts.push(`${helperName}(${x})`);\n }\n\n return parts.join(\"&&\");\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { ZC_CPL_DECL, ZC_LENGTH_ORIGIN_DECL, ZC_SIZE_ORIGIN_DECL } from "../issue-decls.js";
|
|
1
2
|
import { emitRuntimeHelper } from "../context.js";
|
|
2
3
|
import { emit } from "../emit.js";
|
|
3
4
|
import { tooBig, tooSmall } from "../emit-issue.js";
|
|
4
|
-
import { ZC_CPL_DECL, ZC_LENGTH_ORIGIN_DECL, ZC_SIZE_ORIGIN_DECL } from "../issue-decls.js";
|
|
5
5
|
//#region src/core/codegen/schemas/sizeable.ts
|
|
6
6
|
/**
|
|
7
7
|
* The UTF-16 unit count from which a string is CERTAINLY at least `min` code
|
|
@@ -1,6 +1,19 @@
|
|
|
1
|
-
import { CheckIR, StringIR } from "../../types.js";
|
|
1
|
+
import { CheckIR, CheckStringFormat, StringIR } from "../../types.js";
|
|
2
2
|
import { CodeGenContext, FastGen, SlowGen } from "../context.js";
|
|
3
3
|
//#region src/core/codegen/schemas/string.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Is this a `z.url()` check — one that trims the value and writes back a
|
|
6
|
+
* rewrite of it? A patterned "url" is a custom format that borrowed the name
|
|
7
|
+
* (see buildString), validating through its own regex like any other format.
|
|
8
|
+
*/
|
|
9
|
+
declare function isUrlRewrite(check: StringIR["checks"][number]): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* The url check on the build path: {@link slowUrlCheck}'s verdict and
|
|
12
|
+
* write-back as statements over `value`, which leave the rewritten value in it
|
|
13
|
+
* and `return fail` at the first failure rather than push an issue — the
|
|
14
|
+
* deferred walk reports those.
|
|
15
|
+
*/
|
|
16
|
+
declare function buildUrlCheck(check: CheckStringFormat, value: string, fail: string, temp: (prefix: string) => string, ctx: CodeGenContext): string;
|
|
4
17
|
declare function slowString(ir: StringIR, g: SlowGen): string;
|
|
5
18
|
/**
|
|
6
19
|
* Boolean expression testing ONE compiled string check against `x`, or null when
|
|
@@ -15,5 +28,5 @@ declare function slowString(ir: StringIR, g: SlowGen): string;
|
|
|
15
28
|
declare function fastStringCheck(check: CheckIR, x: string, ctx: CodeGenContext): string | null;
|
|
16
29
|
declare function fastString(ir: StringIR, g: FastGen): string | null;
|
|
17
30
|
//#endregion
|
|
18
|
-
export { fastString, fastStringCheck, slowString };
|
|
31
|
+
export { buildUrlCheck, fastString, fastStringCheck, isUrlRewrite, slowString };
|
|
19
32
|
//# sourceMappingURL=string.d.ts.map
|
|
@@ -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":";;;;;;;;iBAuCgB,aAAa,OAAO;;;;;;;iBA6GpB,cACd,OAAO,mBACP,eACA,cACA,OAAO,2BACP,KAAK;iBAyBS,WAAW,IAAI,UAAU,GAAG;;;;;;;;;;;iBAmK5B,gBAAgB,OAAO,SAAS,WAAW,KAAK;iBAuDhD,WAAW,IAAI,UAAU,GAAG"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { ZC_EMAIL_DECL, ZC_URL_DECL } from "../issue-decls.js";
|
|
1
2
|
import { EMAIL_REGEX_SOURCE, fastTestSource, isDefaultEmailPattern } from "../well-known-regex.js";
|
|
2
3
|
import { checkPriority, emitEffectFn, emitRegex, emitRegexSourceString, emitRuntimeHelper, escapeString } from "../context.js";
|
|
3
4
|
import { emit } from "../emit.js";
|
|
4
5
|
import { invalidFormat, invalidType, tooBig, tooSmall } from "../emit-issue.js";
|
|
5
|
-
import { ZC_EMAIL_DECL } from "../issue-decls.js";
|
|
6
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
|
|
@@ -17,10 +17,36 @@ function lastIndexReset(regexVar, flags) {
|
|
|
17
17
|
*/
|
|
18
18
|
const HTTP_PROTOCOL_SOURCE = "^https?$";
|
|
19
19
|
/**
|
|
20
|
+
* Is this a `z.url()` check — one that trims the value and writes back a
|
|
21
|
+
* rewrite of it? A patterned "url" is a custom format that borrowed the name
|
|
22
|
+
* (see buildString), validating through its own regex like any other format.
|
|
23
|
+
*/
|
|
24
|
+
function isUrlRewrite(check) {
|
|
25
|
+
return check.kind === "string_format" && check.format === "url" && !check.pattern;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Does the url check read the PARSED url — a hostname or protocol test, or
|
|
29
|
+
* `normalize`'s href? Only then is the URL object needed; a check that only asks
|
|
30
|
+
* whether the input parses goes through `__zcUrl` (see ZC_URL_DECL) instead.
|
|
31
|
+
*/
|
|
32
|
+
function urlReadsParsed(check) {
|
|
33
|
+
return Boolean(check.hostname) || Boolean(check.protocol) || check.normalize === true;
|
|
34
|
+
}
|
|
35
|
+
/** $ZodURL's write-back without `normalize`: the trimmed input, tabs and newlines deleted. */
|
|
36
|
+
function urlStripped(trimmedVar, ctx) {
|
|
37
|
+
return `${trimmedVar}.replace(${emitRegex(ctx, "tnl", "[\\t\\n\\r]", "g")},"")`;
|
|
38
|
+
}
|
|
39
|
+
/** `url.protocol` without its trailing colon, the form $ZodURL tests. */
|
|
40
|
+
function urlProtocol(urlVar) {
|
|
41
|
+
return `(${urlVar}.protocol.endsWith(":")?${urlVar}.protocol.slice(0,-1):${urlVar}.protocol)`;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
20
44
|
* Generate the url check, mirroring $ZodURL semantics:
|
|
21
45
|
* trim → (for an http(s)-protocol check without normalize) require `://` →
|
|
22
46
|
* new URL(trimmed) → optional hostname/protocol regex tests → write back
|
|
23
47
|
* url.href (normalize) or the trimmed input with its tabs and newlines deleted.
|
|
48
|
+
* A check that reads nothing off the parsed URL only asks `__zcUrl` whether the
|
|
49
|
+
* input parses, which skips constructing it.
|
|
24
50
|
*
|
|
25
51
|
* The `://` guard is `parseURLObject`'s: without it the URL parser accepts
|
|
26
52
|
* `http:example.com`, and its rejection is a distinct issue (`note: "Invalid
|
|
@@ -36,41 +62,48 @@ const HTTP_PROTOCOL_SOURCE = "^https?$";
|
|
|
36
62
|
*/
|
|
37
63
|
function slowUrlCheck(check, g) {
|
|
38
64
|
const trimmedVar = g.temp("ut");
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if(!${re}.test(${urlVar}.hostname)){
|
|
46
|
-
${invalidFormat(g, "url", {
|
|
47
|
-
extra: `note:"Invalid hostname",pattern:${escapeString(check.hostname)}`,
|
|
48
|
-
message: check.message
|
|
49
|
-
})}
|
|
65
|
+
let parse;
|
|
66
|
+
if (!urlReadsParsed(check)) parse = emit`
|
|
67
|
+
if(!${emitRuntimeHelper(g.ctx, "__zcUrl", ZC_URL_DECL)}(${trimmedVar})){
|
|
68
|
+
${invalidFormat(g, "url", { message: check.message })}
|
|
69
|
+
}else{
|
|
70
|
+
${g.output}=${urlStripped(trimmedVar, g.ctx)};
|
|
50
71
|
}`;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
72
|
+
else {
|
|
73
|
+
const urlVar = g.temp("u");
|
|
74
|
+
let inner = "";
|
|
75
|
+
if (check.hostname) {
|
|
76
|
+
const re = g.regex("host", check.hostname, check.hostnameFlags);
|
|
77
|
+
inner += emit`
|
|
78
|
+
${lastIndexReset(re, check.hostnameFlags)}
|
|
79
|
+
if(!${re}.test(${urlVar}.hostname)){
|
|
80
|
+
${invalidFormat(g, "url", {
|
|
81
|
+
extra: `note:"Invalid hostname",pattern:${escapeString(check.hostname)}`,
|
|
82
|
+
message: check.message
|
|
83
|
+
})}
|
|
84
|
+
}`;
|
|
85
|
+
}
|
|
86
|
+
if (check.protocol) {
|
|
87
|
+
const re = g.regex("proto", check.protocol, check.protocolFlags);
|
|
88
|
+
inner += emit`
|
|
89
|
+
${lastIndexReset(re, check.protocolFlags)}
|
|
90
|
+
if(!${re}.test(${urlProtocol(urlVar)})){
|
|
91
|
+
${invalidFormat(g, "url", {
|
|
92
|
+
extra: `note:"Invalid protocol",pattern:${escapeString(check.protocol)}`,
|
|
93
|
+
message: check.message
|
|
94
|
+
})}
|
|
95
|
+
}`;
|
|
96
|
+
}
|
|
97
|
+
inner += `${g.output}=${check.normalize ? `${urlVar}.href` : urlStripped(trimmedVar, g.ctx)};`;
|
|
98
|
+
parse = emit`
|
|
99
|
+
var ${urlVar}=null;
|
|
100
|
+
try{${urlVar}=new URL(${trimmedVar});}catch(_){}
|
|
101
|
+
if(${urlVar}===null){
|
|
102
|
+
${invalidFormat(g, "url", { message: check.message })}
|
|
103
|
+
}else{
|
|
104
|
+
${inner}
|
|
62
105
|
}`;
|
|
63
106
|
}
|
|
64
|
-
const stripped = check.normalize ? `${urlVar}.href` : `${trimmedVar}.replace(${g.regex("tnl", "[\\t\\n\\r]", "g")},"")`;
|
|
65
|
-
inner += `${g.output}=${stripped};`;
|
|
66
|
-
let parse = emit`
|
|
67
|
-
var ${urlVar}=null;
|
|
68
|
-
try{${urlVar}=new URL(${trimmedVar});}catch(_){}
|
|
69
|
-
if(${urlVar}===null){
|
|
70
|
-
${invalidFormat(g, "url", { message: check.message })}
|
|
71
|
-
}else{
|
|
72
|
-
${inner}
|
|
73
|
-
}`;
|
|
74
107
|
if (!check.normalize && check.protocol === HTTP_PROTOCOL_SOURCE) parse = emit`
|
|
75
108
|
if(!${g.regex("httpUrl", "^https?:\\/\\/", "i")}.test(${trimmedVar})){
|
|
76
109
|
${invalidFormat(g, "url", {
|
|
@@ -84,6 +117,35 @@ function slowUrlCheck(check, g) {
|
|
|
84
117
|
var ${trimmedVar}=${g.input}.trim();
|
|
85
118
|
${parse}`;
|
|
86
119
|
}
|
|
120
|
+
/**
|
|
121
|
+
* The url check on the build path: {@link slowUrlCheck}'s verdict and
|
|
122
|
+
* write-back as statements over `value`, which leave the rewritten value in it
|
|
123
|
+
* and `return fail` at the first failure rather than push an issue — the
|
|
124
|
+
* deferred walk reports those.
|
|
125
|
+
*/
|
|
126
|
+
function buildUrlCheck(check, value, fail, temp, ctx) {
|
|
127
|
+
const trimmedVar = temp("but");
|
|
128
|
+
let code = `${trimmedVar}=${value}.trim();`;
|
|
129
|
+
if (!check.normalize && check.protocol === HTTP_PROTOCOL_SOURCE) {
|
|
130
|
+
const re = emitRegex(ctx, "httpUrl", "^https?:\\/\\/", "i");
|
|
131
|
+
code += `if(!${re}.test(${trimmedVar}))return ${fail};`;
|
|
132
|
+
}
|
|
133
|
+
if (!urlReadsParsed(check)) {
|
|
134
|
+
const parses = `${emitRuntimeHelper(ctx, "__zcUrl", ZC_URL_DECL)}(${trimmedVar})`;
|
|
135
|
+
return `${code}if(!${parses})return ${fail};${value}=${urlStripped(trimmedVar, ctx)};`;
|
|
136
|
+
}
|
|
137
|
+
const urlVar = temp("bur");
|
|
138
|
+
code += `try{${urlVar}=new URL(${trimmedVar});}catch(_){return ${fail};}`;
|
|
139
|
+
if (check.hostname) {
|
|
140
|
+
const re = emitRegex(ctx, "host", check.hostname, check.hostnameFlags);
|
|
141
|
+
code += `${lastIndexReset(re, check.hostnameFlags)}if(!${re}.test(${urlVar}.hostname))return ${fail};`;
|
|
142
|
+
}
|
|
143
|
+
if (check.protocol) {
|
|
144
|
+
const re = emitRegex(ctx, "proto", check.protocol, check.protocolFlags);
|
|
145
|
+
code += `${lastIndexReset(re, check.protocolFlags)}if(!${re}.test(${urlProtocol(urlVar)}))return ${fail};`;
|
|
146
|
+
}
|
|
147
|
+
return `${code}${value}=${check.normalize ? `${urlVar}.href` : urlStripped(trimmedVar, ctx)};`;
|
|
148
|
+
}
|
|
87
149
|
function slowString(ir, g) {
|
|
88
150
|
let code = "";
|
|
89
151
|
if (ir.coerce) code += emit`try{${g.output}=String(${g.input});}catch(_){}`;
|
|
@@ -223,7 +285,7 @@ function fastStringCheck(check, x, ctx) {
|
|
|
223
285
|
case "starts_with": return `${x}.startsWith(${escapeString(check.prefix)})`;
|
|
224
286
|
case "ends_with": return `${x}.endsWith(${escapeString(check.suffix)})`;
|
|
225
287
|
case "string_format": {
|
|
226
|
-
if (check
|
|
288
|
+
if (isUrlRewrite(check)) return null;
|
|
227
289
|
let pattern;
|
|
228
290
|
let prefix;
|
|
229
291
|
if (check.format === "email") {
|
|
@@ -259,6 +321,6 @@ function fastString(ir, g) {
|
|
|
259
321
|
return parts.join("&&");
|
|
260
322
|
}
|
|
261
323
|
//#endregion
|
|
262
|
-
export { fastString, fastStringCheck, slowString };
|
|
324
|
+
export { buildUrlCheck, fastString, fastStringCheck, isUrlRewrite, slowString };
|
|
263
325
|
|
|
264
326
|
//# sourceMappingURL=string.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"string.js","names":[],"sources":["../../../../src/core/codegen/schemas/string.ts"],"sourcesContent":["import type { CheckIR, CheckStringFormat, StringIR } from \"../../types.js\";\nimport type { CodeGenContext, FastGen, SlowGen } from \"../context.js\";\nimport {\n checkPriority,\n emitEffectFn,\n emitRegex,\n emitRegexSourceString,\n emitRuntimeHelper,\n escapeString,\n} from \"../context.js\";\nimport { emit } from \"../emit.js\";\nimport { invalidFormat, invalidType, tooBig, tooSmall } from \"../emit-issue.js\";\nimport { ZC_EMAIL_DECL } from \"../issue-decls.js\";\nimport {\n EMAIL_REGEX_SOURCE,\n fastTestSource,\n isDefaultEmailPattern,\n UUID_REGEX_SOURCE,\n} from \"../well-known-regex.js\";\nimport { fastRefineTest, refineCheck, superRefineCheck, superRefineFastTest } from \"./effect.js\";\nimport { stringLengthTests, whenGatedSizeChecks } from \"./sizeable.js\";\n\n/** `re.lastIndex=0;` reset statement for stateful (g/y-flagged) regexes. */\nfunction lastIndexReset(regexVar: string, flags: string | undefined): string {\n return flags && /[gy]/.test(flags) ? `${regexVar}.lastIndex=0;` : \"\";\n}\n\n/**\n * `regexes.httpProtocol.source`. `parseURLObject` compares a url check's\n * protocol SOURCE against it — not the schema constructor — so any check whose\n * protocol is spelled this way gets the guard, `z.httpUrl()` or not.\n */\nconst HTTP_PROTOCOL_SOURCE = \"^https?$\";\n\n/**\n * Generate the url check, mirroring $ZodURL semantics:\n * trim → (for an http(s)-protocol check without normalize) require `://` →\n * new URL(trimmed) → optional hostname/protocol regex tests → write back\n * url.href (normalize) or the trimmed input with its tabs and newlines deleted.\n *\n * The `://` guard is `parseURLObject`'s: without it the URL parser accepts\n * `http:example.com`, and its rejection is a distinct issue (`note: \"Invalid\n * URL format\"`, no `pattern`) pushed BEFORE the parser runs. The deletion of\n * `\\t`, `\\n` and `\\r` (`stripTabAndNewline`) matches what the parser itself\n * drops before it reads the host, so the returned value names the host that was\n * validated.\n *\n * $ZodURL is another constructor that OVERRIDES the `??=`-installed default\n * check, so none of the issues below carries `origin` — and the two that do\n * carry a `pattern` use `regex.source`, not the default check's\n * `regex.toString()` (no delimiters, no flags). Both are reproduced verbatim.\n */\nfunction slowUrlCheck(check: CheckStringFormat, g: SlowGen): string {\n const trimmedVar = g.temp(\"ut\");\n const urlVar = g.temp(\"u\");\n let inner = \"\";\n if (check.hostname) {\n const re = g.regex(\"host\", check.hostname, check.hostnameFlags);\n inner += emit`\n ${lastIndexReset(re, check.hostnameFlags)}\n if(!${re}.test(${urlVar}.hostname)){\n ${invalidFormat(g, \"url\", {\n extra: `note:\"Invalid hostname\",pattern:${escapeString(check.hostname)}`,\n message: check.message,\n })}\n }`;\n }\n if (check.protocol) {\n const re = g.regex(\"proto\", check.protocol, check.protocolFlags);\n const protoExpr = `(${urlVar}.protocol.endsWith(\":\")?${urlVar}.protocol.slice(0,-1):${urlVar}.protocol)`;\n inner += emit`\n ${lastIndexReset(re, check.protocolFlags)}\n if(!${re}.test(${protoExpr})){\n ${invalidFormat(g, \"url\", {\n extra: `note:\"Invalid protocol\",pattern:${escapeString(check.protocol)}`,\n message: check.message,\n })}\n }`;\n }\n // Zod writes the value back even when hostname/protocol issues were pushed.\n const stripped = check.normalize\n ? `${urlVar}.href`\n : `${trimmedVar}.replace(${g.regex(\"tnl\", \"[\\\\t\\\\n\\\\r]\", \"g\")},\"\")`;\n inner += `${g.output}=${stripped};`;\n let parse = emit`\n var ${urlVar}=null;\n try{${urlVar}=new URL(${trimmedVar});}catch(_){}\n if(${urlVar}===null){\n ${invalidFormat(g, \"url\", { message: check.message })}\n }else{\n ${inner}\n }`;\n if (!check.normalize && check.protocol === HTTP_PROTOCOL_SOURCE) {\n const re = g.regex(\"httpUrl\", \"^https?:\\\\/\\\\/\", \"i\");\n parse = emit`\n if(!${re}.test(${trimmedVar})){\n ${invalidFormat(g, \"url\", { extra: `note:\"Invalid URL format\"`, message: check.message })}\n }else{\n ${parse}\n }`;\n }\n return emit`\n var ${trimmedVar}=${g.input}.trim();\n ${parse}`;\n}\n\nexport function slowString(ir: StringIR, g: SlowGen): string {\n let code = \"\";\n if (ir.coerce) {\n code += emit`try{${g.output}=String(${g.input});}catch(_){}`;\n }\n code += emit`\n if(typeof ${g.input}!==\"string\"){\n ${invalidType(g, \"string\")}\n ${whenGatedSizeChecks(ir.checks, g, \"length\")}\n }`;\n\n if (ir.checks.length > 0) {\n code += `else{`;\n // Insertion order mirrors zod's issue order for multi-failure inputs;\n // the slow path collects all issues with no short-circuit.\n for (const check of ir.checks) {\n switch (check.kind) {\n // Length is measured in code points where the unit count leaves the\n // verdict in doubt — see stringLengthTests.\n case \"min_length\":\n code += emit`\n if(${stringLengthTests.minFails(g.input, check.minimum, g.ctx)}){\n ${tooSmall(g, check.minimum, \"string\", true, { message: check.message })}\n }`;\n break;\n case \"max_length\":\n code += emit`\n if(${stringLengthTests.maxFails(g.input, check.maximum, g.ctx)}){\n ${tooBig(g, check.maximum, \"string\", true, { message: check.message })}\n }`;\n break;\n case \"length_equals\": {\n const length = g.temp(\"cl\");\n code += emit`\n var ${length}=${stringLengthTests.measure(g.input, check.length, g.ctx)};\n if(${length}<${check.length}){\n ${tooSmall(g, check.length, \"string\", true, { exact: true, message: check.message })}\n }else if(${length}>${check.length}){\n ${tooBig(g, check.length, \"string\", true, { exact: true, message: check.message })}\n }`;\n break;\n }\n // includes/starts_with/ends_with each carry `origin:\"string\"` but NO\n // `pattern`: $ZodCheckIncludes/StartsWith/EndsWith bypass\n // $ZodCheckStringFormat entirely (they init from $ZodCheck and assign\n // `inst._zod.check` directly), and the pattern they build is registered\n // in the bag for JSON Schema only, never put on the issue.\n case \"includes\":\n code += emit`\n if(!${g.input}.includes(${escapeString(check.includes)}${check.position !== undefined ? `,${check.position}` : \"\"})){\n ${invalidFormat(g, \"includes\", { origin: \"string\", extra: `includes:${escapeString(check.includes)}`, message: check.message })}\n }`;\n break;\n case \"starts_with\":\n code += emit`\n if(!${g.input}.startsWith(${escapeString(check.prefix)})){\n ${invalidFormat(g, \"starts_with\", { origin: \"string\", extra: `prefix:${escapeString(check.prefix)}`, message: check.message })}\n }`;\n break;\n case \"ends_with\":\n code += emit`\n if(!${g.input}.endsWith(${escapeString(check.suffix)})){\n ${invalidFormat(g, \"ends_with\", { origin: \"string\", extra: `suffix:${escapeString(check.suffix)}`, message: check.message })}\n }`;\n break;\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 case \"overwrite_effect\":\n // $ZodCheckOverwrite: value = tx(value). Later checks read the\n // rewritten value because input aliases the output location.\n code += emit`${g.output}=${emitEffectFn(g.ctx, check.source)}(${g.input});`;\n break;\n case \"string_format\": {\n let prefix: string;\n let pattern: string;\n // Only the BUILT-IN z.url() gets the URL-parser check, and extraction\n // never gives that one a pattern. A `pattern` on a \"url\"-named check\n // therefore marks a custom format that merely borrowed the name\n // (`z.stringFormat(\"url\", /re/)`); it validates through its own regex,\n // so fall through and compile that instead of the URL parser.\n if (check.format === \"url\" && !check.pattern) {\n code += slowUrlCheck(check, g);\n continue;\n }\n if (check.format === \"email\") {\n pattern = check.pattern ?? EMAIL_REGEX_SOURCE;\n prefix = \"email\";\n } else if (check.format === \"regex\" && check.pattern) {\n pattern = check.pattern;\n prefix = \"str\";\n } else if (check.format === \"uuid\") {\n pattern = check.pattern ?? UUID_REGEX_SOURCE;\n prefix = \"uuid\";\n } else {\n if (check.pattern) {\n pattern = check.pattern;\n prefix = \"str\";\n } else {\n // Extraction guarantees a pattern for non-special formats;\n // defensive skip kept for hand-built IR.\n continue;\n }\n }\n // Zod's default email pattern is tested by the `__zcEmail` scanner, so\n // no RegExp is declared for it at all (see ZC_EMAIL_DECL); the issue\n // below still names the pattern, through the shared source string.\n const scanner = isDefaultEmailPattern(pattern, check.patternFlags)\n ? emitRuntimeHelper(g.ctx, \"__zcEmail\", ZC_EMAIL_DECL)\n : null;\n const regexVar = scanner === null ? g.regex(prefix, pattern, check.patternFlags) : null;\n // Zod's invalid_format shape depends on WHICH check instance ran.\n // `$ZodCheckStringFormat.init` installs the default pattern check with\n // `??=`, and that default pushes `origin:\"string\"` + `pattern`. A\n // constructor that OVERRIDES `inst._zod.check` pushes its own issue\n // instead — `$ZodCustomStringFormat` (z.stringFormat/z.hex/z.hostname/\n // z.hash) pushes a bare `{code, format, input}` because it validates\n // through `def.fn` and never reads `def.pattern`. We test the pattern\n // either way, so the issue shape is driven off the extracted flag.\n let extra: string | undefined;\n if (!check.bareIssue) {\n // When emitRegex swapped in a faster equivalent pattern (or the\n // scanner stands in for the RegExp), the runtime regex's toString()\n // would leak the rewrite into the issue. Reference the shared\n // original-pattern string instead (pattern came from RegExp.source,\n // so it matches zod's `.toString()` byte-for-byte).\n const rewritten = !check.patternFlags && fastTestSource(pattern) !== null;\n const patternExpr =\n rewritten || regexVar === null\n ? emitRegexSourceString(g.ctx, pattern)\n : `${regexVar}.toString()`;\n extra = `pattern:${patternExpr}`;\n }\n const test =\n regexVar === null ? `${scanner}(${g.input})` : `${regexVar}.test(${g.input})`;\n code += emit`\n ${regexVar === null ? \"\" : lastIndexReset(regexVar, check.patternFlags)}\n if(!${test}){\n ${invalidFormat(g, { expr: escapeString(check.format) }, { origin: check.bareIssue ? undefined : \"string\", extra, message: check.message })}\n }`;\n break;\n }\n }\n }\n code += `}`;\n }\n\n return `${code}\\n`;\n}\n\n/**\n * Boolean expression testing ONE compiled string check against `x`, or null when\n * the check is not expressible as a pure predicate (`z.url()`, which trims and\n * normalizes, and an unknown format with no pattern).\n *\n * Shared by the fast path — which sorts the checks cheapest-first and joins them\n * with `&&` — and by the build path, which emits them one statement at a time in\n * DECLARATION order so an interleaved `.trim()` rewrite is visible to the checks\n * that follow it (see buildString).\n */\nexport function fastStringCheck(check: CheckIR, x: string, ctx: CodeGenContext): string | null {\n switch (check.kind) {\n case \"min_length\":\n return stringLengthTests.min(x, check.minimum, ctx);\n case \"max_length\":\n return stringLengthTests.max(x, check.maximum, ctx);\n case \"length_equals\":\n return stringLengthTests.equals(x, check.length, ctx);\n case \"includes\":\n return check.position !== undefined\n ? `${x}.includes(${escapeString(check.includes)},${check.position})`\n : `${x}.includes(${escapeString(check.includes)})`;\n case \"starts_with\":\n return `${x}.startsWith(${escapeString(check.prefix)})`;\n case \"ends_with\":\n return `${x}.endsWith(${escapeString(check.suffix)})`;\n case \"string_format\": {\n // URL validation mutates (trims) and uses try/catch — not a predicate.\n // A patterned \"url\" check is a custom format that borrowed the name (see\n // buildString), and its regex IS a predicate.\n if (check.format === \"url\" && !check.pattern) return null;\n let pattern: string;\n let prefix: string;\n if (check.format === \"email\") {\n prefix = \"email\";\n pattern = check.pattern ?? EMAIL_REGEX_SOURCE;\n } else if (check.format === \"uuid\") {\n prefix = \"uuid\";\n pattern = check.pattern ?? UUID_REGEX_SOURCE;\n } else if (check.pattern) {\n prefix = \"re\";\n pattern = check.pattern;\n } else {\n // Unknown format without pattern — can't generate a check\n return null;\n }\n // Zod's default email pattern runs as a linear scan instead of a RegExp\n // (see ZC_EMAIL_DECL) — a plain call, so it needs no parens either.\n if (isDefaultEmailPattern(pattern, check.patternFlags)) {\n return `${emitRuntimeHelper(ctx, \"__zcEmail\", ZC_EMAIL_DECL)}(${x})`;\n }\n const v = emitRegex(ctx, prefix, pattern, check.patternFlags);\n // Stateful (g/y) regexes need lastIndex reset; comma expression keeps\n // this usable inside the boolean chain.\n return check.patternFlags && /[gy]/.test(check.patternFlags)\n ? `((${v}.lastIndex=0),${v}.test(${x}))`\n : `${v}.test(${x})`;\n }\n default:\n // A check kind this generator does not model (number/bigint/date/set\n // shapes never reach here from a string node).\n return null;\n }\n}\n\nexport function fastString(ir: StringIR, g: FastGen): string | null {\n if (ir.coerce) return null;\n // Overwrite effects rewrite the value — the fast path returns input\n // unchanged, so any mutation makes it ineligible.\n if (ir.checks.some((c) => c.kind === \"overwrite_effect\")) return null;\n\n const x = g.input;\n const parts: string[] = [`typeof ${x}===\"string\"`];\n const checks = ir.checks.filter(\n (c): c is CheckIR => c.kind !== \"refine_effect\" && c.kind !== \"overwrite_effect\",\n );\n\n for (const check of checks.sort(checkPriority)) {\n const expr = fastStringCheck(check, x, g.ctx);\n if (expr === null) return null;\n parts.push(expr);\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
|
+
{"version":3,"file":"string.js","names":[],"sources":["../../../../src/core/codegen/schemas/string.ts"],"sourcesContent":["import type { CheckIR, CheckStringFormat, StringIR } from \"../../types.js\";\nimport type { CodeGenContext, FastGen, SlowGen } from \"../context.js\";\nimport {\n checkPriority,\n emitEffectFn,\n emitRegex,\n emitRegexSourceString,\n emitRuntimeHelper,\n escapeString,\n} from \"../context.js\";\nimport { emit } from \"../emit.js\";\nimport { invalidFormat, invalidType, tooBig, tooSmall } from \"../emit-issue.js\";\nimport { ZC_EMAIL_DECL, ZC_URL_DECL } from \"../issue-decls.js\";\nimport {\n EMAIL_REGEX_SOURCE,\n fastTestSource,\n isDefaultEmailPattern,\n UUID_REGEX_SOURCE,\n} from \"../well-known-regex.js\";\nimport { fastRefineTest, refineCheck, superRefineCheck, superRefineFastTest } from \"./effect.js\";\nimport { stringLengthTests, whenGatedSizeChecks } from \"./sizeable.js\";\n\n/** `re.lastIndex=0;` reset statement for stateful (g/y-flagged) regexes. */\nfunction lastIndexReset(regexVar: string, flags: string | undefined): string {\n return flags && /[gy]/.test(flags) ? `${regexVar}.lastIndex=0;` : \"\";\n}\n\n/**\n * `regexes.httpProtocol.source`. `parseURLObject` compares a url check's\n * protocol SOURCE against it — not the schema constructor — so any check whose\n * protocol is spelled this way gets the guard, `z.httpUrl()` or not.\n */\nconst HTTP_PROTOCOL_SOURCE = \"^https?$\";\n\n/**\n * Is this a `z.url()` check — one that trims the value and writes back a\n * rewrite of it? A patterned \"url\" is a custom format that borrowed the name\n * (see buildString), validating through its own regex like any other format.\n */\nexport function isUrlRewrite(check: StringIR[\"checks\"][number]): boolean {\n return check.kind === \"string_format\" && check.format === \"url\" && !check.pattern;\n}\n\n/**\n * Does the url check read the PARSED url — a hostname or protocol test, or\n * `normalize`'s href? Only then is the URL object needed; a check that only asks\n * whether the input parses goes through `__zcUrl` (see ZC_URL_DECL) instead.\n */\nfunction urlReadsParsed(check: CheckStringFormat): boolean {\n return Boolean(check.hostname) || Boolean(check.protocol) || check.normalize === true;\n}\n\n/** $ZodURL's write-back without `normalize`: the trimmed input, tabs and newlines deleted. */\nfunction urlStripped(trimmedVar: string, ctx: CodeGenContext): string {\n return `${trimmedVar}.replace(${emitRegex(ctx, \"tnl\", \"[\\\\t\\\\n\\\\r]\", \"g\")},\"\")`;\n}\n\n/** `url.protocol` without its trailing colon, the form $ZodURL tests. */\nfunction urlProtocol(urlVar: string): string {\n return `(${urlVar}.protocol.endsWith(\":\")?${urlVar}.protocol.slice(0,-1):${urlVar}.protocol)`;\n}\n\n/**\n * Generate the url check, mirroring $ZodURL semantics:\n * trim → (for an http(s)-protocol check without normalize) require `://` →\n * new URL(trimmed) → optional hostname/protocol regex tests → write back\n * url.href (normalize) or the trimmed input with its tabs and newlines deleted.\n * A check that reads nothing off the parsed URL only asks `__zcUrl` whether the\n * input parses, which skips constructing it.\n *\n * The `://` guard is `parseURLObject`'s: without it the URL parser accepts\n * `http:example.com`, and its rejection is a distinct issue (`note: \"Invalid\n * URL format\"`, no `pattern`) pushed BEFORE the parser runs. The deletion of\n * `\\t`, `\\n` and `\\r` (`stripTabAndNewline`) matches what the parser itself\n * drops before it reads the host, so the returned value names the host that was\n * validated.\n *\n * $ZodURL is another constructor that OVERRIDES the `??=`-installed default\n * check, so none of the issues below carries `origin` — and the two that do\n * carry a `pattern` use `regex.source`, not the default check's\n * `regex.toString()` (no delimiters, no flags). Both are reproduced verbatim.\n */\nfunction slowUrlCheck(check: CheckStringFormat, g: SlowGen): string {\n const trimmedVar = g.temp(\"ut\");\n let parse: string;\n if (!urlReadsParsed(check)) {\n parse = emit`\n if(!${emitRuntimeHelper(g.ctx, \"__zcUrl\", ZC_URL_DECL)}(${trimmedVar})){\n ${invalidFormat(g, \"url\", { message: check.message })}\n }else{\n ${g.output}=${urlStripped(trimmedVar, g.ctx)};\n }`;\n } else {\n const urlVar = g.temp(\"u\");\n let inner = \"\";\n if (check.hostname) {\n const re = g.regex(\"host\", check.hostname, check.hostnameFlags);\n inner += emit`\n ${lastIndexReset(re, check.hostnameFlags)}\n if(!${re}.test(${urlVar}.hostname)){\n ${invalidFormat(g, \"url\", {\n extra: `note:\"Invalid hostname\",pattern:${escapeString(check.hostname)}`,\n message: check.message,\n })}\n }`;\n }\n if (check.protocol) {\n const re = g.regex(\"proto\", check.protocol, check.protocolFlags);\n inner += emit`\n ${lastIndexReset(re, check.protocolFlags)}\n if(!${re}.test(${urlProtocol(urlVar)})){\n ${invalidFormat(g, \"url\", {\n extra: `note:\"Invalid protocol\",pattern:${escapeString(check.protocol)}`,\n message: check.message,\n })}\n }`;\n }\n // Zod writes the value back even when hostname/protocol issues were pushed.\n inner += `${g.output}=${check.normalize ? `${urlVar}.href` : urlStripped(trimmedVar, g.ctx)};`;\n parse = emit`\n var ${urlVar}=null;\n try{${urlVar}=new URL(${trimmedVar});}catch(_){}\n if(${urlVar}===null){\n ${invalidFormat(g, \"url\", { message: check.message })}\n }else{\n ${inner}\n }`;\n }\n if (!check.normalize && check.protocol === HTTP_PROTOCOL_SOURCE) {\n const re = g.regex(\"httpUrl\", \"^https?:\\\\/\\\\/\", \"i\");\n parse = emit`\n if(!${re}.test(${trimmedVar})){\n ${invalidFormat(g, \"url\", { extra: `note:\"Invalid URL format\"`, message: check.message })}\n }else{\n ${parse}\n }`;\n }\n return emit`\n var ${trimmedVar}=${g.input}.trim();\n ${parse}`;\n}\n\n/**\n * The url check on the build path: {@link slowUrlCheck}'s verdict and\n * write-back as statements over `value`, which leave the rewritten value in it\n * and `return fail` at the first failure rather than push an issue — the\n * deferred walk reports those.\n */\nexport function buildUrlCheck(\n check: CheckStringFormat,\n value: string,\n fail: string,\n temp: (prefix: string) => string,\n ctx: CodeGenContext,\n): string {\n const trimmedVar = temp(\"but\");\n let code = `${trimmedVar}=${value}.trim();`;\n if (!check.normalize && check.protocol === HTTP_PROTOCOL_SOURCE) {\n const re = emitRegex(ctx, \"httpUrl\", \"^https?:\\\\/\\\\/\", \"i\");\n code += `if(!${re}.test(${trimmedVar}))return ${fail};`;\n }\n if (!urlReadsParsed(check)) {\n const parses = `${emitRuntimeHelper(ctx, \"__zcUrl\", ZC_URL_DECL)}(${trimmedVar})`;\n return `${code}if(!${parses})return ${fail};${value}=${urlStripped(trimmedVar, ctx)};`;\n }\n const urlVar = temp(\"bur\");\n code += `try{${urlVar}=new URL(${trimmedVar});}catch(_){return ${fail};}`;\n if (check.hostname) {\n const re = emitRegex(ctx, \"host\", check.hostname, check.hostnameFlags);\n code += `${lastIndexReset(re, check.hostnameFlags)}if(!${re}.test(${urlVar}.hostname))return ${fail};`;\n }\n if (check.protocol) {\n const re = emitRegex(ctx, \"proto\", check.protocol, check.protocolFlags);\n code += `${lastIndexReset(re, check.protocolFlags)}if(!${re}.test(${urlProtocol(urlVar)}))return ${fail};`;\n }\n return `${code}${value}=${check.normalize ? `${urlVar}.href` : urlStripped(trimmedVar, ctx)};`;\n}\n\nexport function slowString(ir: StringIR, g: SlowGen): string {\n let code = \"\";\n if (ir.coerce) {\n code += emit`try{${g.output}=String(${g.input});}catch(_){}`;\n }\n code += emit`\n if(typeof ${g.input}!==\"string\"){\n ${invalidType(g, \"string\")}\n ${whenGatedSizeChecks(ir.checks, g, \"length\")}\n }`;\n\n if (ir.checks.length > 0) {\n code += `else{`;\n // Insertion order mirrors zod's issue order for multi-failure inputs;\n // the slow path collects all issues with no short-circuit.\n for (const check of ir.checks) {\n switch (check.kind) {\n // Length is measured in code points where the unit count leaves the\n // verdict in doubt — see stringLengthTests.\n case \"min_length\":\n code += emit`\n if(${stringLengthTests.minFails(g.input, check.minimum, g.ctx)}){\n ${tooSmall(g, check.minimum, \"string\", true, { message: check.message })}\n }`;\n break;\n case \"max_length\":\n code += emit`\n if(${stringLengthTests.maxFails(g.input, check.maximum, g.ctx)}){\n ${tooBig(g, check.maximum, \"string\", true, { message: check.message })}\n }`;\n break;\n case \"length_equals\": {\n const length = g.temp(\"cl\");\n code += emit`\n var ${length}=${stringLengthTests.measure(g.input, check.length, g.ctx)};\n if(${length}<${check.length}){\n ${tooSmall(g, check.length, \"string\", true, { exact: true, message: check.message })}\n }else if(${length}>${check.length}){\n ${tooBig(g, check.length, \"string\", true, { exact: true, message: check.message })}\n }`;\n break;\n }\n // includes/starts_with/ends_with each carry `origin:\"string\"` but NO\n // `pattern`: $ZodCheckIncludes/StartsWith/EndsWith bypass\n // $ZodCheckStringFormat entirely (they init from $ZodCheck and assign\n // `inst._zod.check` directly), and the pattern they build is registered\n // in the bag for JSON Schema only, never put on the issue.\n case \"includes\":\n code += emit`\n if(!${g.input}.includes(${escapeString(check.includes)}${check.position !== undefined ? `,${check.position}` : \"\"})){\n ${invalidFormat(g, \"includes\", { origin: \"string\", extra: `includes:${escapeString(check.includes)}`, message: check.message })}\n }`;\n break;\n case \"starts_with\":\n code += emit`\n if(!${g.input}.startsWith(${escapeString(check.prefix)})){\n ${invalidFormat(g, \"starts_with\", { origin: \"string\", extra: `prefix:${escapeString(check.prefix)}`, message: check.message })}\n }`;\n break;\n case \"ends_with\":\n code += emit`\n if(!${g.input}.endsWith(${escapeString(check.suffix)})){\n ${invalidFormat(g, \"ends_with\", { origin: \"string\", extra: `suffix:${escapeString(check.suffix)}`, message: check.message })}\n }`;\n break;\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 case \"overwrite_effect\":\n // $ZodCheckOverwrite: value = tx(value). Later checks read the\n // rewritten value because input aliases the output location.\n code += emit`${g.output}=${emitEffectFn(g.ctx, check.source)}(${g.input});`;\n break;\n case \"string_format\": {\n let prefix: string;\n let pattern: string;\n // Only the BUILT-IN z.url() gets the URL-parser check, and extraction\n // never gives that one a pattern. A `pattern` on a \"url\"-named check\n // therefore marks a custom format that merely borrowed the name\n // (`z.stringFormat(\"url\", /re/)`); it validates through its own regex,\n // so fall through and compile that instead of the URL parser.\n if (check.format === \"url\" && !check.pattern) {\n code += slowUrlCheck(check, g);\n continue;\n }\n if (check.format === \"email\") {\n pattern = check.pattern ?? EMAIL_REGEX_SOURCE;\n prefix = \"email\";\n } else if (check.format === \"regex\" && check.pattern) {\n pattern = check.pattern;\n prefix = \"str\";\n } else if (check.format === \"uuid\") {\n pattern = check.pattern ?? UUID_REGEX_SOURCE;\n prefix = \"uuid\";\n } else {\n if (check.pattern) {\n pattern = check.pattern;\n prefix = \"str\";\n } else {\n // Extraction guarantees a pattern for non-special formats;\n // defensive skip kept for hand-built IR.\n continue;\n }\n }\n // Zod's default email pattern is tested by the `__zcEmail` scanner, so\n // no RegExp is declared for it at all (see ZC_EMAIL_DECL); the issue\n // below still names the pattern, through the shared source string.\n const scanner = isDefaultEmailPattern(pattern, check.patternFlags)\n ? emitRuntimeHelper(g.ctx, \"__zcEmail\", ZC_EMAIL_DECL)\n : null;\n const regexVar = scanner === null ? g.regex(prefix, pattern, check.patternFlags) : null;\n // Zod's invalid_format shape depends on WHICH check instance ran.\n // `$ZodCheckStringFormat.init` installs the default pattern check with\n // `??=`, and that default pushes `origin:\"string\"` + `pattern`. A\n // constructor that OVERRIDES `inst._zod.check` pushes its own issue\n // instead — `$ZodCustomStringFormat` (z.stringFormat/z.hex/z.hostname/\n // z.hash) pushes a bare `{code, format, input}` because it validates\n // through `def.fn` and never reads `def.pattern`. We test the pattern\n // either way, so the issue shape is driven off the extracted flag.\n let extra: string | undefined;\n if (!check.bareIssue) {\n // When emitRegex swapped in a faster equivalent pattern (or the\n // scanner stands in for the RegExp), the runtime regex's toString()\n // would leak the rewrite into the issue. Reference the shared\n // original-pattern string instead (pattern came from RegExp.source,\n // so it matches zod's `.toString()` byte-for-byte).\n const rewritten = !check.patternFlags && fastTestSource(pattern) !== null;\n const patternExpr =\n rewritten || regexVar === null\n ? emitRegexSourceString(g.ctx, pattern)\n : `${regexVar}.toString()`;\n extra = `pattern:${patternExpr}`;\n }\n const test =\n regexVar === null ? `${scanner}(${g.input})` : `${regexVar}.test(${g.input})`;\n code += emit`\n ${regexVar === null ? \"\" : lastIndexReset(regexVar, check.patternFlags)}\n if(!${test}){\n ${invalidFormat(g, { expr: escapeString(check.format) }, { origin: check.bareIssue ? undefined : \"string\", extra, message: check.message })}\n }`;\n break;\n }\n }\n }\n code += `}`;\n }\n\n return `${code}\\n`;\n}\n\n/**\n * Boolean expression testing ONE compiled string check against `x`, or null when\n * the check is not expressible as a pure predicate (`z.url()`, which trims and\n * normalizes, and an unknown format with no pattern).\n *\n * Shared by the fast path — which sorts the checks cheapest-first and joins them\n * with `&&` — and by the build path, which emits them one statement at a time in\n * DECLARATION order so an interleaved `.trim()` rewrite is visible to the checks\n * that follow it (see buildString).\n */\nexport function fastStringCheck(check: CheckIR, x: string, ctx: CodeGenContext): string | null {\n switch (check.kind) {\n case \"min_length\":\n return stringLengthTests.min(x, check.minimum, ctx);\n case \"max_length\":\n return stringLengthTests.max(x, check.maximum, ctx);\n case \"length_equals\":\n return stringLengthTests.equals(x, check.length, ctx);\n case \"includes\":\n return check.position !== undefined\n ? `${x}.includes(${escapeString(check.includes)},${check.position})`\n : `${x}.includes(${escapeString(check.includes)})`;\n case \"starts_with\":\n return `${x}.startsWith(${escapeString(check.prefix)})`;\n case \"ends_with\":\n return `${x}.endsWith(${escapeString(check.suffix)})`;\n case \"string_format\": {\n // URL validation rewrites the value (see isUrlRewrite) — not a predicate.\n // A patterned \"url\" check is a custom format that borrowed the name, and\n // its regex IS a predicate.\n if (isUrlRewrite(check)) return null;\n let pattern: string;\n let prefix: string;\n if (check.format === \"email\") {\n prefix = \"email\";\n pattern = check.pattern ?? EMAIL_REGEX_SOURCE;\n } else if (check.format === \"uuid\") {\n prefix = \"uuid\";\n pattern = check.pattern ?? UUID_REGEX_SOURCE;\n } else if (check.pattern) {\n prefix = \"re\";\n pattern = check.pattern;\n } else {\n // Unknown format without pattern — can't generate a check\n return null;\n }\n // Zod's default email pattern runs as a linear scan instead of a RegExp\n // (see ZC_EMAIL_DECL) — a plain call, so it needs no parens either.\n if (isDefaultEmailPattern(pattern, check.patternFlags)) {\n return `${emitRuntimeHelper(ctx, \"__zcEmail\", ZC_EMAIL_DECL)}(${x})`;\n }\n const v = emitRegex(ctx, prefix, pattern, check.patternFlags);\n // Stateful (g/y) regexes need lastIndex reset; comma expression keeps\n // this usable inside the boolean chain.\n return check.patternFlags && /[gy]/.test(check.patternFlags)\n ? `((${v}.lastIndex=0),${v}.test(${x}))`\n : `${v}.test(${x})`;\n }\n default:\n // A check kind this generator does not model (number/bigint/date/set\n // shapes never reach here from a string node).\n return null;\n }\n}\n\nexport function fastString(ir: StringIR, g: FastGen): string | null {\n if (ir.coerce) return null;\n // Overwrite effects rewrite the value — the fast path returns input\n // unchanged, so any mutation makes it ineligible.\n if (ir.checks.some((c) => c.kind === \"overwrite_effect\")) return null;\n\n const x = g.input;\n const parts: string[] = [`typeof ${x}===\"string\"`];\n const checks = ir.checks.filter(\n (c): c is CheckIR => c.kind !== \"refine_effect\" && c.kind !== \"overwrite_effect\",\n );\n\n for (const check of checks.sort(checkPriority)) {\n const expr = fastStringCheck(check, x, g.ctx);\n if (expr === null) return null;\n parts.push(expr);\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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"}
|
|
@@ -57,7 +57,7 @@ function slowTuple(ir, g) {
|
|
|
57
57
|
original: g.input,
|
|
58
58
|
copy: `${x}.slice()`,
|
|
59
59
|
key: String(i),
|
|
60
|
-
path: extendStaticPathIndex(g.path, i),
|
|
60
|
+
path: extendStaticPathIndex(g.ctx, g.path, i),
|
|
61
61
|
issues: itemIssues
|
|
62
62
|
});
|
|
63
63
|
const absent = `${i}>=${lenVar}`;
|
|
@@ -96,7 +96,7 @@ function slowTuple(ir, g) {
|
|
|
96
96
|
original: g.input,
|
|
97
97
|
copy: `${x}.slice()`,
|
|
98
98
|
key: idxVar,
|
|
99
|
-
path: extendPath(g.path, idxVar),
|
|
99
|
+
path: extendPath(g.ctx, g.path, idxVar),
|
|
100
100
|
issues: g.issues
|
|
101
101
|
});
|
|
102
102
|
itemsCode += emit`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tuple.js","names":[],"sources":["../../../../src/core/codegen/schemas/tuple.ts"],"sourcesContent":["import type { SchemaIR, TupleIR } from \"../../types.js\";\nimport type { FastGen, SlowGen } from \"../context.js\";\nimport {\n declareFastTemps,\n extendPath,\n extendStaticPathIndex,\n hasMutation,\n outputAlwaysDefined,\n tupleRewritesShortInput,\n visitMember,\n} from \"../context.js\";\nimport { orderByRuntimeCost } from \"../fast-size.js\";\nimport { emit } from \"../emit.js\";\nimport { invalidType, tooBig, tooSmall } from \"../emit-issue.js\";\n\n/**\n * What an ABSENT slot (index at or past the input's length) does to the output,\n * per zod's `handleTupleResults`:\n *\n * - `present`: can never be absent — below `optStart` without a rest element,\n * where a shorter input is already `too_small`.\n * - `skip`: at or past `optoutStart` and on the \"optional\" rung of `optin`.\n * The output ends here; the item is not consulted and nothing after it runs.\n * - `tail`: at or past `optoutStart` on any other rung. The item runs on\n * `undefined`; a failure ends the output here with its issues dropped, a\n * success is written back and may be trimmed later if it is `undefined`.\n * - `pad`: below `optoutStart`, so a later slot has to keep its index. The\n * item runs on `undefined`, its issues count, and its result — an own\n * `undefined` at the least — is written back.\n */\ntype SlotKind = \"present\" | \"skip\" | \"tail\" | \"pad\";\n\nfunction slotKinds(ir: TupleIR): SlotKind[] {\n const len = ir.items.length;\n const optoutStart = ir.optoutStart ?? len;\n const optionalIn = new Set(ir.optionalIn ?? []);\n const absentFrom = ir.rest === null ? ir.optStart : 0;\n return ir.items.map((_, i) => {\n if (i < absentFrom) return \"present\";\n if (i < optoutStart) return \"pad\";\n return optionalIn.has(i) ? \"skip\" : \"tail\";\n });\n}\n\n/**\n * Mirrors $ZodTuple. Without rest, an input shorter than `optStart` is a single\n * `too_small` (`minimum: optStart`, `inclusive: true`) that skips item\n * validation, and a longer one a single `too_big` that does NOT — zod pushes it\n * and runs the items anyway, so their issues follow it. Both are created by\n * the tuple node (schema error applies) and both share the `code, minimum |\n * maximum, inclusive, origin` key order that check-created size issues do not.\n *\n * Every item then runs, absent slots included, and the output is shaped by\n * {@link slotKinds}. The generated code keeps zod's invariant that the output\n * grows one slot at a time from the input's length: a slot that is about to be\n * handled as absent is exactly the output's current length, and once a `skip`\n * or a failed `tail` ends the output, every later absent slot sees a shorter\n * array and falls through. Ending the output at slot `i` needs no truncation\n * beyond what the failed item may have written, because nothing past `i` was\n * ever assigned.\n */\nexport function slowTuple(ir: SchemaIR & { type: \"tuple\" }, g: SlowGen): string {\n const len = ir.items.length;\n const kinds = slotKinds(ir);\n const optoutStart = ir.optoutStart ?? len;\n\n // Every read and write goes through ONE binding, seeded from the input and\n // re-pointed by each copy, with the result written to `g.output` at the end.\n // Reading `g.input` and writing `g.output` directly only works while the two\n // are the same identifier — true at every `createSlowGen` root, but not where\n // a parent hands over a separate local for each (an object's pass-through\n // property, a container member that rewrites nothing: see visitMember). There\n // a copy lands in `g.output` while the writes go on hitting the original, so\n // the output keeps the pristine array and the caller's array collects the\n // changes instead.\n const x = g.temp(\"ta\");\n\n // With a rest element the fixed items' issues are BUFFERED and flushed after\n // the rest loop, because that is the order zod reports them in: `$ZodTuple`\n // collects the fixed items into `itemResults` WITHOUT touching the payload,\n // runs the rest loop (which pushes through `handleTupleResult`), and only\n // then calls `handleTupleResults`, which pushes what it buffered. So\n // `z.tuple([z.string()]).rest(z.number())` on `[1,\"b\"]` reports the rest\n // element's issue first. `ZodError.message` is `JSON.stringify(issues)`, so\n // the order is user-visible, not just an array detail.\n const itemIssues = ir.rest === null ? g.issues : g.temp(\"tqi\");\n\n let code = emit`\n if(!Array.isArray(${g.input})){\n ${invalidType(g, \"tuple\")}\n }else{\n var ${x}=${g.input};\n ${ir.rest === null ? \"\" : `var ${itemIssues}=[];`}`;\n\n let itemsCode = \"\";\n const mutates = ir.items.some(hasMutation) || (ir.rest !== null && hasMutation(ir.rest));\n if (mutates) {\n itemsCode += `${x}=${x}.slice();`;\n }\n\n // The input's length, read once: absent slots are written back and would\n // otherwise shift the presence test for the slots after them.\n const lenVar = kinds.some((k) => k !== \"present\") ? g.temp(\"tl\") : \"\";\n if (lenVar) itemsCode += `var ${lenVar}=${x}.length;`;\n // A short input gains slots, so it is copied before the first write — the\n // caller's array must not grow. Guarded on the length so a well-formed input\n // never allocates; a mutating item has already paid for the copy.\n if (!mutates && tupleRewritesShortInput(ir)) {\n itemsCode += `if(${lenVar}<${len}){${x}=${x}.slice();}`;\n }\n\n for (let i = 0; i < len; i++) {\n const itemIR = ir.items[i] as SchemaIR;\n const elemExpr = `${x}[${i}]`;\n // Read into a local and handed back through visitMember, which lands a\n // replacement on a copy while `x` is still the caller's array.\n const itemCode = visitMember(g, itemIR, {\n container: x,\n original: g.input,\n copy: `${x}.slice()`,\n key: String(i),\n path: extendStaticPathIndex(g.path, i),\n issues: itemIssues,\n });\n // One copy of the item code per slot: an absent slot is materialized as an\n // own `undefined` first (`x[i] = undefined`, which is also what extends the\n // copied array), then validated exactly as a present one would be.\n const absent = `${i}>=${lenVar}`;\n switch (kinds[i]) {\n case \"present\":\n itemsCode += itemCode;\n break;\n case \"skip\":\n itemsCode += emit`if(${i}<${lenVar}){${itemCode}}`;\n break;\n case \"tail\": {\n const beforeVar = g.temp(\"tb\");\n itemsCode += emit`\n if(${i}<${lenVar}||${x}.length===${i}){\n var ${beforeVar}=${itemIssues}.length;\n if(${absent}){${elemExpr}=undefined;}\n ${itemCode}\n if(${absent}&&${itemIssues}.length>${beforeVar}){${itemIssues}.length=${beforeVar};${x}.length=${i};}\n }`;\n break;\n }\n case \"pad\":\n itemsCode += emit`if(${absent}){${elemExpr}=undefined;}${itemCode}`;\n break;\n }\n }\n\n // zod's trailing trim: absent slots that produced `undefined` are dropped\n // from the end while the item is optional-out. Only a `tail` slot can leave\n // one behind (a `pad` slot sits below `optoutStart`, a `skip` slot writes\n // nothing), so the loop is emitted only when some tail item might.\n if (kinds.some((k, i) => k === \"tail\" && !outputAlwaysDefined(ir.items[i] as SchemaIR))) {\n const tVar = g.temp(\"tt\");\n const floor = optoutStart > 0 ? `${tVar}>=${optoutStart}&&` : \"\";\n itemsCode += `for(var ${tVar}=${x}.length-1;${floor}${tVar}>=${lenVar}&&${x}[${tVar}]===undefined;${tVar}--){${x}.length=${tVar};}`;\n }\n\n if (ir.rest !== null) {\n const idxVar = g.temp(\"ti\");\n const restCode = visitMember(g, ir.rest, {\n container: x,\n original: g.input,\n copy: `${x}.slice()`,\n key: idxVar,\n path: extendPath(g.path, idxVar),\n issues: g.issues,\n });\n itemsCode += emit`\n for(var ${idxVar}=${len};${idxVar}<${x}.length;${idxVar}++){\n ${restCode}\n }`;\n const flushVar = g.temp(\"tqj\");\n itemsCode += `for(var ${flushVar}=0;${flushVar}<${itemIssues}.length;${flushVar}++){${g.issues}.push(${itemIssues}[${flushVar}]);}`;\n itemsCode += `${g.output}=${x};`;\n code += itemsCode;\n } else {\n const body = emit`\n if(${x}.length>${len}){\n ${tooBig(g, len, \"array\", true, { layout: \"tuple\", aborts: true })}\n }\n ${itemsCode}\n ${g.output}=${x};`;\n code +=\n ir.optStart > 0\n ? emit`\n if(${x}.length<${ir.optStart}){\n ${tooSmall(g, ir.optStart, \"array\", true, { layout: \"tuple\", aborts: true })}\n }else{\n ${body}\n }`\n : body;\n }\n\n code += `}\\n`;\n return code;\n}\n\nexport function fastTuple(ir: TupleIR, g: FastGen): string | null {\n const x = g.input;\n const parts: string[] = [`Array.isArray(${x})`];\n const kinds = slotKinds(ir);\n\n if (ir.rest === null) {\n if (ir.optStart === ir.items.length) {\n parts.push(`${x}.length===${ir.items.length}`);\n } else {\n if (ir.optStart > 0) parts.push(`${x}.length>=${ir.optStart}`);\n parts.push(`${x}.length<=${ir.items.length}`);\n }\n }\n\n // Per-index checks, cheapest-first: positions are independent, so the emitted\n // order only decides which one a reject stops on (see estimateRuntimeCost).\n // The Array.isArray + length conjuncts above stay in front — the element\n // reads are only meaningful once they hold.\n const indexed = ir.items.flatMap((itemIR, index) => (itemIR ? [{ index, itemIR }] : []));\n for (const { index, itemIR } of orderByRuntimeCost(indexed, (e) => e.itemIR, g.ctx)) {\n const itemCheck = g.visit(itemIR, { input: `${x}[${index}]` });\n if (itemCheck === null) return null;\n const kind = kinds[index];\n // The fast check answers true only where the output IS the input (see\n // fastResultIsInput), so a slot that would be written back when absent —\n // `pad` and `tail` alike — must be present here; the slow walk builds the\n // longer output. A `skip` slot is the reverse: absent, it is accepted\n // whatever its item says, since zod ends the output there without\n // consulting it.\n if (kind === \"pad\" || kind === \"tail\") {\n parts.push(\n itemCheck === \"true\" ? `${x}.length>${index}` : `${x}.length>${index}&&${itemCheck}`,\n );\n } else if (itemCheck === \"true\") {\n continue;\n } else {\n parts.push(kind === \"skip\" ? `(${x}.length<=${index}||${itemCheck})` : itemCheck);\n }\n }\n\n // Rest element validation via preamble helper (avoids .slice().every()\n // allocation). Fresh scope: the helper is its own function, size-gated\n // independently. (Fixed items above stay inline in the caller's && chain.)\n if (ir.rest !== null) {\n const rv = g.temp(\"tr\");\n const restGen = g.scoped(rv);\n const restCheck = restGen.visit(ir.rest);\n if (restCheck === null) return null;\n if (restCheck !== \"true\") {\n const helperName = g.temp(\"te\");\n g.ctx.preamble.push(\n `function ${helperName}(a,s){${declareFastTemps(restGen.scope)}for(var ${rv},i=s;i<a.length;i++){${rv}=a[i];if(!(${restCheck})){return false;}}return true;}`,\n );\n parts.push(`${helperName}(${x},${ir.items.length})`);\n }\n }\n\n return parts.join(\"&&\");\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tuple.js","names":[],"sources":["../../../../src/core/codegen/schemas/tuple.ts"],"sourcesContent":["import type { SchemaIR, TupleIR } from \"../../types.js\";\nimport type { FastGen, SlowGen } from \"../context.js\";\nimport {\n declareFastTemps,\n extendPath,\n extendStaticPathIndex,\n hasMutation,\n outputAlwaysDefined,\n tupleRewritesShortInput,\n visitMember,\n} from \"../context.js\";\nimport { orderByRuntimeCost } from \"../fast-size.js\";\nimport { emit } from \"../emit.js\";\nimport { invalidType, tooBig, tooSmall } from \"../emit-issue.js\";\n\n/**\n * What an ABSENT slot (index at or past the input's length) does to the output,\n * per zod's `handleTupleResults`:\n *\n * - `present`: can never be absent — below `optStart` without a rest element,\n * where a shorter input is already `too_small`.\n * - `skip`: at or past `optoutStart` and on the \"optional\" rung of `optin`.\n * The output ends here; the item is not consulted and nothing after it runs.\n * - `tail`: at or past `optoutStart` on any other rung. The item runs on\n * `undefined`; a failure ends the output here with its issues dropped, a\n * success is written back and may be trimmed later if it is `undefined`.\n * - `pad`: below `optoutStart`, so a later slot has to keep its index. The\n * item runs on `undefined`, its issues count, and its result — an own\n * `undefined` at the least — is written back.\n */\ntype SlotKind = \"present\" | \"skip\" | \"tail\" | \"pad\";\n\nfunction slotKinds(ir: TupleIR): SlotKind[] {\n const len = ir.items.length;\n const optoutStart = ir.optoutStart ?? len;\n const optionalIn = new Set(ir.optionalIn ?? []);\n const absentFrom = ir.rest === null ? ir.optStart : 0;\n return ir.items.map((_, i) => {\n if (i < absentFrom) return \"present\";\n if (i < optoutStart) return \"pad\";\n return optionalIn.has(i) ? \"skip\" : \"tail\";\n });\n}\n\n/**\n * Mirrors $ZodTuple. Without rest, an input shorter than `optStart` is a single\n * `too_small` (`minimum: optStart`, `inclusive: true`) that skips item\n * validation, and a longer one a single `too_big` that does NOT — zod pushes it\n * and runs the items anyway, so their issues follow it. Both are created by\n * the tuple node (schema error applies) and both share the `code, minimum |\n * maximum, inclusive, origin` key order that check-created size issues do not.\n *\n * Every item then runs, absent slots included, and the output is shaped by\n * {@link slotKinds}. The generated code keeps zod's invariant that the output\n * grows one slot at a time from the input's length: a slot that is about to be\n * handled as absent is exactly the output's current length, and once a `skip`\n * or a failed `tail` ends the output, every later absent slot sees a shorter\n * array and falls through. Ending the output at slot `i` needs no truncation\n * beyond what the failed item may have written, because nothing past `i` was\n * ever assigned.\n */\nexport function slowTuple(ir: SchemaIR & { type: \"tuple\" }, g: SlowGen): string {\n const len = ir.items.length;\n const kinds = slotKinds(ir);\n const optoutStart = ir.optoutStart ?? len;\n\n // Every read and write goes through ONE binding, seeded from the input and\n // re-pointed by each copy, with the result written to `g.output` at the end.\n // Reading `g.input` and writing `g.output` directly only works while the two\n // are the same identifier — true at every `createSlowGen` root, but not where\n // a parent hands over a separate local for each (an object's pass-through\n // property, a container member that rewrites nothing: see visitMember). There\n // a copy lands in `g.output` while the writes go on hitting the original, so\n // the output keeps the pristine array and the caller's array collects the\n // changes instead.\n const x = g.temp(\"ta\");\n\n // With a rest element the fixed items' issues are BUFFERED and flushed after\n // the rest loop, because that is the order zod reports them in: `$ZodTuple`\n // collects the fixed items into `itemResults` WITHOUT touching the payload,\n // runs the rest loop (which pushes through `handleTupleResult`), and only\n // then calls `handleTupleResults`, which pushes what it buffered. So\n // `z.tuple([z.string()]).rest(z.number())` on `[1,\"b\"]` reports the rest\n // element's issue first. `ZodError.message` is `JSON.stringify(issues)`, so\n // the order is user-visible, not just an array detail.\n const itemIssues = ir.rest === null ? g.issues : g.temp(\"tqi\");\n\n let code = emit`\n if(!Array.isArray(${g.input})){\n ${invalidType(g, \"tuple\")}\n }else{\n var ${x}=${g.input};\n ${ir.rest === null ? \"\" : `var ${itemIssues}=[];`}`;\n\n let itemsCode = \"\";\n const mutates = ir.items.some(hasMutation) || (ir.rest !== null && hasMutation(ir.rest));\n if (mutates) {\n itemsCode += `${x}=${x}.slice();`;\n }\n\n // The input's length, read once: absent slots are written back and would\n // otherwise shift the presence test for the slots after them.\n const lenVar = kinds.some((k) => k !== \"present\") ? g.temp(\"tl\") : \"\";\n if (lenVar) itemsCode += `var ${lenVar}=${x}.length;`;\n // A short input gains slots, so it is copied before the first write — the\n // caller's array must not grow. Guarded on the length so a well-formed input\n // never allocates; a mutating item has already paid for the copy.\n if (!mutates && tupleRewritesShortInput(ir)) {\n itemsCode += `if(${lenVar}<${len}){${x}=${x}.slice();}`;\n }\n\n for (let i = 0; i < len; i++) {\n const itemIR = ir.items[i] as SchemaIR;\n const elemExpr = `${x}[${i}]`;\n // Read into a local and handed back through visitMember, which lands a\n // replacement on a copy while `x` is still the caller's array.\n const itemCode = visitMember(g, itemIR, {\n container: x,\n original: g.input,\n copy: `${x}.slice()`,\n key: String(i),\n path: extendStaticPathIndex(g.ctx, g.path, i),\n issues: itemIssues,\n });\n // One copy of the item code per slot: an absent slot is materialized as an\n // own `undefined` first (`x[i] = undefined`, which is also what extends the\n // copied array), then validated exactly as a present one would be.\n const absent = `${i}>=${lenVar}`;\n switch (kinds[i]) {\n case \"present\":\n itemsCode += itemCode;\n break;\n case \"skip\":\n itemsCode += emit`if(${i}<${lenVar}){${itemCode}}`;\n break;\n case \"tail\": {\n const beforeVar = g.temp(\"tb\");\n itemsCode += emit`\n if(${i}<${lenVar}||${x}.length===${i}){\n var ${beforeVar}=${itemIssues}.length;\n if(${absent}){${elemExpr}=undefined;}\n ${itemCode}\n if(${absent}&&${itemIssues}.length>${beforeVar}){${itemIssues}.length=${beforeVar};${x}.length=${i};}\n }`;\n break;\n }\n case \"pad\":\n itemsCode += emit`if(${absent}){${elemExpr}=undefined;}${itemCode}`;\n break;\n }\n }\n\n // zod's trailing trim: absent slots that produced `undefined` are dropped\n // from the end while the item is optional-out. Only a `tail` slot can leave\n // one behind (a `pad` slot sits below `optoutStart`, a `skip` slot writes\n // nothing), so the loop is emitted only when some tail item might.\n if (kinds.some((k, i) => k === \"tail\" && !outputAlwaysDefined(ir.items[i] as SchemaIR))) {\n const tVar = g.temp(\"tt\");\n const floor = optoutStart > 0 ? `${tVar}>=${optoutStart}&&` : \"\";\n itemsCode += `for(var ${tVar}=${x}.length-1;${floor}${tVar}>=${lenVar}&&${x}[${tVar}]===undefined;${tVar}--){${x}.length=${tVar};}`;\n }\n\n if (ir.rest !== null) {\n const idxVar = g.temp(\"ti\");\n const restCode = visitMember(g, ir.rest, {\n container: x,\n original: g.input,\n copy: `${x}.slice()`,\n key: idxVar,\n path: extendPath(g.ctx, g.path, idxVar),\n issues: g.issues,\n });\n itemsCode += emit`\n for(var ${idxVar}=${len};${idxVar}<${x}.length;${idxVar}++){\n ${restCode}\n }`;\n const flushVar = g.temp(\"tqj\");\n itemsCode += `for(var ${flushVar}=0;${flushVar}<${itemIssues}.length;${flushVar}++){${g.issues}.push(${itemIssues}[${flushVar}]);}`;\n itemsCode += `${g.output}=${x};`;\n code += itemsCode;\n } else {\n const body = emit`\n if(${x}.length>${len}){\n ${tooBig(g, len, \"array\", true, { layout: \"tuple\", aborts: true })}\n }\n ${itemsCode}\n ${g.output}=${x};`;\n code +=\n ir.optStart > 0\n ? emit`\n if(${x}.length<${ir.optStart}){\n ${tooSmall(g, ir.optStart, \"array\", true, { layout: \"tuple\", aborts: true })}\n }else{\n ${body}\n }`\n : body;\n }\n\n code += `}\\n`;\n return code;\n}\n\nexport function fastTuple(ir: TupleIR, g: FastGen): string | null {\n const x = g.input;\n const parts: string[] = [`Array.isArray(${x})`];\n const kinds = slotKinds(ir);\n\n if (ir.rest === null) {\n if (ir.optStart === ir.items.length) {\n parts.push(`${x}.length===${ir.items.length}`);\n } else {\n if (ir.optStart > 0) parts.push(`${x}.length>=${ir.optStart}`);\n parts.push(`${x}.length<=${ir.items.length}`);\n }\n }\n\n // Per-index checks, cheapest-first: positions are independent, so the emitted\n // order only decides which one a reject stops on (see estimateRuntimeCost).\n // The Array.isArray + length conjuncts above stay in front — the element\n // reads are only meaningful once they hold.\n const indexed = ir.items.flatMap((itemIR, index) => (itemIR ? [{ index, itemIR }] : []));\n for (const { index, itemIR } of orderByRuntimeCost(indexed, (e) => e.itemIR, g.ctx)) {\n const itemCheck = g.visit(itemIR, { input: `${x}[${index}]` });\n if (itemCheck === null) return null;\n const kind = kinds[index];\n // The fast check answers true only where the output IS the input (see\n // fastResultIsInput), so a slot that would be written back when absent —\n // `pad` and `tail` alike — must be present here; the slow walk builds the\n // longer output. A `skip` slot is the reverse: absent, it is accepted\n // whatever its item says, since zod ends the output there without\n // consulting it.\n if (kind === \"pad\" || kind === \"tail\") {\n parts.push(\n itemCheck === \"true\" ? `${x}.length>${index}` : `${x}.length>${index}&&${itemCheck}`,\n );\n } else if (itemCheck === \"true\") {\n continue;\n } else {\n parts.push(kind === \"skip\" ? `(${x}.length<=${index}||${itemCheck})` : itemCheck);\n }\n }\n\n // Rest element validation via preamble helper (avoids .slice().every()\n // allocation). Fresh scope: the helper is its own function, size-gated\n // independently. (Fixed items above stay inline in the caller's && chain.)\n if (ir.rest !== null) {\n const rv = g.temp(\"tr\");\n const restGen = g.scoped(rv);\n const restCheck = restGen.visit(ir.rest);\n if (restCheck === null) return null;\n if (restCheck !== \"true\") {\n const helperName = g.temp(\"te\");\n g.ctx.preamble.push(\n `function ${helperName}(a,s){${declareFastTemps(restGen.scope)}for(var ${rv},i=s;i<a.length;i++){${rv}=a[i];if(!(${restCheck})){return false;}}return true;}`,\n );\n parts.push(`${helperName}(${x},${ir.items.length})`);\n }\n }\n\n return parts.join(\"&&\");\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { ZC_FZ_DECL, abortingCodeTest } from "../issue-decls.js";
|
|
1
2
|
import { emitRuntimeHelper, hasMutation } from "../context.js";
|
|
2
3
|
import { emit } from "../emit.js";
|
|
3
|
-
import { ZC_FZ_DECL, abortingCodeTest } from "../issue-decls.js";
|
|
4
4
|
import { detectUnionDiscriminator, emitFastDiscriminatedSwitch } from "./discriminated-union.js";
|
|
5
5
|
import { orderByRuntimeCost } from "../fast-size.js";
|
|
6
6
|
//#region src/core/codegen/schemas/union.ts
|
package/dist/core/iife.d.ts
CHANGED
|
@@ -53,18 +53,17 @@ declare const ZOD_MSG_DECLARATION: string;
|
|
|
53
53
|
* trace) — stays deferred inside the cached accessor exactly as before, since
|
|
54
54
|
* the issues array is observable solely through `.error`.
|
|
55
55
|
*
|
|
56
|
-
* `input`
|
|
57
|
-
* `"input" in issue`, `Object.keys(issue)`, object spread,
|
|
58
|
-
* against a zod issue — and zod's `util.finalizeIssue`
|
|
59
|
-
* whenever `reportInput` is off, so assignment left every
|
|
60
|
-
* enumerable key wider than zod's.
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
* JSON.stringify of every issue).
|
|
56
|
+
* `input` and `continue` are left OUT, not assigned `undefined`. Key PRESENCE is
|
|
57
|
+
* observable — `"input" in issue`, `Object.keys(issue)`, object spread,
|
|
58
|
+
* `toStrictEqual` against a zod issue — and zod's `util.finalizeIssue` deletes
|
|
59
|
+
* `continue`, and `input` whenever `reportInput` is off, so assignment left every
|
|
60
|
+
* compiled issue one enumerable key wider than zod's. Each issue is COPIED
|
|
61
|
+
* without them rather than `delete`d from: removing a key that is not the last
|
|
62
|
+
* one added drops the object into V8's dictionary mode, ~70 ns an issue. `.error`
|
|
63
|
+
* over six issues went 1.49 -> 1.06 us, over one 653 -> 608 ns. The copy keeps
|
|
64
|
+
* key order and takes own keys only, so an enumerable key added to
|
|
65
|
+
* `Object.prototype` stays out, as it does from zod's spread. All of it runs only
|
|
66
|
+
* after a caller asks for `.error` on a failed parse, and is memoised in `_c`.
|
|
68
67
|
*/
|
|
69
68
|
declare const FAIL_CLASS_DECL: string;
|
|
70
69
|
/** Eager finalizer (mutation / partial-fast-path schemas): issues already
|
package/dist/core/iife.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"iife.d.ts","names":[],"sources":["../../src/core/iife.ts"],"mappings":";;;;;;;;cAca;;;;;;;;;;;;;;;;;;;;cAsBA
|
|
1
|
+
{"version":3,"file":"iife.d.ts","names":[],"sources":["../../src/core/iife.ts"],"mappings":";;;;;;;;cAca;;;;;;;;;;;;;;;;;;;;cAsBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA6CA;;;cAWA;;;;;;;;;;;;;;;;;;;;;;;cAyBA;;;;;;;;;;;;;;;;;;;;;;;;;;cA2BA;;cAMA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAqGA;;;;;;;;;;;;;;;;;iBA6BG,iBAAiB,QAAQ;;;;;;;;;;;;;iBAgBzB,aACd,oBACA,QAAQ,oBACR;EAAY;EAAiC"}
|
package/dist/core/iife.js
CHANGED
|
@@ -57,20 +57,19 @@ const ZOD_MSG_DECLARATION = "function __zcUw(m){return typeof m===\"string\"?m:(
|
|
|
57
57
|
* trace) — stays deferred inside the cached accessor exactly as before, since
|
|
58
58
|
* the issues array is observable solely through `.error`.
|
|
59
59
|
*
|
|
60
|
-
* `input`
|
|
61
|
-
* `"input" in issue`, `Object.keys(issue)`, object spread,
|
|
62
|
-
* against a zod issue — and zod's `util.finalizeIssue`
|
|
63
|
-
* whenever `reportInput` is off, so assignment left every
|
|
64
|
-
* enumerable key wider than zod's.
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
* JSON.stringify of every issue).
|
|
60
|
+
* `input` and `continue` are left OUT, not assigned `undefined`. Key PRESENCE is
|
|
61
|
+
* observable — `"input" in issue`, `Object.keys(issue)`, object spread,
|
|
62
|
+
* `toStrictEqual` against a zod issue — and zod's `util.finalizeIssue` deletes
|
|
63
|
+
* `continue`, and `input` whenever `reportInput` is off, so assignment left every
|
|
64
|
+
* compiled issue one enumerable key wider than zod's. Each issue is COPIED
|
|
65
|
+
* without them rather than `delete`d from: removing a key that is not the last
|
|
66
|
+
* one added drops the object into V8's dictionary mode, ~70 ns an issue. `.error`
|
|
67
|
+
* over six issues went 1.49 -> 1.06 us, over one 653 -> 608 ns. The copy keeps
|
|
68
|
+
* key order and takes own keys only, so an enumerable key added to
|
|
69
|
+
* `Object.prototype` stays out, as it does from zod's spread. All of it runs only
|
|
70
|
+
* after a caller asks for `.error` on a failed parse, and is memoised in `_c`.
|
|
72
71
|
*/
|
|
73
|
-
const FAIL_CLASS_DECL = "function __ZcFail(e,f,i){this.success=false;this._e=e;this._f=f;this._i=i;this._c=undefined;}Object.defineProperty(__ZcFail.prototype,\"error\",{configurable:true,get:function(){if(this._c)return this._c;var e=this._f!==null?this._f(this._i):this._e;for(var i=0;i<e.length;i++){
|
|
72
|
+
const FAIL_CLASS_DECL = "function __ZcFail(e,f,i){this.success=false;this._e=e;this._f=f;this._i=i;this._c=undefined;}Object.defineProperty(__ZcFail.prototype,\"error\",{configurable:true,get:function(){if(this._c)return this._c;var e=this._f!==null?this._f(this._i):this._e;for(var i=0;i<e.length;i++){var s=e[i],o={},k;if(s.message===undefined&&typeof __zcMsg===\"function\")s.message=__zcMsg(s);for(k in s){if(k!==\"input\"&&k!==\"continue\"&&Object.prototype.hasOwnProperty.call(s,k))o[k]=s[k];}e[i]=o;}return this._c=new __zcZodError(e);}});";
|
|
74
73
|
/** Eager finalizer (mutation / partial-fast-path schemas): issues already
|
|
75
74
|
* collected in `e`; success short-circuits to a plain result literal. */
|
|
76
75
|
const FIN_DECL = "function __zcFin(e,d){if(!e.length)return{success:true,data:d};return new __ZcFail(e,null,null);}";
|