Verified against code 2026-08-29.
LoroMap, LoroList, LoroText, LoroTree, LoroMovableList, and
LoroCounter expose id from Rust through wasm-bindgen. The generated getter
crosses into WASM and creates a new JS string on every call.
HandlerTrait::idincrates/loro-internal/src/handler.rsreads the immutableBasicHandler.idfor an attached handler. Detached handlers return their type-specificID::NONE_IDplaceholder.- Attaching does not rebind the detached JS wrapper.
insertContainerreturns a new attached wrapper, while the original detached handler records an attached back-reference used bygetAttached(). - Calling
getAttached()on an already attached wrapper clones the Rust handler into another JS wrapper. The two wrappers have the same container id but separate wasm-bindgen pointers and cache entries. - wasm-bindgen sets
__wbg_ptrto zero in__destroy_into_raw(). Anidread afterfree()must keep raising wasm-bindgen's null-pointer error.
scripts/container_id_cache_patch.js therefore stores the id in a
non-enumerable, module-private Symbol property on the JS wrapper. It clears the
property during __destroy_into_raw() and bypasses the cache for a zero
pointer. The cache has exactly the wrapper's lifetime and does not retain the
wrapper from another object.
scripts/build.ts appends the cache patch to the raw wasm-bindgen module for
each of nodejs, web, browser, and bundler. Normal package entrypoints
and exported raw-binding subpaths therefore use the same decorated classes;
post-rollup.ts derives base64 from the patched bundler target. Do not patch
generated loro_wasm*.js files by hand.
Run pnpm bench-wasm-container-id from the repository root after a dev or
release WASM build. The benchmark separates repeated reads of one wrapper,
first reads of many wrappers, a mirror-like reused-wrapper pass, and a
fresh-wrapper boundary.