fs.watch/fs.watchFile: remove unsafe from the watcher modules
What
Same programme as #40055 / #40135 / #40136 / #40139 / #40187 / #40190, applied to the fs.watch / fs.watchFile machinery: path_watcher.rs 66 → 0, fs_events.rs 64 → 0, node_fs_stat_watcher.rs 44 → 0, node_fs_watcher.rs 28 → 0 (one unsafe extern "C" { safe fn } decl for a C++ symbol that takes this file's EventType).
- Registrations, not pointers. A
PathWatcheris owned by the manager'sGuarded<State>and addressed by a never-reusedWatcherId; inotify wds / kqueue entries / FSEvents streams carry ids.fs.watchgets apath_watcher::Registration(RAII; drop = detach). The reader thread's per-handler state is a typedEventSink(batch of ≤8 events,Arc<Activity>,VmHandle) inside the locked state; batches reach the JS thread as ownedBox<FSWatchTask>via a newVmHandle::post_boxed(refused ⇒ box handed back). FSWatcher:Activity { mutex, closed, pending }in anArcshared with sinks/tasks/GC thread; the abort listener is a newbun_jsc::AbortSubscription(holds a signal ref, unregisters on drop; subscriber passed asBackRef); wrapper viato_js_boxed;finalize_js_box.StatWatcher/ scheduler: queue isGuarded<Vec<RefPtr<StatWatcher>>>; the stat pass is an owned, ticketedStatPassTaskholding the scheduler ref; timer hops are boxed payloads owning theirRefPtr; the per-thread scheduler slot moves fromRareData(erased pointer) toRuntimeStatewith a scopedwith_stat_watcher_scheduler(|slot| ..);deinit→Drop.- FSEvents (macOS): new
bun_sys::core_foundation— dlsym'd typed function table, ownedCFString/CFStringArray/RunLoop/RunLoopSource/EventStream(created and scheduled on the calling thread's run loop; start/stop/drop assert that thread), typed&'static Hhandler trampolines. Cross-thread requests are two atomic bits +RunLoopSourceHandler::perform; state underGuarded; registrations by id. bun_ptr::ThreadBound<T>: aBackRefplus the owningstdthread id; any thread may carry it, only the owner thread mayget()(asserted) — the typed way to hand a JS-thread object's address to a watcher thread purely so it can be handed back.bun_sys::linux::inotify_add_watch_z.
Pre-existing issue fixed in passing: the old FSWatchTaskPosix::release_unrun dereferenced the FSWatcher (which may already be finalized at teardown); the new one only touches the shared Arc<Activity>.
Testing
Debug+ASAN (Linux): test/js/node/watch/*.test.ts, all 38 test-fs-watch* / test-fs-watchfile* node parallel tests, fs.test.ts -t watch, test/cli/hot/{hot,watch,watch-many-dirs}, test/cli/watch/{watch,watcher-trace}, source lints. Hand-driven: recursive watch with create/rename/delete and close inside the listener; watchFile + unwatchFile mid-poll and immediately; 200 watchers on one path then GC; an unclosed unref'd watcher collected; a worker with fs.watch/fs.watchFile terminated while main keeps watching — exit 0, no ASAN output, outputs match 1.4.0. clippy clean on every touched crate (host + aarch64-apple-darwin); rust-check-all x86_64-pc-windows-msvc + aarch64-apple-darwin pass (macOS FSEvents / Windows ReadDirectoryChangesW paths are type-checked, not executed here).