From d7a4f8ffaa6ff1db146cd261f670e94e23ae50ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Sun, 20 Sep 2026 00:18:36 +0200 Subject: [PATCH 1/2] refactor(stdlib): remove nanoid native binding customAlphabet(alphabet, size) is documented to return a generator function; native customAlphabet instead returns the generated id string directly (js_nanoid_custom's own doc comment: "For simplicity, we combine this into one call"), so the only documented usage -- const gen = customAlphabet(...); gen(); -- crashes with TypeError: value is not a function. Per #10678 (duplicate extern "C" exports across perry-ext-*/perry-stdlib pairs), this binding existed twice: crates/perry-ext-nanoid/ (governance- tracked) and crates/perry-stdlib/src/nanoid.rs (a second, independent implementation behind the default-on bundled-nanoid feature, exporting the same js_nanoid/js_nanoid_sized/js_nanoid_custom symbols). customAlphabet is declared to codegen (data_stores.rs's js_nanoid_custom) but has no call-site wiring anywhere in perry-codegen -- no NativeModSig row, no lower_call special case. Only plain nanoid(size) had a dispatch row (native_table/utils_crypto.rs, routing to js_nanoid_sized), consistent with customAlphabet not being a first-class compiled call at all. Removed both crates, the 1-entry NativeModSig dispatch row, the 2 js_nanoid* FFI declarations, the well_known_bindings.toml entry, the "nanoid" NATIVE_MODULES entry + manifest row, the bundled-nanoid stdlib feature, and 2 Android stub exports. DELETED the shipped_unproven_bindings_are_partial test rather than emptying it. Its subject population was exactly the hand-written wrappers shipped without proven upstream parity: uuid (#10701), dotenv (#10691, landed in train 227) and nanoid (here). With the last one gone the array would read `for name in []` -- a test that compiles, runs, asserts nothing and reports green forever, which is failure mode #4 in CLAUDE.md's "four ways a gate can be unable to fail". Coverage is not lost: shipped_subset_bindings_are_partial is a separate test and still asserts the same property for undici, node-forge, lru-cache and qs. Verified the deletion orphans nothing -- the test module is `use super::*`, lookup_well_known has 9 other callers, BindingCompat::Partial has 5 other uses, and nothing in the tree keys on the test's name. The "ids" umbrella is now EMPTY. It was retargeted to ["bundled-nanoid"] when #10701 removed uuid (train 225); removing bundled-nanoid leaves it with no members, so it is kept as `ids = []` -- an intentionally harmless no-op that preserves `--features ids` for existing callers rather than breaking them. The stale comments that described the two-member split (perry-stdlib/Cargo.toml, perry-stdlib/src/lib.rs, stdlib_features.rs) are rewritten to say so. perry-stdlib's `uuid` crate dependency is untouched: #10701 already made it non-optional because crypto/random.rs calls it unconditionally. test-files/test_parity_nanoid.ts (the exact customAlphabet(...)(); reproduction) is already excluded from the parity gate -- known_failures.json classifies it "ci-env": Node's own oracle fails with ERR_MODULE_NOT_FOUND in CI because nanoid was never added to the repo's root package.json, so npm ci never installs it. Not touched -- provisioning a real npm dependency in the root package.json is out of scope for a binding-removal PR, and the test remains excluded before and after this change for the same underlying reason. Every absolute count re-derived from its own script against the resolved tree, never carried across the rebase and never hand-merged: workspace-architecture.json 77 -> 76 members, externalize 28 -> 27, keep 44 (scripts/workspace_architecture.py; git auto-merged this file with NO conflict and left the stale 77/28, which the script caught); docs/api/perry.d.ts 2065 entries/131 modules -> 2064/130 and docs/src/api/reference.md 3007/133 -> 3006/132, both regenerated by running the built binary's --print-api-manifest rather than editing the headers; Cargo.lock regenerated with `cargo metadata`. native_result_ledger (376 rows/326 providers), unrooted_local_shape (578) and string_payload_access (perry-stdlib inline-offset 37) confirmed unchanged by running them, not by assuming. Rebased onto v0.5.1606 (train 227). Conflicts were all of the form "main deleted dotenv, this branch deleted nanoid, in one hunk"; every one was resolved to main's current content minus nanoid's own entries, which is neither side. --- Cargo.lock | 19 ---- Cargo.toml | 2 - crates/perry-api-manifest/src/entries.rs | 1 - .../perry-api-manifest/src/entries/part_1.rs | 12 -- .../lower_call/native_table/utils_crypto.rs | 12 -- .../runtime_decls/stdlib_ffi/data_stores.rs | 4 - crates/perry-ext-nanoid/Cargo.toml | 20 ---- crates/perry-ext-nanoid/src/lib.rs | 106 ------------------ crates/perry-stdlib/Cargo.toml | 15 +-- crates/perry-stdlib/src/lib.rs | 15 +-- crates/perry-stdlib/src/nanoid.rs | 62 ---------- crates/perry-ui-android/src/stdlib_stubs.rs | 8 -- .../perry/src/commands/compile/well_known.rs | 12 -- crates/perry/src/commands/stdlib_features.rs | 13 +-- crates/perry/well_known_bindings.toml | 15 --- docs/api/perry.d.ts | 7 +- docs/src/api/reference.md | 9 +- docs/src/native-libraries/governance.md | 1 - workspace-architecture.json | 9 +- 19 files changed, 21 insertions(+), 321 deletions(-) delete mode 100644 crates/perry-ext-nanoid/Cargo.toml delete mode 100644 crates/perry-ext-nanoid/src/lib.rs delete mode 100644 crates/perry-stdlib/src/nanoid.rs diff --git a/Cargo.lock b/Cargo.lock index 37062ae16c..b6b0cfce43 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4875,15 +4875,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13d2233c9842d08cfe13f9eac96e207ca6a2ea10b80259ebe8ad0268be27d2af" -[[package]] -name = "nanoid" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8628de41fe064cc3f0cf07f3d299ee3e73521adaff72278731d5c8cae3797873" -dependencies = [ - "rand 0.9.4", -] - [[package]] name = "ndk-context" version = "0.1.1" @@ -6050,15 +6041,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "perry-ext-nanoid" -version = "0.5.1606" -dependencies = [ - "nanoid", - "perry-ffi", - "rand 0.10.2", -] - [[package]] name = "perry-ext-net" version = "0.5.1606" @@ -6382,7 +6364,6 @@ dependencies = [ "md-5 0.11.0", "ml-kem", "mongodb", - "nanoid", "once_cell", "p256", "p384", diff --git a/Cargo.toml b/Cargo.toml index 766712ef22..336f2b35af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,6 @@ members = [ "crates/perry-runtime", "crates/perry-ffi", "crates/perry-native-registration", - "crates/perry-ext-nanoid", "crates/perry-ext-bcrypt", "crates/perry-ext-argon2", "crates/perry-perex", @@ -471,7 +470,6 @@ perry-dispatch = { path = "crates/perry-dispatch" } perry-runtime = { path = "crates/perry-runtime", version = "0.5.1011", default-features = false } perry-ffi = { path = "crates/perry-ffi", version = "0.5.1011" } perry-native-registration = { path = "crates/perry-native-registration", version = "0.5.1534" } -perry-ext-nanoid = { path = "crates/perry-ext-nanoid" } perry-ext-bcrypt = { path = "crates/perry-ext-bcrypt" } perry-ext-argon2 = { path = "crates/perry-ext-argon2" } perry-perex = { path = "crates/perry-perex" } diff --git a/crates/perry-api-manifest/src/entries.rs b/crates/perry-api-manifest/src/entries.rs index 53e9dbe339..1ac5de2cb5 100644 --- a/crates/perry-api-manifest/src/entries.rs +++ b/crates/perry-api-manifest/src/entries.rs @@ -43,7 +43,6 @@ pub const NATIVE_MODULES: &[&str] = &[ "ws", // WebSocket client/server "zlib", // (Node builtin) gzip/deflate/brotli/zstd compression "crypto", // (Node builtin) hashing, HMAC, cipher, sign/verify, WebCrypto - "nanoid", // compact URL-safe ID generation "ethers", // Ethereum library (utils/wallet/ABI) "mongodb", // MongoDB driver "better-sqlite3", // synchronous SQLite (replaces the N-API addon) diff --git a/crates/perry-api-manifest/src/entries/part_1.rs b/crates/perry-api-manifest/src/entries/part_1.rs index 9dc26c00a7..05bc326238 100644 --- a/crates/perry-api-manifest/src/entries/part_1.rs +++ b/crates/perry-api-manifest/src/entries/part_1.rs @@ -1111,18 +1111,6 @@ pub(crate) const API_MANIFEST_PART_1: &[ApiEntry] = &[ ), method("nodemailer", "sendMail", true, None), method("nodemailer", "verify", true, None), - method_sig( - "nanoid", - "nanoid", - false, - None, - &[ParamSpec::Named { - name: "size", - ty: TypeSpec::Number, - optional: false, - }], - TypeSpec::String, - ), // #4917 — real retry semantics: options (numOfAttempts/startingDelay/ // timeMultiple/maxDelay/delayFirstAttempt/jitter/retry) honored; // Promise-returning tasks retry on rejection via promise reactions. diff --git a/crates/perry-codegen/src/lower_call/native_table/utils_crypto.rs b/crates/perry-codegen/src/lower_call/native_table/utils_crypto.rs index 4b41aa587c..83189264ff 100644 --- a/crates/perry-codegen/src/lower_call/native_table/utils_crypto.rs +++ b/crates/perry-codegen/src/lower_call/native_table/utils_crypto.rs @@ -29,18 +29,6 @@ pub(super) const UTILS_CRYPTO_ROWS: &[NativeModSig] = &[ args: &[], ret: NR_GCPTR, }, - // ========== nanoid ========== - // js_nanoid_sized(NaN) → size=0 → falls back to js_nanoid() (21-char default), - // so nanoid() and nanoid(N) both route through the same entry safely. - NativeModSig { - module: "nanoid", - has_receiver: false, - method: "nanoid", - class_filter: None, - runtime: "js_nanoid_sized", - args: &[NA_F64], - ret: NR_STR, - }, // ========== exponential-backoff ========== NativeModSig { module: "exponential-backoff", diff --git a/crates/perry-codegen/src/runtime_decls/stdlib_ffi/data_stores.rs b/crates/perry-codegen/src/runtime_decls/stdlib_ffi/data_stores.rs index d9da94b533..13a2d481e2 100644 --- a/crates/perry-codegen/src/runtime_decls/stdlib_ffi/data_stores.rs +++ b/crates/perry-codegen/src/runtime_decls/stdlib_ffi/data_stores.rs @@ -288,8 +288,4 @@ pub(crate) fn declare_data_stores(module: &mut LlModule) { module.declare_function("js_crypto_x25519_shared_secret", I64, &[I64, I64]); module.declare_function("js_keccak256_native", I64, &[I64]); module.declare_function("js_keccak256_native_bytes", I64, &[I64]); - - // ========== Nanoid ========== - module.declare_function("js_nanoid", I64, &[DOUBLE]); - module.declare_function("js_nanoid_custom", I64, &[I64, DOUBLE]); } diff --git a/crates/perry-ext-nanoid/Cargo.toml b/crates/perry-ext-nanoid/Cargo.toml deleted file mode 100644 index e264ea3e8b..0000000000 --- a/crates/perry-ext-nanoid/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "perry-ext-nanoid" -version.workspace = true -edition.workspace = true -license.workspace = true -description = "Native bindings for the npm `nanoid` package — uses only `perry-ffi`. Second port under #466 Phase 5 (after dotenv)." - -[lints] -workspace = true - -[lib] -crate-type = ["staticlib", "rlib"] - -[dependencies] -perry-ffi.workspace = true -nanoid = "0.5" -rand = "0.10" - -[dev-dependencies] -perry-ffi = { workspace = true, features = ["runtime-link"] } diff --git a/crates/perry-ext-nanoid/src/lib.rs b/crates/perry-ext-nanoid/src/lib.rs deleted file mode 100644 index 8b58aecc9a..0000000000 --- a/crates/perry-ext-nanoid/src/lib.rs +++ /dev/null @@ -1,106 +0,0 @@ -//! Native bindings for the npm `nanoid` package. -//! -//! Functionally identical to `crates/perry-stdlib/src/nanoid.rs`. The -//! point of this crate is that it depends only on [`perry_ffi`], not -//! on `perry-runtime` internals — proving the perry-ffi v0.5 surface -//! still suffices for the second wrapper port (#466 Phase 5 step 2). - -use nanoid::nanoid; -use perry_ffi::{alloc_string, read_string, JsString, StringHeader}; - -/// `nanoid()` — 21-char URL-safe id with the default alphabet. -#[no_mangle] -pub extern "C" fn js_nanoid() -> *mut StringHeader { - let id = nanoid!(); - alloc_string(&id).as_raw() -} - -/// `nanoid(size)` — id with a custom length. -#[no_mangle] -pub extern "C" fn js_nanoid_sized(size: f64) -> *mut StringHeader { - let size = size as usize; - if size == 0 { - return js_nanoid(); - } - let id = nanoid!(size); - alloc_string(&id).as_raw() -} - -/// `customAlphabet(alphabet, size)()` — id with a user-supplied -/// alphabet. Perry collapses this into a single call rather than the -/// curried form Node uses, so the FFI surface stays flat. -/// -/// # Safety -/// -/// `alphabet_ptr` must be null or a pointer to a Perry-runtime -/// `StringHeader`. -#[no_mangle] -pub unsafe extern "C" fn js_nanoid_custom( - alphabet_ptr: *const StringHeader, - size: f64, -) -> *mut StringHeader { - let handle = JsString::from_raw(alphabet_ptr as *mut StringHeader); - let alphabet = match read_string(handle) { - Some(a) => a, - None => return js_nanoid(), - }; - - let size = if size <= 0.0 { 21 } else { size as usize }; - let alphabet_chars: Vec = alphabet.chars().collect(); - - if alphabet_chars.is_empty() { - return js_nanoid(); - } - - use rand::RngExt; - let mut rng = rand::rng(); - let id: String = (0..size) - .map(|_| { - let idx = rng.random_range(0..alphabet_chars.len()); - alphabet_chars[idx] - }) - .collect(); - - alloc_string(&id).as_raw() -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn default_id_is_21_chars() { - let handle = unsafe { JsString::from_raw(js_nanoid()) }; - let s = read_string(handle).expect("non-null"); - assert_eq!(s.chars().count(), 21); - } - - #[test] - fn sized_id_honors_length() { - for n in [1, 5, 16, 100] { - let handle = unsafe { JsString::from_raw(js_nanoid_sized(n as f64)) }; - let s = read_string(handle).expect("non-null"); - assert_eq!(s.chars().count(), n, "size={}", n); - } - } - - #[test] - fn sized_zero_falls_back_to_default() { - let handle = unsafe { JsString::from_raw(js_nanoid_sized(0.0)) }; - let s = read_string(handle).expect("non-null"); - assert_eq!(s.chars().count(), 21); - } - - #[test] - fn custom_alphabet_round_trips_through_perry_ffi() { - // Allocate the alphabet through perry-ffi so the FFI is - // exercised end-to-end. - let alphabet = alloc_string("abc"); - let handle = unsafe { js_nanoid_custom(alphabet.as_raw() as *const _, 8.0) }; - let s = read_string(unsafe { JsString::from_raw(handle) }).expect("non-null"); - assert_eq!(s.chars().count(), 8); - for c in s.chars() { - assert!("abc".contains(c), "char `{}` not in alphabet", c); - } - } -} diff --git a/crates/perry-stdlib/Cargo.toml b/crates/perry-stdlib/Cargo.toml index 0357575ca8..bbf85a49a5 100644 --- a/crates/perry-stdlib/Cargo.toml +++ b/crates/perry-stdlib/Cargo.toml @@ -282,14 +282,12 @@ rate-limit = ["bundled-ratelimit"] bundled-ratelimit = ["dep:governor", "async-runtime"] -# UUID/nanoid — `ids` stays as the umbrella for backwards compat; -# from v0.5.534 onwards the per-binding split (`bundled-uuid` / -# `bundled-nanoid`) is what the well-known bindings flip (#466 -# Phase 4 step 2) toggles. Each sub-feature pulls in its own -# optional dep + gates its own module so a wrapper port can strip -# exactly one binding from perry-stdlib without affecting the other. -ids = ["bundled-nanoid"] -bundled-nanoid = ["dep:nanoid"] +# `ids` is now an empty umbrella kept only for backwards compat: +# both members are gone — `bundled-uuid` with the uuid binding +# (#10701) and `bundled-nanoid` with the nanoid binding (#10693). +# Real `uuid` / `nanoid` now compile from npm source, so nothing +# needs to be toggled here; enabling `ids` is a harmless no-op. +ids = [] # Async runtime (tokio) - internal feature async-runtime = ["dep:tokio"] @@ -435,7 +433,6 @@ governor = { version = "0.10", optional = true } # never optional, regardless of the bundled-uuid npm-binding feature # (removed; see #10678/#466). uuid = { version = "1.23", features = ["v4", "v1", "v3", "v5", "v7"] } -nanoid = { version = "0.5", optional = true } # LRU Cache — optional from v0.5.539 so the well-known flip can # strip the perry-stdlib copy when `import 'lru-cache'` resolves diff --git a/crates/perry-stdlib/src/lib.rs b/crates/perry-stdlib/src/lib.rs index 7c4e9ed331..2c64ee8296 100644 --- a/crates/perry-stdlib/src/lib.rs +++ b/crates/perry-stdlib/src/lib.rs @@ -398,16 +398,11 @@ pub mod ratelimit; pub use ratelimit::*; // === IDs === -// `bundled-uuid` / `bundled-nanoid` (v0.5.534) replace the old -// `ids` umbrella so the well-known flip (#466 Phase 4) can toggle -// each binding independently. The umbrella stays as -// `ids = ["bundled-uuid", "bundled-nanoid"]` so existing -// `--features ids` callers keep working byte-identically. - -#[cfg(feature = "bundled-nanoid")] -pub mod nanoid; -#[cfg(feature = "bundled-nanoid")] -pub use nanoid::*; +// Nothing left to gate: `bundled-uuid` went with the uuid binding +// (#10701) and `bundled-nanoid` with the nanoid binding (#10693); +// real `uuid` / `nanoid` now compile from npm source. The `ids` +// umbrella stays (empty) in Cargo.toml so existing +// `--features ids` callers keep working. // === Container Module === #[cfg(feature = "container")] diff --git a/crates/perry-stdlib/src/nanoid.rs b/crates/perry-stdlib/src/nanoid.rs deleted file mode 100644 index b39c64441a..0000000000 --- a/crates/perry-stdlib/src/nanoid.rs +++ /dev/null @@ -1,62 +0,0 @@ -//! NanoID module (nanoid compatible) -//! -//! Native implementation of the 'nanoid' npm package. -//! Generates short, URL-friendly unique IDs. - -use nanoid::nanoid; -use perry_runtime::{js_string_from_bytes, StringHeader}; - -use crate::common::string_from_header; - -/// Generate a nanoid with default settings (21 chars, URL-safe alphabet) -/// nanoid() -> string -#[no_mangle] -pub extern "C" fn js_nanoid() -> *mut StringHeader { - let id = nanoid!(); - js_string_from_bytes(id.as_ptr(), id.len() as u32) -} - -/// Generate a nanoid with custom length -/// nanoid(size) -> string -#[no_mangle] -pub extern "C" fn js_nanoid_sized(size: f64) -> *mut StringHeader { - let size = size as usize; - if size == 0 { - return js_nanoid(); - } - let id = nanoid!(size); - js_string_from_bytes(id.as_ptr(), id.len() as u32) -} - -/// Generate a nanoid with custom alphabet and size -/// customAlphabet(alphabet, size)() -> string -/// For simplicity, we combine this into one call: nanoid.custom(alphabet, size) -#[no_mangle] -pub unsafe extern "C" fn js_nanoid_custom( - alphabet_ptr: *const StringHeader, - size: f64, -) -> *mut StringHeader { - let alphabet = match string_from_header(alphabet_ptr) { - Some(a) => a, - None => return js_nanoid(), - }; - - let size = if size <= 0.0 { 21 } else { size as usize }; - let alphabet_chars: Vec = alphabet.chars().collect(); - - if alphabet_chars.is_empty() { - return js_nanoid(); - } - - // Generate ID using custom alphabet - use rand::RngExt; - let mut rng = rand::rng(); - let id: String = (0..size) - .map(|_| { - let idx = rng.random_range(0..alphabet_chars.len()); - alphabet_chars[idx] - }) - .collect(); - - js_string_from_bytes(id.as_ptr(), id.len() as u32) -} diff --git a/crates/perry-ui-android/src/stdlib_stubs.rs b/crates/perry-ui-android/src/stdlib_stubs.rs index 6da9579d68..0fc7b58452 100644 --- a/crates/perry-ui-android/src/stdlib_stubs.rs +++ b/crates/perry-ui-android/src/stdlib_stubs.rs @@ -1267,14 +1267,6 @@ pub extern "C" fn js_mysql2_pool_query() -> i64 { 0 } #[no_mangle] -pub extern "C" fn js_nanoid() -> i64 { - 0 -} -#[no_mangle] -pub extern "C" fn js_nanoid_custom() -> i64 { - 0 -} -#[no_mangle] pub extern "C" fn js_new_instance() -> i64 { 0 } diff --git a/crates/perry/src/commands/compile/well_known.rs b/crates/perry/src/commands/compile/well_known.rs index 4307c829bc..dab94870d9 100644 --- a/crates/perry/src/commands/compile/well_known.rs +++ b/crates/perry/src/commands/compile/well_known.rs @@ -476,18 +476,6 @@ mod tests { } } - #[test] - fn shipped_unproven_bindings_are_partial() { - for name in ["nanoid"] { - let b = lookup_well_known(name).unwrap_or_else(|| panic!("{name} registered")); - assert_eq!( - b.compat, - BindingCompat::Partial, - "{name} omits upstream API/behavior and must stay partial" - ); - } - } - #[test] fn aliases_inherit_target_compat_and_cycles_fail_closed() { let raw = r#" diff --git a/crates/perry/src/commands/stdlib_features.rs b/crates/perry/src/commands/stdlib_features.rs index b1b39d6359..6ca239fa23 100644 --- a/crates/perry/src/commands/stdlib_features.rs +++ b/crates/perry/src/commands/stdlib_features.rs @@ -160,13 +160,12 @@ pub fn module_to_features(module: &str) -> &'static [&'static str] { "argon2" => &["bundled-argon2"], // ── IDs (uuid / nanoid) ─────────────────────────────────────── - // Per-binding split as of v0.5.534 (#466 Phase 4 step 2) - // so the well-known flip can swap each one out - // independently. The `ids` umbrella stays in - // perry-stdlib/Cargo.toml as `bundled-uuid + bundled-nanoid` - // for backwards compat, but feature-set computation goes - // straight to the per-binding feature. - "nanoid" => &["bundled-nanoid"], + // No entries: the uuid binding (#10701) and the nanoid + // binding (#10693) are gone, so `import "uuid"` / + // `import "nanoid"` compile the real npm packages from + // source and need no perry-stdlib feature. The `ids` + // umbrella survives (empty) in perry-stdlib/Cargo.toml + // for backwards compat only. // ── Container ───────────────────────────────────────────────── "perry/container" | "perry/container-compose" | "perry/compose" | "perry/workloads" => { diff --git a/crates/perry/well_known_bindings.toml b/crates/perry/well_known_bindings.toml index 4721c63bc0..1a913b4cff 100644 --- a/crates/perry/well_known_bindings.toml +++ b/crates/perry/well_known_bindings.toml @@ -36,21 +36,6 @@ # requires every ext crate and package mapping to have an explicit decision # (#5716). -[bindings.nanoid] -crate = "perry-ext-nanoid" -lib = "perry_ext_nanoid" -tracking = "#466" -# Partial: the wrapper covers nanoid + custom alphabets, but not the complete -# upstream export set and flattens the curried customAlphabet contract. -compat = "partial" - -[bindings.nanoid.upstream] -version = "6.0.0" -sha256 = "5cade80a39ccf4fd174c8e412eca13accfce36c9c2a0982b4ea23403d729a8d8" -repo = "https://github.com/ai/nanoid" -ref = "4dacb107b54ffd0e1abfe91b7ef452e0fd5a8e12" -ported-at = "6.0.0" -date = "2026-07-30" [bindings.qs] crate = "perry-ext-qs" lib = "perry_ext_qs" diff --git a/docs/api/perry.d.ts b/docs/api/perry.d.ts index 47a24f494b..78c924c802 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: 2065 entries across 131 modules +// Coverage: 2064 entries across 130 modules type PerryI8 = number & { readonly __perryI8?: never }; type PerryI16 = number & { readonly __perryI16?: never }; @@ -2215,11 +2215,6 @@ declare module "mysql2/promise" { export function createPool(p0: any): any; } -declare module "nanoid" { - /** stdlib */ - export function nanoid(size: number): string; -} - declare module "net" { /** stdlib */ export class BlockList { [key: string]: any; } diff --git a/docs/src/api/reference.md b/docs/src/api/reference.md index c0dd3c2f51..5ea2875952 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: 3007 entries across 133 modules. +Total: 3006 entries across 132 modules. ## Modules @@ -69,7 +69,6 @@ Total: 3007 entries across 133 modules. - [`mongodb`](#mongodb) - [`mysql2`](#mysql2) - [`mysql2/promise`](#mysql2promise) -- [`nanoid`](#nanoid) - [`net`](#net) - [`node-cron`](#node-cron) - [`node-fetch`](#node-fetch) @@ -2174,12 +2173,6 @@ Total: 3007 entries across 133 modules. - `release` — instance - `rollback` — instance -## `nanoid` - -### Methods - -- `nanoid` — module - ## `net` ### Classes diff --git a/docs/src/native-libraries/governance.md b/docs/src/native-libraries/governance.md index 3939cc0e72..d893a096a4 100644 --- a/docs/src/native-libraries/governance.md +++ b/docs/src/native-libraries/governance.md @@ -104,7 +104,6 @@ from `well_known_bindings.toml`. Regenerate this table with | `perry-ext-moment` | `moment` | Source package | Compile the upstream package source | Bundled; migration pending | | `perry-ext-mongodb` | `mongodb` | Source package | Compile the upstream package source | Bundled; migration pending | | `perry-ext-mysql2` | `mysql2`
`mysql2/promise` | Source package | Compile the upstream package source | Bundled; migration pending | -| `perry-ext-nanoid` | `nanoid` | Source package | Compile the upstream package source | Bundled; migration pending | | `perry-ext-net` | `net` | Runtime API | Keep near core; consolidate when practical | Bundled; retained | | `perry-ext-node-forge` | `node-forge` | Source package | Compile the upstream package source | Bundled; migration pending | | `perry-ext-nodemailer` | `nodemailer` | Source package | Compile the upstream package source | Bundled; migration pending | diff --git a/workspace-architecture.json b/workspace-architecture.json index 85139f6cfb..7de6b0d0a6 100644 --- a/workspace-architecture.json +++ b/workspace-architecture.json @@ -25,7 +25,7 @@ ] }, "baseline": { - "workspace_members": 77, + "workspace_members": 76, "default_dependency_closure": [ "perry", "perry-api-manifest", @@ -68,7 +68,7 @@ "perry-updater" ], "decision_counts": { - "externalize": 28, + "externalize": 27, "keep": 44, "merge": 1, "remove": 1, @@ -255,11 +255,6 @@ "decision": "externalize", "migration": "compile-source" }, - "perry-ext-nanoid": { - "category": "binding", - "decision": "externalize", - "migration": "compile-source" - }, "perry-ext-net": { "category": "binding", "decision": "keep", From 9a400cc114c9127f3fa468c7daad20462ee9c3a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Sun, 20 Sep 2026 00:18:36 +0200 Subject: [PATCH 2/2] changelog: add fragment for #10693 (nanoid native binding removal) --- changelog.d/10693-nanoid-native-binding-removal.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 changelog.d/10693-nanoid-native-binding-removal.md diff --git a/changelog.d/10693-nanoid-native-binding-removal.md b/changelog.d/10693-nanoid-native-binding-removal.md new file mode 100644 index 0000000000..6bdd6dfe35 --- /dev/null +++ b/changelog.d/10693-nanoid-native-binding-removal.md @@ -0,0 +1,14 @@ +Removed the native `nanoid` binding: `customAlphabet(alphabet, size)` is +documented to return a generator function, but the native implementation +returned the generated id string directly, so the only documented usage +(`const gen = customAlphabet(...); gen();`) crashed with `TypeError: value +is not a function`. `import { nanoid, customAlphabet } from "nanoid"` (no +`perry.compilePackages` entry) now compiles the real npm package from +source, matching Node exactly. + +Deleted both duplicate hand-written implementations +(`crates/perry-ext-nanoid` and `crates/perry-stdlib/src/nanoid.rs`, which +independently exported the same `js_nanoid_*` symbols — #10678). +`customAlphabet` turned out to have no call-site wiring anywhere in +`perry-codegen` at all — only plain `nanoid(size)` had a dispatch row, +consistent with the reported crash.