diff --git a/apps/mobile-shell/scripts/check-expo-config.mjs b/apps/mobile-shell/scripts/check-expo-config.mjs index 22ae2a2f0..120477e2e 100644 --- a/apps/mobile-shell/scripts/check-expo-config.mjs +++ b/apps/mobile-shell/scripts/check-expo-config.mjs @@ -63,6 +63,18 @@ function assertIncludes(list, expected, label) { } } +function assertSameList(actual, expected, label) { + if ( + !Array.isArray(actual) || + actual.length !== expected.length || + actual.some((value, index) => value !== expected[index]) + ) { + throw new Error( + `Expo config ${label} drifted: expected ${JSON.stringify(expected)} but got ${JSON.stringify(actual)}`, + ); + } +} + function findPlugin(name) { return expoConfig.plugins?.find((plugin) => Array.isArray(plugin) ? plugin[0] === name : plugin === name, @@ -76,6 +88,7 @@ assertEqual(expoConfig.version, packageConfig.version, 'version'); assertEqual(expoConfig.version, appConfig.version, 'app version'); assertEqual(expoConfig.orientation, 'default', 'orientation'); assertEqual(expoConfig.userInterfaceStyle, 'automatic', 'userInterfaceStyle'); +assertSameList(expoConfig.assetBundlePatterns, ['**/*'], 'asset bundle patterns'); assertEqual(expoConfig.icon, './assets/icon.png', 'icon'); assertEqual(expoConfig.splash?.image, './assets/icon.png', 'splash image'); assertEqual(expoConfig.splash?.resizeMode, 'contain', 'splash resize mode'); @@ -135,6 +148,7 @@ assertEqual( 'resize', 'Android software keyboard layout mode', ); +assertSameList(expoConfig.android?.permissions ?? [], [], 'Android explicit permissions'); assertIncludes( expoConfig.android?.blockedPermissions, 'android.permission.MANAGE_EXTERNAL_STORAGE', diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index 51775ed36..2a70ccd71 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -2373,6 +2373,6 @@ ## 2026-06-18 原生壳身份与 capability 作用域门禁 - 背景:Expo 移动壳和 Tauri 桌面壳已经具备首批真实 HostBridge 能力,但如果包身份、桥版本、Android 默认权限、Tauri 窗口列表或 capability 文件在后续迭代中漂移,会把 H5 主站装进更宽的宿主权限面。 -- 决策:移动壳配置门禁固定 Expo `name`、`slug`、`userInterfaceStyle`、`assetBundlePatterns`、`extra.genarrativeHostBridgeVersion`,并要求 Android 默认 `permissions` 为空,只通过 `blockedPermissions` 阻断当前不需要的高风险权限;新增权限必须先有真实宿主能力、系统权限说明和 H5 fallback。桌面壳配置门禁固定唯一 `label=main` 主窗口,`src-tauri/capabilities/` 只能存在 `main.json`,且该 capability 只能绑定 `windows=["main"]`、`permissions=["core:default","allow-host-bridge-request"]`,继续只暴露 `host_bridge_request` 一个受控入口。 -- 影响范围:`apps/mobile-shell/scripts/check-config.mjs`、`apps/desktop-shell/scripts/check-config.mjs`、Expo / Tauri HostBridge 方案文档。 +- 决策:移动壳配置门禁固定 Expo `name`、`slug`、`userInterfaceStyle`、`assetBundlePatterns`、`extra.genarrativeHostBridgeVersion`,并要求 Android 默认 `permissions` 为空,只通过 `blockedPermissions` 阻断当前不需要的高风险权限;`apps/mobile-shell/scripts/check-config.mjs` 检查源 `app.json`,`apps/mobile-shell/scripts/check-expo-config.mjs` 检查 Expo CLI 最终解析出的 public config,防止 config plugin 或解析阶段引入身份、资源或权限漂移。新增权限必须先有真实宿主能力、系统权限说明和 H5 fallback。桌面壳配置门禁固定唯一 `label=main` 主窗口,`src-tauri/capabilities/` 只能存在 `main.json`,且该 capability 只能绑定 `windows=["main"]`、`permissions=["core:default","allow-host-bridge-request"]`,继续只暴露 `host_bridge_request` 一个受控入口。 +- 影响范围:`apps/mobile-shell/scripts/check-config.mjs`、`apps/mobile-shell/scripts/check-expo-config.mjs`、`apps/desktop-shell/scripts/check-config.mjs`、Expo / Tauri HostBridge 方案文档。 - 验证方式:`npm run check:native-shells`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。 diff --git a/docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md b/docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md index 800a4667d..487b576b1 100644 --- a/docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md +++ b/docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md @@ -299,7 +299,7 @@ GameBridge 禁止: 2026-06-18 追加:移动壳默认显式关闭 Expo OTA 更新,直到存在真实发布通道、更新端点、签名 / 回滚策略和团队发布流程后再接入。`app.json` 只允许 `updates.enabled=false`,不得配置 `runtimeVersion`、release channel、EAS channel、`expo-updates` 插件或移动端 crash / analytics / CodePush 依赖;`apps/mobile-shell/scripts/check-config.mjs` 和 Expo public config smoke 会共同拒绝这些发布通道能力被提前打开,移动壳生产入口、HostBridge 和 URL/runtime 配置也不得提前初始化 Sentry、Firebase Analytics、PostHog、Amplitude、Segment、CodePush 或 Expo Updates。 -2026-06-18 追加:移动壳可分发身份、外观和默认权限进入配置门禁。Expo `name` 固定为 `Genarrative`,`slug` 固定为 `genarrative-mobile-shell`,`userInterfaceStyle` 固定为 `automatic`,`assetBundlePatterns` 固定为 `["**/*"]`,`extra.genarrativeHostBridgeVersion` 固定为 `1`;Android `permissions` 默认不得显式请求任何权限,所有当前不需要的高风险权限只能通过 `blockedPermissions` 阻断,后续新增权限必须先有真实宿主能力、系统权限说明和 H5 fallback 方案,再补配置与检查。 +2026-06-18 追加:移动壳可分发身份、外观和默认权限进入配置门禁。Expo `name` 固定为 `Genarrative`,`slug` 固定为 `genarrative-mobile-shell`,`userInterfaceStyle` 固定为 `automatic`,`assetBundlePatterns` 固定为 `["**/*"]`,`extra.genarrativeHostBridgeVersion` 固定为 `1`;Android `permissions` 默认不得显式请求任何权限,所有当前不需要的高风险权限只能通过 `blockedPermissions` 阻断,后续新增权限必须先有真实宿主能力、系统权限说明和 H5 fallback 方案,再补配置与检查。`apps/mobile-shell/scripts/check-config.mjs` 会检查源 `app.json`,`apps/mobile-shell/scripts/check-expo-config.mjs` 会检查 Expo CLI 最终解析出的 public config,避免 config plugin 或 Expo 解析阶段引入身份、资源或权限漂移。 ### Phase 3:Tauri 桌面壳 MVP