client-hmr is a normal plugin package composed into dev graphs only. It listens on /plugins/events and reloads one plugin per rebuilt frame, serialized: invalidate, prefetch (fresh factory registers while the old fiber still serves), registry.delete before touching the fiber, drain disposers, drop owned style tags, entry.refresh(), fiber.await() loud. Dependency cascade costs zero client code — fiber activation epochs re-load dependents through cordis itself. Reload is coarse by design; no rollback in v1; self-reload works with a frame gap the next rebuild heals. scripts/dev-web.ts (pnpm run dev:web) is the convenience watch-build: it discovers its package list by scanning packages/*/*/package.json for dshClient platform "web" at startup — no hardcoded roster — and talks no protocol to the host. Gate bookkeeping rides along: knip entries for the new packages, README model-experience allowlist rows.
20 lines
2.2 KiB
Markdown
20 lines
2.2 KiB
Markdown
# @deepseek-ai/dsh-client-hmr
|
|
|
|
Hot reload for fetch-arrival client plugins. A static-arrival entry composed only into `--dev` graphs (`dsh web --dev`); production graphs omit the row, so the shell-bundled code stays inert.
|
|
|
|
The plugin subscribes to the webserver's system SSE channel (`GET /plugins/events`) and reloads one plugin per `rebuilt` frame, serialized through a queue (the bundle handoff slot is single). The sequence per frame — `prefetch` (fetch the new bundle before touching anything), `invalidate`, `registry.delete` (before the fiber: a bare fiber dispose trips the vendored Loader's self-dispose branch, which would mark the entry disabled), drain the old fiber, delete `entry.fiber`, remove owned `<style data-plugin>` tags, `entry.refresh()` re-imports and remounts, `fiber.await()` rethrows startup failures loud. Dependents reload through cordis itself: a fiber's activation epoch strings its service providers' uids, so replacing a provider's fiber cascades every dependent with zero client-side graph analysis. Rebuild detection lives on the webserver: in dev mode it stat-polls each plugin's built `lib/client.js` (`fs.watchFile`) and broadcasts the `rebuilt` frame when the bundle's rev changes, so any tsdown watch process producing the bundle triggers HMR with no builder→host channel.
|
|
|
|
## Model Experience
|
|
|
|
None, as the reload driver is browser-side machinery; nothing here reaches a model request.
|
|
|
|
#### KV Cache effect
|
|
|
|
None; this package neither assembles nor sends a provider request.
|
|
|
|
## Known Limitations and Deferred Work
|
|
|
|
- **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 re-runs the factory" and is deliberately out.
|
|
- **No failure rollback** — a reload that fails leaves the entry FAILED and loud in the loader status projection; restoring the previous bundle automatically is deferred until a real need shows.
|
|
- **Graph rev is not refreshed by rebuilt frames** — the stale rev is harmless (the bundle endpoint serves no-cache); rev refresh lands with the reconnect-handshake mechanism.
|