From 6a5869596c083bf737509ff36962c56ec722636f Mon Sep 17 00:00:00 2001 From: kdletters Date: Thu, 18 Jun 2026 12:30:43 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B6=E5=8F=A3=E6=A1=8C=E9=9D=A2=E5=A3=B3JS?= =?UTF-8?q?=E4=BE=9D=E8=B5=96=E8=BE=B9=E7=95=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 移除根H5与桌面壳未使用的Tauri JS guest依赖 新增桌面壳检查,禁止根包和桌面壳包安装Tauri JS guest包 更新原生壳方案和共享决策记录,明确桌面系统能力只由Rust HostBridge分发 --- apps/desktop-shell/package.json | 4 --- apps/desktop-shell/scripts/check-config.mjs | 27 +++++++++++++++ .../shared-memory/decision-log.md | 7 ++++ ...ExpoReactNative与Tauri宿主壳方案-2026-06-17.md | 3 ++ package-lock.json | 34 ------------------- package.json | 2 -- 6 files changed, 37 insertions(+), 40 deletions(-) diff --git a/apps/desktop-shell/package.json b/apps/desktop-shell/package.json index cb678ecbc..8d38f6e25 100644 --- a/apps/desktop-shell/package.json +++ b/apps/desktop-shell/package.json @@ -8,10 +8,6 @@ "build": "tauri build", "typecheck": "node scripts/check-config.mjs" }, - "dependencies": { - "@tauri-apps/api": "^2.11.0", - "@tauri-apps/plugin-opener": "^2.5.0" - }, "devDependencies": { "@tauri-apps/cli": "^2.11.2", "typescript": "~5.8.2" diff --git a/apps/desktop-shell/scripts/check-config.mjs b/apps/desktop-shell/scripts/check-config.mjs index b9b8a8402..081a574fd 100644 --- a/apps/desktop-shell/scripts/check-config.mjs +++ b/apps/desktop-shell/scripts/check-config.mjs @@ -4,6 +4,8 @@ const configPath = new URL('../src-tauri/tauri.conf.json', import.meta.url); const config = JSON.parse(fs.readFileSync(configPath, 'utf8')); const packagePath = new URL('../package.json', import.meta.url); const packageConfig = JSON.parse(fs.readFileSync(packagePath, 'utf8')); +const rootPackagePath = new URL('../../../package.json', import.meta.url); +const rootPackageConfig = JSON.parse(fs.readFileSync(rootPackagePath, 'utf8')); const capabilityPath = new URL( '../src-tauri/capabilities/main.json', import.meta.url, @@ -136,6 +138,29 @@ function assertNoBlockedNpmDependencies() { } } +function collectNpmDependencyNames(packageJson) { + const dependencySections = [ + 'dependencies', + 'devDependencies', + 'optionalDependencies', + 'peerDependencies', + ]; + + return dependencySections.flatMap((section) => + Object.keys(packageJson[section] ?? {}), + ); +} + +function assertNoTauriGuestNpmDependencies(packageJson, packageLabel) { + for (const dependency of collectNpmDependencyNames(packageJson)) { + if (dependency === '@tauri-apps/api' || dependency.startsWith('@tauri-apps/plugin-')) { + throw new Error( + `${packageLabel} must not depend on ${dependency}; desktop H5 must use HostBridge through the injected Tauri global only`, + ); + } + } +} + function assertNoBlockedCargoDependencies() { for (const dependency of blockedDesktopCargoDependencies) { const dependencyPattern = new RegExp( @@ -220,6 +245,8 @@ assertNoDevScaffoldTerms( productionSourceRoots.flatMap((root) => collectProductionSourceFiles(root)), ); assertNoBlockedNpmDependencies(); +assertNoTauriGuestNpmDependencies(packageConfig, 'desktop shell package'); +assertNoTauriGuestNpmDependencies(rootPackageConfig, 'root H5 package'); assertNoBlockedCargoDependencies(); assertNoBlockedDesktopSdkSnippets(); diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index 946c7e078..1ed7464f2 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -2376,3 +2376,10 @@ - 决策:移动壳配置门禁固定 Expo `name`、`slug`、`userInterfaceStyle`、`assetBundlePatterns`、`extra.genarrativeHostBridgeVersion`,并要求 Android 默认 `permissions` 为空,只通过 `blockedPermissions` 阻断当前不需要的高风险权限;`apps/mobile-shell/scripts/check-config.mjs` 检查源 `app.json`,`apps/mobile-shell/scripts/check-expo-config.mjs` 检查 Expo CLI 最终解析出的 public config,防止 config plugin 或解析阶段引入身份、资源或权限漂移。新增权限必须先有真实宿主能力、系统权限说明和 H5 fallback。桌面壳配置门禁固定唯一 `label=main` 主窗口,`src-tauri/capabilities/` 只能存在 `main.json`,且该 capability 只能绑定 `windows=["main"]`、`permissions=["core:default","allow-host-bridge-request"]`,继续只暴露 `host_bridge_request` 一个受控入口。 - 影响范围:`apps/mobile-shell/scripts/check-config.mjs`、`apps/mobile-shell/scripts/check-expo-config.mjs`、`apps/desktop-shell/scripts/check-config.mjs`、Expo / Tauri HostBridge 方案文档。 - 验证方式:`npm run check:native-shells`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。 + +## 2026-06-18 桌面壳 JS guest 依赖收口 + +- 背景:Tauri 桌面壳的生产前端实际只通过 `HostBridge` 和注入的 `window.__TAURI__.core.invoke('host_bridge_request')` 与 Rust 通信;如果根 H5 包或桌面壳包安装 `@tauri-apps/api` / `@tauri-apps/plugin-*` JS 客户端包,后续容易绕过唯一 command 与 capability 边界。 +- 决策:根 H5 `package.json` 和 `apps/desktop-shell/package.json` 不安装 `@tauri-apps/api` 或任何 `@tauri-apps/plugin-*` JS guest 包;opener、clipboard、dialog、notification 等桌面系统能力只保留 Rust Cargo 插件,由 `host_bridge_request` 内部分发。`apps/desktop-shell/scripts/check-config.mjs` 对两个 package 都做依赖门禁,Tauri CLI 仅作为构建工具保留。 +- 影响范围:根依赖、桌面壳依赖、桌面壳配置检查和 Expo / Tauri HostBridge 方案文档。 +- 验证方式:`npm run check:native-shells`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。 diff --git a/docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md b/docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md index c7c785f56..fb20d2263 100644 --- a/docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md +++ b/docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md @@ -182,6 +182,7 @@ Tauri 壳同样只负责桌面宿主能力,不承接玩法业务。 - 主 WebView 默认拒绝网页自动下载或 `` 触发的落盘动作;用户保存文本、图片、音频等内容必须走已声明的 `file.exportText`、`file.exportImage`、`file.exportAudio` HostBridge method,由 Rust 侧执行 MIME、大小、文件名清洗和系统保存对话框确认。 - 主 WebView 显式关闭 DevTools,Cargo 不启用 Tauri `devtools` feature;本地调试通过普通浏览器和 Vite 完成,不把可分发桌面壳变成调试容器。 - 崩溃上报、前端 analytics、桌面遥测日志、自动更新和渠道分发 SDK 都必须等真实端点、采集字段、用户同意、隐私策略、签名和发布流程确定后逐项接入;当前桌面壳不安装 Sentry、Datadog、PostHog、Segment、Amplitude、Bugsnag、OpenTelemetry、Tauri log / updater 等相关依赖。 +- 桌面壳和根 H5 包不安装 `@tauri-apps/api` 或 `@tauri-apps/plugin-*` JS guest 包;生产 H5 只通过 Tauri 注入的 `window.__TAURI__.core.invoke('host_bridge_request', request)` 进入 HostBridge。opener、clipboard、dialog、notification 等能力只保留 Rust Cargo 插件,由 Rust 内部分发并受 capability 白名单约束。 桌面 release 和 dev 模式: @@ -342,6 +343,8 @@ GameBridge 禁止: 2026-06-18 追加:桌面壳 capability 作用域进入配置门禁。Tauri 配置只能声明一个 `label=main` 的主窗口,`src-tauri/capabilities/` 只能存在 `main.json`,该 capability 的 `identifier` 必须为 `main`、`windows` 必须只包含 `main`、`permissions` 必须只包含 `core:default` 和 `allow-host-bridge-request`。这保证 opener、clipboard、dialog、notification 等插件只由 Rust 内部通过受控 HostBridge 分发使用,不把插件 JS guest API 或额外窗口权限授给 H5 主站。 +2026-06-18 追加:桌面壳 JS guest 依赖进入门禁。`apps/desktop-shell/package.json` 和根 H5 `package.json` 不得安装 `@tauri-apps/api` 或任何 `@tauri-apps/plugin-*` 包,避免生产前端绕过 `nativeAppHostBridge` 直接调用 Tauri JS 客户端 API;Tauri CLI 仍只作为构建工具留在 devDependencies,桌面系统能力继续由 Rust 侧 Cargo 插件和唯一 `host_bridge_request` command 承接。 + 2026-06-18 追加:桌面壳 release CSP 与 dev CSP 分离。Release `csp` 不再包含 `http://127.0.0.1:*` 或 `ws://127.0.0.1:*`,只允许打包资产、自身脚本、生产 HTTPS / WSS API、图片、媒体和 sandbox frame 所需来源;本地 Vite、HMR WebSocket 和开发 frame 只写入 Tauri `devCsp`。`apps/desktop-shell/scripts/check-config.mjs` 会拒绝 release CSP 混入本机调试源,也会校验 dev CSP 仍保留本机开发源。 2026-06-18 追加:桌面壳 release 构建烟测进入统一验收。`npm run check:native-shells` 会在 H5 HostBridge、Expo 壳和 Tauri 单测通过后执行 `npm run desktop-shell:build -- --no-bundle`,确认根 `dist` H5 资产、Tauri release 入口、受控命令白名单、图标和 Rust release 编译可以共同产出桌面二进制;该烟测不生成平台安装包,避免把 Linux 本机缺少的系统打包器误判为 HostBridge 回归。 diff --git a/package-lock.json b/package-lock.json index 44c204c6e..15cda2fbe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,8 +10,6 @@ "dependencies": { "@expo/metro-runtime": "^56.0.15", "@tailwindcss/vite": "^4.1.14", - "@tauri-apps/api": "^2.11.0", - "@tauri-apps/plugin-opener": "^2.5.4", "@vitejs/plugin-react": "^5.0.4", "cannon-es": "^0.20.0", "dotenv": "^17.2.3", @@ -3677,16 +3675,6 @@ "vite": "^5.2.0 || ^6 || ^7 || ^8" } }, - "node_modules/@tauri-apps/api": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.11.0.tgz", - "integrity": "sha512-7CinYODhky9lmO23xHnUFv0Xt43fbtWMyxZcLcRBlFkcgXKuEirBvHpmtJ89YMhyeGcq20Wuc47Fa4XjyniywA==", - "license": "Apache-2.0 OR MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/tauri" - } - }, "node_modules/@tauri-apps/cli": { "version": "2.11.2", "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.11.2.tgz", @@ -3904,15 +3892,6 @@ "node": ">= 10" } }, - "node_modules/@tauri-apps/plugin-opener": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-opener/-/plugin-opener-2.5.4.tgz", - "integrity": "sha512-1HnPkb+AmgO29HBazm4uPLKB+r7zzcTBW1d0fyYp1uP+jwtpoiNDGKMMzz58SFp49nOIrxdE3aUJtT57lfO9CQ==", - "license": "MIT OR Apache-2.0", - "dependencies": { - "@tauri-apps/api": "^2.11.0" - } - }, "node_modules/@testing-library/dom": { "version": "10.4.1", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", @@ -15542,11 +15521,6 @@ "tailwindcss": "4.2.2" } }, - "@tauri-apps/api": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.11.0.tgz", - "integrity": "sha512-7CinYODhky9lmO23xHnUFv0Xt43fbtWMyxZcLcRBlFkcgXKuEirBvHpmtJ89YMhyeGcq20Wuc47Fa4XjyniywA==" - }, "@tauri-apps/cli": { "version": "2.11.2", "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.11.2.tgz", @@ -15643,14 +15617,6 @@ "dev": true, "optional": true }, - "@tauri-apps/plugin-opener": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-opener/-/plugin-opener-2.5.4.tgz", - "integrity": "sha512-1HnPkb+AmgO29HBazm4uPLKB+r7zzcTBW1d0fyYp1uP+jwtpoiNDGKMMzz58SFp49nOIrxdE3aUJtT57lfO9CQ==", - "requires": { - "@tauri-apps/api": "^2.11.0" - } - }, "@testing-library/dom": { "version": "10.4.1", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", diff --git a/package.json b/package.json index 6aa13dd49..6209d5a25 100644 --- a/package.json +++ b/package.json @@ -82,8 +82,6 @@ "dependencies": { "@expo/metro-runtime": "^56.0.15", "@tailwindcss/vite": "^4.1.14", - "@tauri-apps/api": "^2.11.0", - "@tauri-apps/plugin-opener": "^2.5.4", "@vitejs/plugin-react": "^5.0.4", "cannon-es": "^0.20.0", "dotenv": "^17.2.3",