Files
Genarrative/apps/mobile-shell/src/shell/url.ts
T
kdletters 81c353e586 收紧移动壳本机入口传递边界
移动壳 URL 构建默认拒绝本机 H5 入口

Deep Link 与原生页导航显式传递开发态入口选项

补充移动壳 URL 与 Deep Link 边界测试

同步原生壳门禁和项目决策日志
2026-06-21 19:19:27 +08:00

103 lines
2.6 KiB
TypeScript

import {
HOST_BRIDGE_NATIVE_APP_QUERY,
HOST_BRIDGE_NATIVE_APP_QUERY_KEY,
HOST_BRIDGE_PUBLIC_WEB_ORIGIN,
HOST_BRIDGE_PUBLIC_WEB_URL,
HOST_BRIDGE_VERSION,
type HostBridgeCapability,
type NativeHostPlatform,
} from '../../../../packages/shared/src/contracts/hostBridge';
export type MobileShellUrlOptions = {
platform: Extract<NativeHostPlatform, 'ios' | 'android'>;
hostVersion: string;
capabilities: readonly HostBridgeCapability[];
};
export type MobileShellBaseWebUrlOptions = {
allowLocalDevelopment?: boolean;
};
export const DEFAULT_MOBILE_SHELL_WEB_URL = HOST_BRIDGE_PUBLIC_WEB_URL;
export const ALLOWED_PRODUCTION_WEB_ORIGIN = HOST_BRIDGE_PUBLIC_WEB_ORIGIN;
const LOCAL_DEVELOPMENT_WEB_HOSTS = new Set([
'127.0.0.1',
'localhost',
'[::1]',
]);
function isAllowedMobileShellBaseUrl(
url: URL,
options: MobileShellBaseWebUrlOptions = {},
) {
if (url.origin === ALLOWED_PRODUCTION_WEB_ORIGIN) {
return true;
}
return Boolean(options.allowLocalDevelopment) &&
url.protocol === 'http:' &&
LOCAL_DEVELOPMENT_WEB_HOSTS.has(url.hostname);
}
export function resolveMobileShellBaseWebUrl(
rawUrl: unknown,
options: MobileShellBaseWebUrlOptions = {},
) {
if (typeof rawUrl !== 'string') {
return DEFAULT_MOBILE_SHELL_WEB_URL;
}
const value = rawUrl.trim();
if (!value) {
return DEFAULT_MOBILE_SHELL_WEB_URL;
}
try {
const url = new URL(value);
if (!isAllowedMobileShellBaseUrl(url, options)) {
return DEFAULT_MOBILE_SHELL_WEB_URL;
}
return url.toString();
} catch {
return DEFAULT_MOBILE_SHELL_WEB_URL;
}
}
export function buildMobileShellUrl(
rawUrl: string,
options: MobileShellUrlOptions,
baseWebUrlOptions: MobileShellBaseWebUrlOptions = {},
) {
const url = new URL(resolveMobileShellBaseWebUrl(rawUrl, baseWebUrlOptions));
url.searchParams.set(
HOST_BRIDGE_NATIVE_APP_QUERY_KEY.clientRuntime,
HOST_BRIDGE_NATIVE_APP_QUERY.clientRuntime,
);
url.searchParams.set(
HOST_BRIDGE_NATIVE_APP_QUERY_KEY.clientType,
HOST_BRIDGE_NATIVE_APP_QUERY.clientType,
);
url.searchParams.set(
HOST_BRIDGE_NATIVE_APP_QUERY_KEY.hostShell,
HOST_BRIDGE_NATIVE_APP_QUERY.hostShellExpoMobile,
);
url.searchParams.set(
HOST_BRIDGE_NATIVE_APP_QUERY_KEY.hostPlatform,
options.platform,
);
url.searchParams.set(
HOST_BRIDGE_NATIVE_APP_QUERY_KEY.hostVersion,
options.hostVersion,
);
url.searchParams.set(
HOST_BRIDGE_NATIVE_APP_QUERY_KEY.bridgeVersion,
HOST_BRIDGE_VERSION.toString(),
);
url.searchParams.set(
HOST_BRIDGE_NATIVE_APP_QUERY_KEY.hostCapabilities,
options.capabilities.join(','),
);
return url.toString();
}