Repository navigation
fs.constants / os.constants / crypto.constants namespace tables missing #649
Description
Repro
import * as fs from "node:fs";
import * as os from "node:os";
import * as crypto from "node:crypto";
console.log("fs.constants type:", typeof fs.constants);
console.log("fs.constants.F_OK:", fs.constants?.F_OK);
console.log("fs.constants.O_RDONLY:", fs.constants?.O_RDONLY);
console.log("os.constants type:", typeof os.constants);
console.log("os.constants.signals.SIGINT:", os.constants?.signals?.SIGINT);
console.log("os.constants.errno.ENOENT:", os.constants?.errno?.ENOENT);
console.log("os.constants.priority.PRIORITY_NORMAL:", os.constants?.priority?.PRIORITY_NORMAL);
console.log("os.constants.dlopen.RTLD_LAZY:", os.constants?.dlopen?.RTLD_LAZY);
console.log("crypto.constants type:", typeof crypto.constants);
console.log("crypto.constants.RSA_PKCS1_PADDING:", crypto.constants?.RSA_PKCS1_PADDING);Actual (Perry)
fs.constants type: undefined
fs.constants.F_OK: undefined
fs.constants.O_RDONLY: undefined
os.constants type: undefined
os.constants.signals.SIGINT: undefined
os.constants.errno.ENOENT: undefined
os.constants.priority.PRIORITY_NORMAL: undefined
os.constants.dlopen.RTLD_LAZY: undefined
crypto.constants type: undefined
crypto.constants.RSA_PKCS1_PADDING: undefined
Expected (Node)
fs.constants type: object
fs.constants.F_OK: 0
fs.constants.O_RDONLY: 0
os.constants type: object
os.constants.signals.SIGINT: 2
os.constants.errno.ENOENT: 2
os.constants.priority.PRIORITY_NORMAL: 0
os.constants.dlopen.RTLD_LAZY: 5
crypto.constants type: object
crypto.constants.RSA_PKCS1_PADDING: 1
Impact
These three constant tables are ubiquitous in Node ecosystem code:
fs.constants.F_OK/R_OK/W_OK/X_OK— used byfs.access(path, fs.constants.R_OK)and similar permission probes. ~50 entries total (file mode bits, open flags, copyfile flags, dirent type tags).os.constants.signals.*— every signal name to number (SIGINT,SIGTERM,SIGHUP, etc.); used byprocess.kill(pid, signal)andprocess.on('SIGINT')setups. ~30 entries on POSIX.os.constants.errno.*— POSIX errno codes (ENOENT,EACCES, etc.); used to compareerr.errnoin error handlers. ~80 entries.os.constants.priority.*— process priority levels (PRIORITY_NORMAL,PRIORITY_LOW, etc.) used byos.setPriority.os.constants.dlopen.*— dlopen flags (RTLD_LAZY,RTLD_NOW,RTLD_GLOBAL, etc.).crypto.constants.*— RSA padding, SSL_OP_, ENGINE_METHOD_ used by every cipher/sign/verify code path. ~100 entries.
All three are pure static value tables — no runtime logic, no platform branching beyond what's already in crates/perry-runtime. Filling them is mechanical: define the constant maps once, expose them as namespace-keyed lookups when the user imports the parent module.
Currently every consumer that imports node:fs/os/crypto and references .constants.X gets undefined and silently mis-behaves (e.g. fs.access(path, fs.constants.R_OK) becomes fs.access(path, undefined), which Node treats as F_OK).
The signal-name → number table is also blocking test_parity_os from running its full surface — when os.constants.signals is undefined, the next Object.keys(...) call short-circuits the whole test through the catch arm, masking real signal-handling bugs.
Acceptance
The 11-line repro above prints output byte-identical to Node.
Related
Surfaced via test-files/test_parity_fs.ts (fs.constants entirely absent — accounts for ~44 of the test's 409 diff lines), test_parity_os.ts (the test only completes 15 of 60 lines because the constants table is missing), and test_parity_crypto.ts (135 of 199 lines short — partly because crypto.constants.* is referenced throughout the cipher/sign/verify probes).
Activity
fix: closes #645 #648 #649 + partial #650 — refs #647 (v0.5.770)
proggeramlug commentedon May 10, 2026
Fixed in 3ef40d3 (v0.5.770). Added
os_signal_const/os_errno_const/os_priority_const/os_dlopen_const/crypto_constdispatch tables inget_native_module_constant(usinglibc::*on Unix for byte-identical parity with Node, which itself reads them from libuv). Newjs_native_module_property_by_nameruntime helper + codegen arm forPropertyGet { NativeModuleRef, .. }(sinceNativeModuleReflowers to0.0), plus aNATIVE_MODULE_CLASS_IDarm injs_object_get_field_by_namefor chained sub-namespace reads. Manifest entries foros.constants/crypto.constantsregistered. The 11-line repro is byte-identical to Node output.fix(hir,runtime): path.join() zero/one arg + os.constants.UV_UDP_REUS…