Files
Genarrative/apps/mobile-shell/src/host-bridge/protocol.ts
T
kdletters fb19e5e55a 收紧移动壳错误响应边界
限制 Expo HostBridge 只透传共享协议错误码和字符串消息

未知原生异常统一归一为 host_error 固定失败文案

新增移动壳测试和配置门禁防止错误对象泄漏

同步 HostBridge envelope 错误归一决策记录
2026-06-19 16:50:33 +08:00

116 lines
2.6 KiB
TypeScript

import {
HOST_BRIDGE_PROTOCOL,
HOST_BRIDGE_VERSION,
type HostBridgeError,
type HostBridgeMethod,
type HostBridgeRequest,
type HostBridgeResponse,
isHostBridgeMethod,
normalizeHostBridgeRequestId,
} from '../../../../packages/shared/src/contracts/hostBridge';
import type { MobileShellUrlOptions } from '../shell/url';
const HOST_BRIDGE_ERROR_CODES = new Set<HostBridgeError['code']>([
'invalid_request',
'unsupported_method',
'unsupported_capability',
'timeout',
'cancelled',
'host_error',
]);
export type MobileHostBridgeNavigation = {
allowedOrigin: string;
urlOptions: MobileShellUrlOptions;
openWebViewUrl: (url: string) => void;
reloadWebView: () => void;
};
export function unsupported(method: HostBridgeMethod): HostBridgeError {
return {
code: 'unsupported_method',
message: `${method} unsupported in mobile shell`,
};
}
export function invalidRequest(message: string): HostBridgeError {
return {
code: 'invalid_request',
message,
};
}
export function isHostBridgeRequest(value: unknown): value is HostBridgeRequest {
if (!value || typeof value !== 'object') {
return false;
}
const candidate = value as Partial<HostBridgeRequest>;
const requestId = normalizeHostBridgeRequestId(candidate.id);
return (
candidate.bridge === HOST_BRIDGE_PROTOCOL &&
candidate.version === HOST_BRIDGE_VERSION &&
requestId !== null &&
isHostBridgeMethod(candidate.method)
);
}
export function parseRequest(raw: string) {
try {
return JSON.parse(raw) as unknown;
} catch {
return null;
}
}
export function ok<Result>(
request: HostBridgeRequest,
result?: Result,
): HostBridgeResponse<Result> {
return {
bridge: HOST_BRIDGE_PROTOCOL,
version: HOST_BRIDGE_VERSION,
id: request.id,
ok: true,
result,
};
}
export function failure(
request: Pick<HostBridgeRequest, 'id'>,
error: HostBridgeError,
): HostBridgeResponse {
return {
bridge: HOST_BRIDGE_PROTOCOL,
version: HOST_BRIDGE_VERSION,
id: request.id,
ok: false,
error,
};
}
export function normalizeMobileHostBridgeError(error: unknown): HostBridgeError {
if (
error &&
typeof error === 'object' &&
'code' in error &&
'message' in error &&
typeof error.code === 'string' &&
HOST_BRIDGE_ERROR_CODES.has(error.code as HostBridgeError['code']) &&
typeof error.message === 'string'
) {
return {
code: error.code as HostBridgeError['code'],
message: error.message,
};
}
return {
code: 'host_error',
message:
error instanceof Error
? error.message
: 'mobile host bridge request failed',
};
}