fix: #811 — net.isIP/isIPv4/isIPv6 + auto-select-family defaults#818
Merged
Conversation
`net.isIP`, `net.isIPv4`, `net.isIPv6`, `net.getDefaultAutoSelectFamily`,
`net.setDefaultAutoSelectFamily`, `net.getDefaultAutoSelectFamilyAttemptTimeout`,
and `net.setDefaultAutoSelectFamilyAttemptTimeout` previously returned
`undefined` because they had no NATIVE_MODULE_TABLE entries and no
perry-ext-net runtime implementations.
Adds:
- perry-ext-net runtime fns:
- `js_net_is_ip` — returns 0/4/6 via `std::net::Ipv4Addr` /
`std::net::Ipv6Addr` parse. `is_ipv6_str` rejects bracketed and
zone-id (`%`) forms to match Node.
- `js_net_is_ipv4` / `js_net_is_ipv6` — boolean.
- `js_net_get_default_auto_select_family` /
`js_net_set_default_auto_select_family` — read/write
`AUTO_SELECT_FAMILY` (AtomicBool, default `true`).
- `js_net_get_default_auto_select_family_attempt_timeout` /
`js_net_set_default_auto_select_family_attempt_timeout` — read/write
`AUTO_SELECT_FAMILY_ATTEMPT_TIMEOUT_MS` (AtomicI32, default 500ms
to match Node v20+).
- NATIVE_MODULE_TABLE entries dispatch `net.<method>(args)` to the new
runtime fns via the standard NA_STR/NA_F64 + NR_F64 protocol (return
values are already NaN-boxed JSValues).
- perry-api-manifest entries flip the #463 strict-API gate from "not
implemented" to "supported".
6-line repro from the issue now matches Node byte-for-byte; full
auto-select-family round-trip works.
cargo fmt --all and ./scripts/regen_api_docs.sh — fixes the lint and api-docs-drift checks on PR #818.
3 tasks
proggeramlug
added a commit
that referenced
this pull request
May 18, 2026
… fallback
The V8-fallback bundler (`__perry_js_bundle.js`) only included the first
JS module it landed on — pure-ESM packages whose entry re-exports
siblings (hono's `dist/index.js` → `./hono.js` → `./hono-base.js` → …
twenty-some submodules) ended up with one entry, dropping the rest.
Compiled binaries still worked when their `node_modules/` tree happened
to sit next to them (V8 reads files off disk), but the realistic hono
call path (`app.fetch(req)` running cross-thread) cascaded into rc=139
segfaults when those resolved paths weren't co-located, and shipping the
binary on its own surfaced `Cannot resolve module` for every missing
sibling.
Root cause was the JS branch of `collect_modules` bailing after one
insert with the comment "V8 will handle that at runtime". Native
TypeScript imports already recursed via `cached_resolve_import`; JS did
not. Added a lightweight regex-based scanner
(`collect_js_module_imports`) that pulls out static `import` /
`export ... from` / string-literal `import("...")` specifiers, resolves
each relative / absolute path via the existing `resolve_with_extensions`
helper, and re-enters `collect_modules` for each — re-running the
JS/native classification so cross-package landings into a
`compilePackages`-overridden TS file are still handled correctly. Bare
specifiers (`react`, `@foo/bar`) stay the entry walker's job since the
top-level TS path already pulls package deps; the relative walk only
needs to cover the inside-a-package case, which is always relative-path.
Validation: hono@4.12.19 repro under `/tmp/perry-hono/` — pre-fix
bundle had 1 module entry, post-fix has 24 (every `dist/**/*.js`
reachable from `index.js` through the full re-export graph). New
fixture `test-files/test_hono_bundle.ts` documents the smoke shape;
the bundle-walks-recursively assertion lives in the PR description
because the parity suite doesn't install npm packages.
Next blocker (out of scope): V8's `ModuleLoader::load` in
`crates/perry-jsruntime/src/modules.rs` still reads from disk via
`std::fs::read_to_string` and never consults `__COMPILETS_MODULES`, so
the binary isn't yet self-contained even with the correct bundle.
proggeramlug
added a commit
that referenced
this pull request
May 18, 2026
… fallback (#987) The V8-fallback bundler (`__perry_js_bundle.js`) only included the first JS module it landed on — pure-ESM packages whose entry re-exports siblings (hono's `dist/index.js` → `./hono.js` → `./hono-base.js` → … twenty-some submodules) ended up with one entry, dropping the rest. Compiled binaries still worked when their `node_modules/` tree happened to sit next to them (V8 reads files off disk), but the realistic hono call path (`app.fetch(req)` running cross-thread) cascaded into rc=139 segfaults when those resolved paths weren't co-located, and shipping the binary on its own surfaced `Cannot resolve module` for every missing sibling. Root cause was the JS branch of `collect_modules` bailing after one insert with the comment "V8 will handle that at runtime". Native TypeScript imports already recursed via `cached_resolve_import`; JS did not. Added a lightweight regex-based scanner (`collect_js_module_imports`) that pulls out static `import` / `export ... from` / string-literal `import("...")` specifiers, resolves each relative / absolute path via the existing `resolve_with_extensions` helper, and re-enters `collect_modules` for each — re-running the JS/native classification so cross-package landings into a `compilePackages`-overridden TS file are still handled correctly. Bare specifiers (`react`, `@foo/bar`) stay the entry walker's job since the top-level TS path already pulls package deps; the relative walk only needs to cover the inside-a-package case, which is always relative-path. Validation: hono@4.12.19 repro under `/tmp/perry-hono/` — pre-fix bundle had 1 module entry, post-fix has 24 (every `dist/**/*.js` reachable from `index.js` through the full re-export graph). New fixture `test-files/test_hono_bundle.ts` documents the smoke shape; the bundle-walks-recursively assertion lives in the PR description because the parity suite doesn't install npm packages. Next blocker (out of scope): V8's `ModuleLoader::load` in `crates/perry-jsruntime/src/modules.rs` still reads from disk via `std::fs::read_to_string` and never consults `__COMPILETS_MODULES`, so the binary isn't yet self-contained even with the correct bundle.
5 tasks
proggeramlug
added a commit
that referenced
this pull request
May 18, 2026
v0.5.993 closed half of #818: the bundle now contains every transitive ESM sibling. The other half — `NodeModuleLoader::load` still reading sources via `std::fs::read_to_string` and ignoring the bundle — meant binaries still required `node_modules/` to live next to them at runtime. This commit makes V8-fallback binaries truly self-contained. Three pieces: 1. `perry-jsruntime/src/modules.rs` grows two `RwLock<HashMap>`s — `EMBEDDED_MODULES` (build-time canonical path -> source) and `EMBEDDED_ALIASES` (bare specifier -> path) — plus `#[no_mangle]` FFIs `js_register_embedded_module` / `js_register_embedded_alias` and Rust-facing wrappers. 2. `NodeModuleLoader::resolve_module_path` consults the alias map for bare specs and the path map (with extension/index fallback) when on-disk probes fail. `NodeModuleLoader::load` checks the map before `std::fs::read_to_string`. Keys are build-time canonical paths used as opaque identifiers — `canonicalize()` in `resolve()` already gracefully falls back when the path doesn't exist on the runtime filesystem, and the `file://` URL handed to V8 works either way. 3. New `targets::generate_embedded_js_object` writes a generated `.c` file with one `static const char[]` per JS module source + length pair (and matching pairs for every TS import edge whose `resolved_path` is in `ctx.js_modules`), wrapped in a `__attribute__((constructor(101)))` that calls the registration FFIs at process start. `cc -c` produces an `.o`; compile.rs appends it to `obj_paths` whenever `needs_js_runtime && !js_modules.is_empty()`. Octal escapes for every non-printable byte keep the generated C ASCII-clean regardless of the JS file's encoding; `?` is escaped to defang trigraph hazards. Validation: `import { Hono } from 'hono'` compiles, the resulting binary moved to an isolated directory with no `node_modules/` and no source still prints `object function`. The on-disk `__perry_js_bundle.js` is retained as a build-time debugging artifact but is no longer needed at runtime. New fixture: `test-files/test_v8_self_contained.ts`.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
net.isIP,net.isIPv4,net.isIPv6, and the four*DefaultAutoSelectFamily*accessors in perry-ext-net.NATIVE_MODULE_TABLE(NA_STR/NA_F64 → NR_F64, since the runtime fns return already-NaN-boxed JSValue bits) and adds matchingperry-api-manifestentries so the Compile-time error for unimplemented Node / Web APIs #463 strict-API gate accepts them.is_ipv6_strrejects bracketed and zone-id (%) forms to match Node'snet.isIPv6.autoSelectFamilyAttemptTimeoutis 500ms — Node v20+ default; byte-for-byte parity required this.Test plan
isIP 127.0.0.1: 4,isIP ::1: 6,isIPv4: true,isIPv6: true,getDefaultAutoSelectFamily: true).setDefault*(getDefault*())runs without error, attempt-timeout default = 500.cargo build --release -p perry-ext-net -p perrysucceeds.Notes
net.BlockList,net.SocketAddress,net.Serverclass — those are the larger socket-layer gap fromtest_parity_net.ts. Only the pure string/global-flag helpers from the issue are implemented here.Closes #811.