补齐小程序壳路由一致性门禁
根级原生壳验收新增小程序页面路由一致性检查 订阅授权页面常量纳入微信协议反查 小程序 H5 页面常量、分享入口、来源 query 和 HTTPS 域名格式纳入门禁 同步宿主壳文档和共享决策记录
This commit is contained in:
@@ -32,6 +32,7 @@
|
||||
- 2026-06-18 原生壳统一验收门禁:根级 `npm run check:native-shells` 统一执行 H5 HostBridge 关键测试、Expo 壳 typecheck / test / config smoke / Metro export smoke、Tauri 壳 typecheck / cargo test、桌面 release `--no-bundle` 构建烟测,以及可分发壳与 H5 HostBridge 真实调用链的临时替身词扫描;根级 `npm run check` 会在 lint、主站测试、构建和内容检查后继续执行该门禁,避免 HostBridge、三端壳、Expo managed config、移动端 production bundle、桌面 release 入口和 H5 HostBridge 真实调用链禁替身验收散落成容易漏跑的单项命令。
|
||||
- 2026-06-19 原生壳临时替身扫描范围:`npm run check:native-shells` 的生产替身词扫描必须覆盖微信小程序壳生产 `.js`、Expo / Tauri 壳源码与配置、共享 HostBridge 契约、H5 native transport,并自动覆盖已接入真实宿主能力 facade 的 H5 生产调用链文件;新增 H5 调用点接入 HostBridge 时,必须让自动扫描覆盖对应文件或在同等门禁中证明生产代码没有 mock / fake / stub / TODO / FIXME / 模拟 / 伪造。
|
||||
- 2026-06-18 微信壳桥接层纳入统一验收:`npm run check:native-shells` 还会运行 `miniprogram/host-bridge/`、`miniprogram/shell/`、`pages/web-view` 样式和 `scripts/miniprogram-web-view-auth.test.ts` 的微信壳测试,覆盖 WebView 入口、登录触发、分享目标、支付结果、订阅消息结果和九宫切图行为;三端桥接层文件结构检查只证明目录边界,行为回归必须由同一门禁中的微信壳测试证明。
|
||||
- 2026-06-19 微信壳路由一致性门禁:`npm run check:native-shells` 必须反查 `miniprogram/app.json.pages`、`miniprogram/host-bridge/protocol.js`、H5 `src/services/host-bridge/hostBridge.ts` 小程序页面常量、H5 `src/services/wechatMiniProgramSubscribe.ts` 订阅授权页面常量、`miniprogram/host-bridge/webView.js` 分享入口 / 分享消息类型和 `miniprogram/config.js` source query / 域名格式。新增或调整小程序页面、登录派生 URL、支付页、九宫切图页、订阅页、WebView 来源标记、H5 入口域名或 API base URL 时,必须同步这几处常量并保持生产 / 开发域名都显式配置为纯 HTTPS domain;运行时开发域名回退生产域名只作为异常兜底。
|
||||
- 2026-06-18 登录 / 支付能力禁伪声明:`auth.requestLogin` 和 `payment.request` 保留在共享 HostBridge 契约中供未来真实接入,但 Expo / Tauri 壳在真实 SDK、渠道流程和后端契约落地前不得声明这些 capability,也不得把它们写入入口 URL `hostCapabilities`;两端检查脚本会拒绝伪声明,请求实际到达壳层时必须返回明确 `unsupported_method` 并让 H5 fallback,两端壳测试直接覆盖这两个 method。
|
||||
- 2026-06-18 移动壳触觉反馈边界:`haptics.impact` 只接受 `light`、`medium`、`heavy` 三档 impact style,缺省为 `light`;未知值必须返回 `invalid_request`,不得静默降级成真实设备触觉反馈。桌面壳不声明该 capability,H5 继续按 HostBridge fallback 处理。
|
||||
- 2026-06-18 分享卡图片导出:新增 `file.exportImage` HostBridge capability,H5 分享卡下载在 native app 中优先把 canvas 生成的 base64 图片交给宿主导出;Expo 壳写缓存图片后交给系统分享 / 保存面板,Tauri 壳通过系统保存对话框写入图片字节。该能力只接受 `image/png` / `image/jpeg` / `image/webp`、单次 5 MiB 内图片数据,成功只返回文件名和字节数,不暴露本机绝对路径;宿主未声明时保留浏览器下载。
|
||||
|
||||
@@ -218,6 +218,7 @@ npm run check:native-shells
|
||||
|
||||
该命令会覆盖 H5 HostBridge 关键测试、微信 / Expo / Tauri 三端桥接层文件结构门禁、Expo 壳 typecheck / test / config smoke / Metro export smoke、Tauri 壳 typecheck / cargo test、桌面壳 release `--no-bundle` 构建烟测,以及可分发壳与 H5 HostBridge 真实调用链的临时替身词扫描,确认 Expo managed config、移动端 iOS / Android production bundle、打包 H5 资产、Tauri release 入口和 H5 HostBridge 真实调用链没有漂移;扫描范围包含微信小程序壳生产 `.js`、共享 HostBridge 契约、H5 native transport,并自动覆盖已接入真实宿主能力 facade 的 H5 生产调用链文件。壳源码和配置继续严格禁止 mock / fake / placeholder / stub / TODO / FIXME / 占位 / 模拟 / 伪造;H5 业务调用链允许正常表单 `placeholder` 属性和业务占位图文案,但仍禁止 mock / fake / stub / TODO / FIXME / 模拟 / 伪造等替身痕迹。
|
||||
该命令同时会运行微信小程序 `miniprogram/host-bridge/`、`miniprogram/shell/`、`pages/web-view` 样式和 `scripts/miniprogram-web-view-auth.test.ts` 的壳层测试,保证微信桥接层拆分后的支付、订阅消息、九宫切图、分享目标和 WebView 登录 / 分享入口行为与 Expo、Tauri 壳一起验收。
|
||||
该命令还会反查微信小程序 `app.json.pages` 与 `host-bridge/protocol.js` 页面 URL、H5 小程序页面常量、H5 订阅授权页面常量、WebView 分享入口、分享目标消息类型、`WEB_VIEW_SOURCE_QUERY` 和 H5 / API base URL 格式,避免页面路由、来源标记或域名配置在微信壳、H5 HostBridge 与运行时配置之间分叉。生产 / 开发 H5 与 API 域名都必须显式配置为纯 HTTPS domain,运行时开发域名回退生产域名只作为异常兜底。
|
||||
|
||||
内容检查:
|
||||
|
||||
|
||||
@@ -256,7 +256,7 @@ GameBridge 禁止:
|
||||
- 每个请求必须有超时;H5 的 React Native WebView transport 和 Tauri `invoke` transport 都必须在前端侧按 `timeoutMs` 释放请求,宿主侧执行超时也只能返回标准 HostBridge 错误。重复 `id` 不得重复执行支付、登录、系统分享、文件导入导出、本地通知等宿主副作用;Expo 和 Tauri 壳都必须按 request id 回放首次完成结果。
|
||||
- HostBridge 的 capability profile、文件 MIME 清单、导入 / 导出体积上限、文件名 fallback / 长度上限、request id 长度、角标上限、剪贴板文本长度、二维码文本长度和本地通知标题 / 正文长度都必须以 `packages/shared/src/contracts/hostBridge.ts` 为声明来源;Expo 移动壳直接导入共享 profile 和契约常量,Tauri 壳按 Rust 运行时代码镜像并由配置门禁反查共享契约。
|
||||
- 能力按 `capabilities` / `hostCapabilities` 下发,H5 会过滤未知能力,并根据声明结果决定是否展示入口、发起宿主请求或走 fallback;进入 `native_app` 后主 App 会再通过真实 `host.getRuntime` 回读一次宿主 runtime 并缓存能力,用来补齐裁剪壳或旧入口 URL 缺少 `hostCapabilities` 的场景。不能只凭 `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 / 支付 / 订阅 / 分享桥接行为、Expo managed config、移动端 production bundle、桌面 release 构建入口和微信 / Expo / Tauri 三端生产源码临时替身词扫描不得漂移。
|
||||
- 壳能力声明与三端壳验收必须通过 `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 / API HTTPS 域名格式、Expo managed config、移动端 production bundle、桌面 release 构建入口和微信 / Expo / Tauri 三端生产源码临时替身词扫描不得漂移。
|
||||
- Expo SDK、React Native、`react-native-webview`、Tauri CLI、Tauri Rust crate 和桌面 Cargo 插件版本属于宿主壳行为边界。升级这些依赖前必须同步更新壳配置检查、`package-lock.json` / `Cargo.lock` 解析版本、本文档和对应验证结果,不能只改 package / Cargo 版本让生产壳行为静默漂移。
|
||||
- 登录和支付能力在真实 SDK、渠道流程、后端契约和失败回退全部落地前不得进入 Expo / Tauri capabilities,也不得写进入口 URL `hostCapabilities`;两端配置检查会拒绝 `auth.requestLogin` 和 `payment.request` 的伪声明。
|
||||
- 宿主壳不得把长期 token、支付密钥或用户敏感资料回传给 H5。
|
||||
@@ -453,6 +453,8 @@ GameBridge 禁止:
|
||||
|
||||
2026-06-18 追加:微信壳行为测试进入原生壳统一验收。`npm run check:native-shells` 会运行 `miniprogram/host-bridge/`、`miniprogram/shell/`、`pages/web-view` 样式和 `scripts/miniprogram-web-view-auth.test.ts` 测试,覆盖微信 WebView 入口、登录触发、分享目标、支付结果、订阅消息结果和九宫切图桥接行为;三端桥接层文件结构检查只锁定职责边界,真实行为回归必须由同一门禁中的微信壳测试证明。
|
||||
|
||||
2026-06-19 追加:微信小程序壳路由一致性进入原生壳统一验收。`npm run check:native-shells` 会反查 `miniprogram/app.json.pages` 与 `miniprogram/host-bridge/protocol.js` 页面 URL 常量一致,H5 `src/services/host-bridge/hostBridge.ts` 的小程序登录、支付、九宫切图页面常量、H5 `src/services/wechatMiniProgramSubscribe.ts` 的订阅授权页面常量与微信协议常量一致,`miniprogram/host-bridge/webView.js` 的 WebView 分享入口和分享目标消息类型不漂移;同时会校验 `miniprogram/config.js` 的生产 / 开发 H5 入口与 API base URL 都是显式配置的纯 HTTPS 域名,`WEB_VIEW_SOURCE_QUERY` 只包含 `clientType=mini_program` 和 `clientRuntime=wechat_mini_program`。运行时对开发域名的生产域名回退只作为异常兜底,不作为配置口径;新增小程序页面、改页面路径、调整来源 query 或切换域名格式时,必须同步协议常量、H5 HostBridge / 订阅服务常量、`app.json` 和这条门禁。
|
||||
|
||||
2026-06-18 追加:原生壳本地生成物不作为生产源码门禁输入。Expo `.expo/`、Expo export smoke 临时目录、Tauri `target/`、Tauri schema `gen/` 和 Tauri 自动生成权限目录都必须保持 gitignored;根级生产壳敏感词扫描只检查可提交的壳源码和配置,避免本机工具输出影响生产门禁。手写 capability / 权限配置仍需保留在扫描范围内。
|
||||
|
||||
2026-06-18 追加:移动壳启动页与 Android adaptive icon 复用现有真实品牌图标 `apps/mobile-shell/assets/icon.png`,背景色固定为 H5 壳根背景 `#fffdf9`。该 PNG 是 1024x1024 RGBA 透明前景品牌资产,不新增占位图;Expo `splash` 使用同一图标 `contain` 展示,Android `adaptiveIcon.foregroundImage` 使用同一透明前景图,`check-config.mjs` 会校验图标尺寸、透明像素、启动页和 adaptive icon 配置。
|
||||
@@ -499,7 +501,7 @@ GameBridge 禁止:
|
||||
- AI sandbox 无法调用 HostBridge,也无法读取 H5 登录态。
|
||||
- Tauri release 包不允许任意远端页面调用桌面命令。
|
||||
- Expo WebView 外链离开主站后不保留完整 HostBridge。
|
||||
- 根级验收入口 `npm run check:native-shells` 必须同时覆盖 H5 HostBridge 关键路径、三端桥接层文件结构门禁、Expo 壳 typecheck / test / config smoke / Metro export smoke、Tauri 壳 typecheck / cargo test、桌面 release `--no-bundle` 构建烟测,以及可分发壳与 H5 HostBridge 真实调用链的临时替身词扫描。该扫描范围必须包含微信小程序壳生产 `.js`、共享 HostBridge 契约、H5 native transport,并自动覆盖已接入真实宿主能力 facade 的 H5 生产调用链文件。
|
||||
- 根级验收入口 `npm run check:native-shells` 必须同时覆盖 H5 HostBridge 关键路径、微信小程序页面路由与来源 query 反查、三端桥接层文件结构门禁、Expo 壳 typecheck / test / config smoke / Metro export smoke、Tauri 壳 typecheck / cargo test、桌面 release `--no-bundle` 构建烟测,以及可分发壳与 H5 HostBridge 真实调用链的临时替身词扫描。该扫描范围必须包含微信小程序壳生产 `.js`、共享 HostBridge 契约、H5 native transport,并自动覆盖已接入真实宿主能力 facade 的 H5 生产调用链文件。
|
||||
|
||||
## 参考资料
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ HostBridge 事件名以 `packages/shared/src/contracts/hostBridge.ts` 的 `HOST_
|
||||
2. `authService` 保留原导出,但内部委托 HostBridge,避免一次性改动 AuthGate。
|
||||
3. 分享弹窗、分享目标同步、九宫切图、微信小程序支付和订阅授权改用 HostBridge 通用接口;旧微信命名服务只作为兼容导出。
|
||||
4. 后续新增 `native_app` adapter 时只补桥接实现和测试,业务层不新增平台分叉;主 App 启动会触发一次 `host.getRuntime` 回读并订阅能力变化,避免裁剪壳或旧入口 URL 缺少 `hostCapabilities` 时长期隐藏真实可用能力。
|
||||
5. 每次新增或调整 native capability / HostBridge event 后,必须先更新 `packages/shared/src/contracts/hostBridge.ts` 中对应 Expo / Tauri capability profile 和事件白名单,再运行 `npm run check:native-shells`,统一覆盖 H5 HostBridge 关键测试、三端桥接层文件结构门禁、Expo 壳 typecheck / test / config smoke / Metro export smoke、Tauri 壳 typecheck / cargo test、桌面 release `--no-bundle` 构建烟测,以及可分发壳与 H5 HostBridge 真实调用链的临时替身词扫描;排查单端问题时再单独运行 `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`。
|
||||
5. 每次新增或调整 native capability / HostBridge event 后,必须先更新 `packages/shared/src/contracts/hostBridge.ts` 中对应 Expo / Tauri capability profile 和事件白名单,再运行 `npm run check:native-shells`,统一覆盖 H5 HostBridge 关键测试、三端桥接层文件结构门禁、微信小程序页面路由与 H5 常量反查、Expo 壳 typecheck / test / config smoke / Metro export smoke、Tauri 壳 typecheck / cargo test、桌面 release `--no-bundle` 构建烟测,以及可分发壳与 H5 HostBridge 真实调用链的临时替身词扫描;排查单端问题时再单独运行 `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`。
|
||||
|
||||
## 验收
|
||||
|
||||
@@ -88,7 +88,7 @@ HostBridge 事件名以 `packages/shared/src/contracts/hostBridge.ts` 的 `HOST_
|
||||
- 小程序支付仍跳转 `/pages/wechat-pay/index` 并保留支付结果 hash 回灌确认。
|
||||
- 小程序订阅授权仍跳转 `/pages/subscribe-message/index`,且返回不阻断生成主链路。
|
||||
- 普通浏览器分享、H5 支付和 Native 二维码支付不受影响。
|
||||
- 原生壳统一验收入口 `npm run check:native-shells` 通过,能力白名单、共享 capability profile、HostBridge event 白名单、壳 runtime 回包、URL `hostCapabilities`、H5 fallback、三端桥接层结构、两端壳实现、Expo managed config、移动端 production bundle、桌面 release 构建入口,以及可分发壳与 H5 HostBridge 真实调用链的临时替身词扫描没有漂移;扫描范围包含微信小程序壳生产 `.js`、共享 HostBridge 契约、H5 native transport,并自动覆盖已接入真实宿主能力 facade 的 H5 生产调用链文件。H5 业务文件允许正常表单 `placeholder` 属性和业务占位图文案,但不得出现 mock / fake / stub / TODO / FIXME / 模拟 / 伪造等替身痕迹。
|
||||
- 原生壳统一验收入口 `npm run check:native-shells` 通过,能力白名单、共享 capability profile、HostBridge event 白名单、壳 runtime 回包、URL `hostCapabilities`、H5 fallback、微信小程序 `app.json.pages` 与 `protocol.js` 页面 URL、H5 小程序页面常量、H5 订阅授权页面常量、WebView 分享入口、分享目标消息类型、WebView source query、生产 / 开发 H5 与 API HTTPS 域名格式、三端桥接层结构、两端壳实现、Expo managed config、移动端 production bundle、桌面 release 构建入口,以及可分发壳与 H5 HostBridge 真实调用链的临时替身词扫描没有漂移;扫描范围包含微信小程序壳生产 `.js`、共享 HostBridge 契约、H5 native transport,并自动覆盖已接入真实宿主能力 facade 的 H5 生产调用链文件。H5 业务文件允许正常表单 `placeholder` 属性和业务占位图文案,但不得出现 mock / fake / stub / TODO / FIXME / 模拟 / 伪造等替身痕迹。
|
||||
|
||||
## 后续
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import {spawnSync} from 'node:child_process';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import vm from 'node:vm';
|
||||
|
||||
const npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
||||
const nativeShellPlanPath =
|
||||
@@ -634,6 +635,17 @@ function extractRustStringArray(source, constName) {
|
||||
return [...match[1].matchAll(/"([^"]+)"/g)].map((entry) => entry[1]);
|
||||
}
|
||||
|
||||
function extractStringConst(source, constName) {
|
||||
const match = source.match(
|
||||
new RegExp(`const ${constName}\\s*=\\s*['"]([^'"]+)['"];`),
|
||||
);
|
||||
if (!match) {
|
||||
throw new Error(`unable to read string const ${constName}`);
|
||||
}
|
||||
|
||||
return match[1];
|
||||
}
|
||||
|
||||
function extractDocumentCapabilityList(source, marker) {
|
||||
const markerIndex = source.indexOf(marker);
|
||||
if (markerIndex === -1) {
|
||||
@@ -815,6 +827,141 @@ function assertExternalUrlProtocolParity() {
|
||||
);
|
||||
}
|
||||
|
||||
function requireCommonJsModule(modulePath) {
|
||||
const absolutePath = path.resolve(modulePath);
|
||||
const module = { exports: {} };
|
||||
const sandbox = {
|
||||
module,
|
||||
exports: module.exports,
|
||||
URL,
|
||||
URLSearchParams,
|
||||
};
|
||||
|
||||
vm.runInNewContext(fs.readFileSync(absolutePath, 'utf8'), sandbox, {
|
||||
filename: absolutePath,
|
||||
});
|
||||
|
||||
return module.exports;
|
||||
}
|
||||
|
||||
function pageRouteFromMiniProgramUrl(url) {
|
||||
return String(url).split('?')[0].replace(/^\//, '');
|
||||
}
|
||||
|
||||
function assertHttpsDomain(value, label) {
|
||||
const trimmed = String(value ?? '').trim();
|
||||
let url;
|
||||
try {
|
||||
url = new URL(trimmed);
|
||||
} catch {
|
||||
throw new Error(`${label} must be a valid HTTPS domain`);
|
||||
}
|
||||
|
||||
if (
|
||||
url.protocol !== 'https:' ||
|
||||
url.username ||
|
||||
url.password ||
|
||||
url.port ||
|
||||
url.pathname !== '/' ||
|
||||
url.search ||
|
||||
url.hash ||
|
||||
(trimmed !== url.origin && trimmed !== `${url.origin}/`) ||
|
||||
url.hostname === 'localhost' ||
|
||||
/^\d+\.\d+\.\d+\.\d+$/u.test(url.hostname) ||
|
||||
url.hostname.includes(':') ||
|
||||
!url.hostname.includes('.')
|
||||
) {
|
||||
throw new Error(`${label} must be an HTTPS domain without path or query`);
|
||||
}
|
||||
}
|
||||
|
||||
function assertWechatMiniProgramRouteParity() {
|
||||
const appConfig = JSON.parse(fs.readFileSync('miniprogram/app.json', 'utf8'));
|
||||
const runtimeConfig = requireCommonJsModule('miniprogram/config.js');
|
||||
const protocol = requireCommonJsModule('miniprogram/host-bridge/protocol.js');
|
||||
const webViewBridgeSource = fs.readFileSync(
|
||||
'miniprogram/host-bridge/webView.js',
|
||||
'utf8',
|
||||
);
|
||||
const h5HostBridgeSource = fs.readFileSync(
|
||||
'src/services/host-bridge/hostBridge.ts',
|
||||
'utf8',
|
||||
);
|
||||
const h5SubscribeSource = fs.readFileSync(
|
||||
'src/services/wechatMiniProgramSubscribe.ts',
|
||||
'utf8',
|
||||
);
|
||||
|
||||
assertSameList(
|
||||
appConfig.pages ?? [],
|
||||
[
|
||||
protocol.WECHAT_WEB_VIEW_PAGE_URL,
|
||||
protocol.WECHAT_SHARE_GRID_PAGE_URL,
|
||||
protocol.WECHAT_PAY_PAGE_URL,
|
||||
protocol.WECHAT_SUBSCRIBE_MESSAGE_PAGE_URL,
|
||||
].map(pageRouteFromMiniProgramUrl),
|
||||
'wechat mini program app pages',
|
||||
);
|
||||
|
||||
const h5RoutePairs = [
|
||||
['MINI_PROGRAM_AUTH_PAGE_URL', protocol.WECHAT_AUTH_PAGE_URL],
|
||||
['MINI_PROGRAM_PAY_PAGE_URL', protocol.WECHAT_PAY_PAGE_URL],
|
||||
['MINI_PROGRAM_SHARE_GRID_PAGE_URL', protocol.WECHAT_SHARE_GRID_PAGE_URL],
|
||||
];
|
||||
for (const [constName, expectedValue] of h5RoutePairs) {
|
||||
const actualValue = extractStringConst(h5HostBridgeSource, constName);
|
||||
if (actualValue !== expectedValue) {
|
||||
throw new Error(
|
||||
`H5 HostBridge ${constName} drifted: expected ${expectedValue} but got ${actualValue}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const h5SubscribePageUrl = extractStringConst(
|
||||
h5SubscribeSource,
|
||||
'MINI_PROGRAM_SUBSCRIBE_MESSAGE_PAGE_URL',
|
||||
);
|
||||
if (h5SubscribePageUrl !== protocol.WECHAT_SUBSCRIBE_MESSAGE_PAGE_URL) {
|
||||
throw new Error(
|
||||
`H5 subscribe page URL drifted: expected ${protocol.WECHAT_SUBSCRIBE_MESSAGE_PAGE_URL} but got ${h5SubscribePageUrl}`,
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
extractStringConst(webViewBridgeSource, 'WEB_VIEW_SHARE_PATH') !==
|
||||
protocol.WECHAT_WEB_VIEW_PAGE_URL
|
||||
) {
|
||||
throw new Error('wechat mini program share path must use the web-view page route');
|
||||
}
|
||||
|
||||
if (
|
||||
extractStringConst(webViewBridgeSource, 'SHARE_TARGET_MESSAGE_TYPE') !==
|
||||
protocol.WECHAT_SHARE_TARGET_MESSAGE_TYPE
|
||||
) {
|
||||
throw new Error('wechat mini program share target message type drifted');
|
||||
}
|
||||
|
||||
for (const [configKey, label] of [
|
||||
['WEB_VIEW_ENTRY_URL', 'wechat release web-view entry URL'],
|
||||
['DEV_WEB_VIEW_ENTRY_URL', 'wechat dev web-view entry URL'],
|
||||
['API_BASE_URL', 'wechat release API base URL'],
|
||||
['DEV_API_BASE_URL', 'wechat dev API base URL'],
|
||||
]) {
|
||||
assertHttpsDomain(runtimeConfig[configKey], label);
|
||||
}
|
||||
|
||||
const sourceQuery = runtimeConfig.WEB_VIEW_SOURCE_QUERY ?? {};
|
||||
if (
|
||||
sourceQuery.clientType !== 'mini_program' ||
|
||||
sourceQuery.clientRuntime !== 'wechat_mini_program' ||
|
||||
Object.keys(sourceQuery).some(
|
||||
(key) => key !== 'clientType' && key !== 'clientRuntime',
|
||||
)
|
||||
) {
|
||||
throw new Error('wechat web-view source query drifted from HostBridge runtime markers');
|
||||
}
|
||||
}
|
||||
|
||||
function assertHostBridgeLayerLayout() {
|
||||
const wechatBridgeFiles = fs
|
||||
.readdirSync('miniprogram/host-bridge', { withFileTypes: true })
|
||||
@@ -980,6 +1127,9 @@ assertNativeShellCapabilityPlan();
|
||||
console.log('[check:native-shells] external-url-protocol-parity');
|
||||
assertExternalUrlProtocolParity();
|
||||
|
||||
console.log('[check:native-shells] wechat-mini-program-route-parity');
|
||||
assertWechatMiniProgramRouteParity();
|
||||
|
||||
console.log('[check:native-shells] production-shell-dev-scaffold-scan');
|
||||
assertNoProductionShellDevScaffoldTerms();
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
} from './host-bridge/hostBridge';
|
||||
|
||||
const SUBSCRIBE_RESULT_HASH_KEY = 'wx_subscribe_result';
|
||||
const MINI_PROGRAM_SUBSCRIBE_MESSAGE_PAGE_URL = '/pages/subscribe-message/index';
|
||||
const SUBSCRIBE_RESULT_TIMEOUT_MS = 2_500;
|
||||
const SUBSCRIBE_RESULT_RETURN_FALLBACK_MS = 800;
|
||||
|
||||
@@ -110,7 +111,7 @@ export async function requestGenerationResultSubscribePermission() {
|
||||
let resultPromise: Promise<string | null> | null = null;
|
||||
try {
|
||||
const navigated = await navigateHostNativePage(
|
||||
`/pages/subscribe-message/index?requestId=${encodeURIComponent(requestId)}&scene=generation-result`,
|
||||
`${MINI_PROGRAM_SUBSCRIBE_MESSAGE_PAGE_URL}?requestId=${encodeURIComponent(requestId)}&scene=generation-result`,
|
||||
{
|
||||
errorMessage: 'wechat_js_sdk_unavailable',
|
||||
beforeNavigate() {
|
||||
|
||||
Reference in New Issue
Block a user