From 68bb9718d63e62046dd3e2b762dd3e34a0a79297 Mon Sep 17 00:00:00 2001 From: kdletters Date: Sat, 20 Jun 2026 09:38:33 +0800 Subject: [PATCH] =?UTF-8?q?=E9=94=81=E5=AE=9A=E7=A7=BB=E5=8A=A8=E5=A3=B3?= =?UTF-8?q?=E7=94=9F=E5=91=BD=E5=91=A8=E6=9C=9F=E6=98=A0=E5=B0=84=E9=97=A8?= =?UTF-8?q?=E7=A6=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 移动壳配置检查反查生命周期映射函数和四类 AppState 测试 共享决策日志记录生命周期映射门禁约束 --- apps/mobile-shell/scripts/check-config.mjs | 37 +++++++++++++++++++ .../shared-memory/decision-log.md | 7 ++++ 2 files changed, 44 insertions(+) diff --git a/apps/mobile-shell/scripts/check-config.mjs b/apps/mobile-shell/scripts/check-config.mjs index 51cf074e7..a7d5f2912 100644 --- a/apps/mobile-shell/scripts/check-config.mjs +++ b/apps/mobile-shell/scripts/check-config.mjs @@ -150,6 +150,10 @@ const loadFailureTestPath = new URL( const loadFailureTestSource = fs.readFileSync(loadFailureTestPath, 'utf8'); const runtimePath = new URL('../src/shell/runtime.ts', import.meta.url); const runtimeSource = fs.readFileSync(runtimePath, 'utf8'); +const lifecyclePath = new URL('../src/shell/lifecycle.ts', import.meta.url); +const lifecycleSource = fs.readFileSync(lifecyclePath, 'utf8'); +const lifecycleTestPath = new URL('../src/shell/lifecycle.test.ts', import.meta.url); +const lifecycleTestSource = fs.readFileSync(lifecycleTestPath, 'utf8'); const safeAreaTestPath = new URL('../src/shell/safeArea.test.ts', import.meta.url); const safeAreaTestSource = fs.readFileSync(safeAreaTestPath, 'utf8'); const sharedContractPath = new URL( @@ -1397,6 +1401,39 @@ for (const snippet of [ } } +for (const snippet of [ + 'lifecyclePayloadFromAppState', + "state === 'active'", + "state === 'background'", + "? 'background'", + ": 'inactive'", + 'focused: state === \'active\'', + 'nativeState: state', +]) { + if (!lifecycleSource.includes(snippet)) { + throw new Error(`mobile shell lifecycle mapper missing ${snippet}`); + } +} + +for (const snippet of [ + "describe('lifecycle'", + "test('把 React Native AppState 映射为统一 HostBridge 生命周期状态'", + "lifecyclePayloadFromAppState('active')", + "lifecyclePayloadFromAppState('background')", + "lifecyclePayloadFromAppState('inactive')", + "lifecyclePayloadFromAppState('unknown')", + "state: 'active'", + "state: 'background'", + "state: 'inactive'", + 'focused: true', + 'focused: false', + "nativeState: 'unknown'", +]) { + if (!lifecycleTestSource.includes(snippet)) { + throw new Error(`mobile shell lifecycle tests missing ${snippet}`); + } +} + for (const snippet of [ 'type HostBridgeEventName', 'isHostBridgeEventName', diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index c5a3c7260..90143941f 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -2930,6 +2930,13 @@ - 影响范围:`apps/mobile-shell/src/shell/ShellApp.tsx`、`apps/mobile-shell/src/shell/ShellApp.test.tsx`、`apps/mobile-shell/scripts/check-config.mjs`、宿主壳能力统一协议文档。 - 验证方式:`npm run mobile-shell:test -- src/shell/ShellApp.test.tsx`、`npm run mobile-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。 +## 2026-06-20 移动壳生命周期映射门禁 + +- 背景:Expo `AppState` 的 `active`、`background`、`inactive` 以及未知状态都会进入 `app.lifecycle` 事件;如果归一化映射漂移,H5 游戏循环、背景音乐和固定玩法音频会错误恢复或暂停。 +- 决策:`apps/mobile-shell/src/shell/lifecycle.test.ts` 必须直接覆盖 `active`、`background`、`inactive` 和未知状态到统一 `state`、`focused`、`nativeState` 的映射;`apps/mobile-shell/scripts/check-config.mjs` 反查映射函数和测试片段,确保未知状态继续归为 `inactive` 且只有 `active` 视为 focused。 +- 影响范围:`apps/mobile-shell/src/shell/lifecycle.ts`、`apps/mobile-shell/src/shell/lifecycle.test.ts`、`apps/mobile-shell/scripts/check-config.mjs`。 +- 验证方式:`npm run mobile-shell:test -- src/shell/lifecycle.test.ts`、`npm run mobile-shell:config`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。 + ## 2026-06-20 移动分享单测边界 - 背景:Expo 移动壳 `share.open` / `share.setTarget` 已经由 `share.ts` 承接共享 HostBridge 分享 URL 归一和缓存目标,但关键边界主要压在巨型 `bridge.test.ts` 中,后续拆分桥接 helper 时容易遗漏非法显式 payload 不回退缓存、空分享拒绝和缓存目标保留语义。