From d93388dc4194f4fef11aadbc2f43daf82572596f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Fri, 18 Sep 2026 11:08:24 +0000 Subject: [PATCH 1/4] chore: remove Tier A native bindings (fetch alias, tursodb, iroh) Removes the bare-name node-fetch alias binding and the vestigial in-tree accounting for the tursodb/iroh native bindings, whose actual implementations already moved to @perryts/tursodb and @perryts/iroh in v0.5.557. See changelog fragment for details. --- crates/perry-api-manifest/src/entries.rs | 3 -- .../perry-api-manifest/src/entries/part_1.rs | 29 ------------------- .../tests/unimplemented_api_check.rs | 12 -------- .../compile/optimized_libs/freshness.rs | 1 - crates/perry/src/commands/stdlib_features.rs | 10 ------- crates/perry/well_known_bindings.toml | 8 ----- 6 files changed, 63 deletions(-) diff --git a/crates/perry-api-manifest/src/entries.rs b/crates/perry-api-manifest/src/entries.rs index 98d0061f8a..182a94665e 100644 --- a/crates/perry-api-manifest/src/entries.rs +++ b/crates/perry-api-manifest/src/entries.rs @@ -54,8 +54,6 @@ pub const NATIVE_MODULES: &[&str] = &[ "mongodb", // MongoDB driver "better-sqlite3", // synchronous SQLite (replaces the N-API addon) "sqlite", // node:sqlite builtin surface - "tursodb", // Turso/libSQL client (legacy in-tree; now @perryts/tursodb) - "iroh", // iroh p2p (legacy in-tree; now @perryts/iroh) // #6562: Bun FFI (C-ABI). The `bun:` prefix is part of the specifier // (unlike `node:`, which is stripped) — `import { dlopen } from "bun:ffi"`. "bun:ffi", @@ -158,7 +156,6 @@ pub const NATIVE_MODULES: &[&str] = &[ // ── More third-party npm packages ── "redis", // npm `redis` client (aliases ioredis) "rate-limiter-flexible", // rate limiting - "fetch", // bare-name alias for the node-fetch surface // `undici` (#466) — served by perry's native fetch stack via the // bundled perry-ext-undici wrapper (ProxyAgent / Agent / // setGlobalDispatcher / getGlobalDispatcher / fetch subset). diff --git a/crates/perry-api-manifest/src/entries/part_1.rs b/crates/perry-api-manifest/src/entries/part_1.rs index 8ccae06fe6..aa51556739 100644 --- a/crates/perry-api-manifest/src/entries/part_1.rs +++ b/crates/perry-api-manifest/src/entries/part_1.rs @@ -303,35 +303,6 @@ pub(crate) const API_MANIFEST_PART_1: &[ApiEntry] = &[ method("sqlite", "setAllowUnknownNamedParameters", true, None), method("sqlite", "sourceSQL", true, None), method("sqlite", "expandedSQL", true, None), - // tursodb (#424). open / exec / execBatch / close / - // lastInsertRowid / isAutocommit shipped in v0.5.543; queryAll / - // queryOne shipped in v0.5.553 (close the row-as-object gap by - // building shapes inside spawn_blocking and resolving with - // POINTER_TAG'd JsValues). - method("tursodb", "open", false, None), - method("tursodb", "exec", true, None), - method("tursodb", "execBatch", true, None), - method("tursodb", "queryAll", true, None), - method("tursodb", "queryOne", true, None), - method("tursodb", "close", true, None), - method("tursodb", "lastInsertRowid", true, None), - method("tursodb", "isAutocommit", true, None), - // iroh (#425). bind / nodeId / close shipped in v0.5.544; the - // peer connection + stream surface (connect / acceptOne / - // openBi / acceptBi / streamWrite / streamFinish / - // streamReadToEnd / connClose) shipped in v0.5.554. ALPN is - // hardcoded to `b"perry-iroh/0"` for v0. - method("iroh", "bind", false, None), - method("iroh", "nodeId", true, None), - method("iroh", "close", true, None), - method("iroh", "connect", true, None), - method("iroh", "acceptOne", true, None), - method("iroh", "openBi", true, None), - method("iroh", "acceptBi", true, None), - method("iroh", "streamWrite", true, None), - method("iroh", "streamFinish", true, None), - method("iroh", "streamReadToEnd", true, None), - method("iroh", "connClose", true, None), property("sea", "default"), method("sea", "isSea", false, None), method("sea", "getAsset", false, None), diff --git a/crates/perry-hir/tests/unimplemented_api_check.rs b/crates/perry-hir/tests/unimplemented_api_check.rs index a9120201d7..14fa823fbc 100644 --- a/crates/perry-hir/tests/unimplemented_api_check.rs +++ b/crates/perry-hir/tests/unimplemented_api_check.rs @@ -364,20 +364,11 @@ fn perry_native_namespace_rejects_unknown_call_in_strict_mode() { /// /// Side-effect-only sub-paths (`dotenv/config`) are skipped — they have /// no value binding to read properties off, so the gate doesn't apply. -/// `tursodb` and `iroh` are external bindings (live in standalone -/// `@perryts/*` repos as of v0.5.557) — their manifest entries exist -/// but the in-tree resolver doesn't recognise them as a `NativeModuleRef` -/// without `node_modules//package.json` declaring `perry.nativeLibrary`, -/// so the gate's prerequisite shape never triggers in this isolated -/// HIR test. #[test] fn every_supported_module_rejects_bogus_member() { const SKIP: &[&str] = &[ // Side-effect-only — no value binding to access. "dotenv/config", - // External (non-bundled) bindings — out-of-tree as of v0.5.557. - "tursodb", - "iroh", ]; let mut failures: Vec = Vec::new(); @@ -455,9 +446,6 @@ fn every_supported_module_rejects_bogus_call() { const SKIP: &[&str] = &[ // Side-effect-only — no value binding to access. "dotenv/config", - // External (non-bundled) bindings — out-of-tree as of v0.5.557. - "tursodb", - "iroh", ]; let mut failures: Vec = Vec::new(); diff --git a/crates/perry/src/commands/compile/optimized_libs/freshness.rs b/crates/perry/src/commands/compile/optimized_libs/freshness.rs index b2e25ccde3..8aab6c74c7 100644 --- a/crates/perry/src/commands/compile/optimized_libs/freshness.rs +++ b/crates/perry/src/commands/compile/optimized_libs/freshness.rs @@ -749,7 +749,6 @@ pub(crate) fn binding_needs_shared_tokio(module: &str) -> bool { // HTTP clients (reqwest, hyper) | "axios" | "node-fetch" - | "fetch" // undici — glue over the native fetch stack (network I/O family). // The wrapper itself has no tokio dep today, but it rides the // shared build so the driver auto-builds its archive alongside diff --git a/crates/perry/src/commands/stdlib_features.rs b/crates/perry/src/commands/stdlib_features.rs index bc51e18251..a7f4b0561b 100644 --- a/crates/perry/src/commands/stdlib_features.rs +++ b/crates/perry/src/commands/stdlib_features.rs @@ -81,16 +81,6 @@ pub fn module_to_features(module: &str) -> &'static [&'static str] { // `database-sqlite` feature with better-sqlite3 — DatabaseSync / // StatementSync route to the same `js_sqlite_*` runtime. "sqlite" | "bun:sqlite" => &["database-sqlite"], - // tursodb (#424) lives in the external - // `PerryTS/tursodb-bindings` repo (`bun add @perryts/tursodb`) - // since v0.5.557 — perry's package.json `perry.nativeLibrary` - // resolution path picks it up from `node_modules/`. No - // perry-stdlib feature gate to manage. - "tursodb" => &[], - // iroh (#425) lives in the external `PerryTS/iroh-bindings` - // repo (`bun add @perryts/iroh`) since v0.5.557 — same model - // as tursodb above. - "iroh" => &[], // Redis is detected via the ioredis class name in collect_modules, // but if it shows up as an explicit import we still need the feature. // `database-redis` umbrella retained for backwards-compat; diff --git a/crates/perry/well_known_bindings.toml b/crates/perry/well_known_bindings.toml index b72de46d51..59e13d4598 100644 --- a/crates/perry/well_known_bindings.toml +++ b/crates/perry/well_known_bindings.toml @@ -511,14 +511,6 @@ repo = "https://github.com/node-fetch/node-fetch" ref = "8b3320d2a7c07bce4afc6b2bf6c3bbddda85b01f" ported-at = "3.3.2" date = "2026-07-30" -[bindings.fetch] -crate = "perry-ext-fetch" -lib = "perry_ext_fetch" -tracking = "#466" -# Bare `fetch` is an alias for the node-fetch surface (same wrapper crate); it -# is not itself an npm package, so it inherits node-fetch's provenance. -alias-of = "node-fetch" - [bindings.ws] crate = "perry-ext-ws" lib = "perry_ext_ws" From cb08c5745e6a058917aec5066b61af0b9e2cec17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Fri, 18 Sep 2026 11:28:56 +0000 Subject: [PATCH 2/4] chore: regen API docs + governance table for the binding removal Fetch's manifest entries stay (internal dispatch tag for the built-in Web Fetch API), so mark it in the test-only INTERNAL_MODULE_KEYS allowlist now that it is no longer a NATIVE_MODULES import specifier. --- crates/perry-api-manifest/src/entries.rs | 9 +++++-- docs/api/perry.d.ts | 12 +-------- docs/src/api/reference.md | 33 +----------------------- docs/src/native-libraries/governance.md | 2 +- 4 files changed, 10 insertions(+), 46 deletions(-) diff --git a/crates/perry-api-manifest/src/entries.rs b/crates/perry-api-manifest/src/entries.rs index 182a94665e..ce9ae85f87 100644 --- a/crates/perry-api-manifest/src/entries.rs +++ b/crates/perry-api-manifest/src/entries.rs @@ -228,9 +228,14 @@ pub const NODE_SUBMODULES: &[&str] = &[ ]; /// Internal manifest keys used by dispatch/property gates but not importable -/// module specifiers. +/// module specifiers. `fetch` covers the built-in Web Fetch API's value-typed +/// dispatch tag (`Response`/`Headers`/`Request`/`Blob`/`FormData` + the bare +/// global `fetch()` call) — real fetch/HTTP-client I/O, distinct from the +/// removed bare-name `"fetch"` alias for the `node-fetch` npm package that +/// used to double as its NATIVE_MODULES entry (see well_known_bindings.toml). #[cfg(test)] -pub(crate) const INTERNAL_MODULE_KEYS: &[&str] = &["inspector.Network", "punycode.ucs2"]; +pub(crate) const INTERNAL_MODULE_KEYS: &[&str] = + &["inspector.Network", "punycode.ucs2", "fetch"]; /// Modules handled entirely by `perry-runtime` — the linker doesn't /// need to pull in `perry-stdlib` for these. Migrated from diff --git a/docs/api/perry.d.ts b/docs/api/perry.d.ts index cb700c98cd..87dc9693bb 100644 --- a/docs/api/perry.d.ts +++ b/docs/api/perry.d.ts @@ -1,6 +1,6 @@ // Auto-generated from Perry's API manifest (#465). Do not edit by hand. // Source: perry-api-manifest::API_MANIFEST -// Coverage: 2095 entries across 138 modules +// Coverage: 2093 entries across 136 modules type PerryI8 = number & { readonly __perryI8?: never }; type PerryI16 = number & { readonly __perryI16?: never }; @@ -2096,11 +2096,6 @@ declare module "iovalkey" { export function createClient(...args: any[]): any; } -declare module "iroh" { - /** stdlib */ - export function bind(...args: any[]): any; -} - declare module "jsonwebtoken" { /** stdlib */ export function decode(token: string): any; @@ -4141,11 +4136,6 @@ declare module "tty" { export function isatty(...args: any[]): any; } -declare module "tursodb" { - /** stdlib */ - export function open(...args: any[]): any; -} - declare module "typescript" { /** stdlib */ export const DiagnosticCategory: any; diff --git a/docs/src/api/reference.md b/docs/src/api/reference.md index 457840e2d4..afcbe5c54a 100644 --- a/docs/src/api/reference.md +++ b/docs/src/api/reference.md @@ -2,7 +2,7 @@ This page is auto-generated from Perry's compile-time API manifest (`perry-api-manifest::API_MANIFEST`). It is the source of truth for what `perry compile` accepts; references to symbols not listed here produce `R005 UnimplementedApi` (issue #463). Stubs (#464) are flagged ⚠ — they link cleanly but no-op at runtime on the chosen target. -Total: 3054 entries across 140 modules. +Total: 3035 entries across 138 modules. ## Modules @@ -64,7 +64,6 @@ Total: 3054 entries across 140 modules. - [`inspector/promises`](#inspectorpromises) - [`ioredis`](#ioredis) - [`iovalkey`](#iovalkey) -- [`iroh`](#iroh) - [`jsonwebtoken`](#jsonwebtoken) - [`lodash`](#lodash) - [`lru-cache`](#lru-cache) @@ -132,7 +131,6 @@ Total: 3054 entries across 140 modules. - [`timers/promises`](#timerspromises) - [`tls`](#tls) - [`tty`](#tty) -- [`tursodb`](#tursodb) - [`typescript`](#typescript) - [`undici`](#undici) - [`url`](#url) @@ -2014,22 +2012,6 @@ Total: 3054 entries across 140 modules. - `createClient` — module -## `iroh` - -### Methods - -- `acceptBi` — instance -- `acceptOne` — instance -- `bind` — module -- `close` — instance -- `connClose` — instance -- `connect` — instance -- `nodeId` — instance -- `openBi` — instance -- `streamFinish` — instance -- `streamReadToEnd` — instance -- `streamWrite` — instance - ## `jsonwebtoken` ### Methods @@ -3786,19 +3768,6 @@ Total: 3054 entries across 140 modules. - `removeListener` — instance *(class: `WriteStream`)* - `setRawMode` — instance *(class: `ReadStream`)* -## `tursodb` - -### Methods - -- `close` — instance -- `exec` — instance -- `execBatch` — instance -- `isAutocommit` — instance -- `lastInsertRowid` — instance -- `open` — module -- `queryAll` — instance -- `queryOne` — instance - ## `typescript` ### Methods diff --git a/docs/src/native-libraries/governance.md b/docs/src/native-libraries/governance.md index dd9a302695..1591f27649 100644 --- a/docs/src/native-libraries/governance.md +++ b/docs/src/native-libraries/governance.md @@ -99,7 +99,7 @@ from `well_known_bindings.toml`. Regenerate this table with | `perry-ext-events` | `events` | Runtime API | Keep near core; consolidate when practical | Bundled; retained | | `perry-ext-exponential-backoff` | `exponential-backoff` | Source package | Compile the upstream package source | Bundled; migration pending | | `perry-ext-fastify` | `fastify` | Source package | Compile the upstream package source | Bundled; migration pending | -| `perry-ext-fetch` | `fetch`
`node-fetch` | Source package | Compile the upstream package source | Bundled; migration pending | +| `perry-ext-fetch` | `node-fetch` | Source package | Compile the upstream package source | Bundled; migration pending | | `perry-ext-http` | `http`
`http2`
`https` | Runtime API | Keep near core; consolidate when practical | Bundled; retained | | `perry-ext-ioredis` | `ioredis`
`iovalkey`
`redis` | Source package | Compile the upstream package source | Bundled; migration pending | | `perry-ext-jsonwebtoken` | `jsonwebtoken` | Source package | Compile the upstream package source | Bundled; migration pending | From 754dc3345a80ff9db942e80d54a9d31a22939d3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Fri, 18 Sep 2026 11:36:55 +0000 Subject: [PATCH 3/4] chore: cargo fmt --- crates/perry-api-manifest/src/entries.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/perry-api-manifest/src/entries.rs b/crates/perry-api-manifest/src/entries.rs index ce9ae85f87..0e1415b629 100644 --- a/crates/perry-api-manifest/src/entries.rs +++ b/crates/perry-api-manifest/src/entries.rs @@ -234,8 +234,7 @@ pub const NODE_SUBMODULES: &[&str] = &[ /// removed bare-name `"fetch"` alias for the `node-fetch` npm package that /// used to double as its NATIVE_MODULES entry (see well_known_bindings.toml). #[cfg(test)] -pub(crate) const INTERNAL_MODULE_KEYS: &[&str] = - &["inspector.Network", "punycode.ucs2", "fetch"]; +pub(crate) const INTERNAL_MODULE_KEYS: &[&str] = &["inspector.Network", "punycode.ucs2", "fetch"]; /// Modules handled entirely by `perry-runtime` — the linker doesn't /// need to pull in `perry-stdlib` for these. Migrated from From 25f5088bbc34855bf25fea6f027b2ab9623d8197 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Fri, 18 Sep 2026 12:03:17 +0000 Subject: [PATCH 4/4] docs: changelog fragment for #10618 --- .../10618-tier-a-native-binding-removal.md | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 changelog.d/10618-tier-a-native-binding-removal.md diff --git a/changelog.d/10618-tier-a-native-binding-removal.md b/changelog.d/10618-tier-a-native-binding-removal.md new file mode 100644 index 0000000000..c6fc6d55ce --- /dev/null +++ b/changelog.d/10618-tier-a-native-binding-removal.md @@ -0,0 +1,39 @@ +Removed the three Tier A native bindings that were safe to delete without +any compiler work: the bare-name `fetch` alias for `node-fetch`, and the +leftover in-tree accounting for `tursodb`/`iroh` (their actual crates and +`well_known_bindings.toml` entries had already moved out to +`@perryts/tursodb` / `@perryts/iroh` in v0.5.557 — this finishes the job). + +`fetch` alias: dropped `[bindings.fetch]` from `well_known_bindings.toml` +and the `"fetch"` entry from `NATIVE_MODULES`. `node-fetch` itself, its +`perry-ext-fetch` crate, and the manifest entries backing the built-in Web +Fetch API's `Response`/`Headers`/`Request`/`Blob`/`FormData` dispatch tag +(a separate mechanism, confirmed via the pre-existing +`builtin_fetch_usage_does_not_synthesize_well_known_fetch` test) are +unaffected; `"fetch"` moved to the test-only `INTERNAL_MODULE_KEYS` +allowlist so `known_modules_consistent_with_manifest` still holds. + +`tursodb`/`iroh`: removed their `NATIVE_MODULES` entries, manifest method +rows, `stdlib_features.rs` feature-gate arms, and skip-list entries in the +`unimplemented_api_check.rs` coverage sweeps. Their presence in +`NATIVE_MODULES` with no backing crate made the bare `import * as tursodb +from "tursodb"` specifier short-circuit past file resolution and silently +claim nativeness with nothing behind it — a dangling reference, not a +working feature. The real `@perryts/tursodb` / `@perryts/iroh` packages are +consumed via their scoped specifier, which was never in `NATIVE_MODULES` +and resolves through the unrelated `node_modules`/`perry.nativeLibrary` +path, untouched here. + +Regenerated `docs/src/api/reference.md`, `docs/api/perry.d.ts` (drop the +`tursodb`/`iroh` sections, keep `## fetch`), and +`docs/src/native-libraries/governance.md`'s generated table (`perry-ext-fetch`'s +package mapping drops `fetch`, keeps `node-fetch`). + +Validated: cargo tests on `perry-api-manifest`, `perry-hir`, `perry-codegen` +(`manifest_consistency`), and `perry`'s `stdlib_features`/`optimized_libs` +unit tests all green; `run_lint_gates.sh` 76/77 (the one red, +"Public benchmark evidence freshness", is pre-existing on every PR); +targeted gap suite (every fixture using `node-fetch` or built-in `fetch`) +at 100% parity; hand probes confirm `import * as tursodb from "tursodb"` +now fails at compile time with a clear error instead of compiling to a +broken no-op, and built-in `fetch`/`Response`/`Headers` still resolve.