@crazx/dsh-client-modules 0.1.2-rc.1.zw.1 → 0.1.5-alpha.1.zw.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: 9e7dee9fd3162d7bbcec0b06294d36ffc60d0794
6
- README.zh.md: 88fa3b7780c638bf3c41766844442a01759f861b
5
+ README.md: 3a1f22f267b7fbeeb7bed43b9802ff065f97aec2
6
+ README.zh.md: 5393993a8fd6bf7fad8643486069598a29b3f989
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
 
@@ -25,6 +25,8 @@ English | [中文](README.zh.md)
25
25
  <a id="use-this-package"></a>
26
26
  ## Use this package
27
27
 
28
+ Use [`DshClientManifest`](../../util/package-manifest/README.md) for the declaration type. Client-modules validates the JSON and owns the normalized boot graph.
29
+
28
30
  Use it when you compose or build a browser client plugin: the package turns a package's `dsh.client` declaration into a loadable browser bundle with no per-plugin wiring. It activates with the web composition; the shell boots it before any plugin runs.
29
31
 
30
32
  ### Declaring a client plugin
@@ -69,13 +71,13 @@ The node half snapshots each client bundle and available source map before publi
69
71
 
70
72
  ### Boot manifest injection
71
73
 
72
- 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.
73
75
 
74
76
  ### Source map
75
77
 
76
78
  | File | Role |
77
79
  |---|---|
78
- | [`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 |
79
81
  | [`src/client/index.ts`](src/client/index.ts) | Browser half: bootstrap export, `ctx.modules` enrollment |
80
82
  | [`src/client/system.ts`](src/client/system.ts) | `ClientModuleSystem`: load/materialize/invalidate machinery |
81
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
 
@@ -25,6 +25,8 @@ kind: "package-reference"
25
25
  <a id="use-this-package"></a>
26
26
  ## 使用本包
27
27
 
28
+ 声明类型使用 [`DshClientManifest`](../../util/package-manifest/README.zh.md)。Client-modules 负责 JSON 校验和归一化的启动图。
29
+
28
30
  组合或构建浏览器客户端插件时使用它:本包把包的 `dsh.client` 声明变成可加载的浏览器 bundle,无需任何逐插件接线。它随 web 组合激活;外壳在任何插件运行前启动它。
29
31
 
30
32
  ### 声明客户端插件
@@ -69,13 +71,13 @@ node 半侧会在发布前快照每个客户端 bundle 及其现有 source map
69
71
 
70
72
  ### 启动清单注入
71
73
 
72
- 宿主 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 模式。
73
75
 
74
76
  ### 源码地图
75
77
 
76
78
  | 文件 | 职责 |
77
79
  |---|---|
78
- | [`src/index.ts`](src/index.ts) | node 半侧:`ClientModuleRegistry`、扫描、产物快照、combo 路由、索引 tap |
80
+ | [`src/index.ts`](src/index.ts) | node 半侧:`ClientModuleRegistry`、扫描、产物快照、可选 combo 路由、结构化 index 行 |
79
81
  | [`src/client/index.ts`](src/client/index.ts) | 浏览器半侧:bootstrap 导出、`ctx.modules` 登记 |
80
82
  | [`src/client/system.ts`](src/client/system.ts) | `ClientModuleSystem`:加载/物化/失效机制 |
81
83
  | [`src/client/manifest.ts`](src/client/manifest.ts) | 协议类型与启动清单解析 |
package/lib/index.js CHANGED
@@ -454,7 +454,7 @@ window.__ModuleLoader__={
454
454
  * boot activation audit reports it).
455
455
  */
456
456
  var ClientModuleRegistry = class extends Service {
457
- static inject = ["webServer", "loader"];
457
+ static inject = ["loader"];
458
458
  table = /* @__PURE__ */ new Map();
459
459
  sources = /* @__PURE__ */ new Map();
460
460
  pkgMeta = /* @__PURE__ */ new Map();
@@ -471,7 +471,7 @@ var ClientModuleRegistry = class extends Service {
471
471
  composed;
472
472
  /**
473
473
  * Build the service: subscribe, seed, and run the activation flush.
474
- * @param ctx - plugin context carrying webServer and loader.
474
+ * @param ctx - plugin context carrying Loader and an optional Web carrier.
475
475
  */
476
476
  constructor(ctx) {
477
477
  super(ctx, "clientModules");
@@ -493,11 +493,15 @@ var ClientModuleRegistry = class extends Service {
493
493
  const failures = [];
494
494
  this.flush((err) => failures.push(err));
495
495
  if (failures.length > 0) throw new ClientPackageCompositionError(failures);
496
- ctx.effect(() => ctx.webServer.register({
497
- kind: "prefix",
498
- path: "/plugins",
499
- handler: this.serveBundle
500
- }), "client-modules: bundle route");
496
+ const registerWebCarrier = (webCtx) => {
497
+ webCtx.effect(() => webCtx.webServer.register({
498
+ kind: "prefix",
499
+ path: "/plugins",
500
+ handler: this.serveBundle
501
+ }), "client-modules: bundle route");
502
+ };
503
+ if (ctx.get("webServer") === void 0) ctx.inject(["webServer"], registerWebCarrier);
504
+ else registerWebCarrier(ctx);
501
505
  ctx.on("webserver/index-inject", (table) => {
502
506
  table.push(...bootInjections(this.composed));
503
507
  });
@@ -518,6 +522,21 @@ var ClientModuleRegistry = class extends Service {
518
522
  return this.table.get(id)?.meta.clientPath;
519
523
  }
520
524
  /**
525
+ * Serve an advertised revisioned bundle or source map without a Web server.
526
+ * Unknown URLs return 404, unsupported methods return 405, and `HEAD`
527
+ * returns the same immutable headers without a body.
528
+ * @param request - shell-carrier request for a `/plugins` resource.
529
+ * @returns the exact response also exposed by the optional Web route.
530
+ */
531
+ fetchBundle(request) {
532
+ const resource = this.bundleResource(request.method, request.url);
533
+ const body = resource.body === void 0 ? null : Uint8Array.from(resource.body);
534
+ return new Response(body, {
535
+ status: resource.status,
536
+ ...resource.headers === void 0 ? {} : { headers: resource.headers }
537
+ });
538
+ }
539
+ /**
521
540
  * Filesystem baseline captured before an entry's current bytes were read.
522
541
  * HMR compares it with the live files when installing a watch, so a write
523
542
  * between startup composition and watch installation cannot disappear into
@@ -853,27 +872,27 @@ var ClientModuleRegistry = class extends Service {
853
872
  this.composed = composed;
854
873
  this.notifyGraphChanged();
855
874
  }
856
- serveBundle = (req, res) => {
857
- if (req.method !== "GET" && req.method !== "HEAD") {
858
- res.writeHead(405);
859
- res.end();
860
- return;
861
- }
862
- /* v8 ignore next -- `?? '/'` arm: node:http always sets url on server requests. */
863
- const requestUrl = new URL(req.url ?? "/", "http://x");
875
+ bundleResource(method, url) {
876
+ if (method !== "GET" && method !== "HEAD") return { status: 405 };
877
+ const requestUrl = new URL(url, "http://x");
864
878
  const resourceUrl = `${requestUrl.pathname}${requestUrl.search}`;
865
879
  const response = this.responses.get(resourceUrl) ?? this.previousBatchResponses.get(resourceUrl);
866
- if (response !== void 0) {
867
- res.writeHead(200, {
880
+ if (response !== void 0) return {
881
+ status: 200,
882
+ headers: {
868
883
  "content-type": response.contentType,
869
884
  "cache-control": IMMUTABLE_CACHE,
870
- "content-length": response.body.length
871
- });
872
- res.end(req.method === "HEAD" ? void 0 : response.body);
873
- return;
874
- }
875
- res.writeHead(404);
876
- res.end();
885
+ "content-length": String(response.body.length)
886
+ },
887
+ ...method === "HEAD" ? {} : { body: response.body }
888
+ };
889
+ return { status: 404 };
890
+ }
891
+ serveBundle = (req, res) => {
892
+ /* v8 ignore next -- `?? '/'` arm: node:http always sets url on server requests. */
893
+ const response = this.bundleResource(req.method, req.url ?? "/");
894
+ res.writeHead(response.status, response.headers);
895
+ res.end(response.body);
877
896
  };
878
897
  };
879
898
  //#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
@@ -172,6 +180,7 @@ export declare class ClientModuleRegistry extends Service {
172
180
  private resolveSource;
173
181
  private reconcilePackage;
174
182
  private flush;
183
+ private bundleResource;
175
184
  private readonly serveBundle;
176
185
  }
177
186
  export default ClientModuleRegistry;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@crazx/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.2-rc.1.zw.1",
4
+ "version": "0.1.5-alpha.1.zw.1",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -39,9 +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.2-rc.1",
43
- "@deepseek-ai/dsh-invariants": "^0.1.2-rc.1",
44
- "@deepseek-ai/cordis": "^4.0.2"
42
+ "@deepseek-ai/dsh-host-webserver": "^0.1.5-alpha.1",
43
+ "@deepseek-ai/dsh-invariants": "^0.1.5-alpha.1",
44
+ "@deepseek-ai/cordis": "^4.0.2",
45
+ "@deepseek-ai/dsh-package-manifest": "^0.1.5-alpha.1"
45
46
  },
46
47
  "files": [
47
48
  "lib/index.js",