From 6843185a6c6245b0cda7a97868a46f44f8cefc3c Mon Sep 17 00:00:00 2001 From: kdletters Date: Thu, 18 Jun 2026 01:16:25 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=9F=E4=B8=80=E5=8E=9F=E7=94=9F=E5=A3=B3?= =?UTF-8?q?=E9=AA=8C=E6=94=B6=E9=97=A8=E7=A6=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增 check:native-shells 根级验收脚本 将 H5 HostBridge 与 Expo、Tauri 壳检查串成统一门禁 把原生壳验收入口写入方案文档和共享工作流 --- docs/README.md | 2 +- .../shared-memory/decision-log.md | 1 + .../shared-memory/development-workflow.md | 6 ++ ...ExpoReactNative与Tauri宿主壳方案-2026-06-17.md | 3 +- ...前端架构】宿主壳能力统一协议-2026-06-17.md | 3 +- package.json | 3 +- scripts/check-native-shells.mjs | 72 +++++++++++++++++++ 7 files changed, 86 insertions(+), 4 deletions(-) create mode 100644 scripts/check-native-shells.mjs diff --git a/docs/README.md b/docs/README.md index 584d1c7b..1c40e290 100644 --- a/docs/README.md +++ b/docs/README.md @@ -24,7 +24,7 @@ 微信小程序壳、未来原生 App 壳、固定内置玩法与 AI H5 沙箱之间的宿主能力边界见 [【前端架构】宿主壳能力统一协议-2026-06-17.md](./【前端架构】宿主壳能力统一协议-2026-06-17.md)。 移动端壳采用 Expo + React Native、桌面端壳采用 Tauri,并统一作为 HostBridge adapter 的分阶段方案见 [【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md](./【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md)。 -当前首轮工程入口:`npm run mobile-shell:dev`、`npm run mobile-shell:test`、`npm run desktop-shell:dev`、`npm run desktop-shell:test`。 +当前首轮工程入口:`npm run mobile-shell:dev`、`npm run desktop-shell:dev`;统一验收入口:`npm run check:native-shells`。排查单端问题时再分别运行 `npm run mobile-shell:typecheck`、`npm run mobile-shell:test`、`npm run desktop-shell:typecheck`、`npm run desktop-shell:test`。 本地通过 SSH alias 管理多台服务器、查看硬件 / systemd / HTTP 健康状态并执行受控服务启停的 egui 桌面工具见 [【开发运维】本地SSH服务器管理面板技术方案-2026-06-11.md](./technical/【开发运维】本地SSH服务器管理面板技术方案-2026-06-11.md)。 diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index 4eca5163..aaedf125 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -26,6 +26,7 @@ - 2026-06-18 能力声明收紧:`packages/shared/src/contracts/hostBridge.ts` 提供 HostBridge method / capability 白名单,H5 的 `getHostRuntime()` 会解析并过滤 `hostCapabilities`;`openHostShare`、`writeHostClipboardText`、`requestHostHapticsImpact`、`setHostAppTitle`、`exportHostTextFile` 等 native 能力只在宿主声明对应 capability 后调用。发布分享弹窗只有声明 `share.open` 时才显示“系统分享”,避免旧壳或裁剪壳露出不可用入口。 - 2026-06-18 宿主 runtime 回读:主 App 启动时会通过真实 `host.getRuntime` 回读 Expo / Tauri runtime 并缓存过滤后的能力清单,能力来源为 URL `hostCapabilities` 与宿主真实回包的并集;裁剪壳或旧入口 URL 缺少 `hostCapabilities` 时也能启用真实声明能力,但仍不会仅凭 `native_app` 或 transport 存在推断能力可用。 - 2026-06-18 壳能力防漂移:`npm run mobile-shell:typecheck` 与 `npm run desktop-shell:typecheck` 会校验 Expo / Tauri 壳声明的 capability 均来自共享 HostBridge 白名单,并校验壳 runtime 回包、H5 URL `hostCapabilities` 和实现分支保持一致;新增能力必须先更新契约和真实壳实现,再通过这些检查。 +- 2026-06-18 原生壳统一验收门禁:根级 `npm run check:native-shells` 统一执行 H5 HostBridge 关键测试、Expo 壳 typecheck / test 和 Tauri 壳 typecheck / cargo test;根级 `npm run check` 会在 lint、主站测试、构建和内容检查后继续执行该门禁,避免 HostBridge 与两端壳验收散落成容易漏跑的单项命令。 - 影响范围:`src/services/host-bridge/`、未来 `apps/mobile-shell/`、未来 `apps/desktop-shell/`、移动端支付 / 分享 / 深链 / 推送、桌面端系统能力、AI H5 sandbox 的 GameBridge 边界。 - 验证方式:普通浏览器、小程序、Expo 壳、Tauri 壳都能返回正确 `getHostRuntime()`;未支持能力能回退 H5;固定玩法在各宿主中读取同一作品数据和运行态 snapshot;AI sandbox 无法直接调用 HostBridge;Tauri release 不允许任意远端页面调用桌面命令。 - 关联文档:`docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md`、`docs/【前端架构】宿主壳能力统一协议-2026-06-17.md`。 diff --git a/docs/project-memory/shared-memory/development-workflow.md b/docs/project-memory/shared-memory/development-workflow.md index af677416..4dcfaf33 100644 --- a/docs/project-memory/shared-memory/development-workflow.md +++ b/docs/project-memory/shared-memory/development-workflow.md @@ -210,6 +210,12 @@ npm run test npm run build ``` +原生壳验收: + +```bash +npm run check:native-shells +``` + 内容检查: ```bash diff --git a/docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md b/docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md index 6d766e5f..06ad6e17 100644 --- a/docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md +++ b/docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md @@ -215,7 +215,7 @@ GameBridge 禁止: - 壳层只接受来自允许 origin / packaged asset 的消息。 - 每个请求必须有超时,重复 `id` 不得重复执行支付、登录等非幂等动作。 - 能力按 `capabilities` / `hostCapabilities` 下发,H5 会过滤未知能力,并根据声明结果决定是否展示入口、发起宿主请求或走 fallback;进入 `native_app` 后主 App 会再通过真实 `host.getRuntime` 回读一次宿主 runtime 并缓存能力,用来补齐裁剪壳或旧入口 URL 缺少 `hostCapabilities` 的场景。不能只凭 `native_app` 宿主类型假设能力可用。 -- 壳能力声明必须通过 `npm run mobile-shell:typecheck` / `npm run desktop-shell:typecheck` 校验:声明的 capability 必须存在于共享 HostBridge 白名单,壳 runtime 回包、H5 URL `hostCapabilities` 和壳实现不得漂移。 +- 壳能力声明与两端壳测试必须通过 `npm run check:native-shells` 统一校验;排查单端问题时可再分别运行 `npm run mobile-shell:typecheck`、`npm run mobile-shell:test`、`npm run desktop-shell:typecheck` 或 `npm run desktop-shell:test`。声明的 capability 必须存在于共享 HostBridge 白名单,壳 runtime 回包、H5 URL `hostCapabilities` 和壳实现不得漂移。 - 宿主壳不得把长期 token、支付密钥或用户敏感资料回传给 H5。 - Tauri 禁止把 shell / fs 等高危插件作为默认能力暴露给主 WebView。 - RN WebView 禁止打开任意 URL 后仍保留完整 HostBridge;跳外链只允许 `http:`、`https:`、`mailto:`、`tel:`,并使用系统浏览器或降级能力,危险协议直接阻断。 @@ -277,6 +277,7 @@ GameBridge 禁止: - AI sandbox 无法调用 HostBridge,也无法读取 H5 登录态。 - Tauri release 包不允许任意远端页面调用桌面命令。 - Expo WebView 外链离开主站后不保留完整 HostBridge。 +- 根级验收入口 `npm run check:native-shells` 必须同时覆盖 H5 HostBridge 关键路径、Expo 壳 typecheck / test、Tauri 壳 typecheck / cargo test。 ## 参考资料 diff --git a/docs/【前端架构】宿主壳能力统一协议-2026-06-17.md b/docs/【前端架构】宿主壳能力统一协议-2026-06-17.md index 33381e1c..0a542bb5 100644 --- a/docs/【前端架构】宿主壳能力统一协议-2026-06-17.md +++ b/docs/【前端架构】宿主壳能力统一协议-2026-06-17.md @@ -58,7 +58,7 @@ AI H5 sandbox 2. `authService` 保留原导出,但内部委托 HostBridge,避免一次性改动 AuthGate。 3. 分享弹窗、分享目标同步、九宫切图、微信小程序支付和订阅授权改用 HostBridge 通用接口;旧微信命名服务只作为兼容导出。 4. 后续新增 `native_app` adapter 时只补桥接实现和测试,业务层不新增平台分叉;主 App 启动会触发一次 `host.getRuntime` 回读并订阅能力变化,避免裁剪壳或旧入口 URL 缺少 `hostCapabilities` 时长期隐藏真实可用能力。 -5. 每次新增或调整 native capability 后,必须运行 `npm run mobile-shell:typecheck` 和 `npm run desktop-shell:typecheck`,确保共享白名单、壳 runtime 回包和 URL `hostCapabilities` 没有漂移。 +5. 每次新增或调整 native capability 后,必须运行 `npm run check:native-shells`,统一覆盖 H5 HostBridge 关键测试、Expo 壳 typecheck / test 和 Tauri 壳 typecheck / cargo test;排查单端问题时再单独运行 `npm run mobile-shell:typecheck`、`npm run mobile-shell:test`、`npm run desktop-shell:typecheck` 或 `npm run desktop-shell:test`。 ## 验收 @@ -67,6 +67,7 @@ AI H5 sandbox - 小程序支付仍跳转 `/pages/wechat-pay/index` 并保留支付结果 hash 回灌确认。 - 小程序订阅授权仍跳转 `/pages/subscribe-message/index`,且返回不阻断生成主链路。 - 普通浏览器分享、H5 支付和 Native 二维码支付不受影响。 +- 原生壳统一验收入口 `npm run check:native-shells` 通过,能力白名单、壳 runtime 回包、URL `hostCapabilities`、H5 fallback 和两端壳实现没有漂移。 ## 后续 diff --git a/package.json b/package.json index cc66baab..a688674c 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "desktop-shell:build": "npm --prefix apps/desktop-shell run build --", "desktop-shell:typecheck": "npm --prefix apps/desktop-shell run typecheck", "desktop-shell:test": "cargo test --manifest-path apps/desktop-shell/src-tauri/Cargo.toml", + "check:native-shells": "node scripts/check-native-shells.mjs", "server-manager:panel": "cargo run -p server-manager-panel --manifest-path server-rs/Cargo.toml", "dev:spacetime:logs": "node scripts/run-bash-script.mjs scripts/spacetime-logs-local.sh", "otel:debug": "node scripts/run-otelcol.mjs debug", @@ -63,7 +64,7 @@ "container:config": "node scripts/container-compose.mjs config", "container:k6": "node scripts/container-compose.mjs k6", "container:worker-smoke": "node scripts/container-worker-smoke.mjs", - "check": "npm run lint && npm run test && npm run build && npm run check:content", + "check": "npm run lint && npm run test && npm run build && npm run check:content && npm run check:native-shells", "check:data": "node scripts/run-tsx.cjs scripts/validate-content.ts", "check:overrides": "node scripts/run-tsx.cjs scripts/validate-overrides.ts", "check:smoke": "node scripts/run-tsx.cjs scripts/smoke-content.ts", diff --git a/scripts/check-native-shells.mjs b/scripts/check-native-shells.mjs new file mode 100644 index 00000000..ddc811aa --- /dev/null +++ b/scripts/check-native-shells.mjs @@ -0,0 +1,72 @@ +#!/usr/bin/env node + +import {spawnSync} from 'node:child_process'; + +const npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm'; + +const h5HostBridgeTests = [ + 'packages/shared/src/contracts/hostBridge.test.ts', + 'src/services/host-bridge/hostBridge.test.ts', + 'src/services/host-bridge/nativeAppHostBridge.test.ts', + 'src/App.test.tsx', + 'src/components/common/PublishShareModal.test.tsx', + 'src/services/runtimeAudioFeedback.test.ts', + 'src/services/clipboard.test.ts', + 'src/services/appTitle.test.ts', +]; + +const steps = [ + { + label: 'h5-host-bridge-tests', + command: npmCommand, + args: ['run', 'test', '--', ...h5HostBridgeTests], + }, + { + label: 'mobile-shell-typecheck', + command: npmCommand, + args: ['run', 'mobile-shell:typecheck'], + }, + { + label: 'mobile-shell-test', + command: npmCommand, + args: ['run', 'mobile-shell:test'], + }, + { + label: 'desktop-shell-typecheck', + command: npmCommand, + args: ['run', 'desktop-shell:typecheck'], + }, + { + label: 'desktop-shell-test', + command: npmCommand, + args: ['run', 'desktop-shell:test'], + }, +]; + +for (const step of steps) { + console.log(`[check:native-shells] ${step.label}`); + const result = spawnSync(step.command, step.args, { + cwd: process.cwd(), + stdio: 'inherit', + }); + + if (result.error) { + console.error( + `[check:native-shells] failed to start ${step.label}: ${result.error.message}`, + ); + process.exit(1); + } + + if (result.signal) { + console.error( + `[check:native-shells] ${step.label} was terminated by signal ${result.signal}`, + ); + process.exit(1); + } + + if ((result.status ?? 0) !== 0) { + process.exit(result.status ?? 1); + } +} + +console.log('[check:native-shells] OK');