Type process.getBuiltinModule results as module namespaces

Under @types/node, process.getBuiltinModule("node:os") types as the
module namespace, which lowered as an unchecked dynamic value and failed.
A string-literal id naming a supported builtin now lowers to the namespace
value an `import * as` binding has, including pi's cast-and-probe spelling
`(process as P).getBuiltinModule?.("node:os") ?? null`; a nullable
namespace binding narrowed present keys the same member tables, and a
namespace is truthy. process.features read as a whole value (`"sea" in
process.features`) is an object holding none of the flags, matching the
existing member reads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Malte Ubl
2026-10-18 15:15:04 -07:00
co-authored by Claude Opus 5.5
parent 5e964965f6
commit 8a02efaa91
8 changed files with 108 additions and 1 deletions
@@ -0,0 +1,28 @@
// process.getBuiltinModule and process.features under @types/node: a
// literal id types as the module namespace, so the result is the same
// object `import * as os` binds (pi's user-agent probe casts process and
// calls the member optionally), and process.features is an object a
// feature probe can test with `in` (pi's single-executable check).
import * as os from "node:os";
import type * as NodeOs from "node:os";
const direct = process.getBuiltinModule("node:os");
console.log("direct", direct.platform() === os.platform(), direct.EOL === os.EOL);
console.log("bare id", process.getBuiltinModule("os").arch() === os.arch());
type ProcessWithOsBuiltinModule = typeof process & {
getBuiltinModule?: (id: "node:os") => typeof NodeOs;
};
function loadNodeOs(): typeof NodeOs | null {
if (typeof process === "undefined" || !(process.versions?.node || process.versions?.bun)) {
return null;
}
return (process as ProcessWithOsBuiltinModule).getBuiltinModule?.("node:os") ?? null;
}
const nodeOs = loadNodeOs();
const agent = nodeOs ? `pi (${nodeOs.platform()} ${nodeOs.release()}; ${nodeOs.arch()})` : "pi (browser)";
console.log("agent", agent === `pi (${os.platform()} ${os.release()}; ${os.arch()})`);
console.log("present", nodeOs !== null, nodeOs ? typeof nodeOs.homedir() : "none");
const isNodeSeaBinary = "sea" in process.features && process.features.sea === true;
console.log("sea", isNodeSeaBinary, "sea" in process.features);
@@ -0,0 +1 @@
{ "type": "module" }
@@ -0,0 +1,7 @@
{
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true
}
}