diff --git a/apps/desktop-shell/scripts/check-config.mjs b/apps/desktop-shell/scripts/check-config.mjs index 562c920ee..d620043bb 100644 --- a/apps/desktop-shell/scripts/check-config.mjs +++ b/apps/desktop-shell/scripts/check-config.mjs @@ -2133,11 +2133,11 @@ for (const snippet of [ 'open_desktop_host_bridge_external_url', 'external_url_unavailable_response', 'log_desktop_navigation_failure', - 'desktop navigation failed for {label}: {error}', + 'desktop navigation failed for {label}', 'desktop_external_url_from_request', 'normalize_external_url', 'open_normalized_desktop_external_url(app, url)', - 'log_desktop_navigation_failure("external.open", &error.to_string())', + 'log_desktop_navigation_failure("external.open")', 'desktop_external_url_request_accepts_only_safe_system_protocols', '"external URL cannot be opened"', 'external_url_unavailable_response_is_stable', @@ -2146,18 +2146,20 @@ for (const snippet of [ 'desktop_native_page_url_from_request', 'normalize_native_page_url', 'window.navigate(url)', - 'log_desktop_navigation_failure("native.navigate", &error.to_string())', - 'log_desktop_navigation_failure("native.window", "main window unavailable")', + 'log_desktop_navigation_failure("native.navigate")', + 'log_desktop_navigation_failure("native.window")', 'desktop_native_page_request_accepts_only_same_origin_h5_routes', 'navigation_failures_are_logged_without_exposing_native_detail', + 'navigation_failures_log_stable_label_only', '"native page unavailable"', 'native_page_unavailable_response_is_stable', 'reload_desktop_host_bridge_webview', 'webview_reload_unavailable_response', 'window.reload()', - 'log_desktop_navigation_failure("webview.reload", &error.to_string())', - 'log_desktop_navigation_failure("webview.window", "main window unavailable")', + 'log_desktop_navigation_failure("webview.reload")', + 'log_desktop_navigation_failure("webview.window")', 'webview_reload_failures_are_logged_without_exposing_native_detail', + 'webview_reload_failures_log_stable_label_only', '"webview reload unavailable"', 'webview_reload_unavailable_response_is_stable', ]) { @@ -2167,7 +2169,13 @@ for (const snippet of [ } if ( desktopHostBridgeNavigationSource.includes('failed(request.id.clone(), "host_error", error.to_string())') || - desktopHostBridgeNavigationSource.includes('"main window not found"') + desktopHostBridgeNavigationSource.includes('"main window not found"') || + desktopHostBridgeNavigationSource.includes('desktop navigation failed for {label}: {error}') || + desktopHostBridgeNavigationSource.includes('log_desktop_navigation_failure("external.open", &error.to_string())') || + desktopHostBridgeNavigationSource.includes('log_desktop_navigation_failure("native.navigate", &error.to_string())') || + desktopHostBridgeNavigationSource.includes('log_desktop_navigation_failure("webview.reload", &error.to_string())') || + desktopHostBridgeNavigationSource.includes('log_desktop_navigation_failure("native.window", "main window unavailable")') || + desktopHostBridgeNavigationSource.includes('log_desktop_navigation_failure("webview.window", "main window unavailable")') ) { throw new Error('desktop shell navigation module must hide native navigation errors'); } diff --git a/apps/desktop-shell/src-tauri/src/host_bridge/navigation.rs b/apps/desktop-shell/src-tauri/src/host_bridge/navigation.rs index aeb5b1fcb..0943481de 100644 --- a/apps/desktop-shell/src-tauri/src/host_bridge/navigation.rs +++ b/apps/desktop-shell/src-tauri/src/host_bridge/navigation.rs @@ -48,8 +48,8 @@ pub(crate) fn open_desktop_host_bridge_external_url( match open_normalized_desktop_external_url(app, url) { Ok(()) => ok(request.id.clone(), json!(true)), - Err(error) => { - log_desktop_navigation_failure("external.open", &error.to_string()); + Err(_error) => { + log_desktop_navigation_failure("external.open"); external_url_unavailable_response(request) } } @@ -75,13 +75,13 @@ pub(crate) fn open_desktop_host_bridge_native_page( match app.get_webview_window("main") { Some(window) => match window.navigate(url) { Ok(()) => ok(request.id.clone(), json!(true)), - Err(error) => { - log_desktop_navigation_failure("native.navigate", &error.to_string()); + Err(_error) => { + log_desktop_navigation_failure("native.navigate"); native_page_unavailable_response(request) } }, None => { - log_desktop_navigation_failure("native.window", "main window unavailable"); + log_desktop_navigation_failure("native.window"); native_page_unavailable_response(request) } } @@ -91,11 +91,8 @@ fn native_page_unavailable_response(request: &HostBridgeRequest) -> HostBridgeRe failed(request.id.clone(), "host_error", "native page unavailable") } -fn log_desktop_navigation_failure(label: &str, error: &str) -> bool { - if !error.is_empty() { - eprintln!("desktop navigation failed for {label}: {error}"); - } - +fn log_desktop_navigation_failure(label: &str) -> bool { + eprintln!("desktop navigation failed for {label}"); false } @@ -202,18 +199,14 @@ mod tests { #[test] fn navigation_failures_are_logged_without_exposing_native_detail() { - assert!(!log_desktop_navigation_failure( - "external.open", - "private external detail" - )); - assert!(!log_desktop_navigation_failure( - "native.navigate", - "private navigation detail" - )); - assert!(!log_desktop_navigation_failure( - "native.window", - "main window unavailable" - )); + assert!(!log_desktop_navigation_failure("external.open")); + assert!(!log_desktop_navigation_failure("native.navigate")); + assert!(!log_desktop_navigation_failure("native.window")); + } + + #[test] + fn navigation_failures_log_stable_label_only() { + assert!(!log_desktop_navigation_failure("external.open")); } } @@ -224,13 +217,13 @@ pub(crate) fn reload_desktop_host_bridge_webview( match app.get_webview_window("main") { Some(window) => match window.reload() { Ok(()) => ok(request.id.clone(), json!(true)), - Err(error) => { - log_desktop_navigation_failure("webview.reload", &error.to_string()); + Err(_error) => { + log_desktop_navigation_failure("webview.reload"); webview_reload_unavailable_response(request) } }, None => { - log_desktop_navigation_failure("webview.window", "main window unavailable"); + log_desktop_navigation_failure("webview.window"); webview_reload_unavailable_response(request) } } @@ -261,13 +254,12 @@ mod reload_tests { #[test] fn webview_reload_failures_are_logged_without_exposing_native_detail() { - assert!(!log_desktop_navigation_failure( - "webview.reload", - "private reload detail" - )); - assert!(!log_desktop_navigation_failure( - "webview.window", - "main window unavailable" - )); + assert!(!log_desktop_navigation_failure("webview.reload")); + assert!(!log_desktop_navigation_failure("webview.window")); + } + + #[test] + fn webview_reload_failures_log_stable_label_only() { + assert!(!log_desktop_navigation_failure("webview.reload")); } } diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index 18c255a81..1cc9df5d1 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -3170,6 +3170,7 @@ - 背景:Tauri 桌面壳的外链打开、同源 H5 route 导航和 WebView reload 都直接影响原生壳内 H5 的完整流程;这些系统调用失败时,H5 只应得到稳定错误语义,但开发侧需要能区分外链打开失败、窗口导航失败、reload 失败和主窗口缺失。 - 决策:`apps/desktop-shell/src-tauri/src/host_bridge/navigation.rs` 必须在外链打开失败、同源 H5 route 导航失败、WebView reload 失败和主窗口缺失时记录 `desktop navigation failed for ...` 日志。HostBridge 对 H5 仍只返回稳定 `external URL cannot be opened`、`native page unavailable` 或 `webview reload unavailable`,不透传系统错误、窗口内部信息或平台细节。 +- 2026-06-21 调整:桌面导航失败日志只记录 `desktop navigation failed for ` 固定标签,不输出 opener、window.navigate、WebView reload 错误或主窗口缺失说明;配置检查拒绝 `navigation.rs` 重新拼接 `: {error}`、`&error.to_string()` 或把主窗口缺失文本传给日志函数。 - 验证方式:`cargo test --manifest-path apps/desktop-shell/src-tauri/Cargo.toml host_bridge::navigation`、`npm run desktop-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。 ## 2026-06-20 桌面壳网络状态系统异常必须可观测 diff --git a/docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md b/docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md index 2a8acbf78..0b237e6e9 100644 --- a/docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md +++ b/docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md @@ -356,7 +356,7 @@ GameBridge 禁止: 2026-06-20 追加:桌面壳窗口状态小能力的系统异常必须可观测。Tauri 主窗口缺失、主题读取失败、角标设置失败或窗口标题设置失败时,桌面壳必须分别记录 `desktop appearance failed for ...`、`desktop app badge failed for ...` 或 `desktop window title failed for ...` 日志;HostBridge 回包仍只暴露稳定 `appearance unavailable`、`badge unavailable` 或 `window title unavailable` 语义,不透传系统错误、窗口内部信息或平台细节。2026-06-21 调整:桌面外观、角标和窗口标题失败日志只记录 `desktop appearance failed for ` / `desktop app badge failed for ` / `desktop window title failed for ` 固定标签,不把 Tauri `theme()` / `set_badge_count` / `set_title` 错误详情或主窗口缺失细节写入 stderr。 -2026-06-20 追加:桌面壳导航系统异常必须可观测。Tauri 外链打开失败、同源 H5 route 导航失败、WebView reload 失败或主窗口缺失时,桌面壳必须记录 `desktop navigation failed for ...` 日志;HostBridge 回包仍只暴露稳定 `external URL cannot be opened`、`native page unavailable` 或 `webview reload unavailable` 语义,不透传系统错误、窗口内部信息或平台细节。 +2026-06-20 追加:桌面壳导航系统异常必须可观测。Tauri 外链打开失败、同源 H5 route 导航失败、WebView reload 失败或主窗口缺失时,桌面壳必须记录 `desktop navigation failed for ...` 日志;HostBridge 回包仍只暴露稳定 `external URL cannot be opened`、`native page unavailable` 或 `webview reload unavailable` 语义,不透传系统错误、窗口内部信息或平台细节。2026-06-21 调整:该日志只记录 `desktop navigation failed for ` 固定标签,不把 opener、window.navigate、WebView reload 错误或主窗口缺失说明写入 stderr。 2026-06-20 追加:桌面壳网络状态查询系统异常必须可观测。Tauri 桌面壳后台解析网络状态任务失败时必须记录 `desktop network failed for status.resolve` 日志;HostBridge 回包仍只暴露稳定 `network status unavailable` 语义,不透传 resolver、线程或系统错误细节。2026-06-21 调整:该日志只记录固定标签,不把后台任务 join 错误或 resolver 异常写入 stderr。