新增 Expo 与 Tauri 原生宿主壳
新增 HostBridge 原生宿主契约和 H5 native_app transport 新增 Expo React Native 移动壳并收紧 WebView 外链边界 新增 Tauri 桌面壳并用 capability 收口受控命令 更新宿主壳方案、文档索引和共享记忆
This commit is contained in:
@@ -1,7 +1,12 @@
|
||||
import type {
|
||||
HostBridgeMethod,
|
||||
HostBridgeRuntimeResult,
|
||||
} from '../../../packages/shared/src/contracts/hostBridge';
|
||||
import type {
|
||||
WechatMiniProgramPayParams,
|
||||
WechatMiniProgramVirtualPayParams,
|
||||
} from '../../../packages/shared/src/contracts/runtime';
|
||||
import { requestNativeAppHostBridge } from './nativeAppHostBridge';
|
||||
|
||||
const WECHAT_JS_SDK_URL = 'https://res.wx.qq.com/open/js/jweixin-1.6.0.js';
|
||||
const MINI_PROGRAM_AUTH_PAGE_URL =
|
||||
@@ -18,6 +23,9 @@ export type HostRuntimeSnapshot = {
|
||||
kind: HostRuntimeKind;
|
||||
clientType: string | null;
|
||||
clientRuntime: string | null;
|
||||
hostShell: string | null;
|
||||
hostPlatform: string | null;
|
||||
hostVersion: string | null;
|
||||
miniProgramEnv: string | null;
|
||||
};
|
||||
|
||||
@@ -25,6 +33,8 @@ export type HostRuntimeContext = {
|
||||
location?: Pick<Location, 'search'> | null;
|
||||
navigator?: Partial<Pick<Navigator, 'userAgent'>> | null;
|
||||
wx?: Window['wx'] | null;
|
||||
tauri?: Window['__TAURI__'] | null;
|
||||
reactNativeWebView?: Window['ReactNativeWebView'] | null;
|
||||
};
|
||||
|
||||
export type HostNativePageNavigationOptions = {
|
||||
@@ -47,6 +57,28 @@ export type HostShareGridRequest = {
|
||||
publicWorkCode: string;
|
||||
};
|
||||
|
||||
function isUnsupportedHostBridgeError(error: unknown) {
|
||||
return (
|
||||
error instanceof Error &&
|
||||
(error.name === 'unsupported_method' ||
|
||||
error.name === 'unsupported_capability')
|
||||
);
|
||||
}
|
||||
|
||||
async function requestNativeHostBoolean(
|
||||
method: HostBridgeMethod,
|
||||
payload?: unknown,
|
||||
) {
|
||||
try {
|
||||
return Boolean(await requestNativeAppHostBridge(method, payload));
|
||||
} catch (error) {
|
||||
if (isUnsupportedHostBridgeError(error)) {
|
||||
return false;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
function resolveLocation(context: HostRuntimeContext) {
|
||||
return (
|
||||
context.location ?? (typeof window !== 'undefined' ? window.location : null)
|
||||
@@ -64,6 +96,19 @@ function resolveWxBridge(context: HostRuntimeContext) {
|
||||
return context.wx ?? (typeof window !== 'undefined' ? window.wx : null);
|
||||
}
|
||||
|
||||
function resolveTauriBridge(context: HostRuntimeContext) {
|
||||
return (
|
||||
context.tauri ?? (typeof window !== 'undefined' ? window.__TAURI__ : null)
|
||||
);
|
||||
}
|
||||
|
||||
function resolveReactNativeWebView(context: HostRuntimeContext) {
|
||||
return (
|
||||
context.reactNativeWebView ??
|
||||
(typeof window !== 'undefined' ? window.ReactNativeWebView : null)
|
||||
);
|
||||
}
|
||||
|
||||
function hasWechatMiniProgramBridge(wxBridge: Window['wx'] | null | undefined) {
|
||||
return Boolean(
|
||||
wxBridge?.miniProgram?.postMessage || wxBridge?.miniProgram?.navigateTo,
|
||||
@@ -85,9 +130,14 @@ export function resolveHostRuntime(
|
||||
const params = new URLSearchParams(location?.search ?? '');
|
||||
const clientType = params.get('clientType');
|
||||
const clientRuntime = params.get('clientRuntime');
|
||||
const hostShell = params.get('hostShell');
|
||||
const hostPlatform = params.get('hostPlatform');
|
||||
const hostVersion = params.get('hostVersion');
|
||||
const miniProgramEnv = params.get('miniProgramEnv');
|
||||
const navigatorLike = resolveNavigator(context);
|
||||
const wxBridge = resolveWxBridge(context);
|
||||
const tauriBridge = resolveTauriBridge(context);
|
||||
const reactNativeWebView = resolveReactNativeWebView(context);
|
||||
|
||||
if (
|
||||
clientRuntime === 'wechat_mini_program' ||
|
||||
@@ -99,15 +149,26 @@ export function resolveHostRuntime(
|
||||
kind: 'wechat_mini_program',
|
||||
clientType,
|
||||
clientRuntime,
|
||||
hostShell,
|
||||
hostPlatform,
|
||||
hostVersion,
|
||||
miniProgramEnv,
|
||||
};
|
||||
}
|
||||
|
||||
if (clientRuntime === 'native_app' || clientType === 'native_app') {
|
||||
if (
|
||||
clientRuntime === 'native_app' ||
|
||||
clientType === 'native_app' ||
|
||||
typeof tauriBridge?.core?.invoke === 'function' ||
|
||||
typeof reactNativeWebView?.postMessage === 'function'
|
||||
) {
|
||||
return {
|
||||
kind: 'native_app',
|
||||
clientType,
|
||||
clientRuntime,
|
||||
hostShell,
|
||||
hostPlatform,
|
||||
hostVersion,
|
||||
miniProgramEnv,
|
||||
};
|
||||
}
|
||||
@@ -116,6 +177,9 @@ export function resolveHostRuntime(
|
||||
kind: 'browser',
|
||||
clientType,
|
||||
clientRuntime,
|
||||
hostShell,
|
||||
hostPlatform,
|
||||
hostVersion,
|
||||
miniProgramEnv,
|
||||
};
|
||||
}
|
||||
@@ -214,6 +278,17 @@ export async function navigateHostNativePage(
|
||||
options: HostNativePageNavigationOptions = {},
|
||||
) {
|
||||
const runtime = getHostRuntime();
|
||||
if (runtime.kind === 'native_app') {
|
||||
const result = await requestNativeHostBoolean(
|
||||
'navigation.openNativePage',
|
||||
{ url },
|
||||
);
|
||||
if (result) {
|
||||
options.beforeNavigate?.();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if (runtime.kind !== 'wechat_mini_program') {
|
||||
return false;
|
||||
}
|
||||
@@ -225,6 +300,10 @@ export async function navigateHostNativePage(
|
||||
}
|
||||
|
||||
export async function requestHostLogin() {
|
||||
if (getHostRuntime().kind === 'native_app') {
|
||||
return await requestNativeHostBoolean('auth.requestLogin');
|
||||
}
|
||||
|
||||
return await navigateHostNativePage(MINI_PROGRAM_AUTH_PAGE_URL, {
|
||||
errorMessage: '请在微信小程序内完成登录',
|
||||
});
|
||||
@@ -238,7 +317,15 @@ export async function requestHostPayment({
|
||||
payload,
|
||||
orderId,
|
||||
}: HostPaymentRequest) {
|
||||
if (getHostRuntime().kind !== 'wechat_mini_program') {
|
||||
const runtime = getHostRuntime();
|
||||
if (runtime.kind === 'native_app') {
|
||||
return await requestNativeHostBoolean('payment.request', {
|
||||
payload,
|
||||
orderId,
|
||||
});
|
||||
}
|
||||
|
||||
if (runtime.kind !== 'wechat_mini_program') {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -321,6 +408,15 @@ export async function openWechatMiniProgramShareGridPage(
|
||||
}
|
||||
|
||||
export function setHostShareTarget(message: unknown) {
|
||||
if (getHostRuntime().kind === 'native_app') {
|
||||
void requestNativeAppHostBridge('share.setTarget', {
|
||||
target: message,
|
||||
}).catch(() => {
|
||||
// 分享目标同步是宿主提示能力,失败不应打断当前 H5 流程。
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
typeof window === 'undefined' ||
|
||||
getHostRuntime().kind !== 'wechat_mini_program' ||
|
||||
@@ -338,3 +434,13 @@ export function setHostShareTarget(message: unknown) {
|
||||
export function postWechatMiniProgramMessage(message: unknown) {
|
||||
return setHostShareTarget(message);
|
||||
}
|
||||
|
||||
export async function getNativeAppHostRuntime() {
|
||||
if (getHostRuntime().kind !== 'native_app') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return await requestNativeAppHostBridge<HostBridgeRuntimeResult>(
|
||||
'host.getRuntime',
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user