Overview
dsh-hot-reload
README / EN
Package documentation
Registry summary
dsh.pub verifies the pinned bundle contract, runtime facts, and distribution semantics. The complete README remains in the source repository.
Read the full README on GitHubLIMITATIONS
Known limitations
This plugin is **optimistic**, not verified. It attempts the reload and falls back to "restart needed" only when something **throws** (or when there is no live fiber to swap). It does **not** detect *silent* leaks: - A plugin that acquires a **raw resource outside cordis** — a bare `setInterval`, a `net`/`http` server, a `WebSocketServer`, an `fs.watch`, a `child_process` — **without a `ctx.effect` disposer** can reload without throwing yet leave that resource dangling (a stray timer, a duplicate listener, an orphaned watcher). These accumulate across upgrades and are cleared only by an eventual restart. - Cordis auto-unwinds everything a plugin registers **through `ctx`** (`ctx.effect`, `ctx.on`, `ctx.provide`, tool schemas, adapters), so well-behaved plugins reload cleanly. The risk is limited to plugins that bypass `ctx`. If in doubt, have such a plugin set `dsh.hotReload: false`. - Reloading a plugin that holds **live connections** (e.g. a WebSocket bridge) drops and re-establishes them; clients must reconnect. That's expected, not an error. - The reload path relies on the cordis/loader internals listed under [Compatibility](#compatibility). If they are unavailable (no `--expose-internals` and no `node-addon-require-builtin` addon), the plugin degrades to reporting "restart needed" for every change instead of reloading. - The lockfile is only the **trigger**. Version numbers are read from each package's installed `package.json`, because that is the only file that says what an import would really get. On pnpm 11 (measured on 11.21.0) the files on disk are written **first** and the lockfile **last**, so by the time this plugin acts, the versions it reads have settled. But nothing here *checks* that. If some future pnpm wrote the lockfile first, a check could read the old version, skip it, and never look again — the lockfile is the only thing watched, so that upgrade would be missed **silently**, with no message, until you install another version or restart dsh. The `debounce` setting does not help: the gap measured 2.5–4 seconds, far longer than any sane debounce. - The message channel (`GET /dsh-hot-reload/events`) has **no password check**, the same as dsh's own `/plugins/events`. It sends plugin names and version numbers. dsh already shows those through its plugin list, so this adds no new secret. But if you bind dsh to `0.0.0.0`, count it as one more address that anyone on your network can open. - A plugin that loads **after** `dsh-hot-reload` in the bundle order is reloaded once — to its current version — on the first lockfile write after boot, even when that write was for an unrelated package. Until this plugin has tracked the package across one cycle, it cannot tell whether the running code is the old or the current version, so it reloads rather than adopting a version that may never have run. For an HMR-safe plugin this is a harmless single redundant reload. - The state file is **required**. The plugin writes its tracked state to `profileDir/.dsh-hot-reload-state.json`. If that file cannot be written — for example because the profile directory is read-only — the plugin refuses to start (it throws) rather than running with in-memory-only state. Scope note: this handles **upgrades of already-loaded plugins**. Installing a *brand-new* plugin is a separate concern (adding its row to `cordis.patch.yml`, which dsh already hot-applies).
