收口原生壳命令入口
移动壳配置检查锁定壳级和根级脚本入口 桌面壳配置检查锁定壳级和根级脚本入口 桌面壳配置检查锁定Tauri dev与build前置命令 宿主壳方案文档补充命令入口门禁 共享决策日志记录原生壳命令入口边界
This commit is contained in:
@@ -193,6 +193,15 @@ function assertNoBlockedDesktopSdkSnippets() {
|
||||
}
|
||||
}
|
||||
|
||||
function assertPackageScript(packageJson, packageLabel, scriptName, expected) {
|
||||
const actual = packageJson.scripts?.[scriptName];
|
||||
if (actual !== expected) {
|
||||
throw new Error(
|
||||
`${packageLabel} script ${scriptName} drifted: expected ${expected} but got ${actual}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function collectProductionSourceFiles(entry) {
|
||||
const stats = fs.statSync(entry);
|
||||
if (stats.isDirectory()) {
|
||||
@@ -250,6 +259,23 @@ assertNoTauriGuestNpmDependencies(rootPackageConfig, 'root H5 package');
|
||||
assertNoBlockedCargoDependencies();
|
||||
assertNoBlockedDesktopSdkSnippets();
|
||||
|
||||
for (const [scriptName, expected] of Object.entries({
|
||||
dev: 'tauri dev',
|
||||
build: 'tauri build',
|
||||
typecheck: 'node scripts/check-config.mjs',
|
||||
})) {
|
||||
assertPackageScript(packageConfig, 'desktop shell package', scriptName, expected);
|
||||
}
|
||||
|
||||
for (const [scriptName, expected] of Object.entries({
|
||||
'desktop-shell:dev': 'npm --prefix apps/desktop-shell run dev',
|
||||
'desktop-shell:build': 'npm --prefix apps/desktop-shell run build --',
|
||||
'desktop-shell:typecheck': 'npm --prefix apps/desktop-shell run typecheck',
|
||||
'desktop-shell:test': 'cargo test --manifest-path apps/desktop-shell/src-tauri/Cargo.toml',
|
||||
})) {
|
||||
assertPackageScript(rootPackageConfig, 'root package', scriptName, expected);
|
||||
}
|
||||
|
||||
function readPngSize(file) {
|
||||
const buffer = fs.readFileSync(file);
|
||||
if (
|
||||
@@ -566,6 +592,10 @@ if (config.build?.beforeBuildCommand !== 'npm --prefix ../.. run build:raw && np
|
||||
throw new Error('desktop shell build command must run from apps/desktop-shell');
|
||||
}
|
||||
|
||||
if (config.build?.beforeDevCommand !== 'npm --prefix ../.. run dev:web') {
|
||||
throw new Error('desktop shell dev command must run the root H5 Vite dev server');
|
||||
}
|
||||
|
||||
const [mainWindow] = config.app?.windows ?? [];
|
||||
if (!mainWindow || mainWindow.create !== false) {
|
||||
throw new Error('desktop shell must create the main window from Rust setup');
|
||||
|
||||
@@ -188,6 +188,15 @@ function assertNoBlockedMobileChannelSnippets() {
|
||||
}
|
||||
}
|
||||
|
||||
function assertPackageScript(packageJson, packageLabel, scriptName, expected) {
|
||||
const actual = packageJson.scripts?.[scriptName];
|
||||
if (actual !== expected) {
|
||||
throw new Error(
|
||||
`${packageLabel} script ${scriptName} drifted: expected ${expected} but got ${actual}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function collectProductionSourceFiles(entry) {
|
||||
const stats = fs.statSync(entry);
|
||||
if (stats.isDirectory()) {
|
||||
@@ -272,6 +281,28 @@ assertNoBlockedMobileChannelDependencies(packageConfig, 'mobile shell package');
|
||||
assertNoBlockedMobileChannelDependencies(rootPackageConfig, 'root H5 package');
|
||||
assertNoBlockedMobileChannelSnippets();
|
||||
|
||||
for (const [scriptName, expected] of Object.entries({
|
||||
dev: 'expo start',
|
||||
android: 'expo run:android',
|
||||
ios: 'expo run:ios',
|
||||
test: 'vitest run -c vitest.config.ts',
|
||||
'config:smoke': 'node scripts/check-expo-config.mjs',
|
||||
'export:smoke': 'node scripts/check-expo-export.mjs',
|
||||
typecheck: 'tsc -p tsconfig.json --noEmit && node scripts/check-config.mjs',
|
||||
})) {
|
||||
assertPackageScript(packageConfig, 'mobile shell package', scriptName, expected);
|
||||
}
|
||||
|
||||
for (const [scriptName, expected] of Object.entries({
|
||||
'mobile-shell:dev': 'npm --prefix apps/mobile-shell run dev',
|
||||
'mobile-shell:typecheck': 'npm --prefix apps/mobile-shell run typecheck',
|
||||
'mobile-shell:test': 'npm --prefix apps/mobile-shell run test',
|
||||
'mobile-shell:config': 'npm --prefix apps/mobile-shell run config:smoke',
|
||||
'mobile-shell:export': 'npm --prefix apps/mobile-shell run export:smoke',
|
||||
})) {
|
||||
assertPackageScript(rootPackageConfig, 'root package', scriptName, expected);
|
||||
}
|
||||
|
||||
const sharedCapabilities = extractStringArrayExport(
|
||||
sharedContractSource,
|
||||
'HOST_BRIDGE_CAPABILITIES',
|
||||
|
||||
@@ -2438,6 +2438,13 @@
|
||||
- 影响范围:`apps/mobile-shell/app.json`、`apps/mobile-shell/scripts/check-config.mjs`、`apps/mobile-shell/scripts/check-expo-config.mjs`、Expo / Tauri HostBridge 方案文档。
|
||||
- 验证方式:`npm run check:native-shells`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。
|
||||
|
||||
## 2026-06-18 原生壳命令入口收口
|
||||
|
||||
- 背景:Expo / Tauri 壳的源码、权限和构建配置已经进入门禁,但 package scripts 仍属于真实开发和验收入口;如果后续把根级或壳级 dev / build / typecheck / test 命令改成临时快捷命令,就可能绕过 Expo public config、Metro export、Tauri dev、Tauri release build smoke 或根 H5 构建。
|
||||
- 决策:移动壳 `apps/mobile-shell/package.json` 的 `dev`、`android`、`ios`、`test`、`config:smoke`、`export:smoke`、`typecheck` 和根 `mobile-shell:*` 入口必须保持指向真实 Expo / RN / Vitest / Expo config / Metro export / 配置检查流程;桌面壳 `apps/desktop-shell/package.json` 的 `dev`、`build`、`typecheck`、根 `desktop-shell:*` 入口,以及 Tauri `beforeDevCommand` / `beforeBuildCommand` 必须保持指向真实 Tauri dev / build、根 H5 `dev:web` / `build:raw` 和桌面壳配置检查流程。两端配置检查负责拒绝命令入口漂移。
|
||||
- 影响范围:根 `package.json`、`apps/mobile-shell/package.json`、`apps/desktop-shell/package.json`、`apps/mobile-shell/scripts/check-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 原生 HostBridge 入站消息来源收口
|
||||
|
||||
- 背景:H5 主站会同时承载原生壳 HostBridge 和后续 AI H5 sandbox / GameBridge;如果 H5 侧只按 JSON envelope 识别 HostBridge response / event,sandbox iframe 可以构造同形 `postMessage` 干扰待处理宿主请求或伪造宿主事件。
|
||||
|
||||
@@ -305,6 +305,8 @@ GameBridge 禁止:
|
||||
|
||||
2026-06-18 追加:移动壳可分发身份、外观和默认权限进入配置门禁。Expo `name` 固定为 `Genarrative`,`slug` 固定为 `genarrative-mobile-shell`,`userInterfaceStyle` 固定为 `automatic`,`assetBundlePatterns` 固定为 `["**/*"]`,`extra.genarrativeHostBridgeVersion` 固定为 `1`;Android `permissions` 默认不得显式请求任何权限,所有当前不需要的高风险权限只能通过 `blockedPermissions` 阻断,后续新增权限必须先有真实宿主能力、系统权限说明和 H5 fallback 方案,再补配置与检查。`apps/mobile-shell/scripts/check-config.mjs` 会检查源 `app.json`,`apps/mobile-shell/scripts/check-expo-config.mjs` 会检查 Expo CLI 最终解析出的 public config,避免 config plugin 或 Expo 解析阶段引入身份、资源或权限漂移。
|
||||
|
||||
2026-06-18 追加:移动壳命令入口进入配置门禁。`apps/mobile-shell/package.json` 的 `dev`、`android`、`ios`、`test`、`config:smoke`、`export:smoke`、`typecheck` 以及根 `package.json` 的 `mobile-shell:*` 入口必须保持指向真实 Expo / RN / Vitest / Expo config / Metro export / 配置检查流程,不能替换成只跑静态脚本或绕过生产 bundler 的快捷命令。
|
||||
|
||||
### Phase 3:Tauri 桌面壳 MVP
|
||||
|
||||
- 新增 `apps/desktop-shell/`。
|
||||
@@ -334,6 +336,8 @@ GameBridge 禁止:
|
||||
|
||||
2026-06-18 追加:桌面壳安装包身份固定为 `world.genarrative.desktop`,产品名为 `Genarrative`,Tauri、Node package 与 Cargo package 版本统一为 `0.1.0`。Release 主窗口只能从打包进二进制的 `index.html` 进入根 `dist` H5 资产,dev URL 只能指向本机 Vite 调试入口;CSP 必须保持 `script-src 'self'`,不得加入 `unsafe-eval`、`tauri:` 或 `file:` 这类扩大桌面攻击面的来源。当前不配置自动更新器,直到存在真实更新端点、签名密钥和发布流程再接入;`apps/desktop-shell/scripts/check-config.mjs` 会校验这些包身份、版本、CSP 和 updater 禁用约束。
|
||||
|
||||
2026-06-18 追加:桌面壳命令入口进入配置门禁。`apps/desktop-shell/package.json` 的 `dev`、`build`、`typecheck`,根 `package.json` 的 `desktop-shell:*` 入口,以及 Tauri `beforeDevCommand` / `beforeBuildCommand` 都必须保持在真实 Tauri dev / build、根 H5 `dev:web`、根 H5 `build:raw` 和桌面壳配置检查路径上;本地桌面调试不能改成加载外部 H5、跳过根 Vite 或绕过 Tauri release build smoke 的命令。
|
||||
|
||||
2026-06-18 追加:桌面壳默认不接入崩溃上报、analytics、遥测日志或渠道分发 SDK。`apps/desktop-shell/scripts/check-config.mjs` 会拒绝 Sentry、Datadog、PostHog、Segment、Amplitude、Bugsnag、OpenTelemetry、Tauri log / updater 等 Node / Cargo 依赖和 Rust 初始化片段;后续只有在真实采集端点、数据字段、用户授权、隐私披露、签名和发布流程确定后,才能按单项能力补充方案与实现。
|
||||
|
||||
2026-06-18 追加:桌面壳 release / dev 入口 URL 的 `hostVersion` 必须与 Tauri `tauri.conf.json`、Node package 和 Cargo package 版本一致;`host.getRuntime` 回包继续使用 `env!("CARGO_PKG_VERSION")`,配置检查会拒绝入口 query 版本、Tauri 配置版本或 Rust runtime 版本来源分叉,避免桌面包升级时 H5 首屏上下文与 runtime 回读不一致。
|
||||
|
||||
Reference in New Issue
Block a user