Merge main

This commit is contained in:
Chris Tate
2026-10-26 11:36:06 -05:00
186 changed files with 13581 additions and 7337 deletions
+2
View File
@@ -122,6 +122,8 @@ jobs:
SCRIPTC_TEST_WORKERS: "1"
- name: Verify mixed-source generated AST getters
run: pnpm generate:ts7-ast --check
- name: Verify the generated IR field readers
run: pnpm generate:ir-fields --check
native_macos:
needs: [validation_scope, lanes]
+13 -1
View File
@@ -283,10 +283,22 @@
},
{
// Ambient declarations describe an existing constructor and its prototype methods.
"files": ["packages/compiler/ambient/scriptc-node-fallback.d.ts"],
"files": [
"packages/compiler/ambient/scriptc-node-fallback.d.ts",
"packages/compiler/ambient/node-fallback/*.d.ts",
],
"rules": {
"typescript/no-unsafe-declaration-merging": "off",
},
},
{
// Each module's fallback declarations are a script that declares the
// module ambiently; it names the files of the modules it uses by
// reference, since a script cannot import them.
"files": ["packages/compiler/ambient/node-fallback/*.d.ts"],
"rules": {
"typescript/triple-slash-reference": "off",
},
},
],
}
@@ -1,4 +1,8 @@
- **Globals:** Stored `globalThis` references support identity and probes for absent capabilities.
Most builtin members require direct global access; access through a stored global object can raise `SC2020`.
In JavaScript, reading a builtin global through the global object by name (`globalThis[name]`, or a stored global object) answers what the global's own spelling reads, and builtin module members that are global objects in Node, such as `require("buffer").atob` and `require("timers").setTimeout`, are the same values.
`WebAssembly` read this way, and builtin members read through a global object that untyped code received, can raise `SC2020`.
`for (key in globalThis)` visits Node's enumerable globals in Node's order, then the program's own global properties.
`navigator` answers Node's `hardwareConcurrency`, `language`, `languages`, `platform`, and `userAgent`, with `language` derived from `LC_ALL`, `LC_MESSAGES`, or `LANG` as Node does.
`navigator.locks` raises `SC2020`, as does `navigator.language` for a locale outside the `language_TERRITORY.codeset@modifier` form, or one whose language or territory needs ICU's tables (a three-letter territory, for example).
`Temporal` is not defined, as in Node.js 24: `typeof Temporal` is `"undefined"`, `globalThis.Temporal` is `undefined`, and any other use throws `ReferenceError: Temporal is not defined`.
Programs that name it type-check against TypeScript's `Temporal` declarations.
@@ -7,6 +7,7 @@ Some builtins can be stored and passed as functions:
In JavaScript, `setTimeout`, `setInterval`, `setImmediate`, `clearTimeout`, `clearInterval`, `clearImmediate`, `queueMicrotask`, and `process.nextTick` are function values wherever they are stored, including conditional and logical expressions and assignments: `typeof` answers `"function"`, identity holds across spellings, and calls schedule as the direct calls do, returning the same numeric timer handles.
Calls through these values bypass `node:test` mock timers.
In JavaScript, `atob`, `btoa`, and `structuredClone` are function values the same way, and calls through them run what the direct calls run.
Other builtin globals stored in JavaScript remain opaque identity values.
In JavaScript, `require("events")` and the `EventEmitter` class assigned to an untyped binding or stored in an object (`ReleaseEmitter = EventEmitter`) are the untyped `EventEmitter` constructor that constructor functions inherit from (see below): `new` through it builds such an emitter object, and identity and `instanceof` agree with the class.
A class can extend `require("events")` directly.
+12 -12
View File
@@ -36684,9 +36684,9 @@
"inherited": true
},
"static": {
"status": "not-implemented",
"detail": "No static compiler lowering is registered for this API yet.",
"verification": "registry-gap"
"status": "supported",
"detail": "Implemented for the documented scriptc module-loader subset.",
"verification": "test-backed"
}
},
{
@@ -36707,9 +36707,9 @@
"inherited": true
},
"static": {
"status": "not-implemented",
"detail": "No static compiler lowering is registered for this API yet.",
"verification": "registry-gap"
"status": "partial",
"detail": "Implemented for the documented scriptc module-loader subset.",
"verification": "test-backed"
}
},
{
@@ -36730,9 +36730,9 @@
"inherited": true
},
"static": {
"status": "not-implemented",
"detail": "No static compiler lowering is registered for this API yet.",
"verification": "registry-gap"
"status": "partial",
"detail": "Implemented for the documented scriptc module-loader subset.",
"verification": "test-backed"
}
},
{
@@ -36776,9 +36776,9 @@
"inherited": true
},
"static": {
"status": "not-implemented",
"detail": "No static compiler lowering is registered for this API yet.",
"verification": "registry-gap"
"status": "supported",
"detail": "Implemented for the documented scriptc module-loader subset.",
"verification": "test-backed"
}
},
{
@@ -52431,6 +52431,7 @@
"tests/corpus/6331-jose-root-entry/main.ts",
"tests/corpus/7743-transform-stream-identity.ts",
"tests/corpus/7747-text-codec-untyped-slot.js",
"tests/corpus/8162-navigator-global.cjs",
"tests/corpus/text-codec-values/main.ts",
"tests/corpus/webassembly-api.mjs",
"tests/corpus/webassembly-dylink/main.mjs",
@@ -54483,6 +54484,7 @@
"status": "partial",
"evidence": "compiler-feature:globals.navigator",
"tests": [
"tests/corpus/8162-navigator-global.cjs",
"tests/fixtures/node-types/navigator.ts",
"tests/harness/project-config.test.ts",
"tests/corpus/6331-jose-root-entry/main.ts"
@@ -54508,8 +54510,12 @@
"inherited": true
},
"static": {
"status": "not-implemented",
"evidence": "compiler-unmatched:globals"
"status": "supported",
"evidence": "compiler-feature:globals.hardwareConcurrency",
"tests": [
"tests/corpus/8162-navigator-global.cjs",
"tests/fixtures/node-types/navigator.ts"
]
},
"anchorSource": "ancestor"
},
@@ -54531,8 +54537,12 @@
"inherited": true
},
"static": {
"status": "not-implemented",
"evidence": "compiler-unmatched:globals"
"status": "partial",
"evidence": "compiler-feature:globals.language",
"tests": [
"tests/corpus/8162-navigator-global.cjs",
"tests/fixtures/node-types/navigator.ts"
]
},
"anchorSource": "ancestor"
},
@@ -54554,8 +54564,12 @@
"inherited": true
},
"static": {
"status": "not-implemented",
"evidence": "compiler-unmatched:globals"
"status": "partial",
"evidence": "compiler-feature:globals.languages",
"tests": [
"tests/corpus/8162-navigator-global.cjs",
"tests/fixtures/node-types/navigator.ts"
]
},
"anchorSource": "ancestor"
},
@@ -54600,8 +54614,12 @@
"inherited": true
},
"static": {
"status": "not-implemented",
"evidence": "compiler-unmatched:globals"
"status": "supported",
"evidence": "compiler-feature:globals.platform",
"tests": [
"tests/corpus/8162-navigator-global.cjs",
"tests/fixtures/node-types/navigator.ts"
]
},
"anchorSource": "ancestor"
},
+3 -1
View File
@@ -222,8 +222,10 @@
{ "chapter": "webcrypto", "signatures": ["Class: `CryptoKey`", "`cryptoKey.algorithm`", "`extractable` Type: {boolean}", "`type` Type: {string} One of `'secret'`, `'private'`, or `'public'`.", "`usages` Type: {string\\[]}"], "status": "partial", "evidence": ["tests/corpus/7124-webcrypto-secret-keys.ts", "tests/corpus/6291-webcrypto-hmac-sign-verify.ts", "tests/corpus/6294-webcrypto-js-values.mjs", "tests/corpus/6295-jose-hs256/main.mjs"] },
{ "chapter": "webcrypto", "signatures": ["Class: `Crypto`"], "status": "partial", "evidence": ["tests/corpus/7120-webcrypto-random-uuid.ts", "tests/corpus/7121-webcrypto-crypto-object.mjs", "tests/corpus/6293-webcrypto-digest.ts"] },
{ "chapter": "webcrypto", "symbols": ["crypto.randomUUID", "crypto.getRandomValues"], "status": "supported", "evidence": ["tests/corpus/7120-webcrypto-random-uuid.ts", "tests/corpus/7121-webcrypto-crypto-object.mjs", "tests/corpus/6204-crypto-get-random-values.ts", "tests/corpus/6205-crypto-get-random-values.js"] },
{ "chapter": "globals", "signatures": ["`navigator`"], "status": "partial", "evidence": ["tests/fixtures/node-types/navigator.ts", "tests/harness/project-config.test.ts", "tests/corpus/6331-jose-root-entry/main.ts"] },
{ "chapter": "globals", "signatures": ["`navigator`"], "status": "partial", "evidence": ["tests/corpus/8162-navigator-global.cjs", "tests/fixtures/node-types/navigator.ts", "tests/harness/project-config.test.ts", "tests/corpus/6331-jose-root-entry/main.ts"] },
{ "chapter": "globals", "signatures": ["`userAgent` Type: {string}"], "status": "supported", "evidence": ["tests/fixtures/node-types/navigator.ts", "tests/harness/project-config.test.ts"] },
{ "chapter": "globals", "signatures": ["`hardwareConcurrency` Type: {number}", "`platform` Type: {string}"], "status": "supported", "evidence": ["tests/corpus/8162-navigator-global.cjs", "tests/fixtures/node-types/navigator.ts"] },
{ "chapter": "globals", "signatures": ["`language` Type: {string}", "`languages` Type: {string\\[]}"], "status": "partial", "evidence": ["tests/corpus/8162-navigator-global.cjs", "tests/fixtures/node-types/navigator.ts"] },
{ "chapter": "globals", "signatures": ["Class: `CryptoKey`", "Class: `SubtleCrypto`"], "status": "partial", "evidence": ["tests/corpus/6291-webcrypto-hmac-sign-verify.ts", "tests/corpus/6292-webcrypto-hmac-errors.ts", "tests/corpus/6293-webcrypto-digest.ts", "tests/corpus/6294-webcrypto-js-values.mjs", "tests/corpus/6295-jose-hs256/main.mjs"] },
{ "chapter": "errors", "parent": "Class: `SystemError`", "symbols": ["code", "errno", "syscall", "path", "dest"], "status": "partial", "evidence": ["tests/corpus/caught-system-error-fields.ts", "tests/fixtures/node-types/terminal-error-fields.ts", "tests/harness/project-config.test.ts"] },
{"chapter": "events", "symbols": ["EventTarget", "eventTarget.addEventListener", "eventTarget.removeEventListener", "eventTarget.dispatchEvent"], "status": "partial", "evidence": ["tests/corpus/5130-event-target-dispatch.js", "tests/corpus/5131-event-target-listener-errors.js", "tests/corpus/5132-event-target-typed.ts", "tests/corpus/5133-abort-signal-event-target.ts", "tests/corpus/5135-event-target-warnings.js", "tests/corpus/5240-event-target-subclass.ts", "tests/corpus/5242-event-target-subclass-hierarchy.ts", "tests/corpus/5243-event-target-subclass-dynamic.ts", "tests/corpus/5244-event-target-subclass.js", "tests/corpus/5245-event-target-subclass-signals.ts", "tests/corpus/5248-event-target-subclass-warnings.ts"]},
+1 -1
View File
@@ -33,7 +33,7 @@ lowering, verifies before and after optimization, and publishes through a
private sibling file so a failed or interrupted request cannot truncate the
requested output.
Repeating `--output` divides the program into that many partitions, which are compiled concurrently and linked together. The helper splits the emitter's LLVM assembly text directly, so partitions also parse concurrently; input outside the emitter's narrow grammar is parsed whole and split with LLVM instead. Internal definitions become hidden so partitions can reference each other. At opt-level 0 each partition is compiled independently. Optimized partitions follow the ThinLTO model: each is simplified and summarized on its own thread, a thin link decides which small functions every partition imports for inlining and which hidden definitions become internal again, and each partition is then optimized and compiled on its own thread. Partition placement depends only on the module and the partition count, so output never depends on the host.
Repeating `--output` divides the program into that many partitions, which are compiled concurrently and linked together. The helper splits the emitter's LLVM assembly text directly, so partitions also parse concurrently; input outside the emitter's narrow grammar is parsed whole and split with LLVM instead. Internal definitions that no other definition reaches are left out of every partition, and the others become hidden so partitions can reference each other. At opt-level 0 each partition is compiled independently. Optimized partitions follow the ThinLTO model: each is simplified and summarized on its own thread, a thin link decides which small functions every partition imports for inlining and which hidden definitions become internal again, and each partition is then optimized and compiled on its own thread. Partition placement depends only on the module and the partition count, so output never depends on the host.
WebAssembly code generation differs from LLVM's default pipeline in two ways that do not change its output. The pass that gives multiple-entry loops a single entry runs only on functions whose control flow is irreducible; its reachability analysis grows with the square of a function's block count even when it has nothing to change. Object emission also leaves out the module pass that collects external symbols for assembly declarations, so each function's machine code is freed once it is printed instead of when the whole module is compiled.
+20
View File
@@ -64,6 +64,9 @@ static constexpr size_t MaxPartitions = 64;
// multiple of the usual size limit, in builds with a profile. See
// limitProfileImports().
static constexpr const char *ProfileImportMultiplier = "2";
// The thin link's size limit, in instructions, for a callee it imports into
// a partition that calls it. See limitImports().
static constexpr const char *ImportInstructionLimit = "40";
std::optional<EmitOptions> parseEmitOptions(int Argc, char **Argv) {
EmitOptions Options;
@@ -173,6 +176,22 @@ void limitProfileImports() {
(void)Limited;
}
// The thin link imports a callee of up to 100 instructions, LLVM's default,
// into every partition that calls it, and each partition then optimizes and
// compiles its imported copies again. On tsc-ts (64 partitions) a limit of 40
// takes about 7% off the helper's time, and the program runs as fast; 30 and
// below made it run 1-8% slower. The limit is a process-wide option; the
// helper sets it once.
void limitImports() {
static const bool Limited = [] {
if (cl::Option *Option =
cl::getRegisteredOptions().lookup("import-instr-limit"))
Option->addOccurrence(0, "import-instr-limit", ImportInstructionLimit);
return true;
}();
(void)Limited;
}
// Drops the names of a partition's struct types before its summary is
// written. Every partition declares the program's types under the same
// names, and a thin link loads the importing partition and the partitions
@@ -852,6 +871,7 @@ compileOptimizedPartitions(const EmitOptions &Options,
std::vector<std::string> Names(Count);
for (size_t I = 0; I < Count; ++I)
Names[I] = "partition-" + std::to_string(I);
limitImports();
if (!Options.ProfileUse.empty())
limitProfileImports();
lto::LTO Link(std::move(Config),
+117 -61
View File
@@ -98,7 +98,7 @@ void placeGroups(std::vector<Symbol> &Symbols, ArrayRef<unsigned> Roots,
StringMap<uint64_t> GroupBytes;
for (unsigned I = 0; I < Symbols.size(); ++I) {
const Symbol &S = Symbols[I];
if (S.Kind != SymbolKind::Function)
if (S.Kind != SymbolKind::Function || S.Partition == NoPartition)
continue;
Total += Bytes(S);
StringRef Group = Symbols[Roots[I]].Group;
@@ -119,8 +119,10 @@ void placeGroups(std::vector<Symbol> &Symbols, ArrayRef<unsigned> Roots,
StringMap<uint64_t> KeyBytes;
for (unsigned I = 0; I < Symbols.size(); ++I) {
const Symbol &S = Symbols[I];
if (S.Kind != SymbolKind::Function || S.Partition == NoPartition)
continue;
StringRef Group = Symbols[Roots[I]].Group;
if (S.Kind != SymbolKind::Function || Group.empty())
if (Group.empty())
continue;
uint64_t Chunks = 1;
while (Chunks * Chunk < GroupBytes[Group])
@@ -534,64 +536,6 @@ std::optional<ProgramPartitions> partitionProgramText(StringRef Text,
}
}
// An internal function referenced by exactly one other function, and by
// no global, is a whole-program inlining candidate; keep it with that
// function so its partition can inline it without importing. A function
// body larger than this is past LLVM's inline cost limit even for the only
// call to a local function, so it is placed by its own name. Otherwise a
// program's module initializers, which only the entry point calls, would
// all share the entry point's partition.
constexpr size_t MaxInlinedBodyBytes = 256 * 1024;
constexpr unsigned Shared = ~1u;
std::vector<unsigned> Referrer(Symbols.size(), NoPartition);
for (unsigned I = 0; I < Symbols.size(); ++I) {
const Symbol &S = Symbols[I];
if (S.Kind == SymbolKind::Declaration)
continue;
for (unsigned Ref : S.Refs) {
if (Ref == I)
continue;
if (S.Kind == SymbolKind::Variable || Referrer[Ref] != NoPartition)
Referrer[Ref] = Shared;
else
Referrer[Ref] = I;
}
}
std::vector<unsigned> Roots(Symbols.size());
for (unsigned I = 0; I < Symbols.size(); ++I) {
if (Symbols[I].Kind != SymbolKind::Function)
continue;
unsigned Root = I;
for (unsigned Steps = 0; Steps < 1024; ++Steps) {
const Symbol &S = Symbols[Root];
unsigned Parent = Referrer[Root];
if (!S.Local || S.Body.size() > MaxInlinedBodyBytes ||
Parent == NoPartition || Parent == Shared ||
Symbols[Parent].Kind != SymbolKind::Function || Parent == I)
break;
Root = Parent;
}
Roots[I] = Root;
Symbols[I].Partition = stableHash(Symbols[Root].Name) % Count;
}
placeGroups(Symbols, Roots, Count);
for (const Symbol &S : Symbols) {
if (S.Kind != SymbolKind::Function)
continue;
for (unsigned Ref : S.Refs)
if (Symbols[Ref].Kind == SymbolKind::Variable &&
Symbols[Ref].Partition == NoPartition)
Symbols[Ref].Partition = S.Partition;
}
for (Symbol &S : Symbols)
if (S.Kind == SymbolKind::Variable && S.Partition == NoPartition)
S.Partition = stableHash(S.Name) % Count;
ProgramPartitions Result;
for (const Symbol &S : Symbols)
if (S.Kind != SymbolKind::Declaration && !S.Local)
Result.PublicSymbols.insert(S.Name);
// Metadata nodes are numbered in the emitter's global order, so one added
// node would renumber the references in every partition. Each partition
// instead numbers the nodes it uses in order of first use, which keeps an
@@ -606,10 +550,122 @@ std::optional<ProgramPartitions> partitionProgramText(StringRef Text,
for (StringRef Line : NamedMetadata)
scanReferences(Line, Index, NamedSymbols, NamedRoots);
// An internal definition that no other definition reaches is left out of
// every partition: the optimizer would delete it, and the emitter writes
// variants of helpers that the program never calls. Leaving them out
// shortens every partition's parse, summary and optimization, and since
// placement ignores them too, an edit that adds one changes no partition.
std::vector<char> Live(Symbols.size(), 0);
{
std::vector<unsigned> Work;
auto Reach = [&](unsigned I) {
if (!Live[I]) {
Live[I] = 1;
Work.push_back(I);
}
};
for (unsigned I = 0; I < Symbols.size(); ++I)
if (Symbols[I].Kind != SymbolKind::Declaration && !Symbols[I].Local)
Reach(I);
for (unsigned I : NamedSymbols)
Reach(I);
for (auto &Entry : Metadata)
for (unsigned I : Entry.second.Symbols)
Reach(I);
while (!Work.empty()) {
unsigned I = Work.back();
Work.pop_back();
for (unsigned Ref : Symbols[I].Refs)
Reach(Ref);
}
}
// An internal definition reached from exactly one other definition is an
// inlining candidate of it, or data only it uses; keep it with that
// definition so its partition can inline it without importing, and so a
// helper an edit adds lands in the partition of the code that uses it. A
// chain through a global continues to the function that uses the global.
// A function body larger than this is past LLVM's inline cost limit even
// for the only call to a local function, so it is placed by its own name.
// A chain stops where it would leave a function's source-module group:
// module initializers, which only the entry point calls, keep their own
// partitions instead of all sharing the entry point's.
constexpr size_t MaxInlinedBodyBytes = 256 * 1024;
constexpr unsigned Shared = ~1u;
std::vector<unsigned> Referrer(Symbols.size(), NoPartition);
for (unsigned I = 0; I < Symbols.size(); ++I) {
const Symbol &S = Symbols[I];
if (S.Kind == SymbolKind::Declaration || !Live[I])
continue;
for (unsigned Ref : S.Refs) {
if (Ref == I)
continue;
Referrer[Ref] = Referrer[Ref] == NoPartition ? I : Shared;
}
}
auto Follows = [&](unsigned Child, unsigned Parent) {
const Symbol &S = Symbols[Child];
if (!S.Local || Parent == NoPartition || Parent == Shared ||
Symbols[Parent].Kind == SymbolKind::Declaration)
return false;
if (S.Kind == SymbolKind::Function &&
(S.Body.size() > MaxInlinedBodyBytes ||
(Symbols[Parent].Kind == SymbolKind::Function && !S.Group.empty() &&
S.Group != Symbols[Parent].Group)))
return false;
return true;
};
std::vector<unsigned> Roots(Symbols.size());
for (unsigned I = 0; I < Symbols.size(); ++I) {
if (Symbols[I].Kind == SymbolKind::Declaration || !Live[I])
continue;
unsigned Root = I;
for (unsigned Steps = 0; Steps < 1024; ++Steps) {
unsigned Parent = Referrer[Root];
if (!Follows(Root, Parent) || Parent == I)
break;
Root = Parent;
}
// A function whose chain ends at a global that is not itself placed
// with a function (a table many functions use) is placed by its own
// name, so the functions of one table spread over the partitions.
if (Symbols[I].Kind == SymbolKind::Function &&
Symbols[Root].Kind != SymbolKind::Function)
Root = I;
Roots[I] = Root;
if (Symbols[I].Kind == SymbolKind::Function)
Symbols[I].Partition = stableHash(Symbols[Root].Name) % Count;
}
placeGroups(Symbols, Roots, Count);
// A global follows the function its chain ends at, then the first
// function that references it, then its own name.
for (unsigned I = 0; I < Symbols.size(); ++I)
if (Symbols[I].Kind == SymbolKind::Variable && Live[I] &&
Symbols[Roots[I]].Kind == SymbolKind::Function)
Symbols[I].Partition = Symbols[Roots[I]].Partition;
for (unsigned I = 0; I < Symbols.size(); ++I) {
const Symbol &S = Symbols[I];
if (S.Kind != SymbolKind::Function || !Live[I])
continue;
for (unsigned Ref : S.Refs)
if (Symbols[Ref].Kind == SymbolKind::Variable &&
Symbols[Ref].Partition == NoPartition)
Symbols[Ref].Partition = S.Partition;
}
for (unsigned I = 0; I < Symbols.size(); ++I)
if (Symbols[I].Kind == SymbolKind::Variable && Live[I] &&
Symbols[I].Partition == NoPartition)
Symbols[I].Partition = stableHash(Symbols[I].Name) % Count;
ProgramPartitions Result;
for (const Symbol &S : Symbols)
if (S.Kind != SymbolKind::Declaration && !S.Local)
Result.PublicSymbols.insert(S.Name);
Result.Sources.resize(Count);
std::vector<std::vector<unsigned>> Owned(Count);
for (unsigned I = 0; I < Symbols.size(); ++I)
if (Symbols[I].Kind != SymbolKind::Declaration)
if (Symbols[I].Kind != SymbolKind::Declaration && Live[I])
Owned[Symbols[I].Partition].push_back(I);
auto Assemble = [&](unsigned P) {
+9 -5
View File
@@ -24,11 +24,15 @@ struct ProgramPartitions {
// more partitions, functions under a "; scriptc-group <key>" line are placed
// with the other functions of that source-module group, which is divided
// into chunks when it is larger than the mean partition or 1 MiB; other
// functions are placed by a stable hash of their name. An internal function referenced by exactly one other function stays
// with that function unless its body is too large to inline, and a global
// stays with the first function that references it. Returns nullopt for any
// construct outside the emitter's narrow top-level grammar; callers then
// split a parsed module.
// functions are placed by a stable hash of their name. An internal
// definition referenced by exactly one other definition stays with it, also
// through a global, unless it is a function whose body is too large to
// inline or that would join a function of another source-module group; a
// global otherwise stays with the first function that references it.
// Internal definitions that no non-internal definition, named metadata or
// metadata node reaches are left out of every partition. Returns nullopt
// for any construct outside the emitter's narrow top-level grammar; callers
// then split a parsed module.
std::optional<ProgramPartitions> partitionProgramText(llvm::StringRef Text,
unsigned Count);
+1
View File
@@ -16,6 +16,7 @@
"test:ts7": "node scripts/test-ts7.mjs",
"test:checker-identity": "node scripts/checker-identity.mjs",
"generate:ts7-ast": "node scripts/generate-ts7-ast-schema.mjs",
"generate:ir-fields": "node scripts/generate-ir-fields.mjs",
"test:fetch-conformance": "vitest run tests/harness/fetch-conformance.test.ts",
"test:test262": "tsx tests/test262/run.ts",
"test:test262:fetch": "node tests/test262/fetch.mjs",
+2 -2
View File
@@ -72,7 +72,7 @@ describe.runIf(supported)("macOS arm64 native link info", () => {
minimum_os: "14.0",
},
program: { object, entry_symbol: "main" },
runtime_abi: { version: 46, marker: "scr_runtime_abi_v46" },
runtime_abi: { version: 47, marker: "scr_runtime_abi_v47" },
runtime_pack: {
kind: "precompiled",
package: "@scriptc/runtime-darwin-arm64",
@@ -165,6 +165,6 @@ describe.runIf(supported)("macOS arm64 native link info", () => {
() => null,
(failure: { stderr?: string }) => failure,
);
expect(error?.stderr).toContain("scr_runtime_abi_v46");
expect(error?.stderr).toContain("scr_runtime_abi_v47");
});
});
@@ -0,0 +1,47 @@
/// <reference path="./assert.d.ts" />
/* node:assert/strict — the same surface with the loose names bound to the
* strict comparisons (equal IS strictEqual, ...), exactly Node's aliasing;
* the module object is callable like assert's. */
declare module "assert/strict" {
function strict(value: unknown, message?: string): void;
namespace strict {
function ok(value: unknown, message?: string): void;
function equal(actual: unknown, expected: unknown, message?: string): void;
function notEqual(actual: unknown, expected: unknown, message?: string): void;
function deepEqual(actual: unknown, expected: unknown, message?: string): void;
function notDeepEqual(actual: unknown, expected: unknown, message?: string): void;
function strictEqual(actual: unknown, expected: unknown, message?: string): void;
function notStrictEqual(actual: unknown, expected: unknown, message?: string): void;
function deepStrictEqual(actual: unknown, expected: unknown, message?: string): void;
function notDeepStrictEqual(actual: unknown, expected: unknown, message?: string): void;
function partialDeepStrictEqual(actual: unknown, expected: unknown, message?: string): void;
function fail(message?: string): never;
function throws(fn: () => unknown, expected?: unknown, message?: string): void;
function doesNotThrow(fn: () => unknown, expected?: unknown, message?: string): void;
function rejects(
fn: (() => Promise<unknown>) | Promise<unknown>,
expected?: unknown,
message?: string,
): Promise<void>;
function doesNotReject(
fn: (() => Promise<unknown>) | Promise<unknown>,
expected?: unknown,
message?: string,
): Promise<void>;
function match(value: string, regExp: RegExp, message?: string): void;
function doesNotMatch(value: string, regExp: RegExp, message?: string): void;
function ifError(value: unknown): void;
const strict: typeof import("assert/strict");
type AssertOptions = import("assert").AssertOptions;
type Assert = import("assert").Assert;
type AssertStrict = import("assert").AssertStrict;
const Assert: typeof import("assert").Assert;
}
export = strict;
}
declare module "node:assert/strict" {
import strict = require("assert/strict");
export = strict;
}
+86
View File
@@ -0,0 +1,86 @@
/// <reference path="./assert-strict.d.ts" />
declare module "assert" {
function assert(value: unknown, message?: string): void;
namespace assert {
interface AssertionErrorOptions {
message?: string | undefined;
actual?: unknown;
expected?: unknown;
operator?: string | undefined;
stackStartFn?: Function | undefined;
diff?: "simple" | "full" | undefined;
}
class AssertionError extends Error {
constructor(options: AssertionErrorOptions);
actual: unknown;
expected: unknown;
generatedMessage: boolean;
code: "ERR_ASSERTION";
operator: string;
}
function ok(value: unknown, message?: string): void;
function strictEqual(actual: unknown, expected: unknown, message?: string): void;
function notStrictEqual(actual: unknown, expected: unknown, message?: string): void;
function deepStrictEqual(actual: unknown, expected: unknown, message?: string): void;
function notDeepStrictEqual(actual: unknown, expected: unknown, message?: string): void;
function partialDeepStrictEqual(actual: unknown, expected: unknown, message?: string): void;
function equal(actual: unknown, expected: unknown, message?: string): void;
function notEqual(actual: unknown, expected: unknown, message?: string): void;
function deepEqual(actual: unknown, expected: unknown, message?: string): void;
function notDeepEqual(actual: unknown, expected: unknown, message?: string): void;
function fail(message?: string): never;
function throws(fn: () => unknown, expected?: unknown, message?: string): void;
function doesNotThrow(fn: () => unknown, expected?: unknown, message?: string): void;
function rejects(
fn: (() => Promise<unknown>) | Promise<unknown>,
expected?: unknown,
message?: string,
): Promise<void>;
function doesNotReject(
fn: (() => Promise<unknown>) | Promise<unknown>,
expected?: unknown,
message?: string,
): Promise<void>;
function match(value: string, regExp: RegExp, message?: string): void;
function doesNotMatch(value: string, regExp: RegExp, message?: string): void;
function ifError(value: unknown): void;
const strict: typeof import("assert/strict");
interface AssertOptions {
diff?: "simple" | "full" | undefined;
strict?: boolean | undefined;
skipPrototype?: boolean | undefined;
}
type AssertMethodNames =
| "deepEqual"
| "deepStrictEqual"
| "doesNotMatch"
| "doesNotReject"
| "doesNotThrow"
| "equal"
| "fail"
| "ifError"
| "match"
| "notDeepEqual"
| "notDeepStrictEqual"
| "notEqual"
| "notStrictEqual"
| "ok"
| "partialDeepStrictEqual"
| "rejects"
| "strictEqual"
| "throws";
interface Assert extends Pick<typeof assert, AssertMethodNames> {}
interface AssertStrict extends Pick<typeof strict, AssertMethodNames> {}
const Assert: {
new (options?: AssertOptions & { strict?: true | undefined }): AssertStrict;
new (options: AssertOptions): Assert;
};
}
export = assert;
}
declare module "node:assert" {
import assert = require("assert");
export = assert;
}
@@ -0,0 +1,41 @@
/* node:async_hooks — declared surface without lowering: async-hook
* instrumentation is guarded behind env flags in harness code; a reached
* use fences at its site. */
declare module "async_hooks" {
/* AsyncLocalStorage: fiber-carried context snapshots — run/getStore/
* exit/enterWith/disable lower onto the runtime's active-slot machinery
* (als.* libCalls); values cross as dyn values. bind and snapshot bind
* functions to the active context; the options carry the store's
* default value and name. */
export interface AsyncLocalStorageOptions {
defaultValue?: any;
name?: string | undefined;
}
export class AsyncLocalStorage<T = any> {
constructor(options?: AsyncLocalStorageOptions);
static bind<Func extends (...args: any[]) => any>(fn: Func): Func;
static snapshot(): <R, TArgs extends any[]>(fn: (...args: TArgs) => R, ...args: TArgs) => R;
readonly name: string;
run<R>(store: T, callback: (...args: any[]) => R, ...args: any[]): R;
exit<R>(callback: (...args: any[]) => R, ...args: any[]): R;
getStore(): T | undefined;
enterWith(store: T): void;
disable(): void;
}
export interface AsyncHook {
enable(): AsyncHook;
disable(): AsyncHook;
}
export function createHook(callbacks: {
init?: (asyncId: number, type: string, triggerAsyncId: number, resource: unknown) => void;
before?: (asyncId: number) => void;
after?: (asyncId: number) => void;
destroy?: (asyncId: number) => void;
}): AsyncHook;
export function executionAsyncId(): number;
export function triggerAsyncId(): number;
}
declare module "node:async_hooks" {
export * from "async_hooks";
}
+23
View File
@@ -0,0 +1,23 @@
/* node:buffer — the module spelling of the Buffer/atob/btoa globals.
* Buffer re-exports the GLOBAL declaration, so an imported binding
* resolves to the same symbol the lowerings already answer for (and
* third-party .d.ts files that `import { Buffer } from "node:buffer"` —
* file-entry-cache in a typed-JS source graph — typecheck). */
declare module "buffer" {
export function isAscii(input: ArrayBuffer | ArrayBufferView): boolean;
export function isUtf8(input: ArrayBuffer | ArrayBufferView): boolean;
export function transcode(source: Uint8Array, fromEncoding: string, toEncoding: string): Buffer;
export function atob(data: string): string;
export function btoa(data: string): string;
export const kMaxLength: number;
export const kStringMaxLength: number;
export const Buffer: BufferConstructor;
export type Blob = globalThis.Blob;
export type File = globalThis.File;
export const Blob: typeof globalThis.Blob;
export const File: typeof globalThis.File;
}
declare module "node:buffer" {
export * from "buffer";
}
@@ -0,0 +1,223 @@
/* node:child_process — the synchronous slice: spawnSync(command, args?)
* runs the child to completion (posix_spawn + waitpid) with stdout/stderr
* captured as utf8 strings. `status` is the exit code, or null when the
* child died to a signal OR could not be spawned at all (nonexistent
* binary, permission denied) — Node reports spawn failure through the
* result's `error` property, which this surface does not carry, and types
* stdout/stderr null there where scriptc returns "" (SEMANTICS.md).
* No shell, no options object: the command runs directly (PATH-searched,
* like Node). */
declare module "child_process" {
export interface SpawnSyncReturns {
readonly status: number | null;
readonly stdout: string;
readonly stderr: string;
/* The termination signal's name ("SIGTERM", ...) when a signal
* killed the child (a timeout's killSignal included), null otherwise
* — Node's shape. */
readonly signal: string | null;
/* Node's spawn-failure carrier: a real Error ("spawnSync <file>
* ENOENT", `code` stamped) when the spawn itself failed — status is
* null and both outputs "" then — or ETIMEDOUT after a timeout kill;
* undefined otherwise. */
readonly error?: Error;
}
/* scriptc always captures utf8 strings; pass { encoding: "utf8" }
* whenever stdout/stderr is READ so plain Node agrees (without it Node
* hands Buffers). The other supported options: timeout (ms — sends
* killSignal at the deadline and the result carries error: ETIMEDOUT +
* the signal, never a throw), killSignal (a signal NAME literal),
* stdio ("ignore"/"inherit"/"pipe" or a 3-tuple of those — captures
* read "" for non-piped streams), env (replaces the child's
* environment), and windowsHide (a POSIX no-op). */
export function spawnSync(
command: string,
args?: string[],
options?: {
encoding?: "utf8" | "utf-8";
timeout?: number;
killSignal?: string;
stdio?: "pipe" | "ignore" | "inherit" | ("pipe" | "ignore" | "inherit")[];
env?: { [k: string]: string | undefined };
windowsHide?: boolean;
},
): SpawnSyncReturns;
/* The asynchronous slice: spawn with stdio "ignore"/"inherit"/fd/"pipe"
* tuples (piped stdin is writable through child.stdin; piped stdout/stderr
* deliver through child.stdout/stderr) and the terminal events. "exit"
* fires once with the exit code, or
* null when the child died to a signal; "error" fires ONLY when the
* child could not be spawned at all (Node's split: a spawn failure
* emits "error" and never "exit"). "close" fires after exit/error and
* every piped stdio handle reaches EOF. Exit/close listeners may take
* Node's code and signal parameters. `on`/`once` return void here (Node
* returns the child; chaining is fenced). The event loop keeps the process
* alive until every spawned child is reaped, like Node — reaping polls at
* loop quiescence (SEMANTICS.md documents the divergence). An "error"
* event with no registered listener prints the error and exits 1,
* exactly the unhandled-'error' EventEmitter behavior. */
export interface ChildStdin {
readonly writable: boolean;
write(chunk: string | Uint8Array): boolean;
end(): void;
destroy(): void;
on(event: "drain" | "finish", listener: () => void): void;
on(event: "error", listener: (err: Error) => void): void;
on(event: string, listener: (...args: any[]) => void): void;
once(event: "drain" | "finish", listener: () => void): void;
once(event: "error", listener: (err: Error) => void): void;
once(event: string, listener: (...args: any[]) => void): void;
}
export interface ChildProcess extends Disposable {
ref(): void;
on(event: "spawn", listener: () => void): this;
once(event: "spawn", listener: () => void): this;
/* The exit listener may also take Node's second parameter — the
* terminating signal's name, null for a normal exit. */
on(event: "exit", listener: (code: number | null, signal: string | null) => void): this;
on(event: "close", listener: (code: number | null, signal: string | null) => void): this;
on(event: "error", listener: (err: Error) => void): this;
once(event: "exit", listener: (code: number | null, signal: string | null) => void): this;
once(event: "close", listener: (code: number | null, signal: string | null) => void): this;
once(event: "error", listener: (err: Error) => void): this;
on(event: "message", listener: (message: any) => void): this;
on(event: "disconnect", listener: () => void): this;
once(event: "message", listener: (message: any) => void): this;
once(event: "disconnect", listener: () => void): this;
/* The lifecycle members, Node's exact shapes: pid is undefined exactly
* when the spawn failed; exitCode is null while running, the code
* after a normal exit, null for a signal death, and -errno once a
* spawn failure's "error" event has fired; kill resolves signal names
* through Node's table (unknown names throw the Unknown signal
* TypeError), answers true when the signal was sent, false once the
* child was reaped (or never spawned), and sets `killed` on success;
* unref() drops the child from the event loop's keep-alive set (it is
* still reaped while the loop runs for other reasons — SEMANTICS.md
* has the reap story). */
readonly pid: number | undefined;
readonly exitCode: number | null;
readonly killed: boolean;
kill(signal?: string | number): boolean;
unref(): void;
readonly connected: boolean;
send(message: object, callback?: (error: Error | null) => void): boolean;
disconnect(): void;
[Symbol.dispose](): void;
/* The piped streams — non-null exactly when the matching stdio slot
* was "pipe" (Node's shape). */
readonly stdin: ChildStdin | null;
readonly stdout: NodeJS.ReadableStream | null;
readonly stderr: NodeJS.ReadableStream | null;
}
export interface SpawnOptions {
/* The 3-tuple form admits number fds in the stdout/stderr slots —
* openSync results dup2'd into the child (the daemon-log idiom
* ["ignore", logFd, logFd]) — and "pipe" there too (child.stdout/
* child.stderr streams); "pipe" in the stdin slot exposes
* child.stdin. */
stdio?: "ignore" | "inherit" | "pipe" | ("ignore" | "inherit" | "pipe" | number)[];
/* detached gives the child its own session and process group
* (POSIX_SPAWN_SETSID); env REPLACES the child environment; cwd
* sets its working directory; windowsHide is a POSIX no-op; shell
* runs the complete command string through /bin/sh or cmd.exe. */
detached?: boolean;
env?: { [k: string]: string | undefined };
cwd?: string;
windowsHide?: boolean;
shell?: boolean;
}
export function spawn(command: string, args?: string[], options?: SpawnOptions): ChildProcess;
export function spawn(command: string, options?: SpawnOptions): ChildProcess;
export function fork(
modulePath: string | URL,
args?: string[],
options?: {
cwd?: string;
env?: { [k: string]: string | undefined };
execArgv?: string[];
silent?: boolean;
stdio?: "pipe" | "ignore" | "inherit" | ("pipe" | "ignore" | "inherit" | "ipc")[];
serialization?: "json" | "advanced";
windowsHide?: boolean;
},
): ChildProcess;
/* The synchronous exec pair: execFileSync runs a file directly (no
* shell), execSync runs a command through /bin/sh -c. Both capture
* stdout as a utf8 string (pass { encoding: "utf8" } so plain Node
* agrees — without it Node returns a Buffer) and THROW on a non-zero
* exit / signal death — the Error's message is Node's "Command failed:
* <cmd>" (with the captured stderr appended). Supported options:
* encoding ("utf8"), cwd, env (REPLACES the child environment), input
* (fed to stdin), timeout (ms — SIGTERMs the child, then throws
* ETIMEDOUT), stdio ("pipe"/"ignore" or a 3-tuple of those — the
* default captures stderr AND echoes it to the parent, like Node), and
* maxBuffer (accepted, not enforced). Node's status/stdout/stderr error
* properties are not carried (SEMANTICS.md). */
interface ExecSyncOptions {
encoding?: "utf8" | "utf-8";
cwd?: string;
env?: { [k: string]: string | undefined };
input?: string;
timeout?: number;
stdio?: "pipe" | "ignore" | "inherit" | ("pipe" | "ignore" | "inherit")[];
maxBuffer?: number;
killSignal?: string;
windowsHide?: boolean;
shell?: boolean;
}
/* The named options interface for the string-encoding exec form —
* @types/node's ExecFileSyncOptionsWithStringEncoding, narrowed to the
* honestly-implemented members. VALUES of this type are a real record
* (type-mapper.ts interns the shape), so a typed const or a runner function's
* options parameter flows to execFileSync at runtime — the windows-ca
* command-runner idiom. */
export interface ExecFileSyncOptionsWithStringEncoding {
encoding: "utf8" | "utf-8";
cwd?: string;
input?: string;
timeout?: number;
stdio?: "pipe" | "ignore" | "inherit" | ("pipe" | "ignore" | "inherit")[];
maxBuffer?: number;
windowsHide?: boolean;
}
export function execFileSync(file: string, args?: string[], options?: ExecSyncOptions): string;
export function execSync(command: string, options?: ExecSyncOptions): string;
export type ExecFileCallback = (error: Error | null, stdout: string, stderr: string) => void;
/* The callback form starts an asynchronous child and captures utf8
* stdout/stderr. The no-callback overload remains for util.promisify's
* static target recognition; reached no-callback calls still fence. */
export function execFile(file: string, callback: ExecFileCallback): ChildProcess;
export function execFile(
file: string,
args: string[] | null,
callback: ExecFileCallback,
): ChildProcess;
export function execFile(
file: string,
args: string[] | null,
options: ExecSyncOptions,
callback: ExecFileCallback,
): ChildProcess;
export function execFile(
file: string,
args?: string[] | null,
options?: ExecSyncOptions,
): unknown;
/* The async shell form. The callback forms lower like execFile's, with
* the command line run by /bin/sh -c, and util.promisify recognizes it
* statically, as it does execFile; reached calls without a callback
* fence at their site. */
export function exec(
command: string,
options?: ExecSyncOptions | ((error: Error | null, stdout: string, stderr: string) => void),
callback?: (error: Error | null, stdout: string, stderr: string) => void,
): ChildProcess;
}
declare module "node:child_process" {
export * from "child_process";
}
+17
View File
@@ -0,0 +1,17 @@
/* node:cluster — the primary-probe slice. A compiled binary never runs
* as a cluster worker (cluster forks the node binary itself — the
* mechanism is na for compiled programs), so isPrimary lowers to `true`
* and isWorker to `false`; everything else is undeclared. */
declare module "cluster" {
const cluster: {
readonly isPrimary: boolean;
readonly isMaster: boolean;
readonly isWorker: boolean;
};
export = cluster;
}
declare module "node:cluster" {
import cluster = require("cluster");
export = cluster;
}
+15
View File
@@ -0,0 +1,15 @@
/* node:constants (deprecated, DEP0008) — one frozen object: os.constants'
* dlopen, errno, priority and signals groups, fs.constants, and
* crypto.constants. A platform's missing constant reads as undefined. */
declare module "constants" {
const constants: { readonly [name: string]: number } & {
readonly defaultCoreCipherList: string;
readonly defaultCipherList: string;
};
export = constants;
}
declare module "node:constants" {
import constants = require("constants");
export = constants;
}
+837
View File
@@ -0,0 +1,837 @@
/// <reference path="./stream.d.ts" />
/// <reference path="./tls.d.ts" />
/* node:crypto: the static hashing/MAC/random/key-derivation utility slice. */
declare module "crypto" {
import { Transform } from "stream";
export type KeyObjectType = "secret" | "public" | "private";
export interface JsonWebKey {
crv?: string;
d?: string;
dp?: string;
dq?: string;
e?: string;
k?: string;
kty?: string;
n?: string;
p?: string;
q?: string;
qi?: string;
x?: string;
y?: string;
[key: string]: unknown;
}
export interface JwkKeyExportOptions {
format: "jwk";
}
export interface KeyExportOptions {
format?: "buffer" | undefined;
}
export type KeyFormat = "pem" | "der" | "jwk";
export interface AsymmetricKeyExportOptions<T extends KeyFormat> {
type: "pkcs1" | "spki" | "pkcs8" | "sec1";
format: T;
cipher?: string | undefined;
passphrase?: string | Buffer | undefined;
}
export interface AsymmetricKeyDetails {
modulusLength?: number;
publicExponent?: bigint;
namedCurve?: string;
}
/* Secret keys, and RSA and EC private and public keys. */
export class KeyObject {
private constructor();
type: KeyObjectType;
symmetricKeySize?: number;
asymmetricKeyType?: string;
asymmetricKeyDetails?: AsymmetricKeyDetails;
export(options: AsymmetricKeyExportOptions<"pem">): string | Buffer;
export(options: AsymmetricKeyExportOptions<"der">): Buffer;
export(options?: KeyExportOptions): Buffer;
export(options: JwkKeyExportOptions): JsonWebKey;
equals(otherKeyObject: KeyObject): boolean;
}
export interface PrivateKeyInput {
key: string | Buffer;
format?: "pem" | "der" | undefined;
type?: "pkcs1" | "pkcs8" | "sec1" | undefined;
encoding?: string | undefined;
passphrase?: string | Buffer | undefined;
}
export interface PublicKeyInput {
key: string | Buffer;
format?: "pem" | "der" | undefined;
type?: "pkcs1" | "spki" | undefined;
encoding?: string | undefined;
}
export interface JsonWebKeyInput {
key: JsonWebKey;
format: "jwk";
}
export function createPrivateKey(
key: PrivateKeyInput | string | Buffer | JsonWebKeyInput,
): KeyObject;
export function createPublicKey(
key: PublicKeyInput | string | Buffer | KeyObject | JsonWebKeyInput,
): KeyObject;
export interface KeyPairKeyObjectResult {
publicKey: KeyObject;
privateKey: KeyObject;
}
export interface KeyPairSyncResult<T1 extends string | Buffer, T2 extends string | Buffer> {
publicKey: T1;
privateKey: T2;
}
export interface RSAKeyPairKeyObjectOptions {
modulusLength: number;
publicExponent?: number | undefined;
}
export interface ECKeyPairKeyObjectOptions {
namedCurve: string;
}
export interface RSAKeyPairOptions<PubF extends KeyFormat, PrivF extends KeyFormat> {
modulusLength: number;
publicExponent?: number | undefined;
publicKeyEncoding: { type: "pkcs1" | "spki"; format: PubF };
privateKeyEncoding: {
type: "pkcs1" | "pkcs8";
format: PrivF;
cipher?: string | undefined;
passphrase?: string | undefined;
};
}
export interface ECKeyPairOptions<PubF extends KeyFormat, PrivF extends KeyFormat> {
namedCurve: string;
publicKeyEncoding: { type: "pkcs1" | "spki"; format: PubF };
privateKeyEncoding: {
type: "sec1" | "pkcs8";
format: PrivF;
cipher?: string | undefined;
passphrase?: string | undefined;
};
}
export function generateKeyPairSync(
type: "rsa",
options: RSAKeyPairOptions<"pem", "pem">,
): KeyPairSyncResult<string, string>;
export function generateKeyPairSync(
type: "rsa",
options: RSAKeyPairOptions<"der", "der">,
): KeyPairSyncResult<Buffer, Buffer>;
export function generateKeyPairSync(
type: "rsa",
options: RSAKeyPairKeyObjectOptions,
): KeyPairKeyObjectResult;
export function generateKeyPairSync(
type: "ec",
options: ECKeyPairOptions<"pem", "pem">,
): KeyPairSyncResult<string, string>;
export function generateKeyPairSync(
type: "ec",
options: ECKeyPairOptions<"der", "der">,
): KeyPairSyncResult<Buffer, Buffer>;
export function generateKeyPairSync(
type: "ec",
options: ECKeyPairKeyObjectOptions,
): KeyPairKeyObjectResult;
export interface ED25519KeyPairOptions<PubF extends KeyFormat, PrivF extends KeyFormat> {
publicKeyEncoding: { type: "spki"; format: PubF };
privateKeyEncoding: {
type: "pkcs8";
format: PrivF;
cipher?: string | undefined;
passphrase?: string | undefined;
};
}
export function generateKeyPairSync(
type: "ed25519",
options: ED25519KeyPairOptions<"pem", "pem">,
): KeyPairSyncResult<string, string>;
export function generateKeyPairSync(
type: "ed25519",
options: ED25519KeyPairOptions<"der", "der">,
): KeyPairSyncResult<Buffer, Buffer>;
export function generateKeyPairSync(type: "ed25519", options?: {}): KeyPairKeyObjectResult;
export interface X25519KeyPairOptions<PubF extends KeyFormat, PrivF extends KeyFormat> {
publicKeyEncoding: { type: "spki"; format: PubF };
privateKeyEncoding: {
type: "pkcs8";
format: PrivF;
cipher?: string | undefined;
passphrase?: string | undefined;
};
}
export function generateKeyPairSync(
type: "x25519",
options: X25519KeyPairOptions<"pem", "pem">,
): KeyPairSyncResult<string, string>;
export function generateKeyPairSync(
type: "x25519",
options: X25519KeyPairOptions<"der", "der">,
): KeyPairSyncResult<Buffer, Buffer>;
export function generateKeyPairSync(type: "x25519", options?: {}): KeyPairKeyObjectResult;
export function generateKeyPair(
type: "x25519",
options: {} | undefined,
callback: (err: Error | null, publicKey: KeyObject, privateKey: KeyObject) => void,
): void;
export function generateKeyPair(
type: "ed25519",
options: {} | undefined,
callback: (err: Error | null, publicKey: KeyObject, privateKey: KeyObject) => void,
): void;
export function generateKeyPair(
type: "rsa",
options: RSAKeyPairKeyObjectOptions,
callback: (err: Error | null, publicKey: KeyObject, privateKey: KeyObject) => void,
): void;
export function generateKeyPair(
type: "ec",
options: ECKeyPairKeyObjectOptions,
callback: (err: Error | null, publicKey: KeyObject, privateKey: KeyObject) => void,
): void;
export interface SigningOptions {
padding?: number | undefined;
saltLength?: number | undefined;
dsaEncoding?: "der" | "ieee-p1363" | undefined;
}
export interface SignPrivateKeyInput extends PrivateKeyInput, SigningOptions {}
export interface SignKeyObjectInput extends SigningOptions {
key: KeyObject;
}
export interface VerifyPublicKeyInput extends PublicKeyInput, SigningOptions {}
export interface VerifyKeyObjectInput extends SigningOptions {
key: KeyObject;
}
export function diffieHellman(options: { privateKey: KeyObject; publicKey: KeyObject }): Buffer;
export function diffieHellman(
options: { privateKey: KeyObject; publicKey: KeyObject },
callback: (err: Error | null, secret: Buffer) => void,
): void;
export function sign(
algorithm: string | null | undefined,
data: ArrayBufferView,
key: KeyObject | string | Buffer | SignKeyObjectInput | SignPrivateKeyInput,
): Buffer;
export function verify(
algorithm: string | null | undefined,
data: ArrayBufferView,
key: KeyObject | string | Buffer | VerifyKeyObjectInput | VerifyPublicKeyInput,
signature: ArrayBufferView,
): boolean;
/* Sign and Verify accumulate update() data; they are not usable as
* writable streams. */
export class Sign {
private constructor();
update(data: string | ArrayBufferView): this;
update(data: string, inputEncoding: Encoding): this;
sign(
privateKey: KeyObject | string | Buffer | SignKeyObjectInput | SignPrivateKeyInput,
): Buffer;
sign(
privateKey: KeyObject | string | Buffer | SignKeyObjectInput | SignPrivateKeyInput,
outputFormat: BinaryToTextEncoding,
): string;
}
export class Verify {
private constructor();
update(data: string | ArrayBufferView): this;
update(data: string, inputEncoding: Encoding): this;
verify(
object: KeyObject | string | Buffer | VerifyKeyObjectInput | VerifyPublicKeyInput,
signature: ArrayBufferView,
): boolean;
verify(
object: KeyObject | string | Buffer | VerifyKeyObjectInput | VerifyPublicKeyInput,
signature: string,
signatureEncoding?: BinaryToTextEncoding,
): boolean;
}
export function createSign(algorithm: string): Sign;
export function createVerify(algorithm: string): Verify;
export function createSecretKey(key: ArrayBufferView | ArrayBufferLike): KeyObject;
export function createSecretKey(key: string, encoding: BufferEncoding): KeyObject;
export function generateKeySync(type: "hmac" | "aes", options: { length: number }): KeyObject;
export function generateKey(
type: "hmac" | "aes",
options: { length: number },
callback: (err: Error | null, key: KeyObject) => void,
): void;
export interface RandomUUIDOptions {
disableEntropyCache?: boolean | undefined;
}
export function randomUUID(options?: RandomUUIDOptions): string;
export function randomUUIDv7(options?: RandomUUIDOptions): string;
export function randomBytes(size: number): Buffer;
export type BinaryToTextEncoding = "base64" | "base64url" | "hex" | "binary";
export type CharacterEncoding = "utf8" | "utf-8" | "utf16le" | "utf-16le" | "latin1";
export type LegacyCharacterEncoding = "ascii" | "binary" | "ucs2" | "ucs-2";
export type Encoding = BinaryToTextEncoding | CharacterEncoding | LegacyCharacterEncoding;
export interface HashOptions {
outputLength?: number | undefined;
}
/* Hash and Hmac are Transforms: written chunks update the digest, which
* the readable side delivers once the writable side ends. */
export interface Hash extends Transform {
copy(options?: HashOptions): Hash;
update(data: string | ArrayBufferView): Hash;
update(data: string, inputEncoding: Encoding): Hash;
digest(): Buffer;
digest(encoding: BinaryToTextEncoding): string;
}
export interface Hmac extends Transform {
update(data: string | ArrayBufferView): Hmac;
update(data: string, inputEncoding: Encoding): Hmac;
digest(): Buffer;
digest(encoding: BinaryToTextEncoding): string;
}
export function createHash(algorithm: string, options?: HashOptions): Hash;
export function createHmac(
algorithm: string,
key: BinaryLike | KeyObject,
options?: { encoding?: BufferEncoding | undefined },
): Hmac;
export interface OneShotDigestOptions {
outputEncoding?: BinaryToTextEncoding | "buffer" | undefined;
outputLength?: number | undefined;
}
export interface OneShotDigestOptionsWithStringEncoding extends OneShotDigestOptions {
outputEncoding?: BinaryToTextEncoding | undefined;
}
export interface OneShotDigestOptionsWithBufferEncoding extends OneShotDigestOptions {
outputEncoding: "buffer";
}
export function hash(
algorithm: string,
data: string | ArrayBufferView,
options?: OneShotDigestOptionsWithStringEncoding | BinaryToTextEncoding,
): string;
export function hash(
algorithm: string,
data: string | ArrayBufferView,
options: OneShotDigestOptionsWithBufferEncoding | "buffer",
): Buffer;
export function hash(
algorithm: string,
data: string | ArrayBufferView,
options: OneShotDigestOptions | BinaryToTextEncoding | "buffer",
): string | Buffer;
export function timingSafeEqual(
a: ArrayBufferView | ArrayBufferLike,
b: ArrayBufferView | ArrayBufferLike,
): boolean;
/* WebCrypto's getRandomValues (also the webcrypto object's): integer
* typed arrays, filled in place and returned. */
type RandomValuesArray =
| Int8Array
| Uint8Array
| Uint8ClampedArray
| Int16Array
| Uint16Array
| Int32Array
| Uint32Array
| BigInt64Array
| BigUint64Array;
export function getRandomValues<T extends RandomValuesArray>(typedArray: T): T;
/* The WebCrypto global's object and its SubtleCrypto. */
export const webcrypto: Crypto;
export const subtle: SubtleCrypto;
export function randomFillSync<T extends ArrayBufferView | ArrayBufferLike>(
buffer: T,
offset?: number,
size?: number,
): T;
export function randomFill<T extends ArrayBufferView | ArrayBufferLike>(
buffer: T,
callback: (err: Error | null, buf: T) => void,
): void;
export function randomFill<T extends ArrayBufferView | ArrayBufferLike>(
buffer: T,
offset: number,
callback: (err: Error | null, buf: T) => void,
): void;
export function randomFill<T extends ArrayBufferView | ArrayBufferLike>(
buffer: T,
offset: number,
size: number,
callback: (err: Error | null, buf: T) => void,
): void;
export function randomInt(max: number): number;
export function randomInt(min: number, max: number): number;
export function randomInt(
max: number,
callback: (err: Error | null, value: number) => void,
): void;
export function randomInt(
min: number,
max: number,
callback: (err: Error | null, value: number) => void,
): void;
export function pbkdf2Sync(
password: string | Uint8Array,
salt: string | Uint8Array,
iterations: number,
keylen: number,
digest: string,
): Buffer;
export function pbkdf2(
password: string | Uint8Array,
salt: string | Uint8Array,
iterations: number,
keylen: number,
digest: string,
callback: (error: Error | null, derivedKey: Buffer) => void,
): void;
export function randomBytes(
size: number,
callback: (error: Error | null, buffer: Buffer) => void,
): void;
export type BinaryLike = string | ArrayBuffer | ArrayBufferView;
/* Cipheriv/Decipheriv: aes-128/192/256 in cbc, ctr, gcm, ccm, ecb, cfb,
* cfb8, cfb1, and ofb modes and their key wraps, des-ede3 and des-ede,
* chacha20, and chacha20-poly1305. Both are Transforms: written chunks go
* through update(), and the end pushes final(). */
export type CipherCCMTypes = "aes-128-ccm" | "aes-192-ccm" | "aes-256-ccm";
export type CipherGCMTypes = "aes-128-gcm" | "aes-192-gcm" | "aes-256-gcm";
export type CipherChaCha20Poly1305Types = "chacha20-poly1305";
export type CipherKey = BinaryLike | KeyObject;
export interface CipherCCMOptions {
authTagLength: number;
}
export interface CipherGCMOptions {
authTagLength?: number;
}
export interface CipherChaCha20Poly1305Options {
authTagLength?: number;
}
export interface Cipheriv extends Transform {
update(data: string | ArrayBufferView): Buffer;
update(data: string, inputEncoding: BufferEncoding): Buffer;
update(data: ArrayBufferView, inputEncoding: undefined, outputEncoding: BufferEncoding): string;
update(
data: string,
inputEncoding: BufferEncoding | undefined,
outputEncoding: BufferEncoding,
): string;
final(): Buffer;
final(outputEncoding: BufferEncoding): string;
setAutoPadding(autoPadding?: boolean): this;
}
export interface CipherCCM extends Cipheriv {
setAAD(buffer: ArrayBufferView, options: { plaintextLength: number }): this;
getAuthTag(): Buffer;
}
export interface CipherGCM extends Cipheriv {
setAAD(buffer: ArrayBufferView, options?: { plaintextLength: number }): this;
getAuthTag(): Buffer;
}
export interface CipherChaCha20Poly1305 extends Cipheriv {
setAAD(buffer: ArrayBufferView, options?: { plaintextLength: number }): this;
getAuthTag(): Buffer;
}
export interface Decipheriv extends Transform {
update(data: ArrayBufferView): Buffer;
update(data: string, inputEncoding: BufferEncoding): Buffer;
update(data: ArrayBufferView, inputEncoding: undefined, outputEncoding: BufferEncoding): string;
update(
data: string,
inputEncoding: BufferEncoding | undefined,
outputEncoding: BufferEncoding,
): string;
final(): Buffer;
final(outputEncoding: BufferEncoding): string;
setAutoPadding(autoPadding?: boolean): this;
}
export interface DecipherCCM extends Decipheriv {
setAuthTag(buffer: ArrayBufferView): this;
setAAD(buffer: ArrayBufferView, options: { plaintextLength: number }): this;
}
export interface DecipherGCM extends Decipheriv {
setAuthTag(buffer: ArrayBufferView): this;
setAAD(buffer: ArrayBufferView, options?: { plaintextLength: number }): this;
}
export interface DecipherChaCha20Poly1305 extends Decipheriv {
setAuthTag(buffer: ArrayBufferView): this;
setAAD(buffer: ArrayBufferView, options?: { plaintextLength: number }): this;
}
export function createCipheriv(
algorithm: CipherCCMTypes,
key: CipherKey,
iv: BinaryLike,
options: CipherCCMOptions,
): CipherCCM;
export function createCipheriv(
algorithm: CipherGCMTypes,
key: CipherKey,
iv: BinaryLike,
options?: CipherGCMOptions,
): CipherGCM;
export function createCipheriv(
algorithm: CipherChaCha20Poly1305Types,
key: CipherKey,
iv: BinaryLike,
options?: CipherChaCha20Poly1305Options,
): CipherChaCha20Poly1305;
export function createCipheriv(
algorithm: string,
key: CipherKey,
iv: BinaryLike | null,
options?: { authTagLength?: number },
): Cipheriv;
export function createDecipheriv(
algorithm: CipherCCMTypes,
key: CipherKey,
iv: BinaryLike,
options: CipherCCMOptions,
): DecipherCCM;
export function createDecipheriv(
algorithm: CipherGCMTypes,
key: CipherKey,
iv: BinaryLike,
options?: CipherGCMOptions,
): DecipherGCM;
export function createDecipheriv(
algorithm: CipherChaCha20Poly1305Types,
key: CipherKey,
iv: BinaryLike,
options?: CipherChaCha20Poly1305Options,
): DecipherChaCha20Poly1305;
export function createDecipheriv(
algorithm: string,
key: CipherKey,
iv: BinaryLike | null,
options?: { authTagLength?: number },
): Decipheriv;
export type ECDHKeyFormat = "compressed" | "uncompressed" | "hybrid";
export class ECDH {
private constructor();
static convertKey(
key: BinaryLike,
curve: string,
inputEncoding?: BinaryToTextEncoding,
outputEncoding?: "latin1" | "hex" | "base64" | "base64url",
format?: ECDHKeyFormat,
): Buffer | string;
generateKeys(): Buffer;
generateKeys(encoding: BinaryToTextEncoding, format?: ECDHKeyFormat): string;
computeSecret(otherPublicKey: ArrayBufferView): Buffer;
computeSecret(otherPublicKey: string, inputEncoding: BinaryToTextEncoding): Buffer;
computeSecret(otherPublicKey: ArrayBufferView, outputEncoding: BinaryToTextEncoding): string;
computeSecret(
otherPublicKey: string,
inputEncoding: BinaryToTextEncoding,
outputEncoding: BinaryToTextEncoding,
): string;
getPrivateKey(): Buffer;
getPrivateKey(encoding: BinaryToTextEncoding): string;
getPublicKey(encoding?: null, format?: ECDHKeyFormat): Buffer;
getPublicKey(encoding: BinaryToTextEncoding, format?: ECDHKeyFormat): string;
setPrivateKey(privateKey: ArrayBufferView): void;
setPrivateKey(privateKey: string, encoding: BinaryToTextEncoding): void;
}
export function createECDH(curveName: string): ECDH;
export class DiffieHellman {
private constructor();
generateKeys(): Buffer;
generateKeys(encoding: BinaryToTextEncoding): string;
computeSecret(
otherPublicKey: ArrayBufferView,
inputEncoding?: null,
outputEncoding?: null,
): Buffer;
computeSecret(
otherPublicKey: string,
inputEncoding: BinaryToTextEncoding,
outputEncoding?: null,
): Buffer;
computeSecret(
otherPublicKey: ArrayBufferView,
inputEncoding: null,
outputEncoding: BinaryToTextEncoding,
): string;
computeSecret(
otherPublicKey: string,
inputEncoding: BinaryToTextEncoding,
outputEncoding: BinaryToTextEncoding,
): string;
getPrime(): Buffer;
getPrime(encoding: BinaryToTextEncoding): string;
getGenerator(): Buffer;
getGenerator(encoding: BinaryToTextEncoding): string;
getPublicKey(): Buffer;
getPublicKey(encoding: BinaryToTextEncoding): string;
getPrivateKey(): Buffer;
getPrivateKey(encoding: BinaryToTextEncoding): string;
setPublicKey(publicKey: ArrayBufferView): void;
setPublicKey(publicKey: string, encoding: BufferEncoding): void;
setPrivateKey(privateKey: ArrayBufferView): void;
setPrivateKey(privateKey: string, encoding: BufferEncoding): void;
verifyError: number;
}
export type DiffieHellmanGroup = Omit<DiffieHellman, "setPublicKey" | "setPrivateKey">;
export function createDiffieHellman(primeLength: number, generator?: number): DiffieHellman;
export function createDiffieHellman(
prime: ArrayBuffer | ArrayBufferView,
generator?: number | ArrayBuffer | ArrayBufferView,
): DiffieHellman;
export function createDiffieHellman(
prime: ArrayBuffer | ArrayBufferView,
generator: string,
generatorEncoding: BinaryToTextEncoding,
): DiffieHellman;
export function createDiffieHellman(
prime: string,
primeEncoding: BinaryToTextEncoding,
generator?: number | ArrayBuffer | ArrayBufferView,
): DiffieHellman;
export function createDiffieHellman(
prime: string,
primeEncoding: BinaryToTextEncoding,
generator: string,
generatorEncoding: BinaryToTextEncoding,
): DiffieHellman;
export function getDiffieHellman(groupName: string): DiffieHellmanGroup;
export function createDiffieHellmanGroup(name: string): DiffieHellmanGroup;
export type LargeNumberLike = ArrayBufferView | SharedArrayBuffer | ArrayBuffer | bigint;
export interface GeneratePrimeOptions {
add?: LargeNumberLike | undefined;
rem?: LargeNumberLike | undefined;
safe?: boolean | undefined;
bigint?: boolean | undefined;
}
export interface GeneratePrimeOptionsBigInt extends GeneratePrimeOptions {
bigint: true;
}
export interface GeneratePrimeOptionsArrayBuffer extends GeneratePrimeOptions {
bigint?: false | undefined;
}
export function generatePrime(
size: number,
callback: (err: Error | null, prime: ArrayBuffer) => void,
): void;
export function generatePrime(
size: number,
options: GeneratePrimeOptionsBigInt,
callback: (err: Error | null, prime: bigint) => void,
): void;
export function generatePrime(
size: number,
options: GeneratePrimeOptionsArrayBuffer,
callback: (err: Error | null, prime: ArrayBuffer) => void,
): void;
export function generatePrime(
size: number,
options: GeneratePrimeOptions,
callback: (err: Error | null, prime: ArrayBuffer | bigint) => void,
): void;
export function generatePrimeSync(size: number): ArrayBuffer;
export function generatePrimeSync(size: number, options: GeneratePrimeOptionsBigInt): bigint;
export function generatePrimeSync(
size: number,
options: GeneratePrimeOptionsArrayBuffer,
): ArrayBuffer;
export function generatePrimeSync(
size: number,
options: GeneratePrimeOptions,
): ArrayBuffer | bigint;
export interface CheckPrimeOptions {
checks?: number | undefined;
}
export function checkPrime(
value: LargeNumberLike,
callback: (err: Error | null, result: boolean) => void,
): void;
export function checkPrime(
value: LargeNumberLike,
options: CheckPrimeOptions,
callback: (err: Error | null, result: boolean) => void,
): void;
export function checkPrimeSync(candidate: LargeNumberLike, options?: CheckPrimeOptions): boolean;
export interface ScryptOptions {
N?: number;
r?: number;
p?: number;
cost?: number;
blockSize?: number;
parallelization?: number;
maxmem?: number;
}
export function scryptSync(
password: BinaryLike,
salt: BinaryLike,
keylen: number,
options?: ScryptOptions,
): Buffer;
export function scrypt(
password: BinaryLike,
salt: BinaryLike,
keylen: number,
callback: (error: Error | null, derivedKey: Buffer) => void,
): void;
export function scrypt(
password: BinaryLike,
salt: BinaryLike,
keylen: number,
options: ScryptOptions,
callback: (error: Error | null, derivedKey: Buffer) => void,
): void;
export function hkdfSync(
digest: string,
ikm: BinaryLike | KeyObject,
salt: BinaryLike,
info: BinaryLike,
keylen: number,
): ArrayBuffer;
export function hkdf(
digest: string,
ikm: BinaryLike | KeyObject,
salt: BinaryLike,
info: BinaryLike,
keylen: number,
callback: (error: Error | null, derivedKey: ArrayBuffer) => void,
): void;
/* The lowered X509Certificate surface is the data-record slice: the
* fields below, all computed at construction (raw decodes the input,
* and the others read raw). validFrom/validTo are Node's
* ASN1_TIME_print strings ("Jul 1 00:00:00 2026 GMT"). Unparseable
* input throws Node's ERR_OSSL_PEM_NO_START_LINE Error. */
export class X509Certificate {
constructor(buffer: Uint8Array | string);
readonly ca: boolean;
readonly fingerprint: string;
readonly fingerprint256: string;
readonly fingerprint512: string;
readonly subject: string;
readonly subjectAltName: string | undefined;
readonly infoAccess: string | undefined;
readonly issuer: string;
readonly raw: Buffer;
readonly serialNumber: string;
readonly signatureAlgorithm: string | undefined;
readonly signatureAlgorithmOid: string;
readonly validFrom: string;
readonly validFromDate: Date;
readonly validTo: string;
readonly validToDate: Date;
/* the certificate's public key; every read answers the same KeyObject */
readonly publicKey: KeyObject;
verify(publicKey: KeyObject): boolean;
checkPrivateKey(privateKey: KeyObject): boolean;
checkIssued(otherCert: X509Certificate): boolean;
checkHost(name: string, options?: X509CheckOptions): string | undefined;
checkEmail(email: string, options?: X509CheckOptions): string | undefined;
checkIP(ip: string): string | undefined;
toLegacyObject(): import("tls").PeerCertificate;
}
export interface X509CheckOptions {
subject?: "always" | "default" | "never";
wildcards?: boolean;
partialWildcards?: boolean;
multiLabelWildcards?: boolean;
singleLabelSubdomains?: boolean;
}
/* The crypto introspection statics: getFips() answers 0 (a compiled
* binary is never a FIPS build), the three name lists bake as fresh
* string[] literals of Node v24 answers, and constants (below) bakes
* per member like http2.constants. */
export function getFips(): 1 | 0;
export function getCiphers(): string[];
export type CipherMode =
| "cbc"
| "ccm"
| "cfb"
| "ctr"
| "ecb"
| "gcm"
| "ocb"
| "ofb"
| "stream"
| "wrap"
| "xts";
export interface CipherInfoOptions {
keyLength?: number | undefined;
ivLength?: number | undefined;
}
export interface CipherInfo {
name: string;
nid: number;
blockSize?: number | undefined;
ivLength?: number | undefined;
keyLength: number;
mode: CipherMode;
}
export function getCipherInfo(
nameOrNid: string | number,
options?: CipherInfoOptions,
): CipherInfo | undefined;
export function getHashes(): string[];
export function getCurves(): string[];
/* Node v24 crypto.constants, literal-typed (the Http2Constants
* stance). Every ACCESS bakes as its literal at lowering; the object
* itself never materializes (bare crypto.constants value uses fence). */
export interface CryptoConstants {
readonly OPENSSL_VERSION_NUMBER: 810549376;
readonly SSL_OP_ALL: 2147485776;
readonly SSL_OP_ALLOW_NO_DHE_KEX: 1024;
readonly SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION: 262144;
readonly SSL_OP_CIPHER_SERVER_PREFERENCE: 4194304;
readonly SSL_OP_CISCO_ANYCONNECT: 32768;
readonly SSL_OP_COOKIE_EXCHANGE: 8192;
readonly SSL_OP_CRYPTOPRO_TLSEXT_BUG: 2147483648;
readonly SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS: 2048;
readonly SSL_OP_LEGACY_SERVER_CONNECT: 4;
readonly SSL_OP_NO_COMPRESSION: 131072;
readonly SSL_OP_NO_ENCRYPT_THEN_MAC: 524288;
readonly SSL_OP_NO_QUERY_MTU: 4096;
readonly SSL_OP_NO_RENEGOTIATION: 1073741824;
readonly SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION: 65536;
readonly SSL_OP_NO_SSLv2: 0;
readonly SSL_OP_NO_SSLv3: 33554432;
readonly SSL_OP_NO_TICKET: 16384;
readonly SSL_OP_NO_TLSv1: 67108864;
readonly SSL_OP_NO_TLSv1_1: 268435456;
readonly SSL_OP_NO_TLSv1_2: 134217728;
readonly SSL_OP_NO_TLSv1_3: 536870912;
readonly SSL_OP_PRIORITIZE_CHACHA: 2097152;
readonly SSL_OP_TLS_ROLLBACK_BUG: 8388608;
readonly ENGINE_METHOD_RSA: 1;
readonly ENGINE_METHOD_DSA: 2;
readonly ENGINE_METHOD_DH: 4;
readonly ENGINE_METHOD_RAND: 8;
readonly ENGINE_METHOD_EC: 2048;
readonly ENGINE_METHOD_CIPHERS: 64;
readonly ENGINE_METHOD_DIGESTS: 128;
readonly ENGINE_METHOD_PKEY_METHS: 512;
readonly ENGINE_METHOD_PKEY_ASN1_METHS: 1024;
readonly ENGINE_METHOD_ALL: 65535;
readonly ENGINE_METHOD_NONE: 0;
readonly DH_CHECK_P_NOT_SAFE_PRIME: 2;
readonly DH_CHECK_P_NOT_PRIME: 1;
readonly DH_UNABLE_TO_CHECK_GENERATOR: 4;
readonly DH_NOT_SUITABLE_GENERATOR: 8;
readonly RSA_PKCS1_PADDING: 1;
readonly RSA_SSLV23_PADDING: 2;
readonly RSA_NO_PADDING: 3;
readonly RSA_PKCS1_OAEP_PADDING: 4;
readonly RSA_X931_PADDING: 5;
readonly RSA_PKCS1_PSS_PADDING: 6;
readonly RSA_PSS_SALTLEN_DIGEST: -1;
readonly RSA_PSS_SALTLEN_MAX_SIGN: -2;
readonly RSA_PSS_SALTLEN_AUTO: -2;
readonly defaultCoreCipherList: "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA256:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!SRP:!CAMELLIA";
readonly TLS1_VERSION: 769;
readonly TLS1_1_VERSION: 770;
readonly TLS1_2_VERSION: 771;
readonly TLS1_3_VERSION: 772;
readonly POINT_CONVERSION_COMPRESSED: 2;
readonly POINT_CONVERSION_UNCOMPRESSED: 4;
readonly POINT_CONVERSION_HYBRID: 6;
readonly defaultCipherList: "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA256:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!SRP:!CAMELLIA";
}
export const constants: CryptoConstants;
}
declare module "node:crypto" {
export * from "crypto";
}
+58
View File
@@ -0,0 +1,58 @@
/* node:dgram — UDP sockets over the event loop (scr_dgram.c, linked only
* into using binaries). createSocket takes "udp4" or the
* { type: "udp4", reuseAddr? } literal; bind/connect bind NOW and defer
* 'listening'/'connect' to the next loop turn; send takes one string or
* Buffer datagram with an explicit port + address (the connected-send
* and callback forms have no lowering); address() returns the real
* {address, family, port} record and THROWS "Not running" before bind,
* like Node. The socket options throw Node's errors (EBADF before bind);
* add/dropMembership before bind is refused at run time (SC2020). 'error'
* with no listener exits 1 (the unhandled EventEmitter 'error' behavior). */
declare module "dgram" {
export interface AddressInfo {
address: string;
family: string;
port: number;
}
export interface RemoteInfo {
address: string;
family: string;
port: number;
size: number;
}
export interface Socket {
bind(port: number, address?: string, callback?: () => void): void;
connect(port: number, address?: string, callback?: () => void): void;
send(msg: string | Uint8Array, port: number, address: string): void;
address(): AddressInfo;
close(callback?: () => void): void;
disconnect(): void;
unref(): void;
ref(): void;
setBroadcast(flag: boolean): void;
setTTL(ttl: number): number;
setMulticastTTL(ttl: number): number;
setMulticastLoopback(flag: boolean): boolean;
setMulticastInterface(multicastInterface: string): void;
addMembership(multicastAddress: string, multicastInterface?: string): void;
dropMembership(multicastAddress: string, multicastInterface?: string): void;
getRecvBufferSize(): number;
getSendBufferSize(): number;
setRecvBufferSize(size: number): void;
setSendBufferSize(size: number): void;
getSendQueueSize(): number;
getSendQueueCount(): number;
on(event: "message", listener: (msg: Buffer, rinfo: RemoteInfo) => void): void;
on(event: "listening" | "close" | "connect", listener: () => void): void;
on(event: "error", listener: (err: Error) => void): void;
once(event: "message", listener: (msg: Buffer, rinfo: RemoteInfo) => void): void;
once(event: "listening" | "close" | "connect", listener: () => void): void;
once(event: "error", listener: (err: Error) => void): void;
}
export function createSocket(type: "udp4"): Socket;
export function createSocket(options: { type: "udp4"; reuseAddr?: boolean }): Socket;
}
declare module "node:dgram" {
export * from "dgram";
}
@@ -0,0 +1,87 @@
/// <reference path="./async_hooks.d.ts" />
/* node:diagnostics_channel — the in-process pub/sub core: named channels,
* subscribe/unsubscribe (module-level and channel-level), publish with any
* dyn-convertible message. Channel values are f64 handles into the
* runtime's channel registry (the readline.Interface precedent — channels
* are process-lived in Node too, its WeakRef machinery aside).
* The store-binding surface (bindStore/unbindStore/runStores) lowers onto
* the AsyncLocalStorage integration (dc.chan* libCalls).
*
* TracingChannel (tracingChannel) is the five-event-channel collection:
* an f64 handle into the runtime's tracing registry (type-mapper.ts maps the
* name like Channel). subscribe/unsubscribe walk a handlers object;
* traceSync/traceCallback run Node's publish choreography in the runtime
* (context defaults to a fresh {}); tracePromise is declared (the member
* exists in Node) but fences at the call site — the traced function's
* promise cannot cross the checked-dynamic tree boundary yet. */
declare module "node:diagnostics_channel" {
export type ChannelListener = (message: unknown, name: string) => void;
export interface Channel {
readonly name: string;
readonly hasSubscribers: boolean;
publish(message: unknown): void;
subscribe(onMessage: ChannelListener): void;
unsubscribe(onMessage: ChannelListener): boolean;
bindStore(
store: import("async_hooks").AsyncLocalStorage<any>,
transform?: (context: any) => any,
): void;
unbindStore(store: import("async_hooks").AsyncLocalStorage<any>): boolean;
runStores(context: unknown, fn: (...args: any[]) => any, thisArg?: any, ...args: any[]): any;
}
export function channel(name: string): Channel;
export function subscribe(name: string, onMessage: ChannelListener): void;
export function unsubscribe(name: string, onMessage: ChannelListener): boolean;
export function hasSubscribers(name: string): boolean;
export interface TracingChannelSubscribers {
start?: (message: any) => void;
end?: (message: any) => void;
asyncStart?: (message: any) => void;
asyncEnd?: (message: any) => void;
error?: (message: any) => void;
}
export interface TracingChannelCollection {
start: Channel;
end: Channel;
asyncStart: Channel;
asyncEnd: Channel;
error: Channel;
}
export interface TracingChannel {
start: Channel;
end: Channel;
asyncStart: Channel;
asyncEnd: Channel;
error: Channel;
readonly hasSubscribers: boolean;
subscribe(subscribers: TracingChannelSubscribers): void;
/** True when every present handler was found and removed (Node's
* all-found conjunction; @types/node under-declares this as void). */
unsubscribe(subscribers: TracingChannelSubscribers): boolean;
traceSync<ThisArg = any, Args extends any[] = any[], Result = any>(
fn: (this: ThisArg, ...args: Args) => Result,
context?: object,
thisArg?: ThisArg,
...args: Args
): Result;
tracePromise<ThisArg = any, Args extends any[] = any[], Result = any>(
fn: (this: ThisArg, ...args: Args) => Promise<Result>,
context?: object,
thisArg?: ThisArg,
...args: Args
): Promise<Result>;
traceCallback<ThisArg = any, Args extends any[] = any[], Result = any>(
fn: (this: ThisArg, ...args: Args) => Result,
position?: number,
context?: object,
thisArg?: ThisArg,
...args: Args
): Result;
}
export function tracingChannel(nameOrChannels: string | TracingChannelCollection): TracingChannel;
}
declare module "diagnostics_channel" {
export * from "node:diagnostics_channel";
}
@@ -0,0 +1,27 @@
/// <reference path="./dns.d.ts" />
/* node:dns/promises — lookup and lookupService as promises over the same
* resolution as the callback forms: argument errors throw synchronously,
* failures reject with the callbacks' errors. */
declare module "dns/promises" {
import type { LookupAddress, LookupAllOptions, LookupOneOptions, LookupOptions } from "dns";
export function lookup(hostname: string, family: number): Promise<LookupAddress>;
export function lookup(hostname: string, options: LookupOneOptions): Promise<LookupAddress>;
export function lookup(hostname: string, options: LookupAllOptions): Promise<LookupAddress[]>;
export function lookup(
hostname: string,
options: LookupOptions,
): Promise<LookupAddress | LookupAddress[]>;
export function lookup(hostname: string): Promise<LookupAddress>;
export function lookupService(
address: string,
port: number,
): Promise<{ hostname: string; service: string }>;
export function getServers(): string[];
export function getDefaultResultOrder(): "ipv4first" | "ipv6first" | "verbatim";
export function setDefaultResultOrder(order: "ipv4first" | "ipv6first" | "verbatim"): void;
}
declare module "node:dns/promises" {
export * from "dns/promises";
}
+65
View File
@@ -0,0 +1,65 @@
/// <reference path="./dns-promises.d.ts" />
/* node:dns — the lookup slice (scr_dgram.c hosts it; getaddrinfo runs AT
* CALL TIME and the callback defers to the next loop turn — SEMANTICS.md
* documents the blocking divergence). Only the { family: 4 } options
* form lowers; failures deliver Node's error shape ("getaddrinfo
* ENOTFOUND <hostname>") as the callback's Error argument, with "" for
* the address where Node passes undefined. */
declare module "dns" {
export interface LookupAddress {
address: string;
family: number;
}
export interface LookupOptions {
family?: number | "IPv4" | "IPv6";
hints?: number;
all?: boolean;
order?: "ipv4first" | "ipv6first" | "verbatim";
verbatim?: boolean;
}
export interface LookupOneOptions extends LookupOptions {
all?: false;
}
export interface LookupAllOptions extends LookupOptions {
all: true;
}
/* The callback comes with the other lookup deliveries (literal
* addresses on the next tick); the options follow lib/dns.js. */
export function lookup(
hostname: string,
callback: (err: NodeJS.ErrnoException | null, address: string, family: number) => void,
): void;
export function lookup(
hostname: string,
family: number,
callback: (err: NodeJS.ErrnoException | null, address: string, family: number) => void,
): void;
export function lookup(
hostname: string,
options: LookupOneOptions,
callback: (err: NodeJS.ErrnoException | null, address: string, family: number) => void,
): void;
export function lookup(
hostname: string,
options: LookupAllOptions,
callback: (err: NodeJS.ErrnoException | null, addresses: LookupAddress[]) => void,
): void;
/* getnameinfo with NI_NAMEREQD: the host's name and the port's service
* name (the number when it has none). */
export function lookupService(
address: string,
port: number,
callback: (err: NodeJS.ErrnoException | null, hostname: string, service: string) => void,
): void;
/* The nameservers /etc/resolv.conf lists. */
export function getServers(): string[];
export function getDefaultResultOrder(): "ipv4first" | "ipv6first" | "verbatim";
export function setDefaultResultOrder(order: "ipv4first" | "ipv6first" | "verbatim"): void;
/* The promise forms (node:dns/promises). */
export const promises: typeof import("dns/promises");
}
declare module "node:dns" {
export * from "dns";
}
+60
View File
@@ -0,0 +1,60 @@
/* node:events — the EventEmitter class (the module object IS the class,
* like Node's `module.exports = EventEmitter`). Listener/emit signatures
* mirror @types/node's untyped `...args: any[]` surface; the compiler's
* lowering unifies each event name's argument tuple program-wide and
* checks annotated listener parameters against it (unannotated non-empty
* parameter lists have no static types and fence at the registration). */
declare module "events" {
class EventEmitter {
constructor();
static defaultMaxListeners: number;
/* Listeners registered under this symbol observe 'error' emits first
* (an emit with no 'error' listener still throws). */
static readonly errorMonitor: unique symbol;
static addAbortListener(signal: AbortSignal, listener: (event: Event) => void): Disposable;
on(eventName: string | symbol, listener: (...args: any[]) => void): this;
addListener(eventName: string | symbol, listener: (...args: any[]) => void): this;
once(eventName: string | symbol, listener: (...args: any[]) => void): this;
prependListener(eventName: string | symbol, listener: (...args: any[]) => void): this;
prependOnceListener(eventName: string | symbol, listener: (...args: any[]) => void): this;
off(eventName: string | symbol, listener: (...args: any[]) => void): this;
removeListener(eventName: string | symbol, listener: (...args: any[]) => void): this;
removeAllListeners(eventName?: string | symbol): this;
emit(eventName: string | symbol, ...args: any[]): boolean;
listenerCount(eventName: string | symbol, listener?: (...args: any[]) => void): number;
/* Declared as zero-parameter closures so the RESULT is statically
* consumable (.length, indexing, identity): the lowering only admits
* these calls for events whose argument tuple is empty — a listener
* pulled from the array is then safely callable too. Events with
* arguments fence (their element type would lie). */
listeners(eventName: string): Array<() => void>;
rawListeners(eventName: string): Array<() => void>;
eventNames(): string[];
setMaxListeners(n: number): this;
getMaxListeners(): number;
}
import internal = require("node:events");
namespace EventEmitter {
// The class under its own name, so `import { EventEmitter }` resolves
// (the @types/node namespace-alias pattern).
export { internal as EventEmitter };
// The event's arguments, as checked-dynamic values.
export function once(
emitter: EventEmitter | NodeJS.ReadableStream | NodeJS.WritableStream,
eventName: string,
options?: { signal?: AbortSignal },
): Promise<unknown[]>;
/* The emitter's own listenerCount/getMaxListeners/setMaxListeners/
* listeners, over EventEmitter instances. */
export function listenerCount(emitter: EventEmitter, eventName: string | symbol): number;
export function getMaxListeners(emitter: EventEmitter): number;
export function setMaxListeners(n?: number, ...eventTargets: EventEmitter[]): void;
export function getEventListeners(emitter: EventEmitter, name: string): Array<() => void>;
}
export = EventEmitter;
}
declare module "node:events" {
import events = require("events");
export = events;
}
+225
View File
@@ -0,0 +1,225 @@
/// <reference path="./fs.d.ts" />
/* fs/promises — the SAME synchronous operations behind already-settled
* promises: success fulfills, failure REJECTS with the would-be thrown
* error (catchable at the await, like Node). DOCUMENTED DIVERGENCE
* (SEMANTICS.md): the syscall blocks the event loop, so I/O never
* interleaves with timers or other fibers — observable only in concurrent
* code. readFile is utf8-only, like readFileSync. */
declare module "fs/promises" {
export interface FileReadResult<T extends Uint8Array> {
bytesRead: number;
buffer: T;
}
export interface FileWriteResult<T> {
bytesWritten: number;
buffer: T;
}
export interface FileReadvResult<T extends Uint8Array[]> {
bytesRead: number;
buffers: T;
}
export interface FileWritevResult<T extends Uint8Array[]> {
bytesWritten: number;
buffers: T;
}
export interface FileReadOptions<T extends Uint8Array = Buffer> {
buffer?: T;
offset?: number | null;
length?: number | null;
position?: number | null;
}
/* The streams over a FileHandle (createReadStream, createWriteStream,
* readLines): fs streams that hold the handle until they close; closing
* the stream closes the handle unless autoClose is false. */
export interface CreateReadStreamOptions {
encoding?: BufferEncoding | null;
autoClose?: boolean;
emitClose?: boolean;
start?: number;
end?: number;
highWaterMark?: number;
}
export interface CreateWriteStreamOptions {
encoding?: BufferEncoding | null;
autoClose?: boolean;
emitClose?: boolean;
start?: number;
highWaterMark?: number;
flush?: boolean;
}
export interface FileHandle extends AsyncDisposable {
readonly fd: number;
close(): Promise<void>;
createReadStream(options?: CreateReadStreamOptions): import("node:fs").ReadStream;
createWriteStream(options?: CreateWriteStreamOptions): import("node:fs").WriteStream;
readLines(options?: CreateReadStreamOptions): import("node:readline").Interface;
sync(): Promise<void>;
datasync(): Promise<void>;
truncate(len?: number): Promise<void>;
chmod(mode: number): Promise<void>;
utimes(atime: number | string | Date, mtime: number | string | Date): Promise<void>;
readv<T extends Uint8Array[]>(
buffers: T,
position?: number | null,
): Promise<FileReadvResult<T>>;
writev<T extends Uint8Array[]>(
buffers: T,
position?: number | null,
): Promise<FileWritevResult<T>>;
read<T extends Uint8Array = Buffer>(
options?: FileReadOptions<T> | null,
): Promise<FileReadResult<T>>;
read<T extends Uint8Array>(
buffer: T,
options: Omit<FileReadOptions<T>, "buffer">,
): Promise<FileReadResult<T>>;
read<T extends Uint8Array>(
buffer: T,
offset?: number | null,
length?: number | null,
position?: number | null,
): Promise<FileReadResult<T>>;
write<T extends Uint8Array>(
buffer: T,
offset?: number | null,
length?: number | null,
position?: number | null,
): Promise<FileWriteResult<T>>;
write<T extends Uint8Array>(
buffer: T,
options: Omit<FileReadOptions<T>, "buffer">,
): Promise<FileWriteResult<T>>;
write(
data: string,
position?: number | null,
encoding?: "utf8" | "utf-8" | null,
): Promise<FileWriteResult<string>>;
readFile(options?: null): Promise<Buffer>;
readFile(encoding: "utf8" | "utf-8"): Promise<string>;
writeFile(data: string | Uint8Array, encoding?: "utf8" | "utf-8" | null): Promise<void>;
appendFile(data: string | Uint8Array, encoding?: "utf8" | "utf-8" | null): Promise<void>;
stat(): Promise<import("node:fs").Stats>;
[Symbol.asyncDispose](): Promise<void>;
}
export function open(path: string | URL, flags?: string, mode?: number): Promise<FileHandle>;
export function link(
existingPath: string | Uint8Array | URL,
newPath: string | Uint8Array | URL,
): Promise<void>;
export function symlink(
target: string | Uint8Array | URL,
path: string | Uint8Array | URL,
type?: string | null,
): Promise<void>;
export function cp(
source: string | URL,
destination: string | URL,
options?: import("node:fs").CopyOptions,
): Promise<void>;
export function readlink(
path: string | Uint8Array | URL,
options: "buffer" | { encoding: "buffer" },
): Promise<Buffer>;
export function readlink(
path: string | Uint8Array | URL,
options?: BufferEncoding | { encoding?: BufferEncoding | null } | null,
): Promise<string>;
export function readlink(
path: string | Uint8Array | URL,
options: string | { encoding?: string | null } | null,
): Promise<string | Buffer>;
export function utimes(
path: string | Uint8Array | URL,
atime: number | string | Date,
mtime: number | string | Date,
): Promise<void>;
export function lutimes(
path: string | Uint8Array | URL,
atime: number | string | Date,
mtime: number | string | Date,
): Promise<void>;
export function readFile(path: string | URL, encoding: "utf8" | "utf-8"): Promise<string>;
export function readFile(
path: string | URL,
options: BufferEncoding | { encoding: BufferEncoding },
): Promise<string>;
export function readFile(path: string | URL): Promise<Buffer>;
export function writeFile(
path: string | URL,
data: string,
options?: {
mode?: number;
encoding?: "utf8" | "utf-8";
flag?: string;
[option: string]: unknown;
},
): Promise<void>;
export function writeFile(
path: string | URL,
data: string,
encoding: "utf8" | "utf-8",
): Promise<void>;
export function mkdir(
path: string | URL,
options?: { recursive?: boolean; mode?: number },
): Promise<void>;
export function readdir(path: string | URL): Promise<string[]>;
export function readdir(
path: string | URL,
options: {
encoding?: "utf8" | "utf-8";
withFileTypes: true;
recursive?: boolean;
[option: string]: unknown;
},
): Promise<import("node:fs").Dirent[]>;
/* The async iterator form of fs.globSync: iterate it with for await. */
export function glob(
pattern: string | readonly string[],
options?: import("node:fs").GlobOptions,
): AsyncIterableIterator<string>;
export function glob(
pattern: string | readonly string[],
options: import("node:fs").GlobOptionsWithFileTypes,
): AsyncIterableIterator<import("node:fs").Dirent>;
export function rm(path: string | URL): Promise<void>;
export function rm(
path: string | URL,
options: { recursive?: boolean; force?: boolean; maxRetries?: number; retryDelay?: number },
): Promise<void>;
export function stat(path: string | URL): Promise<import("node:fs").Stats>;
export function statfs(
path: string | Uint8Array | URL,
options?: import("node:fs").StatFsOptions & { bigint?: false },
): Promise<import("node:fs").StatsFs>;
export function statfs(
path: string | Uint8Array | URL,
options: import("node:fs").StatFsOptions & { bigint: true },
): Promise<import("node:fs").BigIntStatsFs>;
export function statfs(
path: string | Uint8Array | URL,
options?: import("node:fs").StatFsOptions,
): Promise<import("node:fs").StatsFs | import("node:fs").BigIntStatsFs>;
export function realpath(path: string | URL): Promise<string>;
export function lstat(path: string | URL): Promise<import("node:fs").Stats>;
export function unlink(path: string | URL): Promise<void>;
export function chmod(path: string | URL, mode: number): Promise<void>;
export function chown(path: string | URL, uid: number, gid: number): Promise<void>;
export function lchown(path: string | URL, uid: number, gid: number): Promise<void>;
export function access(path: string | URL, mode?: number): Promise<void>;
export function truncate(path: string | URL, len?: number): Promise<void>;
export function appendFile(path: string | URL, data: string | Uint8Array): Promise<void>;
export function copyFile(src: string | URL, dest: string | URL): Promise<void>;
export function mkdtemp(prefix: string | URL): Promise<string>;
export function rmdir(path: string | URL): Promise<void>;
export function rename(oldPath: string | URL, newPath: string | URL): Promise<void>;
export function opendir(
path: string | URL,
options?: import("node:fs").OpenDirOptions,
): Promise<import("node:fs").Dir>;
}
declare module "node:fs/promises" {
export * from "fs/promises";
}
+587
View File
@@ -0,0 +1,587 @@
/// <reference path="./fs-promises.d.ts" />
/// <reference path="./stream.d.ts" />
/* The synchronous node:fs surface — utf8-only, no options objects, no
* Buffers. Importing "node:fs" resolves here (preflight allowlists exactly
* the ambient-declared node: modules); every function lowers to a `libCall`
* with a scr_fs_* runtime implementation. Failures throw CATCHABLE string
* values formatted like Node's error messages ("ENOENT: no such file or
* directory, open 'x'"). `rmSync` removes FILES only (like Node without
* `recursive`); empty directories go through `rmdirSync`. */
declare module "node:fs" {
import { Readable, Writable } from "stream";
export function readFileSync(path: string | number | URL, encoding: BufferEncoding): string;
export function readFileSync(
path: string | number | URL,
options: { encoding: BufferEncoding },
): string;
export function readFileSync(path: string | number | URL): Buffer;
export function readFileSync(path: string | URL, options: unknown): string | Buffer;
/* Error-first callbacks select string or Buffer data from their encoding.
* Unknown options retain that union until the caller validates the value. */
export function readFile(
path: string,
options: unknown,
callback: (error: Error | null, data: string | Buffer) => void,
): void;
/* The options form carries the mode (applied at CREATION only, like
* Node — an existing file keeps its permissions) and/or the utf8
* encoding spelling. */
export function writeFileSync(
path: string | URL,
data: string,
/* The options-record stance (see http.RequestOptions): flag fences
* by name at the call site; undocumented keys drop like Node. */
options?: {
mode?: number;
encoding?: "utf8" | "utf-8";
flag?: string;
[option: string]: unknown;
},
): void;
/* The bare-encoding spelling — the options record's encoding key alone. */
export function writeFileSync(path: string | URL, data: string, encoding: "utf8" | "utf-8"): void;
export function writeFileSync(path: string | URL, data: string | Uint8Array): void;
export function appendFileSync(path: string | URL, data: string | Uint8Array): void;
export function existsSync(path: string | URL): boolean;
export function mkdirSync(path: string | URL): void;
export function mkdirSync(
path: string | URL,
options: { recursive?: boolean; mode?: number },
): void;
export function unlinkSync(path: string | URL): void;
export function chmodSync(path: string | URL, mode: number): void;
export function fchmodSync(fd: number, mode: number): void;
export function fsyncSync(fd: number): void;
export function linkSync(
existingPath: string | Uint8Array | URL,
newPath: string | Uint8Array | URL,
): void;
export function symlinkSync(
target: string | Uint8Array | URL,
path: string | Uint8Array | URL,
type?: string | null,
): void;
/* fs.cpSync / fs.cp: copies a file, symlink or (with recursive) a
* directory tree. filter decides per path; the callback and promise
* forms complete the copy before reporting. */
export interface CopySyncOptions {
dereference?: boolean;
errorOnExist?: boolean;
filter?: (source: string, destination: string) => boolean;
force?: boolean;
mode?: number;
preserveTimestamps?: boolean;
recursive?: boolean;
verbatimSymlinks?: boolean;
}
export interface CopyOptions {
dereference?: boolean;
errorOnExist?: boolean;
filter?: (source: string, destination: string) => boolean | Promise<boolean>;
force?: boolean;
mode?: number;
preserveTimestamps?: boolean;
recursive?: boolean;
verbatimSymlinks?: boolean;
}
export function cpSync(
source: string | URL,
destination: string | URL,
options?: CopySyncOptions,
): void;
export function cp(
source: string | URL,
destination: string | URL,
callback: (err: NodeJS.ErrnoException | null) => void,
): void;
export function cp(
source: string | URL,
destination: string | URL,
options: CopyOptions,
callback: (err: NodeJS.ErrnoException | null) => void,
): void;
export function symlink(
target: string | Uint8Array | URL,
path: string | Uint8Array | URL,
callback: (err: NodeJS.ErrnoException | null) => void,
): void;
export function symlink(
target: string | Uint8Array | URL,
path: string | Uint8Array | URL,
type: string | null | undefined,
callback: (err: NodeJS.ErrnoException | null) => void,
): void;
export function link(
existingPath: string | Uint8Array | URL,
newPath: string | Uint8Array | URL,
callback: (err: NodeJS.ErrnoException | null) => void,
): void;
export function readlink(
path: string | Uint8Array | URL,
callback: (err: NodeJS.ErrnoException | null, target: string) => void,
): void;
export function readlink(
path: string | Uint8Array | URL,
options: "buffer" | { encoding: "buffer" },
callback: (err: NodeJS.ErrnoException | null, target: Buffer) => void,
): void;
export function readlink(
path: string | Uint8Array | URL,
options: BufferEncoding | { encoding?: BufferEncoding | null } | null,
callback: (err: NodeJS.ErrnoException | null, target: string) => void,
): void;
export function readlink(
path: string | Uint8Array | URL,
options: string | { encoding?: string | null } | null,
callback: (err: NodeJS.ErrnoException | null, target: string | Buffer) => void,
): void;
export function readlinkSync(
path: string | Uint8Array | URL,
options: "buffer" | { encoding: "buffer" },
): Buffer;
export function readlinkSync(
path: string | Uint8Array | URL,
options?: BufferEncoding | { encoding?: BufferEncoding | null } | null,
): string;
export function readlinkSync(
path: string | Uint8Array | URL,
options: string | { encoding?: string | null } | null,
): string | Buffer;
export function chownSync(path: string | URL, uid: number, gid: number): void;
export function lchownSync(path: string | URL, uid: number, gid: number): void;
export function fchownSync(fd: number, uid: number, gid: number): void;
export function truncateSync(path: string | URL, len?: number): void;
/* The 2-argument form only (Node's mode flags have no lowering). The
* destination is created or truncated carrying the SOURCE's mode. */
export function copyFileSync(src: string | URL, dest: string | URL): void;
export function rename(
oldPath: string,
newPath: string,
callback: (err: NodeJS.ErrnoException | null) => void,
): void;
export function renameSync(oldPath: string | URL, newPath: string | URL): void;
export function rmSync(path: string | URL): void;
/* maxRetries/retryDelay are accepted no-ops (Node retries around
* EBUSY-class races; the synchronous lowering has no re-entrancy that
* needs them). */
export function rmSync(
path: string | URL,
options: { recursive?: boolean; force?: boolean; maxRetries?: number; retryDelay?: number },
): void;
export function rmdirSync(path: string | URL): void;
export function readdirSync(path: string | URL): string[];
/* The recursive form: every entry below the path, relative to it. */
export function readdirSync(
path: string | URL,
options: {
recursive?: boolean;
withFileTypes?: false;
encoding?: "utf8" | "utf-8";
[option: string]: unknown;
},
): string[];
/* The withFileTypes form: Dirent rows (name + parentPath + the type
* probes — the honest subset of Node's Dirent surface). The type is
* interned explicitly in type-mapper.ts (the record's hidden %dtype field
* carries the entry kind; isFile/isDirectory/isSymbolicLink lower as
* reads of it in builtins/process.ts). */
export interface Dirent {
name: string;
parentPath: string;
isFile(): boolean;
isDirectory(): boolean;
isSymbolicLink(): boolean;
}
export function readdirSync(
path: string | URL,
/* The options-record stance (see http.RequestOptions): recursive
* fences by name at the call site; undocumented keys drop like Node. */
options: { withFileTypes: true; recursive?: boolean; [option: string]: unknown },
): Dirent[];
/* fs.globSync / fs.glob: Node's glob walk over the file system. Patterns
* are minimatch globs (case-insensitive on macOS and Windows); results
* are relative to cwd unless the pattern is absolute. withFileTypes must
* be a literal so the result type is known. */
export interface GlobOptions {
cwd?: string | URL;
exclude?: readonly string[] | ((fileName: string) => boolean);
followSymlinks?: boolean;
withFileTypes?: false;
}
export interface GlobOptionsWithFileTypes {
cwd?: string | URL;
exclude?: readonly string[];
followSymlinks?: boolean;
withFileTypes: true;
}
export function globSync(pattern: string | readonly string[], options?: GlobOptions): string[];
export function globSync(
pattern: string | readonly string[],
options: GlobOptionsWithFileTypes,
): Dirent[];
export function glob(
pattern: string | readonly string[],
callback: (err: NodeJS.ErrnoException | null, matches?: string[]) => void,
): void;
export function glob(
pattern: string | readonly string[],
options: GlobOptions,
callback: (err: NodeJS.ErrnoException | null, matches?: string[]) => void,
): void;
/* fs.Dir: an open directory stream (opendirSync, fs.promises.opendir)
* read one Dirent at a time in OS order; utf8 names, no recursive walk. */
export interface Dir {
readonly path: string;
readSync(): Dirent | null;
read(): Promise<Dirent | null>;
closeSync(): void;
close(): Promise<void>;
[Symbol.asyncIterator](): AsyncIterableIterator<Dirent>;
}
export interface OpenDirOptions {
encoding?: "utf8" | "utf-8";
bufferSize?: number;
}
export function opendirSync(path: string | URL, options?: OpenDirOptions): Dir;
/* fs.ReadStream / fs.WriteStream: runtime Readable and Writable streams
* over a file (scr_fs_stream.c). The options are checked at runtime
* with Node's rules; flags and mode are checked when the file opens, so
* their errors arrive as 'error' events. Instances also emit 'open'
* (with the fd) and 'ready'. */
export interface StreamOptions {
flags?: string;
encoding?: BufferEncoding;
fd?: number;
mode?: number | string;
autoClose?: boolean;
emitClose?: boolean;
start?: number;
highWaterMark?: number;
}
export interface ReadStreamOptions extends StreamOptions {
end?: number;
}
export interface WriteStreamOptions extends StreamOptions {
flush?: boolean;
}
export class ReadStream extends Readable {
readonly bytesRead: number;
readonly path: string | Buffer;
readonly pending: boolean;
close(callback?: () => void): void;
}
export class WriteStream extends Writable {
readonly bytesWritten: number;
readonly path: string | Buffer;
readonly pending: boolean;
close(callback?: () => void): void;
}
export function createReadStream(
path: string | Buffer | URL,
options?: BufferEncoding | ReadStreamOptions,
): ReadStream;
export function createWriteStream(
path: string | Buffer | URL,
options?: BufferEncoding | WriteStreamOptions,
): WriteStream;
export function accessSync(path: string | URL, mode?: number): void;
export function mkdtempSync(prefix: string | URL): string;
export const constants: {
readonly F_OK: number;
readonly R_OK: number;
readonly W_OK: number;
readonly X_OK: number;
readonly O_RDONLY: number;
readonly O_WRONLY: number;
readonly O_RDWR: number;
readonly O_CREAT: number;
readonly O_EXCL: number;
readonly O_NOFOLLOW: number;
readonly O_NONBLOCK: number;
readonly O_TRUNC: number;
readonly O_APPEND: number;
readonly COPYFILE_EXCL: number;
readonly COPYFILE_FICLONE: number;
readonly COPYFILE_FICLONE_FORCE: number;
readonly UV_FS_SYMLINK_DIR: number;
readonly UV_FS_SYMLINK_JUNCTION: number;
readonly UV_DIRENT_UNKNOWN: number;
readonly UV_DIRENT_FILE: number;
readonly UV_DIRENT_DIR: number;
readonly UV_DIRENT_LINK: number;
readonly UV_DIRENT_FIFO: number;
readonly UV_DIRENT_SOCKET: number;
readonly UV_DIRENT_CHAR: number;
readonly UV_DIRENT_BLOCK: number;
readonly UV_FS_O_FILEMAP: number;
readonly UV_FS_O_TEMPORARY: number;
readonly UV_FS_O_SHORT_LIVED: number;
readonly UV_FS_O_SEQUENTIAL: number;
readonly UV_FS_O_RANDOM: number;
readonly UV_FS_COPYFILE_EXCL: number;
readonly UV_FS_COPYFILE_FICLONE: number;
readonly UV_FS_COPYFILE_FICLONE_FORCE: number;
readonly S_IFMT: number;
readonly S_IFREG: number;
readonly S_IFDIR: number;
readonly S_IFCHR: number;
readonly S_IFBLK: number;
readonly S_IFIFO: number;
readonly S_IFLNK: number;
readonly S_IFSOCK: number;
readonly O_NOCTTY: number;
readonly O_DIRECTORY: number;
readonly O_NOATIME: number;
readonly O_SYNC: number;
readonly O_DSYNC: number;
readonly O_SYMLINK: number;
readonly O_DIRECT: number;
readonly S_IRWXU: number;
readonly S_IRUSR: number;
readonly S_IWUSR: number;
readonly S_IXUSR: number;
readonly S_IRWXG: number;
readonly S_IRGRP: number;
readonly S_IWGRP: number;
readonly S_IXGRP: number;
readonly S_IRWXO: number;
readonly S_IROTH: number;
readonly S_IWOTH: number;
readonly S_IXOTH: number;
};
/* A stat(2) snapshot (statSync follows symlinks, lstatSync does not —
* Node's split) — immutable; the supported surface is exactly these
* members. dev/ino identify the filesystem entry, blocks is the allocated
* size in 512-byte units, and the time fields are milliseconds with their
* sub-second fractions. */
export interface Stats {
isFile(): boolean;
isDirectory(): boolean;
isSymbolicLink(): boolean;
isBlockDevice(): boolean;
isCharacterDevice(): boolean;
isFIFO(): boolean;
isSocket(): boolean;
readonly dev: number;
readonly mode: number;
readonly nlink: number;
readonly uid: number;
readonly gid: number;
readonly rdev: number;
readonly blksize: number;
readonly ino: number;
readonly size: number;
readonly blocks: number;
readonly atimeMs: number;
readonly mtimeMs: number;
readonly ctimeMs: number;
readonly birthtimeMs: number;
readonly atime: Date;
readonly mtime: Date;
readonly ctime: Date;
readonly birthtime: Date;
}
/* throwIfNoEntry: false answers undefined for a missing entry (ENOENT or
* ENOTDIR) instead of throwing. BigIntStats (bigint: true) has no
* lowering. */
export interface StatSyncOptions {
bigint?: boolean;
throwIfNoEntry?: boolean;
}
export function statSync(path: string | URL, options?: undefined): Stats;
export function statSync(
path: string | URL,
options: StatSyncOptions & { bigint?: false; throwIfNoEntry: false },
): Stats | undefined;
export function statSync(
path: string | URL,
options: StatSyncOptions & { bigint?: false; throwIfNoEntry?: true },
): Stats;
/* bigint: true type-checks so the lowering can name it as unsupported. */
export function statSync(path: string | URL, options: StatSyncOptions & { bigint: true }): Stats;
export function statSync(path: string | URL, options: StatSyncOptions): Stats | undefined;
export function lstatSync(path: string | URL, options?: undefined): Stats;
export function lstatSync(
path: string | URL,
options: StatSyncOptions & { bigint?: false; throwIfNoEntry: false },
): Stats | undefined;
export function lstatSync(
path: string | URL,
options: StatSyncOptions & { bigint?: false; throwIfNoEntry?: true },
): Stats;
/* bigint: true type-checks so the lowering can name it as unsupported. */
export function lstatSync(path: string | URL, options: StatSyncOptions & { bigint: true }): Stats;
export function lstatSync(path: string | URL, options: StatSyncOptions): Stats | undefined;
/* The fd pair behind spawn's fd-stdio form: openSync(path, flags) →
* the raw fd. Numeric flags use an inline OR of the O_* constants. */
export function openSync(path: string | URL, flags: string): number;
export function openSync(path: string | URL, flags: number, mode?: number): number;
export function closeSync(fd: number): void;
export function fdatasyncSync(fd: number): void;
export function ftruncateSync(fd: number, len?: number): void;
export function utimesSync(
path: string | Uint8Array | URL,
atime: number | string | Date,
mtime: number | string | Date,
): void;
export function futimesSync(
fd: number,
atime: number | string | Date,
mtime: number | string | Date,
): void;
export function lutimesSync(
path: string | Uint8Array | URL,
atime: number | string | Date,
mtime: number | string | Date,
): void;
export function utimes(
path: string | Uint8Array | URL,
atime: number | string | Date,
mtime: number | string | Date,
callback: (err: NodeJS.ErrnoException | null) => void,
): void;
export function futimes(
fd: number,
atime: number | string | Date,
mtime: number | string | Date,
callback: (err: NodeJS.ErrnoException | null) => void,
): void;
export function lutimes(
path: string | Uint8Array | URL,
atime: number | string | Date,
mtime: number | string | Date,
callback: (err: NodeJS.ErrnoException | null) => void,
): void;
export function readvSync(fd: number, buffers: Uint8Array[], position?: number | null): number;
export function writevSync(fd: number, buffers: Uint8Array[], position?: number | null): number;
/* Read into a caller buffer from the fd's current position when position
* is omitted/null, or from a numeric byte position without advancing the
* fd. Answers the byte count, 0 at EOF. */
export interface ReadSyncOptions {
offset?: number;
length?: number;
position?: number | null;
}
export function readSync(fd: number, buffer: Uint8Array, options?: ReadSyncOptions): number;
export function readSync(
fd: number,
buffer: Uint8Array,
offset: number,
length: number,
position?: number | null,
): number;
/* Write a caller-buffer window, or a utf8 string, at the fd's current
* position when position is omitted/null (advancing it), or at a numeric
* byte position without advancing it. Answers the byte count. */
export interface WriteSyncOptions {
offset?: number;
length?: number;
position?: number | null;
}
export function writeSync(fd: number, buffer: Uint8Array, options: WriteSyncOptions): number;
export function writeSync(
fd: number,
buffer: Uint8Array,
offset?: number,
length?: number,
position?: number | null,
): number;
export function writeSync(
fd: number,
string: string,
position?: number | null,
encoding?: "utf8" | "utf-8",
): number;
/* statSync over an open fd — the same Stats snapshot. */
export function fstatSync(fd: number): Stats;
/* realpath(3) — resolves symlinks, `.`/`..`, throwing Node's fs error
* shapes for missing paths. */
export function realpathSync(path: string | URL): string;
export namespace realpathSync {
function native(path: string | URL): string;
}
/* statfs(2)/statvfs(3) — the filesystem-capacity snapshot (the fields
* Node's statfsSync reports; bavail × bsize is the free-space probe). */
export interface StatsFsBase<T> {
type: T;
bsize: T;
blocks: T;
bfree: T;
bavail: T;
files: T;
ffree: T;
}
export interface StatsFs extends StatsFsBase<number> {}
export interface BigIntStatsFs extends StatsFsBase<bigint> {}
export interface StatFsOptions {
bigint?: boolean;
}
export function statfsSync(
path: string | Uint8Array | URL,
options?: StatFsOptions & { bigint?: false },
): StatsFs;
export function statfsSync(
path: string | Uint8Array | URL,
options: StatFsOptions & { bigint: true },
): BigIntStatsFs;
export function statfsSync(
path: string | Uint8Array | URL,
options?: StatFsOptions,
): StatsFs | BigIntStatsFs;
export function statfs(
path: string | Uint8Array | URL,
callback: (err: NodeJS.ErrnoException | null, stats: StatsFs) => void,
): void;
export function statfs(
path: string | Uint8Array | URL,
options: (StatFsOptions & { bigint?: false }) | undefined,
callback: (err: NodeJS.ErrnoException | null, stats: StatsFs) => void,
): void;
export function statfs(
path: string | Uint8Array | URL,
options: StatFsOptions & { bigint: true },
callback: (err: NodeJS.ErrnoException | null, stats: BigIntStatsFs) => void,
): void;
export function statfs(
path: string | Uint8Array | URL,
options: StatFsOptions | undefined,
callback: (err: NodeJS.ErrnoException | null, stats: StatsFs | BigIntStatsFs) => void,
): void;
/* fs.watch — the file-watching slice (scr_watch.c, kqueue EVFILT_VNODE):
* "rename"/"change" events on ONE path, watcher.close() to stop. The
* listener takes zero parameters or the eventType string (the filename
* parameter has no lowering); an open watcher keeps the loop alive. A
* path that cannot be opened THROWS Node's fs error synchronously. */
export interface FSWatcher {
close(): void;
}
/* The options-record stance (see http.RequestOptions): persistent: true,
* recursive: false, and encoding: "utf8" state the lowered behavior and
* are accepted; persistent: false, recursive: true, and signal fence by
* name at the call site; undocumented keys drop like Node. */
export interface WatchOptions {
persistent?: boolean;
recursive?: boolean;
encoding?: string;
[option: string]: unknown;
}
export function watch(path: string, listener?: (eventType: string) => void): FSWatcher;
export function watch(
path: string,
options: WatchOptions,
listener?: (eventType: string) => void,
): FSWatcher;
/* fs.promises IS the fs/promises module (Node's rule) — the namespace-
* import form `fs.promises.readFile(...)` lowers through the same
* fs/promises table as `import { readFile } from "node:fs/promises"`. */
export const promises: typeof import("node:fs/promises");
}
/* Every builtin answers to BOTH specifier forms, like in Node: the bare
* module re-exports its node:-prefixed twin (or holds the declarations
* itself — one of the pair forwards). The lowerer recognizes members by
* their import SPECIFIER, so both forms lower identically. */
declare module "fs" {
export * from "node:fs";
}
@@ -0,0 +1,9 @@
/// <reference path="./fs.d.ts" />
/* graceful-fs — served as node:fs (it monkeypatches fs and retries on
* EMFILE/ENFILE; compiled programs call fs directly, without the retry
* queue). gracefulify is a no-op statement. The @types/graceful-fs shape. */
declare module "graceful-fs" {
export * from "node:fs";
export function gracefulify<T>(fsModule: T): T;
}
+326
View File
@@ -0,0 +1,326 @@
/// <reference path="./http2.d.ts" />
/// <reference path="./net.d.ts" />
/* node:http — the SERVER and CLIENT slices (scr_http.c layers a
* hand-written HTTP/1.1 parser + serializer on scr_net.c; both link only
* into using binaries). createServer(handler) returns a net.Server —
* listen/close/address().port/'error' are the same lowered surface. The
* request carries url/method (always present on server requests),
* statusCode (a number on client responses, undefined on server
* requests), the underlying socket, and lowercased headers read as
* `string | undefined`; the body streams through on("data"/"end"). The
* response is setHeader/writeHead/write/end/destroy with Node's framing
* (Content-Length for a single end(body), chunked after writeHead/write)
* and the auto Date/Connection headers; writeHead's headers argument is
* an object literal or a Record<string, string>. request/get dial one
* connection per call (no agent pooling) with Node's exact wire head. */
declare module "http" {
import { Server as NetServer, Socket } from "net";
export const METHODS: string[];
export const STATUS_CODES: { [status: number]: string | undefined };
export const maxHeaderSize: number;
export interface Server extends NetServer {
timeout: number;
setTimeout(msecs: number, callback?: (socket: Socket) => void): this;
closeAllConnections(): void;
closeIdleConnections(): void;
keepAliveTimeout: number;
keepAliveTimeoutBuffer: number;
headersTimeout: number;
requestTimeout: number;
}
export type RequestListener = (req: IncomingMessage, res: ServerResponse) => void;
/* The Server VALUE — Node's constructor works with and without `new`
* (test/parallel's http.Server(fn) spelling); both route to the
* createServer lowering. */
export const Server: {
new (requestListener?: (req: IncomingMessage, res: ServerResponse) => void): Server;
new (
options: ServerOptions,
requestListener?: (req: IncomingMessage, res: ServerResponse) => void,
): Server;
(requestListener?: (req: IncomingMessage, res: ServerResponse) => void): Server;
(
options: ServerOptions,
requestListener?: (req: IncomingMessage, res: ServerResponse) => void,
): Server;
};
/* The options-record stance (the RequestOptions precedent): every
* documented key typechecks; the lowering's option walk decides per
* key — requireHostHeader: false, joinDuplicateHeaders, and
* keepAliveTimeoutBuffer lower,
* documented-but-unlowered keys fence by name, unknown keys drop. */
export interface ServerOptions {
requireHostHeader?: boolean;
joinDuplicateHeaders?: boolean;
keepAliveTimeoutBuffer?: number;
maxHeaderSize?: number;
[option: string]: unknown;
}
/* The outgoing-header shape, @types/node's matrix: numbers format via
* String(n), arrays write one line per element (set-cookie's shape).
* Incoming headers stay `string | undefined` — what the parser answers
* (under @types/node the two SLOTS unify so `{ ...req.headers }`
* merges into outgoing literals as a copy; the fallback keeps reads
* simple and spreads target the same string | undefined slot). */
export type OutgoingHttpHeaders = { [name: string]: number | string | string[] | undefined };
export interface IncomingMessage {
readonly url: string;
readonly method: string;
readonly httpVersion: string;
readonly complete: boolean;
readonly aborted: boolean;
readonly statusCode: number | undefined;
readonly statusMessage: string | undefined;
readonly socket: Socket;
readonly connection: Socket;
readonly headers: { [name: string]: string | undefined };
readonly headersDistinct: { [name: string]: string[] | undefined };
readonly rawHeaders: string[];
readonly trailers: { [name: string]: string | undefined };
readonly trailersDistinct: { [name: string]: string[] | undefined };
readonly rawTrailers: string[];
resume(): void;
pause(): void;
destroy(): void;
setTimeout(msecs: number, callback?: () => void): this;
/* setEncoding('utf8'): 'data' delivers strings (other real encodings
* throw a "not supported yet" Error at runtime; unknown names throw
* ERR_UNKNOWN_ENCODING). */
setEncoding(encoding: string): void;
/* The proxy legs: the body streams into a ServerResponse, a
* ClientRequest, or a raw Socket; natural end ends the destination. */
pipe(
destination: ServerResponse | import("http2").Http2ServerResponse | ClientRequest | Socket,
): void;
on(event: "data", listener: (chunk: any) => void): void;
on(event: "end" | "close" | "aborted", listener: () => void): void;
on(event: "error", listener: (err: Error) => void): void;
/* addListener IS on (Node aliases them) — the suite spells both. */
addListener(event: "data", listener: (chunk: any) => void): void;
addListener(event: "end" | "close" | "aborted", listener: () => void): void;
addListener(event: "error", listener: (err: Error) => void): void;
once(event: "data", listener: (chunk: any) => void): void;
once(event: "end" | "close" | "aborted", listener: () => void): void;
once(event: "error", listener: (err: Error) => void): void;
}
export interface ServerResponse {
readonly headersSent: boolean;
readonly writableEnded: boolean;
readonly finished: boolean;
readonly writableFinished: boolean;
readonly writableCorked: number;
readonly req: IncomingMessage;
readonly socket: Socket | null;
readonly connection: Socket | null;
sendDate: boolean;
strictContentLength: boolean;
/* Node's writable head properties: the implicit head reads them. */
statusCode: number;
statusMessage: string | undefined;
/* The value keeps its type: getHeader answers the number, string or
* array that was set. */
setHeader(name: string, value: number | string | readonly string[]): void;
appendHeader(name: string, value: string | readonly string[]): void;
getHeader(name: string): number | string | string[] | undefined;
getHeaderNames(): string[];
getRawHeaderNames(): string[];
getHeaders(): OutgoingHttpHeaders;
hasHeader(name: string): boolean;
removeHeader(name: string): void;
/* Both overloads answer the response (`return this` chaining); the
* headers argument also takes Node's flat [name, value, ...] array. */
writeHead(statusCode: number, headers?: OutgoingHttpHeaders | string[]): ServerResponse;
writeHead(
statusCode: number,
statusMessage: string,
headers?: OutgoingHttpHeaders | string[],
): ServerResponse;
writeContinue(callback?: () => void): void;
writeProcessing(callback?: () => void): void;
writeEarlyHints(hints: Record<string, string | string[]>, callback?: () => void): void;
write(data: string | Uint8Array): void;
flushHeaders(): void;
cork(): void;
uncork(): void;
addTrailers(headers: OutgoingHttpHeaders | ReadonlyArray<[string, string]>): void;
setTimeout(msecs: number, callback?: () => void): this;
/* end's callback forms fire once the body went out (the 'finish'
* emit, deferred past the handler's synchronous tail). */
end(data?: string | Uint8Array, callback?: () => void): void;
end(callback: () => void): void;
destroy(): void;
on(event: "close" | "finish", listener: () => void): void;
/* addListener IS on (Node aliases them) — the suite spells both. */
addListener(event: "close" | "finish", listener: () => void): void;
once(event: "close" | "finish", listener: () => void): void;
}
/* The CLIENT slice (http.request/http.get): the options-object form
* with hostname/host, port, path, method, timeout, and headers; the
* callback receives the response as an IncomingMessage (statusCode,
* headers, body via on("data"/"end")). */
export interface RequestOptions {
hostname?: string | null;
host?: string | null;
/* A string port (a URL's .port) is accepted, as by Node. */
port?: number | string | null;
path?: string | null;
method?: string;
timeout?: number;
headers?: OutgoingHttpHeaders;
/* The caller's own dialer (the proxy's loopback dial): invoked once,
* synchronously; its socket carries the exchange. */
createConnection?: () => Socket;
/* agent: false is a one-shot dial with Connection: close — exactly
* the compiled client's model; null/undefined are the default; an
* Agent VALUE threads through (getName-keyed maxSockets accounting
* over one-dial-per-request connections). */
agent?: Agent | boolean | null;
/* Aborting destroys the request with an AbortError. */
signal?: AbortSignal;
/* HTTP over the Unix socket at this path instead of host:port. */
socketPath?: string;
/* The options-record stance: Node ignores an options record's
* unknown keys, so every key typechecks here — the option WALK in
* the lowering decides per key (lower, fence by name, or drop
* undocumented keys exactly like Node). */
[option: string]: unknown;
}
/* The Agent lowers to a checked-dynamic HANDLE: option parsing at the
* literal construction site, getName, destroy, the sockets/requests/
* freeSockets snapshots, and REAL maxSockets accounting (over-limit
* requests defer their dial and queue). keepAlive: true keeps finished
* connections in freeSockets for the next request to the same name,
* taken in `scheduling` order. */
export interface AgentOptions {
keepAlive?: boolean;
keepAliveMsecs?: number;
maxSockets?: number;
maxFreeSockets?: number;
maxTotalSockets?: number;
scheduling?: "fifo" | "lifo";
timeout?: number;
port?: number;
[option: string]: unknown;
}
export class Agent {
constructor(options?: AgentOptions);
destroy(): void;
/* Node's exact key shape: host:port:localAddress[:family][:socketPath]. */
getName(options?: {
host?: string;
port?: number;
localAddress?: string;
family?: number;
socketPath?: string;
[option: string]: unknown;
}): string;
maxSockets: number;
maxFreeSockets: number;
keepAliveMsecs: number;
readonly keepAlive: boolean;
readonly protocol: string;
/* Settable — a portless request through this agent dials it (Node's
* option merge). */
defaultPort: number;
readonly totalSocketCount: number;
readonly sockets: { [key: string]: unknown[] };
readonly freeSockets: { [key: string]: unknown[] };
readonly requests: { [key: string]: unknown[] };
}
/* Node's keepAlive global agent for requests without an agent option. */
export const globalAgent: Agent;
export interface ClientRequest {
readonly destroyed: boolean;
readonly aborted: boolean;
readonly socket: Socket;
readonly connection: Socket;
readonly writableCorked: number;
readonly method: string;
readonly path: string;
readonly host: string;
readonly protocol: string;
readonly headersSent: boolean;
readonly writableEnded: boolean;
readonly writableFinished: boolean;
readonly finished: boolean;
readonly reusedSocket: boolean;
setHeader(name: string, value: string): void;
getHeader(name: string): string | undefined;
hasHeader(name: string): boolean;
removeHeader(name: string): void;
getHeaderNames(): string[];
getRawHeaderNames(): string[];
getHeaders(): OutgoingHttpHeaders;
flushHeaders(): void;
addTrailers(headers: OutgoingHttpHeaders | ReadonlyArray<[string, string]>): void;
cork(): void;
uncork(): void;
setNoDelay(noDelay?: boolean): void;
setSocketKeepAlive(enable?: boolean, initialDelay?: number): void;
setTimeout(timeout: number, callback?: () => void): this;
write(data: string | Uint8Array): void;
end(data?: string | Uint8Array): void;
destroy(error?: Error): void;
abort(): void;
on(event: "response", listener: (res: IncomingMessage) => void): void;
on(event: "socket", listener: (socket: Socket) => void): void;
on(
event: "upgrade",
listener: (res: IncomingMessage, socket: Socket, head: Buffer) => void,
): void;
on(event: "timeout" | "close" | "finish" | "abort" | "continue", listener: () => void): void;
on(event: "error", listener: (err: Error) => void): void;
/* addListener IS on (Node aliases them) — the suite spells both. */
addListener(event: "response", listener: (res: IncomingMessage) => void): void;
addListener(event: "socket", listener: (socket: Socket) => void): void;
addListener(
event: "upgrade",
listener: (res: IncomingMessage, socket: Socket, head: Buffer) => void,
): void;
addListener(event: "timeout" | "close" | "finish" | "abort", listener: () => void): void;
addListener(event: "error", listener: (err: Error) => void): void;
once(event: "response", listener: (res: IncomingMessage) => void): void;
once(event: "socket", listener: (socket: Socket) => void): void;
once(
event: "upgrade",
listener: (res: IncomingMessage, socket: Socket, head: Buffer) => void,
): void;
once(event: "timeout" | "close" | "finish" | "abort" | "continue", listener: () => void): void;
once(event: "error", listener: (err: Error) => void): void;
}
export function createServer(
requestListener?: (req: IncomingMessage, res: ServerResponse) => void,
): Server;
export function createServer(
options: ServerOptions,
requestListener?: (req: IncomingMessage, res: ServerResponse) => void,
): Server;
/* One signature with a UNION first parameter rather than Node's two
* overloads: `const requestFn = tls ? https.request : http.request`
* (the portless isProxyRunning shape) unions two function types, and
* resolving a call on that union collapses each constituent to a
* single signature — with an overload SET on both sides the options
* literal ends up checked against URL, in either declaration order.
* A union parameter has nothing to collapse. The lowering dispatches
* on the argument's own type, so it reads the same either way.
*
* `target` is a URL string or URL object (the spelling a from-scratch
* client reaches for first — dialed directly, with a non-http scheme
* an ERR_INVALID_PROTOCOL at runtime rather than a type error), or
* the options record. */
export function request(
target: RequestOptions | string | URL,
callback?: (res: IncomingMessage) => void,
): ClientRequest;
export function get(
target: RequestOptions | string | URL,
callback?: (res: IncomingMessage) => void,
): ClientRequest;
export function validateHeaderName(name: string, label?: string): void;
export function validateHeaderValue(name: string, value: unknown): void;
}
declare module "node:http" {
export * from "http";
}
+816
View File
@@ -0,0 +1,816 @@
/// <reference path="./dgram.d.ts" />
/// <reference path="./http.d.ts" />
/// <reference path="./net.d.ts" />
/// <reference path="./tls.d.ts" />
/* node:http2 — the compatibility surface: allowHTTP1 servers negotiate
* h2 or HTTP/1.1 and expose the shared request/response API. HTTP/2
* requests retain their backing stream; server session failures surface
* through sessionError. */
declare module "http2" {
import { Socket } from "net";
export interface Http2Stream {
on(event: "error" | "close", listener: (err: Error) => void): void;
once(event: "error" | "close", listener: (err: Error) => void): void;
addListener(event: "error" | "close", listener: (err: Error) => void): void;
destroy(): void;
}
export interface Http2ServerRequest {
readonly url: string;
readonly method: string;
readonly statusCode: number | undefined;
readonly socket: Socket;
readonly headers: { [name: string]: string | undefined };
readonly stream: Http2Stream;
/* The h2 compat request's version triple ("2.0" over real h2 streams;
* the allowHTTP1 lowering's HTTP/1.1 connections answer 1.x). */
readonly httpVersion: string;
readonly httpVersionMajor: number;
readonly httpVersionMinor: number;
readonly aborted: boolean;
readonly complete: boolean;
resume(): void;
destroy(): void;
setEncoding(encoding: string): void;
pipe(destination: Socket | import("http").ClientRequest): void;
on(event: "data", listener: (chunk: Buffer) => void): void;
on(event: "end" | "close" | "aborted", listener: () => void): void;
on(event: "error", listener: (err: Error) => void): void;
/* addListener IS on (Node aliases them) — and the compat req IS the
* http.IncomingMessage surface, so the alias mirrors here too. */
addListener(event: "data", listener: (chunk: Buffer) => void): void;
addListener(event: "end" | "close" | "aborted", listener: () => void): void;
addListener(event: "error", listener: (err: Error) => void): void;
once(event: "data", listener: (chunk: Buffer) => void): void;
once(event: "end" | "close" | "aborted", listener: () => void): void;
once(event: "error", listener: (err: Error) => void): void;
}
export interface Http2ServerResponse {
readonly headersSent: boolean;
readonly writableEnded: boolean;
readonly writableCorked: number;
/* The same lowered surface as http.ServerResponse — the allowHTTP1
* lowering serves every connection as HTTP/1.1, where the compat
* response IS this parser's response handle. */
statusCode: number;
statusMessage: string;
setHeader(name: string, value: string | number): void;
getHeader(name: string): string | undefined;
getHeaderNames(): string[];
getRawHeaderNames(): string[];
getHeaders(): import("http").OutgoingHttpHeaders;
hasHeader(name: string): boolean;
removeHeader(name: string): void;
writeHead(
statusCode: number,
headers?: import("http").OutgoingHttpHeaders | string[],
): Http2ServerResponse;
writeHead(
statusCode: number,
statusMessage: string,
headers?: import("http").OutgoingHttpHeaders | string[],
): Http2ServerResponse;
writeContinue(): void;
writeEarlyHints(hints: Record<string, string | string[]>): void;
write(data: string | Uint8Array): void;
flushHeaders(): void;
cork(): void;
uncork(): void;
addTrailers(
headers: import("http").OutgoingHttpHeaders | ReadonlyArray<[string, string]>,
): void;
end(data?: string | Uint8Array, callback?: () => void): void;
end(callback: () => void): void;
destroy(): void;
on(event: "close", listener: () => void): void;
/* addListener IS on (Node aliases them) — and the compat res IS the
* http.ServerResponse surface (assignability into ServerResponse
* unions — IncomingMessage.pipe's destination — needs the alias). */
addListener(event: "close", listener: () => void): void;
once(event: "close", listener: () => void): void;
}
export interface Http2SecureServer {
listen(port: number, callback?: () => void): void;
close(callback?: () => void): void;
/* The bound AddressInfo (Node answers null before listen — this
* surface answers the record with port 0 there, the serverPort
* stance). */
address(): import("node:dgram").AddressInfo;
emit(event: "connection", socket: Socket): void;
on(event: "connection", listener: (socket: Socket) => void): void;
on(
event: "request",
listener: (req: Http2ServerRequest, res: Http2ServerResponse) => void,
): void;
on(event: "error", listener: (err: Error) => void): void;
on(event: "close", listener: () => void): void;
on(event: "sessionError", listener: (err: Error, session: ServerHttp2Session) => void): void;
/* The ALPN=h2 server's surface (createSecureServer WITHOUT
* allowHTTP1 — the real h2-over-TLS stack): per-stream and
* per-session listeners, exactly the h2c server's. */
on(
event: "stream",
listener: (stream: ServerHttp2Stream, headers: IncomingHttpHeaders, flags: number) => void,
): void;
on(event: "session", listener: (session: ServerHttp2Session) => void): void;
/* HTTP CONNECT (and, in Node, the h2 extended CONNECT — which the
* allowHTTP1 lowering never sees): the second argument is the raw
* socket on HTTP/1.1 connections; narrow with instanceof. */
on(
event: "connect",
listener: (
req: Http2ServerRequest | import("http").IncomingMessage,
resOrSocket: Http2ServerResponse | Socket,
) => void,
): void;
once(event: "connection", listener: (socket: Socket) => void): void;
once(
event: "request",
listener: (req: Http2ServerRequest, res: Http2ServerResponse) => void,
): void;
once(event: "error", listener: (err: Error) => void): void;
once(event: "close", listener: () => void): void;
once(event: "sessionError", listener: (err: Error, session: ServerHttp2Session) => void): void;
once(
event: "stream",
listener: (stream: ServerHttp2Stream, headers: IncomingHttpHeaders, flags: number) => void,
): void;
once(event: "session", listener: (session: ServerHttp2Session) => void): void;
once(
event: "connect",
listener: (
req: Http2ServerRequest | import("http").IncomingMessage,
resOrSocket: Http2ServerResponse | Socket,
) => void,
): void;
addListener(event: "connection", listener: (socket: Socket) => void): void;
addListener(
event: "request",
listener: (req: Http2ServerRequest, res: Http2ServerResponse) => void,
): void;
addListener(event: "error", listener: (err: Error) => void): void;
addListener(event: "close", listener: () => void): void;
addListener(
event: "sessionError",
listener: (err: Error, session: ServerHttp2Session) => void,
): void;
addListener(
event: "stream",
listener: (stream: ServerHttp2Stream, headers: IncomingHttpHeaders, flags: number) => void,
): void;
addListener(event: "session", listener: (session: ServerHttp2Session) => void): void;
addListener(
event: "connect",
listener: (
req: Http2ServerRequest | import("http").IncomingMessage,
resOrSocket: Http2ServerResponse | Socket,
) => void,
): void;
}
export interface SecureServerOptions {
allowHTTP1?: boolean;
cert?: string | Uint8Array;
key?: string | Uint8Array;
SNICallback?: (
servername: string,
cb: (err: Error | null, ctx?: import("tls").SecureContext) => void,
) => void;
/* The options-record stance (see http.RequestOptions): h2 session-
* tuning knobs are accepted as literals and ignored (no h2 session
* ever exists under the allowHTTP1 lowering); other documented keys
* fence by name; undocumented keys drop like Node. */
[option: string]: unknown;
}
/* The full Node v24 constants table (240 members), literal-typed so
* computed header keys ([HTTP2_HEADER_PATH]: value) stay literal keys.
* Every ACCESS bakes as its literal at lowering; the object itself
* never materializes (bare http2.constants value uses fence). */
export interface Http2Constants {
readonly NGHTTP2_ERR_FRAME_SIZE_ERROR: -522;
readonly NGHTTP2_SESSION_SERVER: 0;
readonly NGHTTP2_SESSION_CLIENT: 1;
readonly NGHTTP2_STREAM_STATE_IDLE: 1;
readonly NGHTTP2_STREAM_STATE_OPEN: 2;
readonly NGHTTP2_STREAM_STATE_RESERVED_LOCAL: 3;
readonly NGHTTP2_STREAM_STATE_RESERVED_REMOTE: 4;
readonly NGHTTP2_STREAM_STATE_HALF_CLOSED_LOCAL: 5;
readonly NGHTTP2_STREAM_STATE_HALF_CLOSED_REMOTE: 6;
readonly NGHTTP2_STREAM_STATE_CLOSED: 7;
readonly NGHTTP2_FLAG_NONE: 0;
readonly NGHTTP2_FLAG_END_STREAM: 1;
readonly NGHTTP2_FLAG_END_HEADERS: 4;
readonly NGHTTP2_FLAG_ACK: 1;
readonly NGHTTP2_FLAG_PADDED: 8;
readonly NGHTTP2_FLAG_PRIORITY: 32;
readonly DEFAULT_SETTINGS_HEADER_TABLE_SIZE: 4096;
readonly DEFAULT_SETTINGS_ENABLE_PUSH: 1;
readonly DEFAULT_SETTINGS_MAX_CONCURRENT_STREAMS: 4294967295;
readonly DEFAULT_SETTINGS_INITIAL_WINDOW_SIZE: 65535;
readonly DEFAULT_SETTINGS_MAX_FRAME_SIZE: 16384;
readonly DEFAULT_SETTINGS_MAX_HEADER_LIST_SIZE: 65535;
readonly DEFAULT_SETTINGS_ENABLE_CONNECT_PROTOCOL: 0;
readonly MAX_MAX_FRAME_SIZE: 16777215;
readonly MIN_MAX_FRAME_SIZE: 16384;
readonly MAX_INITIAL_WINDOW_SIZE: 2147483647;
readonly NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: 1;
readonly NGHTTP2_SETTINGS_ENABLE_PUSH: 2;
readonly NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: 3;
readonly NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: 4;
readonly NGHTTP2_SETTINGS_MAX_FRAME_SIZE: 5;
readonly NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: 6;
readonly NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL: 8;
readonly PADDING_STRATEGY_NONE: 0;
readonly PADDING_STRATEGY_ALIGNED: 1;
readonly PADDING_STRATEGY_MAX: 2;
readonly PADDING_STRATEGY_CALLBACK: 1;
readonly NGHTTP2_NO_ERROR: 0;
readonly NGHTTP2_PROTOCOL_ERROR: 1;
readonly NGHTTP2_INTERNAL_ERROR: 2;
readonly NGHTTP2_FLOW_CONTROL_ERROR: 3;
readonly NGHTTP2_SETTINGS_TIMEOUT: 4;
readonly NGHTTP2_STREAM_CLOSED: 5;
readonly NGHTTP2_FRAME_SIZE_ERROR: 6;
readonly NGHTTP2_REFUSED_STREAM: 7;
readonly NGHTTP2_CANCEL: 8;
readonly NGHTTP2_COMPRESSION_ERROR: 9;
readonly NGHTTP2_CONNECT_ERROR: 10;
readonly NGHTTP2_ENHANCE_YOUR_CALM: 11;
readonly NGHTTP2_INADEQUATE_SECURITY: 12;
readonly NGHTTP2_HTTP_1_1_REQUIRED: 13;
readonly NGHTTP2_DEFAULT_WEIGHT: 16;
readonly HTTP2_HEADER_STATUS: ":status";
readonly HTTP2_HEADER_METHOD: ":method";
readonly HTTP2_HEADER_AUTHORITY: ":authority";
readonly HTTP2_HEADER_SCHEME: ":scheme";
readonly HTTP2_HEADER_PATH: ":path";
readonly HTTP2_HEADER_PROTOCOL: ":protocol";
readonly HTTP2_HEADER_ACCEPT_ENCODING: "accept-encoding";
readonly HTTP2_HEADER_ACCEPT_LANGUAGE: "accept-language";
readonly HTTP2_HEADER_ACCEPT_RANGES: "accept-ranges";
readonly HTTP2_HEADER_ACCEPT: "accept";
readonly HTTP2_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS: "access-control-allow-credentials";
readonly HTTP2_HEADER_ACCESS_CONTROL_ALLOW_HEADERS: "access-control-allow-headers";
readonly HTTP2_HEADER_ACCESS_CONTROL_ALLOW_METHODS: "access-control-allow-methods";
readonly HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN: "access-control-allow-origin";
readonly HTTP2_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS: "access-control-expose-headers";
readonly HTTP2_HEADER_ACCESS_CONTROL_REQUEST_HEADERS: "access-control-request-headers";
readonly HTTP2_HEADER_ACCESS_CONTROL_REQUEST_METHOD: "access-control-request-method";
readonly HTTP2_HEADER_AGE: "age";
readonly HTTP2_HEADER_AUTHORIZATION: "authorization";
readonly HTTP2_HEADER_CACHE_CONTROL: "cache-control";
readonly HTTP2_HEADER_CONNECTION: "connection";
readonly HTTP2_HEADER_CONTENT_DISPOSITION: "content-disposition";
readonly HTTP2_HEADER_CONTENT_ENCODING: "content-encoding";
readonly HTTP2_HEADER_CONTENT_LENGTH: "content-length";
readonly HTTP2_HEADER_CONTENT_TYPE: "content-type";
readonly HTTP2_HEADER_COOKIE: "cookie";
readonly HTTP2_HEADER_DATE: "date";
readonly HTTP2_HEADER_ETAG: "etag";
readonly HTTP2_HEADER_FORWARDED: "forwarded";
readonly HTTP2_HEADER_HOST: "host";
readonly HTTP2_HEADER_IF_MODIFIED_SINCE: "if-modified-since";
readonly HTTP2_HEADER_IF_NONE_MATCH: "if-none-match";
readonly HTTP2_HEADER_IF_RANGE: "if-range";
readonly HTTP2_HEADER_LAST_MODIFIED: "last-modified";
readonly HTTP2_HEADER_LINK: "link";
readonly HTTP2_HEADER_LOCATION: "location";
readonly HTTP2_HEADER_RANGE: "range";
readonly HTTP2_HEADER_REFERER: "referer";
readonly HTTP2_HEADER_SERVER: "server";
readonly HTTP2_HEADER_SET_COOKIE: "set-cookie";
readonly HTTP2_HEADER_STRICT_TRANSPORT_SECURITY: "strict-transport-security";
readonly HTTP2_HEADER_TRANSFER_ENCODING: "transfer-encoding";
readonly HTTP2_HEADER_TE: "te";
readonly HTTP2_HEADER_UPGRADE_INSECURE_REQUESTS: "upgrade-insecure-requests";
readonly HTTP2_HEADER_UPGRADE: "upgrade";
readonly HTTP2_HEADER_USER_AGENT: "user-agent";
readonly HTTP2_HEADER_VARY: "vary";
readonly HTTP2_HEADER_X_CONTENT_TYPE_OPTIONS: "x-content-type-options";
readonly HTTP2_HEADER_X_FRAME_OPTIONS: "x-frame-options";
readonly HTTP2_HEADER_KEEP_ALIVE: "keep-alive";
readonly HTTP2_HEADER_PROXY_CONNECTION: "proxy-connection";
readonly HTTP2_HEADER_X_XSS_PROTECTION: "x-xss-protection";
readonly HTTP2_HEADER_ALT_SVC: "alt-svc";
readonly HTTP2_HEADER_CONTENT_SECURITY_POLICY: "content-security-policy";
readonly HTTP2_HEADER_EARLY_DATA: "early-data";
readonly HTTP2_HEADER_EXPECT_CT: "expect-ct";
readonly HTTP2_HEADER_ORIGIN: "origin";
readonly HTTP2_HEADER_PURPOSE: "purpose";
readonly HTTP2_HEADER_TIMING_ALLOW_ORIGIN: "timing-allow-origin";
readonly HTTP2_HEADER_X_FORWARDED_FOR: "x-forwarded-for";
readonly HTTP2_HEADER_PRIORITY: "priority";
readonly HTTP2_HEADER_ACCEPT_CHARSET: "accept-charset";
readonly HTTP2_HEADER_ACCESS_CONTROL_MAX_AGE: "access-control-max-age";
readonly HTTP2_HEADER_ALLOW: "allow";
readonly HTTP2_HEADER_CONTENT_LANGUAGE: "content-language";
readonly HTTP2_HEADER_CONTENT_LOCATION: "content-location";
readonly HTTP2_HEADER_CONTENT_MD5: "content-md5";
readonly HTTP2_HEADER_CONTENT_RANGE: "content-range";
readonly HTTP2_HEADER_DNT: "dnt";
readonly HTTP2_HEADER_EXPECT: "expect";
readonly HTTP2_HEADER_EXPIRES: "expires";
readonly HTTP2_HEADER_FROM: "from";
readonly HTTP2_HEADER_IF_MATCH: "if-match";
readonly HTTP2_HEADER_IF_UNMODIFIED_SINCE: "if-unmodified-since";
readonly HTTP2_HEADER_MAX_FORWARDS: "max-forwards";
readonly HTTP2_HEADER_PREFER: "prefer";
readonly HTTP2_HEADER_PROXY_AUTHENTICATE: "proxy-authenticate";
readonly HTTP2_HEADER_PROXY_AUTHORIZATION: "proxy-authorization";
readonly HTTP2_HEADER_REFRESH: "refresh";
readonly HTTP2_HEADER_RETRY_AFTER: "retry-after";
readonly HTTP2_HEADER_TRAILER: "trailer";
readonly HTTP2_HEADER_TK: "tk";
readonly HTTP2_HEADER_VIA: "via";
readonly HTTP2_HEADER_WARNING: "warning";
readonly HTTP2_HEADER_WWW_AUTHENTICATE: "www-authenticate";
readonly HTTP2_HEADER_HTTP2_SETTINGS: "http2-settings";
readonly HTTP2_METHOD_ACL: "ACL";
readonly HTTP2_METHOD_BASELINE_CONTROL: "BASELINE-CONTROL";
readonly HTTP2_METHOD_BIND: "BIND";
readonly HTTP2_METHOD_CHECKIN: "CHECKIN";
readonly HTTP2_METHOD_CHECKOUT: "CHECKOUT";
readonly HTTP2_METHOD_CONNECT: "CONNECT";
readonly HTTP2_METHOD_COPY: "COPY";
readonly HTTP2_METHOD_DELETE: "DELETE";
readonly HTTP2_METHOD_GET: "GET";
readonly HTTP2_METHOD_HEAD: "HEAD";
readonly HTTP2_METHOD_LABEL: "LABEL";
readonly HTTP2_METHOD_LINK: "LINK";
readonly HTTP2_METHOD_LOCK: "LOCK";
readonly HTTP2_METHOD_MERGE: "MERGE";
readonly HTTP2_METHOD_MKACTIVITY: "MKACTIVITY";
readonly HTTP2_METHOD_MKCALENDAR: "MKCALENDAR";
readonly HTTP2_METHOD_MKCOL: "MKCOL";
readonly HTTP2_METHOD_MKREDIRECTREF: "MKREDIRECTREF";
readonly HTTP2_METHOD_MKWORKSPACE: "MKWORKSPACE";
readonly HTTP2_METHOD_MOVE: "MOVE";
readonly HTTP2_METHOD_OPTIONS: "OPTIONS";
readonly HTTP2_METHOD_ORDERPATCH: "ORDERPATCH";
readonly HTTP2_METHOD_PATCH: "PATCH";
readonly HTTP2_METHOD_POST: "POST";
readonly HTTP2_METHOD_PRI: "PRI";
readonly HTTP2_METHOD_PROPFIND: "PROPFIND";
readonly HTTP2_METHOD_PROPPATCH: "PROPPATCH";
readonly HTTP2_METHOD_PUT: "PUT";
readonly HTTP2_METHOD_REBIND: "REBIND";
readonly HTTP2_METHOD_REPORT: "REPORT";
readonly HTTP2_METHOD_SEARCH: "SEARCH";
readonly HTTP2_METHOD_TRACE: "TRACE";
readonly HTTP2_METHOD_UNBIND: "UNBIND";
readonly HTTP2_METHOD_UNCHECKOUT: "UNCHECKOUT";
readonly HTTP2_METHOD_UNLINK: "UNLINK";
readonly HTTP2_METHOD_UNLOCK: "UNLOCK";
readonly HTTP2_METHOD_UPDATE: "UPDATE";
readonly HTTP2_METHOD_UPDATEREDIRECTREF: "UPDATEREDIRECTREF";
readonly HTTP2_METHOD_VERSION_CONTROL: "VERSION-CONTROL";
readonly HTTP_STATUS_CONTINUE: 100;
readonly HTTP_STATUS_SWITCHING_PROTOCOLS: 101;
readonly HTTP_STATUS_PROCESSING: 102;
readonly HTTP_STATUS_EARLY_HINTS: 103;
readonly HTTP_STATUS_OK: 200;
readonly HTTP_STATUS_CREATED: 201;
readonly HTTP_STATUS_ACCEPTED: 202;
readonly HTTP_STATUS_NON_AUTHORITATIVE_INFORMATION: 203;
readonly HTTP_STATUS_NO_CONTENT: 204;
readonly HTTP_STATUS_RESET_CONTENT: 205;
readonly HTTP_STATUS_PARTIAL_CONTENT: 206;
readonly HTTP_STATUS_MULTI_STATUS: 207;
readonly HTTP_STATUS_ALREADY_REPORTED: 208;
readonly HTTP_STATUS_IM_USED: 226;
readonly HTTP_STATUS_MULTIPLE_CHOICES: 300;
readonly HTTP_STATUS_MOVED_PERMANENTLY: 301;
readonly HTTP_STATUS_FOUND: 302;
readonly HTTP_STATUS_SEE_OTHER: 303;
readonly HTTP_STATUS_NOT_MODIFIED: 304;
readonly HTTP_STATUS_USE_PROXY: 305;
readonly HTTP_STATUS_TEMPORARY_REDIRECT: 307;
readonly HTTP_STATUS_PERMANENT_REDIRECT: 308;
readonly HTTP_STATUS_BAD_REQUEST: 400;
readonly HTTP_STATUS_UNAUTHORIZED: 401;
readonly HTTP_STATUS_PAYMENT_REQUIRED: 402;
readonly HTTP_STATUS_FORBIDDEN: 403;
readonly HTTP_STATUS_NOT_FOUND: 404;
readonly HTTP_STATUS_METHOD_NOT_ALLOWED: 405;
readonly HTTP_STATUS_NOT_ACCEPTABLE: 406;
readonly HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED: 407;
readonly HTTP_STATUS_REQUEST_TIMEOUT: 408;
readonly HTTP_STATUS_CONFLICT: 409;
readonly HTTP_STATUS_GONE: 410;
readonly HTTP_STATUS_LENGTH_REQUIRED: 411;
readonly HTTP_STATUS_PRECONDITION_FAILED: 412;
readonly HTTP_STATUS_PAYLOAD_TOO_LARGE: 413;
readonly HTTP_STATUS_URI_TOO_LONG: 414;
readonly HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE: 415;
readonly HTTP_STATUS_RANGE_NOT_SATISFIABLE: 416;
readonly HTTP_STATUS_EXPECTATION_FAILED: 417;
readonly HTTP_STATUS_TEAPOT: 418;
readonly HTTP_STATUS_MISDIRECTED_REQUEST: 421;
readonly HTTP_STATUS_UNPROCESSABLE_ENTITY: 422;
readonly HTTP_STATUS_LOCKED: 423;
readonly HTTP_STATUS_FAILED_DEPENDENCY: 424;
readonly HTTP_STATUS_TOO_EARLY: 425;
readonly HTTP_STATUS_UPGRADE_REQUIRED: 426;
readonly HTTP_STATUS_PRECONDITION_REQUIRED: 428;
readonly HTTP_STATUS_TOO_MANY_REQUESTS: 429;
readonly HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE: 431;
readonly HTTP_STATUS_UNAVAILABLE_FOR_LEGAL_REASONS: 451;
readonly HTTP_STATUS_INTERNAL_SERVER_ERROR: 500;
readonly HTTP_STATUS_NOT_IMPLEMENTED: 501;
readonly HTTP_STATUS_BAD_GATEWAY: 502;
readonly HTTP_STATUS_SERVICE_UNAVAILABLE: 503;
readonly HTTP_STATUS_GATEWAY_TIMEOUT: 504;
readonly HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED: 505;
readonly HTTP_STATUS_VARIANT_ALSO_NEGOTIATES: 506;
readonly HTTP_STATUS_INSUFFICIENT_STORAGE: 507;
readonly HTTP_STATUS_LOOP_DETECTED: 508;
readonly HTTP_STATUS_BANDWIDTH_LIMIT_EXCEEDED: 509;
readonly HTTP_STATUS_NOT_EXTENDED: 510;
readonly HTTP_STATUS_NETWORK_AUTHENTICATION_REQUIRED: 511;
}
export const constants: Http2Constants;
/* The eager handler is the first COMPAT 'request' listener — Node's
* route on either flavor (the allowHTTP1 server's HTTP/1.1 handles,
* the ALPN=h2 server's compat handles over streams). */
export function createSecureServer(
options: SecureServerOptions,
onRequestHandler?: (
req: import("http").IncomingMessage,
res: import("http").ServerResponse,
) => void,
): Http2SecureServer;
/* ── the REAL h2c surface (scr_http2.c: frame codec + HPACK over the
* net loop) — createServer/connect and the session/stream handles the
* Node suite's http2 family exercises. Headers cross as the canonical
* header record (the IncomingHttpHeaders index-signature shape); the
* response :status reads back as a number. Members declared here
* without a lowering fence by name at their use sites. */
/** The http2 module's own header shapes: pure index-signature records
* (the HEADER-FAMILY canonicalization — string[] arm included). */
export interface IncomingHttpHeaders {
[name: string]: number | string | string[] | undefined;
}
export interface OutgoingHttpHeaders {
[name: string]: number | string | string[] | undefined;
}
export interface Http2Session {
close(callback?: () => void): void;
destroy(): void;
readonly closed: boolean;
readonly destroyed: boolean;
readonly encrypted: boolean;
readonly type: number;
readonly alpnProtocol: string;
readonly socket: import("net").Socket;
readonly pendingSettingsAck: boolean;
ref(): void;
unref(): void;
setTimeout(msecs: number, callback?: () => void): void;
ping(callback: (err: Error | null, duration: number, payload: Uint8Array) => void): boolean;
/* The settings surface rides the checked-dynamic machinery (records
* cross as dyn values — the suite's mustCall listeners are dyn). */
settings(settings?: any, callback?: any): void;
readonly localSettings: any;
readonly remoteSettings: any;
goaway(code?: number, lastStreamID?: number, opaqueData?: Uint8Array): void;
on(event: "close" | "timeout", listener: () => void): void;
on(event: "error", listener: (err: Error) => void): void;
on(
event: "connect",
listener: (session: Http2Session, socket: import("net").Socket) => void,
): void;
on(
event: "stream",
listener: (stream: ServerHttp2Stream, headers: IncomingHttpHeaders, flags: number) => void,
): void;
on(
event: "goaway",
listener: (errorCode: number, lastStreamID: number, opaqueData?: Uint8Array) => void,
): void;
on(event: "localSettings" | "remoteSettings", listener: (settings: any) => void): void;
on(event: "frameError", listener: (type: number, code: number, id: number) => void): void;
on(event: "ping", listener: (payload: Uint8Array) => void): void;
once(event: "close" | "timeout", listener: () => void): void;
once(event: "error", listener: (err: Error) => void): void;
once(
event: "connect",
listener: (session: Http2Session, socket: import("net").Socket) => void,
): void;
once(
event: "stream",
listener: (stream: ServerHttp2Stream, headers: IncomingHttpHeaders, flags: number) => void,
): void;
once(
event: "goaway",
listener: (errorCode: number, lastStreamID: number, opaqueData?: Uint8Array) => void,
): void;
once(event: "localSettings" | "remoteSettings", listener: (settings: any) => void): void;
once(event: "frameError", listener: (type: number, code: number, id: number) => void): void;
once(event: "ping", listener: (payload: Uint8Array) => void): void;
addListener(event: "close" | "timeout", listener: () => void): void;
addListener(event: "error", listener: (err: Error) => void): void;
addListener(
event: "connect",
listener: (session: Http2Session, socket: import("net").Socket) => void,
): void;
addListener(
event: "stream",
listener: (stream: ServerHttp2Stream, headers: IncomingHttpHeaders, flags: number) => void,
): void;
addListener(
event: "goaway",
listener: (errorCode: number, lastStreamID: number, opaqueData?: Uint8Array) => void,
): void;
addListener(event: "localSettings" | "remoteSettings", listener: (settings: any) => void): void;
addListener(
event: "frameError",
listener: (type: number, code: number, id: number) => void,
): void;
addListener(event: "ping", listener: (payload: Uint8Array) => void): void;
}
export interface ClientHttp2Session extends Http2Session {
request(
headers?: OutgoingHttpHeaders,
options?: {
endStream?: boolean;
exclusive?: boolean;
parent?: number;
weight?: number;
waitForTrailers?: boolean;
},
): ClientHttp2Stream;
}
export interface ServerHttp2Session extends Http2Session {
altsvc(alt: string, originOrStream: number | string): void;
origin(...origins: string[]): void;
}
/** The shared Http2Stream core (both roles). 'data' chunks are Buffers
* (strings once setEncoding('utf8') is in force — the IncomingMessage
* chunk:any stance keeps both working). */
interface Http2StreamCore {
readonly id: number | undefined;
readonly rstCode: number;
readonly closed: boolean;
readonly destroyed: boolean;
readonly pending: boolean;
readonly aborted: boolean;
readonly session: Http2Session;
readonly sentHeaders: OutgoingHttpHeaders;
readonly state: Record<string, number>;
write(chunk: string | Uint8Array, callback?: (err?: Error | null) => void): boolean;
end(data?: string | Uint8Array, callback?: () => void): void;
end(callback: () => void): void;
close(code?: number, callback?: () => void): void;
destroy(error?: Error): void;
setEncoding(encoding: string): this;
resume(): void;
pause(): void;
setTimeout(msecs: number, callback?: () => void): void;
priority(options: Record<string, number | boolean>): void;
sendTrailers(headers: OutgoingHttpHeaders): void;
}
export interface ServerHttp2Stream extends Http2StreamCore {
on(event: "data", listener: (chunk: any) => void): void;
on(
event:
| "end"
| "close"
| "aborted"
| "ready"
| "timeout"
| "drain"
| "finish"
| "wantTrailers",
listener: () => void,
): void;
on(event: "error", listener: (err: Error) => void): void;
on(event: "frameError", listener: (type: number, code: number, id: number) => void): void;
on(event: "trailers", listener: (trailers: IncomingHttpHeaders, flags: number) => void): void;
once(event: "data", listener: (chunk: any) => void): void;
once(
event:
| "end"
| "close"
| "aborted"
| "ready"
| "timeout"
| "drain"
| "finish"
| "wantTrailers",
listener: () => void,
): void;
once(event: "error", listener: (err: Error) => void): void;
once(event: "frameError", listener: (type: number, code: number, id: number) => void): void;
once(event: "trailers", listener: (trailers: IncomingHttpHeaders, flags: number) => void): void;
addListener(event: "data", listener: (chunk: any) => void): void;
addListener(
event:
| "end"
| "close"
| "aborted"
| "ready"
| "timeout"
| "drain"
| "finish"
| "wantTrailers",
listener: () => void,
): void;
addListener(event: "error", listener: (err: Error) => void): void;
addListener(
event: "frameError",
listener: (type: number, code: number, id: number) => void,
): void;
addListener(
event: "trailers",
listener: (trailers: IncomingHttpHeaders, flags: number) => void,
): void;
respond(
headers?: OutgoingHttpHeaders,
options?: { endStream?: boolean; waitForTrailers?: boolean },
): void;
respondWithFile(
path: string,
headers?: OutgoingHttpHeaders,
options?: Record<string, unknown>,
): void;
respondWithFD(
fd: number,
headers?: OutgoingHttpHeaders,
options?: Record<string, unknown>,
): void;
pushStream(
headers: OutgoingHttpHeaders,
callback: (
err: Error | null,
pushStream: ServerHttp2Stream,
headers: OutgoingHttpHeaders,
) => void,
): void;
readonly headersSent: boolean;
readonly pushAllowed: boolean;
additionalHeaders(headers: OutgoingHttpHeaders): void;
}
export interface ClientHttp2Stream extends Http2StreamCore {
on(event: "response", listener: (headers: IncomingHttpHeaders, flags: number) => void): void;
on(event: "push", listener: (headers: IncomingHttpHeaders, flags: number) => void): void;
on(event: "headers", listener: (headers: IncomingHttpHeaders, flags: number) => void): void;
on(event: "data", listener: (chunk: any) => void): void;
on(
event:
| "end"
| "close"
| "aborted"
| "ready"
| "timeout"
| "drain"
| "finish"
| "wantTrailers",
listener: () => void,
): void;
on(event: "error", listener: (err: Error) => void): void;
on(event: "frameError", listener: (type: number, code: number, id: number) => void): void;
on(event: "trailers", listener: (trailers: IncomingHttpHeaders, flags: number) => void): void;
once(event: "response", listener: (headers: IncomingHttpHeaders, flags: number) => void): void;
once(event: "push", listener: (headers: IncomingHttpHeaders, flags: number) => void): void;
once(event: "headers", listener: (headers: IncomingHttpHeaders, flags: number) => void): void;
once(event: "data", listener: (chunk: any) => void): void;
once(
event:
| "end"
| "close"
| "aborted"
| "ready"
| "timeout"
| "drain"
| "finish"
| "wantTrailers",
listener: () => void,
): void;
once(event: "error", listener: (err: Error) => void): void;
once(event: "frameError", listener: (type: number, code: number, id: number) => void): void;
once(event: "trailers", listener: (trailers: IncomingHttpHeaders, flags: number) => void): void;
addListener(
event: "response",
listener: (headers: IncomingHttpHeaders, flags: number) => void,
): void;
addListener(
event: "push",
listener: (headers: IncomingHttpHeaders, flags: number) => void,
): void;
addListener(
event: "headers",
listener: (headers: IncomingHttpHeaders, flags: number) => void,
): void;
addListener(event: "data", listener: (chunk: any) => void): void;
addListener(
event:
| "end"
| "close"
| "aborted"
| "ready"
| "timeout"
| "drain"
| "finish"
| "wantTrailers",
listener: () => void,
): void;
addListener(event: "error", listener: (err: Error) => void): void;
addListener(
event: "frameError",
listener: (type: number, code: number, id: number) => void,
): void;
addListener(
event: "trailers",
listener: (trailers: IncomingHttpHeaders, flags: number) => void,
): void;
}
export interface Http2Server {
listen(port?: number, callback?: () => void): void;
listen(port: number, host: string, callback?: () => void): void;
close(callback?: () => void): void;
address(): import("node:dgram").AddressInfo;
setTimeout(msecs?: number, callback?: () => void): void;
ref(): void;
unref(): void;
on(
event: "stream",
listener: (stream: ServerHttp2Stream, headers: IncomingHttpHeaders, flags: number) => void,
): void;
on(event: "session", listener: (session: ServerHttp2Session) => void): void;
on(
event: "request",
listener: (req: Http2ServerRequest, res: Http2ServerResponse) => void,
): void;
on(event: "error", listener: (err: Error) => void): void;
on(event: "close" | "listening" | "timeout", listener: () => void): void;
on(event: "connection", listener: (socket: import("net").Socket) => void): void;
on(event: "sessionError", listener: (err: Error, session: ServerHttp2Session) => void): void;
once(
event: "stream",
listener: (stream: ServerHttp2Stream, headers: IncomingHttpHeaders, flags: number) => void,
): void;
once(event: "session", listener: (session: ServerHttp2Session) => void): void;
once(
event: "request",
listener: (req: Http2ServerRequest, res: Http2ServerResponse) => void,
): void;
once(event: "error", listener: (err: Error) => void): void;
once(event: "close" | "listening" | "timeout", listener: () => void): void;
once(event: "connection", listener: (socket: import("net").Socket) => void): void;
once(event: "sessionError", listener: (err: Error, session: ServerHttp2Session) => void): void;
addListener(
event: "stream",
listener: (stream: ServerHttp2Stream, headers: IncomingHttpHeaders, flags: number) => void,
): void;
addListener(event: "session", listener: (session: ServerHttp2Session) => void): void;
addListener(
event: "request",
listener: (req: Http2ServerRequest, res: Http2ServerResponse) => void,
): void;
addListener(event: "error", listener: (err: Error) => void): void;
addListener(event: "close" | "listening" | "timeout", listener: () => void): void;
addListener(event: "connection", listener: (socket: import("net").Socket) => void): void;
addListener(
event: "sessionError",
listener: (err: Error, session: ServerHttp2Session) => void,
): void;
}
export interface ServerOptions {
[option: string]: unknown;
}
export function createServer(
onRequestHandler?: (req: Http2ServerRequest, res: Http2ServerResponse) => void,
): Http2Server;
export function createServer(
options: ServerOptions,
onRequestHandler?: (req: Http2ServerRequest, res: Http2ServerResponse) => void,
): Http2Server;
export function connect(
authority: string,
listener?: (session: ClientHttp2Session, socket: import("net").Socket) => void,
): ClientHttp2Session;
export function connect(
authority: string,
options: Record<string, unknown>,
listener?: (session: ClientHttp2Session, socket: import("net").Socket) => void,
): ClientHttp2Session;
export function getDefaultSettings(): any;
export function getPackedSettings(settings: Record<string, number | boolean>): Uint8Array;
export const sensitiveHeaders: unique symbol;
}
declare module "node:http2" {
export * from "http2";
}
+59
View File
@@ -0,0 +1,59 @@
/// <reference path="./http.d.ts" />
/* node:https — http over the TLS transport (scr_tls.c).
* createServer({ cert, key }, handler) is the http server behind a
* handshake; request/get are the http client with port 443 default and
* the local-CA knobs: `ca` (PEM string/Buffer) replaces the trust
* anchors, rejectUnauthorized: false disables verification (the
* self-signed probe shape). With neither, /etc/ssl/cert.pem stands in
* for Node's bundled roots. */
declare module "https" {
import { Server } from "http";
import { Agent, ClientRequest, IncomingMessage, ServerResponse } from "http";
export { Agent, Server };
/* Node's keepAlive global agent for requests without an agent option. */
export const globalAgent: Agent;
export interface ServerOptions {
cert: string | Uint8Array;
key: string | Uint8Array;
/* The options-record stance (see http.RequestOptions). */
[option: string]: unknown;
}
export interface RequestOptions {
hostname?: string;
host?: string;
port?: number;
path?: string;
method?: string;
timeout?: number;
headers?: { [name: string]: string };
rejectUnauthorized?: boolean;
ca?: string | Uint8Array;
agent?: Agent | boolean | null;
/* Aborting destroys the request with an AbortError. */
signal?: AbortSignal;
socketPath?: string;
/* The options-record stance (see http.RequestOptions). */
[option: string]: unknown;
}
export function createServer(
options: ServerOptions,
requestListener: (req: IncomingMessage, res: ServerResponse) => void,
): Server;
/* One signature with a union first parameter, for the request-fn
* ternary's sake (see the http block). A URL target takes no options,
* which means Node's defaults — the certificate is verified against
* the default trust anchors. */
export function request(
target: RequestOptions | string | URL,
callback?: (res: IncomingMessage) => void,
): ClientRequest;
export function get(
target: RequestOptions | string | URL,
callback?: (res: IncomingMessage) => void,
): ClientRequest;
}
declare module "node:https" {
export * from "https";
}
+10
View File
@@ -0,0 +1,10 @@
declare module "node:module" {
export function createRequire(filename: string | URL): ScriptcRequire;
export const builtinModules: string[];
export function isBuiltin(moduleName: string): boolean;
export function syncBuiltinESMExports(): void;
}
declare module "module" {
export * from "node:module";
}
+374
View File
@@ -0,0 +1,374 @@
/// <reference path="./dgram.d.ts" />
/// <reference path="./dns.d.ts" />
/// <reference path="./http.d.ts" />
/// <reference path="./http2.d.ts" />
/// <reference path="./tls.d.ts" />
/* node:net — the inbound-networking slice (scr_net.c links only into
* net-using binaries). TCP only, Node's shapes narrowed to what lowers:
* createServer with an optional connection handler; listen(port[, cb])
* with the real port discoverable as address().port (the composed read
* is the lowered form — bare address() has no lowering); close([cb])
* firing once connections drain; socket write/end/destroy/pipe and the
* data/end/close/error/connect/drain events (on and once — listeners
* take at most the declared parameter). write returns Node's
* backpressure boolean; end returns void here (Node returns the socket);
* on/once return void (chaining is fenced). connect's host is a numeric IP or "localhost" (pinned to
* 127.0.0.1 — SEMANTICS.md); every failure is the async 'error' event,
* and an 'error' with no listener exits 1 like an unhandled EventEmitter
* 'error'. */
declare module "net" {
export interface Socket {
/* Whether the socket keeps the event loop alive (ref'd by default). */
ref(): void;
unref(): void;
readonly remoteAddress: string | undefined;
/* The peer's fields are cached at first read, like remoteAddress; the
* local ones are undefined while connecting and after close. */
readonly remotePort: number | undefined;
readonly remoteFamily: string | undefined;
readonly localAddress: string | undefined;
readonly localPort: number | undefined;
readonly localFamily: string | undefined;
readonly bytesRead: number;
readonly connecting: boolean;
readonly pending: boolean;
readonly readyState: "opening" | "open" | "readOnly" | "writeOnly" | "closed";
/* true once the fd is gone (destroy() or full close) — Node's flag. */
readonly destroyed: boolean;
/* the write half is open: no end() yet, no FIN sent, fd alive. */
readonly writable: boolean;
/* true until the read half is done (peer FIN / destroy). */
readonly readable: boolean;
/* every byte the write paths accepted (buffered included — Node
* counts those too; plaintext on TLS sockets). */
readonly bytesWritten: number;
/* write's callback runs once the data is flushed, with null; end's
* once the socket finishes. */
/* false when the bytes this write left unsent reach the 65536-byte
* high-water mark, as in Node; 'drain' fires when they are all sent. */
write(data: string | Uint8Array, callback?: (err?: Error | null) => void): boolean;
/* the bytes written and not yet sent */
readonly writableLength: number;
/* a write answered false and 'drain' has not fired yet */
readonly writableNeedDrain: boolean;
end(callback?: () => void): void;
end(data: string | Uint8Array, callback?: () => void): void;
destroy(): void;
/* Flow control: pause holds reads off (kernel/TCP backpressure is
* the buffer); resume flows — and discards without listeners, so
* 'end' stays reachable. Both chain, Node's shape. */
pause(): Socket;
resume(): Socket;
/* TCP_NODELAY on the live fd; missing means true. Chains. */
setNoDelay(noDelay?: boolean): Socket;
/* SO_KEEPALIVE with the initial delay in ms. Chains. */
setKeepAlive(enable?: boolean, initialDelay?: number): Socket;
/* end() now, destroy once the FIN actually flushed. */
destroySoon(): void;
/* An RST instead of a FIN; a connecting socket resets once it
* connects. Throws ERR_INVALID_HANDLE_TYPE on a TLS socket. */
resetAndDestroy(): Socket;
/* The local address, or {} while connecting and once destroyed. */
address(): AddressInfo | {};
/* setEncoding('utf8'): 'data' delivers strings — the IncomingMessage
* twin's contract. */
setEncoding(encoding: string): void;
/* socket→socket, and socket→response (the extended-CONNECT bridge
* leg: raw chunks become response body writes; EOF ends it). */
pipe(
destination: Socket | import("http").ServerResponse | import("http2").Http2ServerResponse,
): void;
/* The callback is a once('timeout') listener (removed again by 0). */
setTimeout(ms: number, callback?: () => void): void;
/* The paused-mode demux surface: once('readable') + read(1) +
* unshift — read answers exactly n buffered bytes or null. */
read(size?: number): Buffer | null;
unshift(chunk: Uint8Array): void;
/* secureConnect/secure/session are TLS-socket events declared on the
* shared socket surface (the lowering collapses TLSSocket onto this kind):
* the runtime gates on the transport, so a plain socket's
* registration never fires — Node's exact split. 'session' fires
* once with the serialized session (the received-ticket event). */
on(event: "data", listener: (chunk: Buffer) => void): void;
/* After setEncoding the chunk is a string. */
on(event: "data", listener: (chunk: string) => void): void;
on(
event:
| "end"
| "connect"
| "ready"
| "timeout"
| "drain"
| "readable"
| "finish"
| "secureConnect"
| "secure",
listener: () => void,
): void;
on(event: "close", listener: (hadError: boolean) => void): void;
on(event: "error", listener: (err: Error) => void): void;
on(event: "session", listener: (session: Buffer) => void): void;
/* The host name's resolution, before the connect. */
on(
event: "lookup",
listener: (err: Error | null, address: string, family: number, host: string) => void,
): void;
/* addListener IS on (Node aliases them) — the suite spells both. */
addListener(event: "data", listener: (chunk: Buffer) => void): void;
/* After setEncoding the chunk is a string. */
addListener(event: "data", listener: (chunk: string) => void): void;
addListener(
event:
| "end"
| "connect"
| "ready"
| "timeout"
| "drain"
| "readable"
| "finish"
| "secureConnect"
| "secure",
listener: () => void,
): void;
addListener(event: "close", listener: (hadError: boolean) => void): void;
addListener(event: "error", listener: (err: Error) => void): void;
addListener(event: "session", listener: (session: Buffer) => void): void;
/* The host name's resolution, before the connect. */
addListener(
event: "lookup",
listener: (err: Error | null, address: string, family: number, host: string) => void,
): void;
once(event: "data", listener: (chunk: Buffer) => void): void;
/* After setEncoding the chunk is a string. */
once(event: "data", listener: (chunk: string) => void): void;
once(
event:
| "end"
| "connect"
| "ready"
| "timeout"
| "drain"
| "readable"
| "finish"
| "secureConnect"
| "secure",
listener: () => void,
): void;
once(event: "close", listener: (hadError: boolean) => void): void;
once(event: "error", listener: (err: Error) => void): void;
once(event: "session", listener: (session: Buffer) => void): void;
/* The host name's resolution, before the connect. */
once(
event: "lookup",
listener: (err: Error | null, address: string, family: number, host: string) => void,
): void;
}
export interface Server {
/* Whether the server keeps the event loop alive (ref'd by default). */
ref(): void;
unref(): void;
readonly listening: boolean;
/* Connections past this many are closed with a 'drop' event;
* undefined (the default) or null lifts the cap. */
maxConnections: number | undefined;
/* Node reads it only in cluster mode: a stored value here. */
dropMaxConnection: boolean | undefined;
/* cb(null, count) on the next tick. */
getConnections(callback: (error: Error | null, count: number) => void): Server;
/* Node answers the server itself (`return this` chaining). */
listen(port: number, callback?: () => void): Server;
/* The positional bind address (the options form's host in argument
* spelling). */
listen(port: number, host: string, callback?: () => void): Server;
/* A Unix-socket server; the socket file is removed when it closes. */
listen(path: string, callback?: () => void): Server;
/* The explicit-interface bind: host is an IP literal (absent = the
* host-less dual-stack any); ipv6Only sets IPV6_V6ONLY and reusePort
* requests kernel connection distribution where Node supports it. */
listen(
options: { port: number; host?: string; ipv6Only?: boolean; reusePort?: boolean },
callback?: () => void,
): Server;
close(callback?: () => void): void;
/* The bound AddressInfo (Node answers null before listen — this
* surface answers the record with port 0 there, the serverPort
* stance). */
address(): import("node:dgram").AddressInfo;
/* The demux route: hand an accepted socket to another server. */
emit(event: "connection", socket: Socket): boolean;
on(event: "connection" | "secureConnection", listener: (socket: Socket) => void): void;
/* tls and https servers: a handshake that failed */
on(
event: "tlsClientError",
listener: (err: Error, tlsSocket: import("tls").TLSSocket) => void,
): void;
on(event: "close" | "listening", listener: () => void): void;
on(event: "drop", listener: (data?: DropArgument) => void): void;
on(event: "timeout", listener: (socket: Socket) => void): void;
on(event: "error", listener: (err: Error) => void): void;
/* The WebSocket handover: fires INSTEAD of 'request' for
* Connection: upgrade requests, with the raw socket + head bytes;
* 'connect' is its twin for CONNECT requests (HTTP tunneling). */
on(
event: "upgrade" | "connect",
listener: (req: import("http").IncomingMessage, socket: Socket, head: Buffer) => void,
): void;
/* http.createServer(handler)'s event twin (a no-parser net server
* never fires it, like Node). */
on(
event: "request" | "checkContinue" | "checkExpectation",
listener: (req: import("http").IncomingMessage, res: import("http").ServerResponse) => void,
): void;
/* addListener IS on (Node aliases them) — the suite spells both. */
addListener(event: "connection" | "secureConnection", listener: (socket: Socket) => void): void;
/* tls and https servers: a handshake that failed */
addListener(
event: "tlsClientError",
listener: (err: Error, tlsSocket: import("tls").TLSSocket) => void,
): void;
addListener(event: "close" | "listening", listener: () => void): void;
addListener(event: "drop", listener: (data?: DropArgument) => void): void;
addListener(event: "timeout", listener: (socket: Socket) => void): void;
addListener(event: "error", listener: (err: Error) => void): void;
addListener(
event: "upgrade" | "connect",
listener: (req: import("http").IncomingMessage, socket: Socket, head: Buffer) => void,
): void;
addListener(
event: "request" | "checkContinue" | "checkExpectation",
listener: (req: import("http").IncomingMessage, res: import("http").ServerResponse) => void,
): void;
once(event: "connection" | "secureConnection", listener: (socket: Socket) => void): void;
/* tls and https servers: a handshake that failed */
once(
event: "tlsClientError",
listener: (err: Error, tlsSocket: import("tls").TLSSocket) => void,
): void;
once(event: "close" | "listening", listener: () => void): void;
once(event: "drop", listener: (data?: DropArgument) => void): void;
once(event: "timeout", listener: (socket: Socket) => void): void;
once(event: "error", listener: (err: Error) => void): void;
once(
event: "upgrade" | "connect",
listener: (req: import("http").IncomingMessage, socket: Socket, head: Buffer) => void,
): void;
once(
event: "request" | "checkContinue" | "checkExpectation",
listener: (req: import("http").IncomingMessage, res: import("http").ServerResponse) => void,
): void;
}
/* The 'drop' payload: the turned-away connection's addresses. */
export interface DropArgument {
localAddress?: string;
localPort?: number;
localFamily?: string;
remoteAddress?: string;
remotePort?: number;
remoteFamily?: string;
}
export type AddressInfo = import("node:dgram").AddressInfo;
export type IPVersion = "ipv4" | "ipv6";
export interface SocketAddressInitOptions {
address?: string;
family?: IPVersion;
flowlabel?: number;
port?: number;
}
/* A native handle (scr_net.c); the getters read its parsed address. */
export class SocketAddress {
constructor(options?: SocketAddressInitOptions);
readonly address: string;
readonly family: IPVersion;
readonly port: number;
readonly flowlabel: number;
toJSON(): { address: string; port: number; family: IPVersion; flowlabel: number };
/* undefined when the input is not "<ip>[:<port>]" */
static parse(input: string): SocketAddress | undefined;
static isSocketAddress(value: unknown): value is SocketAddress;
}
/* Address, range and subnet rules; check() matches IPv4 addresses
* against IPv6 rules through their IPv4-mapped form, as Node does. */
export class BlockList {
constructor();
readonly rules: readonly string[];
readonly size: number;
addAddress(address: string | SocketAddress, type?: IPVersion): void;
addAddresses(addresses: readonly (string | SocketAddress)[], type?: IPVersion): void;
addRange(start: string | SocketAddress, end: string | SocketAddress, type?: IPVersion): void;
addSubnet(net: string | SocketAddress, prefix: number, type?: IPVersion): void;
addCIDR(cidr: string): void;
addCIDRs(cidrs: readonly string[]): void;
removeAddress(address: string | SocketAddress, type?: IPVersion): void;
removeRange(start: string | SocketAddress, end: string | SocketAddress, type?: IPVersion): void;
removeSubnet(net: string | SocketAddress, prefix: number, type?: IPVersion): void;
removeCIDR(cidr: string): void;
clear(): void;
check(address: string | SocketAddress, type?: IPVersion): boolean;
toJSON(): string[];
fromJSON(data: string | readonly string[]): void;
static isBlockList(value: unknown): value is BlockList;
static readonly PRIVATE_RANGES: readonly string[];
}
/* Node's IPv4Reg and IPv6Reg tests: 4, 6, or 0 for isIP. */
export function isIP(input: string): number;
export function isIPv4(input: string): boolean;
export function isIPv6(input: string): boolean;
/* The Socket VALUE — the `x instanceof net.Socket` narrowing target
* (the h2 compat 'connect' listener's test). Constructing bare sockets
* has no lowering; the construct signature exists for instanceof. */
export const Socket: abstract new () => Socket;
export function createServer(connectionListener?: (socket: Socket) => void): Server;
export function connect(port: number, host?: string, connectListener?: () => void): Socket;
export function createConnection(
port: number,
host?: string,
connectListener?: () => void,
): Socket;
/* The caller-resolver dial (@types/node's net.LookupFunction): the
* runtime invokes it as Node's autoSelectFamily dial does (options
* { hints, all: true }, so the callback answers an address list) and
* dials the answered addresses in order. */
export type LookupFunction = (
hostname: string,
options: import("node:dns").LookupOptions,
callback: (
err: NodeJS.ErrnoException | null,
address: string | import("node:dns").LookupAddress[],
family?: number,
) => void,
) => void;
export function connect(
options: {
port: number;
host?: string;
autoSelectFamily?: boolean;
lookup?: LookupFunction;
},
connectListener?: () => void,
): Socket;
export function createConnection(
options: {
port: number;
host?: string;
autoSelectFamily?: boolean;
lookup?: LookupFunction;
},
connectListener?: () => void,
): Socket;
/* A Unix domain socket client (Node's IpcNetConnectOpts and path
* overloads). */
export function connect(path: string, connectListener?: () => void): Socket;
export function connect(options: { path: string }, connectListener?: () => void): Socket;
export function createConnection(path: string, connectListener?: () => void): Socket;
export function createConnection(options: { path: string }, connectListener?: () => void): Socket;
/* The happy-eyeballs attempt budget (Node's process-wide knob; the
* autoSelectFamily dial consults it): one runtime int, default 250ms
* like Node's. */
export function getDefaultAutoSelectFamilyAttemptTimeout(): number;
export function setDefaultAutoSelectFamilyAttemptTimeout(value: number): void;
}
declare module "node:net" {
export * from "net";
}
+288
View File
@@ -0,0 +1,288 @@
/* node:os — the slice a CLI needs: platform() (same implementation as
* process.platform), homedir(), tmpdir() (Node's env cascade), EOL
* (always "\n" — POSIX pinned, like path.sep), and networkInterfaces()
* (getifaddrs(3); the interface shapes mirror @types/node's exactly so
* both type worlds map to the same IR structure). */
declare module "os" {
export function platform(): string;
export function homedir(): string;
export function tmpdir(): string;
/* uname(2)'s release field — Node's own implementation. */
export function release(): string;
/* uname(2)'s sysname field ("Darwin", "Linux") — Node's os.type(). */
export function type(): string;
/* Total system memory in bytes (sysctl hw.memsize / sysconf). */
export function totalmem(): number;
export function availableParallelism(): number;
/* The passwd-entry snapshot (uv_os_get_passwd): shell is `string |
* null` to match @types/node (POSIX always answers the string arm);
* homedir is pw_dir — NOT os.homedir()'s $HOME-first cascade. The type
* is interned explicitly in type-mapper.ts (the AddressInfo pattern — this
* concrete form and @types/node's UserInfo<string> both), and the call
* assembles field-by-field in lowerOsUserInfoCall. */
export interface UserInfo {
username: string;
uid: number;
gid: number;
shell: string | null;
homedir: string;
}
export function userInfo(): UserInfo;
/* The TARGET platform's line terminator (a compile-time constant:
* "\r\n" under a win32 triple, "\n" everywhere else). */
export const EOL: "\r\n" | "\n";
export interface NetworkInterfaceBase {
address: string;
netmask: string;
mac: string;
internal: boolean;
cidr: string | null;
scopeid?: number;
}
export interface NetworkInterfaceInfoIPv4 extends NetworkInterfaceBase {
family: "IPv4";
}
export interface NetworkInterfaceInfoIPv6 extends NetworkInterfaceBase {
family: "IPv6";
scopeid: number;
}
export type NetworkInterfaceInfo = NetworkInterfaceInfoIPv4 | NetworkInterfaceInfoIPv6;
export function networkInterfaces(): { [name: string]: NetworkInterfaceInfo[] | undefined };
/* Host facts, libuv's answers: model/speed/times per logical CPU, the
* host name, the 1/5/15-minute load averages, uptime in seconds, free
* memory in bytes, process.arch, uname's machine and version fields,
* the byte order, and a process's scheduling priority (an omitted pid
* is the current process). Shapes mirror @types/node's. */
export interface CpuInfo {
model: string;
speed: number;
times: {
user: number;
nice: number;
sys: number;
idle: number;
irq: number;
};
}
export function cpus(): CpuInfo[];
export function hostname(): string;
export function loadavg(): number[];
export function uptime(): number;
export function freemem(): number;
export function arch(): string;
export function machine(): string;
export function version(): string;
export function endianness(): "BE" | "LE";
export function getPriority(pid?: number): number;
export function setPriority(priority: number): void;
export function setPriority(pid: number, priority: number): void;
/* The TARGET platform's null device ("\\\\.\\nul" under a win32 triple). */
export const devNull: string;
/* The platform's constants: null-prototype objects with read-only
* members, `signals` frozen (Node's internalBinding('constants').os). */
export type SignalConstants = {
[
key in
| "SIGABRT"
| "SIGALRM"
| "SIGBUS"
| "SIGCHLD"
| "SIGCONT"
| "SIGFPE"
| "SIGHUP"
| "SIGILL"
| "SIGINT"
| "SIGIO"
| "SIGIOT"
| "SIGKILL"
| "SIGPIPE"
| "SIGPOLL"
| "SIGPROF"
| "SIGPWR"
| "SIGQUIT"
| "SIGSEGV"
| "SIGSTKFLT"
| "SIGSTOP"
| "SIGSYS"
| "SIGTERM"
| "SIGTRAP"
| "SIGTSTP"
| "SIGTTIN"
| "SIGTTOU"
| "SIGUNUSED"
| "SIGURG"
| "SIGUSR1"
| "SIGUSR2"
| "SIGVTALRM"
| "SIGWINCH"
| "SIGXCPU"
| "SIGXFSZ"
| "SIGBREAK"
| "SIGLOST"
| "SIGINFO"
]: number;
};
export namespace constants {
const UV_UDP_REUSEADDR: number;
namespace signals {}
const signals: SignalConstants;
namespace errno {
const E2BIG: number;
const EACCES: number;
const EADDRINUSE: number;
const EADDRNOTAVAIL: number;
const EAFNOSUPPORT: number;
const EAGAIN: number;
const EALREADY: number;
const EBADF: number;
const EBADMSG: number;
const EBUSY: number;
const ECANCELED: number;
const ECHILD: number;
const ECONNABORTED: number;
const ECONNREFUSED: number;
const ECONNRESET: number;
const EDEADLK: number;
const EDESTADDRREQ: number;
const EDOM: number;
const EDQUOT: number;
const EEXIST: number;
const EFAULT: number;
const EFBIG: number;
const EHOSTUNREACH: number;
const EIDRM: number;
const EILSEQ: number;
const EINPROGRESS: number;
const EINTR: number;
const EINVAL: number;
const EIO: number;
const EISCONN: number;
const EISDIR: number;
const ELOOP: number;
const EMFILE: number;
const EMLINK: number;
const EMSGSIZE: number;
const EMULTIHOP: number;
const ENAMETOOLONG: number;
const ENETDOWN: number;
const ENETRESET: number;
const ENETUNREACH: number;
const ENFILE: number;
const ENOBUFS: number;
const ENODATA: number;
const ENODEV: number;
const ENOENT: number;
const ENOEXEC: number;
const ENOLCK: number;
const ENOLINK: number;
const ENOMEM: number;
const ENOMSG: number;
const ENOPROTOOPT: number;
const ENOSPC: number;
const ENOSR: number;
const ENOSTR: number;
const ENOSYS: number;
const ENOTCONN: number;
const ENOTDIR: number;
const ENOTEMPTY: number;
const ENOTSOCK: number;
const ENOTSUP: number;
const ENOTTY: number;
const ENXIO: number;
const EOPNOTSUPP: number;
const EOVERFLOW: number;
const EPERM: number;
const EPIPE: number;
const EPROTO: number;
const EPROTONOSUPPORT: number;
const EPROTOTYPE: number;
const ERANGE: number;
const EROFS: number;
const ESPIPE: number;
const ESRCH: number;
const ESTALE: number;
const ETIME: number;
const ETIMEDOUT: number;
const ETXTBSY: number;
const EWOULDBLOCK: number;
const EXDEV: number;
const WSAEINTR: number;
const WSAEBADF: number;
const WSAEACCES: number;
const WSAEFAULT: number;
const WSAEINVAL: number;
const WSAEMFILE: number;
const WSAEWOULDBLOCK: number;
const WSAEINPROGRESS: number;
const WSAEALREADY: number;
const WSAENOTSOCK: number;
const WSAEDESTADDRREQ: number;
const WSAEMSGSIZE: number;
const WSAEPROTOTYPE: number;
const WSAENOPROTOOPT: number;
const WSAEPROTONOSUPPORT: number;
const WSAESOCKTNOSUPPORT: number;
const WSAEOPNOTSUPP: number;
const WSAEPFNOSUPPORT: number;
const WSAEAFNOSUPPORT: number;
const WSAEADDRINUSE: number;
const WSAEADDRNOTAVAIL: number;
const WSAENETDOWN: number;
const WSAENETUNREACH: number;
const WSAENETRESET: number;
const WSAECONNABORTED: number;
const WSAECONNRESET: number;
const WSAENOBUFS: number;
const WSAEISCONN: number;
const WSAENOTCONN: number;
const WSAESHUTDOWN: number;
const WSAETOOMANYREFS: number;
const WSAETIMEDOUT: number;
const WSAECONNREFUSED: number;
const WSAELOOP: number;
const WSAENAMETOOLONG: number;
const WSAEHOSTDOWN: number;
const WSAEHOSTUNREACH: number;
const WSAENOTEMPTY: number;
const WSAEPROCLIM: number;
const WSAEUSERS: number;
const WSAEDQUOT: number;
const WSAESTALE: number;
const WSAEREMOTE: number;
const WSASYSNOTREADY: number;
const WSAVERNOTSUPPORTED: number;
const WSANOTINITIALISED: number;
const WSAEDISCON: number;
const WSAENOMORE: number;
const WSAECANCELLED: number;
const WSAEINVALIDPROCTABLE: number;
const WSAEINVALIDPROVIDER: number;
const WSAEPROVIDERFAILEDINIT: number;
const WSASYSCALLFAILURE: number;
const WSASERVICE_NOT_FOUND: number;
const WSATYPE_NOT_FOUND: number;
const WSA_E_NO_MORE: number;
const WSA_E_CANCELLED: number;
const WSAEREFUSED: number;
}
namespace dlopen {
const RTLD_LAZY: number;
const RTLD_NOW: number;
const RTLD_GLOBAL: number;
const RTLD_LOCAL: number;
const RTLD_DEEPBIND: number;
}
namespace priority {
const PRIORITY_LOW: number;
const PRIORITY_BELOW_NORMAL: number;
const PRIORITY_NORMAL: number;
const PRIORITY_ABOVE_NORMAL: number;
const PRIORITY_HIGH: number;
const PRIORITY_HIGHEST: number;
}
}
}
declare module "node:os" {
export * from "os";
}
+22
View File
@@ -0,0 +1,22 @@
/// <reference path="./path.d.ts" />
declare module "path/posix" {
export function join(...paths: string[]): string;
export function resolve(...paths: string[]): string;
export function normalize(path: string): string;
export function dirname(path: string): string;
export function basename(path: string, suffix?: string): string;
export function extname(path: string): string;
export function isAbsolute(path: string): boolean;
export function relative(from: string, to: string): string;
export function toNamespacedPath(path: string): string;
export function matchesGlob(path: string, pattern: string): boolean;
export function parse(path: string): import("path").ParsedPath;
export function format(pathObject: import("path").FormatInputPathObject): string;
export const sep: "/";
export const delimiter: ":";
}
declare module "node:path/posix" {
export * from "path/posix";
}
+22
View File
@@ -0,0 +1,22 @@
/// <reference path="./path.d.ts" />
declare module "path/win32" {
export function join(...paths: string[]): string;
export function resolve(...paths: string[]): string;
export function normalize(path: string): string;
export function dirname(path: string): string;
export function basename(path: string, suffix?: string): string;
export function extname(path: string): string;
export function isAbsolute(path: string): boolean;
export function relative(from: string, to: string): string;
export function toNamespacedPath(path: string): string;
export function matchesGlob(path: string, pattern: string): boolean;
export function parse(path: string): import("path").ParsedPath;
export function format(pathObject: import("path").FormatInputPathObject): string;
export const sep: "\\";
export const delimiter: ";";
}
declare module "node:path/win32" {
export * from "path/win32";
}
+55
View File
@@ -0,0 +1,55 @@
/// <reference path="./path-posix.d.ts" />
/// <reference path="./path-win32.d.ts" />
/* node:path — the TARGET platform's implementation (Node's rule): posix
* on posix triples, path.win32 under a win32 triple, with the posix and
* win32 namespaces answering their own platform anywhere. Every function
* is a pure string algorithm matching Node's implementations
* exactly; join/resolve are variadic like Node's. */
declare module "path" {
export function join(...paths: string[]): string;
export function resolve(...paths: string[]): string;
export function normalize(path: string): string;
export function dirname(path: string): string;
export function basename(path: string, suffix?: string): string;
export function extname(path: string): string;
export function isAbsolute(path: string): boolean;
export function relative(from: string, to: string): string;
export function toNamespacedPath(path: string): string;
/* matchesGlob() is Node's glob matcher: brace expansion, extglobs, POSIX
* classes and the ** and dot rules. */
export function matchesGlob(path: string, pattern: string): boolean;
/* parse() splits a path into its root, directory, base, extension and
* name; format() joins those fields back (Node's `||` defaults). */
export interface ParsedPath {
root: string;
dir: string;
base: string;
ext: string;
name: string;
}
export interface FormatInputPathObject {
root?: string | undefined;
dir?: string | undefined;
base?: string | undefined;
ext?: string | undefined;
name?: string | undefined;
}
export function parse(path: string): ParsedPath;
export function format(pathObject: FormatInputPathObject): string;
/* The union @types/node declares: the bare module answers the TARGET
* platform's value (a compile-time constant — "\\"/";" under a win32
* triple, "/"/":" everywhere else). */
export const sep: "\\" | "/";
export const delimiter: ";" | ":";
/* The platform-specific namespaces (real Node submodules too —
* "node:path/posix" resolves). Each carries the FULL surface and
* answers ITS platform's rules on any target, Node's own contract;
* the bare module IS the target platform's implementation. */
export const posix: typeof import("path/posix");
export const win32: typeof import("path/win32");
}
declare module "node:path" {
export * from "path";
}
+11
View File
@@ -0,0 +1,11 @@
/* Node's process module default export is the global process object. These
* declaration aliases let the ESM spelling typecheck against the same
* deliberately narrow fallback surface when @types/node is absent. */
declare module "process" {
export = process;
}
declare module "node:process" {
import process = require("process");
export = process;
}
+18
View File
@@ -0,0 +1,18 @@
/* node:punycode (deprecated, DEP0040) — punycode.js 2.1.0: decode and
* toUnicode throw its RangeErrors on malformed input; ucs2 converts
* between strings and UTF-16 code point arrays. */
declare module "punycode" {
export function decode(string: string): string;
export function encode(string: string): string;
export function toUnicode(domain: string): string;
export function toASCII(domain: string): string;
export namespace ucs2 {
function decode(string: string): number[];
function encode(codePoints: readonly number[]): string;
}
export const version: string;
}
declare module "node:punycode" {
export * from "punycode";
}
@@ -0,0 +1,55 @@
/* node:querystring — Node's legacy query-string codec (NOT
* URLSearchParams: '+' means space on the parse side and escape encodes
* spaces as %20). parse answers the null-prototype dictionary as a pure
* index-signature record (repeated keys become string[] buckets;
* @types/node's Dict adds an undefined arm the lowering tolerates);
* stringify serializes string/number/boolean values and arrays of those
* (Node's rules: arrays expand to repeated keys, null/undefined and
* anything else serialize as the empty value). The maxKeys option lowers
* (0 removes the cap, Node's rule); the custom encoder/decoder options
* typecheck under the options-record stance and fence by name at the
* call. decode/encode are Node's own aliases of parse/stringify. */
declare module "querystring" {
export interface ParseOptions {
maxKeys?: number;
decodeURIComponent?: (str: string) => string;
[option: string]: unknown;
}
export interface StringifyOptions {
encodeURIComponent?: (str: string) => string;
[option: string]: unknown;
}
export interface ParsedUrlQuery {
[key: string]: string | string[] | undefined;
}
export interface ParsedUrlQueryInput {
[key: string]:
| string
| number
| boolean
| bigint
| ReadonlyArray<string | number | boolean | bigint>
| null
| undefined;
}
export function parse(
str: string,
sep?: string | null,
eq?: string | null,
options?: ParseOptions,
): ParsedUrlQuery;
export function stringify(
obj?: ParsedUrlQueryInput,
sep?: string | null,
eq?: string | null,
options?: StringifyOptions,
): string;
export const decode: typeof parse;
export const encode: typeof stringify;
export function escape(str: string): string;
export function unescape(str: string): string;
}
declare module "node:querystring" {
export * from "querystring";
}
@@ -0,0 +1,21 @@
declare module "stream/consumers" {
/* The promise consumers over a readable: accumulate every chunk and
* settle — text (the utf8 decode), json (the text through JSON.parse:
* `unknown`, validated with a checked cast; malformed input rejects
* with the parse's SyntaxError), buffer (the concatenated bytes;
* string chunks contribute their utf8 bytes, Node's Blob rule).
* Stream errors reject; an early close rejects with
* ERR_STREAM_PREMATURE_CLOSE. arrayBuffer answers an ArrayBuffer over
* the bytes; blob fences per site. */
function text(stream: unknown): Promise<string>;
function json(stream: unknown): Promise<unknown>;
function buffer(stream: unknown): Promise<Buffer>;
function arrayBuffer(stream: unknown): Promise<ArrayBuffer>;
function blob(stream: unknown): Promise<unknown>;
export { arrayBuffer, blob, buffer, json, text };
}
declare module "node:stream/consumers" {
import streamConsumers = require("stream/consumers");
export = streamConsumers;
}
@@ -0,0 +1,28 @@
/// <reference path="./stream.d.ts" />
declare module "stream/promises" {
/* The promise forms of finished/pipeline: a void promise the stream's
* terminal point settles (rejected with the error, or
* ERR_STREAM_PREMATURE_CLOSE on an early close). Stream arguments are
* the lowered surface — iterables/generators/functions fence per site.
* pipeline's trailing { signal, end } object and finished's options
* (readable, writable, error, cleanup, signal) are supported. A last
* stage that is an async function resolves the promise with its value. */
function pipeline<T extends unknown[]>(...streams: T): import("stream").PipelinePromise<T>;
function finished(
stream: unknown,
options?: {
readable?: boolean;
writable?: boolean;
error?: boolean;
cleanup?: boolean;
signal?: AbortSignal;
} | null,
): Promise<void>;
export { pipeline, finished };
}
declare module "node:stream/promises" {
import streamPromises = require("stream/promises");
export = streamPromises;
}
+17
View File
@@ -0,0 +1,17 @@
/* node:stream/web — the module exports the web stream globals themselves
* (require("stream/web").ReadableStream === globalThis.ReadableStream).
* ReadableStream is the implemented class; its reader and controller are
* exported as types. */
declare module "stream/web" {
export const ReadableStream: typeof globalThis.ReadableStream;
export type ReadableStream<T = unknown> = globalThis.ReadableStream<T>;
export type ReadableStreamDefaultReader<T = unknown> = globalThis.ReadableStreamDefaultReader<T>;
export type ReadableStreamDefaultController<T = unknown> =
globalThis.ReadableStreamDefaultController<T>;
export type UnderlyingSource<T = unknown> = globalThis.UnderlyingSource<T>;
export type QueuingStrategy<T = unknown> = globalThis.QueuingStrategy<T>;
}
declare module "node:stream/web" {
export * from "stream/web";
}
+644
View File
@@ -0,0 +1,644 @@
/// <reference path="./events.d.ts" />
/* node:stream — the static stream classes (runtime-provided, the
* EventEmitter-hierarchy precedent). Phase 1 is the OPTIONS-OBJECT
* constructor surface: `new Readable({ read() {} })`, `new Writable({
* write(chunk, enc, cb) {} })`, Duplex/Transform/PassThrough, push/read,
* flowing vs paused, write/end backpressure + 'drain', pipe/unpipe, and
* destroy — with Node's event orderings. Chunks are BYTES (Buffer), with
* strings converted at push/write like Node, and any value on an
* object-mode side (objectMode, readableObjectMode, writableObjectMode —
* the object-mode constructor overloads come first). The runtime-fired
* events carry per-class FORCED argument tuples ('data' is one chunk,
* 'error' one Error, 'pipe'/'unpipe' one Readable, the rest empty) — the
* overloads below hand tsc the same contract so unannotated listener
* parameters contextually type in .js sources. */
declare module "stream" {
import { EventEmitter } from "events";
interface ReadableOptions {
highWaterMark?: number;
encoding?: string;
objectMode?: boolean;
read?(this: Readable, size: number): void;
destroy?(this: Readable, error: Error | null, callback: (error?: Error | null) => void): void;
construct?(this: Readable, callback: (error?: Error | null) => void): void;
autoDestroy?: boolean;
emitClose?: boolean;
captureRejections?: boolean;
defaultEncoding?: string;
signal?: AbortSignal;
}
interface WritableOptions {
highWaterMark?: number;
decodeStrings?: boolean;
defaultEncoding?: string;
objectMode?: boolean;
write?(
this: Writable,
chunk: Buffer,
encoding: string,
callback: (error?: Error | null) => void,
): void;
writev?(
this: Writable,
chunks: Array<{ chunk: any; encoding: string }>,
callback: (error?: Error | null) => void,
): void;
final?(this: Writable, callback: (error?: Error | null) => void): void;
destroy?(this: Writable, error: Error | null, callback: (error?: Error | null) => void): void;
construct?(this: Writable, callback: (error?: Error | null) => void): void;
autoDestroy?: boolean;
emitClose?: boolean;
captureRejections?: boolean;
signal?: AbortSignal;
}
interface DuplexOptions {
highWaterMark?: number;
readableHighWaterMark?: number;
writableHighWaterMark?: number;
encoding?: string;
decodeStrings?: boolean;
objectMode?: boolean;
readableObjectMode?: boolean;
writableObjectMode?: boolean;
allowHalfOpen?: boolean;
read?(this: Duplex, size: number): void;
write?(
this: Duplex,
chunk: Buffer,
encoding: string,
callback: (error?: Error | null) => void,
): void;
final?(this: Duplex, callback: (error?: Error | null) => void): void;
destroy?(this: Duplex, error: Error | null, callback: (error?: Error | null) => void): void;
construct?(this: Duplex, callback: (error?: Error | null) => void): void;
autoDestroy?: boolean;
emitClose?: boolean;
captureRejections?: boolean;
readable?: boolean;
writable?: boolean;
defaultEncoding?: string;
writev?(
this: Duplex,
chunks: Array<{ chunk: any; encoding: string }>,
callback: (error?: Error | null) => void,
): void;
signal?: AbortSignal;
}
interface TransformOptions {
highWaterMark?: number;
readableHighWaterMark?: number;
writableHighWaterMark?: number;
encoding?: string;
decodeStrings?: boolean;
objectMode?: boolean;
readableObjectMode?: boolean;
writableObjectMode?: boolean;
allowHalfOpen?: boolean;
transform?(
this: Transform,
chunk: Buffer,
encoding: string,
callback: (error?: Error | null, data?: Buffer | string) => void,
): void;
flush?(this: Transform, callback: (error?: Error | null, data?: Buffer | string) => void): void;
destroy?(this: Transform, error: Error | null, callback: (error?: Error | null) => void): void;
construct?(this: Transform, callback: (error?: Error | null) => void): void;
read?(this: Transform, size: number): void;
write?(
this: Transform,
chunk: Buffer,
encoding: string,
callback: (error?: Error | null) => void,
): void;
final?(this: Transform, callback: (error?: Error | null) => void): void;
writev?(this: Transform, chunks: unknown[], callback: (error?: Error | null) => void): void;
autoDestroy?: boolean;
emitClose?: boolean;
captureRejections?: boolean;
readable?: boolean;
writable?: boolean;
defaultEncoding?: string;
signal?: AbortSignal;
}
/* Object-mode options (objectMode: true, or a Duplex's or Transform's
* readableObjectMode / writableObjectMode): chunks and completion data
* are any values, and an object-mode write's encoding argument reaches
* write/transform as passed (undefined when absent, null when a
* callback took its place). The constructors take these before the
* byte-mode options, so a byte-mode stream's callbacks keep Buffer
* chunks. */
type ObjectModeEncoding = string | null | undefined;
type ObjectModeFlag =
| { objectMode: true }
| { readableObjectMode: true }
| { writableObjectMode: true };
interface ObjectWritableOptions extends Omit<WritableOptions, "objectMode" | "write"> {
objectMode: true;
write?(
this: Writable,
chunk: any,
encoding: ObjectModeEncoding,
callback: (error?: Error | null) => void,
): void;
}
type ObjectDuplexOptions = Omit<DuplexOptions, "write"> & {
write?(
this: Duplex,
chunk: any,
encoding: ObjectModeEncoding,
callback: (error?: Error | null) => void,
): void;
} & ObjectModeFlag;
type ObjectTransformOptions = Omit<TransformOptions, "transform" | "flush" | "write"> & {
transform?(
this: Transform,
chunk: any,
encoding: ObjectModeEncoding,
callback: (error?: Error | null, data?: any) => void,
): void;
flush?(this: Transform, callback: (error?: Error | null, data?: any) => void): void;
write?(
this: Transform,
chunk: any,
encoding: ObjectModeEncoding,
callback: (error?: Error | null) => void,
): void;
} & ObjectModeFlag;
/* decodeStrings: false — a string write reaches write/transform as the
* string with its encoding (Buffers keep 'buffer'). */
interface StringWritableOptions extends Omit<WritableOptions, "decodeStrings" | "write"> {
decodeStrings: false;
write?(
this: Writable,
chunk: string | Buffer,
encoding: string,
callback: (error?: Error | null) => void,
): void;
}
type StringDuplexOptions = Omit<DuplexOptions, "decodeStrings" | "write"> & {
decodeStrings: false;
write?(
this: Duplex,
chunk: string | Buffer,
encoding: string,
callback: (error?: Error | null) => void,
): void;
};
type StringTransformOptions = Omit<TransformOptions, "decodeStrings" | "transform"> & {
decodeStrings: false;
transform?(
this: Transform,
chunk: string | Buffer,
encoding: string,
callback: (error?: Error | null, data?: Buffer | string) => void,
): void;
};
/* The operators' options: concurrency and highWaterMark bound map's
* in-flight callbacks and queued results. */
interface ArrayOptions {
concurrency?: number;
highWaterMark?: number;
signal?: AbortSignal;
}
class Readable extends EventEmitter {
constructor(opts?: ReadableOptions);
/* The underscore-method surface (@types/node declares these on the
* classes; assigning them post-construction shadows the prototype
* method and the machinery calls through the assignment — lowered
* onto the option-callback slots). */
_read(size: number): void;
_destroy(error: Error | null, callback: (error?: Error | null) => void): void;
[Symbol.asyncIterator](): AsyncIterableIterator<any>;
iterator(options?: { destroyOnReturn?: boolean }): AsyncIterableIterator<any>;
/* compose(this, stage), destroyed when options.signal aborts. */
compose(stage: ComposeStage, options?: { signal: AbortSignal }): Duplex;
push(chunk: Buffer | string | null, encoding?: string): boolean;
/* An object-mode stream's values. */
push(chunk: any, encoding?: string): boolean;
unshift(chunk: Buffer | string, encoding?: string): void;
unshift(chunk: any, encoding?: string): void;
/* A Buffer, a string once encoded, an object-mode value, or null (as
* @types/node types it). */
read(size?: number): any;
pause(): this;
resume(): this;
isPaused(): boolean;
setEncoding(encoding: string): this;
pipe<T extends NodeJS.WritableStream>(destination: T, options?: { end?: boolean }): T;
unpipe(destination?: NodeJS.WritableStream): this;
destroy(error?: Error): this;
readonly readable: boolean;
readonly readableEnded: boolean;
readonly readableFlowing: boolean | null;
readonly readableLength: number;
readonly readableHighWaterMark: number;
readonly readableObjectMode: boolean;
readonly closed: boolean;
readonly destroyed: boolean;
readonly errored: Error | null;
static from(iterable: unknown, options?: ReadableOptions): Readable;
/* A Readable over a web ReadableStream's reader. */
static fromWeb(
readableStream: globalThis.ReadableStream,
options?: {
encoding?: string;
highWaterMark?: number;
objectMode?: boolean;
signal?: AbortSignal;
},
): Readable;
/* The Readable operators: chunks are strings or Buffers, map and
* flatMap callbacks return strings or Buffers (flatMap: arrays of
* them, or strings), and callbacks that declare the options argument
* are refused. */
map(
fn: (data: any, options?: Pick<ArrayOptions, "signal">) => any,
options?: ArrayOptions,
): Readable;
filter(
fn: (data: any, options?: Pick<ArrayOptions, "signal">) => boolean | Promise<boolean>,
options?: ArrayOptions,
): Readable;
forEach(
fn: (data: any, options?: Pick<ArrayOptions, "signal">) => void | Promise<void>,
options?: ArrayOptions,
): Promise<void>;
toArray(options?: Pick<ArrayOptions, "signal">): Promise<any[]>;
some(
fn: (data: any, options?: Pick<ArrayOptions, "signal">) => boolean | Promise<boolean>,
options?: ArrayOptions,
): Promise<boolean>;
find(
fn: (data: any, options?: Pick<ArrayOptions, "signal">) => boolean | Promise<boolean>,
options?: ArrayOptions,
): Promise<any>;
every(
fn: (data: any, options?: Pick<ArrayOptions, "signal">) => boolean | Promise<boolean>,
options?: ArrayOptions,
): Promise<boolean>;
flatMap(
fn: (data: any, options?: Pick<ArrayOptions, "signal">) => any,
options?: ArrayOptions,
): Readable;
drop(limit: number, options?: Pick<ArrayOptions, "signal">): Readable;
take(limit: number, options?: Pick<ArrayOptions, "signal">): Readable;
reduce<T = any>(
fn: (previous: any, data: any, options?: Pick<ArrayOptions, "signal">) => T,
initial?: undefined,
options?: Pick<ArrayOptions, "signal">,
): Promise<T>;
reduce<T = any>(
fn: (previous: T, data: any, options?: Pick<ArrayOptions, "signal">) => T,
initial: T,
options?: Pick<ArrayOptions, "signal">,
): Promise<T>;
on(event: "data", listener: (chunk: any) => void): this;
on(event: "end" | "close" | "readable" | "pause" | "resume", listener: () => void): this;
on(event: "error", listener: (err: Error) => void): this;
on(event: string, listener: (...args: any[]) => void): this;
once(event: "data", listener: (chunk: any) => void): this;
once(event: "end" | "close" | "readable" | "pause" | "resume", listener: () => void): this;
once(event: "error", listener: (err: Error) => void): this;
once(event: string, listener: (...args: any[]) => void): this;
addListener(event: "data", listener: (chunk: any) => void): this;
addListener(
event: "end" | "close" | "readable" | "pause" | "resume",
listener: () => void,
): this;
addListener(event: "error", listener: (err: Error) => void): this;
addListener(event: string, listener: (...args: any[]) => void): this;
prependListener(event: "data", listener: (chunk: any) => void): this;
prependListener(
event: "end" | "close" | "readable" | "pause" | "resume",
listener: () => void,
): this;
prependListener(event: "error", listener: (err: Error) => void): this;
prependListener(event: string, listener: (...args: any[]) => void): this;
prependOnceListener(event: "data", listener: (chunk: any) => void): this;
prependOnceListener(
event: "end" | "close" | "readable" | "pause" | "resume",
listener: () => void,
): this;
prependOnceListener(event: "error", listener: (err: Error) => void): this;
prependOnceListener(event: string, listener: (...args: any[]) => void): this;
off(event: "data", listener: (chunk: any) => void): this;
off(event: "end" | "close" | "readable" | "pause" | "resume", listener: () => void): this;
off(event: "error", listener: (err: Error) => void): this;
off(event: string, listener: (...args: any[]) => void): this;
removeListener(event: "data", listener: (chunk: any) => void): this;
removeListener(
event: "end" | "close" | "readable" | "pause" | "resume",
listener: () => void,
): this;
removeListener(event: "error", listener: (err: Error) => void): this;
removeListener(event: string, listener: (...args: any[]) => void): this;
}
class Writable extends EventEmitter {
constructor(opts: ObjectWritableOptions);
constructor(opts: StringWritableOptions);
constructor(opts?: WritableOptions);
/* The underscore-method surface (see Readable). */
_write(chunk: any, encoding: string, callback: (error?: Error | null) => void): void;
_writev(
chunks: Array<{ chunk: any; encoding: string }>,
callback: (error?: Error | null) => void,
): void;
_final(callback: (error?: Error | null) => void): void;
_destroy(error: Error | null, callback: (error?: Error | null) => void): void;
write(chunk: Buffer | string, callback?: (error?: Error | null) => void): boolean;
write(
chunk: Buffer | string,
encoding: string,
callback?: (error?: Error | null) => void,
): boolean;
/* An object-mode stream's values (the encoding argument reaches
* _write as passed). */
write(chunk: any, callback?: (error?: Error | null) => void): boolean;
write(
chunk: any,
encoding: ObjectModeEncoding,
callback?: (error?: Error | null) => void,
): boolean;
end(callback?: () => void): this;
end(chunk: Buffer | string, callback?: () => void): this;
end(chunk: Buffer | string, encoding: string, callback?: () => void): this;
end(chunk: any, callback?: () => void): this;
end(chunk: any, encoding: ObjectModeEncoding, callback?: () => void): this;
cork(): void;
uncork(): void;
setDefaultEncoding(encoding: string): this;
destroy(error?: Error): this;
readonly writable: boolean;
readonly writableEnded: boolean;
readonly writableFinished: boolean;
readonly writableLength: number;
readonly writableHighWaterMark: number;
readonly writableObjectMode: boolean;
readonly writableNeedDrain: boolean;
readonly writableCorked: number;
readonly closed: boolean;
readonly destroyed: boolean;
readonly errored: Error | null;
on(event: "drain" | "finish" | "close", listener: () => void): this;
on(event: "error", listener: (err: Error) => void): this;
on(event: "pipe" | "unpipe", listener: (src: Readable) => void): this;
on(event: string, listener: (...args: any[]) => void): this;
once(event: "drain" | "finish" | "close", listener: () => void): this;
once(event: "error", listener: (err: Error) => void): this;
once(event: "pipe" | "unpipe", listener: (src: Readable) => void): this;
once(event: string, listener: (...args: any[]) => void): this;
addListener(event: "drain" | "finish" | "close", listener: () => void): this;
addListener(event: "error", listener: (err: Error) => void): this;
addListener(event: "pipe" | "unpipe", listener: (src: Readable) => void): this;
addListener(event: string, listener: (...args: any[]) => void): this;
prependListener(event: "drain" | "finish" | "close", listener: () => void): this;
prependListener(event: "error", listener: (err: Error) => void): this;
prependListener(event: "pipe" | "unpipe", listener: (src: Readable) => void): this;
prependListener(event: string, listener: (...args: any[]) => void): this;
prependOnceListener(event: "drain" | "finish" | "close", listener: () => void): this;
prependOnceListener(event: "error", listener: (err: Error) => void): this;
prependOnceListener(event: "pipe" | "unpipe", listener: (src: Readable) => void): this;
prependOnceListener(event: string, listener: (...args: any[]) => void): this;
off(event: "drain" | "finish" | "close", listener: () => void): this;
off(event: "error", listener: (err: Error) => void): this;
off(event: "pipe" | "unpipe", listener: (src: Readable) => void): this;
off(event: string, listener: (...args: any[]) => void): this;
removeListener(event: "drain" | "finish" | "close", listener: () => void): this;
removeListener(event: "error", listener: (err: Error) => void): this;
removeListener(event: "pipe" | "unpipe", listener: (src: Readable) => void): this;
removeListener(event: string, listener: (...args: any[]) => void): this;
}
/* Duplex is Readable + Writable in one object. TS single inheritance
* mirrors @types/node: extends Readable, the writable half declared
* again. The lowering treats both halves as first-class. */
class Duplex extends Readable {
/* Duplex.from: an async generator function, an iterable, a string or
* a stream (duplexify.js). */
static from(body: unknown): Duplex;
constructor(opts: ObjectDuplexOptions);
constructor(opts: StringDuplexOptions);
constructor(opts?: DuplexOptions);
/* The underscore-method surface (see Readable). */
_write(chunk: any, encoding: string, callback: (error?: Error | null) => void): void;
_writev(
chunks: Array<{ chunk: any; encoding: string }>,
callback: (error?: Error | null) => void,
): void;
_final(callback: (error?: Error | null) => void): void;
write(chunk: Buffer | string, callback?: (error?: Error | null) => void): boolean;
write(
chunk: Buffer | string,
encoding: string,
callback?: (error?: Error | null) => void,
): boolean;
/* An object-mode stream's values (the encoding argument reaches
* _write as passed). */
write(chunk: any, callback?: (error?: Error | null) => void): boolean;
write(
chunk: any,
encoding: ObjectModeEncoding,
callback?: (error?: Error | null) => void,
): boolean;
end(callback?: () => void): this;
end(chunk: Buffer | string, callback?: () => void): this;
end(chunk: Buffer | string, encoding: string, callback?: () => void): this;
end(chunk: any, callback?: () => void): this;
end(chunk: any, encoding: ObjectModeEncoding, callback?: () => void): this;
cork(): void;
uncork(): void;
setDefaultEncoding(encoding: string): this;
readonly writable: boolean;
readonly writableEnded: boolean;
readonly writableFinished: boolean;
readonly writableLength: number;
readonly writableHighWaterMark: number;
readonly writableObjectMode: boolean;
readonly writableNeedDrain: boolean;
readonly writableCorked: number;
readonly allowHalfOpen: boolean;
on(event: "data", listener: (chunk: any) => void): this;
on(
event: "end" | "close" | "readable" | "pause" | "resume" | "drain" | "finish",
listener: () => void,
): this;
on(event: "error", listener: (err: Error) => void): this;
on(event: "pipe" | "unpipe", listener: (src: Readable) => void): this;
on(event: string, listener: (...args: any[]) => void): this;
once(event: "data", listener: (chunk: any) => void): this;
once(
event: "end" | "close" | "readable" | "pause" | "resume" | "drain" | "finish",
listener: () => void,
): this;
once(event: "error", listener: (err: Error) => void): this;
once(event: "pipe" | "unpipe", listener: (src: Readable) => void): this;
once(event: string, listener: (...args: any[]) => void): this;
addListener(event: "data", listener: (chunk: any) => void): this;
addListener(
event: "end" | "close" | "readable" | "pause" | "resume" | "drain" | "finish",
listener: () => void,
): this;
addListener(event: "error", listener: (err: Error) => void): this;
addListener(event: "pipe" | "unpipe", listener: (src: Readable) => void): this;
addListener(event: string, listener: (...args: any[]) => void): this;
prependListener(event: "data", listener: (chunk: any) => void): this;
prependListener(
event: "end" | "close" | "readable" | "pause" | "resume" | "drain" | "finish",
listener: () => void,
): this;
prependListener(event: "error", listener: (err: Error) => void): this;
prependListener(event: "pipe" | "unpipe", listener: (src: Readable) => void): this;
prependListener(event: string, listener: (...args: any[]) => void): this;
prependOnceListener(event: "data", listener: (chunk: any) => void): this;
prependOnceListener(
event: "end" | "close" | "readable" | "pause" | "resume" | "drain" | "finish",
listener: () => void,
): this;
prependOnceListener(event: "error", listener: (err: Error) => void): this;
prependOnceListener(event: "pipe" | "unpipe", listener: (src: Readable) => void): this;
prependOnceListener(event: string, listener: (...args: any[]) => void): this;
off(event: "data", listener: (chunk: any) => void): this;
off(
event: "end" | "close" | "readable" | "pause" | "resume" | "drain" | "finish",
listener: () => void,
): this;
off(event: "error", listener: (err: Error) => void): this;
off(event: "pipe" | "unpipe", listener: (src: Readable) => void): this;
off(event: string, listener: (...args: any[]) => void): this;
removeListener(event: "data", listener: (chunk: any) => void): this;
removeListener(
event: "end" | "close" | "readable" | "pause" | "resume" | "drain" | "finish",
listener: () => void,
): this;
removeListener(event: "error", listener: (err: Error) => void): this;
removeListener(event: "pipe" | "unpipe", listener: (src: Readable) => void): this;
removeListener(event: string, listener: (...args: any[]) => void): this;
}
class Transform extends Duplex {
constructor(opts: ObjectTransformOptions);
constructor(opts: StringTransformOptions);
constructor(opts?: TransformOptions);
/* The underscore-method surface (see Readable). */
_transform(
chunk: any,
encoding: string,
callback: (error?: Error | null, data?: any) => void,
): void;
_flush(callback: (error?: Error | null, data?: any) => void): void;
}
class PassThrough extends Transform {
constructor(opts: ObjectTransformOptions);
constructor(opts: StringTransformOptions);
constructor(opts?: TransformOptions);
}
/* addAbortSignal(signal, stream): the stream, destroyed with an
* AbortError (cause: signal.reason) when the signal aborts. */
function addAbortSignal<T extends Readable | Writable>(signal: AbortSignal, stream: T): T;
/* pipeline(...stages, callback): the last stream, or the object-mode
* PassThrough that a function destination feeds. */
type PipelineResult<T extends unknown[]> = T extends [
...unknown[],
infer D,
(...args: any[]) => unknown,
]
? D extends (...args: any[]) => unknown
? PassThrough
: D
: unknown;
function pipeline<T extends unknown[]>(...streams: T): PipelineResult<T>;
/* util.promisify's pipeline overload matches only pipeline itself: a
* generic signature alone admits every function. */
namespace pipeline {
const __promisify__: typeof promises.pipeline;
}
/* A compose stage: a stream, an async generator function over the
* previous stage, or (first) an iterable, async iterable or web
* ReadableStream. */
type ComposeStage =
| Readable
| Writable
| Iterable<unknown>
| AsyncIterable<unknown>
| globalThis.ReadableStream
| ((source: any, options?: { signal: AbortSignal }) => unknown);
/* compose(...streams): a Duplex that writes to the first stage and
* reads from the last, with the stages running as a pipeline. */
function compose(...streams: ComposeStage[]): Duplex;
/* finished's options: readable/writable choose which ends count,
* error: false ignores 'error' events, signal aborts the wait. */
interface FinishedOptions {
readable?: boolean;
writable?: boolean;
error?: boolean;
signal?: AbortSignal;
}
function finished(stream: unknown, callback: (err?: Error | null) => void): () => void;
function finished(
stream: unknown,
options: FinishedOptions | null,
callback: (err?: Error | null) => void,
): () => void;
/* Folds to the TARGET platform's default at compile time (win32 16384,
* else 65536 — Node's own state.js split); literal argument only. */
function getDefaultHighWaterMark(objectMode: boolean): number;
/* The promise pipeline's result: a last stage that is an async
* function resolves the promise with its value (before the options
* argument, when one follows). */
type PipelinePromise<T extends unknown[]> = T extends [
...unknown[],
(...args: any[]) => Promise<infer P>,
]
? Promise<P>
: T extends [
...unknown[],
(...args: any[]) => Promise<infer P>,
{ signal?: AbortSignal; end?: boolean },
]
? Promise<P>
: Promise<void>;
namespace promises {
function pipeline<T extends unknown[]>(...streams: T): PipelinePromise<T>;
function finished(
stream: unknown,
options?: FinishedOptions & { cleanup?: boolean },
): Promise<void>;
}
class Stream extends EventEmitter {}
export {
Readable,
Writable,
Duplex,
Transform,
PassThrough,
Stream,
addAbortSignal,
pipeline,
compose,
finished,
getDefaultHighWaterMark,
promises,
};
export type {
ReadableOptions,
WritableOptions,
DuplexOptions,
TransformOptions,
FinishedOptions,
PipelinePromise,
};
}
declare module "node:stream" {
import stream = require("stream");
export = stream;
}
@@ -0,0 +1,20 @@
/* node:string_decoder — StringDecoder over Node's whole encoding set:
* write() decodes complete sequences and buffers a trailing partial one
* across chunk boundaries (utf8's truncated sequence, base64's mod-3
* remainder, utf16le's odd byte / trailing lead surrogate; latin1, ascii
* and hex are stateless); end() flushes the buffered partial. `encoding`
* answers the normalized name. Node's exact algorithms (SEMANTICS.md);
* the encoding must be a literal, and end(buffer) fences — write the
* buffer, then end(). */
declare module "string_decoder" {
export class StringDecoder {
constructor(encoding?: BufferEncoding);
readonly encoding: string;
write(buffer: Uint8Array): string;
end(buffer?: Uint8Array): string;
}
}
declare module "node:string_decoder" {
export * from "string_decoder";
}
+236
View File
@@ -0,0 +1,236 @@
/// <reference path="./assert.d.ts" />
/* node:assert — the static assertion surface. The module object IS a
* callable function (`assert(x)` is `assert.ok(x)`), so the declaration
* is the callable-namespace `export =` shape and BOTH `import assert
* from "node:assert"` and `const assert = require("assert")` bind the
* callable; named imports take the members. Failures throw
* AssertionError (name "AssertionError", code "ERR_ASSERTION" — catch and
* read .name/.message/.code). strictEqual/deepStrictEqual compare with
* Object.is over scalars; deepStrictEqual compares composites
* structurally per their static types. The messages here are plain
* strings (Node accepts Errors — that form fences per site), and the
* shallow legacy pair (equal/notEqual) follows Node's == coercions for
* primitive and primitive-union operands, including Node v24's NaN
* exception. deepEqual/notDeepEqual remain declared-but-fenced;
* assert/strict's loose names alias the strict forms and lower. */
/* node:test — the in-process test runner (prefix-only in Node too:
* require("test") is MODULE_NOT_FOUND). test/it register tests (sync and
* async bodies, skip/todo/only options and method twins), describe/suite
* group them (bodies run at registration, Node's collection phase),
* before/after/beforeEach/afterEach hook the enclosing suite, and the
* TestContext argument carries subtests (t.test), t.skip/t.todo,
* t.diagnostic, and t.assert. mock/run/snapshot are DECLARED so real
* suites typecheck but fence at their use sites (no lowering). */
declare module "node:test" {
type TestFn = (t: TestContext) => void | Promise<void>;
type SuiteFn = (s: SuiteContext) => void;
type HookFn = (c: TestContext | SuiteContext) => void | Promise<void>;
/* The describe/suite callback's context, and the before/after hook
* context of a suite. */
interface SuiteContext {
readonly name: string;
readonly fullName: string;
readonly filePath: string | undefined;
readonly attempt: number;
readonly passed: boolean;
diagnostic(message: string): void;
}
interface TestOptions {
skip?: boolean | string;
todo?: boolean | string;
only?: boolean;
concurrency?: number | boolean;
timeout?: number;
plan?: number;
}
interface TestContext {
readonly name: string;
readonly fullName: string;
readonly filePath: string | undefined;
readonly attempt: number;
readonly passed: boolean;
readonly workerId: number | undefined;
readonly tags: readonly string[];
readonly assert: TestContextAssert;
readonly mock: MockTracker;
test(name?: string, fn?: TestFn): Promise<void>;
test(name?: string, options?: TestOptions, fn?: TestFn): Promise<void>;
test(options?: TestOptions, fn?: TestFn): Promise<void>;
test(fn?: TestFn): Promise<void>;
skip(message?: string): void;
todo(message?: string): void;
diagnostic(message: string): void;
plan(count: number): void;
after(fn: HookFn): void;
before(fn: HookFn): void;
beforeEach(fn: HookFn): void;
afterEach(fn: HookFn): void;
}
interface AssertSnapshotOptions {
readonly serializers?: ReadonlyArray<(value: any) => any> | undefined;
}
interface TestContextAssert {
snapshot(value: any, options?: AssertSnapshotOptions): void;
fileSnapshot(value: any, path: string, options?: AssertSnapshotOptions): void;
ok(value: unknown, message?: string): void;
strictEqual(actual: unknown, expected: unknown, message?: string): void;
notStrictEqual(actual: unknown, expected: unknown, message?: string): void;
deepStrictEqual(actual: unknown, expected: unknown, message?: string): void;
notDeepStrictEqual(actual: unknown, expected: unknown, message?: string): void;
equal(actual: unknown, expected: unknown, message?: string): void;
notEqual(actual: unknown, expected: unknown, message?: string): void;
deepEqual(actual: unknown, expected: unknown, message?: string): void;
notDeepEqual(actual: unknown, expected: unknown, message?: string): void;
fail(message?: string): never;
throws(fn: () => unknown, expected?: unknown, message?: string): void;
match(value: string, regExp: RegExp, message?: string): void;
doesNotMatch(value: string, regExp: RegExp, message?: string): void;
}
/* The name, options and function are each optional, as in Node: a test
* without a name takes the function's name, or "<anonymous>". */
function test(name?: string, fn?: TestFn): Promise<void>;
function test(name?: string, options?: TestOptions, fn?: TestFn): Promise<void>;
function test(options?: TestOptions, fn?: TestFn): Promise<void>;
function test(fn?: TestFn): Promise<void>;
namespace test {
function skip(name?: string, fn?: TestFn): Promise<void>;
function skip(name?: string, options?: TestOptions, fn?: TestFn): Promise<void>;
function skip(options?: TestOptions, fn?: TestFn): Promise<void>;
function skip(fn?: TestFn): Promise<void>;
function todo(name?: string, fn?: TestFn): Promise<void>;
function todo(name?: string, options?: TestOptions, fn?: TestFn): Promise<void>;
function todo(options?: TestOptions, fn?: TestFn): Promise<void>;
function todo(fn?: TestFn): Promise<void>;
function only(name?: string, fn?: TestFn): Promise<void>;
function only(name?: string, options?: TestOptions, fn?: TestFn): Promise<void>;
function only(options?: TestOptions, fn?: TestFn): Promise<void>;
function only(fn?: TestFn): Promise<void>;
}
const it: typeof test;
function describe(name?: string, fn?: SuiteFn): void;
function describe(name?: string, options?: TestOptions, fn?: SuiteFn): void;
function describe(options?: TestOptions, fn?: SuiteFn): void;
function describe(fn?: SuiteFn): void;
namespace describe {
function skip(name?: string, fn?: SuiteFn): void;
function skip(name?: string, options?: TestOptions, fn?: SuiteFn): void;
function skip(options?: TestOptions, fn?: SuiteFn): void;
function skip(fn?: SuiteFn): void;
function todo(name?: string, fn?: SuiteFn): void;
function todo(name?: string, options?: TestOptions, fn?: SuiteFn): void;
function todo(options?: TestOptions, fn?: SuiteFn): void;
function todo(fn?: SuiteFn): void;
function only(name?: string, fn?: SuiteFn): void;
function only(name?: string, options?: TestOptions, fn?: SuiteFn): void;
function only(options?: TestOptions, fn?: SuiteFn): void;
function only(fn?: SuiteFn): void;
}
const suite: typeof describe;
function before(fn: HookFn): void;
function after(fn: HookFn): void;
function beforeEach(fn: HookFn): void;
function afterEach(fn: HookFn): void;
/* The mock function surface: mock.fn, mock.method and their t.mock
* twins, the tracker's restoreAll and reset, and the calls log and the
* implementation controls on fn.mock. */
interface MockFunctionCall<
F extends Function,
ReturnType = F extends (...args: any) => infer T ? T : unknown,
Args = F extends (...args: infer Y) => any ? Y : unknown[],
> {
arguments: Args;
error: unknown | undefined;
result: ReturnType | undefined;
this: unknown;
}
interface MockFunctionContext<F extends Function> {
readonly calls: MockFunctionCall<F>[];
callCount(): number;
mockImplementation(implementation: F): void;
mockImplementationOnce(implementation: F, onCall?: number): void;
resetCalls(): void;
restore(): void;
}
type Mock<F extends Function> = F & { mock: MockFunctionContext<F> };
interface MockFunctionOptions {
times?: number;
}
type FunctionPropertyNames<T> = {
[K in keyof T]: T[K] extends Function ? K : never;
}[keyof T];
interface MockTracker {
fn<F extends Function = (...args: any[]) => undefined>(original?: F): Mock<F>;
fn<F extends Function = (...args: any[]) => undefined, Implementation extends Function = F>(
original?: F,
implementation?: Implementation,
): Mock<F | Implementation>;
method<MockedObject extends object, MethodName extends FunctionPropertyNames<MockedObject>>(
object: MockedObject,
methodName: MethodName,
options?: MockFunctionOptions,
): MockedObject[MethodName] extends Function ? Mock<MockedObject[MethodName]> : never;
method<
MockedObject extends object,
MethodName extends FunctionPropertyNames<MockedObject>,
Implementation extends Function,
>(
object: MockedObject,
methodName: MethodName,
implementation: Implementation,
options?: MockFunctionOptions,
): MockedObject[MethodName] extends Function
? Mock<MockedObject[MethodName] | Implementation>
: never;
reset(): void;
restoreAll(): void;
readonly timers: MockTimers;
}
interface MockTimersOptions {
apis: ReadonlyArray<"setInterval" | "setTimeout" | "setImmediate" | "Date">;
now?: number | Date | undefined;
}
interface MockTimers {
enable(options?: MockTimersOptions): void;
setTime(milliseconds?: number): void;
reset(): void;
tick(milliseconds?: number): void;
runAll(): void;
[Symbol.dispose](): void;
}
const mock: MockTracker;
/* The context of the test, suite or hook running in the current async
* context; undefined outside one. */
function getTestContext(): TestContext | SuiteContext | undefined;
/* Declared for typechecking only — every use fences (no lowering).
* `assert` is the module-level custom-assertion registry (v24's
* `const { assert } = require('node:test')`), not TestContext.assert. */
const assert: { register(name: string, fn: (...args: unknown[]) => unknown): void };
function run(options?: unknown): unknown;
function snapshot(...args: unknown[]): unknown;
export {
test,
it,
describe,
suite,
before,
after,
beforeEach,
afterEach,
mock,
assert,
getTestContext,
run,
snapshot,
TestContext,
SuiteContext,
TestOptions,
Mock,
MockFunctionContext,
MockFunctionCall,
MockTracker,
MockTimers,
MockTimersOptions,
};
export default test;
}
@@ -0,0 +1,31 @@
/* node:timers/promises — the promisified pair the Node test harness leans
* on (`await setTimeout(ms)` as a sleep). setTimeout and setImmediate
* settle with their resolve value (undefined without one) from the shared
* timer heap and the immediate queue; setInterval(delay, value) lowers to a
* typed async iterator. The options object takes `ref`; AbortSignal
* options remain declared surface that fences per site. */
declare module "node:timers/promises" {
export interface TimerOptions {
ref?: boolean;
signal?: AbortSignal;
}
export function setTimeout<T = void>(
delay?: number,
value?: T,
options?: TimerOptions,
): Promise<T>;
export function setImmediate<T = void>(value?: T, options?: TimerOptions): Promise<T>;
export function setInterval<T = unknown>(
delay?: number,
value?: T,
options?: unknown,
): AsyncGenerator<T, void, undefined>;
export const scheduler: {
wait(delay?: number, options?: TimerOptions): Promise<void>;
yield(): Promise<void>;
};
}
declare module "timers/promises" {
export * from "node:timers/promises";
}
+28
View File
@@ -0,0 +1,28 @@
/* node:timers — the module twins of the timer globals (Node's timers
* module re-exports them: require('timers').setTimeout IS setTimeout).
* Named imports, destructured requires, and namespace calls all lower to
* the same libCalls as the globals; the surface beyond these six
* (enroll/unenroll, promises) fences per member. */
declare module "node:timers" {
export function setTimeout(callback: () => void, ms?: number): Timeout;
export function setTimeout(
callback: (...args: never[]) => void,
ms?: number,
...args: unknown[]
): Timeout;
export function clearTimeout(handle?: Timeout | number | null | undefined): void;
export function setInterval(callback: () => void, ms?: number): Timeout;
export function setInterval(
callback: (...args: never[]) => void,
ms?: number,
...args: unknown[]
): Timeout;
export function clearInterval(handle?: Timeout | number | null | undefined): void;
export function setImmediate(callback: () => void): Immediate;
export function setImmediate(callback: (...args: never[]) => void, ...args: unknown[]): Immediate;
export function clearImmediate(handle?: Immediate | null | undefined): void;
}
declare module "timers" {
export * from "node:timers";
}
+169
View File
@@ -0,0 +1,169 @@
/// <reference path="./crypto.d.ts" />
/// <reference path="./net.d.ts" />
/* node:tls — the TLS server slice (scr_tls.c over scr_net.c with the
* vendored mbedTLS; linked only into using binaries).
* createServer({ cert, key }, handler) returns a net.Server; the handler
* is Node's 'secureConnection' — it fires post-handshake with a socket
* that behaves exactly like a net socket (write/end/destroy/pipe and the
* data/end/close/error events are the same lowered surface). cert/key
* are PEM strings or Buffers. Handshake failures tear the socket down
* without emitting an event, Node's default for 'tlsClientError' with no listener. */
declare module "tls" {
import { Server as NetServer, Socket } from "net";
/** tls.Server: a net server whose connections handshake first. */
export interface Server extends NetServer {
/* serve `context` to clients whose SNI name matches `hostname` ('*'
* matches within one label); the newest match wins */
addContext(hostname: string, context: TlsOptions | SecureContext): void;
/* new connections serve a context made from `options` */
setSecureContext(options: TlsOptions): void;
}
export interface TlsOptions {
/* Optional in the DECLARATION only (real Node programs build these
* literals with every TLS knob): the lowering still requires both —
* a literal without them fences at the call site. */
cert?: string | Uint8Array;
key?: string | Uint8Array;
/* The options-record stance (see http.RequestOptions): every key
* typechecks; the option walk fences documented-but-unlowered keys
* by name and drops undocumented ones like Node. */
[option: string]: unknown;
}
export function createServer(
options: TlsOptions,
secureConnectionListener?: (socket: TLSSocket) => void,
): Server;
/** TLSSocket — post-handshake it IS a net socket to every lowered
* member (the mapping collapses it onto the socket kind; the
* secureConnect/session events ride the shared socket declarations).
* The TLS extras: authorized/authorizationError answer Node's verify
* split — authorizationError is the verify-failure CODE STRING
* (DEPTH_ZERO_SELF_SIGNED_CERT, ...), or null. */
export interface TLSSocket extends Socket {
readonly encrypted: true;
authorized: boolean;
authorizationError: string | null;
/* null before the handshake, then the negotiated protocol or false */
readonly alpnProtocol: string | false | null;
/* "TLSv1.2" or "TLSv1.3"; null before the handshake and after close */
getProtocol(): string | null;
/* OpenSSL's suite name, the IANA name and the suite's version */
getCipher(): CipherNameAndProtocol;
/* whether the handshake resumed a cached session */
isSessionReused(): boolean;
/* the peer's certificate: {} before the handshake or without one,
* null once the socket is destroyed; detailed adds issuerCertificate */
getPeerCertificate(detailed: true): DetailedPeerCertificate;
getPeerCertificate(detailed?: false): PeerCertificate;
getPeerCertificate(detailed?: boolean): PeerCertificate | DetailedPeerCertificate;
/* the certificate this socket serves, the same way */
getCertificate(): PeerCertificate | object | null;
/* a new X509Certificate on each call, or undefined without one */
getPeerX509Certificate(): import("crypto").X509Certificate | undefined;
getX509Certificate(): import("crypto").X509Certificate | undefined;
}
/* A certificate as getPeerCertificate() and X509Certificate's
* toLegacyObject() answer it: subject and issuer map attribute short
* names to values, infoAccess its "<method> - <type>" keys to values. */
export interface PeerCertificate {
subject: { [attribute: string]: string | string[] };
issuer: { [attribute: string]: string | string[] };
subjectaltname?: string;
infoAccess?: { [key: string]: string[] };
ca: boolean;
modulus?: string;
exponent?: string;
pubkey?: Buffer;
bits?: number;
valid_from: string;
valid_to: string;
fingerprint: string;
fingerprint256: string;
fingerprint512: string;
ext_key_usage?: string[];
serialNumber: string;
raw: Buffer;
asn1Curve?: string;
nistCurve?: string;
}
/* getPeerCertificate(true): each certificate links to its issuer; a
* self-issued root is its own issuerCertificate */
export interface DetailedPeerCertificate extends PeerCertificate {
issuerCertificate: DetailedPeerCertificate;
}
/** new tls.TLSSocket(socket, options): the server-mode wrap of an open
* socket (a server connection or an http CONNECT socket). The options
* record reads at runtime; isServer, cert, key, secureContext,
* ALPNProtocols, minVersion and maxVersion are implemented. */
export interface TLSSocketOptions {
isServer?: boolean;
cert?: string | Uint8Array;
key?: string | Uint8Array;
secureContext?: SecureContext;
ALPNProtocols?: string[] | Uint8Array;
minVersion?: string;
maxVersion?: string;
[option: string]: unknown;
}
export const TLSSocket: new (socket: Socket, options: TLSSocketOptions) => TLSSocket;
export interface CipherNameAndProtocol {
name: string;
standardName: string;
version: string;
}
export const DEFAULT_MIN_VERSION: string;
export const DEFAULT_MAX_VERSION: string;
/** tls.connect options — the RUNTIME options record (members read at
* runtime; port/host/socket/rejectUnauthorized/ca/servername are the
* honestly-implemented set, every other documented member throws the
* runtime fence — the options-record stance's divergence-66 shape). */
export interface ConnectionOptions {
port?: number;
host?: string;
/* an open socket to run the client handshake over */
socket?: Socket;
rejectUnauthorized?: boolean;
ca?: string | Uint8Array | ReadonlyArray<string | Uint8Array>;
servername?: string;
[option: string]: unknown;
}
export function connect(
options: ConnectionOptions,
secureConnectListener?: () => void,
): TLSSocket;
export function connect(
port: number,
options?: ConnectionOptions,
secureConnectListener?: () => void,
): TLSSocket;
export function connect(
port: number,
host?: string,
options?: ConnectionOptions,
secureConnectListener?: () => void,
): TLSSocket;
/** An opaque parsed cert/key pair — what an SNI callback answers with
* (the value is a runtime handle; the members are runtime-internal). */
export interface SecureContext {}
/* @types/node's name for createSecureContext's options */
export type SecureContextOptions = TlsOptions;
export function createSecureContext(options: TlsOptions): SecureContext;
/** The CA-store introspection surface (scr_tls_ca.c): per-type cached
* PEM string arrays. The host bundle stands in for Node's compiled-in
* Mozilla roots ('bundled', and rootCertificates) AND the platform
* store ('system') — the Windows certificate stores or POSIX bundle probe;
* 'extra' reads NODE_EXTRA_CA_CERTS. An unknown type string throws
* Node's ERR_INVALID_ARG_VALUE TypeError. */
export function getCACertificates(type?: string): string[];
export const rootCertificates: readonly string[];
/** Replaces the 'default' set (deduped) and the trust anchors the TLS
* client verifies against; a certificate-free non-empty array throws
* Node's ERR_CRYPTO_OPERATION_FAILED Error. */
export function setDefaultCACertificates(certs: readonly string[]): void;
}
declare module "node:tls" {
export * from "tls";
}
+95
View File
@@ -0,0 +1,95 @@
/// <reference path="./querystring.d.ts" />
/* node:url — the file-URL bridge pair. fileURLToPath accepts a URL value
* or a URL string and throws Node's TypeErrors (non-file scheme, encoded
* slashes, non-empty host); pathToFileURL resolves the path and percent-
* encodes it into a file: URL. The legacy API: parse() answers a Url
* object, format() serializes a Url-shaped object, a string, or a URL,
* resolve() resolves one URL string against another, the domain pair
* converts a hostname to and from its Punycode form, and urlToHttpOptions
* builds http.request options from a URL. */
declare module "url" {
export type URL = globalThis.URL;
export type URLSearchParams = globalThis.URLSearchParams;
export const URL: typeof globalThis.URL;
export const URLSearchParams: typeof globalThis.URLSearchParams;
export function fileURLToPath(url: string | URL, options?: { windows?: boolean }): string;
export function fileURLToPathBuffer(url: string | URL, options?: { windows?: boolean }): Buffer;
export function pathToFileURL(path: string, options?: { windows?: boolean }): URL;
export interface UrlObject {
auth?: string | null | undefined;
hash?: string | null | undefined;
host?: string | null | undefined;
hostname?: string | null | undefined;
href?: string | null | undefined;
pathname?: string | null | undefined;
protocol?: string | null | undefined;
search?: string | null | undefined;
slashes?: boolean | null | undefined;
port?: string | number | null | undefined;
query?: string | null | import("querystring").ParsedUrlQueryInput | undefined;
}
export interface Url {
auth: string | null;
hash: string | null;
host: string | null;
hostname: string | null;
href: string;
path: string | null;
pathname: string | null;
protocol: string | null;
search: string | null;
slashes: boolean | null;
port: string | null;
query: string | null | import("querystring").ParsedUrlQuery;
}
export interface UrlWithParsedQuery extends Url {
query: import("querystring").ParsedUrlQuery;
}
export interface UrlWithStringQuery extends Url {
query: string | null;
}
export interface URLFormatOptions {
auth?: boolean | undefined;
fragment?: boolean | undefined;
search?: boolean | undefined;
unicode?: boolean | undefined;
}
export function parse(
urlString: string,
parseQueryString?: false,
slashesDenoteHost?: boolean,
): UrlWithStringQuery;
export function parse(
urlString: string,
parseQueryString: true,
slashesDenoteHost?: boolean,
): UrlWithParsedQuery;
export function parse(
urlString: string,
parseQueryString: boolean,
slashesDenoteHost?: boolean,
): Url;
export function format(urlObject: URL, options?: URLFormatOptions): string;
export function format(urlObject: UrlObject | string): string;
export function resolve(from: string, to: string): string;
export function domainToASCII(domain: string): string;
export function domainToUnicode(domain: string): string;
/** urlToHttpOptions's null-prototype request options. */
export interface UrlHttpOptions {
protocol: string;
hostname: string;
hash: string;
search: string;
pathname: string;
path: string;
href: string;
port?: number;
auth?: string;
}
export function urlToHttpOptions(url: URL): UrlHttpOptions;
}
declare module "node:url" {
export * from "url";
}
+50
View File
@@ -0,0 +1,50 @@
/* node:util/types — native runtime brands. Other forms retain their
* per-member lowering boundaries. */
declare module "util/types" {
export function isAnyArrayBuffer(value: unknown): boolean;
export function isArrayBuffer(value: unknown): value is ArrayBuffer;
export function isArrayBufferView(value: unknown): value is ArrayBufferView;
export function isDataView(value: unknown): value is DataView;
export function isTypedArray(value: unknown): boolean;
export function isUint8Array(value: unknown): value is Uint8Array;
export function isUint8ClampedArray(value: unknown): value is Uint8ClampedArray;
export function isUint16Array(value: unknown): value is Uint16Array;
export function isUint32Array(value: unknown): value is Uint32Array;
export function isInt8Array(value: unknown): value is Int8Array;
export function isInt16Array(value: unknown): value is Int16Array;
export function isInt32Array(value: unknown): value is Int32Array;
export function isFloat16Array(value: unknown): value is Float16Array;
export function isFloat32Array(value: unknown): value is Float32Array;
export function isFloat64Array(value: unknown): value is Float64Array;
export function isMap(value: unknown): value is Map<unknown, unknown>;
export function isSet(value: unknown): value is Set<unknown>;
export function isWeakMap(value: unknown): value is WeakMap<object, unknown>;
export function isWeakSet(value: unknown): value is WeakSet<object>;
export function isRegExp(value: unknown): value is RegExp;
export function isNativeError(value: unknown): value is Error;
export function isPromise(value: unknown): value is Promise<unknown>;
export function isProxy(value: unknown): boolean;
export function isAsyncFunction(value: unknown): boolean;
export function isGeneratorFunction(value: unknown): boolean;
export function isGeneratorObject(value: unknown): boolean;
export function isModuleNamespaceObject(value: unknown): boolean;
export function isDate(value: unknown): value is Date;
export function isSharedArrayBuffer(value: unknown): value is SharedArrayBuffer;
export function isMapIterator(value: unknown): boolean;
export function isSetIterator(value: unknown): boolean;
export function isKeyObject(value: unknown): boolean;
export function isNumberObject(value: unknown): boolean;
export function isStringObject(value: unknown): boolean;
export function isBooleanObject(value: unknown): boolean;
export function isBigIntObject(value: unknown): boolean;
export function isSymbolObject(value: unknown): boolean;
export function isBoxedPrimitive(value: unknown): boolean;
export function isBigInt64Array(value: unknown): value is BigInt64Array;
export function isBigUint64Array(value: unknown): value is BigUint64Array;
export function isCryptoKey(value: unknown): boolean;
export function isExternal(value: unknown): boolean;
}
declare module "node:util/types" {
export * from "util/types";
}
+396
View File
@@ -0,0 +1,396 @@
/// <reference path="./child_process.d.ts" />
/// <reference path="./crypto.d.ts" />
/// <reference path="./dns.d.ts" />
/// <reference path="./fs.d.ts" />
/// <reference path="./stream.d.ts" />
/// <reference path="./stream-promises.d.ts" />
/// <reference path="./timers-promises.d.ts" />
/// <reference path="./util-types.d.ts" />
/// <reference path="./zlib.d.ts" />
/* node:util — compile-time projections for child_process.execFile,
* child_process.exec and fs.readFile. The first two retain Node's custom
* { stdout, stderr } result; the last resolves the callback's data
* argument. Other targets and bare
* promisify values fence per site. */
declare module "util" {
export * as types from "util/types";
export function getSystemErrorName(err: number): string;
export function getSystemErrorMessage(err: number): string;
export function getSystemErrorMap(): Map<number, [string, string]>;
export function stripVTControlCharacters(str: string): string;
export function toUSVString(string: string): string;
export function isDeepStrictEqual(val1: unknown, val2: unknown, skipPrototype?: boolean): boolean;
export function styleText(
format: string | readonly string[],
text: string,
options?: { validateStream?: boolean; stream?: unknown },
): string;
export function parseEnv(content: string): { [key: string]: string | undefined };
export function inherits(constructor: unknown, superConstructor: unknown): void;
export class MIMEType {
constructor(input: string | { toString: () => string });
static parse(input: string | { toString: () => string }): MIMEType | null;
type: string;
subtype: string;
readonly essence: string;
readonly params: MIMEParams;
toString(): string;
toJSON(): string;
}
export class MIMEParams {
delete(name: string): void;
entries(): IterableIterator<[name: string, value: string]>;
get(name: string): string | null;
has(name: string): boolean;
keys(): IterableIterator<string>;
set(name: string, value: string): void;
values(): IterableIterator<string>;
toString(): string;
toJSON(): string;
[Symbol.iterator](): IterableIterator<[name: string, value: string]>;
}
export type DiffEntry = [operation: -1 | 0 | 1, value: string];
export function diff(
actual: string | readonly string[],
expected: string | readonly string[],
): DiffEntry[];
export interface DebounceOptions {
leading?: boolean;
rejectOnCancel?: boolean;
signal?: AbortSignal;
}
/** Every call answers a promise of the result of the call it waits for. */
export interface DebouncedFunction<T extends (...args: any[]) => any> {
(...args: Parameters<T>): Promise<Awaited<ReturnType<T>>>;
cancel(reason?: unknown): void;
flush(): void;
ref(): this;
unref(): this;
readonly pending: Promise<Awaited<ReturnType<T>>> | null;
readonly pendingCount: number;
}
export function debounce<T extends (...args: any[]) => any>(
fn: T,
wait: number,
options?: DebounceOptions,
): DebouncedFunction<T>;
export interface ThrottleOptions {
concurrency?: number;
maxPending?: number;
overflow?: "queue" | "drop";
signal?: AbortSignal;
strict?: boolean;
}
export interface ThrottledFunction<T extends (...args: any[]) => any> {
(...args: Parameters<T>): Promise<Awaited<ReturnType<T>>>;
cancel(reason?: unknown): void;
hasImmediateCapacity(): boolean;
ref(): this;
unref(): this;
readonly pending: Promise<Awaited<ReturnType<T>>> | null;
readonly pendingCount: number;
readonly activeCount: number;
}
export function throttle<T extends (...args: any[]) => any>(
fn: T,
limit: number,
interval: number,
options?: ThrottleOptions,
): ThrottledFunction<T>;
/** Resolves with the abort event once `signal` aborts, or with undefined
* when it already has. */
export function aborted(signal: AbortSignal, resource: any): Promise<unknown>;
/** 128 plus the number of the signal, the exit code of a process it terminates. */
export function convertProcessSignalToExitCode(signalCode: string): number;
/** @deprecated Since v4.0.0 - Use `Array.isArray()` instead. */
export function isArray(object: unknown): object is unknown[];
/** @deprecated Since v6.0.0 - Use `Object.assign()` instead. Answers target itself. */
export function _extend<T extends object>(target: T, source: object | null | undefined): T;
export interface DeprecateOptions {
modifyPrototype?: boolean;
}
export function deprecate<T extends Function>(
fn: T,
msg: string,
code?: string,
options?: DeprecateOptions,
): T;
/* The callback receives null on success and the rejection reason
* otherwise (falsy reasons arrive as ERR_FALSY_VALUE_REJECTION). */
export function callbackify(
fn: () => Promise<void>,
): (callback: (err: NodeJS.ErrnoException | null) => void) => void;
export function callbackify<TResult>(
fn: () => Promise<TResult>,
): (callback: (err: NodeJS.ErrnoException | null, result: TResult) => void) => void;
export function callbackify<T1>(
fn: (arg1: T1) => Promise<void>,
): (arg1: T1, callback: (err: NodeJS.ErrnoException | null) => void) => void;
export function callbackify<T1, TResult>(
fn: (arg1: T1) => Promise<TResult>,
): (arg1: T1, callback: (err: NodeJS.ErrnoException | null, result: TResult) => void) => void;
export function callbackify<T1, T2>(
fn: (arg1: T1, arg2: T2) => Promise<void>,
): (arg1: T1, arg2: T2, callback: (err: NodeJS.ErrnoException | null) => void) => void;
export function callbackify<T1, T2, TResult>(
fn: (arg1: T1, arg2: T2) => Promise<TResult>,
): (
arg1: T1,
arg2: T2,
callback: (err: NodeJS.ErrnoException | null, result: TResult) => void,
) => void;
export function callbackify<T1, T2, T3>(
fn: (arg1: T1, arg2: T2, arg3: T3) => Promise<void>,
): (arg1: T1, arg2: T2, arg3: T3, callback: (err: NodeJS.ErrnoException | null) => void) => void;
export function callbackify<T1, T2, T3, TResult>(
fn: (arg1: T1, arg2: T2, arg3: T3) => Promise<TResult>,
): (
arg1: T1,
arg2: T2,
arg3: T3,
callback: (err: NodeJS.ErrnoException | null, result: TResult) => void,
) => void;
export type DebugLoggerFunction = (msg: string, ...param: unknown[]) => void;
export interface DebugLogger extends DebugLoggerFunction {
enabled: boolean;
}
export function debuglog(
section: string,
callback?: (fn: DebugLoggerFunction) => void,
): DebugLogger;
export { debuglog as debug };
export type TextEncoder = globalThis.TextEncoder;
export type TextDecoder = globalThis.TextDecoder;
export var TextEncoder: typeof globalThis.TextEncoder;
export var TextDecoder: typeof globalThis.TextDecoder;
/* An untyped function: an `any` value, or a function whose parameters
* are untyped (JavaScript's `(value, callback) => ...`), the only
* parameters that accept this object type. The promisified function is
* untyped too. It comes first because such functions match every other
* overload. */
export function promisify(fn: (...args: { readonly "scriptc:untyped": true }[]) => unknown): any;
/* stream.pipeline's overload comes next: its __promisify__ member keeps
* every other target out of it. */
export function promisify(
fn: typeof import("node:stream").pipeline,
): typeof import("node:stream/promises").pipeline;
export function promisify(
fn: typeof import("node:fs").readFile,
): (path: string, encoding: "utf8" | "utf-8") => Promise<string>;
export function promisify(
fn: (file: string, args?: string[] | null, options?: object) => unknown,
): (
file: string,
args?: string[],
options?: {
encoding?: "utf8" | "utf-8";
cwd?: string;
env?: { [k: string]: string | undefined };
timeout?: number;
maxBuffer?: number;
killSignal?: string;
windowsHide?: boolean;
},
) => Promise<{ stdout: string; stderr: string }>;
export function promisify(
fn: typeof globalThis.setTimeout,
): typeof import("node:timers/promises").setTimeout;
export function promisify(
fn: typeof globalThis.setImmediate,
): typeof import("node:timers/promises").setImmediate;
export function promisify(fn: typeof import("node:child_process").exec): (
command: string,
options?: {
encoding?: "utf8" | "utf-8";
cwd?: string;
env?: { [k: string]: string | undefined };
timeout?: number;
maxBuffer?: number;
killSignal?: string;
windowsHide?: boolean;
},
) => Promise<{ stdout: string; stderr: string }>;
/* The zlib callback one-shots (deflate, inflate, deflateRaw, inflateRaw,
* gzip, gunzip, unzip) share one type. */
export function promisify(
fn: typeof import("node:zlib").gzip,
): (data: string | Uint8Array, options?: import("node:zlib").ZlibOptions) => Promise<Buffer>;
export function promisify(
fn: typeof import("node:stream").finished,
): typeof import("node:stream/promises").finished;
/* The crypto callback functions: the promise settles with the callback's
* value. */
export function promisify(
fn: typeof import("node:crypto").randomBytes,
): (size: number) => Promise<Buffer>;
export function promisify(
fn: typeof import("node:crypto").pbkdf2,
): (
password: string | Uint8Array,
salt: string | Uint8Array,
iterations: number,
keylen: number,
digest: string,
) => Promise<Buffer>;
export function promisify(
fn: typeof import("node:crypto").scrypt,
): (
password: import("node:crypto").BinaryLike,
salt: import("node:crypto").BinaryLike,
keylen: number,
options?: import("node:crypto").ScryptOptions,
) => Promise<Buffer>;
export function promisify(
fn: typeof import("node:crypto").hkdf,
): (
digest: string,
ikm: import("node:crypto").BinaryLike | import("node:crypto").KeyObject,
salt: import("node:crypto").BinaryLike,
info: import("node:crypto").BinaryLike,
keylen: number,
) => Promise<ArrayBuffer>;
/* A callback-only function (`obj.close.bind(obj)`): before the targets
* whose leading parameters would otherwise capture it. Builtins that take
* arguments before their callback never match these. */
export function promisify<TResult>(
fn: (callback: (err: any, result: TResult) => void) => void,
): () => Promise<TResult>;
export function promisify(fn: (callback: (err?: any) => void) => void): () => Promise<void>;
export function promisify(
fn: typeof import("node:crypto").randomFill,
): <T extends ArrayBufferView | ArrayBufferLike>(
buffer: T,
offset?: number,
size?: number,
) => Promise<T>;
export function promisify(fn: typeof import("node:crypto").randomInt): {
(max: number): Promise<number>;
(min: number, max: number): Promise<number>;
};
export function promisify(
fn: typeof import("node:crypto").generateKey,
): (
type: "hmac" | "aes",
options: { length: number },
) => Promise<import("node:crypto").KeyObject>;
/* dns.lookup's promise settles with { address, family } (the address
* array with options.all), and lookupService's with { hostname, service }. */
export function promisify(fn: typeof import("node:dns").lookup): {
(
hostname: string,
options: import("node:dns").LookupAllOptions,
): Promise<import("node:dns").LookupAddress[]>;
(
hostname: string,
options?: number | import("node:dns").LookupOneOptions,
): Promise<import("node:dns").LookupAddress>;
};
export function promisify(
fn: typeof import("node:dns").lookupService,
): (address: string, port: number) => Promise<{ hostname: string; service: string }>;
/* Declared surface without full lowering (the type-level tolerance
* story: guarded/diagnostic-path code must TYPECHECK; a reached use
* without a lowering fences at its site). */
export interface InspectOptions {
showHidden?: boolean | undefined;
depth?: number | null | undefined;
colors?: boolean | undefined;
customInspect?: boolean | undefined;
showProxy?: boolean | undefined;
maxArrayLength?: number | null | undefined;
maxStringLength?: number | null | undefined;
breakLength?: number | undefined;
compact?: boolean | number | undefined;
sorted?: boolean | ((a: string, b: string) => number) | undefined;
getters?: "get" | "set" | boolean | undefined;
numericSeparator?: boolean | undefined;
}
export type Style =
| "special"
| "number"
| "bigint"
| "boolean"
| "undefined"
| "null"
| "string"
| "symbol"
| "date"
| "regexp"
| "module";
export interface InspectOptionsStylized extends InspectOptions {
stylize(text: string, styleType: Style): string;
}
export type CustomInspectFunction = (
depth: number,
options: InspectOptionsStylized,
inspect: typeof import("node:util").inspect,
) => any;
export function inspect(
object: unknown,
showHidden?: boolean,
depth?: number | null,
color?: boolean,
): string;
export function inspect(object: unknown, options?: InspectOptions): string;
export namespace inspect {
/* The key of an object's custom inspect function:
* Symbol.for('nodejs.util.inspect.custom'). */
const custom: unique symbol;
}
export function format(format?: unknown, ...args: unknown[]): string;
export function formatWithOptions(
inspectOptions: InspectOptions,
format?: unknown,
...args: unknown[]
): string;
export type ParseArgsOptionsType = "boolean" | "string";
export interface ParseArgsOptionDescriptor {
type: ParseArgsOptionsType;
multiple?: boolean;
short?: string;
default?: string | boolean | string[] | boolean[];
}
export interface ParseArgsOptionsConfig {
[longOption: string]: ParseArgsOptionDescriptor;
}
export interface ParseArgsConfig {
args?: readonly string[];
options?: ParseArgsOptionsConfig;
strict?: boolean;
allowPositionals?: boolean;
allowNegative?: boolean;
tokens?: boolean;
}
export type ParseArgsToken =
| {
kind: "option";
index: number;
name: string;
rawName: string;
value: string | undefined;
inlineValue: boolean | undefined;
}
| { kind: "positional"; index: number; value: string }
| { kind: "option-terminator"; index: number };
export interface ParseArgsResult {
values: { [longOption: string]: undefined | string | boolean | Array<string | boolean> };
positionals: string[];
tokens?: ParseArgsToken[];
}
export function parseArgs(config?: ParseArgsConfig): ParseArgsResult;
/* util.getCallSites (Node ≥22.9): the captured-frame slice harness
* code reads. No stack bookkeeping exists in a compiled binary — every
* reached call fences per site. */
export interface CallSite {
readonly functionName: string;
readonly scriptName: string;
readonly lineNumber: number;
readonly column: number;
}
export function getCallSites(frameCount?: number): CallSite[];
}
declare module "node:util" {
export * from "util";
}
+43
View File
@@ -0,0 +1,43 @@
/* node:v8 — importable for the packages that require it at top level.
* getHeapStatistics answers allocator-derived figures in Node's shape
* (approximate: the runtime heap's total and used bytes, the machine's
* memory as the limit; V8-only figures are 0). The other members observe
* V8 itself and fence at their use with the by-design reason; they are
* declared so programs that type-check under their own tsc reach that
* fence. */
declare module "v8" {
type DoesZapCodeSpaceFlag = 0 | 1;
export interface HeapInfo {
total_heap_size: number;
total_heap_size_executable: number;
total_physical_size: number;
total_available_size: number;
used_heap_size: number;
heap_size_limit: number;
malloced_memory: number;
peak_malloced_memory: number;
does_zap_garbage: DoesZapCodeSpaceFlag;
number_of_native_contexts: number;
number_of_detached_contexts: number;
total_global_handles_size: number;
used_global_handles_size: number;
external_memory: number;
}
export function getHeapStatistics(): HeapInfo;
export function cachedDataVersionTag(): number;
export function getHeapSpaceStatistics(): unknown[];
export function getHeapCodeStatistics(): unknown;
export function getHeapSnapshot(options?: unknown): unknown;
export function writeHeapSnapshot(fileName?: string, options?: unknown): string;
export function setFlagsFromString(flags: string): void;
export function setHeapSnapshotNearHeapLimit(limit: number): void;
export function serialize(value: unknown): Buffer;
export function deserialize(buffer: Uint8Array): unknown;
export function takeCoverage(): void;
export function stopCoverage(): void;
export function isStringOneByteRepresentation(content: string): boolean;
}
declare module "node:v8" {
export * from "v8";
}
@@ -0,0 +1,113 @@
/// <reference path="./stream.d.ts" />
/* node:worker_threads — native workers use statically compiled entry points.
* Lowering and runtime diagnostics define the supported options and methods. */
declare module "worker_threads" {
export const isMainThread: boolean;
export const isInternalThread: boolean;
export const threadId: number;
export interface ResourceLimits {
maxYoungGenerationSizeMb?: number;
maxOldGenerationSizeMb?: number;
codeRangeSizeMb?: number;
stackSizeMb?: number;
}
/* {} in the main thread */
export const resourceLimits: ResourceLimits;
/* the Worker's name option, trimmed ("WorkerThread" by default); "" in the main thread */
export const threadName: string | null;
export interface WorkerOptions {
workerData?: unknown;
transferList?: readonly unknown[];
argv?: readonly unknown[];
name?: string;
resourceLimits?: ResourceLimits;
stdout?: boolean;
stderr?: boolean;
stdin?: boolean;
[option: string]: unknown;
}
export class Worker {
constructor(filename: string | URL, options?: WorkerOptions);
readonly threadId: number;
readonly resourceLimits?: ResourceLimits;
readonly threadName: string | null;
/* with the stdout/stderr options: the worker's writes to those streams */
readonly stdout: import("stream").Readable;
readonly stderr: import("stream").Readable;
/* with the stdin option: what the worker reads from process.stdin */
readonly stdin: import("stream").Writable | null;
/* terminate(), resolving with undefined once the worker exited */
[Symbol.asyncDispose](): Promise<void>;
on(event: "online", listener: () => void): this;
on(event: "message", listener: (value: any) => void): this;
on(event: "error", listener: (error: any) => void): this;
on(event: "exit", listener: (code: number) => void): this;
on(event: string, listener: (...args: any[]) => void): this;
once(event: string, listener: (...args: any[]) => void): this;
off(event: string, listener: (...args: any[]) => void): this;
addListener(event: string, listener: (...args: any[]) => void): this;
removeListener(event: string, listener: (...args: any[]) => void): this;
prependListener(event: string, listener: (...args: any[]) => void): this;
prependOnceListener(event: string, listener: (...args: any[]) => void): this;
postMessage(value: unknown, transferList?: readonly unknown[]): void;
ref(): void;
unref(): void;
terminate(): Promise<number>;
removeAllListeners(event?: string): this;
listenerCount(event: string, listener?: (...args: any[]) => void): number;
}
export class MessagePort {
on(event: "message", listener: (value: any) => void): this;
on(event: "close", listener: () => void): this;
on(event: string, listener: (...args: any[]) => void): this;
once(event: string, listener: (...args: any[]) => void): this;
off(event: string, listener: (...args: any[]) => void): this;
addListener(event: string, listener: (...args: any[]) => void): this;
removeListener(event: string, listener: (...args: any[]) => void): this;
prependListener(event: string, listener: (...args: any[]) => void): this;
prependOnceListener(event: string, listener: (...args: any[]) => void): this;
postMessage(value: unknown, transferList?: readonly unknown[]): void;
start(): void;
close(callback?: () => void): void;
ref(): void;
unref(): void;
hasRef(): boolean;
removeAllListeners(event?: string): this;
listenerCount(event: string, listener?: (...args: any[]) => void): number;
}
/* Two entangled ports; a port moves to another thread through a
* postMessage or workerData transferList. */
export class MessageChannel {
readonly port1: MessagePort;
readonly port2: MessagePort;
}
export function receiveMessageOnPort(port: MessagePort): { message: any } | undefined;
/* This thread's environment data; each new Worker starts from a clone. */
export function setEnvironmentData(key: any, value?: any): void;
export function getEnvironmentData(key: any): any;
/* A marked object cannot be listed in a transfer list. */
export function markAsUntransferable(object: any): void;
/* structuredClone and postMessage throw DataCloneError for a marked
* object, error or class instance */
export function markAsUncloneable(object: any): void;
export function isMarkedAsUntransferable(object: any): boolean;
/* Delivers each message to every other open channel with the name, in
* any thread, as a 'message' MessageEvent. */
export class BroadcastChannel extends EventTarget {
constructor(name: string);
readonly name: string;
onmessage: ((event: MessageEvent) => void) | null;
onmessageerror: ((event: MessageEvent) => void) | null;
postMessage(message: unknown): void;
close(): void;
ref(): this;
unref(): this;
}
export const parentPort: MessagePort | null;
export const workerData: any;
}
declare module "node:worker_threads" {
export * from "worker_threads";
}
+422
View File
@@ -0,0 +1,422 @@
/// <reference path="./stream.d.ts" />
/* node:zlib — the one-shot zlib/raw/gzip codecs lower for string/Buffer
* inputs with Node's default options; libz links only into zlib-using
* binaries. Callback forms use the executable worker pool. Options stay
* declared so reached uses receive a named SC2020 refusal. The Deflate,
* Inflate, Gzip, Gunzip, DeflateRaw, InflateRaw, Unzip, BrotliCompress
* and BrotliDecompress streams are runtime Transform streams; Zstd remains
* outside the lowered surface. */
declare module "zlib" {
import { Transform } from "stream";
export interface ZlibOptions {
[option: string]: unknown;
}
export type ZlibCallback = (error: NodeJS.ErrnoException | null, result: Buffer) => void;
export function deflate(data: string | Uint8Array, callback: ZlibCallback): void;
export function deflate(
data: string | Uint8Array,
options: ZlibOptions,
callback: ZlibCallback,
): void;
export function deflateSync(data: string | Uint8Array, options?: ZlibOptions): Buffer;
export function inflate(data: string | Uint8Array, callback: ZlibCallback): void;
export function inflate(
data: string | Uint8Array,
options: ZlibOptions,
callback: ZlibCallback,
): void;
export function inflateSync(data: string | Uint8Array, options?: ZlibOptions): Buffer;
export function deflateRaw(data: string | Uint8Array, callback: ZlibCallback): void;
export function deflateRaw(
data: string | Uint8Array,
options: ZlibOptions,
callback: ZlibCallback,
): void;
export function deflateRawSync(data: string | Uint8Array, options?: ZlibOptions): Buffer;
export function inflateRaw(data: string | Uint8Array, callback: ZlibCallback): void;
export function inflateRaw(
data: string | Uint8Array,
options: ZlibOptions,
callback: ZlibCallback,
): void;
export function inflateRawSync(data: string | Uint8Array, options?: ZlibOptions): Buffer;
export function gzip(data: string | Uint8Array, callback: ZlibCallback): void;
export function gzip(
data: string | Uint8Array,
options: ZlibOptions,
callback: ZlibCallback,
): void;
export function gzipSync(data: string | Uint8Array, options?: ZlibOptions): Buffer;
export function gunzip(data: string | Uint8Array, callback: ZlibCallback): void;
export function gunzip(
data: string | Uint8Array,
options: ZlibOptions,
callback: ZlibCallback,
): void;
export function gunzipSync(data: string | Uint8Array, options?: ZlibOptions): Buffer;
export function unzip(data: string | Uint8Array, callback: ZlibCallback): void;
export function unzip(
data: string | Uint8Array,
options: ZlibOptions,
callback: ZlibCallback,
): void;
export function unzipSync(data: string | Uint8Array, options?: ZlibOptions): Buffer;
export function crc32(data: string | Uint8Array, value?: number): number;
/* Brotli over the vendored brotli 1.2.0 (scr_brotli.c): `params` keys
* are the numeric BROTLI_PARAM_* and BROTLI_DECODER_PARAM_* constants. */
export interface BrotliOptions {
[option: string]: unknown;
}
export function brotliCompress(data: string | Uint8Array, callback: ZlibCallback): void;
export function brotliCompress(
data: string | Uint8Array,
options: BrotliOptions,
callback: ZlibCallback,
): void;
export function brotliCompressSync(data: string | Uint8Array, options?: BrotliOptions): Buffer;
export function brotliDecompress(data: string | Uint8Array, callback: ZlibCallback): void;
export function brotliDecompress(
data: string | Uint8Array,
options: BrotliOptions,
callback: ZlibCallback,
): void;
export function brotliDecompressSync(data: string | Uint8Array, options?: BrotliOptions): Buffer;
/* The stream classes: runtime Transform streams over the zlib engine
* (scr_zlib_stream.c). createGzip(options) and new Gzip(options) build
* the same stream; options are validated at runtime with Node's rules.
* Brotli streams have no params(), as in Node. */
class ZlibBase extends Transform {
readonly bytesWritten: number;
flush(kind?: number, callback?: () => void): void;
flush(callback: () => void): void;
close(callback?: () => void): void;
reset(): void;
}
class Zlib extends ZlibBase {
params(level: number, strategy: number, callback?: () => void): void;
}
export class BrotliCompress extends ZlibBase {
constructor(options?: BrotliOptions);
}
export class BrotliDecompress extends ZlibBase {
constructor(options?: BrotliOptions);
}
export function createBrotliCompress(options?: BrotliOptions): BrotliCompress;
export function createBrotliDecompress(options?: BrotliOptions): BrotliDecompress;
export class Deflate extends Zlib {
constructor(options?: ZlibOptions);
}
export class Inflate extends Zlib {
constructor(options?: ZlibOptions);
}
export class Gzip extends Zlib {
constructor(options?: ZlibOptions);
}
export class Gunzip extends Zlib {
constructor(options?: ZlibOptions);
}
export class DeflateRaw extends Zlib {
constructor(options?: ZlibOptions);
}
export class InflateRaw extends Zlib {
constructor(options?: ZlibOptions);
}
export class Unzip extends Zlib {
constructor(options?: ZlibOptions);
}
export function createDeflate(options?: ZlibOptions): Deflate;
export function createInflate(options?: ZlibOptions): Inflate;
export function createGzip(options?: ZlibOptions): Gzip;
export function createGunzip(options?: ZlibOptions): Gunzip;
export function createDeflateRaw(options?: ZlibOptions): DeflateRaw;
export function createInflateRaw(options?: ZlibOptions): InflateRaw;
export function createUnzip(options?: ZlibOptions): Unzip;
/* Node v24 zlib.constants, literal-typed (the CryptoConstants stance):
* every access bakes as its literal; the object never materializes. The
* members outside BROTLI_* are also module properties (zlib.Z_SYNC_FLUSH). */
export interface ZlibConstants {
readonly Z_NO_FLUSH: 0;
readonly Z_PARTIAL_FLUSH: 1;
readonly Z_SYNC_FLUSH: 2;
readonly Z_FULL_FLUSH: 3;
readonly Z_FINISH: 4;
readonly Z_BLOCK: 5;
readonly Z_OK: 0;
readonly Z_STREAM_END: 1;
readonly Z_NEED_DICT: 2;
readonly Z_ERRNO: -1;
readonly Z_STREAM_ERROR: -2;
readonly Z_DATA_ERROR: -3;
readonly Z_MEM_ERROR: -4;
readonly Z_BUF_ERROR: -5;
readonly Z_VERSION_ERROR: -6;
readonly Z_NO_COMPRESSION: 0;
readonly Z_BEST_SPEED: 1;
readonly Z_BEST_COMPRESSION: 9;
readonly Z_DEFAULT_COMPRESSION: -1;
readonly Z_FILTERED: 1;
readonly Z_HUFFMAN_ONLY: 2;
readonly Z_RLE: 3;
readonly Z_FIXED: 4;
readonly Z_DEFAULT_STRATEGY: 0;
readonly ZLIB_VERNUM: 4897;
readonly DEFLATE: 1;
readonly INFLATE: 2;
readonly GZIP: 3;
readonly GUNZIP: 4;
readonly DEFLATERAW: 5;
readonly INFLATERAW: 6;
readonly UNZIP: 7;
readonly BROTLI_DECODE: 8;
readonly BROTLI_ENCODE: 9;
readonly ZSTD_DECOMPRESS: 11;
readonly ZSTD_COMPRESS: 10;
readonly Z_MIN_WINDOWBITS: 8;
readonly Z_MAX_WINDOWBITS: 15;
readonly Z_DEFAULT_WINDOWBITS: 15;
readonly Z_MIN_CHUNK: 64;
readonly Z_MAX_CHUNK: number;
readonly Z_DEFAULT_CHUNK: 16384;
readonly Z_MIN_MEMLEVEL: 1;
readonly Z_MAX_MEMLEVEL: 9;
readonly Z_DEFAULT_MEMLEVEL: 8;
readonly Z_MIN_LEVEL: -1;
readonly Z_MAX_LEVEL: 9;
readonly Z_DEFAULT_LEVEL: -1;
readonly BROTLI_OPERATION_PROCESS: 0;
readonly BROTLI_OPERATION_FLUSH: 1;
readonly BROTLI_OPERATION_FINISH: 2;
readonly BROTLI_OPERATION_EMIT_METADATA: 3;
readonly BROTLI_PARAM_MODE: 0;
readonly BROTLI_MODE_GENERIC: 0;
readonly BROTLI_MODE_TEXT: 1;
readonly BROTLI_MODE_FONT: 2;
readonly BROTLI_DEFAULT_MODE: 0;
readonly BROTLI_PARAM_QUALITY: 1;
readonly BROTLI_MIN_QUALITY: 0;
readonly BROTLI_MAX_QUALITY: 11;
readonly BROTLI_DEFAULT_QUALITY: 11;
readonly BROTLI_PARAM_LGWIN: 2;
readonly BROTLI_MIN_WINDOW_BITS: 10;
readonly BROTLI_MAX_WINDOW_BITS: 24;
readonly BROTLI_LARGE_MAX_WINDOW_BITS: 30;
readonly BROTLI_DEFAULT_WINDOW: 22;
readonly BROTLI_PARAM_LGBLOCK: 3;
readonly BROTLI_MIN_INPUT_BLOCK_BITS: 16;
readonly BROTLI_MAX_INPUT_BLOCK_BITS: 24;
readonly BROTLI_PARAM_DISABLE_LITERAL_CONTEXT_MODELING: 4;
readonly BROTLI_PARAM_SIZE_HINT: 5;
readonly BROTLI_PARAM_LARGE_WINDOW: 6;
readonly BROTLI_PARAM_NPOSTFIX: 7;
readonly BROTLI_PARAM_NDIRECT: 8;
readonly BROTLI_DECODER_RESULT_ERROR: 0;
readonly BROTLI_DECODER_RESULT_SUCCESS: 1;
readonly BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT: 2;
readonly BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT: 3;
readonly BROTLI_DECODER_PARAM_DISABLE_RING_BUFFER_REALLOCATION: 0;
readonly BROTLI_DECODER_PARAM_LARGE_WINDOW: 1;
readonly BROTLI_DECODER_NO_ERROR: 0;
readonly BROTLI_DECODER_SUCCESS: 1;
readonly BROTLI_DECODER_NEEDS_MORE_INPUT: 2;
readonly BROTLI_DECODER_NEEDS_MORE_OUTPUT: 3;
readonly BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_NIBBLE: -1;
readonly BROTLI_DECODER_ERROR_FORMAT_RESERVED: -2;
readonly BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_META_NIBBLE: -3;
readonly BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_ALPHABET: -4;
readonly BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_SAME: -5;
readonly BROTLI_DECODER_ERROR_FORMAT_CL_SPACE: -6;
readonly BROTLI_DECODER_ERROR_FORMAT_HUFFMAN_SPACE: -7;
readonly BROTLI_DECODER_ERROR_FORMAT_CONTEXT_MAP_REPEAT: -8;
readonly BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_1: -9;
readonly BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_2: -10;
readonly BROTLI_DECODER_ERROR_FORMAT_TRANSFORM: -11;
readonly BROTLI_DECODER_ERROR_FORMAT_DICTIONARY: -12;
readonly BROTLI_DECODER_ERROR_FORMAT_WINDOW_BITS: -13;
readonly BROTLI_DECODER_ERROR_FORMAT_PADDING_1: -14;
readonly BROTLI_DECODER_ERROR_FORMAT_PADDING_2: -15;
readonly BROTLI_DECODER_ERROR_FORMAT_DISTANCE: -16;
readonly BROTLI_DECODER_ERROR_DICTIONARY_NOT_SET: -19;
readonly BROTLI_DECODER_ERROR_INVALID_ARGUMENTS: -20;
readonly BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MODES: -21;
readonly BROTLI_DECODER_ERROR_ALLOC_TREE_GROUPS: -22;
readonly BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MAP: -25;
readonly BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_1: -26;
readonly BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_2: -27;
readonly BROTLI_DECODER_ERROR_ALLOC_BLOCK_TYPE_TREES: -30;
readonly BROTLI_DECODER_ERROR_UNREACHABLE: -31;
readonly ZSTD_e_continue: 0;
readonly ZSTD_e_flush: 1;
readonly ZSTD_e_end: 2;
readonly ZSTD_fast: 1;
readonly ZSTD_dfast: 2;
readonly ZSTD_greedy: 3;
readonly ZSTD_lazy: 4;
readonly ZSTD_lazy2: 5;
readonly ZSTD_btlazy2: 6;
readonly ZSTD_btopt: 7;
readonly ZSTD_btultra: 8;
readonly ZSTD_btultra2: 9;
readonly ZSTD_c_compressionLevel: 100;
readonly ZSTD_c_windowLog: 101;
readonly ZSTD_c_hashLog: 102;
readonly ZSTD_c_chainLog: 103;
readonly ZSTD_c_searchLog: 104;
readonly ZSTD_c_minMatch: 105;
readonly ZSTD_c_targetLength: 106;
readonly ZSTD_c_strategy: 107;
readonly ZSTD_c_enableLongDistanceMatching: 160;
readonly ZSTD_c_ldmHashLog: 161;
readonly ZSTD_c_ldmMinMatch: 162;
readonly ZSTD_c_ldmBucketSizeLog: 163;
readonly ZSTD_c_ldmHashRateLog: 164;
readonly ZSTD_c_contentSizeFlag: 200;
readonly ZSTD_c_checksumFlag: 201;
readonly ZSTD_c_dictIDFlag: 202;
readonly ZSTD_c_nbWorkers: 400;
readonly ZSTD_c_jobSize: 401;
readonly ZSTD_c_overlapLog: 402;
readonly ZSTD_d_windowLogMax: 100;
readonly ZSTD_CLEVEL_DEFAULT: 3;
readonly ZSTD_error_no_error: 0;
readonly ZSTD_error_GENERIC: 1;
readonly ZSTD_error_prefix_unknown: 10;
readonly ZSTD_error_version_unsupported: 12;
readonly ZSTD_error_frameParameter_unsupported: 14;
readonly ZSTD_error_frameParameter_windowTooLarge: 16;
readonly ZSTD_error_corruption_detected: 20;
readonly ZSTD_error_checksum_wrong: 22;
readonly ZSTD_error_literals_headerWrong: 24;
readonly ZSTD_error_dictionary_corrupted: 30;
readonly ZSTD_error_dictionary_wrong: 32;
readonly ZSTD_error_dictionaryCreation_failed: 34;
readonly ZSTD_error_parameter_unsupported: 40;
readonly ZSTD_error_parameter_combination_unsupported: 41;
readonly ZSTD_error_parameter_outOfBound: 42;
readonly ZSTD_error_tableLog_tooLarge: 44;
readonly ZSTD_error_maxSymbolValue_tooLarge: 46;
readonly ZSTD_error_maxSymbolValue_tooSmall: 48;
readonly ZSTD_error_stabilityCondition_notRespected: 50;
readonly ZSTD_error_stage_wrong: 60;
readonly ZSTD_error_init_missing: 62;
readonly ZSTD_error_memory_allocation: 64;
readonly ZSTD_error_workSpace_tooSmall: 66;
readonly ZSTD_error_dstSize_tooSmall: 70;
readonly ZSTD_error_srcSize_wrong: 72;
readonly ZSTD_error_dstBuffer_null: 74;
readonly ZSTD_error_noForwardProgress_destFull: 80;
readonly ZSTD_error_noForwardProgress_inputEmpty: 82;
}
export const constants: ZlibConstants;
export const Z_NO_FLUSH: 0;
export const Z_PARTIAL_FLUSH: 1;
export const Z_SYNC_FLUSH: 2;
export const Z_FULL_FLUSH: 3;
export const Z_FINISH: 4;
export const Z_BLOCK: 5;
export const Z_OK: 0;
export const Z_STREAM_END: 1;
export const Z_NEED_DICT: 2;
export const Z_ERRNO: -1;
export const Z_STREAM_ERROR: -2;
export const Z_DATA_ERROR: -3;
export const Z_MEM_ERROR: -4;
export const Z_BUF_ERROR: -5;
export const Z_VERSION_ERROR: -6;
export const Z_NO_COMPRESSION: 0;
export const Z_BEST_SPEED: 1;
export const Z_BEST_COMPRESSION: 9;
export const Z_DEFAULT_COMPRESSION: -1;
export const Z_FILTERED: 1;
export const Z_HUFFMAN_ONLY: 2;
export const Z_RLE: 3;
export const Z_FIXED: 4;
export const Z_DEFAULT_STRATEGY: 0;
export const ZLIB_VERNUM: 4897;
export const DEFLATE: 1;
export const INFLATE: 2;
export const GZIP: 3;
export const GUNZIP: 4;
export const DEFLATERAW: 5;
export const INFLATERAW: 6;
export const UNZIP: 7;
export const ZSTD_DECOMPRESS: 11;
export const ZSTD_COMPRESS: 10;
export const Z_MIN_WINDOWBITS: 8;
export const Z_MAX_WINDOWBITS: 15;
export const Z_DEFAULT_WINDOWBITS: 15;
export const Z_MIN_CHUNK: 64;
export const Z_MAX_CHUNK: number;
export const Z_DEFAULT_CHUNK: 16384;
export const Z_MIN_MEMLEVEL: 1;
export const Z_MAX_MEMLEVEL: 9;
export const Z_DEFAULT_MEMLEVEL: 8;
export const Z_MIN_LEVEL: -1;
export const Z_MAX_LEVEL: 9;
export const Z_DEFAULT_LEVEL: -1;
export const ZSTD_e_continue: 0;
export const ZSTD_e_flush: 1;
export const ZSTD_e_end: 2;
export const ZSTD_fast: 1;
export const ZSTD_dfast: 2;
export const ZSTD_greedy: 3;
export const ZSTD_lazy: 4;
export const ZSTD_lazy2: 5;
export const ZSTD_btlazy2: 6;
export const ZSTD_btopt: 7;
export const ZSTD_btultra: 8;
export const ZSTD_btultra2: 9;
export const ZSTD_c_compressionLevel: 100;
export const ZSTD_c_windowLog: 101;
export const ZSTD_c_hashLog: 102;
export const ZSTD_c_chainLog: 103;
export const ZSTD_c_searchLog: 104;
export const ZSTD_c_minMatch: 105;
export const ZSTD_c_targetLength: 106;
export const ZSTD_c_strategy: 107;
export const ZSTD_c_enableLongDistanceMatching: 160;
export const ZSTD_c_ldmHashLog: 161;
export const ZSTD_c_ldmMinMatch: 162;
export const ZSTD_c_ldmBucketSizeLog: 163;
export const ZSTD_c_ldmHashRateLog: 164;
export const ZSTD_c_contentSizeFlag: 200;
export const ZSTD_c_checksumFlag: 201;
export const ZSTD_c_dictIDFlag: 202;
export const ZSTD_c_nbWorkers: 400;
export const ZSTD_c_jobSize: 401;
export const ZSTD_c_overlapLog: 402;
export const ZSTD_d_windowLogMax: 100;
export const ZSTD_CLEVEL_DEFAULT: 3;
export const ZSTD_error_no_error: 0;
export const ZSTD_error_GENERIC: 1;
export const ZSTD_error_prefix_unknown: 10;
export const ZSTD_error_version_unsupported: 12;
export const ZSTD_error_frameParameter_unsupported: 14;
export const ZSTD_error_frameParameter_windowTooLarge: 16;
export const ZSTD_error_corruption_detected: 20;
export const ZSTD_error_checksum_wrong: 22;
export const ZSTD_error_literals_headerWrong: 24;
export const ZSTD_error_dictionary_corrupted: 30;
export const ZSTD_error_dictionary_wrong: 32;
export const ZSTD_error_dictionaryCreation_failed: 34;
export const ZSTD_error_parameter_unsupported: 40;
export const ZSTD_error_parameter_combination_unsupported: 41;
export const ZSTD_error_parameter_outOfBound: 42;
export const ZSTD_error_tableLog_tooLarge: 44;
export const ZSTD_error_maxSymbolValue_tooLarge: 46;
export const ZSTD_error_maxSymbolValue_tooSmall: 48;
export const ZSTD_error_stabilityCondition_notRespected: 50;
export const ZSTD_error_stage_wrong: 60;
export const ZSTD_error_init_missing: 62;
export const ZSTD_error_memory_allocation: 64;
export const ZSTD_error_workSpace_tooSmall: 66;
export const ZSTD_error_dstSize_tooSmall: 70;
export const ZSTD_error_srcSize_wrong: 72;
export const ZSTD_error_dstBuffer_null: 74;
export const ZSTD_error_noForwardProgress_destFull: 80;
export const ZSTD_error_noForwardProgress_inputEmpty: 82;
}
declare module "node:zlib" {
export * from "zlib";
}
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -103,8 +103,8 @@ export class BlockBuilder {
}
}
br(label: string): void {
this.terminate(`br label %${label}`);
br(label: string, attachment = ""): void {
this.terminate(`br label %${label}${attachment}`);
}
condBr(v: string, t: string, f: string, attachment = ""): void {
@@ -15,9 +15,10 @@ import { exactInteger, widenInteger } from "./integer-values.js";
import {
emitCountedLoopLimit,
emitCountedLoopSafety,
emitExactIntegerTest,
emitStartIntegerTest,
emitValueIntegerTest,
} from "./counted-loops.js";
import { integerOrNaNRange } from "./wrapping-sums.js";
import { emitBytesData, emitBytesHeaderLength, emitBytesSharedStorage } from "./expr-bytes.js";
import { f64Lit } from "./common.js";
import { InternalCompilerError } from "../../errors.js";
@@ -1228,7 +1229,7 @@ export function emitBytesLoopVersionGuard(
B.line(`${ok} = icmp sge i64 ${widenInteger(host, integer)}, ${counter.min}`);
conditions.push(ok);
}
} else conditions.push(emitExactIntegerTest(host, value.name, counter.min, SAFE).ok);
} else conditions.push(emitValueIntegerTest(host, value, counter.ref, counter.min, SAFE).ok);
for (const wrap of counter.wrapBounds) {
const bound = host.emitExpr(wrap.bound);
const boundInteger = exactInteger(host, bound, wrap.bound);
@@ -1258,6 +1259,9 @@ export function emitBytesLoopVersionGuard(
const startInteger = exactInteger(host, startValue, counted.start);
let start: string;
if (startInteger) start = widenInteger(host, startInteger);
else if (startValue.nanInteger !== undefined && integerOrNaNRange(host, counted.start))
// The counted-loop safety test required the ordered bit.
start = startValue.nanInteger;
else {
// The counted-loop safety test above proved an exact integer.
start = B.tmp();
@@ -1296,7 +1300,13 @@ export function emitBytesLoopVersionGuard(
for (const term of check.terms) {
const value = host.emitExpr(term.expr);
if (version.guarded.has(term.key)) {
const tested = emitExactIntegerTest(host, value.name, GUARDED_TERM.min, GUARDED_TERM.max);
const tested = emitValueIntegerTest(
host,
value,
term.expr,
GUARDED_TERM.min,
GUARDED_TERM.max,
);
conditions.push(tested.ok);
const sum = B.tmp();
B.line(`${sum} = ${term.sign > 0 ? "add" : "sub"} i64 ${base}, ${tested.integer}`);
@@ -87,9 +87,13 @@ test("teardowns of types with children of their own type free their blocks as on
classes: [
{
name: "Linked",
// The array field keeps the recursive teardown: a type whose every
// reference field holds its own type tears down in one loop
// (iterative-teardown.test.ts).
fields: [
{ name: "next", type: linked },
{ name: "other", type: linked },
{ name: "tags", type: { kind: "array", elem: { kind: "string" } } },
],
methods: [],
loc,
@@ -45,6 +45,7 @@ import {
directDestroyTarget,
segmentPrologueLines,
segmentTeardownLines,
iterativeTeardownDefinitions,
segmentedTeardown,
usedDirectTargets,
type DirectDestroy,
@@ -1334,6 +1335,15 @@ export function emitClassShapes(
} else {
const freeBody: string[] = [];
teardown(freeBody);
// A segmented class whose every reference field holds its own type and
// that has no collector header tears down in one loop (shapes.ts
// iterativeTeardownDefinitions).
const iterative =
segmented &&
!traced &&
refFields.every(
(f) => directDestroyTarget(host, f.type, directTargets)?.destroy === destroy,
);
defs.push(
...releaseBody(
host,
@@ -1345,6 +1355,18 @@ export function emitClassShapes(
lifetime,
chained,
segmented,
false,
iterative
? iterativeTeardownDefinitions(
host,
destroy,
struct,
refFields.map((f) => f.index),
`@${mangleClassRelease(cls.name)}`,
"weak",
count,
)
: null,
),
``,
);
@@ -5,9 +5,11 @@ import type {
} from "../../ir/integer-loops.js";
import { FOLLOWER_GUARD_LIMIT } from "../../ir/integer-loops.js";
import type { IrExpr } from "../../ir/ir.js";
import type { LlvmEmitterContext } from "./expr-context.js";
import type { IntegerRange } from "../../ir/integer-ranges.js";
import type { LlValue, LlvmEmitterContext } from "./expr-context.js";
import { f64Lit } from "./common.js";
import { exactInteger, widenInteger } from "./integer-values.js";
import { integerOrNaNRange } from "./wrapping-sums.js";
/** The caller has proved or guarded the direction's finite endpoint. Clamp
* the opposite infinity before integer conversion. Fractional limits become
@@ -69,7 +71,7 @@ export function emitCountedLoopSafety(
if (loop.startGuarded) {
const start = host.emitExpr(loop.start);
const bound = loop.startBound;
safe = andSafe(host, safe, emitExactIntegerTest(host, start.name, -bound, bound).ok);
safe = andSafe(host, safe, emitValueIntegerTest(host, start, loop.start, -bound, bound).ok);
}
for (const follower of loop.followers) {
if (follower.startGuarded) safe = andSafe(host, safe, emitExactStart(host, follower.start));
@@ -138,6 +140,11 @@ function emitExactStart(host: LlvmEmitterContext, expr: IrExpr): string {
const B = host.B;
const start = host.emitExpr(expr);
const integer = exactInteger(host, start, expr);
if (!integer) {
const limit = Number.MAX_SAFE_INTEGER;
const tested = emitNanIntegerTest(host, start, expr, -limit, limit);
if (tested) return tested.ok;
}
if (integer) {
const limit = Number.MAX_SAFE_INTEGER;
if (integer.range.min >= -limit && integer.range.max <= limit) return "true";
@@ -213,6 +220,8 @@ export function emitCountedStart(host: LlvmEmitterContext, expr: IrExpr): string
const value = host.emitExpr(expr);
const integer = exactInteger(host, value, expr);
if (integer) return widenInteger(host, integer);
// The guard's test of an exact integer or NaN required the ordered bit.
if (value.nanInteger !== undefined && integerOrNaNRange(host, expr)) return value.nanInteger;
const start = host.B.tmp();
host.B.line(`${start} = fptosi double ${value.name} to i64`);
return start;
@@ -227,11 +236,55 @@ export function emitStartIntegerTest(
min: number,
max: number,
): { ok: string; integer: string } {
const value = host.emitExpr(expr);
return emitValueIntegerTest(host, host.emitExpr(expr), expr, min, max);
}
/** emitExactIntegerTest of a value with the integer form it carries: an
* exact integer, whose range answers the test or two integer compares do;
* or the integer of an exact integer or NaN (`nanInteger`), which the test
* takes when the ordered bit is set and the range or compares agree. Any
* other value takes the double's test. */
export function emitValueIntegerTest(
host: LlvmEmitterContext,
value: LlValue,
expr: IrExpr | undefined,
min: number,
max: number,
): { ok: string; integer: string } {
const integer = exactInteger(host, value, expr);
if (!integer) return emitExactIntegerTest(host, value.name, min, max);
const wide = widenInteger(host, integer);
if (integer.range.min >= min && integer.range.max <= max) return { ok: "true", integer: wide };
if (integer) return integerRangeTest(host, widenInteger(host, integer), integer.range, min, max);
return (
emitNanIntegerTest(host, value, expr, min, max) ??
emitExactIntegerTest(host, value.name, min, max)
);
}
/** The test of a value's `nanInteger`: its ordered bit, and the range or two
* integer compares. Null when the value carries none. */
function emitNanIntegerTest(
host: LlvmEmitterContext,
value: LlValue,
expr: IrExpr | undefined,
min: number,
max: number,
): { ok: string; integer: string } | null {
if (value.nanInteger === undefined || value.ordered === undefined || !expr) return null;
const range = integerOrNaNRange(host, expr);
if (!range) return null;
const tested = integerRangeTest(host, value.nanInteger, range, min, max);
return { ok: andSafe(host, value.ordered, tested.ok), integer: value.nanInteger };
}
/** An integer within [min, max]: true when its range is, two compares
* otherwise. */
function integerRangeTest(
host: LlvmEmitterContext,
wide: string,
range: IntegerRange,
min: number,
max: number,
): { ok: string; integer: string } {
if (range.min >= min && range.max <= max) return { ok: "true", integer: wide };
const B = host.B;
const low = B.tmp(),
high = B.tmp(),
@@ -159,6 +159,7 @@ export function emitFunction(emitter: LlEmitter, fn: IrFunction): string {
constructorClass(fn.name) === null &&
emitter.finalFields.size > 0;
emitter.integerViews.clear();
emitter.orderedViews.clear();
emitter.fieldPointerTags.clear();
emitter.compressedFieldPtrs.clear();
emitter.integerArrayBindings.clear();
@@ -357,6 +358,15 @@ export function emitFunction(emitter: LlEmitter, fn: IrFunction): string {
`${slot} = alloca i32 ; integer view ${emitter.numericLocals.get(id)!.name}`,
);
emitter.integerViews.set(id, slot);
// A local of exact integers or NaN also keeps whether it is NaN, so
// its integer is usable without converting the double.
if (emitter.integerOrNaNLocals.has(id)) {
const ordered = B.slot();
B.entryAllocas.push(
`${ordered} = alloca i1 ; ordered view ${emitter.numericLocals.get(id)!.name}`,
);
emitter.orderedViews.set(id, ordered);
}
}
}
for (const global of initializerBindings) {
@@ -1,4 +1,5 @@
/* LlEmitter's module-level output: module parts, helper definitions, library declarations and FFI callback definitions. */
import { loopUnrollMetadata } from "./periodic-guards.js";
import { relieveHeapPressure } from "../../heap-pressure.js";
import { emitWasmModules } from "./wasm-modules.js";
import { type LlEmitter, ffiCallbackDummyLl, llStrBytes } from "./emitter.js";
@@ -1099,6 +1100,7 @@ export function emitModuleParts(emitter: LlEmitter): string[] {
out.push(...functionAttributes(emitter, hasNoInlineRecordClone));
out.push(``);
out.push(...emitter.fieldAliasMetadata());
out.push(...loopUnrollMetadata(out));
if (emitter.debug !== null) out.push(emitter.debug.render());
return out;
}
@@ -1274,6 +1276,7 @@ export function emitModuleParts(emitter: LlEmitter): string[] {
);
}
out.push(...emitter.fieldAliasMetadata());
out.push(...loopUnrollMetadata(out));
if (emitter.debug !== null) out.push(emitter.debug.render());
return out;
}
@@ -95,6 +95,7 @@ import {
} from "./string-construction.js";
import { emitStringInputs } from "./string-lifetimes.js";
import { LOOPS } from "./loop-state.js";
import { loopUnrollAttachment, periodicGuardPeriod } from "./periodic-guards.js";
import { loopAccessesTypedArrays } from "../loop-polls.js";
import { emitConstantChoice, matchConstantChoice } from "./constant-choices.js";
@@ -1315,6 +1316,13 @@ export function emitStmtBody(emitter: LlEmitter, s: IrStmt): void {
const lb = B.newLabel("loop.b");
const lu =
s.update || capturedLets.length > 0 || workerBudget !== null ? B.newLabel("loop.u") : lc;
// A periodic guard on the counter (periodic-guards.ts): the back
// edge asks LLVM to unroll the loop by the guard's period.
const period =
countedLoop && integerSlot
? periodicGuardPeriod(s, countedLoop.localId, countedLoop.start, countedLoop.step)
: null;
const backEdge = period === null ? "" : loopUnrollAttachment(period);
B.br(lc);
B.startBlock(lc);
enterLoopHeader(emitter, lc);
@@ -1375,7 +1383,7 @@ export function emitStmtBody(emitter: LlEmitter, s: IrStmt): void {
});
emitter.emitBlock(s.body);
emitter.jumpTargets.pop();
B.br(lu);
B.br(lu, lu === lc ? backEdge : "");
if (lu !== lc) {
B.startBlock(lu);
emitter.emitWorkerLoopCheck(workerBudget);
@@ -1408,7 +1416,7 @@ export function emitStmtBody(emitter: LlEmitter, s: IrStmt): void {
B.line(`store i64 ${next}, ptr ${pairedSlots[i]}`);
});
} else if (s.update) emitter.emitStmt(s.update);
B.br(lc);
B.br(lc, backEdge);
}
});
B.startBlock(le);
@@ -1851,6 +1851,9 @@ export class LlEmitter {
* their scopes. */
aliasScopedVersions = 0;
integerViews = new Map<string, string>();
/** The i1 slot beside an integer view of a local that only holds exact
* integers or NaN: true when the local is not NaN. */
orderedViews = new Map<string, string>();
/** Module globals the current function owns outright (initializer
* bindings): their function-local slot, which stands in for the global. */
privateGlobalSlots = new Map<string, string>();
@@ -7697,6 +7700,14 @@ export class LlEmitter {
if (slot === undefined) return;
const integer = this.emitToUint32(value.name, expr, value.uint32);
this.B.line(`store i32 ${integer}, ptr ${slot}`);
const orderedSlot = this.orderedViews.get(localId);
if (orderedSlot === undefined) return;
let ordered = value.ordered;
if (ordered === undefined) {
ordered = this.B.tmp();
this.B.line(`${ordered} = fcmp ord double ${value.name}, 0.0`);
}
this.B.line(`store i1 ${ordered}, ptr ${orderedSlot}`);
}
emitBytesSet(
@@ -326,7 +326,9 @@ function emitScopedBytesGet(
const signed = elem === "i8" || elem === "i16" || elem === "i32";
B.line(`${number} = ${signed ? "sitofp" : "uitofp"} i32 ${u32} to double`);
B.line(`${out} = select i1 ${valid}, double ${number}, double ${f64Lit(NaN)}`);
return { name: out, type: F64, uint32: u32, ordered: valid };
const integer = B.tmp();
B.line(`${integer} = ${signed ? "sext" : "zext"} i32 ${u32} to i64`);
return { name: out, type: F64, uint32: u32, ordered: valid, nanInteger: integer };
}
function emitBytesLoad(
@@ -25,6 +25,11 @@ export interface LlValue {
* not NaN: set by NaN-coded integer element reads and the wrapping sums
* of them, so ToInt32 consumers need no floating compare. */
ordered?: string;
/** An i64 equal to this number whenever `ordered` is true: the integer of
* a number that is an exact integer or NaN (a NaN-coded integer element
* read, a local that only holds those, or a sum, difference or product of
* them), so integer consumers need no conversion of the double. */
nanInteger?: string;
slot?: boolean;
/** A sequence local held in a capture box; release the box itself. */
boxed?: boolean;
@@ -46,7 +46,12 @@ import {
slotWidth,
type ArrayAccess,
} from "./dense-array-access.js";
import { emitWrappingSum, integerOrNaNRange, markWrappingOperands } from "./wrapping-sums.js";
import {
emitNanArithmetic,
emitWrappingSum,
integerOrNaNRange,
markWrappingOperands,
} from "./wrapping-sums.js";
import { emitPowF64, emitPowerOfTwoQuotient, isPowerOfTwoCall } from "./lib-filesystem.js";
/** The signed integer predicates of the number compares. */
@@ -205,7 +210,19 @@ export function emitLiteralExpr(
// the binding. Consumers must not reload its current view.
const uint32 = B.tmp();
B.line(`${uint32} = load i32, ptr ${integerView}`);
return { name: t, type: e.type, uint32 };
const orderedView = host.orderedViews.get(e.localId);
const range = orderedView && host.integerOrNaNLocals.get(e.localId);
if (!orderedView || !range) return { name: t, type: e.type, uint32 };
const ordered = B.tmp();
B.line(`${ordered} = load i1, ptr ${orderedView}`);
// The view holds the integer's low 32 bits (0 for NaN): its integer
// when the range is within int32 or uint32.
const signed = range.min >= -2147483648 && range.max <= 2147483647;
if (!signed && !(range.min >= 0 && range.max <= 4294967295))
return { name: t, type: e.type, uint32, ordered };
const nanInteger = B.tmp();
B.line(`${nanInteger} = ${signed ? "sext" : "zext"} i32 ${uint32} to i64`);
return { name: t, type: e.type, uint32, ordered, nanInteger };
}
if (isRefCounted(e.type)) {
// An immortal Immutable constant: nothing to count.
@@ -413,8 +430,11 @@ export function emitOperatorExpr(
const wrapped = emitWrappingSum(host, e, l, r, li, ri);
// The exact integer beside the double, for index consumers.
const wideInteger = wideArithmetic(host, e.op, l, li, r, ri);
if (wrapped !== undefined || wideInteger) {
// The integer of an exact integer or NaN, for index and loop guards.
const nan = emitNanArithmetic(host, e, l, r, li, ri);
if (wrapped !== undefined || wideInteger || nan) {
const value: LlValue = { name: t, type: e.type };
if (nan) Object.assign(value, nan);
if (wrapped !== undefined) Object.assign(value, wrapped);
if (wideInteger) value.wideInteger = wideInteger;
return value;
@@ -195,9 +195,31 @@ function emitReplaceWith(
return host.own({ name: t, type: e.type });
}
/** Substitutions whose result owns its text: once the runtime call returns,
* no operand is read again. */
function releasesInputsOnReturn(method: string): boolean {
return method === "replace" || method === "replaceAll";
}
export function emitRegexIntrinsic(
host: LlvmEmitterContext,
e: IrExpr & { kind: "regexIntrinsic" },
): LlValue {
if (!releasesInputsOnReturn(e.method)) return emitRegexOperation(host, e);
// The operands' temps (the subject of a chained substitution, a computed
// replacement) are released when the call returns rather than at the
// end of the statement, so a chain of substitutions holds at most its
// current subject and result.
host.frames.push([]);
const out = emitRegexOperation(host, e);
host.toFrame(out);
host.releaseTemps(host.frames.pop()!);
return host.own(out);
}
function emitRegexOperation(
host: LlvmEmitterContext,
e: IrExpr & { kind: "regexIntrinsic" },
): LlValue {
const B = host.B;
const method = e.method;
@@ -684,22 +684,15 @@ export function streamCbThunkFor(
passed.push(`ptr %sr`);
}
// The stream-owning completion closure (a 1-cap closure boxing the
// retained stream) — shared by the typed and dyn done shapes.
// retained stream) — shared by the typed and dyn done shapes. Every
// write and writev call receives the same one, as Node passes one
// onwrite function; other calls receive a new one, which the stream
// reuses while it holds the only reference.
const doneEntry =
kind === "w" || kind === "v" ? "scr_stream_write_closure" : "scr_stream_done_closure";
const doneClosure = (fnRef: string, tag: string): string => {
host.declare(`declare ptr @scr_closure_new(ptr, ${host.sizeType})`);
host.declare(`declare ptr @scr_box_new_obj(ptr, ptr, ptr)`);
host.declare(`declare void @scr_box_set_ref(ptr, ptr)`);
host.declare(`declare ptr @scr_stream_retain_v(ptr)`);
host.declare(`declare void @scr_stream_release_v(ptr)`);
host.declare(`declare void @scr_stream_trace(ptr, ptr, ptr)`);
d.push(
` %${tag}clo = call ptr @scr_closure_new(ptr ${fnRef}, ${host.sizeType} 1)`,
` %${tag}bx = call ptr @scr_box_new_obj(ptr @scr_stream_retain_v, ptr @scr_stream_release_v, ptr @scr_stream_trace)`,
` %${tag}cp = getelementptr inbounds %ScrClosure, ptr %${tag}clo, i64 1`,
` store ptr %${tag}bx, ptr %${tag}cp`,
` %${tag}sr = call ptr @scr_stream_retain_v(ptr %s)`,
` call void @scr_box_set_ref(ptr %${tag}bx, ptr %${tag}sr)`,
);
host.declare(`declare ptr @${doneEntry}(ptr, ptr)`);
d.push(` %${tag}clo = call ptr @${doneEntry}(ptr %s, ptr ${fnRef})`);
return `%${tag}clo`;
};
for (let i = off; i < declared.length; i++) {
@@ -0,0 +1,68 @@
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { expect, test } from "vitest";
import { compile, deserializeModule } from "../../index.js";
import { emitLlvmModule } from "./emitter.js";
async function moduleFor(source: string) {
const dir = await mkdtemp(join(tmpdir(), "scriptc-iterative-teardown-"));
try {
const entry = join(dir, "main.ts"),
output = join(dir, "main.ir.json");
await writeFile(entry, source);
const result = await compile(entry, { outDir: dir, outPath: output, outputKind: "ir" });
if (!result.ok)
throw new Error(result.diagnostics.map((item) => `${item.code}: ${item.message}`).join("\n"));
return deserializeModule(await readFile(output, "utf8"));
} finally {
await rm(dir, { recursive: true, force: true });
}
}
const SOURCE = `
interface TreeNode { left: TreeNode | null; right: TreeNode | null }
class ListNode {
next: ListNode | null;
value: number;
constructor(next: ListNode | null, value: number) { this.next = next; this.value = value; }
}
interface Labeled { child: Labeled | null; label: string[] }
function tree(d: number): TreeNode { return d > 0 ? { left: tree(d - 1), right: tree(d - 1) } : { left: null, right: null }; }
function size(n: TreeNode | null): number { return n === null ? 0 : 1 + size(n.left) + size(n.right); }
let list: ListNode | null = null;
for (let i = 0; i < 10; i++) list = new ListNode(list, i);
let labeled: Labeled | null = { child: { child: null, label: ["b"] }, label: ["a"] };
console.log(size(tree(4)), list!.value, labeled!.label.length);
list = null;
labeled = null;
`;
/** The definitions whose symbols end in `suffix`. */
function definitions(ir: string, suffix: string): string[] {
return [
...ir.matchAll(new RegExp(`^define internal [^\\n]*@([\\w.$%-]+${suffix})\\(`, "gm")),
].map((m) => m[1]!);
}
test("self-recursive records and classes tear down in one loop", async () => {
const ir = emitLlvmModule(await moduleFor(SOURCE), { targetTriple: "x86_64-unknown-linux-gnu" });
const loops = definitions(ir, "_destroy_it");
// The tree record and the list class; the labeled record also holds an
// array, so it keeps the recursive teardown.
expect(loops.length).toBe(2);
expect(loops.some((name) => name.includes("ListNode"))).toBe(true);
for (const name of loops) {
const body = new RegExp(
`^define internal void @${name.replace(/[$.]/g, "\\$&")}\\(ptr %root\\)[^]*?^}`,
"m",
).exec(ir)![0];
// No call to a teardown: dead children go onto the pending list.
expect(body).not.toMatch(/call [^\n]*_destroy/);
expect(body).toContain("inttoptr");
const base = name.slice(0, -"_it".length);
expect(ir).toContain(`define internal void @${base}(ptr %o)`);
expect(ir).not.toContain(`@${base}_s(`);
}
expect(definitions(ir, "_destroy_s").length).toBe(1);
});
@@ -1232,6 +1232,7 @@ export function emitLibCall(host: LlvmEmitterContext, e: LibCallExpr): LlValue {
case "punycode":
case "constants":
case "promise":
case "navigator":
case "mime":
return host.emitGenericLibCall(e);
case "test":
@@ -401,6 +401,22 @@ export function emitNetworkHttpLibCall(host: LlvmEmitterContext, e: LibCallExpr)
host.emitPendingCheck();
return out;
}
if (e.fn === "net.connectPath" || e.fn === "net.connectPathCb") {
const args = e.args.map((a) => host.emitExpr(a));
let cb = "null";
if (e.fn === "net.connectPathCb") {
host.toHeap(args[2]!);
cb = args[2]!.name;
}
host.declare(`declare ptr @scr_net_connect_path_checked(ptr, i1 zeroext, ptr)`);
const t = B.tmp();
B.line(
`${t} = call ptr @scr_net_connect_path_checked(ptr ${args[0]!.name}, i1 ${args[1]!.name}, ptr ${cb})`,
);
const out = host.own({ name: t, type: e.type });
host.emitPendingCheck();
return out;
}
if (e.fn === "net.sockOnData" || e.fn === "http.reqOnData") {
const cbT = e.args[1]!.type;
if (cbT.kind !== "func")
@@ -294,6 +294,12 @@ export const LIB_FN_SYMS: Record<string, string> = {
"console.table": "scr_console_table",
"console.dirDyn": "scr_console_dir_dyn",
"global.native": "scr_global_native",
"global.installValues": "scr_global_values_install",
"global.forInObject": "scr_global_for_in_object",
"navigator.object": "scr_navigator_object",
"navigator.language": "scr_navigator_language",
"navigator.languages": "scr_navigator_languages",
"navigator.platform": "scr_navigator_platform",
"process.builtinModule": "scr_process_builtin_module",
"process.builtinUnsupported": "scr_process_builtin_unsupported",
"module.namespaceValue": "scr_module_namespace_value",
@@ -1974,6 +1980,8 @@ export const USES_TIMERS_LIB_FNS = new Set<string>([
"net.listenOptsReusePortCb",
"net.connect",
"net.connectCb",
"net.connectPath",
"net.connectPathCb",
"net.connectLookup",
"net.connectLookupDyn",
"net.connectAttempt",
@@ -0,0 +1,80 @@
import { expect, test } from "vitest";
import { countedFor } from "../../ir/build.js";
import { BOOL, F64, type IrExpr, type IrStmt } from "../../ir/ir.js";
import {
loopUnrollAttachment,
loopUnrollMetadata,
periodicGuardPeriod,
} from "./periodic-guards.js";
const loc = { file: "guards.ts", start: 0, end: 0 };
const ref = (localId: string): IrExpr => ({ kind: "varRef", localId, type: F64, loc });
const num = (value: number): IrExpr => ({ kind: "numLit", value, type: F64, loc });
const bin = (op: string, left: IrExpr, right: IrExpr, type = F64): IrExpr =>
({ kind: "bin", op, left, right, type, loc }) as IrExpr;
const and = (left: IrExpr, right: IrExpr): IrExpr => ({
kind: "logical",
op: "&&",
left,
right,
type: BOOL,
loc,
});
const guard = (cond: IrExpr): IrStmt => ({
kind: "if",
cond,
then: [{ kind: "break", loc } as IrStmt],
else_: null,
loc,
});
/** `for (i = 0; i < 50; i++) { ...body }` */
const loop = (body: IrStmt[]) => countedFor(loc, num(50), () => body) as IrStmt & { kind: "for" };
const period = (body: IrStmt[], start = 0, step = 1) =>
periodicGuardPeriod(loop(body), "i.0", num(start), step);
test("a guard on the counter's low bits gives the loop its period", () => {
// if ((i & 3) === 3 && x > 4) break;
const masked = bin("===", bin("&", ref("i.0"), num(3)), num(3), BOOL);
expect(period([guard(and(masked, bin(">", ref("x"), num(4), BOOL)))])).toBe(4);
// if (0 == (7 & i)) ...
expect(period([guard(bin("==", num(0), bin("&", num(7), ref("i.0")), BOOL))])).toBe(8);
expect(period([guard(bin("!==", bin("&", ref("i.0"), num(1)), num(0), BOOL))])).toBe(2);
});
test("other guards and loops get no period", () => {
const masked = (mask: number, value: number, counter = "i.0") =>
guard(bin("===", bin("&", ref(counter), num(mask)), num(value), BOOL));
// Masks that are not 1, 3 or 7, values outside the mask, another variable.
expect(period([masked(5, 1)])).toBeNull();
expect(period([masked(15, 15)])).toBeNull();
expect(period([masked(3, 4)])).toBeNull();
expect(period([masked(3, 3, "j")])).toBeNull();
// The masked test is not the condition's first conjunct.
const later = and(
bin(">", ref("x"), num(4), BOOL),
bin("===", bin("&", ref("i.0"), num(3)), num(3), BOOL),
);
expect(period([guard(later)])).toBeNull();
// A start that is not a multiple of the period, a step other than 1.
expect(period([masked(3, 3)], 1)).toBeNull();
expect(period([masked(3, 3)], 0, 2)).toBeNull();
// A nested loop.
expect(period([masked(3, 3), loop([])])).toBeNull();
});
test("a large body is not unrolled", () => {
const work: IrStmt[] = [];
for (let k = 0; k < 400; k++)
work.push({ kind: "assign", localId: "x", value: bin("+", ref("x"), num(k)), loc } as IrStmt);
expect(
period([guard(bin("===", bin("&", ref("i.0"), num(3)), num(3), BOOL)), ...work]),
).toBeNull();
});
test("the module carries the loop ID nodes its back edges refer to", () => {
const lines = [` br label %loop.c1${loopUnrollAttachment(4)}`, " ret void"];
expect(loopUnrollMetadata(lines)).toEqual([
`!1000004 = distinct !{!1000004, !{!"llvm.loop.unroll.count", i32 4}}`,
]);
expect(loopUnrollMetadata([" ret void"])).toEqual([]);
});
@@ -0,0 +1,97 @@
/* Counted loops with a periodic guard: a branch on the counter's low bits,
* such as `if ((j & 3) === 3 && ...)` (a check every fourth iteration).
* LLVM checks such a guard on every iteration, and when the guarded
* condition is cheap it speculates the condition into the loop. The loop's
* back edge asks LLVM to unroll it by the guard's period
* (`llvm.loop.unroll.count`): in each unrolled copy the counter's low bits
* are known, so the guard folds to a constant, and only one copy in each
* period keeps the check. The metadata changes how LLVM compiles the loop,
* not what it computes. */
import type { IrExpr, IrStmt } from "../../ir/ir.js";
import { everyExprChild, everyStmtChild } from "../../ir/traverse.js";
/** The most IR nodes a loop body may have, times the period, for the loop
* to be unrolled: a larger body gains less than its unrolled copies cost. */
const UNROLLED_BODY_NODES = 4096;
/** The loop IDs, one per period (2, 4 and 8). A loop ID is a distinct
* node whose first operand is itself; loops with the same period share
* one, as the copies of an inlined loop do. */
const loopIds = new Map<number, number>([
[2, 1_000_002],
[4, 1_000_004],
[8, 1_000_008],
]);
/** The guard period of `loop`, a counted loop on `counter` that starts at
* `start` and steps by `step`, or null. The period is M + 1 where the body
* has an `if` whose condition is, or starts with an `&&` of,
* `(counter & M) === C` (or `==`, `!==`, `!=`, either operand order) with
* M one of 1, 3 and 7 and C an integer from 0 to M. The loop steps by 1
* from a multiple of the period, so that the counter's low bits are known
* in each unrolled copy, and its body has no nested loop. */
export function periodicGuardPeriod(
loop: IrStmt & { kind: "for" },
counter: string,
start: IrExpr,
step: number,
): number | null {
if (step !== 1 || start.kind !== "numLit") return null;
let period: number | null = null;
let nodes = 0;
let plain = true;
const expr = (e: IrExpr): boolean => {
nodes++;
return everyExprChild(e, expr, stmt);
};
const stmt = (s: IrStmt): boolean => {
nodes++;
if (s.kind === "for" || s.kind === "while" || s.kind === "doWhile" || s.kind === "forOf") {
plain = false;
return false;
}
if (s.kind === "if" && period === null) period = guardPeriod(s.cond, counter);
return everyStmtChild(s, expr, stmt);
};
for (const s of loop.body) if (!stmt(s)) break;
if (!plain || period === null) return null;
if (start.value % period !== 0 || nodes * period > UNROLLED_BODY_NODES) return null;
return period;
}
function guardPeriod(cond: IrExpr, counter: string): number | null {
let test = cond;
while (test.kind === "logical" && test.op === "&&") test = test.left;
if (test.kind !== "bin" || !["===", "==", "!==", "!="].includes(test.op)) return null;
const masked = (e: IrExpr, other: IrExpr): number | null => {
if (e.kind !== "bin" || e.op !== "&" || other.kind !== "numLit") return null;
const [ref, mask] = e.left.kind === "varRef" ? [e.left, e.right] : [e.right, e.left];
if (ref.kind !== "varRef" || ref.localId !== counter || mask.kind !== "numLit") return null;
const m = mask.value;
if (m !== 1 && m !== 3 && m !== 7) return null;
const c = other.value;
return Number.isInteger(c) && c >= 0 && c <= m ? m + 1 : null;
};
return masked(test.left, test.right) ?? masked(test.right, test.left);
}
/** The attachment that asks LLVM to unroll a loop by `period`, for its
* back edge. */
export function loopUnrollAttachment(period: number): string {
return `, !llvm.loop !${loopIds.get(period)!}`;
}
/** The loop ID nodes that the module's text `lines` refer to. */
export function loopUnrollMetadata(lines: readonly string[]): string[] {
const used = new Set<number>();
for (const line of lines) {
if (!line.includes("!llvm.loop !")) continue;
for (const [period, id] of loopIds) if (line.includes(`!llvm.loop !${id}`)) used.add(period);
}
return [...used]
.sort((a, b) => a - b)
.map((period) => {
const id = loopIds.get(period)!;
return `!${id} = distinct !{!${id}, !{!"llvm.loop.unroll.count", i32 ${period}}}`;
});
}
+129 -1
View File
@@ -35,6 +35,7 @@ import {
emitObjectAlloc,
emitObjectFree,
emitSegmentFree,
type FreeObservers,
CYC_APPEND,
cycleAppendDecls,
cycleAppendHelper,
@@ -1820,6 +1821,115 @@ function sizeLiteral(sizeType: RcCount, value: bigint): string {
return BigInt.asIntN(sizeType === "i64" ? 64 : 32, value).toString();
}
/** The teardown of a type whose reference fields all hold its own type
* (`fields`: their struct indices; `release`: the type's inlinable release;
* no collector header): one loop instead of a call per node. A child whose
* last reference the dead node held joins a pending list linked through its
* own count word, which it no longer needs; the loop takes the list's first
* node next, so nodes are visited parent first, fields in order. Each node
* releases its block as emitObjectFree does, keeping a block of the calling
* thread's arena in a segment linked through its first word, and the segment
* goes onto the free list in one step at the end (emitSegmentFree): the next
* structure built parent first takes the blocks in the order it allocates
* them, as segmentTeardownLines arranges for the recursive form. The loop
* needs no depth budget. `_d` (the direct-chain entry other teardowns call)
* and the plain entry both run it. A class that keeps its id in the count
* word counts in the low half (`count`); a dead node's whole word is free. */
export function iterativeTeardownDefinitions(
host: ShapeHost,
destroy: string,
struct: string,
fields: readonly number[],
release: string,
observers: FreeObservers,
count: RcCount = host.sizeType,
): string[] {
const S = host.sizeType;
const lines = [
`define internal void @${destroy}_it(ptr %root) ${FN_ATTRS} {`,
`entry:`,
` %sg.own = alloca ptr`,
` br label %it.node`,
`it.node:`,
` %o = phi ptr [ %root, %entry ], [ %it.top, %it.next ]`,
` %it.pend = phi ptr [ null, %entry ], [ %it.rest, %it.next ]`,
` %it.first = phi ptr [ null, %entry ], [ %it.fa, %it.next ]`,
` %it.last = phi ptr [ null, %entry ], [ %it.la, %it.next ]`,
...fields.flatMap((index, i) => [
` %it.f${i} = getelementptr inbounds %${struct}, ptr %o, i64 0, i32 ${index}`,
` %it.v${i} = load ptr, ptr %it.f${i}`,
]),
` store ptr null, ptr %sg.own`,
...emitObjectFree(host, false, observers, "%sg.own"),
` br label %it.freed`,
`it.freed:`,
` %it.k = load ptr, ptr %sg.own`,
` %it.kept = icmp ne ptr %it.k, null`,
` br i1 %it.kept, label %it.app, label %it.kids`,
`it.app:`,
` %it.none = icmp eq ptr %it.first, null`,
` br i1 %it.none, label %it.appd, label %it.link`,
`it.link:`,
` store ptr %it.k, ptr %it.last`,
` br label %it.appd`,
`it.appd:`,
` %it.nf = select i1 %it.none, ptr %it.k, ptr %it.first`,
` br label %it.kids`,
`it.kids:`,
` %it.fa = phi ptr [ %it.nf, %it.appd ], [ %it.first, %it.freed ]`,
` %it.la = phi ptr [ %it.k, %it.appd ], [ %it.last, %it.freed ]`,
];
// The fields go onto the list last first, so the first field is next.
let pend = "%it.pend";
let from = "it.kids";
for (let i = fields.length - 1; i >= 0; i--) {
const v = `%it.v${i}`;
lines.push(
` %it.nn${i} = icmp ne ptr ${v}, null`,
` br i1 %it.nn${i}, label %it.chk${i}, label %it.j${i}`,
`it.chk${i}:`,
` %it.rc${i} = load ${count}, ptr ${v}`,
` %it.last${i} = icmp eq ${count} %it.rc${i}, 1`,
` br i1 %it.last${i}, label %it.push${i}, label %it.sh${i}`,
`it.push${i}:`,
` %it.pl${i} = ptrtoint ptr ${pend} to ${S}`,
` store ${S} %it.pl${i}, ptr ${v}`,
` br label %it.j${i}`,
`it.sh${i}:`,
` call void ${release}(ptr ${v}) ; a shared or immortal child`,
` br label %it.j${i}`,
`it.j${i}:`,
` %it.p${i} = phi ptr [ ${pend}, %${from} ], [ ${v}, %it.push${i} ], [ ${pend}, %it.sh${i} ]`,
);
pend = `%it.p${i}`;
from = `it.j${i}`;
}
lines.push(
` %it.empty = icmp eq ptr ${pend}, null`,
` br i1 %it.empty, label %it.done, label %it.next`,
`it.next:`,
` %it.top = getelementptr i8, ptr ${pend}, i64 0`,
` %it.lw = load ${S}, ptr %it.top`,
` %it.rest = inttoptr ${S} %it.lw to ptr`,
` br label %it.node`,
`it.done:`,
...emitSegmentFree(host, "sgf", "%it.fa", "%it.la"),
` ret void`,
`}`,
`define internal void @${destroy}_d(ptr %o, i32 %d) ${FN_ATTRS} {`,
`entry:`,
` call void @${destroy}_it(ptr %o)`,
` ret void`,
`}`,
`define internal void @${destroy}(ptr %o) ${FN_ATTRS} {`,
`entry:`,
` call void @${destroy}_it(ptr %o)`,
` ret void`,
`}`,
);
return lines;
}
/** Common NULL/immortal/decrement skeleton for ordinary object releases.
* `freeBody` owns the zero-ref teardown and must leave the current block at
* its end; traced objects also get the possible-cycle-root branch.
@@ -1838,6 +1948,7 @@ export function releaseBody(
chained = false,
segmented = false,
frozen = false,
destroyDefs: readonly string[] | null = null,
): string[] {
const S = host.sizeType;
if (bounded) host.declare(`declare void @scr_rc_destroy(ptr, ptr)`);
@@ -1845,7 +1956,9 @@ export function releaseBody(
// A freezable record's count is the word without its frozen bit.
const notFrozen = sizeLiteral(S, ~recordFrozenBit(S));
const lines = [
...(bounded ? destroyDefinitions(destroy, freeBody, chained, segmented ? host : null) : []),
...(bounded
? (destroyDefs ?? destroyDefinitions(destroy, freeBody, chained, segmented ? host : null))
: []),
...releaseFastPath(host, fnName, `${fnName}_slow`, traced, comment, immortal, S, frozen),
`define internal void @${fnName}_slow(ptr %o) noinline ${FN_ATTRS} {${comment ? ` ; ${comment}` : ""}`,
`entry:`,
@@ -2141,6 +2254,11 @@ export function emitRecordShapes(
),
);
else freeBody.push(...emitObjectFree(host, traced, observers));
// A segmented shape whose every reference field holds its own type, with
// no collector header and no frozen bit in its count, tears down in one
// loop (iterativeTeardownDefinitions).
const iterative =
segmented && !traced && shape.freezable !== true && ownType.length === refMembers.length;
defs.push(
...releaseBody(
host,
@@ -2153,6 +2271,16 @@ export function emitRecordShapes(
chained,
segmented,
shape.freezable === true,
iterative
? iterativeTeardownDefinitions(
host,
destroy,
struct,
refMembers.map((m) => m.index),
`@${mangleRecordRelease(shape.id)}`,
observers,
)
: null,
),
``,
);
@@ -2,6 +2,7 @@
* pending flag (see unwindAbiFunctions). */
import type { IrExpr, IrModule } from "../../ir/ir.js";
import { everyStmtList } from "../../ir/traverse.js";
import { eachStringField } from "../../ir/fields.generated.js";
/** How many calls above a `throw` functions keep the pending flag. */
const THROW_LEVELS = 3;
@@ -129,13 +130,14 @@ export function unwindAbiFunctions(
}
// Any function a node names other than as a call's callee may be reached
// through a pointer.
// Every string field counts (eachStringField), as a key walk of the node
// would find them; a call's callee does not.
const referenced = new Set<string>();
const noteNames = (node: object, skip: string | undefined): void => {
for (const key in node) {
if (key === skip) continue;
const value = (node as Record<string, unknown>)[key];
if (typeof value === "string" && names.has(value)) referenced.add(value);
}
const noteName = (value: string): void => {
if (names.has(value)) referenced.add(value);
};
const noteCallName = (value: string, field: string): void => {
if (field !== "callee") noteName(value);
};
// The call graph, for the functions near a throw and the recursive ones.
// Calls through values (closures, class values, dynamic calls, callbacks
@@ -171,10 +173,10 @@ export function unwindAbiFunctions(
// flag they keep landing pads out of the top-level code inlined
// into the entry.
if (coroutine || fn.name === mod.entry) blocked.add(e.callee);
noteNames(e, "callee");
eachStringField(e, noteCallName);
return true;
}
noteNames(e, undefined);
eachStringField(e, noteName);
if (e.kind === "virtualCall")
for (const target of e.targets ?? methodsNamed.get(e.method) ?? []) edge(fn.name, target);
else if (e.kind === "new") {
@@ -193,7 +195,7 @@ export function unwindAbiFunctions(
stmt: (s) => {
const kind = s.kind as string;
if (kind === "throw" || kind === "rethrow") throws.add(fn.name);
noteNames(s, undefined);
eachStringField(s, noteName);
return true;
},
});
@@ -2,7 +2,7 @@ import type { IrExpr, IrFunction, IrStmt } from "../../ir/ir.js";
import { everyExprChild, everyStmtChild } from "../../ir/traverse.js";
import { integerArithmeticRange, type IntegerRange } from "../../ir/integer-ranges.js";
import type { LlValue, LlvmEmitterContext } from "./expr-context.js";
import type { LlInteger } from "./integer-values.js";
import { widenInteger, type LlInteger } from "./integer-values.js";
/** Marks the `+`, `-` and `*` operands whose number only reaches
* ToInt32/ToUint32 consumers (`| 0`, the other bitwise operators,
@@ -72,10 +72,16 @@ export function integerOrNaNIndex(
): { wide: string; ordered: string } | null {
const range = expr ? integerOrNaNRange(host, expr) : null;
if (!range) return null;
// A value that carries its integer and ordered bit needs no conversion.
if (value.nanInteger !== undefined && value.ordered !== undefined)
return { wide: value.nanInteger, ordered: value.ordered };
const B = host.B;
const ordered = B.tmp();
const wide = B.tmp();
B.line(`${ordered} = fcmp ord double ${value.name}, 0.0`);
let ordered = value.ordered;
if (ordered === undefined) {
ordered = B.tmp();
B.line(`${ordered} = fcmp ord double ${value.name}, 0.0`);
}
if (value.uint32 !== undefined && range.min >= 0 && range.max <= 4294967295) {
B.line(`${wide} = zext i32 ${value.uint32} to i64`);
} else if (value.uint32 !== undefined && range.min >= -2147483648 && range.max <= 2147483647) {
@@ -182,6 +188,50 @@ export function emitWrappingSum(
return { uint32: out, ordered: valid };
}
/** The integer of `left op right` (`+`, `-` or `*`) and its ordered bit
* when the expression is an exact integer or NaN (`integerOrNaNRange`) and
* each operand carries an integer: its exact integer, or its `nanInteger`
* and ordered bit. The result is NaN exactly when an operand is, and the i64
* arithmetic of the operands' integers is the result's integer otherwise:
* the range keeps it within the safe integers. While an operand is NaN its
* integer is 0 or another integer of these forms, so the i64 arithmetic
* stays within range either way. Undefined, emitting nothing, for any other
* expression. */
export function emitNanArithmetic(
host: LlvmEmitterContext,
e: IrExpr & { kind: "bin" },
l: LlValue,
r: LlValue,
li: LlInteger | null,
ri: LlInteger | null,
): { nanInteger: string; ordered: string } | undefined {
if (e.op !== "+" && e.op !== "-" && e.op !== "*") return undefined;
const operand = (
value: LlValue,
integer: LlInteger | null,
): { integer: string; ordered: string } | null => {
if (integer) return { integer: widenInteger(host, integer), ordered: "true" };
if (value.nanInteger === undefined || value.ordered === undefined) return null;
return { integer: value.nanInteger, ordered: value.ordered };
};
if ((!li && l.nanInteger === undefined) || (!ri && r.nanInteger === undefined)) return undefined;
if (!integerOrNaNRange(host, e)) return undefined;
const left = operand(l, li);
const right = left && operand(r, ri);
if (!right) return undefined;
const B = host.B;
const nanInteger = B.tmp();
B.line(
`${nanInteger} = ${e.op === "+" ? "add" : e.op === "-" ? "sub" : "mul"} i64 ${left!.integer}, ${right.integer}`,
);
let ordered = left!.ordered === "true" ? right.ordered : left!.ordered;
if (left!.ordered !== "true" && right.ordered !== "true") {
ordered = B.tmp();
B.line(`${ordered} = and i1 ${left!.ordered}, ${right.ordered}`);
}
return { nanInteger, ordered };
}
/** Whether every product of a value in `left` and one in `right` is an
* integer of magnitude below 2^53, which the double product holds exactly.
* The bound's own product is computed in a double: a bound of at least
+2 -2
View File
@@ -2,8 +2,8 @@
* of the link contract: a program object keeps this symbol undefined and the
* matching runtime defines it, so incompatible manual links fail before the
* program can start. */
export const RUNTIME_ABI_VERSION = 46 as const;
export const RUNTIME_ABI_MARKER = "scr_runtime_abi_v46" as const;
export const RUNTIME_ABI_VERSION = 47 as const;
export const RUNTIME_ABI_MARKER = "scr_runtime_abi_v47" as const;
/** Object consumption is useful today, but the complete scr_* surface may
* still change before 1.0. The versioned marker prevents accidental mixing;
@@ -399,6 +399,19 @@ export function runtimePackFlavorKey(
return optimized;
}
/** The units a flavor links for `features`, each with its selected
* variant. Variants of one unit can share an artifact file (the pack stores
* identical objects once), so a variant is identified by its unit and id,
* not by its path. */
export function selectedRuntimeUnits(
flavor: RuntimePackFlavorManifest,
features: RuntimeFeatureSet,
): { source: string; variant: RuntimePackVariant }[] {
return flavor.runtime_units
.filter((unit) => evaluateRuntimePredicate(unit.predicate, features))
.map((unit) => ({ source: unit.source, variant: selectVariant(unit, features) }));
}
/** Selection is deterministic and independent of installation paths. Both
* hosts verify and privately stage exactly these artifacts before linking. */
export function selectRuntimePackArtifacts(
@@ -420,9 +433,7 @@ export function selectRuntimePackArtifacts(
);
return {
features,
runtime: selectedFlavor.runtime_units
.filter((unit) => evaluateRuntimePredicate(unit.predicate, features))
.map((unit) => selectVariant(unit, features)),
runtime: selectedRuntimeUnits(selectedFlavor, features).map(({ variant }) => variant),
archives: manifest.archives.filter((archive) =>
evaluateRuntimePredicate(archive.predicate, features),
),
@@ -52,7 +52,7 @@ async function fixture(target: NativeTargetSpec, workers = false) {
object_format: target.objectFormat,
minimum_os: target.minimumOs,
},
runtime_abi: { version: 46, marker: "scr_runtime_abi_v46" },
runtime_abi: { version: 47, marker: "scr_runtime_abi_v47" },
compiler: { command: "fixture", identity: "fixture", target: target.llvmTriple },
macros: { executable: [], excluded: ["SCR_LIB"], sanitizer: "external-toolchain-required" },
flavors: {
@@ -129,7 +129,7 @@ async function fixture(target: NativeTargetSpec = MACOS_ARM64_TARGET) {
object_format: target.objectFormat,
minimum_os: target.minimumOs,
},
runtime_abi: { version: 46, marker: "scr_runtime_abi_v46" },
runtime_abi: { version: 47, marker: "scr_runtime_abi_v47" },
compiler: {
command: "clang",
identity: "fixture clang",
+156
View File
@@ -0,0 +1,156 @@
/* Canonical mapping (localized-failure design §4.3), stage N1: the IR type
* of a canonical node, built from about ten structural rules plus the
* nominal table. Nominal objects (classes, standard-library interfaces,
* handles), callables, index-signature shapes, tuples, bigint and symbol
* keep their per-type recognitions: they map through mapType on the
* checker type the node came from. Everything structural (primitives,
* literals, units, arrays, flat unions, plain records) maps here, from the
* canonical form alone.
*
* Shadow mode (SCRIPTC_CANON_SHADOW=1) compares this mapping with mapType
* for every successful top-level mapping and appends one JSON line per
* comparison to SCRIPTC_CANON_SHADOW_OUT. It maps into throwaway
* registries, so the program's own IR is untouched. */
import { appendFileSync } from "node:fs";
import { BOOL, DYN, F64, NULL_T, STRING, UNDEFINED_T, typeKey, type IrType } from "../ir/ir.js";
import { canonKey, Normalizer, type Canon } from "./canon.js";
import * as ts from "./ts7/adapter.js";
import {
mapType,
ShapeRegistry,
UnionRegistry,
withUndefinedArm,
type TypeMapperCtx,
} from "./type-mapper.js";
const MAX_DEPTH = 24;
/** The IR type of a canonical node, or null when no rule applies. */
export function mapCanonical(c: Canon, ctx: TypeMapperCtx, depth = 0): IrType | null {
if (depth > MAX_DEPTH) return null;
const viaChecker = (): IrType | null => (c.src === undefined ? null : mapType(c.src, ctx));
switch (c.k) {
case "prim":
return c.p === "number"
? F64
: c.p === "string"
? STRING
: c.p === "boolean"
? BOOL
: viaChecker();
case "lit":
return typeof c.value === "number" ? F64 : typeof c.value === "string" ? STRING : BOOL;
case "unit":
return c.u === "undefined" ? UNDEFINED_T : c.u === "null" ? NULL_T : viaChecker();
case "top":
return DYN;
case "param":
return null;
case "opaque":
case "tuple":
return viaChecker();
case "array": {
const elem = mapCanonical(c.elem, ctx, depth + 1);
return elem === null ? null : { kind: "array", elem };
}
case "union": {
const byKey = new Map<string, IrType>();
for (const arm of c.arms) {
const mapped = mapCanonical(arm, ctx, depth + 1);
if (mapped === null || mapped.kind === "void") return null;
if (mapped.kind === "union") {
const def = ctx.unions.get(mapped.unionId);
if (def === undefined) return null;
for (const a of def.arms) byKey.set(typeKey(a), a);
} else byKey.set(typeKey(mapped), mapped);
}
const arms = [...byKey.values()];
if (arms.length === 1) return arms[0]!;
if (arms.some((a) => a.kind === "dyn")) return DYN;
arms.sort((a, b) => (typeKey(a) < typeKey(b) ? -1 : 1));
return { kind: "union", unionId: ctx.unions.intern(arms) };
}
case "obj": {
if (c.nominal !== undefined || c.call().length > 0 || c.construct().length > 0)
return viaChecker();
if (c.index().length > 0) return viaChecker();
const props = c.props();
// `{}` holds any non-nullish value: the checked dynamic, as mapType.
if (props.length === 0) return DYN;
const fields: { name: string; type: IrType }[] = [];
for (const p of props) {
let t = mapCanonical(p.type(), ctx, depth + 1);
if (t === null) return null;
if (p.optional && t.kind !== "dyn") {
const armed = withUndefinedArm(t, ctx.unions);
if (armed === null) return null;
t = armed;
}
fields.push({ name: p.name, type: t });
}
const declaredOrder = fields.map((f) => f.name);
fields.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
return {
kind: "record",
shapeId: ctx.shapes.intern(fields, false, undefined, declaredOrder),
};
}
}
}
export type ShadowStatus = "equal" | "diverge" | "canon-fail";
/** Installs nothing unless SCRIPTC_CANON_SHADOW=1: the hook mapType calls
* after each successful top-level mapping. */
export function canonShadowHook(
checker: ts.TypeChecker,
): ((type: ts.Type, mapped: IrType, ctx: TypeMapperCtx) => void) | undefined {
if (process.env["SCRIPTC_CANON_SHADOW"] !== "1") return undefined;
const out = process.env["SCRIPTC_CANON_SHADOW_OUT"];
let busy = false;
return (type, mapped, ctx) => {
if (busy) return;
busy = true;
// The shadow records and must never break a compile.
try {
const resolve = ctx.resolveTypeParamTs;
const normalizer = new Normalizer(checker);
const c = normalizer.canon(type, resolve ? (t) => resolve(t) : new Map());
const { typeMemo: _memo, canonShadow: _hook, ...rest } = ctx;
const shadowCtx: TypeMapperCtx = {
...rest,
shapes: new ShapeRegistry(),
unions: new UnionRegistry(),
canMemoizeType: () => false,
};
let canonical: IrType | null = null;
try {
canonical = mapCanonical(c, shadowCtx);
} catch {
canonical = null;
}
const status: ShadowStatus =
canonical === null
? "canon-fail"
: typeKey(canonical) === typeKey(mapped)
? "equal"
: "diverge";
if (out !== undefined)
appendFileSync(
out,
`${JSON.stringify({
status,
kind: c.k,
type: checker.typeToString(type).slice(0, 160),
canon: canonKey(c).slice(0, 160),
strict: typeKey(mapped),
...(canonical !== null ? { canonical: typeKey(canonical) } : {}),
})}\n`,
);
} catch {
// A census line lost, nothing else.
} finally {
busy = false;
}
};
}
+48 -8
View File
@@ -44,7 +44,13 @@ export interface AliasRef {
tsType: ts.Type;
}
export type Canon =
export type Canon = CanonNode & {
/** The checker type the node was normalized from (a type parameter's
* binding for substituted parameters). */
src?: ts.Type;
};
type CanonNode =
| { k: "prim"; p: CanonPrim }
| { k: "lit"; value: string | number | boolean }
| { k: "unit"; u: "undefined" | "null" | "void" | "never" }
@@ -67,8 +73,10 @@ export type Canon =
| { k: "param"; name: string }
| { k: "opaque"; ts: ts.Type; reason: string };
/** Type-parameter bindings: the checker type a parameter is bound to. */
export type CanonEnv = ReadonlyMap<ts.Symbol, ts.Type>;
/** Type-parameter bindings: the checker type a parameter is bound to, by
* the parameter's symbol, or a resolver over the parameter type (the
* lowerer's resolveTypeParamTs). */
export type CanonEnv = ReadonlyMap<ts.Symbol, ts.Type> | ((param: ts.Type) => ts.Type | null);
const PRIM_FLAGS: readonly [number, CanonPrim][] = [
[ts.TypeFlags.Number, "number"],
@@ -78,9 +86,23 @@ const PRIM_FLAGS: readonly [number, CanonPrim][] = [
[ts.TypeFlags.ESSymbol | ts.TypeFlags.UniqueESSymbol, "symbol"],
];
/** A stable key for a canonical node, for deduplication and tests. */
export function canonKey(c: Canon, depth = 0): string {
if (depth > 6) return "…";
/** A stable key for a canonical node, for deduplication and tests. Deep
* and wide types are cut off (depth 6, about 4,000 characters), so equal
* keys mean equal up to that horizon. */
export function canonKey(c: Canon): string {
const budget = { left: 4000 };
return canonKeyAt(c, 0, budget);
}
function canonKeyAt(c: Canon, depth: number, budget: { left: number }): string {
const key = canonKeyInner(c, depth, budget);
budget.left -= key.length;
return key;
}
function canonKeyInner(c: Canon, depth: number, budget: { left: number }): string {
if (depth > 6 || budget.left <= 0) return "…";
const canonKey = (n: Canon, d: number) => canonKeyAt(n, d, budget);
switch (c.k) {
case "prim":
return c.p;
@@ -259,11 +281,15 @@ export class Normalizer {
if (known !== undefined) return known;
}
const node = this.build(type, env, key);
if (node.src === undefined && node.k !== "param") node.src = type;
if (key !== null && !this.interned.has(key)) this.interned.set(key, node);
return node;
}
private envKey(env: CanonEnv): string {
// A resolver's bindings are the caller's current instantiation: a
// normalizer is used for one instantiation at a time.
if (typeof env === "function") return "fn";
if (env.size === 0) return "";
const parts: string[] = [];
for (const [param, bound] of env) {
@@ -294,7 +320,12 @@ export class Normalizer {
for (const [flag, p] of PRIM_FLAGS) if (flags & flag) return { k: "prim", p };
if (flags & ts.TypeFlags.TypeParameter) {
const symbol = type.getSymbol();
const bound = symbol === undefined ? undefined : env.get(symbol);
const bound =
typeof env === "function"
? (env(type) ?? undefined)
: symbol === undefined
? undefined
: env.get(symbol);
if (bound !== undefined && bound !== type) return this.canon(bound, env);
return { k: "param", name: symbol === undefined ? "?" : ts.symbolName(symbol) };
}
@@ -361,7 +392,8 @@ export class Normalizer {
return arms.length === 1 ? arms[0]! : { k: "union", arms };
}
const prims = parts.filter((p) => p.k === "prim" || p.k === "lit");
if (prims.length === 1 && parts.every((p) => p === prims[0] || p.k === "obj")) return prims[0]!;
if (prims.length === 1 && parts.every((p) => p.k === "prim" || p.k === "lit" || p.k === "obj"))
return prims[0]!;
if (parts.every((p) => p.k === "obj" || (p.k === "top" && p.t === "object")))
return this.object(type, env, key);
return { k: "opaque", ts: type, reason: "intersection" };
@@ -406,6 +438,14 @@ export class Normalizer {
});
return node;
}
// A mapped type the checker could not resolve (over an unbound or
// symbolically bound parameter) shows no members: irreducible here.
if (
((type as ts.ObjectType).objectFlags & ts.ObjectFlags.Mapped) !== 0 &&
checker.getPropertiesOfType(type).length === 0 &&
checker.getIndexInfosOfType(type).length === 0
)
return { k: "opaque", ts: type, reason: "unresolved mapped type" };
const symbol = type.getSymbol();
const decl = symbol?.declarations[0];
const nominal =
+148 -5
View File
@@ -7,20 +7,36 @@ export { tsgoPath } from "./ts7/session-path.js";
const require = createRequire(import.meta.url);
let installedDeclarationRoot: string | null = null;
/** The paths `declarationPath` answered, by name, for the current root.
* Lowering asks for them once per mapped type (isFallbackDtsPath), and each
* answer without an installed root costs a package resolution. */
const declarationPaths = new Map<string, string>();
/** A native distribution supplies its relocatable declaration directory. */
export function setDeclarationRoot(root: string): void {
installedDeclarationRoot = root;
declarationPaths.clear();
}
function declarationPath(name: string): string {
let path = declarationPaths.get(name);
if (path === undefined) {
path = resolveDeclarationPath(name);
declarationPaths.set(name, path);
}
return path;
}
function resolveDeclarationPath(name: string): string {
return tsgoPath(
installedDeclarationRoot === null
? name === "scriptc.d.ts"
? require.resolve("@scriptc/compiler/scriptc.d.ts")
: name === "scriptc-overrides.d.ts"
? require.resolve("@scriptc/compiler/scriptc-overrides.d.ts")
: require.resolve("@scriptc/compiler/scriptc-node-fallback.d.ts")
: name === "scriptc-node-fallback.d.ts"
? require.resolve("@scriptc/compiler/scriptc-node-fallback.d.ts")
: join(dirname(require.resolve("@scriptc/compiler/scriptc-node-fallback.d.ts")), name)
: join(installedDeclarationRoot, name),
);
}
@@ -56,6 +72,9 @@ export function compilerOwnedFileName(file: string): string | null {
for (const declaration of [ambientDtsPath(), overridesDtsPath(), fallbackDtsPath()]) {
if (path === declaration) return `scriptc:${path.slice(path.lastIndexOf("/") + 1)}`;
}
const modules = fallbackModuleRoot() + "/";
if (path.startsWith(modules))
return `scriptc:${FALLBACK_MODULE_DIRECTORY}/${path.slice(modules.length)}`;
return null;
}
@@ -75,14 +94,138 @@ export function overridesDtsPath(): string {
return declarationPath("scriptc-overrides.d.ts");
}
/** Path of the shipped FALLBACK declarations (console, process, node:fs) —
* part of the program only when the target project has no @types/node.
* With @types/node, the project's real Node types stand in and this file
* stands down (its declaration forms would collide). */
/** Path of the shipped FALLBACK declarations (the console and process
* globals and the modules they name) — part of the program only when the
* target project has no @types/node. With @types/node, the project's real
* Node types stand in and this file stands down (its declaration forms
* would collide). */
export function fallbackDtsPath(): string {
return declarationPath("scriptc-node-fallback.d.ts");
}
const FALLBACK_MODULE_DIRECTORY = "node-fallback";
/** The fallback declarations of the other Node modules, one file per module
* under node-fallback/, by specifier (both spellings). Each file references
* the files of the modules its declarations name. */
const FALLBACK_MODULE_FILES: Readonly<Record<string, string>> = {
"assert": "assert.d.ts",
"assert/strict": "assert-strict.d.ts",
"async_hooks": "async_hooks.d.ts",
"buffer": "buffer.d.ts",
"child_process": "child_process.d.ts",
"cluster": "cluster.d.ts",
"constants": "constants.d.ts",
"crypto": "crypto.d.ts",
"dgram": "dgram.d.ts",
"diagnostics_channel": "diagnostics_channel.d.ts",
"dns": "dns.d.ts",
"dns/promises": "dns-promises.d.ts",
"events": "events.d.ts",
"fs": "fs.d.ts",
"fs/promises": "fs-promises.d.ts",
"graceful-fs": "graceful-fs.d.ts",
"http": "http.d.ts",
"http2": "http2.d.ts",
"https": "https.d.ts",
"module": "module.d.ts",
"net": "net.d.ts",
"node:assert": "assert.d.ts",
"node:assert/strict": "assert-strict.d.ts",
"node:async_hooks": "async_hooks.d.ts",
"node:buffer": "buffer.d.ts",
"node:child_process": "child_process.d.ts",
"node:cluster": "cluster.d.ts",
"node:constants": "constants.d.ts",
"node:crypto": "crypto.d.ts",
"node:dgram": "dgram.d.ts",
"node:diagnostics_channel": "diagnostics_channel.d.ts",
"node:dns": "dns.d.ts",
"node:dns/promises": "dns-promises.d.ts",
"node:events": "events.d.ts",
"node:fs": "fs.d.ts",
"node:fs/promises": "fs-promises.d.ts",
"node:http": "http.d.ts",
"node:http2": "http2.d.ts",
"node:https": "https.d.ts",
"node:module": "module.d.ts",
"node:net": "net.d.ts",
"node:os": "os.d.ts",
"node:path": "path.d.ts",
"node:path/posix": "path-posix.d.ts",
"node:path/win32": "path-win32.d.ts",
"node:process": "process.d.ts",
"node:punycode": "punycode.d.ts",
"node:querystring": "querystring.d.ts",
"node:stream": "stream.d.ts",
"node:stream/consumers": "stream-consumers.d.ts",
"node:stream/promises": "stream-promises.d.ts",
"node:stream/web": "stream-web.d.ts",
"node:string_decoder": "string_decoder.d.ts",
"node:test": "test.d.ts",
"node:timers": "timers.d.ts",
"node:timers/promises": "timers-promises.d.ts",
"node:tls": "tls.d.ts",
"node:url": "url.d.ts",
"node:util": "util.d.ts",
"node:util/types": "util-types.d.ts",
"node:v8": "v8.d.ts",
"node:worker_threads": "worker_threads.d.ts",
"node:zlib": "zlib.d.ts",
"os": "os.d.ts",
"path": "path.d.ts",
"path/posix": "path-posix.d.ts",
"path/win32": "path-win32.d.ts",
"process": "process.d.ts",
"punycode": "punycode.d.ts",
"querystring": "querystring.d.ts",
"stream": "stream.d.ts",
"stream/consumers": "stream-consumers.d.ts",
"stream/promises": "stream-promises.d.ts",
"stream/web": "stream-web.d.ts",
"string_decoder": "string_decoder.d.ts",
"timers": "timers.d.ts",
"timers/promises": "timers-promises.d.ts",
"tls": "tls.d.ts",
"url": "url.d.ts",
"util": "util.d.ts",
"util/types": "util-types.d.ts",
"v8": "v8.d.ts",
"worker_threads": "worker_threads.d.ts",
"zlib": "zlib.d.ts",
};
function fallbackModuleRoot(): string {
return declarationPath(FALLBACK_MODULE_DIRECTORY);
}
/** The fallback declaration file of the Node module `specifier`, or null. */
export function fallbackModulePath(specifier: string): string | null {
return Object.hasOwn(FALLBACK_MODULE_FILES, specifier)
? declarationPath(`${FALLBACK_MODULE_DIRECTORY}/${FALLBACK_MODULE_FILES[specifier]}`)
: null;
}
const QUOTED_SPECIFIER = /["'`]((?:node:)?[a-z][a-z0-9_]*(?:[-/][a-z0-9_]+)?)["'`]/g;
/** The fallback declaration files of the Node modules `text` names in a
* string literal: an over-approximation of the modules its imports, requires,
* import types and JSDoc types can name. */
export function fallbackModulesNamedIn(text: string): string[] {
const files = new Set<string>();
for (const match of text.matchAll(QUOTED_SPECIFIER)) {
const file = fallbackModulePath(match[1]!);
if (file !== null) files.add(file);
}
return [...files];
}
/** True for the fallback declarations: the core file and the module files. */
export function isFallbackDtsPath(file: string): boolean {
const path = tsgoPath(file);
return path === fallbackDtsPath() || path.startsWith(fallbackModuleRoot() + "/");
}
/** True for files belonging to the adopted Node type surface: the
* @types/node package itself and undici-types (its dependency — the
* web-platform globals: fetch/Response/AbortSignal/ReadableStream/...).
@@ -3,7 +3,7 @@ import { tmpdir } from "node:os";
import { join } from "node:path";
import { expect, test, vi } from "vitest";
import { forkTargetPaths } from "./fork-target.js";
import { fallbackDtsPath } from "./dts-paths.js";
import { fallbackDtsPath, fallbackModulePath } from "./dts-paths.js";
import { Ts7Host } from "./ts7/program-host.js";
import { Ts7Api } from "./ts7/rpc-api.js";
import { ModuleKind, ModuleResolutionKind } from "./ts7/enums.js";
@@ -35,12 +35,15 @@ test("fork discovery batches only candidate bindings and preserves aliases and s
`,
);
const host = new Ts7Host((options) => new Ts7Api(options), { cwd: directory });
const program = host.createProgram([entry, declarations], {
noLib: true,
types: [],
module: ModuleKind.ESNext,
moduleResolution: ModuleResolutionKind.Bundler,
});
const program = host.createProgram(
[entry, declarations, fallbackModulePath("node:child_process")!],
{
noLib: true,
types: [],
module: ModuleKind.ESNext,
moduleResolution: ModuleResolutionKind.Bundler,
},
);
const checker = program.getTypeChecker();
const batch = vi.spyOn(checker, "prefetchSymbolNodesExact");
const whole = vi.spyOn(checker.raw, "getTypeOfSymbol");
@@ -73,12 +76,15 @@ function discover(files: Record<string, string>): { targets: string[]; queried:
for (const [name, text] of Object.entries(files)) writeFileSync(join(directory, name), text);
const entry = join(directory, "main.ts");
const host = new Ts7Host((options) => new Ts7Api(options), { cwd: directory });
const program = host.createProgram([entry, fallbackDtsPath()], {
noLib: true,
types: [],
module: ModuleKind.ESNext,
moduleResolution: ModuleResolutionKind.Bundler,
});
const program = host.createProgram(
[entry, fallbackDtsPath(), fallbackModulePath("node:child_process")!],
{
noLib: true,
types: [],
module: ModuleKind.ESNext,
moduleResolution: ModuleResolutionKind.Bundler,
},
);
const batch = vi.spyOn(program.getTypeChecker(), "prefetchSymbolNodesExact");
try {
const targets = forkTargetPaths(program, [program.getSourceFile(entry)!]).map((path) =>
@@ -3,7 +3,7 @@ import { fileURLToPath, pathToFileURL } from "node:url";
import * as ts from "./ts7/adapter.js";
import {
ambientDtsPath,
fallbackDtsPath,
isFallbackDtsPath,
isNodeTypesPath,
overridesDtsPath,
tsgoPath,
@@ -22,7 +22,7 @@ export function isNodeGlobal(program: ts.Program, expression: ts.Identifier): bo
const source = ts.getSourceFileOfNode(declaration);
return (
source.isDeclarationFile &&
(tsgoPath(source.fileName) === fallbackDtsPath() || isNodeTypesPath(source.fileName))
(isFallbackDtsPath(source.fileName) || isNodeTypesPath(source.fileName))
);
})
);
@@ -333,7 +333,7 @@ const SPELLED_CHILD_PROCESS = new RegExp(
function namesChildProcess(program: ts.Program): boolean {
const known = program.analysis.namesChildProcess;
if (known !== null) return known;
const own = new Set([ambientDtsPath(), overridesDtsPath(), fallbackDtsPath()]);
const own = new Set([ambientDtsPath(), overridesDtsPath()]);
// Implementation files are already fetched; any other file is read as
// the host serves it, without fetching its syntax tree.
const texts = new Map<string, string>();
@@ -343,7 +343,7 @@ function namesChildProcess(program: ts.Program): boolean {
let names = false;
for (const fileName of program.getSourceFileNames()) {
const path = tsgoPath(fileName);
if (isNodeTypesPath(fileName) || own.has(path)) continue;
if (isNodeTypesPath(fileName) || isFallbackDtsPath(fileName) || own.has(path)) continue;
const text = texts.get(path) ?? program.getSourceText(fileName) ?? "";
if (
text.includes("child_process") ||
@@ -316,3 +316,41 @@ test("a file changed shortly before the read is hashed on every check", async ()
expect(frontendInputsStillMatch(snapshot)).toBe(true);
expect(reads.mock.calls.filter(([path]) => path === source)).toHaveLength(1);
});
test("a read before and a read after a file settles record one probe", async () => {
const dir = await mkdtemp(join(tmpdir(), "scriptc-inputs-"));
scratch.push(dir);
const source = join(dir, "entry.ts");
await writeFile(source, "export const answer = 1;\n");
const tracker = new FrontendInputTracker();
// The first read comes right after the write, the second once the file
// has been unchanged long enough for its identity to stand for it.
tracker.run(() => trackedReadFile(source));
vi.useFakeTimers({ toFake: ["Date"] });
vi.setSystemTime(Date.now() + 5000);
try {
tracker.run(() => trackedReadFile(source));
} finally {
vi.useRealTimers();
}
const snapshot = tracker.snapshot();
expect(snapshot.stable).toBe(true);
const probe = snapshot.probes.find((entry) => entry.op === "file");
expect(probe?.op === "file" && probe.identity !== undefined).toBe(true);
expect(frontendInputsStillMatch(snapshot)).toBe(true);
// The identity still invalidates the snapshot on any write.
await writeFile(source, "export const answer = 2;\n");
expect(frontendInputsStillMatch(snapshot)).toBe(false);
});
test("two reads of a file that saw different content prevent cache publication", async () => {
const dir = await mkdtemp(join(tmpdir(), "scriptc-inputs-"));
scratch.push(dir);
const source = join(dir, "entry.ts");
await writeFile(source, "export const answer = 1;\n");
const tracker = new FrontendInputTracker();
tracker.run(() => trackedReadFile(source));
await writeFile(source, "export const answer = 2;\n");
tracker.run(() => trackedReadFile(source));
expect(tracker.snapshot().stable).toBe(false);
});
@@ -84,6 +84,22 @@ function readWithIdentity(path: string): { text: string; identity: string | null
return { text, identity: settled && after === identity ? identity : null };
}
/** Two reads of one file that saw the same content, as one probe: a read
* before the file had been unchanged for IDENTITY_SETTLE_MS records no
* identity, and a later read of the same bytes does. The probe keeps the
* identity, which the settled read proved for that content. Null when the
* reads differ in anything else. */
function sameFileContent(
previous: FrontendInputProbe,
probe: FrontendInputProbe,
): FrontendInputProbe | null {
if (previous.op !== "file" || probe.op !== "file" || previous.digest !== probe.digest)
return null;
if (previous.identity === undefined) return probe;
if (probe.identity === undefined || probe.identity === previous.identity) return previous;
return null;
}
function pathKind(path: string): Extract<FrontendInputProbe, { op: "kind" }>["kind"] {
try {
const info = statSync(path);
@@ -142,12 +158,17 @@ export class FrontendInputTracker {
const key = `${probe.op}\0${probe.path}`;
if (probe.op === "text" && this.probes.has(key)) return;
const previous = this.probes.get(key);
if (previous !== undefined && JSON.stringify(previous) !== JSON.stringify(probe)) {
const merged = previous === undefined ? null : sameFileContent(previous, probe);
if (
merged === null &&
previous !== undefined &&
JSON.stringify(previous) !== JSON.stringify(probe)
) {
// An input changed while this frontend was running. The build may
// still finish normally, but it is not safe to publish as a cache key.
this.stable = false;
}
this.probes.set(key, probe);
this.probes.set(key, merged ?? probe);
if (probe.op === "file") {
// A successful content read supersedes earlier existence probes for the
// same path only when they also observed a file. A missing/non-file
@@ -1,13 +1,13 @@
/* Node 24's `navigator` global (lib/internal/navigator.js), the part
* libraries read to tell runtimes apart: `typeof navigator` is "object"
* and `navigator.userAgent` is `Node.js/<major>`. jose reads exactly
* that at module load (jwks/remote.js). Both fold to string literals, so
* nothing links. The other members (hardwareConcurrency, language,
* languages, platform, locks) and the object as a value keep a named
/* Node 24's `navigator` global (lib/internal/navigator.js): `typeof
* navigator` is "object", `navigator.userAgent` is `Node.js/<major>` (both
* fold to string literals; jose reads them at module load), and
* hardwareConcurrency, language, languages and platform read the runtime's
* answers (scr_node_builtin.c). The object as a value is the runtime's
* Navigator handle, whose getters answer the same. `locks` keeps a named
* refusal. */
import * as ts from "../../ts7/adapter.js";
import { strLit } from "../../../ir/build.js";
import type { IrExpr } from "../../../ir/ir.js";
import { DYN, F64, STRING, type IrExpr, type SrcLoc } from "../../../ir/ir.js";
import { isJsSourceFile, locOf } from "../../program.js";
import type { Lowerer } from "../lowerer.js";
@@ -34,9 +34,18 @@ export function lowerNavigatorMember(
nodeVersion: string,
): IrExpr | null {
if (!isNavigatorGlobal(lowerer, access.expression)) return null;
const loc = locOf(access);
if (access.name.text === "userAgent") {
return strLit(`Node.js/${nodeVersion.split(".")[0]}`, locOf(access));
return strLit(`Node.js/${nodeVersion.split(".")[0]}`, loc);
}
if (access.name.text === "hardwareConcurrency")
return { kind: "libCall", fn: "os.availableParallelism", args: [], type: F64, loc };
if (access.name.text === "language" || access.name.text === "platform") {
const fn = access.name.text === "language" ? "navigator.language" : "navigator.platform";
return { kind: "libCall", fn, args: [], type: STRING, loc };
}
if (access.name.text === "languages")
return { kind: "libCall", fn: "navigator.languages", args: [], type: DYN, loc };
return refuseNavigator(
lowerer,
access,
@@ -55,7 +64,12 @@ export function refuseNavigator(
return lowerer.noLowering(
what,
node,
"of Node's navigator global only typeof navigator and navigator.userAgent compile",
"navigator.locks (the Web Locks API) has no lowering",
symbol ?? null,
);
}
/** The navigator object as a value: the runtime's Navigator handle. */
export function navigatorValue(loc: SrcLoc): IrExpr {
return { kind: "libCall", fn: "navigator.object", args: [], type: DYN, loc };
}
@@ -11,7 +11,7 @@
import { BOOL, type IrExpr, type IrModule, type IrStmt } from "../../ir/ir.js";
import type { IrLibFn } from "../../ir/builtin-signatures.js";
import { mapExprChildren, mapStmtChildren } from "../../ir/traverse.js";
import { moduleRuntimeFeatures } from "../../ir/runtime-features.js";
import { moduleUsesAsyncContext } from "../../ir/runtime-features.js";
/** The argument that is the completion callback: its index; "last" for
* calls whose callback is the final argument when one is passed; or
@@ -45,6 +45,7 @@ const COMPLETION_CALLBACKS: ReadonlyMap<IrLibFn, CallbackPosition> = new Map<
["http.requestDynCb", "last"],
["http.requestUrlCb", "last"],
["net.connectCb", 2],
["net.connectPathCb", 2],
["net.listenCb", 2],
["net.listenOptsCb", 4],
["net.listenOptsReusePortCb", 5],
@@ -148,7 +149,7 @@ function bindCall(e: IrExpr): IrExpr {
/** Binds the completion callbacks of `mod` when its scheduling carries
* AsyncLocalStorage contexts. */
export function bindCompletionCallbacks(mod: IrModule): void {
if (!moduleRuntimeFeatures(mod).asyncContext) return;
if (!moduleUsesAsyncContext(mod)) return;
const rewriteExpr = (e: IrExpr): IrExpr => bindCall(mapExprChildren(e, rewriteExpr, rewriteStmt));
const rewriteStmt = (s: IrStmt): IrStmt => mapStmtChildren(s, rewriteExpr, rewriteStmt);
for (const fn of mod.functions) fn.body = fn.body.map(rewriteStmt);
@@ -1,4 +1,5 @@
/* Identifier references in expression position: lowerExprInner's identifier branch and its helpers. */
import { globalMemberValue } from "../global-values.js";
import { predeclareForwardClass } from "../lower-stmts.js";
import { cjsMemberAliasValueOf } from "../cjs-exports-object.js";
import { builtinClassValue, builtinMemberClassKind } from "../builtins/builtin-class-dyn.js";
@@ -55,6 +56,7 @@ import { varRef } from "../../../ir/build.js";
import {
ambientUndefReadType,
ambientUndefinedFnSymbolOf,
ambientUndefinedVarSymbolOf,
contextualUndefReadType,
fenceEarlyAliasUse,
isTemporalGlobalRoot,
@@ -89,7 +91,7 @@ import { unsupportedModuleFeatureOf } from "../../builtin-modules.js";
import { lowerGlobalValue } from "../lower-global-value.js";
import { fenceEnumObjectValue } from "../lower-enums.js";
import { mixinFnOfCallee } from "../lower-mixins.js";
import { isNavigatorGlobal, refuseNavigator } from "../builtins/navigator.js";
import { isNavigatorGlobal, navigatorValue } from "../builtins/navigator.js";
import { moduleFileName } from "../import-meta.js";
export function lowerIdentifierExpr(lowerer: Lowerer, expr: ts.Identifier, loc: SrcLoc): IrExpr {
@@ -599,6 +601,9 @@ export function lowerIdentifierExpr(lowerer: Lowerer, expr: ts.Identifier, loc:
// the same identity-token story as stdlib globals above (the
// harness adds worker_threads.Worker to its identity Set).
if (isJsSourceFile(ts.getSourceFileOfNode(expr))) {
// A member that is a global object reads as the global does.
const global = globalMemberValue(lowerer, bi, loc);
if (global) return global;
// EventEmitter and the stream classes are the runtime's untyped
// constructors, so constructor functions can inherit from them.
const classKind = builtinMemberClassKind(bi.module, bi.member);
@@ -608,12 +613,7 @@ export function lowerIdentifierExpr(lowerer: Lowerer, expr: ts.Identifier, loc:
return builtinFunctionFenceValue(lowerer, bi, expr, loc);
return {
kind: "strLit",
// node:stream/web exports the web stream globals themselves:
// the same identity token as the global spelling.
value:
bi.module === "stream/web"
? `[builtin ${bi.member}]`
: `[builtin ${bi.module}.${bi.member}]`,
value: `[builtin ${bi.module}.${bi.member}]`,
type: STRING,
loc,
};
@@ -731,43 +731,11 @@ export function lowerIdentifierExpr(lowerer: Lowerer, expr: ts.Identifier, loc:
expr.text === "decodeURI") &&
lowerer.isStdlibGlobal(expr, expr.text)
) {
const name = `%builtin.${expr.text}`;
const type = funcOf([DYN], STRING);
if (!lowerer.liftedFns.some((fn) => fn.name === name)) {
const value: IrExpr = {
kind: "libCall",
fn: "dyn.toStringCoerce",
args: [varRef("value", DYN, loc)],
type: STRING,
loc,
};
const fn =
expr.text === "encodeURIComponent"
? "str.encodeUriComponent"
: expr.text === "encodeURI"
? "str.encodeUri"
: expr.text === "decodeURI"
? "str.decodeUri"
: "str.decodeUriComponent";
lowerer.liftedFns.push({
name,
params: [{ localId: "value", name: "value", type: DYN }],
locals: [{ id: "value", name: "value", type: DYN, mutable: false }],
returnType: STRING,
body: [
{
kind: "return",
value: { kind: "libCall", fn, args: [value], type: STRING, loc },
loc,
},
],
loc,
});
}
return { kind: "closure", fnName: name, captures: [], type, loc };
return uriFunctionValue(lowerer, expr.text, loc);
}
if (lowerer.isStdlibGlobal(expr, "crypto"))
return { kind: "libCall", fn: "webcrypto.object", args: [], type: DYN, loc };
if (isNavigatorGlobal(lowerer, expr)) return navigatorValue(loc);
const nativeAlias = stdlibGlobalNameOf(lowerer, expr);
if (nativeAlias === "Buffer" && expr.text !== "Buffer") {
// An alias through a cast of globalThis carries the cast's static type,
@@ -862,14 +830,7 @@ export function lowerIdentifierExpr(lowerer: Lowerer, expr: ts.Identifier, loc:
// Stdlib/@types/node ambients never reach here (their chokepoint
// is above); only user-file declares do.
{
const sym = lowerer.resolveValueSymbol(expr);
const decl = sym ? sym.declarations[0] : undefined;
if (
decl !== undefined &&
ts.isVariableDeclaration(decl) &&
decl.initializer === undefined &&
ts.getCombinedModifierFlags(decl) & ts.ModifierFlags.Ambient
) {
if (ambientUndefinedVarSymbolOf(lowerer, expr)) {
const declared = lowerer.mapTypeOf(lowerer.typeOf(expr));
if (declared && declared.kind !== "void") {
return {
@@ -1026,14 +987,6 @@ export function lowerIdentifierExpr(lowerer: Lowerer, expr: ts.Identifier, loc:
const ns = lowerNsIdentifierValue(lowerer, expr);
if (ns) return ns;
}
if (isNavigatorGlobal(lowerer, expr)) {
refuseNavigator(
lowerer,
expr,
"the navigator object as a value",
lowerer.checker.getSymbolAtLocation(expr),
);
}
// An import of a CommonJS member export whose value is a binding of the
// exporting module (`module.exports = Client; module.exports.StatsD =
// Client`): the member holds that binding's value.
@@ -1163,3 +1116,42 @@ function cjsExportAccessorRead(lowerer: Lowerer, ident: ts.Identifier): IrExpr |
}
return cjsAccessorCall(lowerer, getter, locOf(ident));
}
/** encodeURIComponent, encodeURI, decodeURI or decodeURIComponent as a
* function value: one interned closure per function. */
export function uriFunctionValue(lowerer: Lowerer, which: string, loc: SrcLoc): IrExpr {
const name = `%builtin.${which}`;
const type = funcOf([DYN], STRING);
if (!lowerer.liftedFns.some((fn) => fn.name === name)) {
const value: IrExpr = {
kind: "libCall",
fn: "dyn.toStringCoerce",
args: [varRef("value", DYN, loc)],
type: STRING,
loc,
};
const fn =
which === "encodeURIComponent"
? "str.encodeUriComponent"
: which === "encodeURI"
? "str.encodeUri"
: which === "decodeURI"
? "str.decodeUri"
: "str.decodeUriComponent";
lowerer.liftedFns.push({
name,
params: [{ localId: "value", name: "value", type: DYN }],
locals: [{ id: "value", name: "value", type: DYN, mutable: false }],
returnType: STRING,
body: [
{
kind: "return",
value: { kind: "libCall", fn, args: [value], type: STRING, loc },
loc,
},
],
loc,
});
}
return { kind: "closure", fnName: name, captures: [], type, loc };
}
@@ -1,6 +1,7 @@
import { widensReducedRecords } from "../function-returns.js";
import { widthNarrowHelper } from "../coercions/unions.js";
/* Property reads (`a.b`): lowerExprInner's property-access branch, intrinsic and union properties, field reads and targets, and match-group reads. */
import { jsSpecialGlobalValue } from "../global-values.js";
import {
arrayClassMemberOwner,
arrayClassReceiver,
@@ -43,7 +44,6 @@ import {
} from "../../type-mapper.js";
import {
BOOL,
BYTES_ELEMENT_NAME,
BYTES_U8,
DYN,
F64,
@@ -68,7 +68,7 @@ import {
unionContainerArmsOk,
BYTES_ELEMENT_SIZE,
} from "../../../ir/ir.js";
import { lowerNavigatorMember } from "../builtins/navigator.js";
import { lowerNavigatorMember, navigatorValue } from "../builtins/navigator.js";
import { lowerX509PublicKey } from "../builtins/x509.js";
import { fenceMimeStaticRead } from "../builtins/mime.js";
import { NODE_COMPAT_VERSION } from "../builtins/process.js";
@@ -619,7 +619,10 @@ export function lowerPropertyAccessExpr(
// (`(_a = globalThis).__config ?? (_a.__config = {})`) reads back from
// the global object's own properties.
const globalType = lowerer.typeOf(staticallySelected(lowerer, expr.expression));
if (globalThisWrittenNames(lowerer, globalType).has(expr.name.text)) {
if (
globalThisWrittenNames(lowerer, globalType).has(expr.name.text) ||
lowerer.globalThisComputedWrite
) {
return {
kind: "dynKeyGet",
value: lowerer.coerceToExpected(lowerer.lowerExpr(expr.expression), DYN),
@@ -628,6 +631,10 @@ export function lowerPropertyAccessExpr(
loc,
};
}
// Node's navigator, which JavaScript reads as a free name when no
// declaration types it (isNavigatorGlobal).
if (expr.name.text === "navigator" && isJsSourceFile(ts.getSourceFileOfNode(expr)))
return navigatorValue(loc);
return dynUndefinedExpr(loc);
}
// `Uint8Array.prototype.toBase64` and its siblings, the base64/hex
@@ -1002,18 +1009,8 @@ export function lowerPropertyAccessExpr(
if (canonical === "globalThis") return lowerGlobalValue(lowerer, expr);
// Typed for the programs that name it, absent in Node 24.
if (canonical === TEMPORAL_GLOBAL) return dynUndefinedExpr(loc);
if (canonical === "ArrayBuffer")
return { kind: "libCall", fn: "arrayBuffer.constructor", args: [], type: DYN, loc };
if (canonical === "fetch")
return { kind: "libCall", fn: "fetch.function", args: [], type: DYN, loc };
if (Object.values(BYTES_ELEMENT_NAME).includes(canonical))
return {
kind: "libCall",
fn: "bytes.constructor",
args: [{ kind: "strLit", value: canonical, type: STRING, loc }],
type: DYN,
loc,
};
const value = jsSpecialGlobalValue(lowerer, canonical, loc);
if (value) return value;
return (
builtinGlobalValueAt(lowerer, expr, canonical, loc, true) ?? {
kind: "strLit",
@@ -4349,9 +4346,13 @@ const GLOBAL_CONSTRUCTOR_VALUES: ReadonlySet<string> = new Set([
/** The builtin global functions JavaScript sources read as interned
* function values (the `cond ? setImmediate : process.nextTick` feature
* probe of bcryptjs and async libraries): typeof answers "function",
* identity holds across spellings, and calls run the timer APIs. */
* probe of bcryptjs and async libraries, and the known globals Node's test
* harness collects): typeof answers "function", identity holds across
* spellings, and calls run what the direct calls run. */
const GLOBAL_FUNCTION_VALUES: ReadonlySet<string> = new Set([
"atob",
"btoa",
"structuredClone",
"setTimeout",
"setInterval",
"setImmediate",
@@ -4729,11 +4730,24 @@ function globalThisWrittenNames(lowerer: Lowerer, globalType: ts.Type): Set<stri
(node.operatorToken.kind === ts.SyntaxKind.EqualsToken ||
node.operatorToken.kind === ts.SyntaxKind.QuestionQuestionEqualsToken ||
node.operatorToken.kind === ts.SyntaxKind.BarBarEqualsToken ||
node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandEqualsToken) &&
ts.isPropertyAccessExpression(node.left) &&
lowerer.typeOf(node.left.expression) === globalType
)
names.add(node.left.name.text);
node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandEqualsToken)
) {
if (
ts.isPropertyAccessExpression(node.left) &&
lowerer.typeOf(node.left.expression) === globalType
)
names.add(node.left.name.text);
// A write by computed name may store any name: every read of a
// name the program does not declare consults the global object.
if (
ts.isElementAccessExpression(node.left) &&
lowerer.typeOf(node.left.expression) === globalType
) {
if (ts.isStringLiteralLike(node.left.argumentExpression))
names.add(node.left.argumentExpression.text);
else lowerer.globalThisComputedWrite = true;
}
}
return undefined;
});
}
@@ -34,7 +34,11 @@ import { varRef } from "../../../ir/build.js";
import { isExecExceptionType, refuseExecExceptionCode } from "../builtins/errors.js";
import { InternalCompilerError } from "../../../errors.js";
import { TEMPORAL_GLOBAL } from "../../builtin-modules.js";
import { isTemporalGlobalRoot } from "../lower-namespaces.js";
import {
ambientUndefinedFnSymbolOf,
ambientUndefinedVarSymbolOf,
isTemporalGlobalRoot,
} from "../lower-namespaces.js";
import { isF64HandleTyped } from "../f64-handles.js";
export function lowerTypeofExpr(lowerer: Lowerer, expr: ts.TypeOfExpression, loc: SrcLoc): IrExpr {
@@ -220,6 +224,16 @@ export function lowerTypeofExpr(lowerer: Lowerer, expr: ts.TypeOfExpression, loc
if (isTemporalGlobalRoot(lowerer, tested, sym)) {
return { kind: "strLit", value: "undefined", type: STRING, loc };
}
// A name the program declares ambiently but nothing defines
// (`declare const Bun`, `declare function f(): void`): Node erases the
// declaration, so typeof answers "undefined" without reading it, where
// a read throws ReferenceError.
if (
ambientUndefinedVarSymbolOf(lowerer, tested) ||
ambientUndefinedFnSymbolOf(lowerer, tested)
) {
return { kind: "strLit", value: "undefined", type: STRING, loc };
}
// `const { inspect } = require("util")` / `import { inspect } from
// "node:util"`: a binding of a builtin module's exported function,
// class or object. JavaScript sources represent such a value without
@@ -0,0 +1,295 @@
/* Builtin globals read through the global object by a name the compiler
* does not see (`globalThis[name]`, `for (name in globalThis)` followed by
* `globalThis[name]`). Such a read answers what the name's static spelling
* reads in a JavaScript source: the interned function and constructor
* values, the WebCrypto object and Buffer, and the opaque
* identity token for the other declared globals.
*
* The values come from one lookup function per program
* (`%global.builtinValue`), which a JavaScript module with such a read
* installs on the global object when its body starts
* (scr_global_values_install). The runtime answers the global object,
* console, fetch and the constants itself and calls the lookup for the
* other declared names: a value, `undefined` for the token, or `null` for
* a name without one, whose read keeps its SC2020 refusal. */
import * as ts from "../ts7/adapter.js";
import {
BYTES_ELEMENT_NAME,
DYN,
STRING,
UNDEFINED_T,
type IrExpr,
type IrStmt,
type IrType,
type SrcLoc,
} from "../../ir/ir.js";
import { isJsSourceFile } from "../program.js";
import type { Lowerer } from "./lowerer.js";
import { TEMPORAL_GLOBAL } from "../builtin-modules.js";
import { stdlibGlobalNameOf } from "./surfaces.js";
import { builtinGlobalValue } from "./expressions/property-access.js";
import { builtinNamespaceValue } from "./builtin-namespaces.js";
import { uriFunctionValue } from "./expressions/identifiers.js";
import { navigatorValue } from "./builtins/navigator.js";
import { primitiveCtorClosure } from "./lower-exprs.js";
const LOOKUP_FN = "%global.builtinValue";
/** Names the runtime answers before the lookup runs. */
const RUNTIME_ANSWERED = new Set([
"globalThis",
"global",
"console",
"fetch",
"undefined",
"NaN",
"Infinity",
]);
/** Declared globals whose reads through the global object stay refused:
* WebAssembly's namespace links the translated modules a program that
* names it statically carries. */
const REFUSED = new Set(["WebAssembly"]);
/** The receiver of the first element read of the global object
* (`globalThis[name]`, `globalThis["name"]`), or of the first for-in over
* it, in the JavaScript source `sf`; null when it has none. */
export function computedGlobalReadOf(lowerer: Lowerer, sf: ts.SourceFile): ts.Expression | null {
if (!isJsSourceFile(sf)) return null;
const cached = lowerer.computedGlobalReads.get(sf);
if (cached !== undefined) return cached;
let found: ts.Expression | null = null;
// An iterative walk: sources can nest expressions thousands deep.
ts.walkPreorder(sf, (node) => {
if (
(ts.isElementAccessExpression(node) || ts.isForInStatement(node)) &&
isGlobalObjectRead(lowerer, node.expression) &&
(ts.isForInStatement(node) || readsBuiltinByName(lowerer, node))
) {
found = node.expression;
return "stop";
}
return undefined;
});
lowerer.computedGlobalReads.set(sf, found);
return found;
}
/** Whether `expr` reads the global object: a spelling of it, or a variable
* whose only declaration initializes it with one (`const g = globalThis`).
* The scan runs before function bodies are lowered, so the alias of a
* variable declared in a function is not registered yet. */
function isGlobalObjectRead(lowerer: Lowerer, expr: ts.Expression): boolean {
if (stdlibGlobalNameOf(lowerer, expr) === "globalThis") return true;
let inner = expr;
while (ts.isParenthesizedExpression(inner)) inner = inner.expression;
if (!ts.isIdentifier(inner)) return false;
const declarations = lowerer.checker.getSymbolAtLocation(inner)?.declarations ?? [];
const declaration = declarations[0];
return (
declarations.length === 1 &&
declaration !== undefined &&
ts.isVariableDeclaration(declaration) &&
ts.isIdentifier(declaration.name) &&
declaration.initializer !== undefined &&
stdlibGlobalNameOf(lowerer, declaration.initializer) === "globalThis"
);
}
/** Whether the element access `node` of the global object can read a
* builtin global by name: its key is not a symbol (`globalThis[key]` with
* `key = Symbol.for(...)` reads the global object's symbol properties),
* and it is neither the target of `=` nor the operand of `delete`. */
function readsBuiltinByName(lowerer: Lowerer, node: ts.ElementAccessExpression): boolean {
if (lowerer.typeOf(node.argumentExpression).flags & ts.TypeFlags.ESSymbolLike) return false;
let outer: ts.Node = node;
while (ts.isParenthesizedExpression(outer.parent)) outer = outer.parent;
const parent = outer.parent;
if (ts.isDeleteExpression(parent)) return false;
return !(
ts.isBinaryExpression(parent) &&
parent.left === outer &&
parent.operatorToken.kind === ts.SyntaxKind.EqualsToken
);
}
/** The value a JavaScript source reads for the declared global `name`
* taken as a value, whatever its spelling (`name`, `globalThis.name`,
* `const { name } = globalThis`, `globalThis[name]`), or null when it
* reads the opaque identity token. */
export function jsGlobalValueByName(lowerer: Lowerer, name: string, loc: SrcLoc): IrExpr | null {
return (
jsSpecialGlobalValue(lowerer, name, loc) ??
builtinNamespaceValue(lowerer, name, loc) ??
builtinGlobalValue(name, loc, true)
);
}
/** jsGlobalValueByName for the globals other than the interned builtin
* functions and constructors (builtinGlobalValue). */
export function jsSpecialGlobalValue(lowerer: Lowerer, name: string, loc: SrcLoc): IrExpr | null {
if (name === "String" || name === "Number" || name === "Boolean" || name === "BigInt")
return primitiveCtorClosure(lowerer, name, loc, true);
if (name === "ArrayBuffer")
return { kind: "libCall", fn: "arrayBuffer.constructor", args: [], type: DYN, loc };
if (name === "Array")
return { kind: "libCall", fn: "dyn.arrayConstructor", args: [], type: DYN, loc };
if ((Object.values(BYTES_ELEMENT_NAME) as string[]).includes(name))
return {
kind: "libCall",
fn: "bytes.constructor",
args: [{ kind: "strLit", value: name, type: STRING, loc }],
type: DYN,
loc,
};
if (URI_FUNCTIONS.has(name)) return uriFunctionValue(lowerer, name, loc);
if (name === "crypto")
return { kind: "libCall", fn: "webcrypto.object", args: [], type: DYN, loc };
if (name === "navigator") return navigatorValue(loc);
if (name === "Buffer")
return { kind: "libCall", fn: "buffer.constructor", args: [], type: DYN, loc };
if (name === "fetch") return { kind: "libCall", fn: "fetch.function", args: [], type: DYN, loc };
if (name === "console")
return { kind: "libCall", fn: "console.native", args: [], type: DYN, loc };
return null;
}
const URI_FUNCTIONS = new Set([
"encodeURIComponent",
"encodeURI",
"decodeURI",
"decodeURIComponent",
]);
/** The statement that installs the program's lookup, built on first use
* from the names the checker declares on `receiver` (a global object). */
export function globalValuesInstall(
lowerer: Lowerer,
receiver: ts.Expression,
loc: SrcLoc,
): IrStmt {
const fnType: IrType & { kind: "func" } = { kind: "func", params: [STRING], ret: DYN };
if (!lowerer.liftedFnNamed(LOOKUP_FN)) {
const names = lowerer.checker
.getPropertiesOfType(lowerer.typeOf(receiver))
.map((property) => ts.symbolName(property))
.filter((name) => name !== TEMPORAL_GLOBAL && !RUNTIME_ANSWERED.has(name));
const name: IrExpr = { kind: "varRef", localId: "name.0", type: STRING, loc };
const body: IrStmt[] = [];
for (const global of [...new Set(names)].sort()) {
const value: IrExpr | null = REFUSED.has(global)
? { kind: "unitLit", unit: "null", type: { kind: "nullT" }, loc }
: jsGlobalValueByName(lowerer, global, loc);
if (value === null) continue;
body.push({
kind: "if",
cond: {
kind: "strEq",
negated: false,
left: name,
right: { kind: "strLit", value: global, type: STRING, loc },
type: { kind: "bool" },
loc,
},
then: [{ kind: "return", value: lowerer.coerceToExpected(value, DYN), loc }],
else_: null,
loc,
});
}
body.push({
kind: "return",
value: lowerer.coerceToExpected(
{ kind: "unitLit", unit: "undefined", type: UNDEFINED_T, loc },
DYN,
),
loc,
});
lowerer.liftedFns.push({
name: LOOKUP_FN,
params: [{ localId: "name.0", name: "name", type: STRING }],
returnType: DYN,
locals: [{ id: "name.0", name: "name", type: STRING, mutable: false }],
body,
loc,
});
}
const lookup = lowerer.coerceToExpected(
{ kind: "closure", fnName: LOOKUP_FN, captures: [], type: fnType, loc },
DYN,
);
return {
kind: "exprStmt",
expr: {
kind: "libCall",
fn: "global.installValues",
args: [lookup],
type: { kind: "void" },
loc,
},
loc,
};
}
/** Builtin module members that are the global objects of the same name in
* Node 24 (`require("buffer").atob === globalThis.atob`). */
const GLOBAL_MEMBERS: Readonly<Record<string, ReadonlySet<string>>> = {
buffer: new Set(["atob", "btoa", "Blob", "File", "Buffer"]),
timers: new Set([
"setTimeout",
"setInterval",
"setImmediate",
"clearTimeout",
"clearInterval",
"clearImmediate",
]),
perf_hooks: new Set([
"performance",
"PerformanceEntry",
"PerformanceMark",
"PerformanceMeasure",
"PerformanceObserver",
"PerformanceObserverEntryList",
"PerformanceResourceTiming",
]),
util: new Set(["TextEncoder", "TextDecoder"]),
url: new Set(["URL", "URLSearchParams"]),
worker_threads: new Set(["BroadcastChannel", "MessageChannel", "MessagePort"]),
"stream/web": new Set([
"ReadableStream",
"ReadableStreamDefaultReader",
"ReadableStreamBYOBReader",
"ReadableStreamBYOBRequest",
"ReadableByteStreamController",
"ReadableStreamDefaultController",
"TransformStream",
"TransformStreamDefaultController",
"WritableStream",
"WritableStreamDefaultWriter",
"WritableStreamDefaultController",
"ByteLengthQueuingStrategy",
"CountQueuingStrategy",
"TextEncoderStream",
"TextDecoderStream",
"CompressionStream",
"DecompressionStream",
]),
};
/** In a JavaScript source, the value of a builtin module member that is a
* global object (GLOBAL_MEMBERS): what the global's own spelling reads,
* so identity holds across the spellings. Null for other members. */
export function globalMemberValue(
lowerer: Lowerer,
bi: { module: string; member: string },
loc: SrcLoc,
): IrExpr | null {
if (!GLOBAL_MEMBERS[bi.module]?.has(bi.member)) return null;
return (
jsGlobalValueByName(lowerer, bi.member, loc) ?? {
kind: "strLit",
value: `[builtin ${bi.member}]`,
type: STRING,
loc,
}
);
}
@@ -1,3 +1,4 @@
import { computedGlobalReadOf, globalValuesInstall } from "./global-values.js";
import type { TypeFailureSlot } from "../../diagnostics/diagnostic.js";
import { THREADS_MODULE } from "../threads-import.js";
import { positionTag } from "./structural-positions.js";
@@ -3070,6 +3071,10 @@ export function lowerFileInit(
});
}
if (sf === lowerer.entry) header.push(...lowerer.punycodeLinkWarning());
// A module that reads builtin globals through the global object by
// computed names installs the program's lookup before its body runs.
const computedGlobalRead = computedGlobalReadOf(lowerer, sf);
if (computedGlobalRead) header.push(globalValuesInstall(lowerer, computedGlobalRead, loc0));
// Top-level `this` of a CommonJS module: its object exists before the
// module's first statement (lowerCjsTopLevelThis).
const exportsThis = lowerer.cjsExportsThis.get(sf);
@@ -676,6 +676,30 @@ export function ambientUndefReadType(lowerer: Lowerer, node: ts.Node): IrType |
return null;
}
/** The symbol of a variable a program file declares ambiently without
* an initializer (`declare const Bun: ...`), which nothing defines: Node
* erases the declaration, so a read throws the catchable ReferenceError
* "<name> is not defined" and typeof answers "undefined". Null for other
* identifiers, including the standard library's declarations, which
* describe real runtime values. */
export function ambientUndefinedVarSymbolOf(
lowerer: Lowerer,
ident: ts.Identifier,
): ts.Symbol | null {
const sym = lowerer.resolveValueSymbol(ident);
const decl = sym ? sym.declarations[0] : undefined;
if (
sym === null ||
decl === undefined ||
lowerer.isStdlibSymbol(sym) ||
!ts.isVariableDeclaration(decl) ||
decl.initializer !== undefined ||
!(ts.getCombinedModifierFlags(decl) & ts.ModifierFlags.Ambient)
)
return null;
return sym;
}
/** The symbol behind `ident` when it names an ambient `declare function`
* NOTHING defines: every function declaration on the symbol is body-less
* and ambient in a USER program file (merge partners — the fundule's
@@ -804,6 +804,19 @@ export function lowerNetModuleCall(
`the supported form is ${bi.member}(port, host?, connectListener?)`,
);
}
// connect(path[, connectListener]): a string first argument names a
// Unix socket, or a localhost port when it reads as a non-negative
// number (Node's isPipeName, decided at run time).
if (lowerer.mapTypeOf(lowerer.typeOf(args[0]!))?.kind === "string") {
return lowerNetConnectPath(
lowerer,
expr,
bi.member,
lowerer.lowerExpr(args[0]!),
true,
args[1],
);
}
const port = lowerer.lowerExprExpecting(args[0]!, F64);
// The optional middle host: a 2-arg call's second argument is the
// host when string-typed, the connect listener when func-typed.
@@ -841,6 +854,47 @@ export function lowerNetModuleCall(
);
}
/** net.connect / net.createConnection of a Unix socket path (a string
* argument or the `path` option) with an optional connect listener. Node
* ignores any other argument between them for a path, so only a listener
* may follow. */
function lowerNetConnectPath(
lowerer: Lowerer,
expr: ts.CallExpression,
member: string,
path: IrExpr,
pipeNameRule: boolean,
listener: ts.Expression | undefined,
): IrExpr {
const loc = locOf(expr);
const rule: IrExpr = { kind: "boolLit", value: pipeNameRule, type: BOOL, loc };
if (expr.arguments.nodes.length > 2) {
lowerer.noLowering(
`${member} of a path with ${expr.arguments.nodes.length} arguments`,
expr,
`the supported form is ${member}(path[, connectListener])`,
);
}
if (listener === undefined)
return { kind: "libCall", fn: "net.connectPath", args: [path, rule], type: NETSOCKET_T, loc };
const { cb } = lowerCallbackArg(
lowerer,
listener,
"connect listeners",
0,
() => false,
"use ()",
[],
);
return {
kind: "libCall",
fn: "net.connectPathCb",
args: [path, rule, cb],
type: NETSOCKET_T,
loc,
};
}
/** True when `t` is the lookup-function shape the connect lowering
* accepts: (hostname: string, options: unknown, callback) => void where
* callback is (err: <union with a null arm>, addresses: { address:
@@ -929,6 +983,29 @@ function lowerNetConnectOptions(
}
}
}
// connect({ path }[, connectListener]): the Unix-socket form.
{
const keys = optsNode.properties.nodes.map((p) =>
(ts.isPropertyAssignment(p) || ts.isShorthandPropertyAssignment(p)) &&
(ts.isIdentifier(p.name) || ts.isStringLiteral(p.name))
? p.name.text
: null,
);
if (keys.length === 1 && keys[0] === "path") {
const prop = optsNode.properties.nodes[0]!;
const path = ts.isPropertyAssignment(prop)
? lowerer.lowerExpr(prop.initializer)
: lowerer.lowerShorthandValue(prop as ts.ShorthandPropertyAssignment);
if (path.type.kind !== "string") {
lowerer.noLowering(
`a ${member} 'path' option of '${lowerer.fmt(path.type)}' values`,
prop,
"the path is a string here",
);
}
return lowerNetConnectPath(lowerer, expr, member, path, false, args[1]);
}
}
let port: IrExpr | null = null;
let host: IrExpr | null = null;
let lookup: IrExpr | null = null;

Some files were not shown because too many files have changed in this diff Show More