@deepseek-ai/dsh-client-hmr 0.0.1-rc.1 → 0.0.1-rc.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/client/hmr/README.md
5
- README.md: 9228292547376d3fbb0ea5ce56b9e0a35ced17b2
6
- README.zh.md: ea62600911458556a3dcc7c46854e97db751c3ef
5
+ README.md: c355595dd53ddcb74be629a6d5e730c6c5fcebbf
6
+ README.zh.md: 6ed4d0e79cb755f84784823749994b448ff209b8
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  English | [中文](README.zh.md)
4
4
 
5
- Hot reload for script-loaded 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.
5
+ Hot reload for script-loaded client plugins. The web bundle mounts the row unconditionally; without a rebuild watcher (`pnpm run dev:web`) rewriting client bundles, the poll observes no changes and the chain stays idle.
6
6
 
7
7
  The browser half subscribes to the system SSE channel (`GET /plugins/events`) and reloads one plugin per `rebuilt` frame through a serialized queue. The sequence per frame — `invalidate`, `prefetch` (load and register the new bundle while the old fiber still serves), `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. The node half detects rebuilds with one interval that stat-polls each graph bundle from a synchronous baseline, immediately re-hashes after adding a row, retains missing rows as dirty, and broadcasts only real rev changes; any tsdown watch process producing the bundle therefore triggers HMR with no builder→host channel.
8
8
 
package/README.zh.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [English](README.md) | 中文
4
4
 
5
- 为通过脚本加载的客户端插件提供热重载。该静态加载配置项只组合进 `--dev` 图(`dsh web --dev`);生产图省略该项,因此打包进 shell 的代码保持不活动。
5
+ 为通过脚本加载的客户端插件提供热重载。web 组合包无条件挂载该行;没有重建 watcher(`pnpm run dev:web`)改写客户端 bundle 时,轮询观察不到变化,链路保持空闲。
6
6
 
7
7
  浏览器侧订阅系统 SSE(Server-Sent Events)通道(`GET /plugins/events`),每个 `rebuilt` 帧重载一个插件,并通过队列串行执行。每帧的顺序是:`invalidate`、`prefetch`(旧 fiber 仍在服务时加载并注册新组合包)、`registry.delete`(在 fiber dispose(资源释放)之前执行:仅 dispose fiber 会触发 vendored Loader 的 self-dispose 分支,把配置项标为禁用)、排空旧 fiber、删除 `entry.fiber`、移除自身拥有的 `<style data-plugin>` 标签、通过 `entry.refresh()` 重新导入并挂载、通过 `fiber.await()` 直接重新抛出启动失败。依赖方由 Cordis 自身重载:fiber 的激活 epoch 会串联其服务提供方的 uid,因此替换提供方 fiber 会级联所有依赖方,无需客户端图分析。node 侧使用一个 interval 检测重建:从同步基线开始 stat-poll 每个图组合包;新增一行后立即重新计算 hash;缺失行保持 dirty;只广播真实 rev 变更。因此,任何生成组合包的 tsdown watch 进程都能触发 HMR(热模块替换),无需 builder→host 通道。
8
8
 
package/lib/index.js CHANGED
@@ -17,12 +17,14 @@ const EVENTS_ENDPOINT = "/plugins/events";
17
17
  * mounts deliver no inotify events), reports content changes through
18
18
  * `clientModuleHost.rebuilt(id)`, and serves the `/plugins/events` SSE channel
19
19
  * broadcasting graph/rebuilt frames to the browser half (src/client/).
20
- * Dev-only row: prod compositions never mount this plugin.
20
+ * The web bundle mounts this row unconditionally: without a rebuild
21
+ * watcher rewriting client bundles, the poll observes no changes and the
22
+ * chain stays idle.
21
23
  */
22
24
  /** Cordis plugin name. */
23
25
  const name = "client-hmr";
24
26
  /** Required services: the web plugin table and the route registry. */
25
- const inject = ["clientModuleHost", "httpServer"];
27
+ const inject = ["clientModules", "webServer"];
26
28
  const Config = z.object({ pollIntervalMs: z.number().step(1).min(1).default(500) });
27
29
  /** Serialize one frame as an SSE data line. */
28
30
  function sseData(frame) {
@@ -30,7 +32,7 @@ function sseData(frame) {
30
32
  }
31
33
  /**
32
34
  * Mount the dev chain: bundle watches, rebuilt reporting, and the SSE channel.
33
- * @param ctx - host plugin context carrying clientModuleHost and httpServer.
35
+ * @param ctx - host plugin context carrying clientModuleHost and webServer.
34
36
  * @param config - validated {@link Config}.
35
37
  */
36
38
  function apply(ctx, config) {
@@ -38,7 +40,7 @@ function apply(ctx, config) {
38
40
  const watched = /* @__PURE__ */ new Map();
39
41
  const rehash = (id, watch, current) => {
40
42
  try {
41
- ctx.clientModuleHost.rebuilt(id);
43
+ ctx.clientModules.rebuilt(id);
42
44
  } catch (error) {
43
45
  if (error.code === "ENOENT") {
44
46
  watch.dirty = true;
@@ -89,8 +91,8 @@ function apply(ctx, config) {
89
91
  };
90
92
  const syncWatches = () => {
91
93
  const rows = /* @__PURE__ */ new Map();
92
- for (const row of ctx.clientModuleHost.graph().entries) {
93
- const path = ctx.clientModuleHost.clientPath(row.id);
94
+ for (const row of ctx.clientModules.graph().entries) {
95
+ const path = ctx.clientModules.clientPath(row.id);
94
96
  if (path !== void 0) rows.set(row.id, path);
95
97
  }
96
98
  for (const [id, watch] of watched) {
@@ -101,7 +103,7 @@ function apply(ctx, config) {
101
103
  };
102
104
  ctx.effect(() => {
103
105
  syncWatches();
104
- const unsubscribe = ctx.clientModuleHost.onGraphChanged(syncWatches);
106
+ const unsubscribe = ctx.clientModules.onGraphChanged(syncWatches);
105
107
  const timer = setInterval(pollWatches, pollIntervalMs);
106
108
  timer.unref();
107
109
  return () => {
@@ -120,7 +122,7 @@ function apply(ctx, config) {
120
122
  res.write(": connected\n\n");
121
123
  res.write(sseData({
122
124
  type: "graph",
123
- graph: ctx.clientModuleHost.graph()
125
+ graph: ctx.clientModules.graph()
124
126
  }));
125
127
  connections.add(res);
126
128
  res.on("close", () => {
@@ -128,7 +130,7 @@ function apply(ctx, config) {
128
130
  });
129
131
  };
130
132
  ctx.effect(() => {
131
- const disposeRoute = ctx.httpServer.register({
133
+ const disposeRoute = ctx.webServer.register({
132
134
  kind: "exact",
133
135
  path: EVENTS_ENDPOINT,
134
136
  handler: (req, res) => {
@@ -140,7 +142,7 @@ function apply(ctx, config) {
140
142
  connect(res);
141
143
  }
142
144
  });
143
- const unsubscribe = ctx.clientModuleHost.onRebuilt((id, rev) => {
145
+ const unsubscribe = ctx.clientModules.onRebuilt((id, rev) => {
144
146
  const line = sseData({
145
147
  type: "rebuilt",
146
148
  id,
@@ -14,7 +14,7 @@ export interface Config {
14
14
  export declare const Config: z<Config>;
15
15
  /**
16
16
  * Mount the dev chain: bundle watches, rebuilt reporting, and the SSE channel.
17
- * @param ctx - host plugin context carrying clientModuleHost and httpServer.
17
+ * @param ctx - host plugin context carrying clientModuleHost and webServer.
18
18
  * @param config - validated {@link Config}.
19
19
  */
20
20
  export declare function apply(ctx: Context, config: Config): void;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-client-hmr",
3
3
  "description": "Dev-only hot-reload driver for script-loaded client entries: SSE rebuilt frames → invalidate/prefetch → fiber swap through the vendored Loader entry",
4
- "version": "0.0.1-rc.1",
4
+ "version": "0.0.1-rc.3",
5
5
  "publishConfig": {
6
6
  "access": "restricted"
7
7
  },
@@ -41,17 +41,17 @@
41
41
  "@deepseek-ai/schemastery": "^3.18.1-rc.1"
42
42
  },
43
43
  "peerDependencies": {
44
+ "@deepseek-ai/dsh-client-modules": "^0.0.1-rc.3",
44
45
  "@deepseek-ai/cordis-plugin-loader": "^1.0.1-rc.1",
45
- "@deepseek-ai/dsh-client-modules": "^0.0.1-rc.1",
46
- "@deepseek-ai/dsh-host-webserver": "^0.0.1-rc.1",
47
- "@deepseek-ai/dsh-invariants": "^0.0.1-rc.1",
48
- "@deepseek-ai/cordis": "^4.0.1-rc.1"
46
+ "@deepseek-ai/dsh-invariants": "^0.0.1-rc.3",
47
+ "@deepseek-ai/cordis": "^4.0.1-rc.1",
48
+ "@deepseek-ai/dsh-host-webserver": "^0.0.1-rc.3"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@deepseek-ai/cordis-plugin-loader": "^1.0.1-rc.1",
52
- "@deepseek-ai/dsh-client-modules": "^0.0.1-rc.1",
53
- "@deepseek-ai/dsh-host-webserver": "^0.0.1-rc.1",
54
- "@deepseek-ai/dsh-invariants": "^0.0.1-rc.1",
52
+ "@deepseek-ai/dsh-host-webserver": "^0.0.1-rc.3",
53
+ "@deepseek-ai/dsh-client-modules": "^0.0.1-rc.3",
54
+ "@deepseek-ai/dsh-invariants": "^0.0.1-rc.3",
55
55
  "@deepseek-ai/cordis": "^4.0.1-rc.1"
56
56
  },
57
57
  "files": [