统一桌面网络探测超时契约
将桌面网络探测超时提升到共享 HostBridge 契约 让 Tauri 网络状态探测镜像共享超时边界 增加桌面壳配置门禁反查网络探测超时 补充宿主壳方案文档和共享决策记录
This commit is contained in:
@@ -1039,6 +1039,10 @@ const sharedHostBridgePayloadLimits = {
|
||||
sharedContractSource,
|
||||
'HOST_BRIDGE_RESPONSE_CACHE_MAX',
|
||||
),
|
||||
HOST_BRIDGE_DESKTOP_NETWORK_CHECK_TIMEOUT_MS: extractTsNumberConst(
|
||||
sharedContractSource,
|
||||
'HOST_BRIDGE_DESKTOP_NETWORK_CHECK_TIMEOUT_MS',
|
||||
),
|
||||
HOST_BRIDGE_BADGE_COUNT_MAX: extractTsNumberConst(
|
||||
sharedContractSource,
|
||||
'HOST_BRIDGE_BADGE_COUNT_MAX',
|
||||
@@ -1097,6 +1101,10 @@ const desktopHostBridgePayloadLimits = {
|
||||
rustHostSource,
|
||||
'HOST_BRIDGE_RESPONSE_CACHE_MAX',
|
||||
),
|
||||
HOST_BRIDGE_DESKTOP_NETWORK_CHECK_TIMEOUT_MS: extractRustNumberConst(
|
||||
desktopShellNetworkSource,
|
||||
'DESKTOP_NETWORK_CHECK_TIMEOUT_MS',
|
||||
),
|
||||
HOST_BRIDGE_BADGE_COUNT_MAX: extractRustNumberConst(
|
||||
rustHostSource,
|
||||
'BADGE_COUNT_MAX',
|
||||
|
||||
@@ -5,7 +5,7 @@ use std::net::{TcpStream, ToSocketAddrs};
|
||||
use std::time::Duration;
|
||||
use tauri::{Url, WebviewWindow};
|
||||
|
||||
const DESKTOP_NETWORK_CHECK_TIMEOUT_MS: u64 = 1200;
|
||||
pub(crate) const DESKTOP_NETWORK_CHECK_TIMEOUT_MS: u64 = 1200;
|
||||
|
||||
fn desktop_network_probe_target() -> Option<(String, u16)> {
|
||||
let url = Url::parse(WEB_APP_ORIGIN).ok()?;
|
||||
|
||||
@@ -2761,3 +2761,10 @@
|
||||
- 决策:`packages/shared/src/contracts/hostBridge.ts` 导出 `HOST_BRIDGE_SCANNER_TIMEOUT_MS`,作为 H5 facade 发起 `scanner.scanQrCode` 请求的唯一超时来源;`src/services/host-bridge/hostBridge.ts` 必须导入共享常量,不得继续手写 `timeoutMs: 60000`。根级原生壳门禁和桌面壳配置检查会拒绝回退到本地字面量。
|
||||
- 影响范围:`packages/shared/src/contracts/hostBridge.ts`、`src/services/host-bridge/hostBridge.ts`、`scripts/check-native-shells.mjs`、Expo / Tauri HostBridge 方案文档和共享开发流程记忆。
|
||||
- 验证方式:`npm run test -- packages/shared/src/contracts/hostBridge.test.ts src/services/host-bridge/hostBridge.test.ts`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
|
||||
|
||||
## 2026-06-19 桌面网络探测超时单一来源
|
||||
|
||||
- 背景:Tauri 桌面壳的 `network.status` 会从主站 origin 解析 host / port 后做短超时 TCP 可达性查询;此前 `DESKTOP_NETWORK_CHECK_TIMEOUT_MS` 只留在 Rust 本地,后续调整网络探测节奏时可能与共享 HostBridge 文档和门禁漂移。
|
||||
- 决策:`packages/shared/src/contracts/hostBridge.ts` 导出 `HOST_BRIDGE_DESKTOP_NETWORK_CHECK_TIMEOUT_MS`,作为桌面壳主站可达性探测超时的声明来源;Tauri Rust 侧保留同名职责镜像 `DESKTOP_NETWORK_CHECK_TIMEOUT_MS`,由 `apps/desktop-shell/scripts/check-config.mjs` 反查共享值并拒绝漂移。该边界只服务桌面宿主内部网络状态,不新增 H5 任意网络探测能力。
|
||||
- 影响范围:`packages/shared/src/contracts/hostBridge.ts`、`apps/desktop-shell/src-tauri/src/shell/network.rs`、`apps/desktop-shell/scripts/check-config.mjs`、宿主壳能力统一协议文档、Expo / Tauri HostBridge 方案文档。
|
||||
- 验证方式:`npm run test -- packages/shared/src/contracts/hostBridge.test.ts`、`npm run desktop-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
|
||||
|
||||
@@ -260,7 +260,7 @@ GameBridge 禁止:
|
||||
- 壳层只接受来自允许 origin / packaged asset 的消息。
|
||||
- H5 侧 HostBridge listener 只接收原生壳注入到当前窗口的 message;带有非当前窗口 `source` 或非当前页面 `origin` 的消息必须忽略,避免 AI sandbox iframe 或其它子上下文伪造 HostBridge response / event。
|
||||
- 每个请求必须有超时;H5 的 React Native WebView transport 和 Tauri `invoke` transport 都必须在前端侧按 `timeoutMs` 释放请求,默认请求超时、最大请求超时和用户交互长操作超时以 `packages/shared/src/contracts/hostBridge.ts` 的 `HOST_BRIDGE_DEFAULT_REQUEST_TIMEOUT_MS` / `HOST_BRIDGE_MAX_REQUEST_TIMEOUT_MS` / `HOST_BRIDGE_USER_INTERACTION_TIMEOUT_MS` 为唯一来源,宿主侧执行超时也只能返回标准 HostBridge 错误。重复 `id` 不得重复执行支付、登录、系统分享、文件导入导出、本地通知等宿主副作用;Expo 和 Tauri 壳都必须按 request id 回放首次完成结果,已完成响应缓存上限以共享契约 `HOST_BRIDGE_RESPONSE_CACHE_MAX` 为唯一来源。
|
||||
- HostBridge 的 capability profile、宿主上下文 query 字段和值、文件 MIME 清单、导入 / 导出体积上限、文件名 fallback / 长度上限、request id 长度、角标上限、剪贴板文本长度、二维码文本长度和本地通知标题 / 正文长度都必须以 `packages/shared/src/contracts/hostBridge.ts` 为声明来源;Expo 移动壳直接导入共享 profile 和契约常量,微信小程序壳和 Tauri 壳分别保留小程序 CommonJS / Rust 运行时代码镜像并由测试和配置门禁反查共享契约。
|
||||
- HostBridge 的 capability profile、宿主上下文 query 字段和值、文件 MIME 清单、导入 / 导出体积上限、文件名 fallback / 长度上限、request id 长度、角标上限、剪贴板文本长度、二维码文本长度、本地通知标题 / 正文长度和桌面网络探测超时都必须以 `packages/shared/src/contracts/hostBridge.ts` 为声明来源;Expo 移动壳直接导入共享 profile 和契约常量,微信小程序壳和 Tauri 壳分别保留小程序 CommonJS / Rust 运行时代码镜像并由测试和配置门禁反查共享契约。
|
||||
- 能力按 `capabilities` / `hostCapabilities` 下发,H5 会过滤未知能力,并根据声明结果决定是否展示入口、发起宿主请求或走 fallback;进入 `native_app` 后主 App 会再通过真实 `host.getRuntime` 回读一次宿主 runtime 并缓存能力,用来补齐裁剪壳或旧入口 URL 缺少 `hostCapabilities` 的场景,该回读请求的短超时以共享契约 `HOST_BRIDGE_RUNTIME_REFRESH_TIMEOUT_MS` 为唯一来源。不能只凭 `native_app` 宿主类型假设能力可用。
|
||||
- 壳能力声明与三端壳验收必须通过 `npm run check:native-shells` 统一校验;排查单端问题时可再分别运行微信壳测试集合、`npm run mobile-shell:typecheck`、`npm run mobile-shell:test`、`npm run mobile-shell:config`、`npm run mobile-shell:export`、`npm run desktop-shell:typecheck`、`npm run desktop-shell:test` 或 `npm run desktop-shell:build -- --no-bundle`。声明的 capability 必须来自共享 HostBridge profile 并存在于共享白名单,壳 runtime 回包、H5 URL `hostCapabilities`、壳实现、文件载荷边界、微信 WebView / 支付 / 订阅 / 分享桥接行为、微信小程序页面路由、WebView source query、微信请求头运行时标记、H5 runtime parser、H5 路由保留字段、微信壳 H5 / API HTTPS 域名格式、Expo managed config、移动端 production bundle、桌面 release 构建入口和微信 / Expo / Tauri 三端生产源码临时替身词扫描不得漂移。微信小程序壳不使用 Expo / Tauri 式统一 request dispatcher,但每个声明 capability 都必须在根级门禁中映射到真实流程文件、关键 `wx.*` 或页面工厂调用和对应测试清单。
|
||||
- Expo SDK、React Native、`react-native-webview`、Tauri CLI、Tauri Rust crate 和桌面 Cargo 插件版本属于宿主壳行为边界。升级这些依赖前必须同步更新壳配置检查、`package-lock.json` / `Cargo.lock` 解析版本、本文档和对应验证结果,不能只改 package / Cargo 版本让生产壳行为静默漂移。
|
||||
|
||||
@@ -52,7 +52,7 @@ Tauri 桌面壳启动时必须按 `label="main"` 解析 `tauri.conf.json` 主窗
|
||||
- `getHostRuntime()`:识别 `browser`、`wechat_mini_program`、`native_app`,并解析 `hostCapabilities` 能力声明;进入 `native_app` 后会通过真实 `host.getRuntime` 回读宿主 runtime 并缓存能力清单,未知能力会被丢弃。H5 业务只根据已声明或已回读的能力展示入口、发起宿主请求或走 fallback。
|
||||
- `getHostAppearanceColorScheme()`:原生 App 宿主的受控外观查询入口。H5 可通过 `appearance.getColorScheme` 读取宿主当前 `light` / `dark` / `unknown` 配色模式;Expo 移动壳通过 React Native `Appearance.getColorScheme()` 读取系统偏好,Tauri 桌面壳通过主窗口 `theme()` 读取窗口主题。该能力只读,不改变 H5 主题,也不覆盖用户或系统偏好。
|
||||
- `subscribeHostAppLifecycle()`:原生 App 宿主的受控生命周期事件入口。Expo 移动壳和 Tauri 桌面壳都声明 `host.events`,表示宿主会通过 HostBridge message 派发事件;其中 Expo 移动壳通过 React Native `AppState` 派发 `app.lifecycle`,Tauri 桌面壳通过主窗口 focus / blur、托盘隐藏 / 恢复和页面加载重放派发同名事件。桌面壳不会把 hidden、minimized 或 tray 扩成新的 `state`,而是读取 `is_visible()`、`is_minimized()`、`is_focused()` 后统一归一为 `active` / `inactive` / `background`,并只把 `hidden`、`minimized`、`focused`、`blurred` 放进 `nativeState` 用于排障。`host.events` 不作为 request method,也不开放 Tauri event 插件或 React Native 私有事件 API。H5 只依赖统一的 `active` / `inactive` / `background` 状态和 `focused` 布尔值,原生细分状态只放在 `nativeState` 用于排障,不作为业务分支依据。H5 统一通过 `useHostLifecycleActive()` 把宿主状态折算为运行态可播放状态;WebAudio 背景音乐和固定玩法 `<audio>` 背景音乐都必须按该状态暂停 / 恢复,宿主进入后台、inactive 或窗口失焦时暂停,回到 active 且 focused 后只在原运行态、音源和用户音量仍允许时恢复。
|
||||
- `getHostNetworkStatus()` / `subscribeHostNetworkStatusChange()`:原生 App 宿主的受控网络状态入口。Expo 移动壳通过 `expo-network` 查询并订阅真实系统网络状态;Tauri 桌面壳从 `WEB_APP_ORIGIN` 解析主站 host / port 后做短超时 TCP 可达性查询,并在主 WebView 内监听 `online` / `offline` 注入变化事件。H5 只依赖统一的 `isConnected`、`isInternetReachable` 和连接类型,不直接读取平台私有网络 API。平台外部生成队列概览通过 `useHostNetworkOnline()` 消费该状态,宿主未声明网络能力时保持原轮询行为,宿主明确离线或不可达时暂停轮询,恢复在线后重新刷新;该状态不替代后端队列事实或生成结果回读。
|
||||
- `getHostNetworkStatus()` / `subscribeHostNetworkStatusChange()`:原生 App 宿主的受控网络状态入口。Expo 移动壳通过 `expo-network` 查询并订阅真实系统网络状态;Tauri 桌面壳从 `WEB_APP_ORIGIN` 解析主站 host / port 后按共享契约 `HOST_BRIDGE_DESKTOP_NETWORK_CHECK_TIMEOUT_MS` 做短超时 TCP 可达性查询,并在主 WebView 内监听 `online` / `offline` 注入变化事件。H5 只依赖统一的 `isConnected`、`isInternetReachable` 和连接类型,不直接读取平台私有网络 API。平台外部生成队列概览通过 `useHostNetworkOnline()` 消费该状态,宿主未声明网络能力时保持原轮询行为,宿主明确离线或不可达时暂停轮询,恢复在线后重新刷新;该状态不替代后端队列事实或生成结果回读。
|
||||
- `subscribeHostNavigationCanGoBack()` / `useHostNavigationCanGoBack()`:原生 App 宿主的受控返回栈状态入口。H5 只有在宿主同时声明 `host.events` 和 `navigation.canGoBack` 时才订阅该事件;Expo 移动壳和 Tauri 桌面壳注入的状态只表示当前 H5 文档路由栈或宿主已归一后的返回状态,不让 H5 读取任意原生 back-forward list。主 App 在原生壳内直达非平台首页、非 runtime 的二级 H5 route 且当前 history state 没有应用导航标记时,会先把当前条目替换为 `/` 返回锚点,再把当前路径连同已保留的宿主 query 推回 history;普通浏览器、小程序、runtime 路由、已有应用 history 或不支持返回栈事件的裁剪壳不触发该补齐。
|
||||
- `requestHostLogin()`:微信小程序跳转原生登录页;浏览器返回 `false`,由 H5 登录弹窗承接。
|
||||
- `requestHostPayment()`:微信小程序支付跳转原生支付页;其它渠道返回 `false`,继续走 H5 / Native 二维码。
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
HOST_BRIDGE_CAPABILITIES,
|
||||
HOST_BRIDGE_DOCUMENT_MIME_TYPES,
|
||||
HOST_BRIDGE_DEFAULT_REQUEST_TIMEOUT_MS,
|
||||
HOST_BRIDGE_DESKTOP_NETWORK_CHECK_TIMEOUT_MS,
|
||||
HOST_BRIDGE_EVENTS,
|
||||
HOST_BRIDGE_IMPORT_DOCUMENT_MAX_BYTES,
|
||||
HOST_BRIDGE_MAX_REQUEST_TIMEOUT_MS,
|
||||
@@ -58,6 +59,7 @@ describe('HostBridge shared contract helpers', () => {
|
||||
expect(HOST_BRIDGE_RUNTIME_REFRESH_TIMEOUT_MS).toBe(3000);
|
||||
expect(HOST_BRIDGE_USER_INTERACTION_TIMEOUT_MS).toBe(30000);
|
||||
expect(HOST_BRIDGE_SCANNER_TIMEOUT_MS).toBe(60000);
|
||||
expect(HOST_BRIDGE_DESKTOP_NETWORK_CHECK_TIMEOUT_MS).toBe(1200);
|
||||
});
|
||||
|
||||
test('固定宿主侧响应回放缓存边界', () => {
|
||||
|
||||
@@ -221,6 +221,7 @@ export const HOST_BRIDGE_MAX_REQUEST_TIMEOUT_MS = 60000;
|
||||
export const HOST_BRIDGE_RUNTIME_REFRESH_TIMEOUT_MS = 3000;
|
||||
export const HOST_BRIDGE_USER_INTERACTION_TIMEOUT_MS = 30000;
|
||||
export const HOST_BRIDGE_SCANNER_TIMEOUT_MS = 60000;
|
||||
export const HOST_BRIDGE_DESKTOP_NETWORK_CHECK_TIMEOUT_MS = 1200;
|
||||
export const HOST_BRIDGE_RESPONSE_CACHE_MAX = 128;
|
||||
export const HOST_BRIDGE_REQUEST_ID_MAX_LENGTH = 120;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user