锁定移动入口宿主上下文清洗
移动 URL 单测覆盖旧宿主 query 被清理 移动壳配置检查反查入口上下文清洗测试 共享决策日志补充移动入口 query 边界
This commit is contained in:
@@ -143,6 +143,8 @@ const hostBridgeSource = bridgeSourceFiles
|
||||
.join('\n');
|
||||
const urlPath = new URL('../src/shell/url.ts', import.meta.url);
|
||||
const urlSource = fs.readFileSync(urlPath, 'utf8');
|
||||
const urlTestPath = new URL('../src/shell/url.test.ts', import.meta.url);
|
||||
const urlTestSource = fs.readFileSync(urlTestPath, 'utf8');
|
||||
const deepLinkPath = new URL('../src/shell/deepLink.ts', import.meta.url);
|
||||
const deepLinkSource = fs.readFileSync(deepLinkPath, 'utf8');
|
||||
const navigationPath = new URL('../src/shell/navigation.ts', import.meta.url);
|
||||
@@ -1715,6 +1717,18 @@ for (const hardcodedHostContextSnippet of [
|
||||
}
|
||||
}
|
||||
|
||||
for (const snippet of [
|
||||
'附加宿主上下文前会清理旧移动壳 query',
|
||||
'clientRuntime=browser&hostShell=old_shell&hostCapabilities=old',
|
||||
"expect(builtUrl.match(/clientRuntime=/g)).toHaveLength(1)",
|
||||
"expect(builtUrl.match(/hostShell=/g)).toHaveLength(1)",
|
||||
"expect(builtUrl.match(/hostCapabilities=/g)).toHaveLength(1)",
|
||||
]) {
|
||||
if (!urlTestSource.includes(snippet)) {
|
||||
throw new Error(`mobile shell URL tests must cover stale host-context cleanup: ${snippet}`);
|
||||
}
|
||||
}
|
||||
|
||||
for (const snippet of [
|
||||
'resolveMobileShellBaseWebUrl(baseWebUrl)',
|
||||
'resolveTargetPath(rawUrl, webOrigin)',
|
||||
|
||||
@@ -41,6 +41,30 @@ describe('buildMobileShellUrl', () => {
|
||||
expect(url.searchParams.get('work')).toBe('PZ-1');
|
||||
});
|
||||
|
||||
test('附加宿主上下文前会清理旧移动壳 query', () => {
|
||||
const rawUrl =
|
||||
'https://app.genarrative.world/works/detail?clientRuntime=browser&hostShell=old_shell&hostCapabilities=old&work=PZ-1';
|
||||
const builtUrl = buildMobileShellUrl(rawUrl, {
|
||||
platform: 'android',
|
||||
hostVersion: '0.1.0',
|
||||
capabilities: ['host.getRuntime', 'share.open'],
|
||||
});
|
||||
const url = new URL(builtUrl);
|
||||
|
||||
expect(url.searchParams.get('work')).toBe('PZ-1');
|
||||
expect(url.searchParams.get('clientRuntime')).toBe('native_app');
|
||||
expect(url.searchParams.get('hostShell')).toBe('expo_mobile');
|
||||
expect(url.searchParams.get('hostCapabilities')).toBe(
|
||||
'host.getRuntime,share.open',
|
||||
);
|
||||
expect(builtUrl.match(/clientRuntime=/g)).toHaveLength(1);
|
||||
expect(builtUrl.match(/hostShell=/g)).toHaveLength(1);
|
||||
expect(builtUrl.match(/hostCapabilities=/g)).toHaveLength(1);
|
||||
expect(builtUrl).not.toContain('clientRuntime=browser');
|
||||
expect(builtUrl).not.toContain('hostShell=old_shell');
|
||||
expect(builtUrl).not.toContain('hostCapabilities=old');
|
||||
});
|
||||
|
||||
test('支持按平台注入不同能力清单', () => {
|
||||
const iosUrl = new URL(
|
||||
buildMobileShellUrl('https://app.genarrative.world/', {
|
||||
|
||||
@@ -2657,6 +2657,7 @@
|
||||
|
||||
- 背景:Expo 移动壳启动 URL 和 deep link 已经会给 H5 追加 `native_app`、`expo_mobile`、平台、版本和 capability query;但 H5 通过 HostBridge 调用 `navigation.openNativePage` 主动跳转同源 route 时,壳层只把裸同源 URL 交给 WebView,目标页首屏可能短暂或持续按普通浏览器运行态识别。
|
||||
- 决策:`navigation.openNativePage` 仍只接受同源 H5 route,不新增真实原生页面、不放宽外域导航;通过校验后的目标 URL 在进入 WebView 前统一调用 `buildMobileShellUrl(...)` 重新附加当前 `MobileShellUrlOptions`,确保主动导航后的页面继续带 `clientRuntime=native_app`、`hostShell=expo_mobile`、真实平台、宿主版本和当前 capability 清单。
|
||||
- 2026-06-20 追加:`buildMobileShellUrl(...)` 对启动 URL、deep link 和主动导航目标补写宿主上下文时,必须先覆盖旧 `clientRuntime`、`hostShell`、`hostCapabilities` 等宿主 query,确保输出只包含当前 Expo 壳真实上下文;移动壳配置检查反查 URL 单测中的旧 query 清洗边界。
|
||||
- 影响范围:`apps/mobile-shell/src/host-bridge/`、`apps/mobile-shell/src/shell/ShellApp.tsx`、Expo / Tauri HostBridge 方案文档。
|
||||
- 验证方式:`npm run mobile-shell:test`、`npm run mobile-shell:typecheck`、`npm run check:native-shells`、`npm run typecheck -- --pretty false`、`npm run check:encoding`、`git diff --check`。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user