From 9c309dc03eefbc48fb7335b6dc1a94c46ef73215 Mon Sep 17 00:00:00 2001 From: imccyu Date: Tue, 4 Aug 2026 14:36:31 +0800 Subject: [PATCH] docs(client): update plugin loading RFC --- ...7-23-client-plugin-loading-model.i18n.yaml | 4 +-- .../2026-07-23-client-plugin-loading-model.md | 25 +++++++++++++------ ...26-07-23-client-plugin-loading-model.zh.md | 25 +++++++++++++------ 3 files changed, 38 insertions(+), 16 deletions(-) diff --git a/.agents/notes/implemented/architecture/2026-07-23-client-plugin-loading-model.i18n.yaml b/.agents/notes/implemented/architecture/2026-07-23-client-plugin-loading-model.i18n.yaml index 451dcd1fca..f8a9c8c260 100644 --- a/.agents/notes/implemented/architecture/2026-07-23-client-plugin-loading-model.i18n.yaml +++ b/.agents/notes/implemented/architecture/2026-07-23-client-plugin-loading-model.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-07-23-client-plugin-loading-model.md -2026-07-23-client-plugin-loading-model.md: 02347f2964942b89ec1f0a6ec483f4c2b2f9e68c -2026-07-23-client-plugin-loading-model.zh.md: ea927d35860fbbba567c47cea0ee3a45133ce0f4 +2026-07-23-client-plugin-loading-model.md: 2dc0c68e5f20bd790c2362f92c16dece171babf5 +2026-07-23-client-plugin-loading-model.zh.md: ce7850e37b9ae2735565a35ce3de28f4f290ed04 diff --git a/.agents/notes/implemented/architecture/2026-07-23-client-plugin-loading-model.md b/.agents/notes/implemented/architecture/2026-07-23-client-plugin-loading-model.md index 02347f2964..2dc0c68e5f 100644 --- a/.agents/notes/implemented/architecture/2026-07-23-client-plugin-loading-model.md +++ b/.agents/notes/implemented/architecture/2026-07-23-client-plugin-loading-model.md @@ -14,7 +14,9 @@ The browser client runs the same cordis plugin mechanism, so it needs the same s Conventional frontend engineering digests all dependencies at build time: one bundle, externals resolved by the bundler, nothing left to manage at runtime. Runtime module management on top of that is the unusual requirement here. The client therefore splits into two layers: the upper layer is cordis plugin loading through the same vendored Loader, and the lower layer is module-granular dependency management — `dsh-client-modules`. -The lower layer supplies four capabilities: externals (the platform list), remote arrival (bundle fetch plus lazy factory registration), versioning (content-hash revs), and hot update (invalidate/prefetch). +The lower layer supplies four capabilities: externals (the platform list), remote arrival (same-origin external classic scripts plus lazy factory registration), versioning (content-hash revs), and hot update (invalidate/prefetch). + +Plugin bundles are built independently outside Vite's module graph. Feeding response text into an inline script leaves the browser with a dynamic source execution: no standard source-map chain connects the network resource, generated bundle, and TypeScript/TSX source, so performance profiles and stacks stop at generated `client.js`; the module system must also buffer the complete source and split one arrival responsibility across fetch and execute transport seams. On top of that, client and host plugins register and load consistently: a package declares `dshClient` once, the host scans the declaration into the boot graph, and the same Loader semantics govern entries on both sides. @@ -46,10 +48,18 @@ Four edge rules govern imports across the two kinds. None of them depends on any The browser mirrors the host's division of labor. `dsh-client-modules` (`ClientModuleSystem`) takes the module-system seat that Node's internal ESM loader holds host-side; the same vendored `@cordisjs/plugin-loader` keeps the governance seat on both sides. The line between them in one sentence: **the module system owns module identity and bytes — how code arrives, registers, and becomes an export surface; the Loader owns plugin lifecycle — when a plugin mounts, what it waits for, and how it is torn down.** -`ClientModuleSystem` is a lazy CJS table. Executing a bundle only **registers** its factory — the bundle calls `window.__ModuleLoader__.load({ id, factory })` and nothing else happens. Every module body side effect, CSS injection included, lives inside the factory closure and runs at materialization: the first `require`/import of that id, memoized after that. A factory that requires a registered-but-unmaterialized sibling materializes it recursively, so no sort order exists anywhere. When asked to import an id, the table resolves through a fixed branch order: seed word → memoized record → static registration (shell-own modules, e.g. app-shell) → registered factory → graph-row fetch + execute → loud throw. That final throw is the runtime mirror of the build-time purity gate. The system also keeps per-module bookkeeping — owned `