diff --git a/apps/mobile-shell/scripts/check-config.mjs b/apps/mobile-shell/scripts/check-config.mjs index c3ea54353..f0846bd8c 100644 --- a/apps/mobile-shell/scripts/check-config.mjs +++ b/apps/mobile-shell/scripts/check-config.mjs @@ -19,6 +19,8 @@ const sharedContractPath = new URL( const sharedContractSource = fs.readFileSync(sharedContractPath, 'utf8'); const packagePath = new URL('../package.json', import.meta.url); const packageConfig = JSON.parse(fs.readFileSync(packagePath, 'utf8')); +const rootPackagePath = new URL('../../../package.json', import.meta.url); +const rootPackageConfig = JSON.parse(fs.readFileSync(rootPackagePath, 'utf8')); const iconPath = new URL('../assets/icon.png', import.meta.url); const icon = PNG.sync.read(fs.readFileSync(iconPath)); const brandBackgroundColor = '#fffdf9'; @@ -131,7 +133,7 @@ function extractStringConstExport(source, exportName) { return match[1]; } -function assertNoBlockedMobileChannelDependencies() { +function assertNoBlockedMobileChannelDependencies(packageJson, packageLabel) { const dependencySections = [ 'dependencies', 'devDependencies', @@ -141,9 +143,9 @@ function assertNoBlockedMobileChannelDependencies() { for (const dependency of blockedMobileChannelDependencies) { for (const section of dependencySections) { - if (packageConfig[section]?.[dependency]) { + if (packageJson[section]?.[dependency]) { throw new Error( - `mobile shell must not depend on ${dependency} before the real channel contract exists`, + `${packageLabel} must not depend on ${dependency} before the real mobile channel contract exists`, ); } } @@ -250,7 +252,8 @@ function assertSameList(actual, expected, label) { assertNoDevScaffoldTerms( productionSourceRoots.flatMap((root) => collectProductionSourceFiles(root)), ); -assertNoBlockedMobileChannelDependencies(); +assertNoBlockedMobileChannelDependencies(packageConfig, 'mobile shell package'); +assertNoBlockedMobileChannelDependencies(rootPackageConfig, 'root H5 package'); assertNoBlockedMobileChannelSnippets(); const sharedCapabilities = extractStringArrayExport( diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index 1ed7464f2..af728b8ef 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -2383,3 +2383,10 @@ - 决策:根 H5 `package.json` 和 `apps/desktop-shell/package.json` 不安装 `@tauri-apps/api` 或任何 `@tauri-apps/plugin-*` JS guest 包;opener、clipboard、dialog、notification 等桌面系统能力只保留 Rust Cargo 插件,由 `host_bridge_request` 内部分发。`apps/desktop-shell/scripts/check-config.mjs` 对两个 package 都做依赖门禁,Tauri CLI 仅作为构建工具保留。 - 影响范围:根依赖、桌面壳依赖、桌面壳配置检查和 Expo / Tauri HostBridge 方案文档。 - 验证方式:`npm run check:native-shells`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。 + +## 2026-06-18 移动壳渠道 SDK 依赖收口 + +- 背景:Expo 移动壳运行时依赖可能从根安装树解析;如果只检查 `apps/mobile-shell/package.json`,根 H5 包仍可能直接引入 Expo Updates、Sentry、Firebase Analytics、PostHog、Amplitude、Segment、CodePush 等移动端发布通道、崩溃上报或 analytics SDK,让壳边界绕过真实渠道契约。 +- 决策:`apps/mobile-shell/scripts/check-config.mjs` 同时检查移动壳包和根 H5 包的直接依赖;在真实发布通道、采集字段、用户授权、隐私披露、签名 / 回滚策略和团队发布流程落地前,两处都不得安装上述移动渠道 SDK。现有即时本地通知、系统分享、文件导入导出等真实宿主能力不受影响。 +- 影响范围:移动壳配置检查、根依赖边界和 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 fb20d2263..cc164cee6 100644 --- a/docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md +++ b/docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md @@ -298,7 +298,7 @@ GameBridge 禁止: 2026-06-18 追加:移动壳 H5 入口 query 和 `host.getRuntime` 回包统一读取 `MOBILE_SHELL_HOST_VERSION`,该常量必须与 Expo `app.json` / `package.json` 版本一致。配置检查会拒绝在 `App.tsx` 或 `mobileHostBridge.ts` 内重新散落硬编码版本,避免升级移动安装包时 H5 首屏上下文和宿主 runtime 回读版本不一致。 -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 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。 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 解析阶段引入身份、资源或权限漂移。