收口桌面壳未声明能力语义

桌面壳未声明共享方法自动派生 unsupported 覆盖清单

桌面壳测试补齐拍照扫码触觉等未声明方法的 unsupported 语义

架构文档记录桌面壳未声明能力不得伪成功
This commit is contained in:
2026-06-20 03:25:52 +08:00
parent 8f107cd991
commit 1355d6f90b
5 changed files with 36 additions and 8 deletions
+26 -5
View File
@@ -1466,11 +1466,9 @@ const desktopCapabilities = extractDesktopCapabilities(
const desktopHandledMethods = extractDesktopHandledMethods(
desktopHostBridgeDispatchSource,
);
const hostBridgeAcceptedUnsupportedMethods = [
'auth.requestLogin',
'payment.request',
'scanner.scanQrCode',
];
const hostBridgeAcceptedUnsupportedMethods = sharedMethods.filter(
(method) => !desktopCapabilities.includes(method),
);
if (desktopHostBridgeProtocol !== sharedHostBridgeProtocol) {
throw new Error(
`desktop shell HostBridge protocol drifted: expected ${sharedHostBridgeProtocol} but got ${desktopHostBridgeProtocol}`,
@@ -1675,6 +1673,29 @@ for (const capability of ['auth.requestLogin', 'payment.request']) {
}
}
if (
!desktopHostBridgeDispatchSource.includes(
'format!("{} unsupported in desktop shell", request.method)',
)
) {
throw new Error(
'desktop shell unsupported HostBridge methods must return explicit unsupported_method errors',
);
}
const desktopUnsupportedMethodTestMatch = desktopHostBridgeDispatchSource.match(
/fn unsupported_method_is_explicit\(\)[\s\S]*?for method in \[([^\]]+)\]/,
);
if (!desktopUnsupportedMethodTestMatch) {
throw new Error('desktop shell unsupported HostBridge method test is missing');
}
assertSameList(
[...desktopUnsupportedMethodTestMatch[1].matchAll(/"([^"]+)"/g)].map(
(match) => match[1],
),
hostBridgeAcceptedUnsupportedMethods,
'desktop shell unsupported HostBridge method coverage',
);
const missingDesktopMethodHandlers = desktopCapabilities.filter(
(capability) =>
sharedMethods.includes(capability) &&
@@ -78,7 +78,13 @@ mod tests {
#[test]
fn unsupported_method_is_explicit() {
for method in ["auth.requestLogin", "payment.request", "scanner.scanQrCode"] {
for method in [
"auth.requestLogin",
"payment.request",
"file.captureImage",
"scanner.scanQrCode",
"haptics.impact",
] {
let response = resolve_host_bridge_request(request(method));
assert!(!response.ok);
@@ -50,6 +50,7 @@
- 2026-06-18 微信壳桥接层纳入统一验收:`npm run check:native-shells` 还会运行 `miniprogram/host-bridge/``miniprogram/shell/``pages/web-view` 样式和 `scripts/miniprogram-web-view-auth.test.ts` 的微信壳测试,覆盖 WebView 入口、登录触发、分享目标、支付结果、订阅消息结果和九宫切图行为;三端桥接层文件结构检查只证明目录边界,行为回归必须由同一门禁中的微信壳测试证明。
- 2026-06-19 微信壳路由一致性门禁:`npm run check:native-shells` 必须反查 `miniprogram/app.json.pages``miniprogram/host-bridge/protocol.js`、H5 `src/services/host-bridge/hostBridge.ts` 小程序页面常量、H5 `src/services/wechatMiniProgramSubscribe.ts` 订阅授权页面常量、`miniprogram/host-bridge/webView.js` 分享入口 / 分享消息类型、`miniprogram/config.js` source query / 域名格式、`miniprogram/shell/webView.js` 请求头来源标记、H5 runtime parser 和 H5 路由保留字段。新增或调整小程序页面、登录派生 URL、支付页、九宫切图页、订阅页、WebView 来源标记、H5 入口域名、API base URL 或宿主上下文 query 字段时,必须同步这几处常量并保持生产 / 开发域名都显式配置为纯 HTTPS domain;运行时开发域名回退生产域名只作为异常兜底。
- 2026-06-18 登录 / 支付能力禁伪声明:`auth.requestLogin``payment.request` 保留在共享 HostBridge 契约中供未来真实接入,但 Expo / Tauri 壳在真实 SDK、渠道流程和后端契约落地前不得声明这些 capability,也不得把它们写入入口 URL `hostCapabilities`;两端检查脚本会拒绝伪声明,请求实际到达壳层时必须返回明确 `unsupported_method` 并让 H5 fallback,两端壳测试直接覆盖这两个 method。
- 2026-06-20 桌面壳未声明 method 禁伪成功:Tauri 桌面壳只声明真实可用 capability;共享 HostBridge method 白名单中未进入桌面 capability profile 的 method,例如 `auth.requestLogin``payment.request``file.captureImage``scanner.scanQrCode``haptics.impact`,请求实际到达桌面壳时必须统一返回明确 `unsupported_method`,不得伪造成功或半接入。桌面单端配置检查会从共享 method 与桌面 capability 差集自动派生 unsupported 测试覆盖清单,后续共享契约新增 method 时必须同步声明真实桌面能力或补进 unsupported 语义。
- 2026-06-18 移动壳触觉反馈边界:`haptics.impact` 只接受 `light``medium``heavy` 三档 impact style,缺省为 `light`;未知值必须返回 `invalid_request`,不得静默降级成真实设备触觉反馈。桌面壳不声明该 capabilityH5 继续按 HostBridge fallback 处理。
- 2026-06-19 移动壳触觉反馈模块边界:Expo `haptics.impact` 的 HostBridge payload 解析、共享 style 归一、`light` / `medium` / `heavy``Haptics.ImpactFeedbackStyle` 的映射、真实 `Haptics.impactAsync(...)` 调用和成功响应包装统一收口在 `apps/mobile-shell/src/host-bridge/haptics.ts``dispatch.ts` 只负责把完整 request 委托给 `runMobileHostBridgeHapticsImpact(...)`,不得直接导入 `expo-haptics`、读取 `HapticsImpactPayload`、调用 `Haptics.impactAsync` 或包装触觉反馈成功响应。移动壳配置检查会覆盖该模块结构、共享 style 边界和 dispatch 委托关系,避免触觉反馈能力散落到分发层。
- 2026-06-18 分享卡图片导出:新增 `file.exportImage` HostBridge capabilityH5 分享卡下载在 native app 中优先把 canvas 生成的 base64 图片交给宿主导出;Expo 壳写缓存图片后交给系统分享 / 保存面板,Tauri 壳通过系统保存对话框写入图片字节。该能力只接受 `image/png` / `image/jpeg` / `image/webp`、单次 5 MiB 内图片数据,成功只返回文件名和字节数,不暴露本机绝对路径;宿主未声明时保留浏览器下载。Expo 图片导出的 payload 校验、缓存写入、系统分享 / 保存面板和 HostBridge 成功响应包装统一收口在 `apps/mobile-shell/src/host-bridge/files.ts``dispatch.ts` 只委托文件模块。
@@ -473,7 +473,7 @@ GameBridge 禁止:
2026-06-19 追加:HostBridge event 白名单进入跨壳门禁。`packages/shared/src/contracts/hostBridge.ts``HOST_BRIDGE_EVENTS` 是宿主注入事件名的唯一来源,当前只包含 `app.lifecycle``network.statusChanged``navigation.canGoBack``file.imageDropped`;事件名必须同时是 capability。Expo 壳事件注入函数使用 `HostBridgeEventName`Tauri 壳 `shell/events.rs` 镜像同一清单并在脚本生成前拒绝未知事件,H5 `nativeAppHostBridge` 只分发 `isHostBridgeEventName()` 认可的事件。H5 `subscribeHostAppLifecycle()``subscribeHostNetworkStatusChange()``subscribeHostNavigationCanGoBack()``subscribeHostImageDrop()` 必须同时校验 `host.events` 与对应事件 capability,缺任一能力时不绑定事件监听;根级 `npm run check:native-shells` 会反查这四个订阅 facade 都通过 `canUseNativeHostEventCapability(...)` 进入同一双能力门控,并拒绝新增事件后漏补 H5 facade 覆盖。
2026-06-18 追加:HostBridge capability 与 request handler 关系进入门禁。共享契约中属于 request method 的 capability,如果被 Expo 或 Tauri 壳声明,就必须在对应壳的 HostBridge 分发中显式处理;反过来,壳分发中处理的 method 必须已被该壳声明,登录 / 支付等等待真实 SDK 的 method 只能保留明确 `unsupported_method` 路径`host.events``app.lifecycle``network.statusChanged``file.imageDropped``navigation.canGoBack` 等事件类 capability 不要求 request handler。
2026-06-18 追加:HostBridge capability 与 request handler 关系进入门禁。共享契约中属于 request method 的 capability,如果被 Expo 或 Tauri 壳声明,就必须在对应壳的 HostBridge 分发中显式处理;反过来,壳分发中处理的 method 必须已被该壳声明。共享 method 白名单中未进入桌面 capability profile 的 method 必须自动派生为桌面 unsupported 覆盖清单,当前包括 `auth.requestLogin``payment.request``file.captureImage``scanner.scanQrCode``haptics.impact`;请求实际到达桌面壳时只能返回明确 `unsupported_method`,不得伪造成功或半接入`host.events``app.lifecycle``network.statusChanged``file.imageDropped``navigation.canGoBack` 等事件类 capability 不要求 request handler。
2026-06-18 追加:桌面壳 release CSP 与 dev CSP 分离。Release `csp` 不再包含 `http://127.0.0.1:*``ws://127.0.0.1:*`,只允许打包资产、自身脚本、生产 HTTPS / WSS API、图片、媒体和 sandbox frame 所需来源;本地 Vite、HMR WebSocket 和开发 frame 只写入 Tauri `devCsp``apps/desktop-shell/scripts/check-config.mjs` 会拒绝 release CSP 混入本机调试源,也会校验 dev CSP 仍保留本机开发源。
@@ -92,7 +92,7 @@ Tauri 桌面壳的文件能力边界统一在 `apps/desktop-shell/src-tauri/src/
2. `authService` 保留原导出,但内部委托 HostBridge,避免一次性改动 AuthGate。
3. 分享弹窗、分享目标同步、九宫切图、微信小程序支付和订阅授权改用 HostBridge 通用接口;旧微信命名服务只作为兼容导出。
4. 后续新增 `native_app` adapter 时只补桥接实现和测试,业务层不新增平台分叉;主 App 启动会触发一次 `host.getRuntime` 回读并订阅能力变化,避免裁剪壳或旧入口 URL 缺少 `hostCapabilities` 时长期隐藏真实可用能力。
5. 每次新增或调整 native capability、HostBridge event 或宿主上下文 query 后,必须先更新 `packages/shared/src/contracts/hostBridge.ts` 中对应微信 / Expo / Tauri capability profile、事件白名单和 query 契约,再运行 `npm run check:native-shells`,统一覆盖 H5 HostBridge 关键测试、三端桥接层文件结构门禁、微信小程序页面路由与 H5 常量反查、Expo 壳 typecheck / test / config smoke / Metro export smoke、Tauri 壳 typecheck / cargo test、桌面 release `--no-bundle` 构建烟测,以及可分发壳与 H5 HostBridge 真实调用链的临时替身词扫描;排查单端问题时再单独运行 `npm run mobile-shell:typecheck``npm run mobile-shell:test``npm run mobile-shell:config``npm run mobile-shell:export``npm run desktop-shell:typecheck``npm run desktop-shell:test``npm run desktop-shell:build -- --no-bundle`
5. 每次新增或调整 native capability、HostBridge event 或宿主上下文 query 后,必须先更新 `packages/shared/src/contracts/hostBridge.ts` 中对应微信 / Expo / Tauri capability profile、事件白名单和 query 契约,再运行 `npm run check:native-shells`,统一覆盖 H5 HostBridge 关键测试、三端桥接层文件结构门禁、微信小程序页面路由与 H5 常量反查、Expo 壳 typecheck / test / config smoke / Metro export smoke、Tauri 壳 typecheck / cargo test、桌面 release `--no-bundle` 构建烟测,以及可分发壳与 H5 HostBridge 真实调用链的临时替身词扫描。桌面壳未声明的共享 request method 必须自动派生为 `unsupported_method` 覆盖清单,当前包括 `auth.requestLogin``payment.request``file.captureImage``scanner.scanQrCode``haptics.impact`,不得伪造成功;排查单端问题时再单独运行 `npm run mobile-shell:typecheck``npm run mobile-shell:test``npm run mobile-shell:config``npm run mobile-shell:export``npm run desktop-shell:typecheck``npm run desktop-shell:test``npm run desktop-shell:build -- --no-bundle`
## 验收