@deepseek-ai/dsh-client-modules 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/modules/README.md
5
- README.md: a1d578850c2518a85dc32f048768b78caf5ffec4
6
- README.zh.md: 772a4870f7ef6730d9d3d4db434ed771d97984f0
5
+ README.md: efaff699839b977cc45f89f3c164402241b90dc2
6
+ README.zh.md: 6ab3c8494d7ea902e8b891008f236aec41301a35
package/README.md CHANGED
@@ -4,7 +4,7 @@ English | [中文](README.zh.md)
4
4
 
5
5
  Client module system: the browser peer of Node's internal ESM loader, built as a lazy CJS table. The web shell mounts the vendored cordis Loader for entry governance (fiber lifecycle, inject waiting, update/refresh) and injects this package's `ClientModuleLoader` through its `internal` contract — the vendored side's only consumption point is `EntryTree.import`, so replacing `internal` replaces exactly "how plugin code arrives" and nothing else.
6
6
 
7
- Lazy CJS model (web2): executing a plugin bundle only REGISTERS its factory (`window.__ModuleLoader__.load({id, factory})`); every module body side effect — CSS injection included — lives in the factory closure and runs at materialization (`factory(require)` → export surface, memoized in `loadCache`), not at script execution. A factory that requires another registered-but-unmaterialized module materializes it recursively, so load order needs no external sequencing; require cycles throw (factory-form CJS cannot deliver partial exports). `<id>/client` and the bare id name the same surface (a plugin bundle IS its package's client half).
7
+ Lazy CJS model (web2): executing a plugin bundle only REGISTERS its factory (`window.__ModuleLoader__.load({id, factory})`); every module body side effect — CSS injection included — lives in the factory closure and runs at materialization (`factory(require)` → exports, memoized in `loadCache`), not at script execution. A factory that requires another registered-but-unmaterialized module materializes it recursively, so load order needs no external sequencing; require cycles throw (factory-form CJS cannot deliver partial exports). `<id>/client` and the bare id resolve to the same exports (a plugin bundle IS its package's client half).
8
8
 
9
9
  Resolution branch order (`import(specifier)`): platform seed word → shell instance; memoized record → surface; shell-own static registry (`registerStatic`, app-shell) → module; registered factory → materialize; graph row (`window.__DSH_BOOT__`) → load its external classic script + materialize; anything else throws — the runtime mirror of the build-time bundle purity gate. The synchronous `require` handed to factories walks the same order minus the asynchronous load branch and records observed edges into the module record. `prefetch` is the stage-one arrival hook (script load and factory registration only; concurrent calls share one in-flight task); `invalidate` drops the factory and materialized record so the next prefetch/import reloads the script (the HMR hook).
10
10
 
package/README.zh.md CHANGED
@@ -2,13 +2,13 @@
2
2
 
3
3
  [English](README.md) | 中文
4
4
 
5
- 客户端模块系统:Node 内部 ESM loader 的浏览器端对等实现,以惰性 CJS 表实现。web 外壳挂载 vendored cordis Loader 来治理配置项(fiber 生命周期、inject 等待、update/refresh),并通过其 `internal` 约定注入该包(package)的 `ClientModuleLoader`;vendored 一侧唯一的消费点是 `EntryTree.import`,因此替换 `internal` 恰好只会替换「插件代码如何到达」,不会改变其他内容。
5
+ 客户端模块系统:Node 内部 ESM loader 的浏览器端对等实现,以惰性 CJS 表实现。web 外壳挂载 vendored cordis Loader 来治理配置项(fiber 生命周期、inject 等待、update/refresh),并通过其 `internal` 约定注入该包的 `ClientModuleLoader`;vendored 一侧唯一的消费点是 `EntryTree.import`,因此替换 `internal` 恰好只会替换「插件代码如何到达」,不会改变其他内容。
6
6
 
7
- 惰性 CJS 模型(web2):执行插件组合包只会注册其 factory(`window.__ModuleLoader__.load({id, factory})`);每个模块主体的副作用(包括 CSS 注入)都位于 factory 闭包中,在物化时运行(`factory(require)` → 导出表层,并在 `loadCache` 中记忆化),不会在脚本执行时运行。如果 factory 依赖另一个已注册但尚未物化的模块,系统会递归物化它,因此加载顺序无需外部编排;require 循环会抛出异常(factory 形式的 CJS 无法提供部分导出)。`<id>/client` 与裸 id 指向同一表层(一个插件组合包就是其包的客户端侧)。
7
+ 惰性 CJS 模型(web2):执行插件 bundle 只会注册其 factory(`window.__ModuleLoader__.load({id, factory})`);每个模块主体的副作用(包括 CSS 注入)都位于 factory 闭包中,在物化时运行(`factory(require)` → 导出表层,并在 `loadCache` 中记忆化),不会在脚本执行时运行。如果 factory 依赖另一个已注册但尚未物化的模块,系统会递归物化它,因此加载顺序无需外部编排;require 循环会抛出异常(factory 形式的 CJS 无法提供部分导出)。`<id>/client` 与裸 id 指向同一表层(一个插件 bundle 就是其包的客户端侧)。
8
8
 
9
- 解析分支顺序(`import(specifier)`):平台种子词 → 外壳实例;记忆化记录 → 表层;外壳自身的静态注册表(`registerStatic`,app-shell)→ 模块;已注册 factory → 物化;模块图记录(`window.__DSH_BOOT__`)→ 加载外部 classic script + 物化;其他情况一律抛出异常。这是构建时组合包纯度门禁的运行时镜像。交给 factory 的同步 `require` 采用相同顺序,但不含异步加载分支,并把观察到的边记录到模块记录中。`prefetch` 是第一阶段到达钩子(只加载脚本并注册 factory;并发调用共享一个进行中的任务);`invalidate` 会丢弃 factory 与物化记录,使下一次 prefetch/import 重新加载脚本;它是 HMR(热模块替换)钩子。
9
+ 解析分支顺序(`import(specifier)`):平台种子词 → 外壳实例;记忆化记录 → 表层;外壳自身的静态注册表(`registerStatic`,app-shell)→ 模块;已注册 factory → 物化;模块图记录(`window.__DSH_BOOT__`)→ 加载外部 classic script + 物化;其他情况一律抛出异常。这是构建时 bundle 纯度门禁的运行时镜像。交给 factory 的同步 `require` 采用相同顺序,但不含异步加载分支,并把观察到的边记录到模块记录中。`prefetch` 是第一阶段到达钩子(只加载脚本并注册 factory;并发调用共享一个进行中的任务);`invalidate` 会丢弃 factory 与物化记录,使下一次 prefetch/import 重新加载脚本;它是 HMR(热模块替换)钩子。
10
10
 
11
- Node 侧会扫描已启用的 Loader 配置项以发现 web `dsh.client` 包,解析每个 `exports["./client"]`,把构建后的组合包哈希写入启动图,并通过 `/plugins` 提供该文件及其 sourcemap。源码启动会把宿主侧导入映射到 TypeScript 源码,但仍消费这一构建后的客户端导出;缺失文件共享一条构建说明,随后以 package/path list 列出各项,而无关的文件系统错误仍是独立故障。
11
+ Node 侧会扫描已启用的 Loader 配置项以发现 web `dsh.client` 包,解析每个 `exports["./client"]`,把构建后的 bundle 哈希写入启动图,并通过 `/plugins` 提供该文件及其 sourcemap。源码启动会把宿主侧导入映射到 TypeScript 源码,但仍消费这一构建后的客户端导出;缺失文件共享一条构建说明,随后以包/路径列表列出各项,而无关的文件系统错误仍是独立故障。
12
12
 
13
13
  ## 模型体验
14
14
 
@@ -20,5 +20,5 @@ Node 侧会扫描已启用的 Loader 配置项以发现 web `dsh.client` 包,
20
20
 
21
21
  ## 已知限制与暂缓事项
22
22
 
23
- - **有意采用扁平模块图**:每个组合包是一个模块节点,其边只指向表中的叶节点;接口(`loadCache`/`edges`/`invalidate`)已经支持通用模块图,因此可以改变 externalization 粒度而不更改接口。
24
- - **自身不记录卸载账目**:样式移除与 fiber 拆卸顺序属于 HMR 驱动器(`@deepseek-ai/dsh-client-hmr`);loader 只在每条记录中登记其拥有的样式标签 id。
23
+ - **有意采用扁平模块图**:每个 bundle 是一个模块节点,其边只指向表中的叶节点;接口(`loadCache`/`edges`/`invalidate`)已经支持通用模块图,因此可以改变 externalization 粒度而不更改接口。
24
+ - **自身不维护卸载记录**:样式移除与 fiber 拆卸顺序属于 HMR 驱动器(`@deepseek-ai/dsh-client-hmr`);loader 只在每条记录中登记其拥有的样式标签 id。
package/lib/client.js CHANGED
@@ -23,7 +23,7 @@ window.__ModuleLoader__.load({
23
23
  /**
24
24
  * A plugin bundle IS its package's client half: `<id>/client` (the exports
25
25
  * subpath external bundles emit) and the bare graph id name the same
26
- * surface, so table lookups normalize the suffix away.
26
+ * exports, so table lookups normalize the suffix away.
27
27
  */
28
28
  const stripClientSuffix = (spec) => spec.endsWith("/client") ? spec.slice(0, -7) : spec;
29
29
  /**
@@ -101,7 +101,7 @@ window.__ModuleLoader__.load({
101
101
  const edges = /* @__PURE__ */ new Set();
102
102
  const record = {
103
103
  id,
104
- surface: registered(this.makeRequire(edges)),
104
+ exports: registered(this.makeRequire(edges)),
105
105
  styles: claimStyles(id),
106
106
  edges
107
107
  };
@@ -125,31 +125,31 @@ window.__ModuleLoader__.load({
125
125
  if (this.statics.has(spec)) return this.statics.get(spec);
126
126
  const id = stripClientSuffix(spec);
127
127
  const record = this.loadCache.get(id);
128
- if (record !== void 0) return record.surface;
129
- if (this.factories.has(id)) return this.materialize(id).surface;
128
+ if (record !== void 0) return record.exports;
129
+ if (this.factories.has(id)) return this.materialize(id).exports;
130
130
  throw new Error(`client-modules: require("${spec}") missed the module table — not a platform seed word, not a shell-own module, and no registered factory (a build-time externals drift, or a forbidden cross-plugin value import)`);
131
131
  };
132
132
  }
133
133
  async import(specifier) {
134
134
  if (this.seed.has(specifier)) return this.seed.get(specifier);
135
135
  const existing = this.loadCache.get(specifier);
136
- if (existing !== void 0) return existing.surface;
136
+ if (existing !== void 0) return existing.exports;
137
137
  if (this.statics.has(specifier)) {
138
- const surface = this.statics.get(specifier);
138
+ const exports = this.statics.get(specifier);
139
139
  this.loadCache.set(specifier, {
140
140
  id: specifier,
141
- surface,
141
+ exports,
142
142
  styles: [],
143
143
  edges: /* @__PURE__ */ new Set()
144
144
  });
145
- return surface;
145
+ return exports;
146
146
  }
147
147
  if (!this.factories.has(specifier)) {
148
148
  const row = this.graphRows.get(specifier);
149
149
  if (row === void 0) throw new Error(`client-modules: cannot resolve "${specifier}" — not a seed word, not a shell-own module, and not a row in the boot graph (the runtime mirror of the bundle purity gate)`);
150
150
  await this.arrive(row);
151
151
  }
152
- return this.materialize(specifier).surface;
152
+ return this.materialize(specifier).exports;
153
153
  }
154
154
  registerStatic(id, module) {
155
155
  if (this.statics.has(id)) throw new Error(`client-modules: shell-own module "${id}" registered twice`);
@@ -180,13 +180,13 @@ window.__ModuleLoader__.load({
180
180
  * factory (`window.__ModuleLoader__.load({id, factory})`); every module body
181
181
  * side effect — including CSS injection — lives inside the factory closure
182
182
  * and runs at materialization, not at script execution. Materialization
183
- * (factory(require) → export surface) happens on first import/require and is
183
+ * (factory(require) → exports) happens on first import/require and is
184
184
  * memoized in {@link ClientModuleLoader.loadCache}; a factory that requires
185
185
  * another registered-but-unmaterialized module materializes it recursively,
186
186
  * so load order needs no external sequencing.
187
187
  *
188
188
  * Resolution branch order (import): seed word → shell instance; memoized
189
- * record → surface; static registry (shell-own modules, e.g. app-shell) →
189
+ * record → exports; static registry (shell-own modules, e.g. app-shell) →
190
190
  * module; registered factory → materialize; graph row → load + materialize;
191
191
  * anything else → throw (loud — the runtime mirror of the
192
192
  * build-time bundle purity gate). The synchronous `require` handed to
package/lib/index.js CHANGED
@@ -23,7 +23,7 @@ import { Service } from "@deepseek-ai/cordis";
23
23
  * negative "not a client package" verdict) is cached per name and never
24
24
  * expires — plugin-set changes take effect on restart; bundle content
25
25
  * changes reach the graph only through
26
- * {@link ClientModuleHostService.rebuilt}.
26
+ * {@link ClientModuleRegistry.rebuilt}.
27
27
  * @module @deepseek-ai/dsh-client-modules
28
28
  */
29
29
  /** Recovery instruction shared by grouped startup and steady-state bundle diagnostics. */
@@ -118,8 +118,8 @@ function injectBootManifest(html, graph) {
118
118
  * already-loaded entries aggregates into one loud throw (FAILED fiber; the
119
119
  * boot activation audit reports it).
120
120
  */
121
- var ClientModuleHostService = class extends Service {
122
- static inject = ["httpServer", "loader"];
121
+ var ClientModuleRegistry = class extends Service {
122
+ static inject = ["webServer", "loader"];
123
123
  table = /* @__PURE__ */ new Map();
124
124
  pkgMeta = /* @__PURE__ */ new Map();
125
125
  rebuildListeners = /* @__PURE__ */ new Set();
@@ -130,10 +130,10 @@ var ClientModuleHostService = class extends Service {
130
130
  composed;
131
131
  /**
132
132
  * Build the service: subscribe, seed, and run the activation flush.
133
- * @param ctx - plugin context carrying httpServer and loader.
133
+ * @param ctx - plugin context carrying webServer and loader.
134
134
  */
135
135
  constructor(ctx) {
136
- super(ctx, "clientModuleHost");
136
+ super(ctx, "clientModules");
137
137
  if (ctx.baseUrl === void 0) throw new Error("client-modules: ctx.baseUrl is unset — the node half needs the config-tree anchor to resolve plugin packages");
138
138
  const require = createRequire(ctx.baseUrl);
139
139
  this.resolvePkgJson = (spec) => require.resolve(`${spec}/package.json`);
@@ -155,12 +155,12 @@ var ClientModuleHostService = class extends Service {
155
155
  const failures = [];
156
156
  this.flush((err) => failures.push(err));
157
157
  if (failures.length > 0) throw new ClientPackageCompositionError(failures);
158
- ctx.effect(() => ctx.httpServer.register({
158
+ ctx.effect(() => ctx.webServer.register({
159
159
  kind: "prefix",
160
160
  path: "/plugins",
161
161
  handler: this.serveBundle
162
162
  }), "client-modules: bundle route");
163
- ctx.effect(() => ctx.httpServer.tapIndex((html) => injectBootManifest(html, this.composed)), "client-modules: boot manifest injection");
163
+ ctx.effect(() => ctx.webServer.tapIndex((html) => injectBootManifest(html, this.composed)), "client-modules: boot manifest injection");
164
164
  }
165
165
  /**
166
166
  * Current composed entry graph (stable object between changes).
@@ -344,4 +344,4 @@ var ClientModuleHostService = class extends Service {
344
344
  };
345
345
  };
346
346
  //#endregion
347
- export { ClientModuleHostService, ClientModuleHostService as default, injectBootManifest };
347
+ export { ClientModuleRegistry, ClientModuleRegistry as default, injectBootManifest };
package/lib/invariant.js CHANGED
@@ -19,7 +19,7 @@ const inject = ["invariants"];
19
19
  */
20
20
  const install = (ctx, fail) => {
21
21
  ctx.on("internal/plugin", () => {
22
- const host = ctx.get("clientModuleHost");
22
+ const host = ctx.get("clientModules");
23
23
  if (host === void 0) return;
24
24
  for (const row of host.graph().entries) if (host.clientPath(row.id) === void 0) fail(`web plugin graph row "${row.id}" advertises ${row.url} but resolves no client bundle path — the served __DSH_BOOT__ would 404 on fetch`);
25
25
  }, { global: true });
@@ -10,13 +10,13 @@
10
10
  * factory (`window.__ModuleLoader__.load({id, factory})`); every module body
11
11
  * side effect — including CSS injection — lives inside the factory closure
12
12
  * and runs at materialization, not at script execution. Materialization
13
- * (factory(require) → export surface) happens on first import/require and is
13
+ * (factory(require) → exports) happens on first import/require and is
14
14
  * memoized in {@link ClientModuleLoader.loadCache}; a factory that requires
15
15
  * another registered-but-unmaterialized module materializes it recursively,
16
16
  * so load order needs no external sequencing.
17
17
  *
18
18
  * Resolution branch order (import): seed word → shell instance; memoized
19
- * record → surface; static registry (shell-own modules, e.g. app-shell) →
19
+ * record → exports; static registry (shell-own modules, e.g. app-shell) →
20
20
  * module; registered factory → materialize; graph row → load + materialize;
21
21
  * anything else → throw (loud — the runtime mirror of the
22
22
  * build-time bundle purity gate). The synchronous `require` handed to
@@ -103,12 +103,12 @@ export interface ClientPluginHandoff {
103
103
  id: string;
104
104
  /**
105
105
  * Closure factory holding the whole bundle body: receives the synchronous
106
- * require bound to the module table and returns the bundle's export
107
- * surface. Runs once, at materialization.
106
+ * require bound to the module table and returns the bundle's exports. Runs
107
+ * once, at materialization.
108
108
  */
109
109
  factory: (require: (spec: string) => unknown) => Record<string, unknown>;
110
110
  }
111
- /** Window surface of the web boot protocol: the host-injected graph, the registration sink, and the kernel handoff slot. */
111
+ /** Window API of the web boot protocol: the host-injected graph, registration sink, and kernel handoff slot. */
112
112
  export interface DshWindow {
113
113
  /** Host-composed entry graph, injected before the shell bundle runs; wire-boundary raw until {@link parseBootManifest}. */
114
114
  __DSH_BOOT__?: unknown;
@@ -128,8 +128,8 @@ export interface DshWindow {
128
128
  export interface ClientModuleRecord {
129
129
  /** Module id (entry name / package name). */
130
130
  id: string;
131
- /** The materialized export surface (factory `module.exports`, or the shell module for static registrations). */
132
- surface: unknown;
131
+ /** Materialized exports (`module.exports` from a factory, or a statically registered shell module). */
132
+ exports: unknown;
133
133
  /** Owned `<style data-plugin>` tag ids (`data-plugin-css` values) injected during materialization. */
134
134
  styles: string[];
135
135
  /** Observed `require()` edges (module-graph boundary; only table words can appear today). */
@@ -143,7 +143,7 @@ export interface ClientModuleRecord {
143
143
  export interface ClientModuleLoader {
144
144
  /** Discriminant against Node's internal loader shapes ('v1'/'v2'). */
145
145
  version: 'client';
146
- /** Materialized-module registry: id → record. The governance-side read face for entry export surfaces. */
146
+ /** Materialized-module registry: id → record. The governance-side read API for entry exports. */
147
147
  loadCache: Map<string, ClientModuleRecord>;
148
148
  /**
149
149
  * Internal contract consumed by the vendored Loader's `tree.import`. Resolves
@@ -152,7 +152,7 @@ export interface ClientModuleLoader {
152
152
  * @param specifier - module specifier (entry name or table word).
153
153
  * @param parentURL - importer URL (unused — the client module graph is flat).
154
154
  * @param attrs - Import attributes (unused; interface parity with Node's loader contract).
155
- * @returns the module's export surface.
155
+ * @returns the module's exports.
156
156
  */
157
157
  import(specifier: string, parentURL: string, attrs: Record<string, unknown>): Promise<unknown>;
158
158
  /**
@@ -16,7 +16,7 @@
16
16
  * negative "not a client package" verdict) is cached per name and never
17
17
  * expires — plugin-set changes take effect on restart; bundle content
18
18
  * changes reach the graph only through
19
- * {@link ClientModuleHostService.rebuilt}.
19
+ * {@link ClientModuleRegistry.rebuilt}.
20
20
  * @module @deepseek-ai/dsh-client-modules
21
21
  */
22
22
  import { Service } from '@deepseek-ai/cordis';
@@ -26,7 +26,7 @@ export type { BootManifest, BootModuleRow, BootPluginRow, WebBootEntry, WebBootG
26
26
  declare module '@deepseek-ai/cordis' {
27
27
  interface Context {
28
28
  /** The web plugin table (provided by the client-modules node half). */
29
- clientModuleHost: ClientModuleHostService;
29
+ clientModules: ClientModuleRegistry;
30
30
  }
31
31
  }
32
32
  /**
@@ -45,7 +45,7 @@ export declare function injectBootManifest(html: string, graph: WebBootGraph): s
45
45
  * already-loaded entries aggregates into one loud throw (FAILED fiber; the
46
46
  * boot activation audit reports it).
47
47
  */
48
- export declare class ClientModuleHostService extends Service {
48
+ export declare class ClientModuleRegistry extends Service {
49
49
  static inject: string[];
50
50
  private readonly table;
51
51
  private readonly pkgMeta;
@@ -57,7 +57,7 @@ export declare class ClientModuleHostService extends Service {
57
57
  private composed;
58
58
  /**
59
59
  * Build the service: subscribe, seed, and run the activation flush.
60
- * @param ctx - plugin context carrying httpServer and loader.
60
+ * @param ctx - plugin context carrying webServer and loader.
61
61
  */
62
62
  constructor(ctx: Context);
63
63
  /**
@@ -107,5 +107,5 @@ export declare class ClientModuleHostService extends Service {
107
107
  private flush;
108
108
  private readonly serveBundle;
109
109
  }
110
- export default ClientModuleHostService;
110
+ export default ClientModuleRegistry;
111
111
  //# sourceMappingURL=index.d.ts.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-client-modules",
3
3
  "description": "Client module system, dual-face: node half composes the __DSH_BOOT__ entry graph (incremental dsh.client scan, bundle route, index tap, webPlugins service); browser half is the lazy-CJS module table the vendored cordis Loader consumes as its internal seam",
4
- "version": "0.0.1-rc.1",
4
+ "version": "0.0.1-rc.3",
5
5
  "publishConfig": {
6
6
  "access": "restricted"
7
7
  },
@@ -39,9 +39,9 @@
39
39
  "license": "BSD-3-Clause",
40
40
  "devDependencies": {
41
41
  "@deepseek-ai/cordis-plugin-loader": "^1.0.1-rc.1",
42
- "@deepseek-ai/dsh-invariants": "^0.0.1-rc.1",
43
- "@deepseek-ai/cordis": "^4.0.1-rc.1",
44
- "@deepseek-ai/dsh-host-webserver": "^0.0.1-rc.1"
42
+ "@deepseek-ai/dsh-host-webserver": "^0.0.1-rc.3",
43
+ "@deepseek-ai/dsh-invariants": "^0.0.1-rc.3",
44
+ "@deepseek-ai/cordis": "^4.0.1-rc.1"
45
45
  },
46
46
  "files": [
47
47
  "lib/index.js",
@@ -50,8 +50,8 @@
50
50
  "lib/types/**/*.d.ts"
51
51
  ],
52
52
  "peerDependencies": {
53
- "@deepseek-ai/dsh-invariants": "^0.0.1-rc.1",
54
- "@deepseek-ai/cordis": "^4.0.1-rc.1"
53
+ "@deepseek-ai/cordis": "^4.0.1-rc.1",
54
+ "@deepseek-ai/dsh-invariants": "^0.0.1-rc.3"
55
55
  },
56
56
  "scripts": {
57
57
  "bundle": "tsdown",