@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 +2 -2
- package/README.md +1 -1
- package/README.zh.md +1 -1
- package/lib/index.js +12 -10
- package/lib/types/index.d.ts +1 -1
- package/package.json +8 -8
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:
|
|
6
|
-
README.zh.md:
|
|
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.
|
|
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
|
-
|
|
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
|
-
*
|
|
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 = ["
|
|
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
|
|
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.
|
|
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.
|
|
93
|
-
const path = ctx.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
145
|
+
const unsubscribe = ctx.clientModules.onRebuilt((id, rev) => {
|
|
144
146
|
const line = sseData({
|
|
145
147
|
type: "rebuilt",
|
|
146
148
|
id,
|
package/lib/types/index.d.ts
CHANGED
|
@@ -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
|
|
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.
|
|
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-
|
|
46
|
-
"@deepseek-ai/
|
|
47
|
-
"@deepseek-ai/dsh-
|
|
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-
|
|
53
|
-
"@deepseek-ai/dsh-
|
|
54
|
-
"@deepseek-ai/dsh-invariants": "^0.0.1-rc.
|
|
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": [
|