@deepseek-ai/dsh-client-modules 0.1.3-alpha.2 → 0.1.5-alpha.1

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: 7bfa39d183bb29f3f9481bb39280c5521786031a
6
- README.zh.md: 64dc5644f535722b464c867616b18ff557e921be
5
+ README.md: 246293de32483adba0ee93d2d9bdbe8b8dbcd5fa
6
+ README.zh.md: 759eb5b94e63c7108013ed9b223393ba405adc33
package/README.md CHANGED
@@ -9,7 +9,7 @@ English | [中文](README.zh.md)
9
9
 
10
10
  ## Summary
11
11
 
12
- `dsh-client-modules` turns a plugin package's `dsh.client` declaration into a loadable browser bundle: the host half scans enabled Loader entries, composes the boot graph, and serves each bundle over `/plugins`, and the browser half loads those bundles lazily on demand. Plugin bundles execute lazily — running a bundle only registers a factory, and module side effects run at materialization — so nothing runs until a plugin is first used. Everything here is browser-kernel machinery; the model never sees it.
12
+ `dsh-client-modules` turns a plugin package's `dsh.client` declaration into a loadable browser bundle: the host half scans enabled Loader entries and composes the boot graph, an available Web carrier serves each bundle over `/plugins`, and a shell-owned carrier dispatches the same exact bundle responses through `fetchBundle()`. The browser half loads those bundles lazily on demand. Plugin bundles execute lazily — running a bundle only registers a factory, and module side effects run at materialization — so nothing runs until a plugin is first used. Everything here is browser-kernel machinery; the model never sees it.
13
13
 
14
14
  ## Table of Contents
15
15
 
@@ -71,13 +71,13 @@ The node half snapshots each client bundle and available source map before publi
71
71
 
72
72
  ### Boot manifest injection
73
73
 
74
- The host taps the index render and injects, 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. The facade's `create()` materializes the modules bundle, delegates construction to its `createClientModuleSystem` export, and leaves the same facade in live-registration mode.
74
+ 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.
75
75
 
76
76
  ### Source map
77
77
 
78
78
  | File | Role |
79
79
  |---|---|
80
- | [`src/index.ts`](src/index.ts) | Node half: `ClientModuleRegistry`, scan, artifact snapshots, combo routes, index tap |
80
+ | [`src/index.ts`](src/index.ts) | Node half: `ClientModuleRegistry`, scan, artifact snapshots, optional combo route, structured index rows |
81
81
  | [`src/client/index.ts`](src/client/index.ts) | Browser half: bootstrap export, `ctx.modules` enrollment |
82
82
  | [`src/client/system.ts`](src/client/system.ts) | `ClientModuleSystem`: load/materialize/invalidate machinery |
83
83
  | [`src/client/manifest.ts`](src/client/manifest.ts) | Wire types and boot-manifest parsing |
package/README.zh.md CHANGED
@@ -9,7 +9,7 @@ kind: "package-reference"
9
9
 
10
10
  ## 概述
11
11
 
12
- `dsh-client-modules` 把插件包的 `dsh.client` 声明变成可加载的浏览器 bundle:宿主半侧扫描已启用的 Loader 条目、组合启动图,并通过 `/plugins` 提供每个 bundle;浏览器半侧按需惰性加载这些 bundle。插件 bundle 惰性执行——运行 bundle 只注册 factory,模块副作用在物化时运行——因此插件首次被使用之前什么都不会运行。这里的一切都是浏览器内核机制;模型永远看不到它。
12
+ `dsh-client-modules` 把插件包的 `dsh.client` 声明变成可加载的浏览器 bundle:宿主半侧扫描已启用的 Loader 条目并组合启动图,可用的 Web 载体通过 `/plugins` 提供每个 bundle,由 shell 持有的载体则通过 `fetchBundle()` 分派完全相同的 bundle 响应。浏览器半侧按需惰性加载这些 bundle。插件 bundle 惰性执行——运行 bundle 只注册 factory,模块副作用在物化时运行——因此插件首次被使用之前什么都不会运行。这里的一切都是浏览器内核机制;模型永远看不到它。
13
13
 
14
14
  ## 目录
15
15
 
@@ -71,13 +71,13 @@ node 半侧会在发布前快照每个客户端 bundle 及其现有 source map
71
71
 
72
72
  ### 启动清单注入
73
73
 
74
- 宿主 tap 索引渲染,并向 `<head>` 注入:`window.__ModuleLoader__` queue facade、每个 application combo 的提示性 preload、阻塞 parser 的 bootstrap combo 脚本,然后才是外壳读取前的启动图。facade 的 `create()` 物化 modules bundle、把构造委托给其 `createClientModuleSystem` 导出,并让同一 facade 进入 live registration 模式。
74
+ 宿主贡献结构化 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 模式。
75
75
 
76
76
  ### 源码地图
77
77
 
78
78
  | 文件 | 职责 |
79
79
  |---|---|
80
- | [`src/index.ts`](src/index.ts) | node 半侧:`ClientModuleRegistry`、扫描、产物快照、combo 路由、索引 tap |
80
+ | [`src/index.ts`](src/index.ts) | node 半侧:`ClientModuleRegistry`、扫描、产物快照、可选 combo 路由、结构化 index |
81
81
  | [`src/client/index.ts`](src/client/index.ts) | 浏览器半侧:bootstrap 导出、`ctx.modules` 登记 |
82
82
  | [`src/client/system.ts`](src/client/system.ts) | `ClientModuleSystem`:加载/物化/失效机制 |
83
83
  | [`src/client/manifest.ts`](src/client/manifest.ts) | 协议类型与启动清单解析 |
package/lib/index.js CHANGED
@@ -438,7 +438,7 @@ window.__ModuleLoader__={
438
438
  * boot activation audit reports it).
439
439
  */
440
440
  var ClientModuleRegistry = class extends Service {
441
- static inject = ["webServer", "loader"];
441
+ static inject = ["loader"];
442
442
  table = /* @__PURE__ */ new Map();
443
443
  sources = /* @__PURE__ */ new Map();
444
444
  pkgMeta = /* @__PURE__ */ new Map();
@@ -455,7 +455,7 @@ var ClientModuleRegistry = class extends Service {
455
455
  composed;
456
456
  /**
457
457
  * Build the service: subscribe, seed, and run the activation flush.
458
- * @param ctx - plugin context carrying webServer and loader.
458
+ * @param ctx - plugin context carrying Loader and an optional Web carrier.
459
459
  */
460
460
  constructor(ctx) {
461
461
  super(ctx, "clientModules");
@@ -477,11 +477,15 @@ var ClientModuleRegistry = class extends Service {
477
477
  const failures = [];
478
478
  this.flush((err) => failures.push(err));
479
479
  if (failures.length > 0) throw new ClientPackageCompositionError(failures);
480
- ctx.effect(() => ctx.webServer.register({
481
- kind: "prefix",
482
- path: "/plugins",
483
- handler: this.serveBundle
484
- }), "client-modules: bundle route");
480
+ const registerWebCarrier = (webCtx) => {
481
+ webCtx.effect(() => webCtx.webServer.register({
482
+ kind: "prefix",
483
+ path: "/plugins",
484
+ handler: this.serveBundle
485
+ }), "client-modules: bundle route");
486
+ };
487
+ if (ctx.get("webServer") === void 0) ctx.inject(["webServer"], registerWebCarrier);
488
+ else registerWebCarrier(ctx);
485
489
  ctx.on("webserver/index-inject", (table) => {
486
490
  table.push(...bootInjections(this.composed));
487
491
  });
@@ -502,6 +506,21 @@ var ClientModuleRegistry = class extends Service {
502
506
  return this.table.get(id)?.meta.clientPath;
503
507
  }
504
508
  /**
509
+ * Serve an advertised revisioned bundle or source map without a Web server.
510
+ * Unknown URLs return 404, unsupported methods return 405, and `HEAD`
511
+ * returns the same immutable headers without a body.
512
+ * @param request - shell-carrier request for a `/plugins` resource.
513
+ * @returns the exact response also exposed by the optional Web route.
514
+ */
515
+ fetchBundle(request) {
516
+ const resource = this.bundleResource(request.method, request.url);
517
+ const body = resource.body === void 0 ? null : Uint8Array.from(resource.body);
518
+ return new Response(body, {
519
+ status: resource.status,
520
+ ...resource.headers === void 0 ? {} : { headers: resource.headers }
521
+ });
522
+ }
523
+ /**
505
524
  * Filesystem baseline captured before an entry's current bytes were read.
506
525
  * HMR compares it with the live files when installing a watch, so a write
507
526
  * between startup composition and watch installation cannot disappear into
@@ -835,26 +854,26 @@ var ClientModuleRegistry = class extends Service {
835
854
  this.composed = composed;
836
855
  this.notifyGraphChanged();
837
856
  }
838
- serveBundle = (req, res) => {
839
- if (req.method !== "GET" && req.method !== "HEAD") {
840
- res.writeHead(405);
841
- res.end();
842
- return;
843
- }
844
- /* v8 ignore next -- `?? '/'` arm: node:http always sets url on server requests. */
845
- const requestUrl = new URL(req.url ?? "/", "http://x");
857
+ bundleResource(method, url) {
858
+ if (method !== "GET" && method !== "HEAD") return { status: 405 };
859
+ const requestUrl = new URL(url, "http://x");
846
860
  const resourceUrl = `${requestUrl.pathname}${requestUrl.search}`;
847
861
  const response = this.responses.get(resourceUrl) ?? this.previousBatchResponses.get(resourceUrl);
848
- if (response !== void 0) {
849
- res.writeHead(200, {
862
+ if (response !== void 0) return {
863
+ status: 200,
864
+ headers: {
850
865
  "content-type": response.contentType,
851
866
  "cache-control": IMMUTABLE_CACHE
852
- });
853
- res.end(req.method === "HEAD" ? void 0 : response.body);
854
- return;
855
- }
856
- res.writeHead(404);
857
- res.end();
867
+ },
868
+ ...method === "HEAD" ? {} : { body: response.body }
869
+ };
870
+ return { status: 404 };
871
+ }
872
+ serveBundle = (req, res) => {
873
+ /* v8 ignore next -- `?? '/'` arm: node:http always sets url on server requests. */
874
+ const response = this.bundleResource(req.method, req.url ?? "/");
875
+ res.writeHead(response.status, response.headers);
876
+ res.end(response.body);
858
877
  };
859
878
  };
860
879
  //#endregion
@@ -91,7 +91,7 @@ export declare class ClientModuleRegistry extends Service {
91
91
  private composed;
92
92
  /**
93
93
  * Build the service: subscribe, seed, and run the activation flush.
94
- * @param ctx - plugin context carrying webServer and loader.
94
+ * @param ctx - plugin context carrying Loader and an optional Web carrier.
95
95
  */
96
96
  constructor(ctx: Context);
97
97
  /**
@@ -105,6 +105,14 @@ export declare class ClientModuleRegistry extends Service {
105
105
  * @returns the path, or undefined for an unknown id.
106
106
  */
107
107
  clientPath(id: string): string | undefined;
108
+ /**
109
+ * Serve an advertised revisioned bundle or source map without a Web server.
110
+ * Unknown URLs return 404, unsupported methods return 405, and `HEAD`
111
+ * returns the same immutable headers without a body.
112
+ * @param request - shell-carrier request for a `/plugins` resource.
113
+ * @returns the exact response also exposed by the optional Web route.
114
+ */
115
+ fetchBundle(request: Request): Response;
108
116
  /**
109
117
  * Filesystem baseline captured before an entry's current bytes were read.
110
118
  * HMR compares it with the live files when installing a watch, so a write
@@ -170,6 +178,7 @@ export declare class ClientModuleRegistry extends Service {
170
178
  private resolveSource;
171
179
  private reconcilePackage;
172
180
  private flush;
181
+ private bundleResource;
173
182
  private readonly serveBundle;
174
183
  }
175
184
  export default ClientModuleRegistry;
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.1.3-alpha.2",
4
+ "version": "0.1.5-alpha.1",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -39,10 +39,10 @@
39
39
  "license": "MIT",
40
40
  "devDependencies": {
41
41
  "@deepseek-ai/cordis-plugin-loader": "^1.0.3",
42
- "@deepseek-ai/dsh-host-webserver": "^0.1.3-alpha.2",
43
- "@deepseek-ai/dsh-invariants": "^0.1.3-alpha.2",
42
+ "@deepseek-ai/dsh-host-webserver": "^0.1.5-alpha.1",
43
+ "@deepseek-ai/dsh-invariants": "^0.1.5-alpha.1",
44
44
  "@deepseek-ai/cordis": "^4.0.2",
45
- "@deepseek-ai/dsh-package-manifest": "^0.1.3-alpha.2"
45
+ "@deepseek-ai/dsh-package-manifest": "^0.1.5-alpha.1"
46
46
  },
47
47
  "files": [
48
48
  "lib/index.js",