zod-compiler 1.28.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +59 -53
- package/dist/core/codegen/build-path.d.ts +12 -1
- package/dist/core/codegen/build-path.d.ts.map +1 -1
- package/dist/core/codegen/build-path.js +47 -9
- package/dist/core/codegen/build-path.js.map +1 -1
- package/dist/core/codegen/context.d.ts +31 -16
- package/dist/core/codegen/context.d.ts.map +1 -1
- package/dist/core/codegen/context.js +43 -18
- package/dist/core/codegen/context.js.map +1 -1
- package/dist/core/codegen/emit-issue.d.ts +17 -20
- package/dist/core/codegen/emit-issue.d.ts.map +1 -1
- package/dist/core/codegen/emit-issue.js +13 -13
- package/dist/core/codegen/emit-issue.js.map +1 -1
- package/dist/core/codegen/index.d.ts.map +1 -1
- package/dist/core/codegen/index.js +9 -7
- package/dist/core/codegen/index.js.map +1 -1
- package/dist/core/codegen/issue-decls.d.ts +41 -7
- package/dist/core/codegen/issue-decls.d.ts.map +1 -1
- package/dist/core/codegen/issue-decls.js +49 -9
- package/dist/core/codegen/issue-decls.js.map +1 -1
- package/dist/core/codegen/schemas/array.js +1 -1
- package/dist/core/codegen/schemas/array.js.map +1 -1
- package/dist/core/codegen/schemas/bigint.d.ts.map +1 -1
- package/dist/core/codegen/schemas/bigint.js +15 -4
- package/dist/core/codegen/schemas/bigint.js.map +1 -1
- package/dist/core/codegen/schemas/catch.d.ts.map +1 -1
- package/dist/core/codegen/schemas/catch.js +3 -2
- package/dist/core/codegen/schemas/catch.js.map +1 -1
- package/dist/core/codegen/schemas/discriminated-union.d.ts.map +1 -1
- package/dist/core/codegen/schemas/discriminated-union.js +2 -1
- package/dist/core/codegen/schemas/discriminated-union.js.map +1 -1
- package/dist/core/codegen/schemas/effect.d.ts.map +1 -1
- package/dist/core/codegen/schemas/effect.js +4 -2
- package/dist/core/codegen/schemas/effect.js.map +1 -1
- package/dist/core/codegen/schemas/file.js +2 -8
- package/dist/core/codegen/schemas/file.js.map +1 -1
- package/dist/core/codegen/schemas/number.js +4 -3
- package/dist/core/codegen/schemas/number.js.map +1 -1
- package/dist/core/codegen/schemas/object.d.ts +11 -1
- package/dist/core/codegen/schemas/object.d.ts.map +1 -1
- package/dist/core/codegen/schemas/object.js +58 -14
- package/dist/core/codegen/schemas/object.js.map +1 -1
- package/dist/core/codegen/schemas/readonly.d.ts.map +1 -1
- package/dist/core/codegen/schemas/readonly.js.map +1 -1
- package/dist/core/codegen/schemas/record.d.ts.map +1 -1
- package/dist/core/codegen/schemas/record.js +8 -3
- package/dist/core/codegen/schemas/record.js.map +1 -1
- package/dist/core/codegen/schemas/sizeable.d.ts +49 -3
- package/dist/core/codegen/schemas/sizeable.d.ts.map +1 -1
- package/dist/core/codegen/schemas/sizeable.js +101 -8
- package/dist/core/codegen/schemas/sizeable.js.map +1 -1
- package/dist/core/codegen/schemas/string.d.ts.map +1 -1
- package/dist/core/codegen/schemas/string.js +45 -16
- package/dist/core/codegen/schemas/string.js.map +1 -1
- package/dist/core/codegen/schemas/tuple.d.ts +14 -11
- package/dist/core/codegen/schemas/tuple.d.ts.map +1 -1
- package/dist/core/codegen/schemas/tuple.js +99 -49
- package/dist/core/codegen/schemas/tuple.js.map +1 -1
- package/dist/core/codegen/well-known-regex.d.ts +7 -0
- package/dist/core/codegen/well-known-regex.d.ts.map +1 -1
- package/dist/core/codegen/well-known-regex.js +10 -3
- package/dist/core/codegen/well-known-regex.js.map +1 -1
- package/dist/core/extract/checks.d.ts.map +1 -1
- package/dist/core/extract/checks.js +1 -0
- package/dist/core/extract/checks.js.map +1 -1
- package/dist/core/extract/extractors/intersection.js +21 -6
- package/dist/core/extract/extractors/intersection.js.map +1 -1
- package/dist/core/extract/extractors/object.d.ts.map +1 -1
- package/dist/core/extract/extractors/object.js +25 -9
- package/dist/core/extract/extractors/object.js.map +1 -1
- package/dist/core/extract/extractors/optional.d.ts.map +1 -1
- package/dist/core/extract/extractors/optional.js +1 -28
- package/dist/core/extract/extractors/optional.js.map +1 -1
- package/dist/core/extract/extractors/record.js +9 -3
- package/dist/core/extract/extractors/record.js.map +1 -1
- package/dist/core/extract/extractors/tuple.d.ts.map +1 -1
- package/dist/core/extract/extractors/tuple.js +16 -20
- package/dist/core/extract/extractors/tuple.js.map +1 -1
- package/dist/core/extract/types.d.ts +16 -8
- package/dist/core/extract/types.d.ts.map +1 -1
- package/dist/core/iife.d.ts +2 -1
- package/dist/core/iife.d.ts.map +1 -1
- package/dist/core/iife.js +2 -1
- package/dist/core/iife.js.map +1 -1
- package/dist/core/types.d.ts +70 -21
- package/dist/core/types.d.ts.map +1 -1
- package/dist/jit.d.ts.map +1 -1
- package/dist/jit.js +47 -12
- package/dist/jit.js.map +1 -1
- package/dist/runtime.d.ts +3 -1
- package/dist/runtime.js +12 -10
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"well-known-regex.js","names":[],"sources":["../../../src/core/codegen/well-known-regex.ts"],"sourcesContent":["/**\n * Well-known regex sources hosted in the virtual module \"virtual:zod-compiler/runtime\".\n *\n * In lean mode (unplugin), `g.regex()` consults this registry and emits a reference\n * like `__zcReEmail` instead of declaring `var __re_email_*=new RegExp(...)`.\n * The bundler then deduplicates the regex literal across all transformed files.\n *\n * Pattern sources are matched verbatim (string equality). Add new entries as Zod\n * exposes additional well-known formats and we want bundle-wide dedup.\n */\n\nimport { unrollRepeats } from \"./regex-unroll.js\";\n\n/** Zod v4's email regex source (string.ts uses this directly when format === \"email\"). */\nexport const EMAIL_REGEX_SOURCE = String.raw`^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$`;\n\n/**\n * Behavior-equivalent rewrite of EMAIL_REGEX_SOURCE that runs ~1.45x faster on V8.\n *\n * Zod's pattern fronts two lookaheads; `(?!.*\\.\\.)` re-scans the entire string\n * before matching starts. The rewrite encodes the same constraints structurally:\n * the local part is dot-separated runs of `[A-Za-z0-9_'+-]` (no leading dot, no\n * empty run ⇒ no `..`) ending in `[A-Za-z0-9_+-]`, and the domain grammar already\n * makes `..` impossible (every label starts with an alphanumeric).\n *\n * Equivalence is enforced by tests/core/codegen/email-fast-regex.test.ts\n * (exhaustive short-string sweep + structured cases + random fuzz).\n */\nexport const EMAIL_FAST_REGEX_SOURCE = String.raw`^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$`;\n\n/** Fallback UUID regex used when the extractor doesn't provide a pattern (e.g. in unit tests). */\nexport const UUID_REGEX_SOURCE =\n \"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$\";\n\nexport interface WellKnownRegex {\n /** Stable virtual-module export name. Always starts with \"__zcRe\". */\n name: string;\n /** Pattern source string (verbatim match against `g.regex()` 2nd argument). */\n source: string;\n /**\n * Behavior-equivalent faster pattern used for the actual `.test()` regex.\n * Issue reporting (`pattern:` field) always uses `source` so generated\n * issues stay byte-identical to zod's.\n */\n testSource?: string;\n}\n\nexport const WELL_KNOWN_REGEXES: readonly WellKnownRegex[] = [\n { name: \"__zcReEmail\", source: EMAIL_REGEX_SOURCE, testSource: EMAIL_FAST_REGEX_SOURCE },\n { name: \"__zcReUuid\", source: UUID_REGEX_SOURCE },\n { name: \"__zcReCuid\", source: \"^[cC][^\\\\s-]{8,}$\" },\n { name: \"__zcReCuid2\", source: \"^[0-9a-z]+$\" },\n { name: \"__zcReUlid\", source: \"^[0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{26}$\" },\n { name: \"__zcReNanoid\", source: \"^[a-zA-Z0-9_-]{21}$\" },\n { name: \"__zcReXid\", source: \"^[0-9a-vA-V]{20}$\" },\n { name: \"__zcReKsuid\", source: \"^[A-Za-z0-9]{27}$\" },\n {\n name: \"__zcReIpv4\",\n source:\n \"^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\\\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$\",\n },\n {\n name: \"__zcReIpv6\",\n source:\n \"^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$\",\n },\n {\n name: \"__zcReBase64\",\n source: \"^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$\",\n },\n { name: \"__zcReBase64Url\", source: \"^[A-Za-z0-9_-]*$\" },\n { name: \"__zcReE164\", source: \"^\\\\+[1-9]\\\\d{6,14}$\" },\n {\n name: \"__zcReGuid\",\n source: \"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$\",\n },\n // The ISO family. Zod BUILDS these from the call's options, so only the\n // default spelling is a fixed string — `z.iso.datetime({ offset: true })` or\n // `{ precision: 3 }` produces a different source that simply misses this\n // exact-match table and keeps its per-IIFE declaration. Defaults are worth\n // listing anyway: `z.iso.datetime()` is everywhere in API schemas and its\n // pattern is ~330 characters, so a bundle that repeats it per validator pays\n // for it in bytes and in a RegExp construction per schema at module init.\n {\n name: \"__zcReIsoDate\",\n source:\n \"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\",\n },\n { name: \"__zcReIsoTime\", source: \"^(?:[01]\\\\d|2[0-3]):[0-5]\\\\d(?::[0-5]\\\\d(?:\\\\.\\\\d+)?)?$\" },\n {\n name: \"__zcReIsoDateTime\",\n source:\n \"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))T(?:(?:[01]\\\\d|2[0-3]):[0-5]\\\\d(?::[0-5]\\\\d(?:\\\\.\\\\d+)?)?(?:Z))$\",\n },\n {\n name: \"__zcReIsoDuration\",\n source:\n \"^P(?:(\\\\d+W)|(?!.*W)(?=\\\\d|T\\\\d)(\\\\d+Y)?(\\\\d+M)?(\\\\d+D)?(T(?=\\\\d)(\\\\d+H)?(\\\\d+M)?(\\\\d+([.,]\\\\d+)?S)?)?)$\",\n },\n];\n\nconst SOURCE_TO_NAME: ReadonlyMap<string, string> = new Map(\n WELL_KNOWN_REGEXES.map((r) => [r.source, r.name]),\n);\n\nconst SOURCE_TO_TEST_SOURCE: ReadonlyMap<string, string> = new Map(\n WELL_KNOWN_REGEXES.filter((r) => r.testSource !== undefined).map((r) => [\n r.source,\n r.testSource as string,\n ]),\n);\n\n/**\n * Look up a well-known regex by its pattern source string.\n * Returns the virtual-module export name or null if the pattern is user-defined.\n */\nexport function lookupWellKnownRegex(source: string): string | null {\n return SOURCE_TO_NAME.get(source) ?? null;\n}\n\n/**\n * Look up the hand-written behavior-equivalent test pattern for a regex source.\n * Returns null when no rewrite is registered in {@link WELL_KNOWN_REGEXES}.\n *\n * This is the TABLE lookup only. Codegen calls {@link fastTestSource}, which\n * layers automatic repeat unrolling on top and also covers user-supplied\n * patterns that are in no table.\n */\nexport function lookupFastRegexSource(source: string): string | null {\n return SOURCE_TO_TEST_SOURCE.get(source) ?? null;\n}\n\n/**\n * The pattern a generated `.test()` should actually run for `source`, or null\n * when `source` is already the best form.\n *\n * Two behavior-preserving rewrites compose here: the hand-written table entry\n * above (currently just email), then {@link unrollRepeats}, which turns bounded\n * repeats of single-character atoms into explicit repetition — worth 1.3-3.4x\n * on the string formats everyday schemas use (uuid, guid, ulid, nanoid, xid,\n * ksuid, base64, e164, iso.date). Unrolling runs on the table rewrite when\n * there is one, so the two stack.\n *\n * Callers must apply this only to flag-less regexes (the flagged form would\n * need its flags carried into the reported pattern too) and must keep\n * reporting the ORIGINAL source in issues — see `emitRegexSourceString`.\n */\nexport function fastTestSource(source: string): string | null {\n const tableRewrite = SOURCE_TO_TEST_SOURCE.get(source) ?? null;\n return unrollRepeats(tableRewrite ?? source) ?? tableRewrite;\n}\n\n/**\n * Virtual-module export name for the ORIGINAL `/source/` pattern string of a\n * rewritten well-known regex (e.g. \"__zcReEmailSrc\"). Issue sites reference it\n * so the original pattern stays a single bundle-wide string even though the\n * runtime regex object is built from testSource.\n */\nexport function wellKnownRegexSourceName(source: string): string | null {\n const name = SOURCE_TO_NAME.get(source);\n if (name === undefined) return null;\n return fastTestSource(source) !== null ? `${name}Src` : null;\n}\n"],"mappings":";;;;;;;;;;;;;AAcA,MAAa,qBAAqB,OAAO,GAAG;;;;;;;;;;;;;AAc5C,MAAa,0BAA0B,OAAO,GAAG;;AAGjD,MAAa,oBACX;AAeF,MAAa,qBAAgD;CAC3D;EAAE,MAAM;EAAe,QAAQ;EAAoB,YAAY;CAAwB;CACvF;EAAE,MAAM;EAAc,QAAQ;CAAkB;CAChD;EAAE,MAAM;EAAc,QAAQ;CAAoB;CAClD;EAAE,MAAM;EAAe,QAAQ;CAAc;CAC7C;EAAE,MAAM;EAAc,QAAQ;CAAwC;CACtE;EAAE,MAAM;EAAgB,QAAQ;CAAsB;CACtD;EAAE,MAAM;EAAa,QAAQ;CAAoB;CACjD;EAAE,MAAM;EAAe,QAAQ;CAAoB;CACnD;EACE,MAAM;EACN,QACE;CACJ;CACA;EACE,MAAM;EACN,QACE;CACJ;CACA;EACE,MAAM;EACN,QAAQ;CACV;CACA;EAAE,MAAM;EAAmB,QAAQ;CAAmB;CACtD;EAAE,MAAM;EAAc,QAAQ;CAAsB;CACpD;EACE,MAAM;EACN,QAAQ;CACV;CAQA;EACE,MAAM;EACN,QACE;CACJ;CACA;EAAE,MAAM;EAAiB,QAAQ;CAA0D;CAC3F;EACE,MAAM;EACN,QACE;CACJ;CACA;EACE,MAAM;EACN,QACE;CACJ;AACF;AAEA,MAAM,iBAA8C,IAAI,IACtD,mBAAmB,KAAK,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,CAClD;AAEA,MAAM,wBAAqD,IAAI,IAC7D,mBAAmB,QAAQ,MAAM,EAAE,eAAe,KAAA,CAAS,CAAC,CAAC,KAAK,MAAM,CACtE,EAAE,QACF,EAAE,UACJ,CAAC,CACH;;;;;AAMA,SAAgB,qBAAqB,QAA+B;CAClE,OAAO,eAAe,IAAI,MAAM,KAAK;AACvC;;;;;;;;;AAUA,SAAgB,sBAAsB,QAA+B;CACnE,OAAO,sBAAsB,IAAI,MAAM,KAAK;AAC9C;;;;;;;;;;;;;;;;AAiBA,SAAgB,eAAe,QAA+B;CAC5D,MAAM,eAAe,sBAAsB,IAAI,MAAM,KAAK;CAC1D,OAAO,cAAc,gBAAgB,MAAM,KAAK;AAClD;;;;;;;AAQA,SAAgB,yBAAyB,QAA+B;CACtE,MAAM,OAAO,eAAe,IAAI,MAAM;CACtC,IAAI,SAAS,KAAA,GAAW,OAAO;CAC/B,OAAO,eAAe,MAAM,MAAM,OAAO,GAAG,KAAK,OAAO;AAC1D"}
|
|
1
|
+
{"version":3,"file":"well-known-regex.js","names":[],"sources":["../../../src/core/codegen/well-known-regex.ts"],"sourcesContent":["/**\n * Well-known regex sources hosted in the virtual module \"virtual:zod-compiler/runtime\".\n *\n * In lean mode (unplugin), `g.regex()` consults this registry and emits a reference\n * like `__zcReEmail` instead of declaring `var __re_email_*=new RegExp(...)`.\n * The bundler then deduplicates the regex literal across all transformed files.\n *\n * Pattern sources are matched verbatim (string equality). Add new entries as Zod\n * exposes additional well-known formats and we want bundle-wide dedup.\n *\n * Verbatim matching means an entry goes STALE SILENTLY when Zod edits a pattern:\n * nothing breaks, the lookup just stops hitting and every transformed file\n * re-declares its own RegExp. The 4.5 bump did exactly that to `cuid`, `ulid`\n * and `iso.datetime` — the last being the ~330-character source this table\n * exists for. tests/core/codegen/well-known-regex.test.ts pins every entry\n * against the live Zod pattern so the next upgrade fails loudly instead.\n */\n\nimport { unrollRepeats } from \"./regex-unroll.js\";\n\n/** Zod v4's email regex source (string.ts uses this directly when format === \"email\"). */\nexport const EMAIL_REGEX_SOURCE = String.raw`^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$`;\n\n/**\n * Behavior-equivalent rewrite of EMAIL_REGEX_SOURCE that runs ~1.45x faster on V8.\n *\n * Zod's pattern fronts two lookaheads; `(?!.*\\.\\.)` re-scans the entire string\n * before matching starts. The rewrite encodes the same constraints structurally:\n * the local part is dot-separated runs of `[A-Za-z0-9_'+-]` (no leading dot, no\n * empty run ⇒ no `..`) ending in `[A-Za-z0-9_+-]`, and the domain grammar already\n * makes `..` impossible (every label starts with an alphanumeric).\n *\n * Equivalence is enforced by tests/core/codegen/email-fast-regex.test.ts\n * (exhaustive short-string sweep + structured cases + random fuzz).\n */\nexport const EMAIL_FAST_REGEX_SOURCE = String.raw`^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$`;\n\n/** Fallback UUID regex used when the extractor doesn't provide a pattern (e.g. in unit tests). */\nexport const UUID_REGEX_SOURCE =\n \"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$\";\n\nexport interface WellKnownRegex {\n /** Stable virtual-module export name. Always starts with \"__zcRe\". */\n name: string;\n /** Pattern source string (verbatim match against `g.regex()` 2nd argument). */\n source: string;\n /**\n * Behavior-equivalent faster pattern used for the actual `.test()` regex.\n * Issue reporting (`pattern:` field) always uses `source` so generated\n * issues stay byte-identical to zod's.\n */\n testSource?: string;\n}\n\nexport const WELL_KNOWN_REGEXES: readonly WellKnownRegex[] = [\n { name: \"__zcReEmail\", source: EMAIL_REGEX_SOURCE, testSource: EMAIL_FAST_REGEX_SOURCE },\n { name: \"__zcReUuid\", source: UUID_REGEX_SOURCE },\n { name: \"__zcReCuid\", source: \"^[cC][0-9a-z]{6,}$\" },\n { name: \"__zcReCuid2\", source: \"^[0-9a-z]+$\" },\n { name: \"__zcReUlid\", source: \"^[0-7][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{25}$\" },\n { name: \"__zcReNanoid\", source: \"^[a-zA-Z0-9_-]{21}$\" },\n { name: \"__zcReXid\", source: \"^[0-9a-vA-V]{20}$\" },\n { name: \"__zcReKsuid\", source: \"^[A-Za-z0-9]{27}$\" },\n {\n name: \"__zcReIpv4\",\n source:\n \"^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\\\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$\",\n },\n {\n name: \"__zcReIpv6\",\n source:\n \"^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$\",\n },\n {\n name: \"__zcReBase64\",\n source: \"^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$\",\n },\n { name: \"__zcReBase64Url\", source: \"^[A-Za-z0-9_-]*$\" },\n { name: \"__zcReE164\", source: \"^\\\\+[1-9]\\\\d{6,14}$\" },\n {\n name: \"__zcReGuid\",\n source: \"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$\",\n },\n // The ISO family. Zod BUILDS these from the call's options, so only the\n // default spelling is a fixed string — `z.iso.datetime({ offset: true })` or\n // `{ precision: 3 }` produces a different source that simply misses this\n // exact-match table and keeps its per-IIFE declaration. Defaults are worth\n // listing anyway: `z.iso.datetime()` is everywhere in API schemas and its\n // pattern is ~330 characters, so a bundle that repeats it per validator pays\n // for it in bytes and in a RegExp construction per schema at module init.\n {\n name: \"__zcReIsoDate\",\n source:\n \"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))$\",\n },\n { name: \"__zcReIsoTime\", source: \"^(?:[01]\\\\d|2[0-3]):[0-5]\\\\d(?::[0-5]\\\\d(?:\\\\.\\\\d+)?)?$\" },\n {\n name: \"__zcReIsoDateTime\",\n source:\n \"^(?:(?:\\\\d\\\\d[2468][048]|\\\\d\\\\d[13579][26]|\\\\d\\\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\\\d|30)|(?:02)-(?:0[1-9]|1\\\\d|2[0-8])))T(?:(?:[01]\\\\d|2[0-3]):[0-5]\\\\d:[0-5]\\\\d(?:\\\\.\\\\d+)?(?:Z))$\",\n },\n {\n name: \"__zcReIsoDuration\",\n source:\n \"^P(?:(\\\\d+W)|(?!.*W)(?=\\\\d|T\\\\d)(\\\\d+Y)?(\\\\d+M)?(\\\\d+D)?(T(?=\\\\d)(\\\\d+H)?(\\\\d+M)?(\\\\d+([.,]\\\\d+)?S)?)?)$\",\n },\n];\n\nconst SOURCE_TO_NAME: ReadonlyMap<string, string> = new Map(\n WELL_KNOWN_REGEXES.map((r) => [r.source, r.name]),\n);\n\nconst SOURCE_TO_TEST_SOURCE: ReadonlyMap<string, string> = new Map(\n WELL_KNOWN_REGEXES.filter((r) => r.testSource !== undefined).map((r) => [\n r.source,\n r.testSource as string,\n ]),\n);\n\n/**\n * Look up a well-known regex by its pattern source string.\n * Returns the virtual-module export name or null if the pattern is user-defined.\n */\nexport function lookupWellKnownRegex(source: string): string | null {\n return SOURCE_TO_NAME.get(source) ?? null;\n}\n\n/**\n * Look up the hand-written behavior-equivalent test pattern for a regex source.\n * Returns null when no rewrite is registered in {@link WELL_KNOWN_REGEXES}.\n *\n * This is the TABLE lookup only. Codegen calls {@link fastTestSource}, which\n * layers automatic repeat unrolling on top and also covers user-supplied\n * patterns that are in no table.\n */\nexport function lookupFastRegexSource(source: string): string | null {\n return SOURCE_TO_TEST_SOURCE.get(source) ?? null;\n}\n\n/**\n * The pattern a generated `.test()` should actually run for `source`, or null\n * when `source` is already the best form.\n *\n * Two behavior-preserving rewrites compose here: the hand-written table entry\n * above (currently just email), then {@link unrollRepeats}, which turns bounded\n * repeats of single-character atoms into explicit repetition — worth 1.3-3.4x\n * on the string formats everyday schemas use (uuid, guid, ulid, nanoid, xid,\n * ksuid, base64, e164, iso.date). Unrolling runs on the table rewrite when\n * there is one, so the two stack.\n *\n * Callers must apply this only to flag-less regexes (the flagged form would\n * need its flags carried into the reported pattern too) and must keep\n * reporting the ORIGINAL source in issues — see `emitRegexSourceString`.\n */\nexport function fastTestSource(source: string): string | null {\n const tableRewrite = SOURCE_TO_TEST_SOURCE.get(source) ?? null;\n return unrollRepeats(tableRewrite ?? source) ?? tableRewrite;\n}\n\n/**\n * Virtual-module export name for the ORIGINAL `/source/` pattern string of a\n * rewritten well-known regex (e.g. \"__zcReEmailSrc\"). Issue sites reference it\n * so the original pattern stays a single bundle-wide string even though the\n * runtime regex object is built from testSource.\n */\nexport function wellKnownRegexSourceName(source: string): string | null {\n const name = SOURCE_TO_NAME.get(source);\n if (name === undefined) return null;\n return fastTestSource(source) !== null ? `${name}Src` : null;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAqBA,MAAa,qBAAqB,OAAO,GAAG;;;;;;;;;;;;;AAc5C,MAAa,0BAA0B,OAAO,GAAG;;AAGjD,MAAa,oBACX;AAeF,MAAa,qBAAgD;CAC3D;EAAE,MAAM;EAAe,QAAQ;EAAoB,YAAY;CAAwB;CACvF;EAAE,MAAM;EAAc,QAAQ;CAAkB;CAChD;EAAE,MAAM;EAAc,QAAQ;CAAqB;CACnD;EAAE,MAAM;EAAe,QAAQ;CAAc;CAC7C;EAAE,MAAM;EAAc,QAAQ;CAA6C;CAC3E;EAAE,MAAM;EAAgB,QAAQ;CAAsB;CACtD;EAAE,MAAM;EAAa,QAAQ;CAAoB;CACjD;EAAE,MAAM;EAAe,QAAQ;CAAoB;CACnD;EACE,MAAM;EACN,QACE;CACJ;CACA;EACE,MAAM;EACN,QACE;CACJ;CACA;EACE,MAAM;EACN,QAAQ;CACV;CACA;EAAE,MAAM;EAAmB,QAAQ;CAAmB;CACtD;EAAE,MAAM;EAAc,QAAQ;CAAsB;CACpD;EACE,MAAM;EACN,QAAQ;CACV;CAQA;EACE,MAAM;EACN,QACE;CACJ;CACA;EAAE,MAAM;EAAiB,QAAQ;CAA0D;CAC3F;EACE,MAAM;EACN,QACE;CACJ;CACA;EACE,MAAM;EACN,QACE;CACJ;AACF;AAEA,MAAM,iBAA8C,IAAI,IACtD,mBAAmB,KAAK,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,CAClD;AAEA,MAAM,wBAAqD,IAAI,IAC7D,mBAAmB,QAAQ,MAAM,EAAE,eAAe,KAAA,CAAS,CAAC,CAAC,KAAK,MAAM,CACtE,EAAE,QACF,EAAE,UACJ,CAAC,CACH;;;;;AAMA,SAAgB,qBAAqB,QAA+B;CAClE,OAAO,eAAe,IAAI,MAAM,KAAK;AACvC;;;;;;;;;AAUA,SAAgB,sBAAsB,QAA+B;CACnE,OAAO,sBAAsB,IAAI,MAAM,KAAK;AAC9C;;;;;;;;;;;;;;;;AAiBA,SAAgB,eAAe,QAA+B;CAC5D,MAAM,eAAe,sBAAsB,IAAI,MAAM,KAAK;CAC1D,OAAO,cAAc,gBAAgB,MAAM,KAAK;AAClD;;;;;;;AAQA,SAAgB,yBAAyB,QAA+B;CACtE,MAAM,OAAO,eAAe,IAAI,MAAM;CACtC,IAAI,SAAS,KAAA,GAAW,OAAO;CAC/B,OAAO,eAAe,MAAM,MAAM,OAAO,GAAG,KAAK,OAAO;AAC1D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checks.d.ts","names":[],"sources":["../../../src/core/extract/checks.ts"],"mappings":";;;;;;;;;;iBAWgB,mBACd,KAAK,kBACL,QAAQ,qBACL;;;;;;;;;;;;;;iBAwBW,yBACd,KAAK,kBACL,QAAQ,qBACL;;;;;;;;;;;iBAsBW,gBACd,KAAK,kBACL,QAAQ,kBACR;;;;;;;
|
|
1
|
+
{"version":3,"file":"checks.d.ts","names":[],"sources":["../../../src/core/extract/checks.ts"],"mappings":";;;;;;;;;;iBAWgB,mBACd,KAAK,kBACL,QAAQ,qBACL;;;;;;;;;;;;;;iBAwBW,yBACd,KAAK,kBACL,QAAQ,qBACL;;;;;;;;;;;iBAsBW,gBACd,KAAK,kBACL,QAAQ,kBACR;;;;;;;iBAqJc,yBAAyB,KAAK;iBAI9B,cACd,QAAQ,kBAQR;;;;;;;;AAAA,aAAa,sCAEb;;AAAA,iBAAiB,sCAEjB;;AAAA,mBAAmB;EAEnB,UAAU;EACV;;KA6NU;EACN;;EACA;EAAgB;;EAChB;;;;;;;;;;;;iBAYU,oBAAoB,iBAAiB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checks.js","names":[],"sources":["../../../src/core/extract/checks.ts"],"sourcesContent":["import type { CheckOrEffectIR, CheckStringFormat } from \"../types.js\";\nimport { isPayloadCheck, isReferenceablePredicate, tryCompileEffect } from \"./effects.js\";\nimport type { ExtractorContext, ZodCheckDef, ZodCheckSchema } from \"./types.js\";\n\n/**\n * Build the `refineRef` registrar for {@link extractChecks} from an extractor's\n * context: it appends a {@link RefEntry} pointing at the check's own predicate\n * (`<schema>._zod.def.checks[i]._zod.def.fn`) so generated code can call the\n * user's function object directly. Returns undefined — meaning \"fall back as\n * before\" — when the extraction is not collecting refs.\n */\nexport function refineRefRegistrar(\n ctx: ExtractorContext,\n checks: ZodCheckSchema[],\n): ((index: number) => number | undefined) | undefined {\n const refs = ctx.refs;\n if (!refs) return undefined;\n return (index) => {\n const fn = checks[index]?._zod?.def?.fn;\n if (!isReferenceablePredicate(fn)) return undefined;\n refs.push({ schema: fn, accessPath: `${ctx.path}._zod.def.checks[${index}]._zod.def.fn` });\n return refs.length - 1;\n };\n}\n\n/**\n * Build the `customParamsRef` registrar for {@link extractChecks}: it appends a\n * {@link RefEntry} pointing at the check's own `params` object\n * (`<schema>._zod.def.checks[i]._zod.def.params`), which $ZodCustom copies onto\n * the issue by reference.\n *\n * A reference rather than a baked literal because `params` is opaque user data:\n * it may hold functions, symbols or a cycle (none of which survive a literal),\n * and zod hands out the SAME object on every issue, so a per-failure copy would\n * break the identity comparison that is the point of passing params.\n *\n * Returns undefined — meaning \"fall back to zod\" — when refs are unavailable.\n */\nexport function customParamsRefRegistrar(\n ctx: ExtractorContext,\n checks: ZodCheckSchema[],\n): ((index: number) => number | undefined) | undefined {\n const refs = ctx.refs;\n if (!refs) return undefined;\n return (index) => {\n refs.push({\n schema: checks[index]?._zod?.def?.params,\n accessPath: `${ctx.path}._zod.def.checks[${index}]._zod.def.params`,\n });\n return refs.length - 1;\n };\n}\n\n/**\n * Register the payload-taking callback of check `index` (`superRefine`) as an\n * `__rf[N]` reference, pointing at zod's own wrapper\n * (`<schema>._zod.def.checks[i]._zod.check`).\n *\n * Only the LAST check on a node qualifies: an issue carrying\n * `fatal`/`continue:false` aborts zod's remaining chain, and compiled output\n * runs every check unconditionally — with nothing following, there is nothing\n * left to abort, so the two agree.\n */\nexport function payloadCheckRef(\n ctx: ExtractorContext,\n checks: ZodCheckSchema[],\n index: number,\n): number | undefined {\n if (index !== checks.length - 1) return undefined;\n const check = checks[index];\n const refs = ctx.refs;\n if (!refs || !check || !isPayloadCheck(check)) return undefined;\n refs.push({\n schema: (check._zod as { check: unknown }).check,\n accessPath: `${ctx.path}._zod.def.checks[${index}]._zod.check`,\n });\n return refs.length - 1;\n}\n\n/**\n * BUILT-IN string formats the codegen can validate without a regex pattern,\n * because it substitutes a known regex of its own (see EMAIL_REGEX_SOURCE /\n * UUID_REGEX_SOURCE in codegen/schemas/string.ts). Everything else without a\n * pattern (e.g. z.jwt(), which validates algorithmically) must fall back to Zod\n * instead of being silently skipped.\n *\n * Built-in ONLY: this is keyed on the format NAME, and a custom format's name is\n * user-chosen, so a match here proves nothing about a `$ZodCustomStringFormat`\n * — see the isCustomStringFormat gate at the use site.\n */\nconst PATTERNLESS_FORMATS = new Set([\"email\", \"uuid\", \"url\"]);\n\n/**\n * String formats whose `def.pattern` is NOT the validator Zod actually runs.\n *\n * Zod attaches a `def.pattern` to every string format because `toJSONSchema()`\n * needs one to emit. A handful of constructors then REPLACE the pattern check\n * outright: `$ZodStringFormat.init` installs the pattern check with `??=`, and\n * the constructor's following `inst._zod.check = …` assignment overwrites it.\n * The pattern survives only as JSON Schema metadata. Compiling it produces a\n * verdict Zod does not share — in BOTH directions — so these delegate instead.\n *\n * - `ipv6`: Zod validates with `new URL(\"http://[\" + value + \"]\")`. The URL\n * parser accepts IPv4-mapped forms the regex has no alternative for\n * (`\"::ffff:1.2.3.4\"`, `\"1:2:3:4:5:6:1.2.3.4\"`) and, since it strips tabs and\n * ends the host at `@` or `/`, even non-addresses (`\"@_c.Z=\\t/X+9\"`).\n * - `cidrv6`: splits on `/`, range-checks the prefix as a number, then runs the\n * same `new URL` probe on the address half — inheriting every `ipv6` gap.\n * - `base64`: `length % 4 === 0 && atob(value)` does not throw. `atob` is\n * HTML's forgiving-base64 decode, which STRIPS ASCII whitespace before\n * decoding, so Zod accepts `\"AAAA \"`, `\"\\t\\t\\t\\tAAAA\"` and `\"+c \\n\"` —\n * all rejected by `regexes.base64`, whose charset has no room for whitespace.\n * - `base64url`: `regexes.base64url.test(v)` AND an `atob` of the re-padded\n * value. The regex alone is `^[A-Za-z0-9_-]*$`, which accepts every\n * `length % 4 === 1` string (`\"a\"`, `\"-\"`, `\"A\"`, `\"9\"`) that decoding fails.\n *\n * Every other string format compiles its pattern, which is only safe while that\n * pattern and Zod's installed check keep agreeing. What keeps this list honest\n * — in both directions — is tests/string-format-parity.test.ts, which fuzzes\n * every reachable string format against Zod over one shared corpus.\n */\nconst NON_AUTHORITATIVE_PATTERN_FORMATS = new Set([\"base64\", \"base64url\", \"cidrv6\", \"ipv6\"]);\n\n/**\n * True when a `string_format` check is a `$ZodCustomStringFormat` — the family\n * Zod's `_stringFormat` helper builds: `z.stringFormat()`, and the built-ins\n * `z.hex()`, `z.hostname()` and `z.hash()` that route through it.\n *\n * `def.fn` is the structural marker: `_stringFormat` is the ONLY producer of a\n * `string_format` check def that carries one (every other format — guid, uuid,\n * email, ipv4, iso.*, lowercase/uppercase, regex, includes/starts_with/ends_with\n * — is built from a plain `{ check, format, pattern }` def). A name list cannot\n * stand in for it: `z.stringFormat(\"anything\", /re/)` lets the caller pick the\n * format name, so the set of names is open.\n *\n * Two things follow from being in this family, both about Zod's OVERRIDE of\n * `inst._zod.check` (see `$ZodCustomStringFormat` in zod/v4/core/schemas.js):\n * the issue it pushes is bare (see CheckStringFormat.bareIssue), and it calls\n * `def.fn` rather than testing `def.pattern` — which is what makes a `g`/`y`\n * pattern stateful (see {@link isStatefulCustomFormat}).\n */\nfunction isCustomStringFormat(def: ZodCheckDef): boolean {\n return typeof def.fn === \"function\";\n}\n\n/**\n * True when a `string_format` check validates through `def.fn` over a STATEFUL\n * (`g`/`y`-flagged) pattern — a custom format built by Zod's `_stringFormat`\n * helper, i.e. `z.stringFormat(\"name\", /ab/g)`.\n *\n * Such a format is stateful in Zod, while the compiled output is deliberately\n * stateless — so the two diverge from the first successful match onward.\n * `_stringFormat` stores the regex as `def.pattern` AND closes over it as\n * `def.fn = (val) => fnOrRegex.test(val)`.\n * `$ZodCustomStringFormat` validates by calling that `def.fn`, and NOTHING\n * resets `lastIndex`, so a `g`/`y` regex carries its cursor across parses:\n * `z.stringFormat(\"f\", /ab/g).safeParse(\"abab\")` returns true, true, FALSE,\n * true over four calls. Contrast `$ZodCheckRegex` — what `.regex(/ab/g)` builds\n * — which does an explicit `def.pattern.lastIndex = 0` before testing and is\n * therefore stateless. The codegen's `lastIndexReset` reproduces THAT reset,\n * which is right for `.regex()` and is exactly what makes a custom format\n * diverge. The state lives in the user's own RegExp object, which the compiled\n * validator does not hold, so there is nothing faithful to emit: delegate.\n *\n * This needs a predicate rather than a name set like\n * NON_AUTHORITATIVE_PATTERN_FORMATS above, because a custom format's name is\n * user-chosen and cannot be enumerated. Narrowness matters in both clauses:\n * `def.fn` alone would over-delegate the built-ins Zod also routes through\n * `_stringFormat` (hostname, hex, hash), and the flag test alone would\n * over-delegate `.regex(/…/g)` — both of which stay compiled because their\n * patterns are unflagged and their check is not `def.fn`, respectively.\n */\nfunction isStatefulCustomFormat(def: ZodCheckDef): boolean {\n return (\n isCustomStringFormat(def) && def.pattern instanceof RegExp && /[gy]/.test(def.pattern.flags)\n );\n}\n\n/**\n * Check kinds where Zod itself installs a `when` guard\n * (`!nullish(value) && value.length/size !== undefined`). Compiled output\n * reproduces that gating structurally — length/size checks only run after the\n * typeof/instanceof guard — so a `when` on these kinds is expected and safe.\n * A `when` anywhere else must be user-supplied → fall back to Zod.\n * (A user-supplied `when` on one of these six kinds is indistinguishable from\n * the internal guard and remains unsupported.)\n */\nconst INTERNAL_WHEN_CHECK_KINDS = new Set([\n \"min_length\",\n \"max_length\",\n \"length_equals\",\n \"min_size\",\n \"max_size\",\n \"size_equals\",\n]);\n\n/** True when a check carries a runtime `when` predicate we cannot reproduce. */\nfunction hasUserWhen(def: ZodCheckDef): boolean {\n return Boolean(def.when) && !INTERNAL_WHEN_CHECK_KINDS.has(def.check);\n}\n\n/**\n * True when a check can't be compiled because of runtime modifiers:\n * a user-supplied `when` predicate, or `abort: true` (which stops later\n * checks and marks the option as aborted in union pruning — compiled\n * output always runs every check).\n */\nexport function hasUncompilableModifiers(def: ZodCheckDef): boolean {\n return hasUserWhen(def) || def.abort === true;\n}\n\nexport function extractChecks(\n checks: ZodCheckSchema[],\n /**\n * Registers the predicate of the check at `index` as an `__rf[N]` reference\n * and returns N, or undefined when references are unavailable (no refs array)\n * or the callback's shape cannot be called directly. Supplied by extractors\n * that know their schema's access path; omitted by callers that do not, which\n * simply keeps the old fall-back-to-zod behavior.\n */\n refineRef?: (index: number) => number | undefined,\n /** Registers a payload-taking callback (superRefine); see payloadCheckRef. */\n payloadRefFor?: (index: number) => number | undefined,\n /** Registers a `.refine(fn, { params })` payload; see customParamsRefRegistrar. */\n customParamsRef?: (index: number) => number | undefined,\n): {\n checkIRs: CheckOrEffectIR[];\n hasFallback: boolean;\n} {\n const checkIRs: CheckOrEffectIR[] = [];\n let hasFallback = false;\n\n for (const [index, check] of checks.entries()) {\n const def = check._zod?.def;\n if (!def) continue;\n\n // Checks gated by a user-supplied runtime `when` predicate or abort:true\n // can't be reproduced in compiled output — delegate the schema to Zod.\n if (hasUncompilableModifiers(def)) {\n hasFallback = true;\n continue;\n }\n\n // Per-check custom error: bake static messages into the IR; error maps\n // that read the issue (dynamic) can't be compiled — fall back so Zod\n // produces the exact message.\n const resolved = resolveCheckMessage(def.error);\n if (resolved.kind === \"dynamic\") {\n hasFallback = true;\n continue;\n }\n const message = resolved.kind === \"static\" ? { message: resolved.message } : {};\n\n switch (def.check) {\n case \"min_length\":\n checkIRs.push({ kind: \"min_length\", minimum: def.minimum, ...message });\n break;\n case \"max_length\":\n checkIRs.push({ kind: \"max_length\", maximum: def.maximum, ...message });\n break;\n case \"length_equals\":\n checkIRs.push({ kind: \"length_equals\", length: def.length, ...message });\n break;\n case \"greater_than\":\n checkIRs.push({\n kind: \"greater_than\",\n value: def.value,\n inclusive: def.inclusive,\n ...message,\n });\n break;\n case \"less_than\":\n checkIRs.push({\n kind: \"less_than\",\n value: def.value,\n inclusive: def.inclusive,\n ...message,\n });\n break;\n case \"multiple_of\":\n checkIRs.push({ kind: \"multiple_of\", value: def.value, ...message });\n break;\n case \"number_format\":\n checkIRs.push({\n kind: \"number_format\",\n format: def.format as \"safeint\" | \"int32\" | \"uint32\" | \"float32\" | \"float64\",\n ...message,\n });\n break;\n case \"string_format\": {\n // `z.stringFormat(name, …)` lets the CALLER pick the format name, so a\n // custom format can be named \"email\", \"url\" — anything a built-in is\n // named. The name therefore does NOT identify which validator Zod runs;\n // only the structural `def.fn` marker does. Every branch below that\n // treats a name as proof of a built-in's behaviour is gated on this.\n const isCustom = isCustomStringFormat(def);\n\n // A custom format over a g/y regex advances that regex's lastIndex on\n // every parse and Zod never resets it — see isStatefulCustomFormat.\n if (isStatefulCustomFormat(def)) {\n hasFallback = true;\n break;\n }\n if (def.format === \"includes\" && typeof def.includes === \"string\") {\n checkIRs.push({\n kind: \"includes\",\n includes: def.includes,\n ...(typeof def.position === \"number\" ? { position: def.position } : {}),\n ...message,\n });\n break;\n }\n if (def.format === \"starts_with\" && typeof def.prefix === \"string\") {\n checkIRs.push({ kind: \"starts_with\", prefix: def.prefix, ...message });\n break;\n }\n if (def.format === \"ends_with\" && typeof def.suffix === \"string\") {\n checkIRs.push({ kind: \"ends_with\", suffix: def.suffix, ...message });\n break;\n }\n // z.url() validates with the URL parser plus optional hostname/protocol\n // probes, and carries no pattern worth compiling. A custom format merely\n // NAMED \"url\" shares none of that — it validates through `def.fn` — so\n // taking this branch both discarded the user's own `def.pattern` and\n // answered with the URL parser's verdict instead of the user's.\n if (def.format === \"url\" && !isCustom) {\n checkIRs.push({\n kind: \"string_format\",\n format: \"url\",\n ...regexFields(\"hostname\", \"hostnameFlags\", def.hostname),\n ...regexFields(\"protocol\", \"protocolFlags\", def.protocol),\n ...(def.normalize ? { normalize: true } : {}),\n ...message,\n });\n break;\n }\n // Not gated on isCustom: this set only ever adds a fallback, so a custom\n // format that happens to borrow one of these names loses its fast path\n // but never its verdict.\n if (NON_AUTHORITATIVE_PATTERN_FORMATS.has(def.format)) {\n hasFallback = true;\n break;\n }\n const pattern = def.pattern instanceof RegExp ? def.pattern.source : def.pattern;\n const flags = def.pattern instanceof RegExp && def.pattern.flags ? def.pattern.flags : \"\";\n if (!pattern && !(!isCustom && PATTERNLESS_FORMATS.has(def.format))) {\n // Nothing to compile: an algorithmic built-in (e.g. jwt), or a custom\n // format built from a FUNCTION rather than a regex. For the latter the\n // predicate lives only in `def.fn`, and a name-matched built-in regex\n // is emphatically not it — `z.stringFormat(\"email\", (v) => v.length\n // === 3)` accepts \"abc\" and rejects \"a@b.co\", the exact inverse of the\n // email regex. Delegate to Zod.\n hasFallback = true;\n break;\n }\n checkIRs.push({\n kind: \"string_format\",\n format: def.format,\n ...(pattern ? { pattern } : {}),\n ...(flags ? { patternFlags: flags } : {}),\n ...(isCustom ? { bareIssue: true } : {}),\n ...message,\n });\n break;\n }\n case \"overwrite\": {\n // $ZodCheckOverwrite (.trim(), .toLowerCase(), ...): value = def.tx(value)\n const source = tryCompileEffect(def.tx);\n if (source) {\n checkIRs.push({ kind: \"overwrite_effect\", source });\n } else {\n hasFallback = true;\n }\n break;\n }\n case \"custom\": {\n // superRefine / raw .check(): the callback collects issues from zod's\n // payload rather than returning a verdict, so it is referenced and\n // called with a synthesized payload (see SuperRefineEffectCheckIR).\n if (isPayloadCheck(check)) {\n const payloadRef = payloadRefFor?.(index);\n if (payloadRef === undefined) {\n hasFallback = true;\n break;\n }\n checkIRs.push({ kind: \"super_refine_effect\", refIndex: payloadRef });\n break;\n }\n // `.refine(fn, { path })` reports the issue against a member of the\n // refined value; anything but plain string/number segments is a shape\n // the generated path expression cannot reproduce.\n const customPath = Array.isArray(def.path) ? def.path : undefined;\n if (customPath?.some((p) => typeof p !== \"string\" && typeof p !== \"number\")) {\n hasFallback = true;\n break;\n }\n const pathIR = customPath && customPath.length > 0 ? { path: customPath } : {};\n // `.refine(fn, { params })`: zod attaches the object to the issue, and\n // an error map reads it back. Held as a reference (see\n // customParamsRefRegistrar); with no ref array to hold it, the issue\n // cannot be reproduced at all, so the schema goes to zod.\n let paramsIR: { paramsRefIndex?: number } = {};\n if (def.params !== undefined) {\n const paramsRefIndex = customParamsRef?.(index);\n if (paramsRefIndex === undefined) {\n hasFallback = true;\n break;\n }\n paramsIR = { paramsRefIndex };\n }\n const source = tryCompileEffect(def.fn);\n if (source) {\n checkIRs.push({ kind: \"refine_effect\", source, ...pathIR, ...paramsIR, ...message });\n break;\n }\n // Not inlineable (the predicate captures outer variables). Call the\n // user's own function object through a schema reference instead of\n // delegating: a captured `.refine()` at the root otherwise costs the\n // whole schema its compiled path.\n const refIndex = refineRef?.(index);\n if (refIndex !== undefined) {\n checkIRs.push({ kind: \"refine_effect\", refIndex, ...pathIR, ...paramsIR, ...message });\n } else {\n hasFallback = true;\n }\n break;\n }\n default:\n // Unknown check kind — never drop silently; delegate to Zod.\n hasFallback = true;\n break;\n }\n }\n\n return { checkIRs, hasFallback };\n}\n\nfunction regexFields(\n sourceKey: \"hostname\" | \"protocol\",\n flagsKey: \"hostnameFlags\" | \"protocolFlags\",\n value: unknown,\n): Partial<CheckStringFormat> {\n if (!(value instanceof RegExp)) return {};\n return { [sourceKey]: value.source, ...(value.flags ? { [flagsKey]: value.flags } : {}) };\n}\n\n// ─── Static error message extraction ────────────────────────────────────────\n\nexport type ResolvedMessage =\n | { kind: \"none\" }\n | { kind: \"static\"; message: string }\n | { kind: \"dynamic\" };\n\n/**\n * Classify a check/schema-level `error` param.\n *\n * Zod normalizes `.min(3, \"msg\")` / `{ error: \"msg\" }` / `{ message: \"msg\" }`\n * into an error-map function that ignores its issue argument and returns the\n * string. We call the function with an access-tracking Proxy: if it never\n * inspects the issue, its return value is a constant we can bake into the\n * generated issue. If it reads any issue property (input-dependent message),\n * it is dynamic and the schema must fall back to Zod for exact messages.\n */\nexport function resolveCheckMessage(error: unknown): ResolvedMessage {\n if (error === null || error === undefined) return { kind: \"none\" };\n if (typeof error === \"string\") return { kind: \"static\", message: error };\n if (typeof error !== \"function\") return { kind: \"dynamic\" };\n\n let accessed = false;\n const track = () => {\n accessed = true;\n };\n const probe = new Proxy(\n {},\n {\n get(_t, prop) {\n track();\n // Avoid breaking string coercion / await probing inside error maps.\n if (prop === Symbol.toPrimitive || prop === \"toString\" || prop === \"valueOf\") {\n return () => \"\";\n }\n return undefined;\n },\n has() {\n track();\n return false;\n },\n ownKeys() {\n track();\n return [];\n },\n getOwnPropertyDescriptor() {\n track();\n return undefined;\n },\n },\n );\n\n let result: unknown;\n try {\n result = (error as (issue: unknown) => unknown)(probe);\n } catch {\n return { kind: \"dynamic\" };\n }\n if (accessed) return { kind: \"dynamic\" };\n if (typeof result === \"string\") return { kind: \"static\", message: result };\n if (\n result !== null &&\n typeof result === \"object\" &&\n typeof (result as { message?: unknown }).message === \"string\"\n ) {\n return { kind: \"static\", message: (result as { message: string }).message };\n }\n // Error map deferred (returned undefined) — no custom message.\n if (result === undefined) return { kind: \"none\" };\n return { kind: \"dynamic\" };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"checks.js","names":[],"sources":["../../../src/core/extract/checks.ts"],"sourcesContent":["import type { CheckOrEffectIR, CheckStringFormat } from \"../types.js\";\nimport { isPayloadCheck, isReferenceablePredicate, tryCompileEffect } from \"./effects.js\";\nimport type { ExtractorContext, ZodCheckDef, ZodCheckSchema } from \"./types.js\";\n\n/**\n * Build the `refineRef` registrar for {@link extractChecks} from an extractor's\n * context: it appends a {@link RefEntry} pointing at the check's own predicate\n * (`<schema>._zod.def.checks[i]._zod.def.fn`) so generated code can call the\n * user's function object directly. Returns undefined — meaning \"fall back as\n * before\" — when the extraction is not collecting refs.\n */\nexport function refineRefRegistrar(\n ctx: ExtractorContext,\n checks: ZodCheckSchema[],\n): ((index: number) => number | undefined) | undefined {\n const refs = ctx.refs;\n if (!refs) return undefined;\n return (index) => {\n const fn = checks[index]?._zod?.def?.fn;\n if (!isReferenceablePredicate(fn)) return undefined;\n refs.push({ schema: fn, accessPath: `${ctx.path}._zod.def.checks[${index}]._zod.def.fn` });\n return refs.length - 1;\n };\n}\n\n/**\n * Build the `customParamsRef` registrar for {@link extractChecks}: it appends a\n * {@link RefEntry} pointing at the check's own `params` object\n * (`<schema>._zod.def.checks[i]._zod.def.params`), which $ZodCustom copies onto\n * the issue by reference.\n *\n * A reference rather than a baked literal because `params` is opaque user data:\n * it may hold functions, symbols or a cycle (none of which survive a literal),\n * and zod hands out the SAME object on every issue, so a per-failure copy would\n * break the identity comparison that is the point of passing params.\n *\n * Returns undefined — meaning \"fall back to zod\" — when refs are unavailable.\n */\nexport function customParamsRefRegistrar(\n ctx: ExtractorContext,\n checks: ZodCheckSchema[],\n): ((index: number) => number | undefined) | undefined {\n const refs = ctx.refs;\n if (!refs) return undefined;\n return (index) => {\n refs.push({\n schema: checks[index]?._zod?.def?.params,\n accessPath: `${ctx.path}._zod.def.checks[${index}]._zod.def.params`,\n });\n return refs.length - 1;\n };\n}\n\n/**\n * Register the payload-taking callback of check `index` (`superRefine`) as an\n * `__rf[N]` reference, pointing at zod's own wrapper\n * (`<schema>._zod.def.checks[i]._zod.check`).\n *\n * Only the LAST check on a node qualifies: an issue carrying\n * `fatal`/`continue:false` aborts zod's remaining chain, and compiled output\n * runs every check unconditionally — with nothing following, there is nothing\n * left to abort, so the two agree.\n */\nexport function payloadCheckRef(\n ctx: ExtractorContext,\n checks: ZodCheckSchema[],\n index: number,\n): number | undefined {\n if (index !== checks.length - 1) return undefined;\n const check = checks[index];\n const refs = ctx.refs;\n if (!refs || !check || !isPayloadCheck(check)) return undefined;\n refs.push({\n schema: (check._zod as { check: unknown }).check,\n accessPath: `${ctx.path}._zod.def.checks[${index}]._zod.check`,\n });\n return refs.length - 1;\n}\n\n/**\n * BUILT-IN string formats the codegen can validate without a regex pattern,\n * because it substitutes a known regex of its own (see EMAIL_REGEX_SOURCE /\n * UUID_REGEX_SOURCE in codegen/schemas/string.ts). Everything else without a\n * pattern (e.g. z.jwt(), which validates algorithmically) must fall back to Zod\n * instead of being silently skipped.\n *\n * Built-in ONLY: this is keyed on the format NAME, and a custom format's name is\n * user-chosen, so a match here proves nothing about a `$ZodCustomStringFormat`\n * — see the isCustomStringFormat gate at the use site.\n */\nconst PATTERNLESS_FORMATS = new Set([\"email\", \"uuid\", \"url\"]);\n\n/**\n * String formats whose `def.pattern` is NOT the validator Zod actually runs.\n *\n * Zod attaches a `def.pattern` to every string format because `toJSONSchema()`\n * needs one to emit. A handful of constructors then REPLACE the pattern check\n * outright: `$ZodStringFormat.init` installs the pattern check with `??=`, and\n * the constructor's following `inst._zod.check = …` assignment overwrites it.\n * The pattern survives only as JSON Schema metadata. Compiling it produces a\n * verdict Zod does not share — in BOTH directions — so these delegate instead.\n *\n * - `ipv6`: Zod validates with `new URL(\"http://[\" + value + \"]\")`. The URL\n * parser accepts IPv4-mapped forms the regex has no alternative for\n * (`\"::ffff:1.2.3.4\"`, `\"1:2:3:4:5:6:1.2.3.4\"`) and, since it strips tabs and\n * ends the host at `@` or `/`, even non-addresses (`\"@_c.Z=\\t/X+9\"`).\n * - `cidrv6`: splits on `/`, range-checks the prefix as a number, then runs the\n * same `new URL` probe on the address half — inheriting every `ipv6` gap.\n * - `base64`: `length % 4 === 0 && atob(value)` does not throw. `atob` is\n * HTML's forgiving-base64 decode, which STRIPS ASCII whitespace before\n * decoding, so Zod accepts `\"AAAA \"`, `\"\\t\\t\\t\\tAAAA\"` and `\"+c \\n\"` —\n * all rejected by `regexes.base64`, whose charset has no room for whitespace.\n * - `base64url`: `regexes.base64url.test(v)` AND an `atob` of the re-padded\n * value. The regex alone is `^[A-Za-z0-9_-]*$`, which accepts every\n * `length % 4 === 1` string (`\"a\"`, `\"-\"`, `\"A\"`, `\"9\"`) that decoding fails.\n *\n * Every other string format compiles its pattern, which is only safe while that\n * pattern and Zod's installed check keep agreeing. What keeps this list honest\n * — in both directions — is tests/string-format-parity.test.ts, which fuzzes\n * every reachable string format against Zod over one shared corpus.\n */\nconst NON_AUTHORITATIVE_PATTERN_FORMATS = new Set([\n \"base64\",\n \"base64url\",\n \"cidrv6\",\n // $ZodCreditCard's pattern is shape-only by design (\"the Luhn check below is\n // not expressible as a pattern\"); its `_zod.check` runs the checksum on top.\n \"credit_card\",\n \"ipv6\",\n]);\n\n/**\n * True when a `string_format` check is a `$ZodCustomStringFormat` — the family\n * Zod's `_stringFormat` helper builds: `z.stringFormat()`, and the built-ins\n * `z.hex()`, `z.hostname()` and `z.hash()` that route through it.\n *\n * `def.fn` is the structural marker: `_stringFormat` is the ONLY producer of a\n * `string_format` check def that carries one (every other format — guid, uuid,\n * email, ipv4, iso.*, lowercase/uppercase, regex, includes/starts_with/ends_with\n * — is built from a plain `{ check, format, pattern }` def). A name list cannot\n * stand in for it: `z.stringFormat(\"anything\", /re/)` lets the caller pick the\n * format name, so the set of names is open.\n *\n * Two things follow from being in this family, both about Zod's OVERRIDE of\n * `inst._zod.check` (see `$ZodCustomStringFormat` in zod/v4/core/schemas.js):\n * the issue it pushes is bare (see CheckStringFormat.bareIssue), and it calls\n * `def.fn` rather than testing `def.pattern` — which is what makes a `g`/`y`\n * pattern stateful (see {@link isStatefulCustomFormat}).\n */\nfunction isCustomStringFormat(def: ZodCheckDef): boolean {\n return typeof def.fn === \"function\";\n}\n\n/**\n * True when a `string_format` check validates through `def.fn` over a STATEFUL\n * (`g`/`y`-flagged) pattern — a custom format built by Zod's `_stringFormat`\n * helper, i.e. `z.stringFormat(\"name\", /ab/g)`.\n *\n * Such a format is stateful in Zod, while the compiled output is deliberately\n * stateless — so the two diverge from the first successful match onward.\n * `_stringFormat` stores the regex as `def.pattern` AND closes over it as\n * `def.fn = (val) => fnOrRegex.test(val)`.\n * `$ZodCustomStringFormat` validates by calling that `def.fn`, and NOTHING\n * resets `lastIndex`, so a `g`/`y` regex carries its cursor across parses:\n * `z.stringFormat(\"f\", /ab/g).safeParse(\"abab\")` returns true, true, FALSE,\n * true over four calls. Contrast `$ZodCheckRegex` — what `.regex(/ab/g)` builds\n * — which does an explicit `def.pattern.lastIndex = 0` before testing and is\n * therefore stateless. The codegen's `lastIndexReset` reproduces THAT reset,\n * which is right for `.regex()` and is exactly what makes a custom format\n * diverge. The state lives in the user's own RegExp object, which the compiled\n * validator does not hold, so there is nothing faithful to emit: delegate.\n *\n * This needs a predicate rather than a name set like\n * NON_AUTHORITATIVE_PATTERN_FORMATS above, because a custom format's name is\n * user-chosen and cannot be enumerated. Narrowness matters in both clauses:\n * `def.fn` alone would over-delegate the built-ins Zod also routes through\n * `_stringFormat` (hostname, hex, hash), and the flag test alone would\n * over-delegate `.regex(/…/g)` — both of which stay compiled because their\n * patterns are unflagged and their check is not `def.fn`, respectively.\n */\nfunction isStatefulCustomFormat(def: ZodCheckDef): boolean {\n return (\n isCustomStringFormat(def) && def.pattern instanceof RegExp && /[gy]/.test(def.pattern.flags)\n );\n}\n\n/**\n * Check kinds where Zod itself installs a `when` guard\n * (`!nullish(value) && value.length/size !== undefined`). Compiled output\n * reproduces that gating structurally — length/size checks only run after the\n * typeof/instanceof guard — so a `when` on these kinds is expected and safe.\n * A `when` anywhere else must be user-supplied → fall back to Zod.\n * (A user-supplied `when` on one of these six kinds is indistinguishable from\n * the internal guard and remains unsupported.)\n */\nconst INTERNAL_WHEN_CHECK_KINDS = new Set([\n \"min_length\",\n \"max_length\",\n \"length_equals\",\n \"min_size\",\n \"max_size\",\n \"size_equals\",\n]);\n\n/** True when a check carries a runtime `when` predicate we cannot reproduce. */\nfunction hasUserWhen(def: ZodCheckDef): boolean {\n return Boolean(def.when) && !INTERNAL_WHEN_CHECK_KINDS.has(def.check);\n}\n\n/**\n * True when a check can't be compiled because of runtime modifiers:\n * a user-supplied `when` predicate, or `abort: true` (which stops later\n * checks and marks the option as aborted in union pruning — compiled\n * output always runs every check).\n */\nexport function hasUncompilableModifiers(def: ZodCheckDef): boolean {\n return hasUserWhen(def) || def.abort === true;\n}\n\nexport function extractChecks(\n checks: ZodCheckSchema[],\n /**\n * Registers the predicate of the check at `index` as an `__rf[N]` reference\n * and returns N, or undefined when references are unavailable (no refs array)\n * or the callback's shape cannot be called directly. Supplied by extractors\n * that know their schema's access path; omitted by callers that do not, which\n * simply keeps the old fall-back-to-zod behavior.\n */\n refineRef?: (index: number) => number | undefined,\n /** Registers a payload-taking callback (superRefine); see payloadCheckRef. */\n payloadRefFor?: (index: number) => number | undefined,\n /** Registers a `.refine(fn, { params })` payload; see customParamsRefRegistrar. */\n customParamsRef?: (index: number) => number | undefined,\n): {\n checkIRs: CheckOrEffectIR[];\n hasFallback: boolean;\n} {\n const checkIRs: CheckOrEffectIR[] = [];\n let hasFallback = false;\n\n for (const [index, check] of checks.entries()) {\n const def = check._zod?.def;\n if (!def) continue;\n\n // Checks gated by a user-supplied runtime `when` predicate or abort:true\n // can't be reproduced in compiled output — delegate the schema to Zod.\n if (hasUncompilableModifiers(def)) {\n hasFallback = true;\n continue;\n }\n\n // Per-check custom error: bake static messages into the IR; error maps\n // that read the issue (dynamic) can't be compiled — fall back so Zod\n // produces the exact message.\n const resolved = resolveCheckMessage(def.error);\n if (resolved.kind === \"dynamic\") {\n hasFallback = true;\n continue;\n }\n const message = resolved.kind === \"static\" ? { message: resolved.message } : {};\n\n switch (def.check) {\n case \"min_length\":\n checkIRs.push({ kind: \"min_length\", minimum: def.minimum, ...message });\n break;\n case \"max_length\":\n checkIRs.push({ kind: \"max_length\", maximum: def.maximum, ...message });\n break;\n case \"length_equals\":\n checkIRs.push({ kind: \"length_equals\", length: def.length, ...message });\n break;\n case \"greater_than\":\n checkIRs.push({\n kind: \"greater_than\",\n value: def.value,\n inclusive: def.inclusive,\n ...message,\n });\n break;\n case \"less_than\":\n checkIRs.push({\n kind: \"less_than\",\n value: def.value,\n inclusive: def.inclusive,\n ...message,\n });\n break;\n case \"multiple_of\":\n checkIRs.push({ kind: \"multiple_of\", value: def.value, ...message });\n break;\n case \"number_format\":\n checkIRs.push({\n kind: \"number_format\",\n format: def.format as \"safeint\" | \"int32\" | \"uint32\" | \"float32\" | \"float64\",\n ...message,\n });\n break;\n case \"string_format\": {\n // `z.stringFormat(name, …)` lets the CALLER pick the format name, so a\n // custom format can be named \"email\", \"url\" — anything a built-in is\n // named. The name therefore does NOT identify which validator Zod runs;\n // only the structural `def.fn` marker does. Every branch below that\n // treats a name as proof of a built-in's behaviour is gated on this.\n const isCustom = isCustomStringFormat(def);\n\n // A custom format over a g/y regex advances that regex's lastIndex on\n // every parse and Zod never resets it — see isStatefulCustomFormat.\n if (isStatefulCustomFormat(def)) {\n hasFallback = true;\n break;\n }\n if (def.format === \"includes\" && typeof def.includes === \"string\") {\n checkIRs.push({\n kind: \"includes\",\n includes: def.includes,\n ...(typeof def.position === \"number\" ? { position: def.position } : {}),\n ...message,\n });\n break;\n }\n if (def.format === \"starts_with\" && typeof def.prefix === \"string\") {\n checkIRs.push({ kind: \"starts_with\", prefix: def.prefix, ...message });\n break;\n }\n if (def.format === \"ends_with\" && typeof def.suffix === \"string\") {\n checkIRs.push({ kind: \"ends_with\", suffix: def.suffix, ...message });\n break;\n }\n // z.url() validates with the URL parser plus optional hostname/protocol\n // probes, and carries no pattern worth compiling. A custom format merely\n // NAMED \"url\" shares none of that — it validates through `def.fn` — so\n // taking this branch both discarded the user's own `def.pattern` and\n // answered with the URL parser's verdict instead of the user's.\n if (def.format === \"url\" && !isCustom) {\n checkIRs.push({\n kind: \"string_format\",\n format: \"url\",\n ...regexFields(\"hostname\", \"hostnameFlags\", def.hostname),\n ...regexFields(\"protocol\", \"protocolFlags\", def.protocol),\n ...(def.normalize ? { normalize: true } : {}),\n ...message,\n });\n break;\n }\n // Not gated on isCustom: this set only ever adds a fallback, so a custom\n // format that happens to borrow one of these names loses its fast path\n // but never its verdict.\n if (NON_AUTHORITATIVE_PATTERN_FORMATS.has(def.format)) {\n hasFallback = true;\n break;\n }\n const pattern = def.pattern instanceof RegExp ? def.pattern.source : def.pattern;\n const flags = def.pattern instanceof RegExp && def.pattern.flags ? def.pattern.flags : \"\";\n if (!pattern && !(!isCustom && PATTERNLESS_FORMATS.has(def.format))) {\n // Nothing to compile: an algorithmic built-in (e.g. jwt), or a custom\n // format built from a FUNCTION rather than a regex. For the latter the\n // predicate lives only in `def.fn`, and a name-matched built-in regex\n // is emphatically not it — `z.stringFormat(\"email\", (v) => v.length\n // === 3)` accepts \"abc\" and rejects \"a@b.co\", the exact inverse of the\n // email regex. Delegate to Zod.\n hasFallback = true;\n break;\n }\n checkIRs.push({\n kind: \"string_format\",\n format: def.format,\n ...(pattern ? { pattern } : {}),\n ...(flags ? { patternFlags: flags } : {}),\n ...(isCustom ? { bareIssue: true } : {}),\n ...message,\n });\n break;\n }\n case \"overwrite\": {\n // $ZodCheckOverwrite (.trim(), .toLowerCase(), ...): value = def.tx(value)\n const source = tryCompileEffect(def.tx);\n if (source) {\n checkIRs.push({ kind: \"overwrite_effect\", source });\n } else {\n hasFallback = true;\n }\n break;\n }\n case \"custom\": {\n // superRefine / raw .check(): the callback collects issues from zod's\n // payload rather than returning a verdict, so it is referenced and\n // called with a synthesized payload (see SuperRefineEffectCheckIR).\n if (isPayloadCheck(check)) {\n const payloadRef = payloadRefFor?.(index);\n if (payloadRef === undefined) {\n hasFallback = true;\n break;\n }\n checkIRs.push({ kind: \"super_refine_effect\", refIndex: payloadRef });\n break;\n }\n // `.refine(fn, { path })` reports the issue against a member of the\n // refined value; anything but plain string/number segments is a shape\n // the generated path expression cannot reproduce.\n const customPath = Array.isArray(def.path) ? def.path : undefined;\n if (customPath?.some((p) => typeof p !== \"string\" && typeof p !== \"number\")) {\n hasFallback = true;\n break;\n }\n const pathIR = customPath && customPath.length > 0 ? { path: customPath } : {};\n // `.refine(fn, { params })`: zod attaches the object to the issue, and\n // an error map reads it back. Held as a reference (see\n // customParamsRefRegistrar); with no ref array to hold it, the issue\n // cannot be reproduced at all, so the schema goes to zod.\n let paramsIR: { paramsRefIndex?: number } = {};\n if (def.params !== undefined) {\n const paramsRefIndex = customParamsRef?.(index);\n if (paramsRefIndex === undefined) {\n hasFallback = true;\n break;\n }\n paramsIR = { paramsRefIndex };\n }\n const source = tryCompileEffect(def.fn);\n if (source) {\n checkIRs.push({ kind: \"refine_effect\", source, ...pathIR, ...paramsIR, ...message });\n break;\n }\n // Not inlineable (the predicate captures outer variables). Call the\n // user's own function object through a schema reference instead of\n // delegating: a captured `.refine()` at the root otherwise costs the\n // whole schema its compiled path.\n const refIndex = refineRef?.(index);\n if (refIndex !== undefined) {\n checkIRs.push({ kind: \"refine_effect\", refIndex, ...pathIR, ...paramsIR, ...message });\n } else {\n hasFallback = true;\n }\n break;\n }\n default:\n // Unknown check kind — never drop silently; delegate to Zod.\n hasFallback = true;\n break;\n }\n }\n\n return { checkIRs, hasFallback };\n}\n\nfunction regexFields(\n sourceKey: \"hostname\" | \"protocol\",\n flagsKey: \"hostnameFlags\" | \"protocolFlags\",\n value: unknown,\n): Partial<CheckStringFormat> {\n if (!(value instanceof RegExp)) return {};\n return { [sourceKey]: value.source, ...(value.flags ? { [flagsKey]: value.flags } : {}) };\n}\n\n// ─── Static error message extraction ────────────────────────────────────────\n\nexport type ResolvedMessage =\n | { kind: \"none\" }\n | { kind: \"static\"; message: string }\n | { kind: \"dynamic\" };\n\n/**\n * Classify a check/schema-level `error` param.\n *\n * Zod normalizes `.min(3, \"msg\")` / `{ error: \"msg\" }` / `{ message: \"msg\" }`\n * into an error-map function that ignores its issue argument and returns the\n * string. We call the function with an access-tracking Proxy: if it never\n * inspects the issue, its return value is a constant we can bake into the\n * generated issue. If it reads any issue property (input-dependent message),\n * it is dynamic and the schema must fall back to Zod for exact messages.\n */\nexport function resolveCheckMessage(error: unknown): ResolvedMessage {\n if (error === null || error === undefined) return { kind: \"none\" };\n if (typeof error === \"string\") return { kind: \"static\", message: error };\n if (typeof error !== \"function\") return { kind: \"dynamic\" };\n\n let accessed = false;\n const track = () => {\n accessed = true;\n };\n const probe = new Proxy(\n {},\n {\n get(_t, prop) {\n track();\n // Avoid breaking string coercion / await probing inside error maps.\n if (prop === Symbol.toPrimitive || prop === \"toString\" || prop === \"valueOf\") {\n return () => \"\";\n }\n return undefined;\n },\n has() {\n track();\n return false;\n },\n ownKeys() {\n track();\n return [];\n },\n getOwnPropertyDescriptor() {\n track();\n return undefined;\n },\n },\n );\n\n let result: unknown;\n try {\n result = (error as (issue: unknown) => unknown)(probe);\n } catch {\n return { kind: \"dynamic\" };\n }\n if (accessed) return { kind: \"dynamic\" };\n if (typeof result === \"string\") return { kind: \"static\", message: result };\n if (\n result !== null &&\n typeof result === \"object\" &&\n typeof (result as { message?: unknown }).message === \"string\"\n ) {\n return { kind: \"static\", message: (result as { message: string }).message };\n }\n // Error map deferred (returned undefined) — no custom message.\n if (result === undefined) return { kind: \"none\" };\n return { kind: \"dynamic\" };\n}\n"],"mappings":"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"}
|
|
@@ -38,16 +38,24 @@ const extractIntersection = (def, ctx) => {
|
|
|
38
38
|
* pass alike.
|
|
39
39
|
*
|
|
40
40
|
* Mirrors {@link hasMutation}'s recursion for which node kinds hold children.
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* at
|
|
41
|
+
*
|
|
42
|
+
* zod 4.5 widened the reconciliation to `invalid_key` as well — `collect` takes
|
|
43
|
+
* any `unrecognized_keys` at the root AND any record `invalid_key` one segment
|
|
44
|
+
* deep — so this covers both producers. `object.strict` reaches the
|
|
45
|
+
* `handleCatchall` unrecognized-keys push; a record reaches one or the other
|
|
46
|
+
* depending on its key schema (see the `record` case). A partial record over a
|
|
47
|
+
* finite key set now COMPILES rather than falling back, so the old claim that a
|
|
48
|
+
* compiled `record` could never raise these no longer holds.
|
|
44
49
|
*/
|
|
50
|
+
function isTotalKeySchema(ir) {
|
|
51
|
+
return ir.type === "string" && ir.checks.length === 0 && ir.coerce !== true;
|
|
52
|
+
}
|
|
45
53
|
function canReportUnrecognizedKeys(ir) {
|
|
46
54
|
switch (ir.type) {
|
|
47
55
|
case "object": return ir.strict === true || ir.catchall !== void 0 && canReportUnrecognizedKeys(ir.catchall) || Object.values(ir.properties).some((p) => canReportUnrecognizedKeys(p));
|
|
48
56
|
case "array": return canReportUnrecognizedKeys(ir.element);
|
|
49
57
|
case "tuple": return ir.items.some(canReportUnrecognizedKeys) || ir.rest !== null && canReportUnrecognizedKeys(ir.rest);
|
|
50
|
-
case "record":
|
|
58
|
+
case "record": return ir.enumerableKeys === true || !isTotalKeySchema(ir.keyType) || canReportUnrecognizedKeys(ir.valueType);
|
|
51
59
|
case "set": return canReportUnrecognizedKeys(ir.valueType);
|
|
52
60
|
case "map": return canReportUnrecognizedKeys(ir.keyType) || canReportUnrecognizedKeys(ir.valueType);
|
|
53
61
|
case "catch":
|
|
@@ -71,7 +79,7 @@ function canReportUnrecognizedKeys(ir) {
|
|
|
71
79
|
function mergeDisjointStripObjects(left, right) {
|
|
72
80
|
if (left.type !== "object" || right.type !== "object") return null;
|
|
73
81
|
if (left.stripUnknownKeys !== true || right.stripUnknownKeys !== true) return null;
|
|
74
|
-
if (left.strict === true || right.strict === true || left.catchall !== void 0 || right.catchall !== void 0 || left.checks !== void 0 || right.checks !== void 0
|
|
82
|
+
if (left.strict === true || right.strict === true || left.catchall !== void 0 || right.catchall !== void 0 || left.checks !== void 0 || right.checks !== void 0) return null;
|
|
75
83
|
const leftKeys = Object.keys(left.properties);
|
|
76
84
|
if (leftKeys.some((key) => Object.hasOwn(right.properties, key))) return null;
|
|
77
85
|
const properties = Object.create(null);
|
|
@@ -80,10 +88,17 @@ function mergeDisjointStripObjects(left, right) {
|
|
|
80
88
|
for (const key of rightKeys) properties[key] = right.properties[key];
|
|
81
89
|
const expectedOrder = [...leftKeys, ...rightKeys];
|
|
82
90
|
if (!Object.keys(properties).every((key, index) => key === expectedOrder[index])) return null;
|
|
91
|
+
const merged = (field) => {
|
|
92
|
+
const keys = [...left[field] ?? [], ...right[field] ?? []];
|
|
93
|
+
return keys.length > 0 ? { [field]: keys } : {};
|
|
94
|
+
};
|
|
83
95
|
return {
|
|
84
96
|
type: "object",
|
|
85
97
|
properties,
|
|
86
|
-
stripUnknownKeys: true
|
|
98
|
+
stripUnknownKeys: true,
|
|
99
|
+
...merged("skipAbsentKeys"),
|
|
100
|
+
...merged("suppressAbsentKeys"),
|
|
101
|
+
...merged("nonoptionalKeys")
|
|
87
102
|
};
|
|
88
103
|
}
|
|
89
104
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intersection.js","names":[],"sources":["../../../../src/core/extract/extractors/intersection.ts"],"sourcesContent":["import { hasMutation } from \"../../codegen/context.js\";\nimport type { ObjectIR, SchemaIR } from \"../../types.js\";\nimport type { Extractor } from \"../types.js\";\n\nexport const extractIntersection: Extractor = (def, ctx) => {\n const left = ctx.visit(def.left, \"._zod.def.left\");\n const right = ctx.visit(def.right, \"._zod.def.right\");\n\n // Zod RECONCILES the two sides' unrecognized_keys issues instead of taking\n // their union: handleIntersectionResults pulls every unrecognized_keys issue\n // out of both result lists, records which side named each key, and re-emits\n // one issue holding only the keys \"unrecognized by BOTH sides\". Nothing in a\n // sequential two-pass run pairs those key sets, so a strict side's complaint\n // that zod would cancel becomes a compiled rejection — and on the merged\n // path below it becomes a rejection with an EMPTY issue list, because the\n // cold path re-asks zod, which accepts. Delegate whenever either side can\n // raise the issue at all; this has to precede the merge, whose own strict\n // bail only inspects the two top-level objects.\n if (canReportUnrecognizedKeys(left) || canReportUnrecognizedKeys(right)) {\n return ctx.fallback(\"unsupported\");\n }\n\n // The everyday `.and()` case: two default z.object() shapes with disjoint\n // keys. Zod parses each against the original input, strips each side to its\n // own keys, then merges the two outputs. A single object over the union of\n // those keys has identical acceptance and output semantics. The pristine\n // intersection remains the source of truth for cold-path issues (notably, a\n // non-object input produces one issue per side).\n const merged = mergeDisjointStripObjects(left, right);\n if (merged !== null && ctx.refs) {\n const refIndex = ctx.refs.length;\n ctx.refs.push({ schema: ctx.schema, accessPath: ctx.path });\n return { type: \"zodDelegate\", inner: merged, refIndex };\n }\n\n // Zod validates both sides on the ORIGINAL input and MERGES the results,\n // throwing on unmergable conflicts. The compiled generator runs the sides\n // sequentially on the same value — equivalent only when neither side\n // rewrites it. Mutating sides (coerce, trim, defaults, ...) delegate to Zod.\n if (hasMutation(left) || hasMutation(right)) {\n return ctx.fallback(\"unsupported\");\n }\n return { type: \"intersection\", left, right };\n};\n\n/**\n * Can this side of an intersection produce an `unrecognized_keys` issue?\n *\n * Zod pairs the two sides' unrecognized keys by NAME ONLY, with no regard for\n * the path the complaint came from: `handleIntersectionResults` walks each\n * side's FLAT issue list, unions the bare `iss.keys` strings into one map, and\n * emits `[...unrecKeys].filter(([, f]) => f.l && f.r)` — \"Report only keys\n * unrecognized by BOTH sides\" — under the first LEFT issue's path. A strict\n * object nested in a property or an array element therefore participates in\n * the reconciliation exactly like a top-level one, so a strict object ANYWHERE\n * in a side disqualifies the compiled sequential run and the merged single\n * pass alike.\n *\n * Mirrors {@link hasMutation}'s recursion for which node kinds hold children.\n * Only `object.strict` reaches zod's `handleCatchall` unrecognized-keys push;\n * the sole other producer — a record over a finite key set — already falls back\n * at extraction, so a compiled `record` cannot raise it.\n */\nfunction canReportUnrecognizedKeys(ir: SchemaIR): boolean {\n switch (ir.type) {\n case \"object\":\n return (\n ir.strict === true ||\n (ir.catchall !== undefined && canReportUnrecognizedKeys(ir.catchall)) ||\n Object.values(ir.properties).some((p) => canReportUnrecognizedKeys(p))\n );\n case \"array\":\n return canReportUnrecognizedKeys(ir.element);\n case \"tuple\":\n return (\n ir.items.some(canReportUnrecognizedKeys) ||\n (ir.rest !== null && canReportUnrecognizedKeys(ir.rest))\n );\n case \"record\":\n case \"set\":\n return canReportUnrecognizedKeys(ir.valueType);\n case \"map\":\n return canReportUnrecognizedKeys(ir.keyType) || canReportUnrecognizedKeys(ir.valueType);\n case \"catch\":\n case \"default\":\n case \"effect\":\n case \"nullable\":\n case \"optional\":\n case \"readonly\":\n case \"recursionTarget\":\n case \"zodDelegate\":\n return canReportUnrecognizedKeys(ir.inner);\n case \"discriminatedUnion\":\n case \"union\":\n return ir.options.some(canReportUnrecognizedKeys);\n case \"intersection\":\n return canReportUnrecognizedKeys(ir.left) || canReportUnrecognizedKeys(ir.right);\n case \"pipe\":\n return canReportUnrecognizedKeys(ir.in) || canReportUnrecognizedKeys(ir.out);\n // Zod runs the original sub-schema here, which may be (or contain) a strict\n // object that no IR node records.\n case \"fallback\":\n return true;\n // A back-edge whose target can sit OUTSIDE this side's IR — refId 0 points\n // at the compiled root, which may be an enclosing strict object. Unknowable\n // locally, so assume the worst.\n case \"recursiveRef\":\n return true;\n default:\n // Leaves and predicate nodes: primitives, literals, enums, templateLiteral,\n // stringBool, file, and custom (which emits `custom` issues only).\n return false;\n }\n}\n\n/** Merge only the object-intersection shape whose equivalence is statically obvious. */\nfunction mergeDisjointStripObjects(left: SchemaIR, right: SchemaIR): ObjectIR | null {\n if (left.type !== \"object\" || right.type !== \"object\") return null;\n if (left.stripUnknownKeys !== true || right.stripUnknownKeys !== true) return null;\n if (\n left.strict === true ||\n right.strict === true ||\n left.catchall !== undefined ||\n right.catchall !== undefined ||\n left.checks !== undefined ||\n right.checks !== undefined ||\n left.suppressAbsentKeys !== undefined ||\n right.suppressAbsentKeys !== undefined\n ) {\n return null;\n }\n\n const leftKeys = Object.keys(left.properties);\n if (leftKeys.some((key) => Object.hasOwn(right.properties, key))) return null;\n\n const properties: Record<string, SchemaIR> = Object.create(null) as Record<string, SchemaIR>;\n for (const key of leftKeys) properties[key] = left.properties[key] as SchemaIR;\n const rightKeys = Object.keys(right.properties);\n for (const key of rightKeys) {\n properties[key] = right.properties[key] as SchemaIR;\n }\n // Integer-like keys are enumerated before other strings regardless of\n // insertion order. If that changes left-then-right traversal, a successful\n // parse could observe transform/refine callbacks in a different order.\n const expectedOrder = [...leftKeys, ...rightKeys];\n if (!Object.keys(properties).every((key, index) => key === expectedOrder[index])) return null;\n return { type: \"object\", properties, stripUnknownKeys: true };\n}\n"],"mappings":";;AAIA,MAAa,uBAAkC,KAAK,QAAQ;CAC1D,MAAM,OAAO,IAAI,MAAM,IAAI,MAAM,gBAAgB;CACjD,MAAM,QAAQ,IAAI,MAAM,IAAI,OAAO,iBAAiB;CAYpD,IAAI,0BAA0B,IAAI,KAAK,0BAA0B,KAAK,GACpE,OAAO,IAAI,SAAS,aAAa;CASnC,MAAM,SAAS,0BAA0B,MAAM,KAAK;CACpD,IAAI,WAAW,QAAQ,IAAI,MAAM;EAC/B,MAAM,WAAW,IAAI,KAAK;EAC1B,IAAI,KAAK,KAAK;GAAE,QAAQ,IAAI;GAAQ,YAAY,IAAI;EAAK,CAAC;EAC1D,OAAO;GAAE,MAAM;GAAe,OAAO;GAAQ;EAAS;CACxD;CAMA,IAAI,YAAY,IAAI,KAAK,YAAY,KAAK,GACxC,OAAO,IAAI,SAAS,aAAa;CAEnC,OAAO;EAAE,MAAM;EAAgB;EAAM;CAAM;AAC7C;;;;;;;;;;;;;;;;;;;AAoBA,SAAS,0BAA0B,IAAuB;CACxD,QAAQ,GAAG,MAAX;EACE,KAAK,UACH,OACE,GAAG,WAAW,QACb,GAAG,aAAa,KAAA,KAAa,0BAA0B,GAAG,QAAQ,KACnE,OAAO,OAAO,GAAG,UAAU,CAAC,CAAC,MAAM,MAAM,0BAA0B,CAAC,CAAC;EAEzE,KAAK,SACH,OAAO,0BAA0B,GAAG,OAAO;EAC7C,KAAK,SACH,OACE,GAAG,MAAM,KAAK,yBAAyB,KACtC,GAAG,SAAS,QAAQ,0BAA0B,GAAG,IAAI;EAE1D,KAAK;EACL,KAAK,OACH,OAAO,0BAA0B,GAAG,SAAS;EAC/C,KAAK,OACH,OAAO,0BAA0B,GAAG,OAAO,KAAK,0BAA0B,GAAG,SAAS;EACxF,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,eACH,OAAO,0BAA0B,GAAG,KAAK;EAC3C,KAAK;EACL,KAAK,SACH,OAAO,GAAG,QAAQ,KAAK,yBAAyB;EAClD,KAAK,gBACH,OAAO,0BAA0B,GAAG,IAAI,KAAK,0BAA0B,GAAG,KAAK;EACjF,KAAK,QACH,OAAO,0BAA0B,GAAG,EAAE,KAAK,0BAA0B,GAAG,GAAG;EAG7E,KAAK,YACH,OAAO;EAIT,KAAK,gBACH,OAAO;EACT,SAGE,OAAO;CACX;AACF;;AAGA,SAAS,0BAA0B,MAAgB,OAAkC;CACnF,IAAI,KAAK,SAAS,YAAY,MAAM,SAAS,UAAU,OAAO;CAC9D,IAAI,KAAK,qBAAqB,QAAQ,MAAM,qBAAqB,MAAM,OAAO;CAC9E,IACE,KAAK,WAAW,QAChB,MAAM,WAAW,QACjB,KAAK,aAAa,KAAA,KAClB,MAAM,aAAa,KAAA,KACnB,KAAK,WAAW,KAAA,KAChB,MAAM,WAAW,KAAA,KACjB,KAAK,uBAAuB,KAAA,KAC5B,MAAM,uBAAuB,KAAA,GAE7B,OAAO;CAGT,MAAM,WAAW,OAAO,KAAK,KAAK,UAAU;CAC5C,IAAI,SAAS,MAAM,QAAQ,OAAO,OAAO,MAAM,YAAY,GAAG,CAAC,GAAG,OAAO;CAEzE,MAAM,aAAuC,OAAO,OAAO,IAAI;CAC/D,KAAK,MAAM,OAAO,UAAU,WAAW,OAAO,KAAK,WAAW;CAC9D,MAAM,YAAY,OAAO,KAAK,MAAM,UAAU;CAC9C,KAAK,MAAM,OAAO,WAChB,WAAW,OAAO,MAAM,WAAW;CAKrC,MAAM,gBAAgB,CAAC,GAAG,UAAU,GAAG,SAAS;CAChD,IAAI,CAAC,OAAO,KAAK,UAAU,CAAC,CAAC,OAAO,KAAK,UAAU,QAAQ,cAAc,MAAM,GAAG,OAAO;CACzF,OAAO;EAAE,MAAM;EAAU;EAAY,kBAAkB;CAAK;AAC9D"}
|
|
1
|
+
{"version":3,"file":"intersection.js","names":[],"sources":["../../../../src/core/extract/extractors/intersection.ts"],"sourcesContent":["import { hasMutation } from \"../../codegen/context.js\";\nimport type { ObjectIR, SchemaIR } from \"../../types.js\";\nimport type { Extractor } from \"../types.js\";\n\nexport const extractIntersection: Extractor = (def, ctx) => {\n const left = ctx.visit(def.left, \"._zod.def.left\");\n const right = ctx.visit(def.right, \"._zod.def.right\");\n\n // Zod RECONCILES the two sides' unrecognized_keys issues instead of taking\n // their union: handleIntersectionResults pulls every unrecognized_keys issue\n // out of both result lists, records which side named each key, and re-emits\n // one issue holding only the keys \"unrecognized by BOTH sides\". Nothing in a\n // sequential two-pass run pairs those key sets, so a strict side's complaint\n // that zod would cancel becomes a compiled rejection — and on the merged\n // path below it becomes a rejection with an EMPTY issue list, because the\n // cold path re-asks zod, which accepts. Delegate whenever either side can\n // raise the issue at all; this has to precede the merge, whose own strict\n // bail only inspects the two top-level objects.\n if (canReportUnrecognizedKeys(left) || canReportUnrecognizedKeys(right)) {\n return ctx.fallback(\"unsupported\");\n }\n\n // The everyday `.and()` case: two default z.object() shapes with disjoint\n // keys. Zod parses each against the original input, strips each side to its\n // own keys, then merges the two outputs. A single object over the union of\n // those keys has identical acceptance and output semantics. The pristine\n // intersection remains the source of truth for cold-path issues (notably, a\n // non-object input produces one issue per side).\n const merged = mergeDisjointStripObjects(left, right);\n if (merged !== null && ctx.refs) {\n const refIndex = ctx.refs.length;\n ctx.refs.push({ schema: ctx.schema, accessPath: ctx.path });\n return { type: \"zodDelegate\", inner: merged, refIndex };\n }\n\n // Zod validates both sides on the ORIGINAL input and MERGES the results,\n // throwing on unmergable conflicts. The compiled generator runs the sides\n // sequentially on the same value — equivalent only when neither side\n // rewrites it. Mutating sides (coerce, trim, defaults, ...) delegate to Zod.\n if (hasMutation(left) || hasMutation(right)) {\n return ctx.fallback(\"unsupported\");\n }\n return { type: \"intersection\", left, right };\n};\n\n/**\n * Can this side of an intersection produce an `unrecognized_keys` issue?\n *\n * Zod pairs the two sides' unrecognized keys by NAME ONLY, with no regard for\n * the path the complaint came from: `handleIntersectionResults` walks each\n * side's FLAT issue list, unions the bare `iss.keys` strings into one map, and\n * emits `[...unrecKeys].filter(([, f]) => f.l && f.r)` — \"Report only keys\n * unrecognized by BOTH sides\" — under the first LEFT issue's path. A strict\n * object nested in a property or an array element therefore participates in\n * the reconciliation exactly like a top-level one, so a strict object ANYWHERE\n * in a side disqualifies the compiled sequential run and the merged single\n * pass alike.\n *\n * Mirrors {@link hasMutation}'s recursion for which node kinds hold children.\n *\n * zod 4.5 widened the reconciliation to `invalid_key` as well — `collect` takes\n * any `unrecognized_keys` at the root AND any record `invalid_key` one segment\n * deep — so this covers both producers. `object.strict` reaches the\n * `handleCatchall` unrecognized-keys push; a record reaches one or the other\n * depending on its key schema (see the `record` case). A partial record over a\n * finite key set now COMPILES rather than falling back, so the old claim that a\n * compiled `record` could never raise these no longer holds.\n */\nfunction isTotalKeySchema(ir: SchemaIR): boolean {\n // The same shape `fastRecord` treats as always-satisfied: for-in yields only\n // strings, so an unconstrained, non-coercing string schema accepts them all.\n return ir.type === \"string\" && ir.checks.length === 0 && ir.coerce !== true;\n}\n\nfunction canReportUnrecognizedKeys(ir: SchemaIR): boolean {\n switch (ir.type) {\n case \"object\":\n return (\n ir.strict === true ||\n (ir.catchall !== undefined && canReportUnrecognizedKeys(ir.catchall)) ||\n Object.values(ir.properties).some((p) => canReportUnrecognizedKeys(p))\n );\n case \"array\":\n return canReportUnrecognizedKeys(ir.element);\n case \"tuple\":\n return (\n ir.items.some(canReportUnrecognizedKeys) ||\n (ir.rest !== null && canReportUnrecognizedKeys(ir.rest))\n );\n // A record raises a RECONCILED key issue two ways: `unrecognized_keys` when\n // an enumerable key schema declares the owned set, and `invalid_key` when a\n // constrained key schema rejects one. An unconstrained string key schema\n // accepts every own enumerable string key and so raises neither.\n case \"record\":\n return (\n ir.enumerableKeys === true ||\n !isTotalKeySchema(ir.keyType) ||\n canReportUnrecognizedKeys(ir.valueType)\n );\n case \"set\":\n return canReportUnrecognizedKeys(ir.valueType);\n case \"map\":\n return canReportUnrecognizedKeys(ir.keyType) || canReportUnrecognizedKeys(ir.valueType);\n case \"catch\":\n case \"default\":\n case \"effect\":\n case \"nullable\":\n case \"optional\":\n case \"readonly\":\n case \"recursionTarget\":\n case \"zodDelegate\":\n return canReportUnrecognizedKeys(ir.inner);\n case \"discriminatedUnion\":\n case \"union\":\n return ir.options.some(canReportUnrecognizedKeys);\n case \"intersection\":\n return canReportUnrecognizedKeys(ir.left) || canReportUnrecognizedKeys(ir.right);\n case \"pipe\":\n return canReportUnrecognizedKeys(ir.in) || canReportUnrecognizedKeys(ir.out);\n // Zod runs the original sub-schema here, which may be (or contain) a strict\n // object that no IR node records.\n case \"fallback\":\n return true;\n // A back-edge whose target can sit OUTSIDE this side's IR — refId 0 points\n // at the compiled root, which may be an enclosing strict object. Unknowable\n // locally, so assume the worst.\n case \"recursiveRef\":\n return true;\n default:\n // Leaves and predicate nodes: primitives, literals, enums, templateLiteral,\n // stringBool, file, and custom (which emits `custom` issues only).\n return false;\n }\n}\n\n/** Merge only the object-intersection shape whose equivalence is statically obvious. */\nfunction mergeDisjointStripObjects(left: SchemaIR, right: SchemaIR): ObjectIR | null {\n if (left.type !== \"object\" || right.type !== \"object\") return null;\n if (left.stripUnknownKeys !== true || right.stripUnknownKeys !== true) return null;\n if (\n left.strict === true ||\n right.strict === true ||\n left.catchall !== undefined ||\n right.catchall !== undefined ||\n left.checks !== undefined ||\n right.checks !== undefined\n ) {\n return null;\n }\n\n const leftKeys = Object.keys(left.properties);\n if (leftKeys.some((key) => Object.hasOwn(right.properties, key))) return null;\n\n const properties: Record<string, SchemaIR> = Object.create(null) as Record<string, SchemaIR>;\n for (const key of leftKeys) properties[key] = left.properties[key] as SchemaIR;\n const rightKeys = Object.keys(right.properties);\n for (const key of rightKeys) {\n properties[key] = right.properties[key] as SchemaIR;\n }\n // Integer-like keys are enumerated before other strings regardless of\n // insertion order. If that changes left-then-right traversal, a successful\n // parse could observe transform/refine callbacks in a different order.\n const expectedOrder = [...leftKeys, ...rightKeys];\n if (!Object.keys(properties).every((key, index) => key === expectedOrder[index])) return null;\n // The absent-key lists are per key, and the keys are disjoint, so each side's\n // rules carry over to the merged shape unchanged.\n const merged = (\n field: \"skipAbsentKeys\" | \"suppressAbsentKeys\" | \"nonoptionalKeys\",\n ): Partial<Pick<ObjectIR, typeof field>> => {\n const keys = [...(left[field] ?? []), ...(right[field] ?? [])];\n return keys.length > 0 ? { [field]: keys } : {};\n };\n return {\n type: \"object\",\n properties,\n stripUnknownKeys: true,\n ...merged(\"skipAbsentKeys\"),\n ...merged(\"suppressAbsentKeys\"),\n ...merged(\"nonoptionalKeys\"),\n };\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"object.d.ts","names":[],"sources":["../../../../src/core/extract/extractors/object.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"object.d.ts","names":[],"sources":["../../../../src/core/extract/extractors/object.ts"],"mappings":";;;iBAmCgB,cAAc,KAAK,QAAQ,KAAK,mBAAmB"}
|
|
@@ -1,15 +1,17 @@
|
|
|
1
|
+
import { rejectsUndefined } from "../../codegen/context.js";
|
|
1
2
|
import { customParamsRefRegistrar, extractChecks, payloadCheckRef, refineRefRegistrar } from "../checks.js";
|
|
2
3
|
//#region src/core/extract/extractors/object.ts
|
|
3
4
|
/**
|
|
4
|
-
* Can this property IR raise an issue when its key is
|
|
5
|
-
* sees is `undefined`)? Only those need zod's
|
|
6
|
-
* wrapped around them.
|
|
5
|
+
* Can this property IR raise an issue, or produce a value, when its key is
|
|
6
|
+
* ABSENT (so the value it sees is `undefined`)? Only those need zod's
|
|
7
|
+
* absent-key rules wrapped around them.
|
|
7
8
|
*
|
|
8
9
|
* `optional` and `undefined` cannot: the first short-circuits `undefined`
|
|
9
|
-
* (or forwards it into a `default`, which substitutes without complaint
|
|
10
|
-
* the
|
|
11
|
-
*
|
|
12
|
-
* —
|
|
10
|
+
* (or forwards it into a `default`, which substitutes without complaint — and
|
|
11
|
+
* is then on the "defaulted" rung, where the substitute is wanted), and the
|
|
12
|
+
* second accepts it. `nullable`/`readonly` are transparent to `undefined`, so
|
|
13
|
+
* they inherit the answer. Everything else — a `fallback` delegate above all —
|
|
14
|
+
* is assumed to act.
|
|
13
15
|
*/
|
|
14
16
|
function reportsOnAbsentKey(ir) {
|
|
15
17
|
switch (ir.type) {
|
|
@@ -28,20 +30,34 @@ function extractObject(def, ctx) {
|
|
|
28
30
|
const catchallFlag = valueCatchall ? { catchall: valueCatchall } : {};
|
|
29
31
|
const strictFlag = strict ? { strict: true } : {};
|
|
30
32
|
const stripFlag = def.catchall ? {} : { stripUnknownKeys: true };
|
|
33
|
+
if (Object.getOwnPropertySymbols(def.shape).length > 0) return ctx.fallback("unsupported");
|
|
31
34
|
const properties = Object.create(null);
|
|
35
|
+
const skipAbsentKeys = [];
|
|
32
36
|
const suppressAbsentKeys = [];
|
|
37
|
+
const nonoptionalKeys = [];
|
|
33
38
|
const refMark = ctx.refs?.length ?? 0;
|
|
34
39
|
for (const [key, value] of Object.entries(def.shape)) {
|
|
40
|
+
if (key === "__proto__") {
|
|
41
|
+
properties[key] = { type: "unknown" };
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
35
44
|
const propIR = ctx.visit(value, `.shape[${JSON.stringify(key)}]`);
|
|
36
45
|
properties[key] = propIR;
|
|
37
|
-
|
|
46
|
+
const { optin, optout } = value._zod;
|
|
47
|
+
if (optin === void 0) {
|
|
48
|
+
if (!rejectsUndefined(propIR)) nonoptionalKeys.push(key);
|
|
49
|
+
} else if (optout === "optional" && reportsOnAbsentKey(propIR)) (optin === "optional" ? skipAbsentKeys : suppressAbsentKeys).push(key);
|
|
38
50
|
}
|
|
39
51
|
const propIRs = Object.values(properties);
|
|
40
52
|
if (propIRs.length > 0 && propIRs.every((p) => p.type === "fallback")) {
|
|
41
53
|
if (ctx.refs) ctx.refs.length = refMark;
|
|
42
54
|
return ctx.fallback("coalesced");
|
|
43
55
|
}
|
|
44
|
-
const suppress =
|
|
56
|
+
const suppress = {
|
|
57
|
+
...skipAbsentKeys.length > 0 ? { skipAbsentKeys } : {},
|
|
58
|
+
...suppressAbsentKeys.length > 0 ? { suppressAbsentKeys } : {},
|
|
59
|
+
...nonoptionalKeys.length > 0 ? { nonoptionalKeys } : {}
|
|
60
|
+
};
|
|
45
61
|
if (def.checks && def.checks.length > 0) {
|
|
46
62
|
const { checkIRs, hasFallback } = extractChecks(def.checks, refineRefRegistrar(ctx, def.checks), (index) => payloadCheckRef(ctx, def.checks, index), customParamsRefRegistrar(ctx, def.checks));
|
|
47
63
|
if (hasFallback) return ctx.fallback("refine");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"object.js","names":[],"sources":["../../../../src/core/extract/extractors/object.ts"],"sourcesContent":["import type { RefineEffectCheckIR, SchemaIR, SuperRefineEffectCheckIR } from \"../../types.js\";\nimport {\n customParamsRefRegistrar,\n extractChecks,\n payloadCheckRef,\n refineRefRegistrar,\n} from \"../checks.js\";\nimport type { ExtractorContext, ZodDef } from \"../types.js\";\n\n/**\n * Can this property IR raise an issue when its key is ABSENT (so the value it\n * sees is `undefined`)? Only those need zod's absent-key issue suppression\n * wrapped around them.\n *\n * `optional` and `undefined` cannot: the first short-circuits `undefined`\n * (or forwards it into a `default`, which substitutes without complaint), and\n * the second accepts it. `nullable`/`readonly` are transparent to `undefined`,\n * so they inherit the answer. Everything else — a `fallback` delegate above all\n * — is assumed to report.\n */\nfunction reportsOnAbsentKey(ir: SchemaIR): boolean {\n switch (ir.type) {\n case \"optional\":\n case \"undefined\":\n return false;\n case \"nullable\":\n case \"readonly\":\n return reportsOnAbsentKey(ir.inner);\n default:\n return true;\n }\n}\n\nexport function extractObject(def: ZodDef, ctx: ExtractorContext): SchemaIR {\n // Unknown-key policies:\n // - z.strictObject / .strict() / .catchall(z.never()) reject unknown keys —\n // compiled as a for-in membership pass (ObjectIR.strict). Pass-through is\n // preserved: valid strict data has no extras, so no clone is needed.\n // - z.looseObject (catchall: unknown/any) matches compiled pass-through.\n // - .catchall(schema) validates unknown keys against a schema (ObjectIR.catchall).\n // - default z.object() (no catchall): STRIPS unknown keys, as zod does\n // (ObjectIR.stripUnknownKeys). looseObject/strictObject are unaffected —\n // the first keeps extras by design, the second rejects them outright.\n const catchallType = def.catchall?._zod?.def?.type;\n const strict = catchallType === \"never\";\n const valueCatchall =\n def.catchall && !strict && catchallType !== \"unknown\" && catchallType !== \"any\"\n ? ctx.visit(def.catchall, \"._zod.def.catchall\")\n : undefined;\n // A delegating catchall would need zod per unknown key; hand it the object.\n if (valueCatchall?.type === \"fallback\") return ctx.fallback(\"unsupported\");\n const catchallFlag = valueCatchall ? { catchall: valueCatchall } : {};\n const strictFlag = strict ? { strict: true } : {};\n const stripFlag = def.catchall ? {} : { stripUnknownKeys: true };\n\n // Null-prototype: on a normal object `properties[\"__proto__\"] = ir` sets the\n // prototype instead of defining a key, so a shape declaring `__proto__` (only\n // reachable via a computed key or a dynamically built shape — an object\n // literal's `__proto__:` is the same setter) silently lost that property.\n // Every consumer reads this through Object.keys/values/entries, so dropping\n // the prototype is invisible to them.\n const properties: Record<string, SchemaIR> = Object.create(null) as Record<string, SchemaIR>;\n // Optional-OUT props: mirror zod's handlePropertyResult, which suppresses\n // issues for ABSENT keys (`if (isOptionalOut && !(key in input)) return`).\n // That is how `z.exactOptional()` accepts a missing key while rejecting an\n // explicit `undefined`.\n //\n // Gated on the property IR still being able to REPORT for an absent key, so\n // the common `.optional()` field pays nothing: its compiled form short-\n // circuits `undefined` and produces no issue to suppress. Testing\n // `propIR.type === \"fallback\"` — as this once did — was too narrow by exactly\n // one wrapper: `z.exactOptional(z.string()).nullable()` extracts to\n // `nullable(fallback)`, keeps `optout === \"optional\"` (nullable propagates\n // it), and delegated `undefined` straight into zod's exactOptional, which\n // rejects it. The object then reported an `invalid_type` for a key zod does\n // not look at.\n const suppressAbsentKeys: string[] = [];\n const refMark = ctx.refs?.length ?? 0;\n for (const [key, value] of Object.entries(def.shape)) {\n const propIR = ctx.visit(value, `.shape[${JSON.stringify(key)}]`);\n properties[key] = propIR;\n if (value._zod.optout === \"optional\" && reportsOnAbsentKey(propIR)) {\n suppressAbsentKeys.push(key);\n }\n }\n\n // Fallback coalescing: when EVERY property delegates to Zod, the per-field\n // wrapper (clone + N safeParse calls + issue path rewrites) is pure overhead\n // — measured ~1.4x slower than letting Zod validate the object in one pass.\n // Delegate the whole object instead. The discarded property fallbacks'\n // ref-table entries are rolled back so __rf[] holds only live schemas.\n const propIRs = Object.values(properties);\n if (propIRs.length > 0 && propIRs.every((p) => p.type === \"fallback\")) {\n if (ctx.refs) ctx.refs.length = refMark;\n return ctx.fallback(\"coalesced\");\n }\n const suppress = suppressAbsentKeys.length > 0 ? { suppressAbsentKeys } : {};\n\n if (def.checks && def.checks.length > 0) {\n const { checkIRs, hasFallback } = extractChecks(\n def.checks,\n refineRefRegistrar(ctx, def.checks),\n (index) => payloadCheckRef(ctx, def.checks, index),\n customParamsRefRegistrar(ctx, def.checks),\n );\n if (hasFallback) return ctx.fallback(\"refine\");\n // Object codegen supports the two callback kinds; anything else (overwrite,\n // exotic .check() entries) must not be dropped.\n if (checkIRs.some((c) => c.kind !== \"refine_effect\" && c.kind !== \"super_refine_effect\")) {\n return ctx.fallback(\"unsupported\");\n }\n const refineChecks = checkIRs.filter(\n (c): c is RefineEffectCheckIR | SuperRefineEffectCheckIR =>\n c.kind === \"refine_effect\" || c.kind === \"super_refine_effect\",\n );\n if (refineChecks.length > 0) {\n return {\n type: \"object\",\n properties,\n checks: refineChecks,\n ...strictFlag,\n ...stripFlag,\n ...catchallFlag,\n ...suppress,\n };\n }\n }\n return { type: \"object\", properties, ...strictFlag, ...stripFlag, ...catchallFlag, ...suppress };\n}\n"],"mappings":";;;;;;;;;;;;;AAoBA,SAAS,mBAAmB,IAAuB;CACjD,QAAQ,GAAG,MAAX;EACE,KAAK;EACL,KAAK,aACH,OAAO;EACT,KAAK;EACL,KAAK,YACH,OAAO,mBAAmB,GAAG,KAAK;EACpC,SACE,OAAO;CACX;AACF;AAEA,SAAgB,cAAc,KAAa,KAAiC;CAU1E,MAAM,eAAe,IAAI,UAAU,MAAM,KAAK;CAC9C,MAAM,SAAS,iBAAiB;CAChC,MAAM,gBACJ,IAAI,YAAY,CAAC,UAAU,iBAAiB,aAAa,iBAAiB,QACtE,IAAI,MAAM,IAAI,UAAU,oBAAoB,IAC5C,KAAA;CAEN,IAAI,eAAe,SAAS,YAAY,OAAO,IAAI,SAAS,aAAa;CACzE,MAAM,eAAe,gBAAgB,EAAE,UAAU,cAAc,IAAI,CAAC;CACpE,MAAM,aAAa,SAAS,EAAE,QAAQ,KAAK,IAAI,CAAC;CAChD,MAAM,YAAY,IAAI,WAAW,CAAC,IAAI,EAAE,kBAAkB,KAAK;CAQ/D,MAAM,aAAuC,OAAO,OAAO,IAAI;CAe/D,MAAM,qBAA+B,CAAC;CACtC,MAAM,UAAU,IAAI,MAAM,UAAU;CACpC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,IAAI,KAAK,GAAG;EACpD,MAAM,SAAS,IAAI,MAAM,OAAO,UAAU,KAAK,UAAU,GAAG,EAAE,EAAE;EAChE,WAAW,OAAO;EAClB,IAAI,MAAM,KAAK,WAAW,cAAc,mBAAmB,MAAM,GAC/D,mBAAmB,KAAK,GAAG;CAE/B;CAOA,MAAM,UAAU,OAAO,OAAO,UAAU;CACxC,IAAI,QAAQ,SAAS,KAAK,QAAQ,OAAO,MAAM,EAAE,SAAS,UAAU,GAAG;EACrE,IAAI,IAAI,MAAM,IAAI,KAAK,SAAS;EAChC,OAAO,IAAI,SAAS,WAAW;CACjC;CACA,MAAM,WAAW,mBAAmB,SAAS,IAAI,EAAE,mBAAmB,IAAI,CAAC;CAE3E,IAAI,IAAI,UAAU,IAAI,OAAO,SAAS,GAAG;EACvC,MAAM,EAAE,UAAU,gBAAgB,cAChC,IAAI,QACJ,mBAAmB,KAAK,IAAI,MAAM,IACjC,UAAU,gBAAgB,KAAK,IAAI,QAAQ,KAAK,GACjD,yBAAyB,KAAK,IAAI,MAAM,CAC1C;EACA,IAAI,aAAa,OAAO,IAAI,SAAS,QAAQ;EAG7C,IAAI,SAAS,MAAM,MAAM,EAAE,SAAS,mBAAmB,EAAE,SAAS,qBAAqB,GACrF,OAAO,IAAI,SAAS,aAAa;EAEnC,MAAM,eAAe,SAAS,QAC3B,MACC,EAAE,SAAS,mBAAmB,EAAE,SAAS,qBAC7C;EACA,IAAI,aAAa,SAAS,GACxB,OAAO;GACL,MAAM;GACN;GACA,QAAQ;GACR,GAAG;GACH,GAAG;GACH,GAAG;GACH,GAAG;EACL;CAEJ;CACA,OAAO;EAAE,MAAM;EAAU;EAAY,GAAG;EAAY,GAAG;EAAW,GAAG;EAAc,GAAG;CAAS;AACjG"}
|
|
1
|
+
{"version":3,"file":"object.js","names":[],"sources":["../../../../src/core/extract/extractors/object.ts"],"sourcesContent":["import { rejectsUndefined } from \"../../codegen/context.js\";\nimport type { RefineEffectCheckIR, SchemaIR, SuperRefineEffectCheckIR } from \"../../types.js\";\nimport {\n customParamsRefRegistrar,\n extractChecks,\n payloadCheckRef,\n refineRefRegistrar,\n} from \"../checks.js\";\nimport type { ExtractorContext, ZodDef } from \"../types.js\";\n\n/**\n * Can this property IR raise an issue, or produce a value, when its key is\n * ABSENT (so the value it sees is `undefined`)? Only those need zod's\n * absent-key rules wrapped around them.\n *\n * `optional` and `undefined` cannot: the first short-circuits `undefined`\n * (or forwards it into a `default`, which substitutes without complaint — and\n * is then on the \"defaulted\" rung, where the substitute is wanted), and the\n * second accepts it. `nullable`/`readonly` are transparent to `undefined`, so\n * they inherit the answer. Everything else — a `fallback` delegate above all —\n * is assumed to act.\n */\nfunction reportsOnAbsentKey(ir: SchemaIR): boolean {\n switch (ir.type) {\n case \"optional\":\n case \"undefined\":\n return false;\n case \"nullable\":\n case \"readonly\":\n return reportsOnAbsentKey(ir.inner);\n default:\n return true;\n }\n}\n\nexport function extractObject(def: ZodDef, ctx: ExtractorContext): SchemaIR {\n // Unknown-key policies:\n // - z.strictObject / .strict() / .catchall(z.never()) reject unknown keys —\n // compiled as a for-in membership pass (ObjectIR.strict). Pass-through is\n // preserved: valid strict data has no extras, so no clone is needed.\n // - z.looseObject (catchall: unknown/any) matches compiled pass-through.\n // - .catchall(schema) validates unknown keys against a schema (ObjectIR.catchall).\n // - default z.object() (no catchall): STRIPS unknown keys, as zod does\n // (ObjectIR.stripUnknownKeys). looseObject/strictObject are unaffected —\n // the first keeps extras by design, the second rejects them outright.\n const catchallType = def.catchall?._zod?.def?.type;\n const strict = catchallType === \"never\";\n const valueCatchall =\n def.catchall && !strict && catchallType !== \"unknown\" && catchallType !== \"any\"\n ? ctx.visit(def.catchall, \"._zod.def.catchall\")\n : undefined;\n // A delegating catchall would need zod per unknown key; hand it the object.\n if (valueCatchall?.type === \"fallback\") return ctx.fallback(\"unsupported\");\n const catchallFlag = valueCatchall ? { catchall: valueCatchall } : {};\n const strictFlag = strict ? { strict: true } : {};\n const stripFlag = def.catchall ? {} : { stripUnknownKeys: true };\n\n // zod 4.5 added SYMBOL keys to the shape walk: `normalizeDef` builds\n // `allKeys` as `[...Object.keys(shape), ...getOwnPropertySymbols(shape)]` and\n // every pass — properties, strict, catchall — iterates that. Everything\n // downstream here is keyed by string (`properties` is a string record, the\n // strict pass compares against `Object.keys`, paths carry strings), so a\n // symbol-keyed property would be silently dropped: never validated, never\n // reported absent, never copied to the output. Delegating is the only honest\n // answer until the IR can carry a symbol key.\n if (Object.getOwnPropertySymbols(def.shape).length > 0) {\n return ctx.fallback(\"unsupported\");\n }\n\n // Null-prototype: on a normal object `properties[\"__proto__\"] = ir` sets the\n // prototype instead of defining a key, so a shape declaring `__proto__` (only\n // reachable via a computed key or a dynamically built shape — an object\n // literal's `__proto__:` is the same setter) silently lost that property.\n // Every consumer reads this through Object.keys/values/entries, so dropping\n // the prototype is invisible to them.\n const properties: Record<string, SchemaIR> = Object.create(null) as Record<string, SchemaIR>;\n // Absent keys follow zod's handlePropertyResult, keyed on the property's\n // LIVE `optin`/`optout` rather than on the IR shape:\n //\n // if (!isPresent && isOptionalOut && optin === \"optional\") return;\n // if (result.issues.length) {\n // if (optin !== undefined && isOptionalOut && !isPresent) return;\n // final.issues.push(...prefixIssues(key, result.issues));\n // }\n // if (!isPresent && optin === undefined) {\n // if (!result.issues.length) final.issues.push({ code: \"invalid_type\", expected: \"nonoptional\", ... });\n // return;\n // }\n //\n // The middle rung (\"optional\" in, optional out) contributes nothing for an\n // absent key, so the property is skipped outright; the top rung (\"defaulted\")\n // runs it and swallows a failure; the bottom rung (`optin` undefined) reports\n // the absence itself when the property accepted `undefined`. Each list is\n // gated on the property IR still being able to ACT on an absent key, so the\n // common `.optional()` field pays nothing: its compiled form short-circuits\n // `undefined` and produces neither an issue nor a value. Testing\n // `propIR.type === \"fallback\"` — as this once did — was too narrow by exactly\n // one wrapper: `z.exactOptional(z.string()).nullable()` extracts to\n // `nullable(fallback)`, keeps `optout === \"optional\"` (nullable propagates\n // it), and delegated `undefined` straight into zod's exactOptional, which\n // rejects it. The object then reported an `invalid_type` for a key zod does\n // not look at.\n const skipAbsentKeys: string[] = [];\n const suppressAbsentKeys: string[] = [];\n const nonoptionalKeys: string[] = [];\n const refMark = ctx.refs?.length ?? 0;\n for (const [key, value] of Object.entries(def.shape)) {\n // zod never runs the schema declared under `__proto__` (its shape loop\n // skips the key outright and the output never carries it), so nothing here\n // should either: the key stays in `properties` as a recognized name for the\n // strict pass, holding a placeholder that codegen skips (see\n // parsedProperties) — the real schema is not visited, so it registers no\n // refs and cannot pull the object into a fallback.\n if (key === \"__proto__\") {\n properties[key] = { type: \"unknown\" };\n continue;\n }\n const propIR = ctx.visit(value, `.shape[${JSON.stringify(key)}]`);\n properties[key] = propIR;\n const { optin, optout } = value._zod;\n if (optin === undefined) {\n if (!rejectsUndefined(propIR)) nonoptionalKeys.push(key);\n } else if (optout === \"optional\" && reportsOnAbsentKey(propIR)) {\n (optin === \"optional\" ? skipAbsentKeys : suppressAbsentKeys).push(key);\n }\n }\n\n // Fallback coalescing: when EVERY property delegates to Zod, the per-field\n // wrapper (clone + N safeParse calls + issue path rewrites) is pure overhead\n // — measured ~1.4x slower than letting Zod validate the object in one pass.\n // Delegate the whole object instead. The discarded property fallbacks'\n // ref-table entries are rolled back so __rf[] holds only live schemas.\n const propIRs = Object.values(properties);\n if (propIRs.length > 0 && propIRs.every((p) => p.type === \"fallback\")) {\n if (ctx.refs) ctx.refs.length = refMark;\n return ctx.fallback(\"coalesced\");\n }\n const suppress = {\n ...(skipAbsentKeys.length > 0 ? { skipAbsentKeys } : {}),\n ...(suppressAbsentKeys.length > 0 ? { suppressAbsentKeys } : {}),\n ...(nonoptionalKeys.length > 0 ? { nonoptionalKeys } : {}),\n };\n\n if (def.checks && def.checks.length > 0) {\n const { checkIRs, hasFallback } = extractChecks(\n def.checks,\n refineRefRegistrar(ctx, def.checks),\n (index) => payloadCheckRef(ctx, def.checks, index),\n customParamsRefRegistrar(ctx, def.checks),\n );\n if (hasFallback) return ctx.fallback(\"refine\");\n // Object codegen supports the two callback kinds; anything else (overwrite,\n // exotic .check() entries) must not be dropped.\n if (checkIRs.some((c) => c.kind !== \"refine_effect\" && c.kind !== \"super_refine_effect\")) {\n return ctx.fallback(\"unsupported\");\n }\n const refineChecks = checkIRs.filter(\n (c): c is RefineEffectCheckIR | SuperRefineEffectCheckIR =>\n c.kind === \"refine_effect\" || c.kind === \"super_refine_effect\",\n );\n if (refineChecks.length > 0) {\n return {\n type: \"object\",\n properties,\n checks: refineChecks,\n ...strictFlag,\n ...stripFlag,\n ...catchallFlag,\n ...suppress,\n };\n }\n }\n return { type: \"object\", properties, ...strictFlag, ...stripFlag, ...catchallFlag, ...suppress };\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"optional.d.ts","names":[],"sources":["../../../../src/core/extract/extractors/optional.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"optional.d.ts","names":[],"sources":["../../../../src/core/extract/extractors/optional.ts"],"mappings":";;cAGa,iBAAiB"}
|
|
@@ -1,36 +1,9 @@
|
|
|
1
|
-
import { hasMutation } from "../../codegen/context.js";
|
|
2
1
|
import { innerAppliesDefaultOnUndefined } from "../../codegen/schemas/optional.js";
|
|
3
2
|
//#region src/core/extract/extractors/optional.ts
|
|
4
|
-
/**
|
|
5
|
-
* Running `ir` on `undefined` provably leaves the value `undefined` — whether it
|
|
6
|
-
* accepts (yielding undefined) or rejects (leaving the payload untouched).
|
|
7
|
-
*
|
|
8
|
-
* This is the condition under which the compiled optional's `if (input !==
|
|
9
|
-
* undefined)` short-circuit is EQUIVALENT to zod's optin branch, which runs the
|
|
10
|
-
* inner and then nullifies a failure whose value is still undefined
|
|
11
|
-
* (`handleOptionalResult`). Both end at "success, undefined".
|
|
12
|
-
*
|
|
13
|
-
* `!hasMutation` is exactly the property needed at the bottom of the chain: a
|
|
14
|
-
* node that cannot rewrite values cannot turn `undefined` into one. The three
|
|
15
|
-
* named wrappers recurse because they pass `undefined` straight through
|
|
16
|
-
* (`$ZodNullable` only intercepts `null`, `$ZodReadonly` freezes the result, and
|
|
17
|
-
* a nested `$ZodOptional` short-circuits or forwards). Everything else —
|
|
18
|
-
* `default`, `catch`, an effect, and above all a `fallback` leaf standing in for
|
|
19
|
-
* `z.prefault()` / a pipe / a union — reports mutating and is handled by the
|
|
20
|
-
* caller instead.
|
|
21
|
-
*/
|
|
22
|
-
function passesUndefinedThrough(ir) {
|
|
23
|
-
switch (ir.type) {
|
|
24
|
-
case "optional":
|
|
25
|
-
case "nullable":
|
|
26
|
-
case "readonly": return passesUndefinedThrough(ir.inner);
|
|
27
|
-
default: return !hasMutation(ir);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
3
|
const extractOptional = (def, ctx) => {
|
|
31
4
|
if ((ctx.schema?._zod?.traits)?.has("$ZodExactOptional")) return ctx.fallback("unsupported");
|
|
32
5
|
const inner = ctx.visit(def.innerType, "._zod.def.innerType");
|
|
33
|
-
if (def.innerType?._zod?.optin === "
|
|
6
|
+
if (def.innerType?._zod?.optin === "defaulted" && !innerAppliesDefaultOnUndefined(inner)) return ctx.fallback("unsupported");
|
|
34
7
|
return {
|
|
35
8
|
type: "optional",
|
|
36
9
|
inner
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"optional.js","names":[],"sources":["../../../../src/core/extract/extractors/optional.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"optional.js","names":[],"sources":["../../../../src/core/extract/extractors/optional.ts"],"sourcesContent":["import { innerAppliesDefaultOnUndefined } from \"../../codegen/schemas/optional.js\";\nimport type { Extractor, ZodSchema } from \"../types.js\";\n\nexport const extractOptional: Extractor = (def, ctx) => {\n // z.exactOptional() shares def.type \"optional\" but rejects explicit\n // `undefined` (only a missing key is allowed) — compiled optionals accept\n // undefined, so delegate to Zod. Detectable only via constructor traits.\n const traits = (ctx.schema as ZodSchema | undefined)?._zod?.traits;\n if (traits?.has(\"$ZodExactOptional\")) {\n return ctx.fallback(\"unsupported\");\n }\n const inner = ctx.visit(def.innerType, \"._zod.def.innerType\");\n\n // `$ZodOptional.parse` short-circuits `undefined` unless the inner schema is\n // on the \"defaulted\" rung of `optin`:\n //\n // if (payload.value === undefined) {\n // if (def.innerType._zod.optin !== \"defaulted\") return payload; // short-circuit\n // return handleOptionalResult(payload, innerType._zod.run({ value: undefined, issues: [] }, ctx));\n // }\n // return innerType._zod.run(payload, ctx);\n //\n // Compiled output models the short-circuit, plus the one defaulted case that\n // matters in practice: a `.default()` beneath, which must SEE the undefined so\n // the default fires (`z.string().default(\"d\").optional()` is \"d\", not\n // undefined) and which can never fail on it.\n //\n // Every other defaulted inner — `z.prefault()`, `.catch()` over a default, a\n // union with a defaulted option — runs on a payload of its own, and\n // `handleOptionalResult` then yields `undefined` WITHOUT issues when that run\n // failed (`z.string().min(5).prefault(\"ab\").optional()` parses `undefined` to\n // `undefined`). Those shapes extract to an opaque `fallback` or a union\n // anyway, and the swallow has no counterpart in the compiled optional, so\n // delegate the whole optional to zod rather than emit the inner twice.\n const innerDefaulted = (def.innerType as ZodSchema | undefined)?._zod?.optin === \"defaulted\";\n if (innerDefaulted && !innerAppliesDefaultOnUndefined(inner)) {\n return ctx.fallback(\"unsupported\");\n }\n\n return { type: \"optional\", inner };\n};\n"],"mappings":";;AAGA,MAAa,mBAA8B,KAAK,QAAQ;CAKtD,KADgB,IAAI,QAAkC,MAAM,OAAA,EAChD,IAAI,mBAAmB,GACjC,OAAO,IAAI,SAAS,aAAa;CAEnC,MAAM,QAAQ,IAAI,MAAM,IAAI,WAAW,qBAAqB;CAwB5D,IADwB,IAAI,WAAqC,MAAM,UAAU,eAC3D,CAAC,+BAA+B,KAAK,GACzD,OAAO,IAAI,SAAS,aAAa;CAGnC,OAAO;EAAE,MAAM;EAAY;CAAM;AACnC"}
|
|
@@ -2,13 +2,19 @@ import { hasMutation } from "../../codegen/context.js";
|
|
|
2
2
|
//#region src/core/extract/extractors/record.ts
|
|
3
3
|
function extractRecord(def, ctx) {
|
|
4
4
|
if (!def.valueType) return ctx.fallback("unsupported");
|
|
5
|
-
const
|
|
6
|
-
if (
|
|
5
|
+
const enumerableKeys = def.keyType?._zod?.values !== void 0;
|
|
6
|
+
if (enumerableKeys && !def.partial) return ctx.fallback("unsupported");
|
|
7
|
+
if (def.mode === "loose") return ctx.fallback("unsupported");
|
|
7
8
|
const keyType = ctx.visit(def.keyType, "._zod.def.keyType");
|
|
8
9
|
const valueType = ctx.visit(def.valueType, "._zod.def.valueType");
|
|
9
10
|
if (!isStringShapedKey(keyType)) return ctx.fallback("unsupported");
|
|
10
11
|
if (hasMutation(keyType)) return ctx.fallback("unsupported");
|
|
11
|
-
return {
|
|
12
|
+
return enumerableKeys ? {
|
|
13
|
+
type: "record",
|
|
14
|
+
keyType,
|
|
15
|
+
valueType,
|
|
16
|
+
enumerableKeys: true
|
|
17
|
+
} : {
|
|
12
18
|
type: "record",
|
|
13
19
|
keyType,
|
|
14
20
|
valueType
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"record.js","names":[],"sources":["../../../../src/core/extract/extractors/record.ts"],"sourcesContent":["import { hasMutation } from \"../../codegen/context.js\";\nimport type { SchemaIR } from \"../../types.js\";\nimport type { ExtractorContext, ZodDef } from \"../types.js\";\n\nexport function extractRecord(def: ZodDef, ctx: ExtractorContext): SchemaIR {\n if (!def.valueType) {\n return ctx.fallback(\"unsupported\");\n }\n // Exhaustive-key records: when the key schema exposes a finite value set\n // (z.record(z.enum(...))), Zod requires EVERY key to be present and rejects\n // unrecognized keys. Compiled records only iterate input keys — delegate to\n // Zod. z.partialRecord()
|
|
1
|
+
{"version":3,"file":"record.js","names":[],"sources":["../../../../src/core/extract/extractors/record.ts"],"sourcesContent":["import { hasMutation } from \"../../codegen/context.js\";\nimport type { SchemaIR } from \"../../types.js\";\nimport type { ExtractorContext, ZodDef } from \"../types.js\";\n\nexport function extractRecord(def: ZodDef, ctx: ExtractorContext): SchemaIR {\n if (!def.valueType) {\n return ctx.fallback(\"unsupported\");\n }\n // Exhaustive-key records: when the key schema exposes a finite value set\n // (z.record(z.enum(...))), Zod requires EVERY key to be present and rejects\n // unrecognized keys. Compiled records only iterate input keys — delegate to\n // Zod. z.partialRecord() keeps the key schema (and its values) but sets\n // `def.partial`, which is what zod itself reads to skip the exhaustive walk;\n // the values then only decide the ISSUE a stray key raises (see RecordIR).\n const keyValues = def.keyType?._zod?.values;\n const enumerableKeys = keyValues !== undefined;\n if (enumerableKeys && !def.partial) {\n return ctx.fallback(\"unsupported\");\n }\n // `z.looseRecord()` sets `def.mode = \"loose\"`, which zod checks BEFORE it ever\n // asks whether a key is recognized: an unrecognized key is copied to the\n // output verbatim rather than raising anything. The compiled walk runs the key\n // schema over every key and reports `invalid_key`, so it rejects input zod\n // passes straight through — `z.looseRecord(z.string().regex(/^a/), z.number())`\n // accepts `{b: 1}`. Nothing in RecordIR expresses \"validate these keys, copy\n // the rest\", so delegate.\n if (def.mode === \"loose\") {\n return ctx.fallback(\"unsupported\");\n }\n const keyType = ctx.visit(def.keyType, \"._zod.def.keyType\");\n const valueType = ctx.visit(def.valueType, \"._zod.def.valueType\");\n // Object keys are strings at runtime. Zod coerces/validates numeric-string\n // keys for z.record(z.number(), ...) — the compiled key check would run\n // typeof on the string key and reject everything. Only string-shaped key\n // schemas compile; everything else delegates to Zod.\n if (!isStringShapedKey(keyType)) {\n return ctx.fallback(\"unsupported\");\n }\n // Rewriting key schemas (z.string().trim()/.toUpperCase(), z.url() — whose\n // check writes back the normalized href) RE-HOME the entry in zod: the output\n // carries the parsed key, so `z.record(z.string().toUpperCase(), z.number())`\n // turns `{ a: 1 }` into `{ A: 1 }`. The compiled record cannot follow, on two\n // independent counts.\n //\n // First, the key is validated with `{ input: keyVar, output: keyVar }`, so an\n // overwrite effect reassigns the loop variable in place; the value is then\n // read as `input[keyVar]` — i.e. at the REWRITTEN key, which is absent from\n // the input object — so the value check sees `undefined` and the record is\n // wrongly rejected with `invalid_type` at the rewritten path.\n //\n // Second, even with the lookup pinned to the original key, the walk iterates\n // the input with for-in and returns it BY REFERENCE, so it has nowhere to put\n // the moved entry: producing zod's output would mean building a fresh object\n // under the parsed keys, which also has to resolve collisions (two keys\n // normalizing to one). Neither half is expressible here, so delegate.\n if (hasMutation(keyType)) {\n return ctx.fallback(\"unsupported\");\n }\n return enumerableKeys\n ? { type: \"record\", keyType, valueType, enumerableKeys: true }\n : { type: \"record\", keyType, valueType };\n}\n\nfunction isStringShapedKey(ir: SchemaIR): boolean {\n switch (ir.type) {\n case \"string\":\n case \"templateLiteral\":\n return true;\n case \"enum\":\n return ir.values.every((v) => typeof v === \"string\");\n case \"literal\":\n // `typeof v === \"string\"` (not merely \"has a source form\") is what keeps a\n // SYMBOL key out, and it has to: the compiled record walks its input with\n // `for-in`, which yields string keys only, so a symbol-keyed entry would\n // be invisible to it — while zod's record enumerates the symbol keys too.\n // Only zod can decide such a record, so delegate.\n return ir.values.every((v) => typeof v === \"string\");\n case \"union\":\n return ir.options.every(isStringShapedKey);\n default:\n return false;\n }\n}\n"],"mappings":";;AAIA,SAAgB,cAAc,KAAa,KAAiC;CAC1E,IAAI,CAAC,IAAI,WACP,OAAO,IAAI,SAAS,aAAa;CASnC,MAAM,iBADY,IAAI,SAAS,MAAM,WACA,KAAA;CACrC,IAAI,kBAAkB,CAAC,IAAI,SACzB,OAAO,IAAI,SAAS,aAAa;CASnC,IAAI,IAAI,SAAS,SACf,OAAO,IAAI,SAAS,aAAa;CAEnC,MAAM,UAAU,IAAI,MAAM,IAAI,SAAS,mBAAmB;CAC1D,MAAM,YAAY,IAAI,MAAM,IAAI,WAAW,qBAAqB;CAKhE,IAAI,CAAC,kBAAkB,OAAO,GAC5B,OAAO,IAAI,SAAS,aAAa;CAmBnC,IAAI,YAAY,OAAO,GACrB,OAAO,IAAI,SAAS,aAAa;CAEnC,OAAO,iBACH;EAAE,MAAM;EAAU;EAAS;EAAW,gBAAgB;CAAK,IAC3D;EAAE,MAAM;EAAU;EAAS;CAAU;AAC3C;AAEA,SAAS,kBAAkB,IAAuB;CAChD,QAAQ,GAAG,MAAX;EACE,KAAK;EACL,KAAK,mBACH,OAAO;EACT,KAAK,QACH,OAAO,GAAG,OAAO,OAAO,MAAM,OAAO,MAAM,QAAQ;EACrD,KAAK,WAMH,OAAO,GAAG,OAAO,OAAO,MAAM,OAAO,MAAM,QAAQ;EACrD,KAAK,SACH,OAAO,GAAG,QAAQ,MAAM,iBAAiB;EAC3C,SACE,OAAO;CACX;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tuple.d.ts","names":[],"sources":["../../../../src/core/extract/extractors/tuple.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"tuple.d.ts","names":[],"sources":["../../../../src/core/extract/extractors/tuple.ts"],"mappings":";;;iBAgBgB,aAAa,KAAK,QAAQ,KAAK,mBAAmB"}
|