锁定移动壳产物宿主上下文
移动 Expo export smoke 校验生产 bundle 宿主上下文 token 移动壳配置门禁反查 export smoke 宿主上下文验收 补充原生壳方案文档和共享决策记录
This commit is contained in:
@@ -8,6 +8,11 @@ const appPath = new URL('../App.tsx', import.meta.url);
|
||||
const appSource = fs.readFileSync(appPath, 'utf8');
|
||||
const shellAppPath = new URL('../src/shell/ShellApp.tsx', import.meta.url);
|
||||
const shellAppSource = fs.readFileSync(shellAppPath, 'utf8');
|
||||
const expoExportSmokePath = new URL(
|
||||
'../scripts/check-expo-export.mjs',
|
||||
import.meta.url,
|
||||
);
|
||||
const expoExportSmokeSource = fs.readFileSync(expoExportSmokePath, 'utf8');
|
||||
const shellAppTestPath = new URL('../src/shell/ShellApp.test.tsx', import.meta.url);
|
||||
const shellAppTestSource = fs.readFileSync(shellAppTestPath, 'utf8');
|
||||
const qrScannerOverlayPath = new URL('../src/shell/QrScannerOverlay.tsx', import.meta.url);
|
||||
@@ -650,6 +655,20 @@ assertSameList(
|
||||
'mobile shell smoke scripts',
|
||||
);
|
||||
|
||||
for (const snippet of [
|
||||
'const requiredNativeHostContextTokens = [',
|
||||
"'native_app'",
|
||||
"'expo_mobile'",
|
||||
"'hostCapabilities'",
|
||||
"'hostVersion'",
|
||||
"'bridgeVersion'",
|
||||
'production bundle must include native host context token',
|
||||
]) {
|
||||
if (!expoExportSmokeSource.includes(snippet)) {
|
||||
throw new Error(`mobile shell Expo export smoke must verify host context token ${snippet}`);
|
||||
}
|
||||
}
|
||||
|
||||
assertNoDevScaffoldTerms(
|
||||
productionSourceRoots.flatMap((root) => collectProductionSourceFiles(root)),
|
||||
);
|
||||
|
||||
@@ -41,6 +41,13 @@ function readHostBridgePublicWebUrl() {
|
||||
}
|
||||
|
||||
const expectedPublicWebUrl = readHostBridgePublicWebUrl();
|
||||
const requiredNativeHostContextTokens = [
|
||||
'native_app',
|
||||
'expo_mobile',
|
||||
'hostCapabilities',
|
||||
'hostVersion',
|
||||
'bridgeVersion',
|
||||
];
|
||||
|
||||
function runExpoExport(platform) {
|
||||
const outputDir = `.expo-export-smoke/${platform}`;
|
||||
@@ -123,6 +130,14 @@ function assertBundle(platform, bundlePath) {
|
||||
);
|
||||
}
|
||||
|
||||
for (const token of requiredNativeHostContextTokens) {
|
||||
if (!bundleSource.includes(token)) {
|
||||
throw new Error(
|
||||
`Expo ${platform} production bundle must include native host context token ${token}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (const blockedPattern of blockedDevelopmentWebUrlPatterns) {
|
||||
if (blockedPattern.test(bundleSource)) {
|
||||
throw new Error(
|
||||
|
||||
@@ -172,6 +172,7 @@
|
||||
- 2026-06-20 桌面 HostBridge command facade 单测边界:Tauri 唯一 `host_bridge_request` command 必须先通过 `prepare_host_bridge_request(...)` 做 envelope、method 和 request id 校验,再进入 `HostBridgeReplayState` reserve / wait / execute;`apps/desktop-shell/src-tauri/src/host_bridge/mod.rs` 的单测必须覆盖非法 envelope 在 replay 前返回 `invalid_request` 且不会占用对应 request id 的 replay slot,桌面配置检查会反查该测试存在。
|
||||
- 2026-06-20 桌面 HostBridge replay 内部失败边界:Tauri `HostBridgeReplayState` 的 cache lock、slot lock 和 condvar wait 异常不得 panic,也不得把 Rust 内部错误细节回传给 H5;桌面壳只写 `desktop host bridge replay failed for ...` stderr 观测日志,并统一返回 `host_error: desktop host bridge request failed`。桌面配置检查反查 `reserve(...)` 的 `Result` 出口、稳定错误响应和 poison lock 单测。
|
||||
- 2026-06-20 移动 HostBridge runtime 能力回包边界:Expo `host.getRuntime` 回包里的 `capabilities` 必须直接等于共享契约 `HOST_BRIDGE_EXPO_MOBILE_BASE_CAPABILITIES` 或 `HOST_BRIDGE_EXPO_MOBILE_IOS_CAPABILITIES`,并使用与 `platform` 字段一致的归一平台值选择 profile;移动壳 runtime 单测和配置检查反查精确 profile 断言,避免 H5 实际消费的能力回包与入口 URL 能力 query 或共享 profile 分叉。
|
||||
- 2026-06-20 移动 production bundle 宿主上下文边界:`apps/mobile-shell/scripts/check-expo-export.mjs` 必须读取 iOS / Android Metro export bundle,确认可分发 bundle 包含共享生产 H5 URL、`native_app`、`expo_mobile`、`hostCapabilities`、`hostVersion` 和 `bridgeVersion`,且不包含本机开发 H5 URL;移动壳配置检查反查 export smoke 的这些 token,避免生产 bundle 丢失宿主上下文或混入本机入口。
|
||||
- 2026-06-20 移动壳协议 helper 单测边界:`apps/mobile-shell/src/host-bridge/protocol.test.ts` 直接覆盖 Expo 移动壳 HostBridge JSON 解析、envelope 和 request id 校验、未知 method 拒绝、ok / failure 响应包装、unsupported / invalid_request 错误构造,以及 native helper 错误归一时只透传共享错误码与字符串 message,不泄露非法错误码、nativeStack 或其它私有字段;根级 `npm run check:native-shells` 会把该测试文件列入移动桥接层结构清单,避免协议边界只靠完整 bridge 流程间接覆盖。
|
||||
- 2026-06-20 移动扫码 overlay 单测边界:`apps/mobile-shell/src/shell/QrScannerOverlay.test.tsx` 直接覆盖移动扫码 overlay 的相机权限请求、二维码扫码成功、权限拒绝失败和关闭取消;单端配置检查会反查该组件测试存在,根级 `npm run check:native-shells` 会把该测试文件列入移动 shell 层结构清单,避免扫码 UI 容器只靠 `ShellApp.test.tsx` 的完整 HostBridge 流程间接覆盖。
|
||||
- 2026-06-18 HostBridge method 白名单跨壳门禁:`packages/shared/src/contracts/hostBridge.ts` 的 `HOST_BRIDGE_METHODS` 是唯一协议来源;Expo 壳 HostBridge 分发不得处理共享契约外 method,Tauri 壳 Rust `HOST_BRIDGE_METHODS` 必须与共享契约逐项一致。新增宿主 method 必须先更新共享契约,再落两端壳实现或明确 unsupported。
|
||||
|
||||
@@ -451,7 +451,7 @@ GameBridge 禁止:
|
||||
|
||||
2026-06-18 追加:桌面壳接入 Tauri deep-link 插件,但不开放插件 JS guest API,也不把 deep-link 命令加入 capability。桌面配置只注册 `genarrative` scheme;Rust 层只接受 `genarrative://open/...`、`genarrative://app/...`、`genarrative://<path>` 和 `https://app.genarrative.world/...`,统一跳到同源 H5 路径并补写 `clientRuntime=native_app`、`hostShell=tauri_desktop`、当前平台、版本与真实 `hostCapabilities`。外域、`http:`、`mailto:`、`javascript:`、`file:` 等来源不进入主 WebView;归一成功后的 `window.navigate(...)` 与主窗口恢复 / 聚焦失败必须记录桌面宿主事件日志,不能静默吞错。
|
||||
|
||||
2026-06-20 追加:移动壳 production bundle smoke 必须读取 Metro 导出的 iOS / Android JS bundle,并确认 bundle 内包含共享 HostBridge 契约中的生产 H5 URL,且不包含 `http://localhost`、`http://127.0.0.1` 或 `http://[::1]` 这类本机开发 H5 入口。开发态仍允许 `EXPO_PUBLIC_GENARRATIVE_WEB_URL` 指向本机 Vite,但可分发包验收不能让本机 URL 混入生产 bundle。
|
||||
2026-06-20 追加:移动壳 production bundle smoke 必须读取 Metro 导出的 iOS / Android JS bundle,并确认 bundle 内包含共享 HostBridge 契约中的生产 H5 URL、`native_app` 宿主上下文、`expo_mobile` 壳标识以及 `hostCapabilities` / `hostVersion` / `bridgeVersion` 等启动 query token,且不包含 `http://localhost`、`http://127.0.0.1` 或 `http://[::1]` 这类本机开发 H5 入口。开发态仍允许 `EXPO_PUBLIC_GENARRATIVE_WEB_URL` 指向本机 Vite,但可分发包验收不能让本机 URL 混入生产 bundle,也不能丢掉 H5 进入原生宿主运行态所需的上下文 token。
|
||||
|
||||
2026-06-18 追加:桌面壳安装包身份固定为 `world.genarrative.desktop`,产品名为 `Genarrative`,Tauri、Node package 与 Cargo package 版本统一为 `0.1.0`。Release 主窗口只能从打包进二进制的 `index.html` 进入根 `dist` H5 资产,dev URL 只能指向本机 Vite 调试入口;CSP 必须保持 `script-src 'self'`,不得加入 `unsafe-eval`、`tauri:` 或 `file:` 这类扩大桌面攻击面的来源。当前不配置自动更新器,直到存在真实更新端点、签名密钥和发布流程再接入;`apps/desktop-shell/scripts/check-config.mjs` 会校验这些包身份、版本、CSP 和 updater 禁用约束。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user