perry 0.5.1220, macOS arm64, zod ^4.0.0 listed in both perry.compilePackages and perry.allow.compilePackages.
A namespace-style export object comes out partially materialised. Direct access to some members works; Object.keys() on it returns nothing at all; and at least one member (z.coerce) is undefined even though it is accessed statically.
Repro
import { z } from "zod";
function main(): void {
console.log("typeof z :", typeof z);
console.log("typeof z.object :", typeof z.object);
console.log("typeof z.string :", typeof z.string);
console.log("typeof z.coerce :", typeof (z as unknown as { coerce?: unknown }).coerce);
const keys = Object.keys(z as unknown as Record<string, unknown>);
console.log("z key count :", keys.length);
console.log("'coerce' in z :", "coerce" in (z as unknown as object));
console.log("descriptor :", Object.getOwnPropertyDescriptor(z as unknown as object, "coerce"));
}
main();
|
perry 0.5.1220 |
node 26 |
typeof z |
object |
object |
typeof z.object |
function |
function |
typeof z.string |
function |
function |
typeof z.coerce |
undefined |
object |
Object.keys(z).length |
0 |
238 |
"coerce" in z |
false |
true |
getOwnPropertyDescriptor(z, "coerce") |
undefined |
{value, writable, enumerable, configurable} |
Where it bites
const Nested = z.object({ rows: z.array(z.object({ n: z.coerce.number() })).min(1) });
→ TypeError: Cannot read properties of undefined (reading 'number')
That is a plain, statically-visible z.coerce.number() — not dynamic access — so whatever decides which members to retain does not appear to follow nested member expressions through the namespace.
The Object.keys(z).length === 0 result is the part I would point at first: the object exists and answers some property reads, but has no own enumerable properties at all. Any library that iterates its own namespace (or does "x" in ns feature detection) would take a wrong branch silently rather than throwing.
Why it took a while to see
The failure is late and looks like a library bug: six earlier assertions in the same file pass — z.object, z.array, z.string, discriminated unions, .transform, z.enum — so zod is clearly compiled and working. Only coerce is missing, and the resulting TypeError names number, which points at the wrong place.
Environment
- perry 0.5.1220 (current latest release), macOS arm64
- compiled with
PERRY_NO_AUTO_OPTIMIZE=1 (prebuilt stdlib)
- zod ^4.0.0, listed in
perry.compilePackages and perry.allow.compilePackages
perry 0.5.1220, macOS arm64, zod ^4.0.0 listed in both
perry.compilePackagesandperry.allow.compilePackages.A namespace-style export object comes out partially materialised. Direct access to some members works;
Object.keys()on it returns nothing at all; and at least one member (z.coerce) isundefinedeven though it is accessed statically.Repro
typeof zobjectobjecttypeof z.objectfunctionfunctiontypeof z.stringfunctionfunctiontypeof z.coerceundefinedobjectObject.keys(z).length0238"coerce" in zfalsetruegetOwnPropertyDescriptor(z, "coerce")undefined{value, writable, enumerable, configurable}Where it bites
→
TypeError: Cannot read properties of undefined (reading 'number')That is a plain, statically-visible
z.coerce.number()— not dynamic access — so whatever decides which members to retain does not appear to follow nested member expressions through the namespace.The
Object.keys(z).length === 0result is the part I would point at first: the object exists and answers some property reads, but has no own enumerable properties at all. Any library that iterates its own namespace (or does"x" in nsfeature detection) would take a wrong branch silently rather than throwing.Why it took a while to see
The failure is late and looks like a library bug: six earlier assertions in the same file pass —
z.object,z.array,z.string, discriminated unions,.transform,z.enum— so zod is clearly compiled and working. Onlycoerceis missing, and the resultingTypeErrornamesnumber, which points at the wrong place.Environment
PERRY_NO_AUTO_OPTIMIZE=1(prebuilt stdlib)perry.compilePackagesandperry.allow.compilePackages