description, kind
| description | kind |
|---|---|
| Development-only hot reload for browser client plugins: rebuilding a plugin bundle swaps the running plugin in place, for developers iterating on the web GUI. | package-reference |
@deepseek-ai/dsh-client-hmr
English | 中文
Summary
dsh-client-hmr reloads a browser client plugin in place when its bundle is rebuilt, so a developer editing plugin source sees the change without a full page reload. The reload chain stays idle without a rebuild watcher: only a pnpm run dev:web-style process rewriting client bundles produces the rebuilds it reacts to. Each reload swaps one plugin with fresh component state while the data layer (connection, runtime, and Session objects) stays untouched. Everything here is development machinery in the browser; the model never sees it.
Table of Contents
- Use this package
- Understand the implementation
- Further Exploration
- Model Experience
- Known Limitations and Deferred Work
- Dev Note
Use this package
Enable the rebuild watcher for the plugin you are editing, then save: the browser picks up the rebuilt bundle from the dev server and swaps the plugin without reloading the page. Use it during client development; nothing observable happens in a production build, where no watcher rewrites bundles.
Starting the reload chain
Run pnpm run dev:web (or any tsdown watch process that writes the plugin's lib/client.js) against the same host; rebuilt plugins are then swapped into the running browser automatically, one at a time.
What a reload does
Each reload re-executes the plugin bundle and remounts the plugin with fresh state. Plugins that depend on the reloaded one reload with it automatically. A reload that fails is reported visibly and retried from scratch on the next rebuild.
Configuration
| Field | Default | Meaning |
|---|---|---|
pollIntervalMs |
500 |
Bundle stat-poll interval in milliseconds |
The generated configuration catalog is the exhaustive source for every accepted field and its JSDoc.
Observing success
A successful swap shows the edited UI immediately with no page reload, and the plugin keeps working after the swap. Remember the trade-off: React state inside the reloaded plugin is lost, while session, workspace, and connection state survives.
Understand the implementation
Implementation internals — click to expand
This section explains how the reload chain is built; observable behavior is covered in Use this package.
Design concept
The chain is two halves with one contract: the node half owns bundle detection and notification, the browser half owns the swap. The node half runs one interval that stat-polls each graph bundle and optional source map from the module host's pre-read baseline. An unchanged startup row starts watching without a content read or hash; a changed row, or a dirty row whose artifact reappears, enters rebuilt(), and only real revision changes are broadcast. It also serves /plugins/events, an SSE channel broadcasting graph and rebuilt frames.
The browser swap
On a rebuilt frame the revision makes invalidate select that plugin's immutable one-resource combo URL instead of its initial multi-resource URL. prefetch loads and registers the new factory while the old fiber still serves. The remaining order is registry-first teardown (registry.delete before the fiber's disposer emits internal/plugin, or the vendored Loader flags the entry disabled), drain the old fiber's unload, delete entry.fiber, remove owned <style data-plugin> tags, then entry.refresh() re-imports and remounts, and fiber.await() rethrows startup failures loud. The swap is safe because execution is pure registration under the lazy-CJS model: every module side effect lives in the factory closure and runs at materialization.
Cascade and self-reload
A fiber's activation epoch strings its service providers' uids, so replacing a provider's fiber re-cascades every dependent through cordis itself with zero HMR-side bookkeeping. This plugin is itself a graph entry, so a rebuilt frame may name it; the in-flight reload keeps running in the old bundle's closure and the new bundle's apply opens a fresh channel.
Failure policy
No rollback: an import failure leaves the entry fiberless (the next rebuilt frame retries from scratch), and an apply failure leaves a FAILED fiber visible in the shell's status projection. Both log loudly.
Source map
| File | Role |
|---|---|
src/index.ts |
Node half: bundle stat-poll, rebuilt reporting, /plugins/events SSE channel |
src/client/index.ts |
Browser half: SSE subscription, serialized reload queue, fiber swap |
src/events.ts |
Shared frame types (graph / rebuilt) and the endpoint constant |
Further Exploration
Read these when the reload contract is not enough: the module system that serves the bundles, the shell that boots them, and the module-graph rules behind the externals.
- Client module system — the lazy-CJS module table and
invalidate/prefetchhooks this driver drives. - Web boot kernel — the shell that boots the plugin tree and shows entry status.
- Client group map — the browser half this package reloads.
- Generated configuration catalog — every accepted config field and its source declaration.
Model Experience
None, as the reload driver is a browser-side UI plugin layer that registers nothing model-facing.
KV Cache effect
None; this package neither assembles nor sends a provider request.
Known Limitations and Deferred Work
These limits define what the reload driver does not preserve or restore. They are current package constraints, not a task backlog.
- Reload is coarse by design — a fresh fiber and fresh components; React state inside the reloaded plugin is lost while the data layer (connection/runtime fibers, Session objects) is untouched. react-refresh-grade state preservation conflicts with re-executing the bundle and is deliberately out.
- No failure rollback — a reload that fails leaves the entry FAILED and visible in the loader status projection; the previous bundle is not restored automatically.
- Rebuilt frames do not replace the boot graph — each frame carries the plugin-artifact revision needed for its one-resource combo reload; a page reload receives the recomposed startup graph.
Dev Note
Working context for maintainers — click to expand
None.