diff --git a/.agents/notes/implemented/architecture/2026-08-19-web-index-injection-table.i18n.yaml b/.agents/notes/implemented/architecture/2026-08-19-web-index-injection-table.i18n.yaml index 1c228865dc..90d1f96dff 100644 --- a/.agents/notes/implemented/architecture/2026-08-19-web-index-injection-table.i18n.yaml +++ b/.agents/notes/implemented/architecture/2026-08-19-web-index-injection-table.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-08-19-web-index-injection-table.md -2026-08-19-web-index-injection-table.md: a4c21974ef6770320b61cd7c2b19fc20372e8111 -2026-08-19-web-index-injection-table.zh.md: 4ccfa8afe6353e9af7e341ce73f5fb3213257ba2 +2026-08-19-web-index-injection-table.md: 9ed02aa94cd318d107a32802d8723652e6b10ea2 +2026-08-19-web-index-injection-table.zh.md: 8ad036766faa14071b20da12ef907ab012cae23f diff --git a/.agents/notes/implemented/architecture/2026-08-19-web-index-injection-table.md b/.agents/notes/implemented/architecture/2026-08-19-web-index-injection-table.md index a4c21974ef..9ed02aa94c 100644 --- a/.agents/notes/implemented/architecture/2026-08-19-web-index-injection-table.md +++ b/.agents/notes/implemented/architecture/2026-08-19-web-index-injection-table.md @@ -20,7 +20,7 @@ One table, two renderers: the served form's `webServer.renderIndex(html)` render - client-modules and ui-theme no longer regex-edit HTML; the worker's `readBootPayload` service-poking (`clientModules`, `settings`, theme constants through `loader.load`) is deleted; the page-side `installModuleLoaderFacade`, `applyBootTheme`, and `PARSER_PRELOAD_IDS` re-implementations retire. - Ordering: across subscribers, subscription order (same as the old tap order); within one subscriber, push order — modules itself guarantees queue → preloads → global. -- The served rendering of the manifest global changed from `window.__DSH_BOOT__ =` to `globalThis["__DSH_BOOT__"] =`; snapshot expectations carrying that text need re-recording. +- The served rendering of the manifest global changed from `window.__DSH_BOOT__ =` to `globalThis["__DSH_BOOT__"] =`; no committed snapshot expectation carries that text, so none needed re-recording. - New model-visible or page-visible boot inputs extend the row union; no new tap consumers. ## Alternatives considered diff --git a/.agents/notes/implemented/architecture/2026-08-19-web-index-injection-table.zh.md b/.agents/notes/implemented/architecture/2026-08-19-web-index-injection-table.zh.md index 4ccfa8afe6..8ad036766f 100644 --- a/.agents/notes/implemented/architecture/2026-08-19-web-index-injection-table.zh.md +++ b/.agents/notes/implemented/architecture/2026-08-19-web-index-injection-table.zh.md @@ -20,7 +20,7 @@ Web 壳的启动 HTML 需要三类注入:client-modules 的引导协议(`__M - client-modules 与 ui-theme 不再各自正则改 HTML;worker 侧 `readBootPayload` 的 `ctx.get` 手掏(clientModules、settings、theme 常量 loader.load)删除;页面侧 `installModuleLoaderFacade`、`applyBootTheme`、`PARSER_PRELOAD_IDS` 三份重抄退役。 - 顺序语义:跨订阅方按订阅注册顺序(与旧 tap 顺序一致),单订阅方内按 push 顺序;modules 自己保证 队列→preload→全局 三行有序。 -- `__DSH_BOOT__` 的 served 渲染文本从 `window.__DSH_BOOT__ =` 变为 `globalThis["__DSH_BOOT__"] =`;含此文本的快照期望需重录。 +- `__DSH_BOOT__` 的 served 渲染文本从 `window.__DSH_BOOT__ =` 变为 `globalThis["__DSH_BOOT__"] =`;已核实无已提交快照期望含此文本,无需重录。 - 新的模型可见/页面可见注入一律走行类型扩展,不再新增 tap 消费者。 ## Alternatives considered diff --git a/docs/subsystems/web-server.i18n.yaml b/docs/subsystems/web-server.i18n.yaml index 519c6e7043..f04a35b545 100644 --- a/docs/subsystems/web-server.i18n.yaml +++ b/docs/subsystems/web-server.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 docs/subsystems/web-server.md -web-server.md: 7dd700d24f63d31f34d8711069477414292e3bf6 -web-server.zh.md: ca67ff231e9aad9144fa5203ee46435f636a93da +web-server.md: 70bc0a2631418c102bdb79c1df29a73df5edf30a +web-server.zh.md: 870c9209dcfacd191da408be2e195edc047ab25d diff --git a/docs/subsystems/web-server.md b/docs/subsystems/web-server.md index 7dd700d24f..70bc0a2631 100644 --- a/docs/subsystems/web-server.md +++ b/docs/subsystems/web-server.md @@ -88,8 +88,9 @@ registerUpgrade(route: WebUpgradeRoute): () => void registerFallback(handler: WebRoute['handler']): () => void /** - * Register an index.html transform, applied by the fallback owner to every - * index response ({@link applyIndexTaps}) in registration order. + * Register a raw-HTML index transform, the escape hatch for markup no + * {@link IndexInjection} row expresses: {@link renderIndex} applies taps in + * registration order after rendering the structured rows. * @param transform - pure html-to-html function. * @returns the disposer removing the transform. */ diff --git a/docs/subsystems/web-server.zh.md b/docs/subsystems/web-server.zh.md index ca67ff231e..870c9209dc 100644 --- a/docs/subsystems/web-server.zh.md +++ b/docs/subsystems/web-server.zh.md @@ -88,8 +88,9 @@ registerUpgrade(route: WebUpgradeRoute): () => void registerFallback(handler: WebRoute['handler']): () => void /** - * Register an index.html transform, applied by the fallback owner to every - * index response ({@link applyIndexTaps}) in registration order. + * Register a raw-HTML index transform, the escape hatch for markup no + * {@link IndexInjection} row expresses: {@link renderIndex} applies taps in + * registration order after rendering the structured rows. * @param transform - pure html-to-html function. * @returns the disposer removing the transform. */ diff --git a/packages/extensions/cordis-host-runner/src/sandbox.ts b/packages/extensions/cordis-host-runner/src/sandbox.ts index f359801af1..fc0a771e4f 100644 --- a/packages/extensions/cordis-host-runner/src/sandbox.ts +++ b/packages/extensions/cordis-host-runner/src/sandbox.ts @@ -201,6 +201,10 @@ export function parseErrorMessage(half: 'code.host' | 'code.client', context: st * unparseable code — and `vm.Script` is only the best-effort prettifier: on a * Node host its failure carries the source-line-and-caret prelude the * teaching text builds on, and where the vm is a stub the message stays bare. + * The two parsers' syntax faces differ at the margin (`new.target` parses in + * a function body but not at the vm wrapper's top level), an accepted cost of + * a vm-free gate; and under a page CSP without `'unsafe-eval'`, `new Function` + * throws `EvalError`, which propagates unwrapped. * @param code - the model-written function body. * @param half - which define argument carried it, for the error text. * @throws when the body does not parse, with the offending line and a teaching hint. @@ -209,7 +213,7 @@ export function precheckCode(code: string, half: 'code.host' | 'code.client'): v const wrapped = `(async () => {\n${code}\n})()` try { // Compile-only: constructing the function parses the source and runs nothing. - // eslint-disable-next-line @typescript-eslint/no-implied-eval -- parse gate over model-written code; nothing is invoked + // oxlint-disable-next-line typescript/no-implied-eval -- parse gate over model-written code; nothing is invoked new Function(wrapped) } catch (error) { if (!isSyntaxError(error)) throw error diff --git a/packages/extensions/tool-cordis/src/api-catalog.ts b/packages/extensions/tool-cordis/src/api-catalog.ts index a20c0b9aff..bf0f6a9e79 100644 --- a/packages/extensions/tool-cordis/src/api-catalog.ts +++ b/packages/extensions/tool-cordis/src/api-catalog.ts @@ -2207,7 +2207,7 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [ }, { signature: 'tapIndex(transform: (html: string) => string): () => void', - description: 'Register an index.html transform, applied by the fallback owner to every index response (applyIndexTaps) in registration order.', + description: 'Register a raw-HTML index transform, the escape hatch for markup no IndexInjection row expresses: renderIndex applies taps in registration order after rendering the structured rows.', parameters: [{ name: 'transform', description: 'pure html-to-html function.' }], returns: 'the disposer removing the transform.', }, diff --git a/packages/host/frontend-static/README.i18n.yaml b/packages/host/frontend-static/README.i18n.yaml index 0514c3889e..2255848c81 100644 --- a/packages/host/frontend-static/README.i18n.yaml +++ b/packages/host/frontend-static/README.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 packages/host/frontend-static/README.md -README.md: e53a369384fb8d7fe020e99da0d2cacca59e8afd -README.zh.md: 7018d80182d477f2bdff7b1d8510971dc98c71d5 +README.md: fb9ab0c9fea6fdaffdc08ecb63b17ffff9ff1e16 +README.zh.md: b0fd0f3aac7e54ccd50f8e87a186fd080c925610 diff --git a/packages/host/frontend-static/README.md b/packages/host/frontend-static/README.md index e53a369384..fb9ab0c9fe 100644 --- a/packages/host/frontend-static/README.md +++ b/packages/host/frontend-static/README.md @@ -2,7 +2,7 @@ English | [中文](README.zh.md) -SPA dist server for the Web shell: a function plugin (config `{distIndex}`) that claims the [webserver](../webserver/README.md)'s single fallback seat and serves the built frontend directory with the shell's locked semantics — traversal outside the dist root is 403, any miss falls back to `index.html` with HTTP 200 (SPA routing), unknown extensions ship as `application/octet-stream`, and non-GET/HEAD without a matching named route is 405. Every index response runs through the webserver's registered index taps (`applyIndexTaps`), which is how the boot manifest reaches the page. `distIndex` is an assembly fact of the composing application: [`dsh-web-app`](../../bundle/web-app/README.md) resolves it through the frontend package's exports and mounts this plugin; a deployment never hardcodes it. +SPA dist server for the Web shell: a function plugin (config `{distIndex}`) that claims the [webserver](../webserver/README.md)'s single fallback seat and serves the built frontend directory with the shell's locked semantics — traversal outside the dist root is 403, any miss falls back to `index.html` with HTTP 200 (SPA routing), unknown extensions ship as `application/octet-stream`, and non-GET/HEAD without a matching named route is 405. Every index response is rendered through the webserver's `renderIndex` — structured injection rows first, then the raw index taps — which is how the boot manifest reaches the page. `distIndex` is an assembly fact of the composing application: [`dsh-web-app`](../../bundle/web-app/README.md) resolves it through the frontend package's exports and mounts this plugin; a deployment never hardcodes it. The fallback seat is single-owner (a second claim throws) and effect-scoped: disposing the plugin's fiber releases the seat, after which the unclaimed webserver answers 404. diff --git a/packages/host/frontend-static/README.zh.md b/packages/host/frontend-static/README.zh.md index 7018d80182..b0fd0f3aac 100644 --- a/packages/host/frontend-static/README.zh.md +++ b/packages/host/frontend-static/README.zh.md @@ -2,7 +2,7 @@ [English](README.md) | 中文 -Web 壳的 SPA dist 服务器:一个函数插件(配置为 `{distIndex}`),占据 [webserver](../webserver/README.md) 的唯一回退席位,并按壳层锁定的语义服务已构建的前端目录——越出 dist 根目录的遍历返回 403,任何未命中项都以 HTTP 200 回退到 `index.html`(SPA 路由),未知扩展名按 `application/octet-stream` 提供,GET/HEAD 之外的方法在没有匹配的具名路由时返回 405。每个 index 响应都会经过 webserver 已注册的 index 转换器(`applyIndexTaps`),启动 manifest(元数据清单)就是经这条路径送达页面的。`distIndex` 是组合应用的组装事实:[`dsh-web-app`](../../bundle/web-app/README.md) 通过前端包的 exports 解析它并挂载本插件;部署绝不硬编码它。 +Web 壳的 SPA dist 服务器:一个函数插件(配置为 `{distIndex}`),占据 [webserver](../webserver/README.md) 的唯一回退席位,并按壳层锁定的语义服务已构建的前端目录——越出 dist 根目录的遍历返回 403,任何未命中项都以 HTTP 200 回退到 `index.html`(SPA 路由),未知扩展名按 `application/octet-stream` 提供,GET/HEAD 之外的方法在没有匹配的具名路由时返回 405。每个 index 响应都经 webserver 的 `renderIndex` 渲染——先结构化注入行、后原始 index 转换器——启动 manifest(元数据清单)就是经这条路径送达页面的。`distIndex` 是组合应用的组装事实:[`dsh-web-app`](../../bundle/web-app/README.md) 通过前端包的 exports 解析它并挂载本插件;部署绝不硬编码它。 回退席位只有单一所有者(第二次占据会抛错),并受 effect 作用域约束:dispose(资源释放)插件的 fiber 会释放席位,此后无人占据的 webserver 回答 404。 diff --git a/packages/host/webserver/README.i18n.yaml b/packages/host/webserver/README.i18n.yaml index 591ca04216..a9301ea812 100644 --- a/packages/host/webserver/README.i18n.yaml +++ b/packages/host/webserver/README.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 packages/host/webserver/README.md -README.md: 7927c791a809ad272edce1310b3c50bbe30720a5 -README.zh.md: 82252adb3945f22c83abddbd3bbdd11556383add +README.md: b6424262305b062f4c57774c446ac1d50e490a03 +README.zh.md: cdeeb26159b98048f8ba5e821e7d6e733f447351 diff --git a/packages/host/webserver/README.md b/packages/host/webserver/README.md index 7927c791a8..b642426230 100644 --- a/packages/host/webserver/README.md +++ b/packages/host/webserver/README.md @@ -2,7 +2,7 @@ English | [中文](README.zh.md) -Web HTTP and upgrade-route registration plugin (default-exported `WebServer`, config `{host, port}`): a `node:http` server that listens on activation and provides `ctx.webServer`. `register(route)` adds a named `exact`/`prefix` HTTP route; `registerUpgrade(route)` adds an upgrade route for an exact pathname. A duplicate path within either table throws because route patterns are a composition-level contract and a collision is a misconfiguration; both methods return a disposer that removes the registration. `registerFallback(handler)` registers the one handler for requests that match no named route. A second registration throws; the SPA dist server [`dsh-host-frontend-static`](../frontend-static/README.md) is the shipped owner, and the server returns 404 while none is registered. `tapIndex(transform)` adds an index.html transform, and `applyIndexTaps(html)` runs a body through the registered transforms in order; the fallback handler calls it on every index response. `port` reads the listening port (the OS-assigned value when `port` is 0), and `host` reads the configured bind host (composition-time facts other plugins adapt to, e.g. the directory-picker chooser). HTTP match order is fixed: exact over the whole table, then longest prefix, then the fallback handler. Upgrades match exactly and unmatched connections are closed; registration order carries no request-facing semantics. +Web HTTP and upgrade-route registration plugin (default-exported `WebServer`, config `{host, port}`): a `node:http` server that listens on activation and provides `ctx.webServer`. `register(route)` adds a named `exact`/`prefix` HTTP route; `registerUpgrade(route)` adds an upgrade route for an exact pathname. A duplicate path within either table throws because route patterns are a composition-level contract and a collision is a misconfiguration; both methods return a disposer that removes the registration. `registerFallback(handler)` registers the one handler for requests that match no named route. A second registration throws; the SPA dist server [`dsh-host-frontend-static`](../frontend-static/README.md) is the shipped owner, and the server returns 404 while none is registered. Index startup inputs are structured rows: `collectIndexInjections()` gathers a fresh `IndexInjection` table over one `webserver/index-inject` emit per call, and `renderIndex(html)` renders the rows into an index.html body before applying the raw `tapIndex(transform)` transforms in registration order (`applyIndexTaps(html)`, the escape hatch for markup no row expresses); the fallback handler calls `renderIndex` on every index response, and a static deployment ships the same rows over its boot payload, rendering with the exported `renderIndexInjections`. `port` reads the listening port (the OS-assigned value when `port` is 0), and `host` reads the configured bind host (composition-time facts other plugins adapt to, e.g. the directory-picker chooser). HTTP match order is fixed: exact over the whole table, then longest prefix, then the fallback handler. Upgrades match exactly and unmatched connections are closed; registration order carries no request-facing semantics. The package knows no harness concepts and serves no files: the `/api` HTTP bridge and downlink WebSockets are routes owned by the connection plugin, plugin bundles and the HMR event stream are routes owned by the modules/hmr plugins, and dist serving belongs to the fallback owner. The upgrade handler owns the protocol handshake and connection contents; the webserver only delivers the raw socket and request. `host` accepts only `127.0.0.1` (default posture) and `0.0.0.0` (deliberate network exposure). This server serves browsers only; Electron loads dist over `file://` and carries fetch over an IPC bridge. This package never prints; the URL line belongs to the shell. diff --git a/packages/host/webserver/README.zh.md b/packages/host/webserver/README.zh.md index 82252adb39..cdeeb26159 100644 --- a/packages/host/webserver/README.zh.md +++ b/packages/host/webserver/README.zh.md @@ -2,7 +2,7 @@ [English](README.md) | 中文 -Web HTTP 与 upgrade route 注册插件(默认导出 `WebServer`,配置为 `{host, port}`):一个在激活时开始监听的 `node:http` 服务器,提供 `ctx.webServer`。`register(route)` 添加具名的 `exact`/`prefix` HTTP route;`registerUpgrade(route)` 添加精确 pathname 的 upgrade route;同一张表内的重复路径会抛错,因为 route 模式是组合层约定,冲突即配置错误;两者返回的 disposer 都会移除注册。`registerFallback(handler)` 注册一个 handler,处理所有未被具名 route 命中的请求。第二次注册会抛错;随附的 SPA dist 服务器 [`dsh-host-frontend-static`](../frontend-static/README.md) 是该 handler 的所有者,没有注册 handler 时服务器返回 404。`tapIndex(transform)` 添加一个 index.html 转换,`applyIndexTaps(html)` 按注册顺序对一段响应体运行已注册的转换;fallback handler 在每次 index 响应时调用它。`port` 读取正在监听的端口(当 `port` 为 0 时读取 OS 分配的值),`host` 读取配置的绑定宿主(这些是其他插件据以自适应的组合期事实,例如 directory-picker 选择器)。HTTP 匹配顺序固定不变:先在整张表中匹配精确 route,再匹配最长前缀,最后交给 fallback handler。upgrade 只做精确匹配,未命中连接直接关闭;注册顺序不影响请求处理。 +Web HTTP 与 upgrade route 注册插件(默认导出 `WebServer`,配置为 `{host, port}`):一个在激活时开始监听的 `node:http` 服务器,提供 `ctx.webServer`。`register(route)` 添加具名的 `exact`/`prefix` HTTP route;`registerUpgrade(route)` 添加精确 pathname 的 upgrade route;同一张表内的重复路径会抛错,因为 route 模式是组合层约定,冲突即配置错误;两者返回的 disposer 都会移除注册。`registerFallback(handler)` 注册一个 handler,处理所有未被具名 route 命中的请求。第二次注册会抛错;随附的 SPA dist 服务器 [`dsh-host-frontend-static`](../frontend-static/README.md) 是该 handler 的所有者,没有注册 handler 时服务器返回 404。index 的启动输入是结构化行:`collectIndexInjections()` 每次调用经一次 `webserver/index-inject` emit 现收一张全新的 `IndexInjection` 表,`renderIndex(html)` 先把行渲染进 index.html 响应体,再按注册顺序应用原始的 `tapIndex(transform)` 转换(`applyIndexTaps(html)`,行无法表达的标记的逃生口);fallback handler 在每次 index 响应时调用 `renderIndex`,静态部署则把同一批行经 boot 载荷下发,用导出的 `renderIndexInjections` 渲染。`port` 读取正在监听的端口(当 `port` 为 0 时读取 OS 分配的值),`host` 读取配置的绑定宿主(这些是其他插件据以自适应的组合期事实,例如 directory-picker 选择器)。HTTP 匹配顺序固定不变:先在整张表中匹配精确 route,再匹配最长前缀,最后交给 fallback handler。upgrade 只做精确匹配,未命中连接直接关闭;注册顺序不影响请求处理。 该包不了解任何 harness 概念,也不提供任何文件服务:`/api` HTTP 桥接与下行 WebSocket 是 connection 插件的 route,插件 bundle 与 HMR(热模块替换)事件流是 modules/hmr 插件的 route,dist 服务则属于 fallback 持有者。upgrade handler 拥有协议握手与连接内容;webserver 只交付原始 socket 与 request。`host` 只接受 `127.0.0.1`(默认安全姿态)和 `0.0.0.0`(有意向网络开放)。该服务器只服务浏览器;Electron 通过 `file://` 加载 dist,并经 IPC 桥接承载 fetch。该包从不打印内容;URL 行属于 shell。 diff --git a/packages/host/webserver/src/index.ts b/packages/host/webserver/src/index.ts index 94aba72382..2257e79623 100644 --- a/packages/host/webserver/src/index.ts +++ b/packages/host/webserver/src/index.ts @@ -145,8 +145,9 @@ export class WebServer extends Service { } /** - * Register an index.html transform, applied by the fallback owner to every - * index response ({@link applyIndexTaps}) in registration order. + * Register a raw-HTML index transform, the escape hatch for markup no + * {@link IndexInjection} row expresses: {@link renderIndex} applies taps in + * registration order after rendering the structured rows. * @param transform - pure html-to-html function. * @returns the disposer removing the transform. */ diff --git a/packages/host/webserver/src/injections.ts b/packages/host/webserver/src/injections.ts index cb1e5336a5..dc38213e85 100644 --- a/packages/host/webserver/src/injections.ts +++ b/packages/host/webserver/src/injections.ts @@ -23,7 +23,7 @@ export type IndexInjection = * loader resolves worker-only URLs such as `/plugins/...`). */ | { kind: 'script-src'; placement: IndexInjectionPlacement; src: string } - /** A `