Merge main

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Malte Ubl
2026-10-27 05:36:35 -07:00
co-authored by Claude Opus 5.5
862 changed files with 55574 additions and 11903 deletions
+37 -13
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]
@@ -663,11 +665,9 @@ jobs:
if: ${{ needs.validation_scope.outputs.full == 'true' && needs.lanes.outputs.macos == 'true' }}
name: test (macOS arm64 LLVM artifacts, no clang, ${{ matrix.shard }}/5)
runs-on: macos-15
# Shard 1 additionally runs package/install and link-parity contracts
# before its LLVM differential slice. The slice itself can take ~10
# minutes on a cold hosted runner, so 20 minutes cancels an otherwise
# healthy job before the differential completes. Slow runners need more
# headroom than that, so the limit is 50 minutes.
# Each LLVM differential slice takes 30 to 35 minutes on a hosted runner.
# The package/install and link-parity contracts run in their own job
# (llvm_artifacts_macos_arm64_contracts), so no slice carries them.
timeout-minutes: 50
strategy:
fail-fast: false
@@ -678,6 +678,36 @@ jobs:
# keeping each macOS job below its timeout while the matrix union still
# exercises every LLVM-tier program.
SCRIPTC_TEST_SHARD: ${{ matrix.shard }}/5
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 11.1.3
- uses: actions/setup-node@v4
with:
node-version-file: .node-version
cache: pnpm
- run: pnpm install --frozen-lockfile
- uses: actions/download-artifact@v4
with:
name: native-darwin-arm64
path: ${{ runner.temp }}
- run: tar -xzf "$RUNNER_TEMP/native-darwin-arm64.tar.gz"
- run: pnpm build
- name: LLVM release/dev differential (${{ matrix.shard }}/5)
env:
SCRIPTC_TEST_WORKERS: "4"
run: pnpm test tests/harness/llvm-differential.test.ts
llvm_artifacts_macos_arm64_contracts:
needs: [validation_scope, lanes, native_macos]
if: ${{ needs.validation_scope.outputs.full == 'true' && needs.lanes.outputs.macos == 'true' }}
name: test (macOS arm64 LLVM artifacts, no clang, contracts)
runs-on: macos-15
# The no-clang object, helper parity, packaged helper and packed npm
# installation contracts: about 12 minutes, which used to run in front
# of shard 1's differential slice and took that job to 45-50 minutes.
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
@@ -695,10 +725,8 @@ jobs:
- run: tar -xzf "$RUNNER_TEMP/native-darwin-arm64.tar.gz"
- run: pnpm build
- name: No-clang assembly/object contract
if: matrix.shard == 1
run: pnpm test packages/cli/test/native-output.test.ts
- name: Helper diagnostics and object/link parity
if: matrix.shard == 1
run: >-
pnpm test
packages/compiler/test/native-codegen-integration.test.ts
@@ -706,17 +734,11 @@ jobs:
packages/cli/test/native-link-info.test.ts
packages/cli/test/runtime-pack.test.ts
tests/harness/native-object-example.test.ts
- name: LLVM release/dev differential (${{ matrix.shard }}/5)
env:
SCRIPTC_TEST_WORKERS: "4"
run: pnpm test tests/harness/llvm-differential.test.ts
- name: Packaged helper contract
if: matrix.shard == 1
run: |
TARBALL=$(pnpm --dir packages/llvm-darwin-arm64 pack --pack-destination "$RUNNER_TEMP" --silent)
node scripts/verify-llvm-package.mjs "$RUNNER_TEMP/$(basename "$TARBALL")"
- name: Packed npm installation smoke
if: matrix.shard == 1
env:
# Release optimization is covered by the compiler bootstrap jobs.
# This lane verifies packaging and installation with a faster seed.
@@ -989,6 +1011,7 @@ jobs:
checker_identity,
llvm_artifacts_linux_x64,
llvm_artifacts_macos_arm64,
llvm_artifacts_macos_arm64_contracts,
llvm_artifacts_macos_x64,
windows_cli,
llvm_artifacts_windows_x64,
@@ -1022,6 +1045,7 @@ jobs:
lane checker_identity "${{ needs.checker_identity.result }}" "${{ needs.lanes.outputs.linux }}"
lane llvm_artifacts_linux_x64 "${{ needs.llvm_artifacts_linux_x64.result }}" "${{ needs.lanes.outputs.linux }}"
lane llvm_artifacts_macos_arm64 "${{ needs.llvm_artifacts_macos_arm64.result }}" "${{ needs.lanes.outputs.macos }}"
lane llvm_artifacts_macos_arm64_contracts "${{ needs.llvm_artifacts_macos_arm64_contracts.result }}" "${{ needs.lanes.outputs.macos }}"
lane llvm_artifacts_macos_x64 "${{ needs.llvm_artifacts_macos_x64.result }}" "${{ needs.lanes.outputs.macos }}"
lane windows_cli "${{ needs.windows_cli.result }}" "${{ needs.lanes.outputs.linux }}"
lane llvm_artifacts_windows_x64 "${{ needs.llvm_artifacts_windows_x64.result }}" "${{ needs.lanes.outputs.linux }}"
+44 -14
View File
@@ -101,17 +101,17 @@ jobs:
if (receiptRun) appendFileSync(process.env.GITHUB_OUTPUT, `receipt_run=${receiptRun}\n`);
NODE
# Collects the native compiler's profile on this commit
# (scripts/refresh-cli-profile.mjs), so every platform's CLI build optimizes
# with a profile of the code it compiles. The committed profile falls behind
# main within a day or two. If this job fails, the CLI builds use the
# committed profile.
# Collects the native compiler's function layout and profile on this commit
# (scripts/refresh-cli-layout.mjs, scripts/refresh-cli-profile.mjs), so every
# platform's CLI build optimizes and lays out the code it compiles. The
# committed ones fall behind main within a day or two. If this job fails,
# the CLI builds use the committed profile and layout.
cli-profile:
name: Collect the native compiler profile
needs: check-release
if: needs.check-release.outputs.should_release == 'true' && needs.check-release.outputs.reuse_artifacts != 'true'
runs-on: ubuntu-24.04
timeout-minutes: 45
timeout-minutes: 75
continue-on-error: true
permissions:
contents: read
@@ -136,14 +136,26 @@ jobs:
git clone --quiet --depth 1 https://github.com/vercel-labs/tsc-ts "$RUNNER_TEMP/tsc-ts"
echo "tsc-ts $(git -C "$RUNNER_TEMP/tsc-ts" rev-parse HEAD)"
npm ci --prefix "$RUNNER_TEMP/tsc-ts"
# The layout comes first: the profile matches the functions of builds
# with the layout its instrumented compiler was built with, so the
# profile is collected with a second instrumented compiler built with
# this layout.
- name: Collect the function layout
run: >-
node scripts/refresh-cli-layout.mjs --jobs 4
--output "$RUNNER_TEMP/cli-profile/cli.layout.gz" --work "$RUNNER_TEMP/cli-layout-work"
- name: Collect the profile
env:
SCRIPTC_NATIVE_LAYOUT: ${{ runner.temp }}/cli-profile/cli.layout.gz
run: >-
node scripts/refresh-cli-profile.mjs --tsc-ts "$RUNNER_TEMP/tsc-ts" --jobs 4
--output "$RUNNER_TEMP/cli-profile/cli.profdata.gz"
--output "$RUNNER_TEMP/cli-profile/cli.profdata.gz" --work "$RUNNER_TEMP/cli-profile-work"
- uses: actions/upload-artifact@v4
with:
name: cli-profile
path: ${{ runner.temp }}/cli-profile/cli.profdata.gz
path: |
${{ runner.temp }}/cli-profile/cli.profdata.gz
${{ runner.temp }}/cli-profile/cli.layout.gz
retention-days: 7
if-no-files-found: error
@@ -336,8 +348,8 @@ jobs:
with:
name: cli-profile
path: ${{ runner.temp }}/cli-profile
# Without the artifact, SCRIPTC_NATIVE_PROFILE_USE stays unset and
# build-native-cli.mts uses the committed profile.
# Without the artifact, SCRIPTC_NATIVE_PROFILE_USE and SCRIPTC_NATIVE_LAYOUT
# stay unset and build-native-cli.mts uses the committed profile and layout.
- name: Select the native compiler profile
if: matrix.cli != ''
shell: bash
@@ -347,6 +359,11 @@ jobs:
if [ -n "$COLLECTED" ]; then
echo "SCRIPTC_NATIVE_PROFILE_USE=$COLLECTED" >> "$GITHUB_ENV"
echo "The native CLI builds with the profile collected on $GITHUB_SHA"
layout="$(dirname "$COLLECTED")/cli.layout.gz"
if [ -f "$layout" ]; then
echo "SCRIPTC_NATIVE_LAYOUT=$layout" >> "$GITHUB_ENV"
echo "The native CLI builds with the function layout collected on $GITHUB_SHA"
fi
else
echo "::warning::No profile was collected on this commit; the native CLI builds with the committed profile"
fi
@@ -424,7 +441,8 @@ jobs:
(needs.check-release.outputs.reuse_artifacts == 'true' ||
(needs.build-native-packages.result == 'success' && needs.build-mobile-runtime-packs.result == 'success'))
runs-on: ubuntu-latest
timeout-minutes: 15
# Two waits for npm availability of at most 25 minutes each.
timeout-minutes: 70
environment: Release
permissions:
contents: read
@@ -577,9 +595,8 @@ jobs:
}
# pnpm pack resolves workspace dependencies; npm handles OIDC.
# Publish dependencies first. Registry propagation after an accepted
# upload must not block the remaining packages; the npm-available
# job waits for it.
# Publish dependencies first. The npm-available job waits for the
# packages that nothing in this release depends on.
publish_dir packages/runtime
publish_dir packages/runtime-darwin-arm64
publish_dir packages/llvm-darwin-arm64
@@ -599,11 +616,24 @@ jobs:
publish_dir packages/runtime-wasm32-wasi
publish_dir packages/runtime-win32-x64-msvc
publish_dir packages/llvm-win32-x64-msvc
# A native CLI package depends on its platform's LLVM helper and
# runtime pack packages, and scriptc on the native CLI packages.
# Each group publishes only once the packages it depends on
# resolve, so an install never finds a package whose dependencies
# npm is still processing. A rerun skips accepted uploads and waits
# again.
wait_available() {
awk 'FILENAME == ARGV[1] { accepted[$1] = $2; next } { print $1, accepted[$1] }' \
"$RUNNER_TEMP/npm-accepted-times.txt" "$RUNNER_TEMP/npm-release.txt" > "$RUNNER_TEMP/npm-wait.txt"
node scripts/wait-npm-available.mjs "$RUNNER_TEMP/npm-wait.txt" --interval 30 --timeout 1500
}
wait_available
for pkg in packages/cli-*; do publish_dir "$pkg"; done
publish_dir packages/compiler
publish_dir packages/experimental
publish_dir packages/threads
publish_dir packages/types
wait_available
publish_dir packages/cli
echo "All package uploads accepted; npm registry propagation may continue after this job." >> "$GITHUB_STEP_SUMMARY"
+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",
},
},
],
}
+14
View File
@@ -25,6 +25,8 @@ their supported native-clang contracts locally; other hosts retain those
checks in the Sandboxes. Both lanes green is the bar before shipping any
change.
`SCRIPTC_SANDBOX_NATIVE_REFERENCE` names a built checkout inside a prepared `SCRIPTC_SANDBOX_SNAPSHOT` or image: each lane copies that checkout's LLVM helper and runtime pack instead of rebuilding them when their source inputs are identical. Sandbox creation is rate limited per team in vCPUs per minute, so the runner retries a rate-limited creation with backoff. Larger Sandboxes (`SCRIPTC_SANDBOX_VCPUS` up to 32, with `SCRIPTC_TEST_WORKERS` at half the vCPUs) shorten the lanes most.
`VERCEL_OIDC_TOKEN` is the preferred credential. `VERCEL_TOKEN` remains
supported with explicit `VERCEL_TEAM_ID` and `VERCEL_PROJECT_ID`. The custom
`SCRIPTC_SANDBOX_IMAGE` is optional: without it, the gate starts from
@@ -58,6 +60,8 @@ A change that must not cost good code anything, such as a compatibility fix that
node scripts/zero-cost-gate.mjs --baseline=/vercel/sandbox/prev --candidate=. --allow='\barguments\b'
```
The whole gate takes 30 to 75 minutes on one 8-vCPU Sandbox, so shard it across Sandboxes. `--shard=i/n` splits the bench and corpus programs; the self-hosted compiler runs in shard 1, or alone with `--set=self`, and its candidate-source build overlaps its comparison build on hosts with 40 GB or more. Each shard writes `--json`, and `node scripts/zero-cost-merge.mjs <shard.json>...` merges them into one verdict, recomputing the compile-time intervals over every shard. `--record-baseline-cache=<file>` builds only the baseline and records each program's artifact hashes; `--baseline-cache=<file>` then skips the baseline build of every program whose candidate artifacts hash the same, which halves the work for a change that leaves most programs identical. A record applies only to the built baseline checkout, toolchain, and paths it was made with, and is ignored otherwise; a program whose entry, relative imports, or optimization level changed since the record builds its baseline.
Test location follows scope:
- Co-locate white-box unit tests with implementation files under
@@ -68,6 +72,16 @@ Test location follows scope:
Keep existing tests in place unless a change already touches their organization;
new tests should follow this convention.
## Numbered registries
Parallel changes must never pick the same number by hand. Each registry has one source of truth and a fast check:
- Diagnostic codes: claim a code by adding its row, in numeric order, to `DIAGNOSTIC_CODES` in `packages/compiler/src/diagnostics/codes.ts` before any site uses it. A shipped construct's code moves to `RETIRED_DIAGNOSTIC_CODES` and is never reused; codes that open work already uses are held in `RESERVED_DIAGNOSTIC_CODES`. `codes.test.ts` fails on a code without a row, a retired code in use, an unused row, or rows out of order, and two claims of one code are a merge conflict or a duplicate key.
- Dyn handle tags: add a new tag by name only to `SCR_DYNH_NEW_TAGS` in `packages/runtime/src/scr_dyn.h`, in name order; the enum numbers it. Pin a tag to an explicit value in `SCR_DYNH_TAGS` only when the LLVM backend needs its number. The header does not compile when two tags share a value, and `dyn-handle-tags.test.ts` names the problem.
- Runtime enums and flag bits the backend mirrors (`ScrDynKind`, `ScrMessageKind`, `ScrBytesElem`, `SCR_CLOSURE_KIND_*`, the `%ScrArr` spare flags) are checked against their C declarations by `packages/compiler/src/backend/llvm/runtime-registries.test.ts`. A new mirrored enum or flag set gets a check there, and C code names flag bits instead of writing their values.
Before taking a number in a pinned registry, run `node scripts/registry-claims.mjs`: it lists the diagnostic codes and pinned handle tags that open pull requests claim and reports collisions; `--check` reports only collisions with the current branch and exits 1 when there is one.
## Where things live
- `packages/compiler` — the frontend (tsc API to IR), typed IR, validator, serializer, and LLVM backend.
+2
View File
@@ -26,6 +26,7 @@ RUN apt-get update \
libclang-rt-22-dev \
libpolly-22-dev \
libzstd-dev \
lld-22 \
llvm-22-dev \
ninja-build \
xz-utils \
@@ -33,6 +34,7 @@ RUN apt-get update \
&& ln -sf clang-22 /usr/bin/clang \
&& ln -sf clang++-22 /usr/bin/clang++ \
&& test "$(llvm-config-22 --version)" = 22.1.8 \
&& test -x "$(dirname "$(readlink -f /usr/bin/clang)")/ld.lld" \
&& rm -rf /var/lib/apt/lists/*
RUN curl --fail --silent --show-error --location \
+4 -2
View File
@@ -14,9 +14,11 @@ To prepare a release:
CI (`.github/workflows/release.yml`) compares the version in `packages/cli/package.json` to what `scriptc` has on npm. If it differs, it builds platform packages on matching macOS, Linux, and Windows runners, verifies the version spine, and publishes the runtime, helper, compiler, and CLI packages in dependency order. After the publish succeeds, a separate job creates the git tag `v<version>` and the GitHub release with the marked changelog entry as its body, and attaches `surface-manifest.json` — the machine-readable listing of the surface the static tier compiles at that version (stable per-entry ids, so two releases diff mechanically; see `packages/compiler/src/coverage/surface-manifest.ts` for the schema). The job regenerates the manifest from the tree and fails on any byte difference from the committed file before attaching, so the asset is always the manifest of the code being released. The same file ships inside the `@scriptc/compiler` package as `@scriptc/compiler/surface-manifest.json`.
The release job builds each pinned LLVM helper, precompiled runtime pack, and native CLI on its matching host. The native CLI bundles its TypeScript checker, LLVM helper, runtime pack, declarations, and comptime evaluator. npm postinstall installs the matching native executable; Node is needed for npm installation but not native compilation or execution. The user's toolchain performs the final platform link. Sanitized builds also use it to instrument program LLVM and compile the C runtime. Platform packages publish ahead of `@scriptc/compiler` and `scriptc`. The GitHub release attaches standalone compiler archives, their SHA-256 checksums, and the surface manifest after npm publication succeeds.
The release job builds each pinned LLVM helper, precompiled runtime pack, and native CLI on its matching host. The native CLI package bundles its TypeScript checker, declarations, and comptime evaluator, and depends on its platform's LLVM helper and runtime pack packages at the same version; the compiler resolves them beside itself. The standalone archives bundle the helper and runtime pack too. npm postinstall installs the matching native executable; Node is needed for npm installation but not native compilation or execution. The user's toolchain performs the final platform link. Sanitized builds also use it to instrument program LLVM and compile the C runtime. Platform packages publish ahead of `@scriptc/compiler` and `scriptc`. The GitHub release attaches standalone compiler archives, their SHA-256 checksums, and the surface manifest after npm publication succeeds.
Release builds of the native CLI optimize the compiler with a profile collected on the release commit. The `cli-profile` job runs `node scripts/refresh-cli-profile.mjs` on Linux x64, which builds an instrumented compiler, runs it on tsc-ts, a slice of the corpus, and the runtime benchmark workloads, and writes the merged sparse profile. Every platform's CLI build uses that profile (`SCRIPTC_NATIVE_PROFILE_USE`), and the GitHub release attaches it as `scriptc-<version>-cli.profdata.gz`. Code the profile does not describe, or describes from older compiler sources, is optimized as without a profile. When the profile job fails, the CLI builds use the committed profile `packages/compiler/native/cli.profdata.gz`, which local and CI builds of the native compiler also use. Refresh the committed profile when compiler code has changed substantially since the last refresh: run `node scripts/refresh-cli-profile.mjs --tsc-ts <tsc-ts checkout>` on a quiet Linux x64 host and commit the new profile on its own.
Release builds of the native CLI optimize the compiler with a profile collected on the release commit. The `cli-profile` job runs `node scripts/refresh-cli-profile.mjs` on Linux x64, which builds an instrumented compiler, runs it on tsc-ts, a slice of the corpus, and the runtime benchmark workloads, and writes the merged profile. The profile keeps the functions that training never ran, with zero counts, so the CLI builds optimize them as cold code and place them after the code that ran. Every platform's CLI build uses that profile (`SCRIPTC_NATIVE_PROFILE_USE`), and the GitHub release attaches it as `scriptc-<version>-cli.profdata.gz`. Code the profile does not describe, or describes from older compiler sources, is optimized as without a profile. When the profile job fails, the CLI builds use the committed profile `packages/compiler/native/cli.profdata.gz`, which local and CI builds of the native compiler also use. Refresh the committed profile when compiler code has changed substantially since the last refresh: run `node scripts/refresh-cli-profile.mjs --tsc-ts <tsc-ts checkout>` on a quiet Linux x64 host and commit the new profile on its own.
Builds of the native compiler also order its functions by the committed layout `packages/compiler/native/cli.layout.gz`: the functions that `--version` and rebuilds of unchanged programs run come first, then the ones other builds run, ordered by the share of the layout's training builds that run each, so a run maps fewer pages of the executable. Functions the layout does not list keep their usual order. Function names of closures and generated helpers change with nearby edits, so the `cli-profile` job also collects a layout on the release commit (`scripts/refresh-cli-layout.mjs`), and the CLI builds use it (`SCRIPTC_NATIVE_LAYOUT`). Instrumented builds use the same layout as optimized builds, because a function laid out in a different partition than in the instrumented build no longer matches its profile record. The job therefore collects the layout first and then builds the instrumented compiler for the profile with that layout. Refresh the committed layout the same way, with `node scripts/refresh-cli-layout.mjs`, before the profile, and commit each on its own.
Publishing uses npm trusted publishing (OIDC) — there is no npm token secret.
Each published package must have a GitHub Actions trusted publisher for
+1
View File
@@ -28,6 +28,7 @@ Application-shaped workloads for measuring the speed of compiled executables. Ea
| `numeric-kernels` | cpu | sieve, hashing, `number[][]` matmul, `Float64Array` |
| `optional-fields` | cpu | records and class instances with `number \| undefined` and other scalar-union fields |
| `published-graphs` | cpu | syntax trees with parent links and a symbol table, published with `@scriptc/threads` and then read |
| `record-untyped-writes` | cpu | typed records written through untyped views: a counter helper taking `unknown` bumps fields of a stats record, and `Object.assign` merges patches into a settings record, with typed reads in between |
| `record-boundaries` | cpu | records built from unchecked array reads passed to and returned from annotated functions |
| `records-sort` | cpu | comparator sorts, group-by, `reduce`/`filter` |
| `regex-logs` | cpu | regex `exec` with groups, `test`, global `replace` |
@@ -0,0 +1,6 @@
{
"entry": "workloads/record-untyped-writes.ts",
"args": [
"4"
]
}
@@ -0,0 +1,49 @@
// Typed records updated through untyped views: a stats record written by a
// generic counter helper that takes `unknown`, settings merged with
// Object.assign, and typed reads in between. Each write stores into the
// native record; typed code reads the same object.
type Stats = { hits: number; misses: number; bytes: number; name: string; ok: boolean };
type Settings = { host: string; port: number; debug: boolean; retries: number };
const scale = Number(process.argv[2] ?? "1");
function bump(target: unknown, key: string, by: number): void {
const record = target as Record<string, number>;
record[key] = record[key]! + by;
}
const stats: Stats[] = [];
for (let i = 0; i < 16; i++) stats.push({ hits: 0, misses: 0, bytes: 0, name: "s" + i, ok: true });
let seed = 12345;
let checksum = 0;
const rounds = Math.floor(1_000_000 * scale);
for (let i = 0; i < rounds; i++) {
seed = (Math.imul(seed, 1103515245) + 12345) >>> 0;
const s = stats[seed & 15]!;
if ((seed >>> 8) % 5 === 0) bump(s, "misses", 1);
else bump(s, "hits", 1);
bump(s, "bytes", (seed >>> 12) & 1023);
checksum += s.hits - s.misses;
}
const settings: Settings = { host: "localhost", port: 80, debug: false, retries: 0 };
const view: object = settings;
const patches: object[] = [
{ port: 8080 },
{ debug: true, retries: 3 },
{ host: "example.test" },
{ port: 443, debug: false },
];
for (let i = 0; i < Math.floor(200_000 * scale); i++) {
Object.assign(view, patches[i & 3]!);
checksum += settings.port + settings.retries;
}
let hits = 0;
let bytes = 0;
for (const s of stats) {
hits += s.hits;
bytes += s.bytes;
}
console.log("hits", hits, "bytes", bytes, "settings", JSON.stringify(settings), "checksum", checksum);
+32 -3
View File
@@ -139,8 +139,8 @@ With `--sparse`, the profile leaves out the functions the runs never reached. Th
<dt><code>--print &lt;native-link-info|diagnostics&gt;</code></dt>
<dd><code>native-link-info</code>: build an object (equivalent to <code>--emit=obj</code>) and print its machine-readable external link recipe as JSON instead of printing the artifact path. The document names the exact installed precompiled runtime pack and all link inputs, but does not invoke a linker. <code>diagnostics</code>: for <code>build</code> and <code>coverage</code>, print the versioned JSON diagnostics document described in <a href="/docs/coverage#machine-readable-diagnostics">Coverage Reports</a> instead of the human output.</dd>
<dt><code>--fail-on &lt;blockers|divergences|dynamic&gt;</code></dt>
<dd>Coverage only. Exit with status 1 when the entry has blockers that would fail a build, with <code>divergences</code>, also when it has Node divergence warnings, or with <code>dynamic</code>, also when it has the <code>SC6101</code> warning for untyped code that likely runs often. See <a href="/docs/coverage#gating-builds-and-ci">Gating builds and CI</a>.</dd>
<dt><code>--fail-on &lt;blockers|divergences|dynamic|dynamic-slots&gt;</code></dt>
<dd>Coverage only. Exit with status 1 when the entry has blockers that would fail a build, with <code>divergences</code>, also when it has Node divergence warnings, with <code>dynamic</code>, also when it has the <code>SC6101</code> warning for untyped code that likely runs often, or with <code>dynamic-slots</code>, also when it has <a href="/docs/coverage#dynamic-slots">dynamic slots</a> (<code>SC6104</code>). See <a href="/docs/coverage#gating-builds-and-ci">Gating builds and CI</a>.</dd>
<dt><code>--ffi &lt;file&gt;</code></dt>
<dd>Bind signature-only TypeScript declarations to native C ABI symbols and link the manifest's archive, object, and system-library inputs. See <a href="/docs/ffi">Native FFI</a>.</dd>
@@ -151,6 +151,9 @@ With `--sparse`, the profile leaves out the functions the runs never reached. Th
<dt><code>--strip</code></dt>
<dd>Remove symbol and debug payload when linking an executable, including cross-compiled targets. This flag preserves code optimization.</dd>
<dt><code>--static</code></dt>
<dd>On Linux, link the C library and the other system libraries into the executable. The executable starts without the dynamic loader, which shortens its startup, and doesn't depend on the run host's glibc version. glibc executables link as static PIE, are about 1 MB larger, and need glibc's static libraries on the build machine (<code>libc6-dev</code> on Debian and Ubuntu, <code>glibc-static</code> on Fedora); they link only for the build machine's own glibc target. A program whose executable includes host, user, or group name lookups, which <code>dns.lookup</code>, <code>net</code> and <code>http</code> connections, <code>fetch</code>, and <code>os.userInfo()</code> use, is refused with <code>SC3002</code>: a static glibc executable loads the NSS modules for those lookups from the run host, which glibc supports only for the glibc version it was linked with. Build such a program without <code>--static</code>, or for an <code>&lt;arch&gt;-linux-musl</code> target. musl executables are always linked statically, and accept the flag. Valid only for executable builds.</dd>
<dt><code>--optimization &lt;release|dev|speed&gt;</code></dt>
<dd>Select native optimization for executable, assembly, or object output. The default is <code>release</code> (<code>-O2</code>), which emits string retains and the retains and releases of dynamic values inline. <code>dev</code> uses <code>-O0</code> and emits source locations for TypeScript and JavaScript breakpoints and stack frames. LLVM builds include source variable names and native storage descriptions; see <a href="/docs/limitations#native-debugging">Native debugging</a> for the supported representations. macOS executable builds also produce an adjacent <code>.dSYM</code> bundle. <code>speed</code> is <code>-O2</code> plus optimizations that trade binary size and build time for run-time speed: it emits the remaining reference counting inline and, on x86-64, starts every runtime function on a cache-line boundary. Expect larger executables and longer cold builds than <code>release</code>; how much faster a program runs depends on how much of its time is spent in the runtime. Where a runtime pack has no speed runtime, <code>speed</code> links the release runtime and keeps inline reference counting. Selecting <code>speed</code> never changes what <code>release</code> builds produce, and the two never share cached artifacts. Library profiles accept <code>"optimization": "speed"</code> for inline reference counting. In executables large enough to be divided into partitions, <code>dev</code> compiles each partition on its own, and <code>release</code> and <code>speed</code> add a step that lets partitions import small functions from each other; see <a href="/docs/how-it-works#build-parallelism">Build parallelism</a>.</dd>
@@ -160,6 +163,9 @@ With `--sparse`, the profile leaves out the functions the runs never reached. Th
<dt><code>--detached-exit</code></dt>
<dd>On Linux, when the program calls <code>process.exit()</code>, hand the release of its memory to a helper process so the parent sees the exit sooner. The kernel otherwise frees the whole address space before the exit is reported, which takes tens of milliseconds per gigabyte. Opt in only where the trade-offs are acceptable: for up to about 100 ms after the exit, the helper keeps the executable mapped (overwriting the binary in place fails with "text file busy"), its memory stays charged to the cgroup, and the teardown CPU time no longer appears in the parent's resource usage. Valid only for executable builds; it has no effect on other platforms.</dd>
<dt><code>--large-pages</code></dt>
<dd>On Linux, when the program starts, move its code onto 2 MiB transparent huge pages, as Node's <code>--use-largepages=silent</code> does. A large program's code then takes a few instruction TLB entries instead of hundreds, and runs faster. The move applies only when the system grants huge pages to memory that asks for them (<code>/sys/kernel/mm/transparent_hugepage/enabled</code> is <code>always</code> or <code>madvise</code>), and only to the code between the first and the last 2 MiB boundary of the executable, so small programs keep their code as it is. Opt in only where the trade-offs are acceptable: the move takes about 3 ms per 10 MiB of code at every start, all of the moved code stays resident and is not shared with other processes running the same executable, and profilers that read symbols from the executable file report the moved code as anonymous memory. If a step of the move fails, the program runs with its code where it was. Valid only for executable builds; it has no effect on other platforms.</dd>
<dt><code>--target-cpu &lt;baseline|x86-64-v2|x86-64-v3|x86-64-v4|native&gt;</code></dt>
<dd>On x86-64 targets, compile the program's code for an x86-64 microarchitecture level. The default, <code>baseline</code>, runs on any x86-64 CPU. A higher level lets LLVM use more instructions, and the executable then runs only on CPUs that support that level. <code>native</code> uses the highest level the build machine supports. Valid for executable, assembly, and object output on x86-64 targets. See <a href="#target-cpu">Target CPU</a>.</dd>
@@ -170,7 +176,7 @@ With `--sparse`, the profile leaves out the functions the runs never reached. Th
<dd>On Linux, build an instrumented executable for profile-guided optimization. Each run writes a raw profile to the path pattern, where <code>%p</code> expands to the process ID; the <code>LLVM_PROFILE_FILE</code> environment variable overrides the pattern at run time. A relative pattern resolves from the current working directory. Instrumented programs run slower. Valid only for executable builds with <code>--optimization=release</code> or <code>speed</code>.</dd>
<dt><code>--profile-use &lt;file.profdata&gt;</code></dt>
<dd>Optimize the executable with a profile from <code>scriptc merge-profiles</code>: code is laid out, inlined, and split by how the profiled runs used it. Code the profile does not describe, or describes from an older version of the program, is optimized as without a profile, so a stale profile only loses part of its gain. A new or changed profile compiles every partition of the program again. Valid only for executable builds with <code>--optimization=release</code> or <code>speed</code>.</dd>
<dd>Optimize the executable with a profile from <code>scriptc merge-profiles</code>: code is laid out, inlined, and split by how the profiled runs used it. Code the profile does not describe, or describes from an older version of the program, is optimized as without a profile, so a stale profile only loses part of its gain. A new or changed profile compiles every partition of the program again. <code>scriptc build</code> prints how the profile matched the program's functions on stderr; see <a href="#profile-guided-optimization">Profile-guided optimization</a>. Valid only for executable builds with <code>--optimization=release</code> or <code>speed</code>.</dd>
<dt><code>--npm-static &lt;pkg[,pkg…]|auto&gt;</code></dt>
<dd>Name npm packages to compile even if they don't ship their own type declarations (repeatable). A trailing <code>...</code>, as in <code>fs-extra...</code>, also names every package that package depends on. <code>auto</code> uses the default automatic selection. Packages that can't compile produce diagnostics. See <a href="/docs/dependencies#explicit-package-selection">npm Dependencies</a>.</dd>
@@ -262,6 +268,25 @@ $ scriptc build app.ts --optimization=speed --profile-use app.profdata
Train on workloads like the ones the program will run: code the training never reached is optimized as without a profile. The profile can be committed and reused as the program changes. Functions whose control flow changed since the profile was collected lose their profile, and the rest keep theirs, so regenerate it when much of the program has changed. [Build parallelism](/docs/how-it-works#build-parallelism) describes what a profile adds to build time.
`scriptc build --profile-use` prints one line on stderr with how the profile matched the program's functions:
```
scriptc: profile app.profdata: 9170 functions annotated, 0 mismatched, 28636 without a record
```
- **annotated:** functions optimized with the profile's counts.
- **mismatched:** functions whose control flow changed since the profile was collected. They are optimized without counts.
- **without a record:** functions the profile does not describe: new functions, and functions the profiled runs never reached.
- When the build reuses cached partitions whose counts were not recorded, the line ends with the number of those partitions.
When more than 10% of the functions the profile describes (annotated and mismatched) are mismatched, the build also prints a warning:
```
scriptc: warning: 3739 of the 8947 functions app.profdata describes (41.8%) changed since it was collected and are optimized without their counts; collect the profile again
```
The build still succeeds. Collecting the profile again with the current program and scriptc version restores the counts.
## Target CPU
By default, x86-64 executables run on any x86-64 CPU, so the compiler uses only the baseline instruction set (SSE2). `--target-cpu` compiles the program's code for a higher x86-64 microarchitecture level. Each level includes the ones below it:
@@ -351,6 +376,10 @@ For source breakpoints in Xcode or LLDB, build with <code>--optimization=dev</co
<td><code>scriptc run</code> on <code>wasm32-wasi</code></td>
<td>Node.js 24 or later on <code>PATH</code></td>
</tr>
<tr>
<td><code>--static</code> on glibc Linux</td>
<td>glibc's static libraries (<code>libc6-dev</code> on Debian and Ubuntu, <code>glibc-static</code> on Fedora)</td>
</tr>
<tr>
<td><code>--sanitize</code></td>
<td>A C compiler and LLVM toolchain</td>
+34 -1
View File
@@ -83,6 +83,39 @@ Without a profile, a dynamic-tier function counts as likely to run often when a
The warning names at most five functions, most likely to run often first, and gives the total. A function expression is named by its own name, else by the variable, property or class field it is assigned to, else as `anonymous function`. Dynamic-tier code in npm packages is summarized on one line with each package's function count. A build that comes from the cache repeats the warning. `--no-dynamic-tier-warning` leaves it out, and `--fail-on=dynamic` fails a coverage run on it.
## Dynamic slots
A declared type that has no native representation does not fail the build. The field, parameter, return value, variable or expression that holds it becomes a checked dynamic slot: the parts of the type that compile keep their native layout, and the failing parts hold checked-dynamic values. Coverage lists the slots under the statement counts, and `scriptc build` prints one `SC6104` warning per slot.
```console
$ cat plugins.ts
type Hook = (() => void) | AbortSignal | Map<string, (x: number) => string>;
interface Plugin {
name: string;
hook: Hook;
}
const plugins: Plugin[] = [{ name: "log", hook: () => console.log("hook ran") }];
for (const p of plugins) {
const hook = p.hook;
if (typeof hook === "function") hook();
console.log(p.name);
}
$ scriptc coverage plugins.ts
statements analyzed 6
compile statically 6 (100%)
in dynamic tier 1 (operate on values typed only at runtime; --explain-dynamic lists them)
dynamic slots 1 (declared types with no static lowering, held as checked-dynamic values; SC6104)
module global 'plugins' (declared 'Plugin[]') (dynamic pieces) plugins.ts:8:7
fully static — all analyzed statements compile to native code.
```
Here `Plugin` keeps its native `name` field, and only `hook` is checked-dynamic. Reading a dynamic value into typed code checks it against the type that reads it, so data that contradicts the declaration still throws a `TypeError`. Slots with the most dynamic operations are listed first. `--fail-on=dynamic-slots` fails a coverage run when any slot exists, and `--tier=static-only` reports the original errors instead.
## Diagnostic codes
Coverage and `scriptc build` report the same diagnostic codes. Common ones include:
@@ -109,7 +142,7 @@ By default, `scriptc coverage` exits with status 0 whenever the program type-che
$ scriptc coverage src/main.ts --fail-on=blockers
```
`--fail-on=blockers` exits with status 1 when the program has blockers that would fail `scriptc build`. Blockers in unreached code and deferred operations don't count. `--fail-on=divergences` also fails on Node divergences, and `--fail-on=dynamic` also fails on the `SC6101` warning for [untyped code that likely runs often](#untyped-code-that-likely-runs-often). A program that doesn't type-check always exits with status 1.
`--fail-on=blockers` exits with status 1 when the program has blockers that would fail `scriptc build`. Blockers in unreached code and deferred operations don't count. `--fail-on=divergences` also fails on Node divergences, `--fail-on=dynamic` also fails on the `SC6101` warning for [untyped code that likely runs often](#untyped-code-that-likely-runs-often), and `--fail-on=dynamic-slots` also fails on [dynamic slots](#dynamic-slots). A program that doesn't type-check always exits with status 1.
Coverage caches its result. Running it again on an unchanged program, or after edits that only touch comments, reuses the previous verdict. Set `SCRIPTC_NO_CACHE=1` to turn the cache off.
+21 -4
View File
@@ -34,7 +34,7 @@ Runtime pack and linker -> executable
## Build parallelism
Most of a build runs on one CPU core. The LLVM helper uses more cores only when it compiles an executable in partitions.
Most of a build runs on one CPU core. The LLVM helper uses more cores when it compiles an executable in partitions, and when it generates code for an optimized executable of one partition.
### Frontend
@@ -62,7 +62,7 @@ The count depends only on the size of the LLVM text, so a program has the same p
These builds are not divided into partitions and compile on one thread: `--emit=asm`, `--emit=obj`, and `--print=native-link-info` output, library builds (`--lib`; `dev` library builds are compiled in pieces, one after another), and `--sanitize` builds, which compile the LLVM IR with the C compiler.
The helper compiles partitions on a pool with one thread per hardware thread of the host, so an executable with four partitions uses at most four cores in the backend. A program of one partition is optimized and compiled on one thread.
The helper compiles partitions on a pool with one thread per hardware thread of the host, so an executable with four partitions uses at most four cores in the backend.
How partitions are compiled depends on `--optimization`:
@@ -71,6 +71,23 @@ How partitions are compiled depends on `--optimization`:
Reading the LLVM text and dividing it into partitions, and the import step, run on one thread.
### Code generation of one partition
A `release` or `speed` executable of one partition is optimized as one module on one thread. When its LLVM text is larger than 128 KiB, the helper then divides the optimized module for code generation and generates machine code for the parts in parallel, on the same pool. Each part holds a run of consecutive functions with about the same amount of code, so the executable keeps the functions in the same order. The module is optimized before it is divided, so each function's machine code is the same as from one thread, except that a jump to a function that another part also uses takes the longer form. When one function holds most of the code, the first part holds the whole module and the others are empty.
<table>
<thead>
<tr><th>LLVM text</th><th>Code generation parts</th></tr>
</thead>
<tbody>
<tr><td>Up to 128 KiB</td><td>1</td></tr>
<tr><td>Over 128 KiB, up to 256 KiB</td><td>4</td></tr>
<tr><td>Over 256 KiB, up to 1 MiB</td><td>8</td></tr>
</tbody>
</table>
Like partitions, the count depends only on the size of the LLVM text. A `dev` executable of one partition is compiled on one thread.
### Rebuilds
With two or more partitions, the helper keeps each partition's summary and object in the build cache (see `SCRIPTC_CACHE_DIR` in the [CLI reference](/docs/cli#environment-variables)). A rebuild after an edit compiles again only the partitions whose functions changed and, in `release` and `speed` builds, the partitions that import a changed function; the others come from the cache. Dividing the program, the import step, and linking run on every build. A cold build, with nothing cached, compiles every partition.
@@ -134,8 +151,8 @@ Program objects define `main` and leave selected `scr_*` runtime functions undef
## Native runtime
- **Memory:** Values use reference counting. Acyclic values are freed when their last reference is released, and a cycle collector runs at defined collection points. Some cycles involving opaque native values can remain allocated. When the main thread's script ends, the process exits without freeing what the program still holds, and the operating system reclaims it; worker threads free their heaps when they exit. Builds with `--sanitize`, and processes run with `SCRIPTC_EXIT_FREE=1` (for external leak checkers), free everything at exit.
- **Concurrency:** An `async` function that suspends only at its own `await` expressions compiles to a stackless coroutine whose heap frame holds the values live across those awaits; other suspending `async` functions and async generators use stackful fibers. The native event loop schedules microtasks, timers, and I/O, using kqueue on macOS and epoll on Linux.
- **Memory:** Values use reference counting. Acyclic values are freed when their last reference is released, and a cycle collector runs at defined collection points. When the compiler proves that a group of objects is released together, such as a tree that a recursive function builds and its caller only reads, the group is allocated in an arena and released at once, without freeing each object or collecting its cycles. Some cycles involving opaque native values can remain allocated. When the main thread's script ends, the process exits without freeing what the program still holds, and the operating system reclaims it; worker threads free their heaps when they exit. Builds with `--sanitize`, and processes run with `SCRIPTC_EXIT_FREE=1` (for external leak checkers), free everything at exit.
- **Concurrency:** An `async` function or async generator that suspends only at its own `await` and `yield` expressions compiles to a stackless coroutine whose heap frame holds the values live across them; other suspending `async` functions and async generators use stackful fibers, as do debug builds, libraries, worker programs and Windows executables. The native event loop schedules microtasks, timers, and I/O, using kqueue on macOS and epoll on Linux.
- **Networking:** Supported `net`, `http`, `https`, `tls`, `dgram`, and `dns` APIs use native implementations. TLS uses vendored mbedTLS.
- **Numbers:** Values use JavaScript's double-precision representation. Number-to-string formatting uses shortest-roundtrip output and is tested against Node.js.
- **Regular expressions and Unicode:** The runtime uses QuickJS's regular-expression and Unicode libraries, linked into programs that use them.
+4
View File
@@ -48,10 +48,14 @@ title: "Limitations"
<include>../limitations/types-and-class-definitions/unions.mdx</include>
<include>../limitations/types-and-class-definitions/dynamic-slots.mdx</include>
<include>../limitations/types-and-class-definitions/null-and-optional-values.mdx</include>
<include>../limitations/types-and-class-definitions/static-field-updates.mdx</include>
<include>../limitations/types-and-class-definitions/array-subclasses.mdx</include>
<include>../limitations/types-and-class-definitions/local-class-expressions.mdx</include>
<include>../limitations/types-and-class-definitions/constructor-function-inheritance.mdx</include>
+1 -1
View File
@@ -49,7 +49,7 @@ $ file fib-linux
fib-linux: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, for GNU/Linux 2.0.0, with debug_info, not stripped
```
`x86_64-linux-gnu.2.36` works the same way. For Alpine and other musl-based systems, use `aarch64-linux-musl` or `x86_64-linux-musl`; these produce statically linked executables.
`x86_64-linux-gnu.2.36` works the same way. For Alpine and other musl-based systems, use `aarch64-linux-musl` or `x86_64-linux-musl`; these produce statically linked executables. On a glibc-based Linux machine, [`--static`](/docs/cli#options) links a static executable for the machine's own target.
Linux executables use the native Linux implementations of the event loop (epoll), servers, TLS (with the system's CA bundle), and `fs.watch`, and they are tested against Node.js on Linux, including Alpine.
@@ -11,6 +11,6 @@ Runtime traps cannot be caught.
User-thrown values and supported exceptions, including JSON parse, checked-cast, filesystem, regex, and `String.prototype.repeat` count errors, can be caught; hard traps such as out-of-range reads of statically typed ordinary arrays terminate the process.
Deep recursion in Linux and macOS executables throws Node's catchable `RangeError: Maximum call stack size exceeded`, on the main thread, in worker threads, and inside async functions and generators.
The depth at which it is thrown differs from Node: the main thread uses the process stack limit (usually 8 MiB), worker threads get an 8 MiB stack, and async functions and generators run on 512 KiB stacks, so native code usually recurses deeper than Node on threads and somewhat less deeply inside async bodies.
The depth at which it is thrown differs from Node: the main thread uses the process stack limit (usually 8 MiB), worker threads get an 8 MiB stack, async functions and generators that keep a fiber run on 512 KiB stacks, and stackless ones run on the stack that resumes them, so native code usually recurses deeper than Node on threads and somewhat less deeply inside fiber bodies.
Recursion inside runtime library operations, such as serializing deeply nested data, is not checked.
Windows executables, WASI builds and library builds do not check the stack, and exhausting it there terminates the process.
@@ -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.
@@ -0,0 +1,3 @@
- **Array subclasses:** A class that extends `Array` compiles: its instances are arrays, and `map`, `filter`, `slice` and the other methods that create arrays return instances of the subclass, as in Node.js.
A generic subclass (`class Bag<T> extends Array<T>`), a class that extends such a subclass, and a subclass that declares `Symbol.species` are unsupported.
A method whose result the subclass's constructor cannot construct throws a `TypeError`: the constructor's parameters must take the one length argument, and a `map` to another element type needs a constructor that adds no elements.
@@ -0,0 +1,5 @@
- **Dynamic slots:** A field, parameter, return value, variable or expression whose declared type has no native representation compiles as a checked dynamic slot instead of failing the build.
The parts of the type that compile keep their native layout, and only the failing parts hold checked-dynamic values.
Operations on those values run in the dynamic tier, and reading one into typed code checks it against the type that reads it, so a value that contradicts its declaration throws a `TypeError` when typed code first uses it rather than when it is stored.
Each slot is an `SC6104` warning; `scriptc coverage --fail-on=dynamic-slots` fails on them, and `--tier=static-only` turns them back into build errors.
Values that scriptc cannot produce at all, such as `WeakRef` instances, still fail where they are created.
+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"]},
+3 -1
View File
@@ -33,7 +33,9 @@ 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.
With `--split codegen`, several outputs divide only code generation. The helper optimizes the module whole, as it does for a single output, then divides its functions in module order into runs of about the same instruction count, one per output, and generates code for every output on its own thread. Linking the outputs in order lays the functions out as one output would. A local definition that another output uses becomes hidden so the objects can reference each other; the others stay local. Every output after the first is generated from a copy of the optimized module read back from bitcode with its use-list order, so each function's machine code matches a single output except for jumps to definitions that another output also uses. When one run would hold more than three quarters of the module's instructions, the first output holds the whole module and the other outputs are empty objects. Placement depends only on the optimized module and the output count. `--split codegen` requires an optimized build and no `--cache-dir`; scriptc passes it for optimized executables with more than 128 KiB of LLVM text that stay one partition.
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.
+291 -3
View File
@@ -6,8 +6,11 @@
#include "runtime-import.h"
#include "target.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/Analysis/RuntimeLibcallInfo.h"
@@ -48,6 +51,7 @@
#include "llvm/TargetParser/Triple.h"
#include "llvm/Transforms/Utils/SplitModule.h"
#include <algorithm>
#include <atomic>
#include <mutex>
#include <optional>
@@ -64,6 +68,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;
@@ -97,6 +104,8 @@ std::optional<EmitOptions> parseEmitOptions(int Argc, char **Argv) {
Options.ProfileGenerate = Value.str();
else if (Arg == "--profile-use")
Options.ProfileUse = Value.str();
else if (Arg == "--split" && Value == "codegen")
Options.SplitCodegen = true;
else
return std::nullopt;
}
@@ -173,6 +182,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
@@ -193,8 +218,8 @@ struct UnnameStructTypes : PassInfoMixin<UnnameStructTypes> {
// How the --profile-use profile matched the functions it annotated, summed
// over partitions: functions that received its counts, functions whose
// control flow changed since it was collected (hash mismatch), and functions
// it has no record of (new, or never run in training: the merged profile is
// sparse). A partition reused from the cache without its counts is counted
// it has no record of (new since it was collected, or never run in training
// when the profile was merged with --sparse). A partition reused from the cache without its counts is counted
// in Unknown.
struct ProfileMatch {
std::atomic<uint64_t> Annotated{0};
@@ -852,6 +877,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),
@@ -1023,6 +1049,251 @@ int emitPartitions(const EmitOptions &Options) {
return 0;
}
// An optimized module prepared for split code generation.
struct CodegenSplit {
// The module as bitcode, with the order of every use list.
SmallVector<char, 0> Bitcode;
// The output that holds each definition, by name.
StringMap<size_t> PartOf;
};
// Divides an optimized module's definitions among Count outputs for code
// generation. Functions are divided in module order into runs of about the
// same instruction count, so linking the outputs in order lays the functions
// out as one module would; a function that would take its run past an even
// share starts the next run. A local definition that another output uses
// becomes hidden so the outputs can reference each other; the others stay
// local. Variables and aliases go with output 0 unless a comdat, an aliasee
// or an ifunc resolver places them. When one run would hold more than three
// quarters of the instructions, dividing cannot shorten code generation: the
// result is empty and M is unchanged. Placement depends only on the module.
std::optional<CodegenSplit> splitForCodegen(Module &M, size_t Count) {
uint64_t Total = 0;
for (const Function &F : M)
if (!F.isDeclaration())
Total += F.getInstructionCount() + 1;
DenseMap<const GlobalValue *, size_t> Part;
DenseMap<const Comdat *, size_t> ComdatPart;
std::vector<uint64_t> Load(Count, 0);
uint64_t Share = (Total + Count - 1) / Count;
size_t Current = 0;
uint64_t InRun = 0;
for (Function &F : M) {
if (F.isDeclaration())
continue;
uint64_t Size = F.getInstructionCount() + 1;
if (InRun > 0 && InRun + Size > Share && Current + 1 < Count) {
++Current;
InRun = 0;
}
size_t Target = Current;
if (const Comdat *C = F.getComdat()) {
auto [It, Inserted] = ComdatPart.try_emplace(C, Target);
Target = It->second;
}
Part[&F] = Target;
Load[Target] += Size;
InRun += Size;
}
if (*std::max_element(Load.begin(), Load.end()) * 4 > Total * 3)
return std::nullopt;
auto PartOf = [&](const GlobalValue *GV) -> size_t {
if (const GlobalObject *Root = GV->getAliaseeObject()) {
if (const auto *IFunc = dyn_cast<GlobalIFunc>(Root))
Root = IFunc->getResolverFunction();
if (Root != nullptr && Root != GV)
GV = Root;
}
if (auto It = Part.find(GV); It != Part.end())
return It->second;
if (const auto *GO = dyn_cast<GlobalObject>(GV))
if (const Comdat *C = GO->getComdat())
if (auto It = ComdatPart.find(C); It != ComdatPart.end())
return It->second;
return 0;
};
// Whether a user of GV, through any constants, lies outside output Home.
auto UsedElsewhere = [&](const GlobalValue &GV, size_t Home) {
SmallVector<const User *, 8> Work(GV.users());
SmallPtrSet<const User *, 8> Seen;
while (!Work.empty()) {
const User *U = Work.pop_back_val();
if (!Seen.insert(U).second)
continue;
if (const auto *I = dyn_cast<Instruction>(U)) {
if (PartOf(I->getFunction()) != Home)
return true;
} else if (const auto *G = dyn_cast<GlobalValue>(U)) {
if (PartOf(G) != Home)
return true;
} else {
append_range(Work, U->users());
}
}
return false;
};
for (GlobalValue &GV : M.global_values()) {
if (GV.hasLocalLinkage() && UsedElsewhere(GV, PartOf(&GV))) {
GV.setLinkage(GlobalValue::ExternalLinkage);
GV.setVisibility(GlobalValue::HiddenVisibility);
}
if (!GV.hasName())
GV.setName("scr.split");
}
CodegenSplit Split;
for (const GlobalValue &GV : M.global_values())
if (!GV.isDeclaration())
Split.PartOf[GV.getName()] = PartOf(&GV);
raw_svector_ostream Stream(Split.Bitcode);
WriteBitcodeToFile(M, Stream, /*ShouldPreserveUseListOrder=*/true);
return Split;
}
// Reduces P, a copy of the split module, to the definitions of output I:
// the other definitions become declarations, and declarations that nothing
// uses are removed. A declaration nothing uses would still emit an untyped
// undefined symbol for its visibility, which some linkers reject against a
// thread-local definition. Removing definitions keeps the order of the
// remaining uses, so each function of the output reaches code generation
// as it would in the whole module.
void keepCodegenPart(Module &P, const StringMap<size_t> &PartOf, size_t I) {
auto Elsewhere = [&](const GlobalValue &GV) {
auto It = PartOf.find(GV.getName());
return It != PartOf.end() && It->second != I;
};
// Aliases and ifuncs cannot be declarations, so a declaration replaces
// them.
SmallVector<GlobalValue *, 4> Indirect;
for (GlobalAlias &GA : P.aliases())
if (Elsewhere(GA))
Indirect.push_back(&GA);
for (GlobalIFunc &GI : P.ifuncs())
if (Elsewhere(GI))
Indirect.push_back(&GI);
for (GlobalValue *GV : Indirect) {
GlobalValue *Declaration;
if (auto *Type = dyn_cast<FunctionType>(GV->getValueType()))
Declaration = Function::Create(Type, GlobalValue::ExternalLinkage,
GV->getAddressSpace(), "", &P);
else
Declaration = new GlobalVariable(
P, GV->getValueType(), /*isConstant=*/false,
GlobalValue::ExternalLinkage, nullptr, "", nullptr,
GV->getThreadLocalMode(), GV->getAddressSpace());
Declaration->setVisibility(GV->getVisibility());
Declaration->setDSOLocal(GV->isDSOLocal());
Declaration->takeName(GV);
GV->replaceAllUsesWith(Declaration);
GV->eraseFromParent();
}
for (Function &F : P)
if (!F.isDeclaration() && Elsewhere(F)) {
F.deleteBody();
F.setComdat(nullptr);
}
for (GlobalVariable &GV : P.globals())
if (!GV.isDeclaration() && Elsewhere(GV)) {
GV.setInitializer(nullptr);
GV.setComdat(nullptr);
GV.setLinkage(GlobalValue::ExternalLinkage);
}
if (I != 0)
P.setModuleInlineAsm("");
// Constant expressions that only the removed bodies used remain as users.
for (GlobalVariable &GV : make_early_inc_range(P.globals()))
if (GV.isDeclaration()) {
GV.removeDeadConstantUsers();
if (GV.use_empty())
GV.eraseFromParent();
}
for (Function &F : make_early_inc_range(P))
if (F.isDeclaration()) {
F.removeDeadConstantUsers();
if (F.use_empty())
F.eraseFromParent();
}
}
// Optimizes M whole, as a single output would, then divides its definitions
// among the outputs and generates code for each on its own thread. The first
// output is generated from M with Machine; the others from copies of M read
// back from bitcode, which keeps the order of every use list, so each
// function's code matches a single-output build except where it refers to a
// definition in another output. Placement depends only
// on the optimized module and the output count. When splitForCodegen keeps
// the module whole, the other outputs are empty objects.
std::optional<EmitFailure>
emitSplitCodegen(const EmitOptions &Options, Module &M, TargetMachine &Machine,
OptimizationLevel Level, ProfileMatch &Match) {
bool Annotating = !Options.ProfileUse.empty();
if (Annotating)
M.getContext().setDiagnosticHandler(
std::make_unique<ProfileMatchHandler>(Match));
{
Pipeline P(Machine, Level, profileOptions(Options));
if (Annotating)
P.countAnnotated(Match.Annotated);
P.PB.buildPerModuleDefaultPipeline(Level).run(M, P.MAM);
}
if (std::optional<EmitFailure> Failure =
verify(M, "post_optimization_verification_failed"))
return Failure;
size_t Count = Options.Outputs.size();
std::optional<CodegenSplit> Split = splitForCodegen(M, Count);
std::string Identifier = M.getModuleIdentifier();
std::string SourceFileName = M.getSourceFileName();
Triple TargetTriple = M.getTargetTriple();
DataLayout Layout = M.getDataLayout();
std::vector<std::vector<SmallString<256>>> Temporaries(Count);
SharedFailure Failures;
forEachPartition(Count, [&](size_t I) {
if (I == 0) {
if (Split)
keepCodegenPart(M, Split->PartOf, 0);
if (std::optional<EmitFailure> Failure =
generateCode(M, Machine, CodeGenFileType::ObjectFile,
Options.Outputs[0], Temporaries[0]))
Failures.set(std::move(*Failure));
return;
}
std::string LookupError;
std::unique_ptr<TargetMachine> PartMachine =
createTargetMachine(Options.Target, Options.OptLevel, LookupError);
if (!PartMachine)
return Failures.set({"target_machine_failed", LookupError});
LLVMContext Context;
std::unique_ptr<Module> Part;
if (Split) {
Expected<std::unique_ptr<Module>> Parsed = parseBitcodeFile(
MemoryBufferRef(
StringRef(Split->Bitcode.data(), Split->Bitcode.size()), "part"),
Context);
if (!Parsed)
return Failures.set(
{"partition_failed", toString(Parsed.takeError())});
Part = std::move(*Parsed);
keepCodegenPart(*Part, Split->PartOf, I);
} else {
Part = std::make_unique<Module>(Identifier, Context);
Part->setSourceFileName(SourceFileName);
Part->setTargetTriple(TargetTriple);
Part->setDataLayout(Layout);
}
if (std::optional<EmitFailure> Failure =
generateCode(*Part, *PartMachine, CodeGenFileType::ObjectFile,
Options.Outputs[I], Temporaries[I]))
return Failures.set(std::move(*Failure));
});
std::optional<EmitFailure> Failure = Failures.take();
for (size_t I = 0; I < Count && !Failure; ++I)
Failure = publish(Temporaries[I][0], Options.Outputs[I]);
if (Failure)
for (const std::vector<SmallString<256>> &Paths : Temporaries)
for (const SmallString<256> &Temporary : Paths)
sys::fs::remove(Temporary);
return Failure;
}
} // namespace
int emit(const EmitOptions &Options) {
@@ -1069,7 +1340,14 @@ int emit(const EmitOptions &Options) {
if (std::optional<EmitFailure> Failure = inspectProfile(Options))
return reportError(Failure->Code, Failure->Message,
Options.DiagnosticFormat);
if (Options.Outputs.size() > 1)
if (Options.SplitCodegen && (Options.Outputs.size() < 2 ||
Options.OptLevel == "0" ||
!Options.CacheDirectory.empty()))
return reportError("invalid_split",
"--split codegen requires several outputs, an "
"optimized build, and no cache directory",
Options.DiagnosticFormat);
if (Options.Outputs.size() > 1 && !Options.SplitCodegen)
return emitPartitions(Options);
SMDiagnostic ParseDiagnostic;
@@ -1127,6 +1405,16 @@ int emit(const EmitOptions &Options) {
CodeGenFileType Type = Options.FileType == "obj"
? CodeGenFileType::ObjectFile
: CodeGenFileType::AssemblyFile;
if (Options.SplitCodegen) {
ProfileMatch Match;
if (std::optional<EmitFailure> Failure =
emitSplitCodegen(Options, *Mod, *Machine, Level, Match))
return reportError(Failure->Code, Failure->Message,
Options.DiagnosticFormat);
if (!Options.ProfileUse.empty())
reportProfileMatch(Match);
return 0;
}
std::vector<SmallString<256>> Temporaries;
ProfileMatch Match;
std::optional<EmitFailure> Failure =
+3
View File
@@ -26,6 +26,9 @@ struct EmitOptions {
std::string ProfileGenerate;
// Indexed profile (.profdata) that guides optimization. Empty: none.
std::string ProfileUse;
// With several outputs: optimize the module whole and divide only its code
// generation among the outputs.
bool SplitCodegen = false;
};
std::optional<EmitOptions> parseEmitOptions(int Argc, char **Argv);
+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",
+5 -1
View File
@@ -18,9 +18,13 @@
"files": [
"dist"
],
"dependencies": {
"@scriptc/llvm-darwin-arm64": "workspace:*",
"@scriptc/runtime-darwin-arm64": "workspace:*"
},
"scripts": {
"build": "node -e \"\"",
"build:native": "node --max-old-space-size=8192 --import tsx ../../scripts/build-native-cli.mts dist",
"build:native": "node --max-old-space-size=8192 --import tsx ../../scripts/build-native-cli.mts dist --platform-packages",
"prepack": "node ../../scripts/verify-native-cli.mjs ."
},
"publishConfig": {
+5 -1
View File
@@ -18,9 +18,13 @@
"files": [
"dist"
],
"dependencies": {
"@scriptc/llvm-darwin-x64": "workspace:*",
"@scriptc/runtime-darwin-x64": "workspace:*"
},
"scripts": {
"build": "node -e \"\"",
"build:native": "node --max-old-space-size=8192 --import tsx ../../scripts/build-native-cli.mts dist",
"build:native": "node --max-old-space-size=8192 --import tsx ../../scripts/build-native-cli.mts dist --platform-packages",
"prepack": "node ../../scripts/verify-native-cli.mjs ."
},
"publishConfig": {
+5 -1
View File
@@ -18,9 +18,13 @@
"files": [
"dist"
],
"dependencies": {
"@scriptc/llvm-linux-arm64-gnu": "workspace:*",
"@scriptc/runtime-linux-arm64-gnu": "workspace:*"
},
"scripts": {
"build": "node -e \"\"",
"build:native": "node --max-old-space-size=8192 --import tsx ../../scripts/build-native-cli.mts dist",
"build:native": "node --max-old-space-size=8192 --import tsx ../../scripts/build-native-cli.mts dist --platform-packages",
"prepack": "node ../../scripts/verify-native-cli.mjs ."
},
"publishConfig": {
+5 -1
View File
@@ -18,9 +18,13 @@
"files": [
"dist"
],
"dependencies": {
"@scriptc/llvm-linux-arm64-musl": "workspace:*",
"@scriptc/runtime-linux-arm64-musl": "workspace:*"
},
"scripts": {
"build": "node -e \"\"",
"build:native": "node --max-old-space-size=8192 --import tsx ../../scripts/build-native-cli.mts dist",
"build:native": "node --max-old-space-size=8192 --import tsx ../../scripts/build-native-cli.mts dist --platform-packages",
"prepack": "node ../../scripts/verify-native-cli.mjs ."
},
"publishConfig": {
+5 -1
View File
@@ -18,9 +18,13 @@
"files": [
"dist"
],
"dependencies": {
"@scriptc/llvm-linux-x64-gnu": "workspace:*",
"@scriptc/runtime-linux-x64-gnu": "workspace:*"
},
"scripts": {
"build": "node -e \"\"",
"build:native": "node --max-old-space-size=8192 --import tsx ../../scripts/build-native-cli.mts dist",
"build:native": "node --max-old-space-size=8192 --import tsx ../../scripts/build-native-cli.mts dist --platform-packages",
"prepack": "node ../../scripts/verify-native-cli.mjs ."
},
"publishConfig": {
+5 -1
View File
@@ -18,9 +18,13 @@
"files": [
"dist"
],
"dependencies": {
"@scriptc/llvm-linux-x64-musl": "workspace:*",
"@scriptc/runtime-linux-x64-musl": "workspace:*"
},
"scripts": {
"build": "node -e \"\"",
"build:native": "node --max-old-space-size=8192 --import tsx ../../scripts/build-native-cli.mts dist",
"build:native": "node --max-old-space-size=8192 --import tsx ../../scripts/build-native-cli.mts dist --platform-packages",
"prepack": "node ../../scripts/verify-native-cli.mjs ."
},
"publishConfig": {
+5 -1
View File
@@ -18,9 +18,13 @@
"files": [
"dist"
],
"dependencies": {
"@scriptc/llvm-win32-x64-msvc": "workspace:*",
"@scriptc/runtime-win32-x64-msvc": "workspace:*"
},
"scripts": {
"build": "node -e \"\"",
"build:native": "node --max-old-space-size=8192 --import tsx ../../scripts/build-native-cli.mts dist",
"build:native": "node --max-old-space-size=8192 --import tsx ../../scripts/build-native-cli.mts dist --platform-packages",
"prepack": "node ../../scripts/verify-native-cli.mjs ."
},
"publishConfig": {
+28
View File
@@ -93,6 +93,34 @@ export function installNativeCli(
const bin = join(directory, "bin");
mkdirSync(bin, { recursive: true });
const manifest = relocateToolchain(original, sourceDirectory, bin);
// A platform package depends on its LLVM helper and runtime pack packages
// instead of bundling them. Record where the package manager put them.
if (manifest.llvm_package === undefined || manifest.runtime_pack === undefined) {
const dependencies = Object.keys(identity.dependencies ?? {});
const installed = (prefix) => {
const name = dependencies.find((dependency) => dependency.startsWith(prefix));
if (name === undefined)
throw new Error(`${packageName} does not depend on a ${prefix}* package`);
let path, found;
try {
path = createRequire(platformManifest).resolve(`${name}/package.json`);
found = JSON.parse(readFileSync(path, "utf8")).version;
} catch {
throw new Error(`scriptc requires ${name}@${version}; reinstall scriptc`);
}
if (found !== version)
throw new Error(`scriptc requires ${name}@${version}, found ${found}; reinstall scriptc`);
return relative(bin, dirname(path));
};
manifest.llvm_package ??= installed("@scriptc/llvm-");
if (manifest.runtime_pack === undefined) {
manifest.runtime_pack = installed("@scriptc/runtime-");
manifest.runtime_packs = [
{ target: manifest.target, path: manifest.runtime_pack },
...(manifest.runtime_packs ?? []),
];
}
}
// npm normalizes modes for payloads outside package bin entries. These
// tools are launched directly by the compiler after installation.
if (process.platform !== "win32") {
+15 -1
View File
@@ -74,7 +74,11 @@ async function tryFastPath(): Promise<number | null> {
values["target-cpu"] !== undefined ||
(values["external-types"] ?? []).length > 0 ||
// The full CLI validates the Node island policy flags.
hasNodeIslandFlags(values)
hasNodeIslandFlags(values) ||
// The full compiler writes the representation report and keys the heap
// census build (backend/llvm/repr-report.ts, heap-census.ts).
(process.env["SCRIPTC_REPR_REPORT"] ?? "") !== "" ||
(process.env["SCRIPTC_HEAP_CENSUS"] ?? "") !== ""
)
return null;
const backend = values.backend;
@@ -140,10 +144,17 @@ async function tryFastPath(): Promise<number | null> {
backend: "llvm",
...(optimization === "dev" || optimization === "speed" ? { optimization } : {}),
...(values.strip ? { strip: true as const } : {}),
...(values.static ? { staticLink: true as const } : {}),
...(values["detached-exit"] ? { detachedExit: true as const } : {}),
...(values["large-pages"] ? { largePages: true as const } : {}),
...(values["cpu-dispatch"] === true ? { cpuDispatch: true as const } : {}),
...(checker === undefined ? {} : { checker }),
npmStatic,
// Mirrors the full compiler's key: shape inference's overlay is opt-in
// (SCRIPTC_SHAPE_OVERLAY=1) and --no-shape-overlay turns it off.
...(process.env["SCRIPTC_SHAPE_OVERLAY"] === "1" && values["shape-overlay"] !== false
? { shapeOverlay: true as const }
: {}),
ffiProfile: ffiPath === null ? null : { path: ffiPath, bytes: ffiBytes! },
target: `${process.env["SCRIPTC_TARGET"] ?? "native"}:${buildPlatform}:${process.arch}:${
helperObjectRoute ? "runtime-pack" : "driver-tu"
@@ -219,7 +230,9 @@ async function coverageFastPath(
(failOn !== undefined && failOn !== "blockers" && failOn !== "divergences") ||
values.emit !== undefined ||
values.strip ||
values.static ||
values["detached-exit"] ||
values["large-pages"] ||
values["target-cpu"] !== undefined ||
values["cpu-dispatch"] !== undefined ||
values.lib ||
@@ -231,6 +244,7 @@ async function coverageFastPath(
values["provenance-sources"] ||
values["emit-ir"] ||
(values["npm-static"] ?? []).length > 0 ||
values["shape-overlay"] === false ||
(values["external-types"] ?? []).length > 0 ||
// The replay covers the default Node island policy only.
hasNodeIslandFlags(values)
@@ -198,3 +198,57 @@ test.skipIf(process.platform === "win32")(
},
120_000,
);
test.skipIf(process.platform !== "linux")(
"a warm executable cache still builds the heap census and writes the representation report",
async () => {
const dir = await mkdtemp(join(tmpdir(), "scriptc-cli-diagnostic-cache-"));
const cacheRoot = join(dir, "cache");
const entry = join(dir, "main.ts");
const outPath = join(dir, "program");
try {
await mkdir(cacheRoot, { mode: 0o700 });
// `new` and construction through a class value both count.
await writeFile(
entry,
[
"class Item { constructor(readonly id: number) {} }",
"const kind: typeof Item = Item;",
"const items = [new Item(1), new kind(2), new kind(3)];",
"console.log(items.length);",
"",
].join("\n"),
);
const build = async (env: NodeJS.ProcessEnv = {}): Promise<string> =>
(
await execFileAsync(
process.execPath,
["--import", tsxLoader, cliEntry, "build", entry, "-o", outPath],
{
env: { ...process.env, SCRIPTC_CACHE_DIR: cacheRoot, SCRIPTC_TIMING: "1", ...env },
maxBuffer: 4 * 1024 * 1024,
},
)
).stderr;
expect(await build()).toContain("scriptc lowering");
expect(await build()).not.toContain("scriptc lowering");
// The census build is its own cache entry.
const census = join(dir, "census.tsv");
await build({ SCRIPTC_HEAP_CENSUS: "1" });
const { stdout } = await execFileAsync(outPath, [], {
env: { ...process.env, SCRIPTC_HEAP_CENSUS: census },
});
expect(stdout).toBe("3\n");
expect(await readFile(census, "utf8")).toMatch(/^new\tItem\t\t3\t[1-9]\d*$/m);
// A report is written even when the executable is cached.
const report = join(dir, "repr.tsv");
await build({ SCRIPTC_REPR_REPORT: report });
expect(await readFile(report, "utf8")).toMatch(/^field\tItem\tid\t/m);
} finally {
await rm(dir, { recursive: true, force: true });
}
},
600_000,
);
+66
View File
@@ -7,6 +7,7 @@ import {
renameSync,
rmSync,
statSync,
symlinkSync,
writeFileSync,
} from "node:fs";
import { tmpdir } from "node:os";
@@ -104,6 +105,71 @@ test("installs a direct executable and relocatable references to platform assets
expect(JSON.stringify(manifest)).not.toContain(f.directory);
});
test.each(["hoisted", "pnpm"])(
"resolves the LLVM helper and runtime pack a platform package depends on (%s)",
(layout) => {
const f = fixture();
const helper = "@scriptc/llvm-darwin-arm64";
const runtime = "@scriptc/runtime-darwin-arm64";
const toolchain: Record<string, unknown> = { ...f.toolchain };
delete toolchain.llvm_package;
delete toolchain.runtime_pack;
delete toolchain.runtime_packs;
writeFileSync(f.binary + ".json", JSON.stringify(toolchain));
// pnpm links a package's dependencies beside it in its virtual store,
// where the installation's node_modules does not see them.
const platform =
layout === "pnpm"
? join(f.directory, "node_modules/.pnpm/cli@1.2.3/node_modules", f.packageName)
: f.platform;
if (layout === "pnpm") {
mkdirSync(dirname(platform), { recursive: true });
renameSync(f.platform, platform);
symlinkSync(platform, f.platform, "junction");
}
const modules =
layout === "pnpm" ? dirname(dirname(platform)) : join(f.directory, "node_modules");
writeFileSync(
join(platform, "package.json"),
JSON.stringify({
name: f.packageName,
version: "1.2.3",
dependencies: { [helper]: "1.2.3", [runtime]: "1.2.3" },
}),
);
for (const name of [helper, runtime]) {
mkdirSync(join(modules, name), { recursive: true });
writeFileSync(
join(modules, name, "package.json"),
JSON.stringify({ name, version: "1.2.3" }),
);
}
mkdirSync(join(modules, helper, "bin"));
writeFileSync(join(modules, helper, "bin/scriptc-llvm-codegen"), "helper", { mode: 0o644 });
const command = installNativeCli(f.directory, f.packageName);
const manifest = JSON.parse(readFileSync(command + ".json", "utf8"));
const bin = dirname(command);
expect(resolve(bin, manifest.llvm_package)).toBe(join(modules, helper));
expect(resolve(bin, manifest.runtime_pack)).toBe(join(modules, runtime));
expect(manifest.runtime_packs).toEqual([
{ target: "macos-arm64", path: manifest.runtime_pack },
]);
if (process.platform !== "win32")
expect(statSync(join(modules, helper, "bin/scriptc-llvm-codegen")).mode & 0o111).toBe(0o111);
writeFileSync(
join(modules, runtime, "package.json"),
JSON.stringify({ name: runtime, version: "1.2.4" }),
);
expect(() => installNativeCli(f.directory, f.packageName)).toThrow(
`scriptc requires ${runtime}@1.2.3, found 1.2.4`,
);
rmSync(join(modules, runtime), { recursive: true });
expect(() => installNativeCli(f.directory, f.packageName)).toThrow(
`scriptc requires ${runtime}@1.2.3; reinstall scriptc`,
);
},
);
test("missing and mismatched packages fail without replacing an installed command", () => {
const f = fixture();
const command = installNativeCli(f.directory, f.packageName);
+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");
});
});
+4
View File
@@ -46,8 +46,12 @@ describe("output option compatibility", () => {
[{ emit: "llvm", windowsSubsystem: "gui" }, /windows-subsystem/],
[{ emit: "obj", strip: true }, /strip/],
[{ emit: "llvm", strip: true }, /strip/],
[{ emit: "obj", staticLink: true }, /--static/],
[{ emit: "llvm", staticLink: true }, /--static/],
[{ emit: "obj", detachedExit: true }, /detached-exit/],
[{ emit: "llvm", detachedExit: true }, /detached-exit/],
[{ emit: "obj", largePages: true }, /large-pages/],
[{ emit: "llvm", largePages: true }, /large-pages/],
[{ emit: "asm", cpuDispatch: true }, /cpu-dispatch/],
[{ emit: "obj", profileGenerate: "/tmp/%p.profraw" }, /profile-generate/],
[{ emit: "llvm", profileUse: "/tmp/a.profdata" }, /profile-use/],
+16 -3
View File
@@ -63,8 +63,16 @@ test.skipIf(process.platform !== "linux")(
expect(merge.stdout).toBe(`${merged}\n`);
const optimized = join(dir, "optimized");
await scriptc(["build", entry, "--profile-use", merged, "-o", optimized], env);
const fresh = await scriptc(["build", entry, "--profile-use", merged, "-o", optimized], env);
expect((await execFileAsync(optimized, ["1000"])).stdout).toBe("total 167499\n");
// The build reports how the profile matched the program's functions.
expect(fresh.stderr).toMatch(
/^scriptc: profile .*: [1-9]\d* functions annotated, 0 mismatched, \d+ without a record\n$/,
);
expect(fresh.stderr).toContain(`scriptc: profile ${merged}: `);
// A build from the cache reports the same match.
const again = await scriptc(["build", entry, "--profile-use", merged, "-o", optimized], env);
expect(again.stderr).toBe(fresh.stderr);
// A sparse profile leaves out the runtime functions the runs never
// reached.
@@ -77,10 +85,15 @@ test.skipIf(process.platform !== "linux")(
await scriptc(["build", entry, "--profile-use", sparse, "-o", optimized], env);
expect((await execFileAsync(optimized, ["1000"])).stdout).toBe("total 167499\n");
// A profile from an older version of the program still builds it.
// A profile from an older version of the program still builds it. The
// edited function no longer matches its counts, and the build warns.
await writeFile(entry, PROGRAM + 'console.log("edited");\n');
await scriptc(["build", entry, "--profile-use", merged, "-o", optimized], env);
const stale = await scriptc(["build", entry, "--profile-use", merged, "-o", optimized], env);
expect((await execFileAsync(optimized, ["1000"])).stdout).toBe("total 167499\nedited\n");
expect(stale.stderr).toMatch(/ [1-9]\d* mismatched, /);
expect(stale.stderr).toMatch(
/\nscriptc: warning: [1-9]\d* of the \d+ functions .* describes \([\d.]+%\) changed since it was collected and are optimized without their counts; collect the profile again\n$/,
);
} finally {
await rm(dir, { recursive: true, force: true });
}
+138
View File
@@ -0,0 +1,138 @@
import { execFile, execFileSync } from "node:child_process";
import { createRequire } from "node:module";
import { existsSync } from "node:fs";
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { dirname, isAbsolute, join } from "node:path";
import { promisify } from "node:util";
import { expect, test } from "vitest";
const execFileAsync = promisify(execFile);
const require = createRequire(import.meta.url);
const repoRoot = join(import.meta.dirname, "../../..");
const cliEntry = join(repoRoot, "packages/cli/src/main.ts");
const bootstrapEntry = join(repoRoot, "packages/cli/dist/bootstrap.js");
const tsxLoader = join(dirname(require.resolve("tsx/package.json")), "dist/loader.mjs");
const glibcHost =
process.platform === "linux" &&
typeof (process.report?.getReport() as { header?: { glibcVersionRuntime?: unknown } })?.header
?.glibcVersionRuntime === "string";
/** Whether the host's clang finds glibc's static C library. */
function hostHasStaticLibc(): boolean {
try {
const path = execFileSync("clang", ["-print-file-name=libc.a"], { encoding: "utf8" }).trim();
return isAbsolute(path) && existsSync(path);
} catch {
return false;
}
}
/** The ELF type and whether the executable names a program interpreter. */
async function elfLinkage(path: string): Promise<{ type: number; interpreter: boolean }> {
const bytes = await readFile(path);
const type = bytes.readUInt16LE(16);
const phoff = Number(bytes.readBigUInt64LE(32));
const phentsize = bytes.readUInt16LE(54);
const phnum = bytes.readUInt16LE(56);
let interpreter = false;
for (let i = 0; i < phnum; i++) {
if (bytes.readUInt32LE(phoff + i * phentsize) === 3 /* PT_INTERP */) interpreter = true;
}
return { type, interpreter };
}
async function withProject<T>(run: (dir: string, env: NodeJS.ProcessEnv) => Promise<T>) {
const dir = await mkdtemp(
join(process.platform === "win32" ? tmpdir() : "/tmp", "scriptc-static-"),
);
try {
return await run(dir, { ...process.env, SCRIPTC_CACHE_DIR: join(dir, "cache") });
} finally {
await rm(dir, { recursive: true, force: true });
}
}
function cli(args: string[], env: NodeJS.ProcessEnv, bootstrap = false) {
return execFileAsync(
process.execPath,
[...(bootstrap ? [bootstrapEntry] : ["--import", tsxLoader, cliEntry]), ...args],
{ env, maxBuffer: 4 * 1024 * 1024 },
);
}
test.skipIf(!glibcHost || !hostHasStaticLibc())(
"--static links a glibc executable as static PIE without mixing cache variants",
async () => {
await withProject(async (dir, env) => {
const entry = join(dir, "main.ts");
const outPath = join(dir, "program");
// The NSS module file name in a string of the program's own is no lookup.
await writeFile(
entry,
'console.log("static-ok", [1, 2, 3].map((n) => n * 2).join(","), "/lib/libnss_files.so.2");\n',
);
const build = async (staticLink: boolean, bootstrap = false) => {
await cli(
["build", entry, "-o", outPath, ...(staticLink ? ["--static"] : [])],
env,
bootstrap,
);
expect((await execFileAsync(outPath)).stdout).toBe(
"static-ok 2,4,6 /lib/libnss_files.so.2\n",
);
return elfLinkage(outPath);
};
// ET_DYN with a program interpreter: the default dynamic PIE link.
expect(await build(false)).toEqual({ type: 3, interpreter: true });
// ET_DYN without one: static PIE.
expect(await build(true)).toEqual({ type: 3, interpreter: false });
expect(await build(true, true)).toEqual({ type: 3, interpreter: false });
expect(await build(false, true)).toEqual({ type: 3, interpreter: true });
});
},
180_000,
);
test.skipIf(!glibcHost || !hostHasStaticLibc())(
"--static refuses a glibc executable that includes user name lookups",
async () => {
await withProject(async (dir, env) => {
const entry = join(dir, "main.ts");
const outPath = join(dir, "program");
await writeFile(
entry,
'import { userInfo } from "node:os";\nconsole.log(userInfo().username);\n',
);
const failure = await cli(["build", entry, "-o", outPath, "--static"], env).then(
() => null,
(error: { code?: number; stderr?: string }) => error,
);
expect(failure?.code).toBe(1);
expect(failure?.stderr).toContain("error SC3002: --static cannot link this program");
expect(existsSync(outPath)).toBe(false);
// The same program links without --static.
await cli(["build", entry, "-o", outPath], env);
expect((await execFileAsync(outPath)).stdout).not.toBe("");
});
},
180_000,
);
test.skipIf(process.platform === "linux")(
"--static is refused for executables that do not target Linux",
async () => {
await withProject(async (dir, env) => {
const entry = join(dir, "main.ts");
await writeFile(entry, 'console.log("x");\n');
const failure = await cli(["build", entry, "-o", join(dir, "program"), "--static"], env).then(
() => null,
(error: { code?: number; stderr?: string }) => error,
);
expect(failure?.code).toBe(1);
expect(failure?.stderr).toContain("--static is only supported for Linux executables");
});
},
180_000,
);
+1 -1
View File
@@ -54,7 +54,7 @@ test.runIf(process.platform === "linux" && process.arch === "x64")(
expect(baseline).not.toContain("sanitize_address");
expect(await build(["--target-cpu=baseline"])).toBe(baseline);
const v3 = await build(["--target-cpu=x86-64-v3"]);
expect(v3).toMatch(/^attributes #0 = \{ "target-cpu"="x86-64-v3" \}$/m);
expect(v3).toMatch(/^attributes #0 = \{ uwtable "target-cpu"="x86-64-v3" \}$/m);
if (runsV3) expect((await execFileAsync(outPath)).stdout).toBe(expected);
// The bootstrap's cached route builds without a level.
expect(await build([], true)).toBe(baseline);
@@ -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
Binary file not shown.
Binary file not shown.
+26 -3
View File
@@ -56,7 +56,9 @@ void scr_collect_cycles_phase(void);
* frontend's syntax trees and program state refer to each other, so
* reference counting frees almost none of them when the frontend is
* disposed, and the collector's schedule alone may not run a full pass
* before the build ends. */
* before the build ends. The sweep then returns the freed pages to the
* system (scr_heap_trim), so code generation does not grow on top of the
* frontend's heap. */
void scriptc_native_collect_cycles(void) {
scr_collect_cycles_phase();
}
@@ -80,9 +82,30 @@ void scriptc_native_unlock(void) {}
#include <sys/file.h>
#include <sys/wait.h>
int32_t scriptc_native_fork(void) { return (int32_t)fork(); }
#if defined(__linux__)
/* LLVM's profile runtime, linked into instrumented builds of the compiler
* (SCRIPTC_NATIVE_PROFILE_GENERATE); other builds leave these null. A worker
* starts with zeroed counters and writes its profile before _exit, which
* skips the runtime's atexit writer, so the profile counts the worker's work
* once. */
extern void __llvm_profile_reset_counters(void) __attribute__((weak));
extern int __llvm_profile_write_file(void) __attribute__((weak));
#endif
void scriptc_native_exit(int32_t code) { _exit(code); }
int32_t scriptc_native_fork(void) {
pid_t pid = fork();
#if defined(__linux__)
if (pid == 0 && __llvm_profile_reset_counters != NULL) __llvm_profile_reset_counters();
#endif
return (int32_t)pid;
}
void scriptc_native_exit(int32_t code) {
#if defined(__linux__)
if (__llvm_profile_write_file != NULL) (void)__llvm_profile_write_file();
#endif
_exit(code);
}
/* The worker's exit status, 128 plus the signal for a worker a signal
* ended, or -1 when waitpid fails. */
@@ -1,6 +1,10 @@
#ifndef _WIN32
#define _POSIX_C_SOURCE 200809L
#endif
/* macOS declares mkdtemp only with the Darwin extensions visible. */
#if defined(__APPLE__) && !defined(_DARWIN_C_SOURCE)
#define _DARWIN_C_SOURCE 1
#endif
#include "ts7-process.h"
#include <assert.h>
#include <errno.h>
+4 -1
View File
@@ -40,10 +40,13 @@
},
"dependencies": {
"@scriptc/runtime": "workspace:*",
"tsc-ts": "0.0.9",
"tsc-ts": "0.0.10",
"typescript": "7.0.2",
"typescript5": "npm:typescript@5.9.3"
},
"devDependencies": {
"typescript-7.1": "npm:typescript@7.1.0-beta"
},
"optionalDependencies": {
"@scriptc/llvm-darwin-arm64": "workspace:*",
"@scriptc/llvm-darwin-x64": "workspace:*",
@@ -146,6 +146,10 @@ function analyzeCycles(
...(c.localCaptures !== undefined
? [{ name: "<class>", type: { kind: "classval" as const, className: c.name } }]
: []),
// An instance of a class that extends Array holds its elements.
...(c.arrayElem !== undefined
? [{ name: "<elements>", type: { kind: "array" as const, elem: c.arrayElem } }]
: []),
],
})),
...(mod.records ?? []).map((r) => ({
@@ -5,10 +5,11 @@ import { hasForeignFfiCallback } from "./ffi-callbacks.js";
import type { NativeLinkFeatures } from "./native-link-info.js";
import { youngNurseryRequested } from "./young-nursery.js";
export function executableLinkFeatures(mod: IrModule): NativeLinkFeatures {
export function executableLinkFeatures(mod: IrModule, largePages = false): NativeLinkFeatures {
const features = moduleRuntimeFeatures(mod);
return {
...(mod.workers ? { workers: true } : {}),
...(largePages ? { largePages: true } : {}),
...(youngNurseryRequested() && mod.lib === undefined ? { youngNursery: true } : {}),
regex: features.regex,
copying: features.copying,
@@ -166,6 +166,7 @@ export async function compilePackedLibrary(
options.programPublicSymbols,
stem,
target.platform,
{ inProcessElf: true },
);
} else {
program = await emit(
@@ -2,6 +2,7 @@
import type { NativeOptimization } from "./optimization.js";
import {
executableOptimizationLinkerArgs,
executableStaticLinkerArgs,
executableStripLinkerArgs,
windowsSubsystemLinkerArgs,
type NativeTargetSpec,
@@ -28,6 +29,8 @@ export function executableLinkInputs(options: {
runtimeSystemLibraries: readonly string[];
optimization: NativeOptimization;
strip?: boolean;
/** Link the C library and the other system libraries statically (`--static`). */
staticLink?: boolean;
windowsSubsystem?: WindowsSubsystem;
/** Instrumented for profile-guided optimization. */
profileGenerate?: boolean;
@@ -65,6 +68,7 @@ export function executableLinkInputs(options: {
),
...executableOptimizationLinkerArgs(options.target.platform, options.optimization),
...executableStripLinkerArgs(options.target.platform, options.strip ?? false),
...executableStaticLinkerArgs(options.target, options.staticLink ?? false),
...windowsSubsystemLinkerArgs(options.target.platform, options.windowsSubsystem),
...profileGenerateLinkerArgs(options.profileGenerate ?? false),
],
@@ -75,7 +79,7 @@ export function executableLinkInputs(options: {
* links as needed: the runtime defines the libm functions most programs
* reach (scr_glibc_math.c) and Math's transcendentals (scr_ieee754.c), so a
* program that calls no other libm function, such as pow, starts without
* loading libm.so. */
* loading libm.so when it links with lld (see inputsWithoutLld). */
export function systemLibraryLinkerArgs(
target: Pick<NativeTargetSpec, "platform">,
names: readonly string[],
@@ -28,6 +28,8 @@ export interface NativeLinkPlan {
programObjectDependencies: NativeArtifactDependency[];
runtimePack: RuntimePackSelection;
darwinDebugSymbols?: boolean;
/** Linked with `--static`: the linked executable is checked before use. */
staticLink?: boolean;
}
export async function createNativeLinkPlan(options: {
@@ -39,6 +41,7 @@ export async function createNativeLinkPlan(options: {
ffi: FfiProfile | null;
optimization: NativeOptimization;
strip?: boolean;
staticLink?: boolean;
windowsSubsystem?: WindowsSubsystem;
profileGenerate?: boolean;
programObjectDependencies?: readonly NativeArtifactDependency[];
@@ -70,6 +73,7 @@ export async function createNativeLinkPlan(options: {
runtimeSystemLibraries: runtimePack.systemLibraries,
optimization: options.optimization,
...(options.strip === undefined ? {} : { strip: options.strip }),
...(options.staticLink === true ? { staticLink: true } : {}),
...(options.windowsSubsystem === undefined
? {}
: { windowsSubsystem: options.windowsSubsystem }),
@@ -78,5 +82,6 @@ export async function createNativeLinkPlan(options: {
dependencyPaths: [...runtimePack.dependencyPaths, ...(options.ffi?.libraries ?? [])],
programObjectDependencies: [...(options.programObjectDependencies ?? [])],
runtimePack,
...(options.staticLink === true ? { staticLink: true } : {}),
};
}
+33 -17
View File
@@ -7,11 +7,13 @@
import { execFile } from "node:child_process";
import { createHash, randomUUID } from "node:crypto";
import { existsSync, realpathSync, statSync } from "node:fs";
import { mkdtemp, rename, rm, stat } from "node:fs/promises";
import { mkdtemp, readFile, rename, rm, stat } from "node:fs/promises";
import { delimiter } from "node:path";
import { basename, dirname, isAbsolute, join, resolve } from "node:path";
import { promisify } from "node:util";
import { CcCompileError, subprocessFailureDetail } from "./native/process.js";
import { NativeCodegenError } from "./native-codegen-core.js";
import { staticExecutableRefusal, staticLinkFailureNote, staticLinkMapArgs } from "./targets.js";
import { nativeArtifactDependenciesStillMatch } from "./native/artifact-stamps.js";
import { parseLinkTraceFiles } from "./native/linker-fingerprint.js";
import {
@@ -21,7 +23,7 @@ import {
import { type NativeArtifactDependency } from "./native/contracts.js";
import { RuntimePackError, stageRuntimePackArtifacts } from "./runtime-pack.js";
import type { NativeLinkPlan } from "./link-plan.js";
import { lldLinkerArgs } from "./lld.js";
import { inputsWithoutLld, lldLinkerArgs } from "./lld.js";
import { systemLibraryLinkerArgs } from "./link-plan-core.js";
import {
createDarwinDebugSymbols,
@@ -238,21 +240,28 @@ export async function linkNativeExecutable(
join(dirname(plan.outputPath), ".scriptc-runtime-pack-link-"),
);
const privateOut = join(privateOutRoot, basename(plan.outputPath));
const linkMap = join(privateOutRoot, "link.map");
const mapArgs = staticLinkMapArgs(plan.target, plan.staticLink === true, linkMap);
let stagedRoot: string | null = null;
try {
const staged = await stageRuntimePackArtifacts(plan.runtimePack);
stagedRoot = staged.root;
// Linux links with lld when the default driver finds it.
const lld = explicitLinker === undefined ? lldLinkerArgs(plan.target.platform, linker) : [];
const args = [
...(explicitLinker === undefined ? plan.target.defaultLinkerArgs : []),
...lld,
...plan.driverFlags,
...plan.inputs.map((input) => staged.replacements.get(input) ?? input),
...systemLibraryLinkerArgs(plan.target, plan.systemLibraries),
"-o",
privateOut,
];
const linkArgs = (withLld: boolean): string[] => {
const inputs = plan.inputs.map((input) => staged.replacements.get(input) ?? input);
return [
...(explicitLinker === undefined ? plan.target.defaultLinkerArgs : []),
...(withLld ? lld : []),
...plan.driverFlags,
...mapArgs,
...(withLld ? inputs : inputsWithoutLld(plan.target.platform, inputs)),
...systemLibraryLinkerArgs(plan.target, plan.systemLibraries),
"-o",
privateOut,
];
};
const args = linkArgs(lld.length !== 0);
// The pack snapshots bracket both verification passes and private staging;
// the program-object snapshot begins before helper emission. Snapshot the
// remaining cache-bearing inputs before the linker consumes them, then
@@ -285,13 +294,14 @@ export async function linkNativeExecutable(
// A failed link with lld runs again with the driver's default linker,
// whose error (if any) is the one reported.
if (lld.length === 0) throw error;
await execFileAsync(
linker,
args.filter((arg) => !lld.includes(arg)),
);
await execFileAsync(linker, linkArgs(false));
});
const output = await stat(privateOut);
if (!output.isFile() || output.size === 0) throw new Error("linker produced no executable");
if (mapArgs.length !== 0) {
const refusal = staticExecutableRefusal(plan.target, true, await readFile(linkMap, "utf8"));
if (refusal !== null) throw new NativeCodegenError("SC3002", refusal);
}
if (plan.darwinDebugSymbols) {
await createDarwinDebugSymbols(privateOut);
await installDarwinDebugSymbols(await readDarwinDebugSymbols(privateOut), plan.outputPath);
@@ -311,12 +321,18 @@ export async function linkNativeExecutable(
await options.onArtifactReady({ dependencies: preLinkDependencies }).catch(() => undefined);
}
} catch (error) {
if (error instanceof CcCompileError || error instanceof RuntimePackError) throw error;
if (
error instanceof CcCompileError ||
error instanceof RuntimePackError ||
error instanceof NativeCodegenError
)
throw error;
const detail = subprocessFailureDetail(error);
throw new CcCompileError(
linker,
detail,
`${linker} failed linking ${basename(plan.outputPath)} from the precompiled runtime pack.\n${detail}`,
`${linker} failed linking ${basename(plan.outputPath)} from the precompiled runtime pack.\n${detail}` +
staticLinkFailureNote(plan.target, plan.staticLink === true),
);
} finally {
await Promise.all([
+8 -1
View File
@@ -2,7 +2,7 @@ import { chmodSync, mkdirSync, mkdtempSync, rmSync, symlinkSync, writeFileSync }
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, expect, test } from "vitest";
import { driverFindsLld, lldLinkerArgs } from "./lld.js";
import { driverFindsLld, inputsWithoutLld, lldLinkerArgs } from "./lld.js";
const directories: string[] = [];
afterEach(() => {
@@ -59,3 +59,10 @@ test.skipIf(process.platform === "win32")(
expect(lldLinkerArgs("linux", clang, { ...env, SCRIPTC_LINKER: clang })).toEqual([]);
},
);
test("Linux links without lld leave out the glibc math unit", () => {
const inputs = ["/pack/scr_lib.o", "/pack/scr_glibc_math.o", "/tmp/program.o"];
expect(inputsWithoutLld("linux", inputs)).toEqual(["/pack/scr_lib.o", "/tmp/program.o"]);
expect(inputsWithoutLld("darwin", inputs)).toEqual(inputs);
expect(inputsWithoutLld("windows", inputs)).toEqual(inputs);
});

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