收紧移动壳能力声明与迟到响应边界

移动壳卸载后丢弃迟到 HostBridge 响应

移动壳 ShellApp 测试覆盖卸载后异步响应不注入

移动壳配置检查拒绝只返回 unsupported 的声明能力

共享决策记录补充移动壳消息注入和能力声明约定
This commit is contained in:
2026-06-20 16:02:47 +08:00
parent 4f95e29895
commit e892113060
4 changed files with 103 additions and 1 deletions
@@ -390,6 +390,26 @@ function extractMobileBridgeHandledMethods(source) {
return [...match[1].matchAll(/case '([^']+)':/g)].map((entry) => entry[1]);
}
function extractMobileBridgeUnsupportedMethods(source) {
const match = source.match(
/async function dispatchMobileHostBridgeRequest[\s\S]*?switch \(request\.method\) \{([\s\S]*?)\n \}/,
);
if (!match) {
throw new Error('unable to read mobile shell HostBridge unsupported methods');
}
const unsupportedMethods = new Set();
const casePattern =
/case '([^']+)':([\s\S]*?)(?=\n case '|\n default:|\n \})/g;
for (const entry of match[1].matchAll(casePattern)) {
if (entry[2].includes('unsupported(request.method)')) {
unsupportedMethods.add(entry[1]);
}
}
return [...unsupportedMethods];
}
function extractFunctionBody(source, functionName) {
const start = source.indexOf(`function ${functionName}`);
if (start === -1) {
@@ -821,6 +841,7 @@ if (sharedPublicWebOriginUrl.protocol !== 'https:') {
const sharedPublicWebHost = sharedPublicWebOriginUrl.hostname;
const sharedPublicWebAssociatedDomain = `applinks:${sharedPublicWebHost}`;
const handledMobileMethods = extractMobileBridgeHandledMethods(dispatchSource);
const unsupportedMobileMethods = extractMobileBridgeUnsupportedMethods(dispatchSource);
const mobileCapabilities = sharedMobileBaseCapabilities;
const iosMobileCapabilities = sharedMobileIosCapabilities;
const mobileCapabilitySet = new Set(mobileCapabilities);
@@ -1054,6 +1075,17 @@ if (missingMobileMethodHandlers.length > 0) {
);
}
const unsupportedMobileCapabilities = iosMobileCapabilities.filter(
(capability) =>
sharedMethods.includes(capability) &&
unsupportedMobileMethods.includes(capability),
);
if (unsupportedMobileCapabilities.length > 0) {
throw new Error(
`mobile shell declares request capabilities backed only by unsupported responses: ${unsupportedMobileCapabilities.join(', ')}`,
);
}
const undeclaredMobileMethodHandlers = handledMobileMethods.filter(
(method) =>
!iosMobileCapabilitySet.has(method) && !sdkBackedCapabilities.includes(method),
@@ -1375,6 +1407,7 @@ for (const snippet of [
'h5CanGoBackRef',
'syncNavigationCanGoBack',
'resetNavigationCanGoBack',
'isShellMountedRef',
'injectHostBridgeMessage',
'injectHostBridgeEvent',
'injectLifecycleEvent',
@@ -1382,6 +1415,7 @@ for (const snippet of [
'logMobileHostEventFailure',
'logMobileHostBridgeMessageFailure',
'try {',
'if (!isShellMountedRef.current)',
'logMobileHostEventFailure(event, error)',
'injectHostBridgeMessage(response, logMobileHostBridgeMessageFailure)',
"console.warn('mobile HostBridge message injection failed', error)",
@@ -2451,6 +2485,8 @@ for (const snippet of [
"test('logs HostBridge response injection failures without crashing the shell'",
"new Error('response injection failed')",
'mobile HostBridge message injection failed',
"test('drops delayed HostBridge responses after shell unmount'",
"id: 'network-request-1'",
"type: 'genarrative.mobile.historyState'",
'mobile host event failed for network.statusChanged',
'external WebView navigation native failures stay outside the WebView',