diff --git a/apps/mobile-shell/scripts/check-config.mjs b/apps/mobile-shell/scripts/check-config.mjs index d55053dc1..31c346079 100644 --- a/apps/mobile-shell/scripts/check-config.mjs +++ b/apps/mobile-shell/scripts/check-config.mjs @@ -712,10 +712,10 @@ if (appConfig.version !== '0.1.0') { const mobileHostVersion = extractStringConstExport( runtimeSource, - 'MOBILE_SHELL_HOST_VERSION', + 'MOBILE_SHELL_HOST_VERSION_FALLBACK', ); if (mobileHostVersion !== appConfig.version) { - throw new Error('mobile shell HostBridge host version must match app.json version'); + throw new Error('mobile shell HostBridge fallback host version must match app.json version'); } if (appConfig.orientation !== 'default') { @@ -1323,6 +1323,21 @@ if (!dispatchSource.includes('hostVersion: MOBILE_SHELL_HOST_VERSION')) { throw new Error('mobile shell runtime response must use the shared mobile shell host version'); } +for (const snippet of [ + "import appConfig from '../../app.json'", + 'resolveMobileShellHostVersion(', + 'appConfig.expo?.version', + 'MOBILE_SHELL_HOST_VERSION_FALLBACK', +]) { + if (!runtimeSource.includes(snippet)) { + throw new Error(`mobile shell runtime version source missing ${snippet}`); + } +} + +if (/export const MOBILE_SHELL_HOST_VERSION\s*=\s*'/.test(runtimeSource)) { + throw new Error('mobile shell host version must be resolved from Expo runtime config'); +} + if ( shellAppSource.includes("hostVersion: '0.1.0'") || hostBridgeSource.includes("hostVersion: '0.1.0'") diff --git a/apps/mobile-shell/src/shell/runtime.test.ts b/apps/mobile-shell/src/shell/runtime.test.ts new file mode 100644 index 000000000..9a7c032b2 --- /dev/null +++ b/apps/mobile-shell/src/shell/runtime.test.ts @@ -0,0 +1,14 @@ +import { describe, expect, test } from 'vitest'; + +import { resolveMobileShellHostVersion } from './runtime'; + +describe('resolveMobileShellHostVersion', () => { + test('uses Expo runtime config version when available', () => { + expect(resolveMobileShellHostVersion('0.2.3')).toBe('0.2.3'); + }); + + test('falls back when Expo runtime config version is unavailable', () => { + expect(resolveMobileShellHostVersion(null)).toBe('0.1.0'); + expect(resolveMobileShellHostVersion(' ')).toBe('0.1.0'); + }); +}); diff --git a/apps/mobile-shell/src/shell/runtime.ts b/apps/mobile-shell/src/shell/runtime.ts index 5563ba9a3..eca2b72c6 100644 --- a/apps/mobile-shell/src/shell/runtime.ts +++ b/apps/mobile-shell/src/shell/runtime.ts @@ -1 +1,18 @@ -export const MOBILE_SHELL_HOST_VERSION = '0.1.0'; +import appConfig from '../../app.json'; + +export const MOBILE_SHELL_HOST_VERSION_FALLBACK = '0.1.0'; + +export function resolveMobileShellHostVersion( + expoConfigVersion: unknown, +): string { + if (typeof expoConfigVersion !== 'string') { + return MOBILE_SHELL_HOST_VERSION_FALLBACK; + } + + const version = expoConfigVersion.trim(); + return version.length > 0 ? version : MOBILE_SHELL_HOST_VERSION_FALLBACK; +} + +export const MOBILE_SHELL_HOST_VERSION = resolveMobileShellHostVersion( + appConfig.expo?.version, +); diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index 318e89cd2..6bebfdc32 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -61,7 +61,7 @@ - 2026-06-18 桌面壳协议常量来源:Tauri Rust 侧 `host_bridge/protocol.rs` 的 `HOST_BRIDGE_PROTOCOL` / `HOST_BRIDGE_VERSION`、桌面入口 URL `bridgeVersion`、HostBridge event 注入和 runtime 回包必须与 `packages/shared/src/contracts/hostBridge.ts` 保持一致;桌面配置检查会反查共享契约并拒绝协议名或协议版本漂移。`tauri.conf.json` 只保留基础入口,`shell/url.rs` 统一补写桌面宿主上下文和真实 capability 清单,配置检查会拒绝把 `hostCapabilities` 等宿主 query 长串重新写回 Tauri 配置。 - 2026-06-18 移动壳默认入口:Expo 壳默认 H5 地址固定为 `https://app.genarrative.world/`,开发联调本机 Vite 必须显式设置 `EXPO_PUBLIC_GENARRATIVE_WEB_URL=http://127.0.0.1:3000/`、`http://localhost:3000/` 或 `http://[::1]:3000/`;生产包不得在未配置环境变量时加载设备本机 localhost,也不得通过环境变量把第三方外域 H5 放入带完整 HostBridge 的 WebView。 - 2026-06-18 移动壳安装包身份:Expo 移动壳的 iOS bundle identifier 与 Android package 统一固定为 `world.genarrative.mobile`,应用版本固定为 `0.1.0`,iOS `buildNumber` 从字符串 `"1"` 起步,Android `versionCode` 从整数 `1` 起步;后续分发安装包时递增构建号 / versionCode,产品版本号按发布节奏调整。移动壳配置检查会校验 `app.json` 与 `package.json` 版本一致,并拒绝缺失或漂移的包标识,当前不写入假商店元数据、假更新端点或占位渠道 SDK 配置。 -- 2026-06-18 移动壳 HostBridge 版本单一来源:Expo 移动壳的 H5 入口 query 和 `host.getRuntime` 回包都读取 `MOBILE_SHELL_HOST_VERSION`,该常量必须与 `app.json` / `package.json` 版本一致;配置检查会拒绝 `App.tsx` 或 `bridge.ts` 重新散落硬编码版本,避免安装包版本升级时 H5 首屏上下文与 runtime 回读分叉。 +- 2026-06-19 移动壳 HostBridge 版本运行时来源:Expo 移动壳的 H5 入口 query 和 `host.getRuntime` 回包都读取 `MOBILE_SHELL_HOST_VERSION`,该值必须从移动壳 `app.json` 的 Expo `version` 配置解析,异常配置只回退到与 `app.json` / `package.json` 一致的受检 fallback;配置检查会拒绝 `App.tsx`、`bridge.ts` 或 `runtime.ts` 重新散落硬编码版本,避免安装包版本升级时 H5 首屏上下文与 runtime 回读分叉。 - 2026-06-18 移动壳发布通道边界:Expo 移动壳默认显式关闭 OTA 更新,只允许 `updates.enabled=false`;在真实发布通道、更新端点、签名 / 回滚策略和团队发布流程落地前,不得配置 `runtimeVersion`、release channel、EAS channel、`expo-updates` 插件或移动端 crash / analytics / CodePush 依赖。移动壳配置检查和 Expo public config smoke 会拒绝这些发布通道能力被提前打开,根 `package-lock.json` 也不得解析 `expo-updates`、Sentry、Firebase Analytics、PostHog、Amplitude、Segment、CodePush 等真实发布 / 观测 SDK。 - 2026-06-18 移动壳观测与渠道 SDK 初始化边界:移动壳生产入口、HostBridge、启动 URL 和 runtime 配置不得提前初始化 Sentry、Firebase Analytics、PostHog、Amplitude、Segment、CodePush 或 Expo Updates;这些 SDK 必须等真实发布通道、采集字段、用户授权、隐私披露、签名 / 回滚策略和团队发布流程落地后逐项接入。配置检查会同时拒绝相关依赖、锁文件解析、Expo 配置和源码初始化片段;`expo-application` 可能由 Expo 自身传递解析,但项目不得主动 direct 依赖它实现渠道逻辑。 - 2026-06-18 桌面图片拖入接入主图槽位:`CreativeImageInputPanel` 在桌面壳声明 `file.imageDropped` 时订阅宿主拖入事件,只在拖入坐标命中当前主图卡片且未被上层元素遮挡时消费事件,避免窗口级拖入被多个创作面板同时接收;成功后仍转换为现有 `File` 上传回调。 @@ -2588,3 +2588,10 @@ - 决策:`apps/desktop-shell/src-tauri/src/app.rs` 启动时必须通过 `desktop_main_window_config(app)?` 按 `label="main"` 解析主窗口配置,并在创建 `WebviewWindowBuilder` 前调用 `desktop_window_config_with_runtime_platform(...)` 补写宿主上下文。缺少 `main` 时返回 Tauri `WindowNotFound` 阻断启动;配置门禁拒绝按 `windows[0]` / `get(0)` 兜底或 `if let Some(config)` 静默跳过主窗口创建。 - 影响范围:`apps/desktop-shell/src-tauri/src/app.rs`、`apps/desktop-shell/scripts/check-config.mjs`、宿主壳能力统一协议文档、Expo / Tauri HostBridge 方案文档。 - 验证方式:`npm run desktop-shell:test`、`npm run desktop-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。 + +## 2026-06-19 移动壳 HostBridge 版本运行时来源 + +- 背景:移动壳 H5 入口 query 和 `host.getRuntime` 回包都需要稳定 `hostVersion`。如果 `runtime.ts` 手写版本字符串,即使配置检查能对比 `app.json`,发布时仍存在多处版本源需要人工同步。 +- 决策:移动壳 `MOBILE_SHELL_HOST_VERSION` 必须通过移动壳 `app.json` 的 Expo `version` 配置解析,异常配置只回退到与 `app.json` / `package.json` 一致的受检 fallback。不新增 `expo-constants`、OTA 更新、渠道分发、应用安装信息业务或发布通道 SDK;配置检查拒绝 `MOBILE_SHELL_HOST_VERSION` 重新写死字符串。 +- 影响范围:`apps/mobile-shell/src/shell/runtime.ts`、`apps/mobile-shell/scripts/check-config.mjs`、Expo / Tauri HostBridge 方案文档。 +- 验证方式:`npm run mobile-shell:test`、`npm run mobile-shell:typecheck`、`npm run mobile-shell:config`、`npm run check:native-shells`、`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 84323c999..2d4feb663 100644 --- a/docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md +++ b/docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md @@ -350,7 +350,7 @@ GameBridge 禁止: 2026-06-18 追加:移动壳安装包身份固定为 `world.genarrative.mobile`。Expo `app.json` 中的 `ios.bundleIdentifier` 与 `android.package` 使用同一包标识,应用版本为 `0.1.0`,iOS `buildNumber` 从字符串 `"1"` 起步,Android `versionCode` 从整数 `1` 起步;后续每次生成可分发安装包时只递增构建号 / versionCode,产品版本号按发布节奏单独调整。`apps/mobile-shell/scripts/check-config.mjs` 会校验这些字段与 `package.json` 版本一致,避免 iOS、Android 和 H5 HostBridge `hostVersion` 发生静默漂移;`npm run mobile-shell:config` 会调用真实 Expo CLI 解析 public managed config,确认最终 Expo 配置仍保留同一包身份、深链、安全字段、插件权限和 HostBridge 版本。当前仍不写入假商店上架信息、假更新端点或占位渠道 SDK 配置。 -2026-06-18 追加:移动壳 H5 入口 query 和 `host.getRuntime` 回包统一读取 `MOBILE_SHELL_HOST_VERSION`,该常量必须与 Expo `app.json` / `package.json` 版本一致。配置检查会拒绝在 `apps/mobile-shell/src/shell/ShellApp.tsx` 或 `apps/mobile-shell/src/host-bridge/bridge.ts` 内重新散落硬编码版本,避免升级移动安装包时 H5 首屏上下文和宿主 runtime 回读版本不一致。 +2026-06-19 追加:移动壳 H5 入口 query 和 `host.getRuntime` 回包统一读取 `MOBILE_SHELL_HOST_VERSION`,该值由移动壳 `app.json` 的 Expo `version` 配置解析,异常配置只回退到与 `app.json` / `package.json` 一致的受检 fallback。配置检查会拒绝在 `apps/mobile-shell/src/shell/ShellApp.tsx`、`apps/mobile-shell/src/host-bridge/bridge.ts` 或 `runtime.ts` 内重新散落硬编码版本,避免升级移动安装包时 H5 首屏上下文和宿主 runtime 回读版本不一致。该收口不引入 `expo-constants`、OTA 更新、渠道分发或应用安装信息业务。 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。由于移动壳运行依赖会从根安装树解析,根 H5 `package.json` 也不得直接安装这些移动端发布通道、崩溃上报、analytics 或 CodePush SDK;根 `package-lock.json` 也不得解析 `expo-updates`、Sentry、Firebase Analytics、PostHog、Amplitude、Segment、CodePush 等真实发布 / 观测 SDK。`expo-application` 可能由 Expo 自身传递解析,但项目不得把它作为 direct dependency 主动用于渠道逻辑。 diff --git a/scripts/check-native-shells.mjs b/scripts/check-native-shells.mjs index 5a8ed4d93..0e4b10ba9 100644 --- a/scripts/check-native-shells.mjs +++ b/scripts/check-native-shells.mjs @@ -137,6 +137,7 @@ const expectedMobileShellFiles = [ 'navigation.ts', 'network.test.ts', 'network.ts', + 'runtime.test.ts', 'runtime.ts', 'safeArea.test.ts', 'safeArea.ts',