Files
Genarrative/apps/mobile-shell/src/shell/navigation.ts
T
kdletters 2f16a43000 收紧移动壳外链与扫码边界
移动壳外链原生异常改由壳层记录日志

移动扫码请求使用共享超时自动清理 pending 状态

原生壳结构门禁补充期望清单唯一性自检

项目记忆同步移动壳边界验收约定
2026-06-20 16:37:06 +08:00

82 lines
1.7 KiB
TypeScript

import { normalizeHostBridgeExternalUrl } from '../../../../packages/shared/src/contracts/hostBridge';
export type MobileShellExternalNavigator = {
canOpenURL: (url: string) => Promise<boolean>;
openURL: (url: string) => Promise<unknown>;
};
export function shouldOpenInMobileShellWebView(
rawUrl: string,
allowedOrigin: string,
) {
if (rawUrl === 'about:blank') {
return true;
}
if (
!rawUrl.startsWith('/') &&
!rawUrl.startsWith('#') &&
!/^[a-z][a-z0-9+.-]*:/i.test(rawUrl)
) {
return false;
}
try {
const url = new URL(rawUrl, allowedOrigin);
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
return false;
}
return url.origin === allowedOrigin;
} catch {
return false;
}
}
export function resolveMobileShellExternalUrl(rawUrl: string) {
return normalizeHostBridgeExternalUrl(rawUrl);
}
export async function openMobileShellExternalNavigation(
navigator: MobileShellExternalNavigator,
rawUrl: string,
) {
const externalUrl = resolveMobileShellExternalUrl(rawUrl);
if (!externalUrl) {
return false;
}
if (!(await navigator.canOpenURL(externalUrl))) {
return false;
}
await navigator.openURL(externalUrl);
return true;
}
export function shouldAcceptMobileShellHostBridgeMessage(
rawUrl: string,
allowedOrigin: string,
) {
return rawUrl !== 'about:blank' &&
shouldOpenInMobileShellWebView(rawUrl, allowedOrigin);
}
export function resolveMobileShellWebViewUrl(
rawUrl: string,
allowedOrigin: string,
) {
if (
rawUrl === 'about:blank' ||
!shouldOpenInMobileShellWebView(rawUrl, allowedOrigin)
) {
return null;
}
try {
return new URL(rawUrl, allowedOrigin).toString();
} catch {
return null;
}
}