@deepseek-ai/dsh-client-modules 0.1.6-alpha.1 → 0.1.6-alpha.2
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 +16 -5
- package/README.zh.md +16 -5
- package/lib/client.js +407 -26
- package/lib/index.js +139 -72
- package/lib/types/client/entries.d.ts +76 -0
- package/lib/types/client/entry-lifecycle.d.ts +14 -0
- package/lib/types/client/index.d.ts +2 -0
- package/lib/types/client/manifest.d.ts +26 -14
- package/lib/types/client/system.d.ts +13 -8
- package/lib/types/index.d.ts +12 -7
- package/package.json +5 -4
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/modules/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: 5b751268430b716e7a0af24f8e25aaf2f865d079
|
|
6
|
+
README.zh.md: 4152d64ca9dde60ab1aeb76c98508d8d86fcf54e
|
package/README.md
CHANGED
|
@@ -35,7 +35,11 @@ A browser plugin package declares `dsh.client` in its `package.json` with `platf
|
|
|
35
35
|
|
|
36
36
|
### What the browser loads
|
|
37
37
|
|
|
38
|
-
The application combo scripts
|
|
38
|
+
The application combo scripts carry only each plugin's `client.js` entry and register those factories once during boot; module bodies remain lazy and run only at first import or materialization. A source `import()` split by tsdown compiles to `require.async("./client.<name>.js")`; its versioned sibling script arrives only when that expression runs. Rows that share a combo URL share one in-flight script task. HMR switches one changed row to its revisioned one-resource combo URL. `<id>/client` and the bare id resolve to the same exports, because a plugin bundle is its package's client half.
|
|
39
|
+
|
|
40
|
+
### Live plugin composition
|
|
41
|
+
|
|
42
|
+
An open Web page follows the Host's complete module graph through the HMR transport. Enabling an ordinary plugin adds its Loader entry; disabling it removes the entry and waits for its asynchronous effects before evicting unused modules and styles. Re-enabling loads one instance with its styles. Other Loader contributors and shared modules still needed by active entries remain loaded. Settings → Plugins → Plugin list shows page-local synchronization failures and offers retry without changing Host enablement.
|
|
39
43
|
|
|
40
44
|
### Sharing modules
|
|
41
45
|
|
|
@@ -61,13 +65,13 @@ The package has two sides: the Node half is the composition and serving side (`c
|
|
|
61
65
|
|
|
62
66
|
### Lazy-CJS model
|
|
63
67
|
|
|
64
|
-
Executing a plugin bundle only registers its factory; every module-body side effect (CSS injection included) lives in the factory closure and runs at materialization (`factory(require)` → exports, memoized in `loadCache`). A factory that requires another registered-but-unmaterialized module materializes it recursively; require cycles throw because factory-form CJS cannot deliver partial exports. Resolution checks the platform seed table, memoized records, boot-graph rows, and registered factories in that order; anything else throws. The synchronous `require` uses the same order without asynchronous graph-row loading and records observed edges into the module record.
|
|
68
|
+
Executing a plugin bundle only registers its factory; every module-body side effect (CSS injection included) lives in the factory closure and runs at materialization (`factory(require)` → exports, memoized in `loadCache`). A factory that requires another registered-but-unmaterialized module materializes it recursively; require cycles throw because factory-form CJS cannot deliver partial exports. Resolution checks the platform seed table, memoized records, boot-graph rows, and registered factories in that order; anything else throws. The synchronous `require` uses the same order without asynchronous graph-row loading and records observed edges into the module record. Its `require.async` operation returns a Promise and fetches a compiler-generated package-local chunk before materializing that chunk once. This protocol supports self-contained chunks only: entry and chunk outputs cannot synchronously require another relative `client*.js` output.
|
|
65
69
|
|
|
66
70
|
### Incremental composition
|
|
67
71
|
|
|
68
72
|
The Node half scans incrementally per package — no full-rescan path. Every `internal/plugin` emission marks the fiber's entry name dirty; a microtask flush reconciles each dirty name against the live loader entries, and the activation pass seeds the same dirty set and flushes synchronously, so first scan and steady state share one implementation. Package metadata is cached per Loader specifier and owning-tree base URL until restart, while the resolved manifest package name identifies the browser module. Distinct active Loader sources resolving to one package name are rejected; removing the conflict promotes the remaining source without requiring its fiber to restart. Bundle content changes reach the graph only through `rebuilt()` (the HMR hook).
|
|
69
73
|
|
|
70
|
-
The Node half snapshots each client
|
|
74
|
+
The Node half snapshots each `client.js` entry before publication and creates combo descriptors without building response bodies. It groups resources into `/plugins/??...&rev=...` combo URLs, with one bootstrap combo for the modules row and one or more application combos for the other rows; each phase is partitioned before a URL exceeds 3 KiB. A script body is combined once on its first `GET` and ends with its map URL. The corresponding map files are read, validated, and combined separately on the first map `GET`; `HEAD` materializes neither body. The Host does not scan or preload sibling chunks: an exact `/plugins/<package>/client.<name>.js?rev=<rev>` request reads and caches that script, and its map remains uncomputed until the map URL is requested. Every combo or chunk map is Indexed Source Map v3 and uses an authored section when available or an identity section for the packaged bundle. Initial per-plugin revisions use process nonces. During development, the shared preset stamps `client.js` after every package output is written; HMR derives the next revision from the entry bytes and that completed-build stamp, so a chunk-only rebuild changes the owner revision without a Host-side chunk scan. Combo revisions derive from the ordered row revisions. Advertised combo responses and requested chunk responses remain immutable across unrelated graph recomposition, and an unknown resource or revision returns 404.
|
|
71
75
|
|
|
72
76
|
### Boot manifest injection
|
|
73
77
|
|
|
@@ -75,6 +79,10 @@ The bundle route follows the injected `webServer` lifetime: it registers when th
|
|
|
75
79
|
|
|
76
80
|
The host contributes structured index rows that inject, into `<head>`: the `window.__ModuleLoader__` queue facade, advisory preloads for every application combo, the parser-blocking bootstrap combo scripts, then the boot graph before the shell reads it. A Web carrier renders those rows into its index response; a shell-owned carrier can render the same rows without a Web server. The facade's `create()` materializes the modules bundle, delegates construction to its `createClientModuleSystem` export, and leaves the same facade in live-registration mode. The shell installs that returned system as its Loader's `internal`; the modules plugin publishes that instance as `ctx.modules`, so separate Cordis trees never select an instance through module-global state.
|
|
77
81
|
|
|
82
|
+
### Entry ownership
|
|
83
|
+
|
|
84
|
+
`ClientEntries` records the entries created during boot and serializes full-graph updates, retries and code reloads over the same Loader. A local generation prevents an older download from mounting after its desired entry or code changes; snapshots of the same targets share the pending load. New arrivals use single-resource URLs, never startup batches that could register existing factories twice. Factories retain their artifact revision before an entry exists; graph updates discard stale unowned factories, their styles and failed arrival targets before importing consumers. Cleanup retains declared and observed transitive module requests from every remaining Loader entry. Its observable status has no runtime library import because the modules bootstrap materializes before platform seeds are available.
|
|
85
|
+
|
|
78
86
|
### Source map
|
|
79
87
|
|
|
80
88
|
| File | Role |
|
|
@@ -82,6 +90,8 @@ The host contributes structured index rows that inject, into `<head>`: the `wind
|
|
|
82
90
|
| [`src/index.ts`](src/index.ts) | Node half: `ClientModuleRegistry`, scan, artifact snapshots, optional combo route, structured index rows |
|
|
83
91
|
| [`src/client/index.ts`](src/client/index.ts) | Browser half: bootstrap export, `ctx.modules` enrollment |
|
|
84
92
|
| [`src/client/system.ts`](src/client/system.ts) | `ClientModuleSystem`: load/materialize/invalidate machinery |
|
|
93
|
+
| [`src/client/entries.ts`](src/client/entries.ts) | Page entry reconciliation, retries and code replacement |
|
|
94
|
+
| [`src/client/entry-lifecycle.ts`](src/client/entry-lifecycle.ts) | Loader fiber teardown through the registry and owned-style cleanup |
|
|
85
95
|
| [`src/client/manifest.ts`](src/client/manifest.ts) | Wire types, boot-manifest parsing, and the `dsh.client` declaration parser |
|
|
86
96
|
|
|
87
97
|
</details>
|
|
@@ -118,8 +128,9 @@ None; this package neither assembles nor sends a provider request.
|
|
|
118
128
|
These limits define what the module system does not do. They are current package constraints, not a task backlog.
|
|
119
129
|
|
|
120
130
|
- **Flat module graph by design** — every bundle is one module node whose edges point only at table leaves; the interface (`loadCache`/`edges`/`invalidate`) already supports a general module graph, so the externalization granularity can change without an interface change.
|
|
121
|
-
- **
|
|
122
|
-
- **
|
|
131
|
+
- **Bootstrap and code replacement limits** — the page retains its modules bootstrap and static platform identities. Removing or replacing the bootstrap requires a page reload; live replacement requests report a page-local error while retaining its fiber and exports; replacing package code and all existing consumers is outside ordinary enable/disable synchronization.
|
|
132
|
+
- **Lazy delivery retains requested bodies** — the Host holds each bundle and lazy response plan; a script or map body remains cached after its first `GET`, and HMR additionally retains one prior startup generation. Memory grows only for response bodies that clients request while preserving one-generation race tolerance.
|
|
133
|
+
- **An unrequested prior-generation map reads the current map file** — combo revisions track executable bundles, not debug artifacts. If HMR rebuilds a map before the retained prior URL receives its first map `GET`, that response uses the current authored map with the prior bundle offsets; requesting the map before the rebuild fixes that URL's response.
|
|
123
134
|
|
|
124
135
|
<a id="dev-note"></a>
|
|
125
136
|
### Dev Note
|
package/README.zh.md
CHANGED
|
@@ -35,7 +35,11 @@ kind: "package-reference"
|
|
|
35
35
|
|
|
36
36
|
### 浏览器加载什么
|
|
37
37
|
|
|
38
|
-
application combo
|
|
38
|
+
application combo 脚本只携带每个插件的 `client.js` 入口,并在启动时仅注册一次这些 factory;模块主体仍保持惰性,只在首次 import 或物化时运行。经 tsdown 拆分的源码 `import()` 会编译为 `require.async("./client.<name>.js")`;只有执行该表达式时,对应的带版本同级脚本才会到达。共享 combo URL 的 row 共用一个进行中的脚本任务。HMR(热模块替换)会让一条发生变化的 row 改用带 revision 的单资源 combo URL。`<id>/client` 与裸 id 解析到同一组导出,因为插件 bundle 就是其包的客户端半侧。
|
|
39
|
+
|
|
40
|
+
### 插件动态组合
|
|
41
|
+
|
|
42
|
+
已打开的 Web 页面通过 HMR 传输跟随 Host 的完整模块图。启用普通插件会添加其 Loader 条目;停用会移除条目,并在其异步 effect 完成清理后回收未使用的模块与样式。再次启用会加载一个带样式的实例。其他 Loader 贡献方的条目及活动条目仍需使用的共享模块会保留。「设置 → 插件 → 插件列表」显示当前页面的同步失败,并提供不改变 Host 启用状态的重试。
|
|
39
43
|
|
|
40
44
|
### 共享模块
|
|
41
45
|
|
|
@@ -61,13 +65,13 @@ application combo 脚本在启动时仅注册一次插件 factory;模块主体
|
|
|
61
65
|
|
|
62
66
|
### 惰性 CJS 模型
|
|
63
67
|
|
|
64
|
-
执行插件 bundle 只注册其 factory;每个模块主体副作用(包括 CSS 注入)都位于 factory 闭包中,在物化时运行(`factory(require)` → 导出,在 `loadCache` 中记忆化)。factory 依赖另一个已注册但未物化的模块时会递归物化它;require 循环会抛出异常,因为 factory 形式的 CJS 无法提供部分导出。解析会依次检查平台 seed 表、已记忆记录、启动图 row 与已注册 factory;其他情况一律抛错。交给 factory 的同步 `require` 使用相同顺序,但不含异步图 row
|
|
68
|
+
执行插件 bundle 只注册其 factory;每个模块主体副作用(包括 CSS 注入)都位于 factory 闭包中,在物化时运行(`factory(require)` → 导出,在 `loadCache` 中记忆化)。factory 依赖另一个已注册但未物化的模块时会递归物化它;require 循环会抛出异常,因为 factory 形式的 CJS 无法提供部分导出。解析会依次检查平台 seed 表、已记忆记录、启动图 row 与已注册 factory;其他情况一律抛错。交给 factory 的同步 `require` 使用相同顺序,但不含异步图 row 加载,并把观察到的边记录到模块记录中。它的 `require.async` 操作返回 Promise,并在一次性物化编译器生成的包内 chunk 前先获取该 chunk。该协议只支持自包含 chunk:入口与 chunk 产物不能同步 require 另一个相对 `client*.js` 产物。
|
|
65
69
|
|
|
66
70
|
### 增量组合
|
|
67
71
|
|
|
68
72
|
Node 半侧逐包增量扫描——没有全量重扫路径。每次发出 `internal/plugin` 事件时,系统都会把该 fiber 的 entry 名标脏;微任务 flush 会把每个脏名与当前 loader 条目对账,激活 pass 会初始化同一个脏集合并同步 flush,因此首次扫描与稳态共用同一实现。包元数据按 Loader specifier 与所属 tree base URL 缓存至重启,解析出的 manifest(元数据清单)包名作为浏览器模块身份。若不同的 active Loader source 解析到同一包名,组合会失败;移除冲突来源后,剩余来源无需重启 fiber 即可接替。bundle 内容变更只能通过 `rebuilt()`(HMR 钩子)进入图。
|
|
69
73
|
|
|
70
|
-
Node
|
|
74
|
+
Node 半侧会在发布前快照每个 `client.js` 入口,并在不构建响应 body 的情况下创建 combo descriptor。它把资源分组到 `/plugins/??...&rev=...` combo URL:modules row 使用一个 bootstrap combo,其余 row 使用一个或多个 application combo;每个阶段都会在 URL 超过 3 KiB 之前分区。脚本 body 在首次 `GET` 时只组合一次,并以对应 map URL 结尾;map 文件则在首次 map `GET` 时单独读取、校验并组合,`HEAD` 不会物化任一 body。Host 不扫描也不预加载同级 chunk:精确的 `/plugins/<package>/client.<name>.js?rev=<rev>` 请求会读取并缓存该脚本,其 map 仍会等到 map URL 被请求后才计算。每个 combo 或 chunk map 都是 Indexed Source Map v3,并在可用时使用作者提供的 section,否则为已打包 bundle 生成 identity section。初始逐插件 revision 使用进程 nonce。开发期间,共享预设会在该包所有输出写完后标记 `client.js`;HMR 从入口字节和该构建完成标记派生下一 revision,因此仅 chunk 发生重建也会更换 owner revision,无需 Host 扫描 chunk。combo revision 从有序 row revision 派生。已公告的 combo 响应与已请求的 chunk 响应会跨无关图重组保持不可变;未知资源或 revision 返回 404。
|
|
71
75
|
|
|
72
76
|
### 启动 manifest 注入
|
|
73
77
|
|
|
@@ -75,6 +79,10 @@ bundle 路由随注入的 `webServer` 生命周期注册:服务就绪时注册
|
|
|
75
79
|
|
|
76
80
|
宿主贡献结构化 index 行,并向 `<head>` 注入:`window.__ModuleLoader__` queue facade、每个 application combo 的提示性 preload、阻塞 parser 的 bootstrap combo 脚本,然后才是外壳读取前的启动图。Web 载体把这些行渲染进 index 响应;由 shell 持有的载体则可以在没有 Web server 时渲染同一批行。facade 的 `create()` 物化 modules bundle、把构造委托给其 `createClientModuleSystem` 导出,并让同一 facade 进入 live registration 模式。外壳把返回的系统装成自身 Loader 的 `internal`;modules 插件将该实例发布为 `ctx.modules`,因此不同 Cordis 树不会通过模块级全局状态选择实例。
|
|
77
81
|
|
|
82
|
+
### 条目所有权
|
|
83
|
+
|
|
84
|
+
`ClientEntries` 记录启动时创建的条目,并在同一个 Loader 上串行执行完整图更新、重试和代码重载。本地代际阻止旧下载在目标条目或代码变化后挂载;目标相同的快照共用进行中的加载。新增模块使用单资源 URL,不会重新执行可能重复注册现有 factory 的启动 batch。Factory 在条目创建前就保留产物 revision;图更新会在导入消费者前丢弃未归属条目的陈旧 factory、其样式和失败的到达目标。清理会保留每个剩余 Loader 条目的已声明及已观察到的传递模块依赖。其可观察状态不导入运行时库,因为 modules bootstrap 在平台种子可用之前物化。
|
|
85
|
+
|
|
78
86
|
### 源码索引
|
|
79
87
|
|
|
80
88
|
| 文件 | 职责 |
|
|
@@ -82,6 +90,8 @@ bundle 路由随注入的 `webServer` 生命周期注册:服务就绪时注册
|
|
|
82
90
|
| [`src/index.ts`](src/index.ts) | Node 半侧:`ClientModuleRegistry`、扫描、产物快照、可选 combo 路由、结构化 index 行 |
|
|
83
91
|
| [`src/client/index.ts`](src/client/index.ts) | 浏览器半侧:bootstrap 导出、`ctx.modules` 登记 |
|
|
84
92
|
| [`src/client/system.ts`](src/client/system.ts) | `ClientModuleSystem`:加载/物化/失效机制 |
|
|
93
|
+
| [`src/client/entries.ts`](src/client/entries.ts) | 页面条目对账、重试与代码替换 |
|
|
94
|
+
| [`src/client/entry-lifecycle.ts`](src/client/entry-lifecycle.ts) | 通过注册表清理 Loader fiber,回收模块自身样式 |
|
|
85
95
|
| [`src/client/manifest.ts`](src/client/manifest.ts) | 协议类型、启动清单解析与 `dsh.client` 声明解析器 |
|
|
86
96
|
|
|
87
97
|
</details>
|
|
@@ -118,8 +128,9 @@ bundle 路由随注入的 `webServer` 生命周期注册:服务就绪时注册
|
|
|
118
128
|
这些限制说明模块系统不做什么。它们是当前包约束,不是任务积压。
|
|
119
129
|
|
|
120
130
|
- **有意采用扁平模块图**——每个 bundle 是一个模块节点,其边只指向表中的叶节点;接口(`loadCache`/`edges`/`invalidate`)已经支持通用模块图,因此可以改变 externalization 粒度而不更改接口。
|
|
121
|
-
-
|
|
122
|
-
-
|
|
131
|
+
- **Bootstrap 与代码替换限制**——页面保留 modules bootstrap 和静态平台模块的身份。移除或替换 bootstrap 需要刷新页面;动态替换请求会报告页面本地错误,并保留其 fiber 与导出;替换包代码及其所有现有消费者不属于普通启停同步。
|
|
132
|
+
- **惰性提供会保留已请求的 body**——Host 在内存中保留每个 bundle 与惰性响应计划;脚本或 map body 在首次 `GET` 后保留缓存,HMR 还会保留上一代启动响应。内存仅随客户端实际请求的响应 body 增长,同时保留一代竞态容忍。
|
|
133
|
+
- **从未请求的上一代 map 会读取当前 map 文件**——combo revision 跟踪可执行 bundle,而不跟踪调试产物。若 HMR 在保留的旧 URL 首次收到 map `GET` 前重建 map,该响应会把当前 authored map 与旧 bundle offset 组合;在重建前请求 map 会固定该 URL 的响应。
|
|
123
134
|
|
|
124
135
|
<a id="dev-note"></a>
|
|
125
136
|
### 开发备注
|