统一原生壳验收门禁
新增 check:native-shells 根级验收脚本 将 H5 HostBridge 与 Expo、Tauri 壳检查串成统一门禁 把原生壳验收入口写入方案文档和共享工作流
This commit is contained in:
@@ -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)。
|
||||
|
||||
|
||||
@@ -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`。
|
||||
|
||||
@@ -210,6 +210,12 @@ npm run test
|
||||
npm run build
|
||||
```
|
||||
|
||||
原生壳验收:
|
||||
|
||||
```bash
|
||||
npm run check:native-shells
|
||||
```
|
||||
|
||||
内容检查:
|
||||
|
||||
```bash
|
||||
|
||||
@@ -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。
|
||||
|
||||
## 参考资料
|
||||
|
||||
|
||||
@@ -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 和两端壳实现没有漂移。
|
||||
|
||||
## 后续
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
72
scripts/check-native-shells.mjs
Normal file
72
scripts/check-native-shells.mjs
Normal file
@@ -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');
|
||||
Reference in New Issue
Block a user