@heybox/hb-sdk 0.8.0-alpha.3 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +136 -422
- package/README.md +28 -18
- package/dist/cli-chunks/{build-qWzAbpS7.cjs → build-PYCNacya.cjs} +8 -5
- package/dist/cli-chunks/{context-CtS2Thp0.cjs → context-m2W2XbL0.cjs} +42 -59
- package/dist/cli-chunks/{create-PV5ua977.cjs → create-BdAg3WGA.cjs} +2 -2
- package/dist/cli-chunks/{dev-CwKbAm_H.cjs → dev-CyZuw7Yn.cjs} +76 -64
- package/dist/cli-chunks/{doctor-tJUGOYrm.cjs → doctor-DU8rCfUF.cjs} +1 -1
- package/dist/cli-chunks/{index-DVuD75Hr.cjs → index-DATObqzK.cjs} +2 -2
- package/dist/cli-chunks/{index-De687C6-.cjs → index-MMW2ibQm.cjs} +32 -21
- package/dist/cli-chunks/{index.esm-B-4yrLNm.cjs → index.esm-BiAaAUFC.cjs} +8 -8
- package/dist/cli-chunks/{login-DolpqD8K.cjs → login-B3TThMss.cjs} +2 -2
- package/dist/cli-chunks/{project-vite-1rvkK-M8.cjs → project-vite-BQj8YLI4.cjs} +1 -1
- package/dist/cli-chunks/{remote-rIAQE_G2.cjs → remote-DNvI7tHH.cjs} +57 -25
- package/dist/cli-chunks/{runtime-gate-DFjw66kF.cjs → runtime-gate-BEFp1w_s.cjs} +11 -3
- package/dist/cli-chunks/{runtime-permission-env-CjsCe5bp.cjs → runtime-permission-env-CtL8rsjB.cjs} +351 -0
- package/dist/cli-chunks/{session-CzaM2Cq3.cjs → session-DjBkjaF8.cjs} +1 -1
- package/dist/cli-chunks/{skill-CM40_9WH.cjs → skill-cR_wnaw2.cjs} +2 -2
- package/dist/cli-chunks/{version-Bz-AfXQU.cjs → version-yEn1E2Bg.cjs} +1 -1
- package/dist/cli.cjs +1 -1
- package/dist/devtools/browser-dev-host/assets/browser-dev-host-TzYf9L6C.js +99 -0
- package/dist/devtools/browser-dev-host/assets/{index-CeP6SLB3.js → index-C5MZZDa5.js} +4 -4
- package/dist/devtools/browser-dev-host/assets/index-P-ra4m1y.css +1 -0
- package/dist/devtools/browser-dev-host/assets/workbench-state-BwV7bm4n.js +5 -0
- package/dist/devtools/browser-dev-host/index.html +3 -3
- package/dist/index.cjs.js +100 -2
- package/dist/index.esm.js +100 -2
- package/dist/protocol.cjs.js +189 -47
- package/dist/protocol.esm.js +189 -47
- package/dist/templates/{vue3-vite-ts → vanilla-vite-js}/.gitignore.ejs +0 -1
- package/dist/templates/vanilla-vite-js/README.md.ejs +14 -0
- package/dist/templates/vanilla-vite-js/index.html.ejs +20 -0
- package/dist/templates/vanilla-vite-js/package.json.ejs +22 -0
- package/dist/templates/vanilla-vite-js/src/assets/heybox-logo.svg +8 -0
- package/dist/templates/vanilla-vite-js/src/main.js +39 -0
- package/dist/templates/vanilla-vite-js/src/styles.css +155 -0
- package/dist/templates/{vue3-vite-ts/vite.config.ts → vanilla-vite-js/vite.config.js} +1 -2
- package/dist/vite.cjs.js +281 -13
- package/dist/vite.esm.js +281 -13
- package/package.json +13 -9
- package/skill/SKILL.md +8 -9
- package/skill/references/api-protocol.md +1 -3
- package/skill/references/api-root.md +65 -21
- package/skill/references/cli.md +17 -5
- package/skill/references/examples.md +3 -3
- package/skill/references/recipes.md +40 -41
- package/skill/references/safety-boundaries.md +9 -5
- package/skill/skill.json +5 -5
- package/types/core/mini-dev-console.d.ts +36 -0
- package/types/miniapp-manifest/index.d.ts +1 -0
- package/types/miniapp-manifest/node.d.ts +3 -0
- package/types/miniapp-manifest/permissions.d.ts +37 -0
- package/types/miniapp-manifest/schema.d.ts +5 -0
- package/types/modules/share/index.d.ts +1 -1
- package/types/modules/share/show-share-menu.d.ts +1 -1
- package/types/modules/share/types.d.ts +2 -4
- package/types/vite/index.d.ts +3 -1
- package/dist/devtools/browser-dev-host/assets/browser-dev-host-uq-Wac6k.js +0 -97
- package/dist/devtools/browser-dev-host/assets/index-KD2f3Jdz.css +0 -1
- package/dist/devtools/browser-dev-host/assets/workbench-state-D-1U0JRq.js +0 -5
- package/dist/templates/vue3-vite-ts/README.md.ejs +0 -47
- package/dist/templates/vue3-vite-ts/index.html.ejs +0 -12
- package/dist/templates/vue3-vite-ts/package.json.ejs +0 -33
- package/dist/templates/vue3-vite-ts/src/App.vue +0 -78
- package/dist/templates/vue3-vite-ts/src/__tests__/App.spec.ts +0 -148
- package/dist/templates/vue3-vite-ts/src/auth-handoff.ts +0 -46
- package/dist/templates/vue3-vite-ts/src/main.ts +0 -5
- package/dist/templates/vue3-vite-ts/src/styles.css +0 -60
- package/dist/templates/vue3-vite-ts/src/vite-env.d.ts +0 -1
- package/dist/templates/vue3-vite-ts/tsconfig.app.json +0 -17
- package/dist/templates/vue3-vite-ts/tsconfig.json +0 -11
- package/dist/templates/vue3-vite-ts/tsconfig.node.json +0 -11
- package/dist/templates/vue3-vite-ts/vitest.config.ts +0 -10
package/README.md
CHANGED
|
@@ -21,6 +21,8 @@ npm run dev
|
|
|
21
21
|
|
|
22
22
|
脚手架已包含 SDK、Vite 配置和常用开发命令。浏览器调试通过后,再从调试台的二维码入口使用手机小黑盒 APP 验收。
|
|
23
23
|
|
|
24
|
+
当前 `0.8` 已发布为稳定版本,创建命令使用 `@latest`。
|
|
25
|
+
|
|
24
26
|
### 接入已有项目
|
|
25
27
|
|
|
26
28
|
```bash
|
|
@@ -45,7 +47,9 @@ hb-sdk skill install --all-agents
|
|
|
45
47
|
|
|
46
48
|
使用 `hb-sdk doctor` 或 `hb-sdk doctor --json` 检查各 Agent 的 Skill 是否与当前 npm 包匹配;已有内容被修改时,使用 `--force` 才会覆盖。
|
|
47
49
|
|
|
48
|
-
SDK 会在导入时启动握手,能力调用会自动等待 SDK
|
|
50
|
+
SDK 会在导入时启动握手,能力调用会自动等待 SDK 就绪。声明 `userInfo` 后,可通过 `user.getInfo()` 读取当前小程序内的隔离身份和已授权资料:
|
|
51
|
+
|
|
52
|
+
使用受保护能力前,需要在项目根 `package.json#heybox.permissions` 中显式声明。`auth.login()` 和 `user.getInfo()` 都只需要 `userInfo`,不依赖公开 `network` 权限;`network` 仅约束 `network.request()` 与 `network.download()`,并且声明后仍需平台批准。完整配置、无需声明白名单和 0.8/0.9 迁移规则见[权限声明](https://docs.xiaoheihe.cn/hb_sdk/guide/permissions)。
|
|
49
53
|
|
|
50
54
|
需要可信用户手势的按钮不能在点击回调里等待握手。页面加载时先用 `getHandshakeState()` 读取当前状态,再用 `onHandshakeStateChange()` 订阅并立即接收当前状态;仅在 `status === 'ready'` 时启用按钮,并在组件或页面销毁时调用订阅返回的取消函数。`ready` 生命周期事件只是握手成功瞬间派发、不会重放的边沿通知,不能用来判断当前状态。
|
|
51
55
|
|
|
@@ -60,7 +64,7 @@ async function getCurrentUser() {
|
|
|
60
64
|
|
|
61
65
|
`user.getInfo()` 不展示授权弹窗,也不要求可信用户手势。未登录时返回 `isHeyboxAppLoggedIn: false`;已登录时隐式授予当前本地用户 scopes(`identity`、`profile`),返回当前小程序隔离的 `app_user_id` 以及 `profile.nickname`、`profile.avatar`。
|
|
62
66
|
|
|
63
|
-
|
|
67
|
+
声明 `userInfo` 后,小程序可在按钮点击等明确的用户操作中调用 `auth.login()`:
|
|
64
68
|
|
|
65
69
|
```ts
|
|
66
70
|
async function loginFromUserAction() {
|
|
@@ -74,7 +78,7 @@ async function loginFromUserAction() {
|
|
|
74
78
|
}
|
|
75
79
|
```
|
|
76
80
|
|
|
77
|
-
`auth.login()` 返回 `{ code, expiresIn: 300, scopes }`,其中 `identity` 隐式强制包含;它不返回 user、profile、`app_user_id` 或 token。页面只能把 code 交给开发者服务端,再由服务端按后端 OpenAPI 文档换取 token
|
|
81
|
+
`auth.login()` 返回 `{ code, expiresIn: 300, scopes }`,其中 `identity` 隐式强制包含;它不返回 user、profile、`app_user_id` 或 token。页面只能把 code 交给开发者服务端,再由服务端按后端 OpenAPI 文档换取 token。它内部使用 Host 受信请求,不依赖公开 `network` 权限;示例中的 `network.request()` 本身仍需声明并获得 `network` 平台批准。完整边界见[用户身份与登录](https://docs.xiaoheihe.cn/hb_sdk/guide/auth)。
|
|
78
82
|
|
|
79
83
|
用户可以在明确的按钮点击和二次确认后撤销当前小程序的授权:
|
|
80
84
|
|
|
@@ -88,7 +92,7 @@ async function revokeAuthorizationFromUserAction() {
|
|
|
88
92
|
}
|
|
89
93
|
```
|
|
90
94
|
|
|
91
|
-
`user.revokeAuthorization()`
|
|
95
|
+
`user.revokeAuthorization()` 位于无需声明白名单,不接收小程序 ID、黑盒用户 ID 或手势参数。调用必须来自可信用户操作,否则返回 `USER_GESTURE_REQUIRED`。撤销成功后,后续身份或授权读取会重新进入授权流程;SDK 不会替业务清理已经持久化的数据。
|
|
92
96
|
|
|
93
97
|
已有 Vite 项目还需要启用构建插件:
|
|
94
98
|
|
|
@@ -132,14 +136,14 @@ const downloaded = await hbSDK.network.download({
|
|
|
132
136
|
console.log(await downloaded.readText());
|
|
133
137
|
```
|
|
134
138
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
Runtime 不认识新 method 时返回 `METHOD_NOT_FOUND`,且不提供 Blob 或内存假实现。
|
|
139
|
+
新 PC 已开放 `files` sandbox、单文件/目录 picker 与公网 `network.download()`;其它 Host 尚未开放
|
|
140
|
+
对应 primitive 时返回 `METHOD_FORBIDDEN`,旧 Runtime 不认识新 method 时返回 `METHOD_NOT_FOUND`。
|
|
141
|
+
新 PC 的下载字节留在插件 Node 并直接原子落盘,不提供 Blob 或内存假实现。
|
|
139
142
|
|
|
140
|
-
外部 picker 必须从可信用户操作触发。`files.saveFile({ suggestedName })`
|
|
141
|
-
|
|
142
|
-
|
|
143
|
+
外部 picker 必须从可信用户操作触发。`files.saveFile({ suggestedName })` 返回精确 File 授权,
|
|
144
|
+
不会额外保留 Directory 授权;新 PC 当前先让用户选择保存目录,再以 `suggestedName` 独占创建空文件,
|
|
145
|
+
同名文件返回 `FILE_ALREADY_EXISTS`。V1 不接受 `accept`,只有 `pickFiles()` 支持扩展名过滤。完整路径、
|
|
146
|
+
创建、存在性和下载语义见[快速开始](https://docs.xiaoheihe.cn/hb_sdk/guide/quick-start#文件与下载)。
|
|
143
147
|
|
|
144
148
|
`remove()` 只允许删除 sandbox 对象;外部 picker、`saveFile()` 及其派生项即使是 `readwrite` 也返回 `FILE_ACCESS_DENIED`。单个 Runtime session 最多保留 1024 个外部 direct handle,超限返回 `FILE_QUOTA_EXCEEDED`。Host 文件错误只向 SDK 暴露稳定 code 与 canonical 安全文案,业务不要依赖平台原始 message 或附加 data。
|
|
145
149
|
|
|
@@ -188,16 +192,16 @@ SDK 还提供 `getHandshakeState()` / `onHandshakeStateChange()` 管理持久握
|
|
|
188
192
|
- `ready` 生命周期事件是不可重放的边沿通知,不代表可查询状态,也不能替代握手状态 API。
|
|
189
193
|
- `auth.login({ scopes? })` 只返回 `{ code, expiresIn: 300, scopes }`;identity 隐式强制包含,code 只能提交给开发者服务端。
|
|
190
194
|
- 需要授权 UI 时,`auth.login()` 必须来自可信用户手势,否则返回 `USER_GESTURE_REQUIRED`;用户取消或关闭时返回 `AUTHORIZATION_CANCELLED`。已授权时可以静默返回新 code。
|
|
191
|
-
- `user.
|
|
192
|
-
- 已开通网络权限时,公开的 `user.getInfo()` 返回 `SERVER_API_REQUIRED`;对应身份和资料数据应由开发者服务端通过 OpenAPI 获取。
|
|
193
|
-
- 未开通网络权限时,`user.getInfo()` 隐式授予 `identity` 与 `profile`,静默返回 `userInfo.app_user_id`、昵称和头像;这条路径仍不能获取 code 或调用 OpenAPI。
|
|
195
|
+
- `auth.login()`、`user.getInfo()` 和 `user.getSteamGameList()` 的可用性不由 `network` 决定;它们分别要求 `userInfo` 或 `steamLibrary`,并继续遵循各自的用户授权和数据规则。
|
|
194
196
|
- `user.revokeAuthorization()` 在两种网络模式下都要求可信用户手势;成功后必须停止使用并清理业务缓存的旧身份、资料和服务端会话。
|
|
195
197
|
- `on()` 返回取消监听函数,组件卸载或页面销毁时需要调用。
|
|
196
198
|
- 业务网络请求使用 `network.request()`,不要依赖浏览器原生网络出口。
|
|
197
199
|
- 公开 `network.request()` 与 `network.download()` 均使用 no-follow redirect policy;3xx 不会在 Host 内静默跳转。
|
|
198
200
|
- `network.request()` 不能访问平台保留的 runtime auth 与 OpenAPI 内部路径;页面不得持有或交换服务端应用凭据。
|
|
199
201
|
- `network.request` 不支持 `multipart/form-data`;App Host 仅支持 form(`application/x-www-form-urlencoded`)与 JSON。表单请用 `URLSearchParams#toString()` 作为 `data`,文件上传请走专用上传能力。
|
|
200
|
-
-
|
|
202
|
+
- 新 PC 开放持久 sandbox、单文件/目录 picker、精确 File 保存与公网 `network.download()`;多选和小黑盒域名流式下载暂不支持。Mobile、Web 与 Browser Dev Host 仍未开放这些 primitive。
|
|
203
|
+
- 新 PC 支持 `network.request`,但会忽略 `withCredentials`;它不改变请求路由或凭据行为。其他平台继续遵循各自的 Host 语义。
|
|
204
|
+
- 新 PC 不支持振动、分享菜单和截图分享;`navigation.openAppPage` 仅支持 `game_detail`,用户/帖子详情会明确失败。调用平台相关能力时应处理 `METHOD_FORBIDDEN` 或 `REQUEST_UNSUPPORTED`。
|
|
201
205
|
- picker 必须来自可信用户手势;取消选择返回 `FILE_PICKER_CANCELLED`。`saveFile()` 只接受 `suggestedName`,不接受 `accept`。
|
|
202
206
|
- `pickFiles()` / `pickDirectory()` 默认只读;`pickFiles()` 仅在显式传入 `multiple: true` 时允许 Host 返回多个文件。要写入或作为下载目标时显式请求 `mode: 'readwrite'`。
|
|
203
207
|
- `file()` / `directory()` 只接受以 `/` 分隔的规范相对路径;拒绝绝对路径、空 segment、`.`、`..`、反斜杠、尾分隔符和控制字符。
|
|
@@ -207,12 +211,16 @@ SDK 还提供 `getHandshakeState()` / `onHandshakeStateChange()` 管理持久握
|
|
|
207
211
|
- 单个 Runtime session 最多保留 1024 个 picker/saveFile direct handle;整批授权超限时返回 `FILE_QUOTA_EXCEEDED`,不会保留部分结果。
|
|
208
212
|
- Host 文件错误只保留稳定 code 与 canonical 安全文案;原始 message、data、native path 和 credential 不会进入小程序。
|
|
209
213
|
- `FileStat.size` 是非负 safe integer;`modifiedAt` / `createdAt` 若存在,单位为 Unix epoch milliseconds。
|
|
210
|
-
-
|
|
214
|
+
- 仅当当前版本声明且平台已批准 `network` 时,`hb-sdk dev` 与 `hb-sdk remote deploy` 构建才会跳过平台 CSP;其他构建继续注入平台 CSP。`useOfficialDomain` 不参与 CSP 跳过判定。
|
|
211
215
|
- 构建必须启用 `miniappManifest({ platforms })` 并显式声明实际承诺适配的平台,推荐统一使用 `hb-sdk build`。新项目模板中的 `['android', 'ios', 'ohos']` 只是初始配置,不代表已经完成真机验收。
|
|
212
216
|
|
|
217
|
+
私有 Page Channel 适配没有修改 `@heybox/hb-sdk` 公开 API 或 iframe wire;本次 files/download
|
|
218
|
+
Runtime 能力随 `0.8.0-alpha.11` release family 发布,部署 detail 前必须保证
|
|
219
|
+
`@heybox/hb-sdk`、`@heybox/hb-sdk-runtime` 与 `@heybox/hb-sdk-protocol` 三个 package 均可解析。
|
|
220
|
+
|
|
213
221
|
## 分享扩展数据
|
|
214
222
|
|
|
215
|
-
|
|
223
|
+
`share.showShareMenu()` 的落地页固定为当前小程序的 `common_share`,不接受自定义 `url`。分享和复制链接可以携带小程序自行定义的页面状态:
|
|
216
224
|
|
|
217
225
|
```ts
|
|
218
226
|
await hbSDK.share.showShareMenu({
|
|
@@ -270,7 +278,9 @@ hb-sdk remote deploy --release-note "本次更新说明"
|
|
|
270
278
|
| `hb-sdk build` | 执行生产构建与产物校验,不包含类型检查 |
|
|
271
279
|
| `hb-sdk remote deploy` | 构建、上传并提交当前版本审核 |
|
|
272
280
|
|
|
273
|
-
`hb-sdk
|
|
281
|
+
`hb-sdk create` 默认生成可直接删除的 Vanilla JavaScript + Vite HelloWorld,只保留 SDK Toast 示例、三端 Manifest 和开发/构建/部署命令,不绑定业务框架或项目内测试工具。
|
|
282
|
+
|
|
283
|
+
`hb-sdk dev` 会启动 Vue 3 Browser Dev Host。左侧诊断面板集中展示日志、当前小程序作用域内的 Storage 快照和问题,右侧设备预览可在 iPhone 16 Pro Max(`440 x 956`)与 Pixel 9 Pro(`410 x 914`)间切换。切换预设只改变设备外框和 viewport,不会重新加载 iframe 或重启 Runtime;授权与操作弹窗、Toast、Loading 和振动反馈都限制在预览框内。
|
|
274
284
|
|
|
275
285
|
浏览器调试入口不要求 CLI 登录或项目绑定;需要账号或远端配置的能力会按当前可用上下文返回结果或明确失败。调试台不提供权限修改入口,远端权限快照始终是 Runtime 的规范输入。手机验收统一从右上角二维码入口进入,选择手机可达的局域网网卡后扫码;二维码使用 `open_inapp` 与 `heybox://` `openWindow` 包装的短链。完整操作见 [CLI 指南](https://docs.xiaoheihe.cn/hb_sdk/guide/cli),实现与安全边界由 [Runtime Host 迁移](./docs/runtime-host-migration.md) 维护。
|
|
276
286
|
|
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
var fs$1 = require('node:fs');
|
|
4
4
|
var fs = require('node:fs/promises');
|
|
5
5
|
var path = require('node:path');
|
|
6
|
-
var runtimePermissionEnv = require('./runtime-permission-env-
|
|
7
|
-
var runtimeGate = require('./runtime-gate-
|
|
8
|
-
var index = require('./index-
|
|
9
|
-
var projectVite = require('./project-vite-
|
|
6
|
+
var runtimePermissionEnv = require('./runtime-permission-env-CtL8rsjB.cjs');
|
|
7
|
+
var runtimeGate = require('./runtime-gate-BEFp1w_s.cjs');
|
|
8
|
+
var index = require('./index-MMW2ibQm.cjs');
|
|
9
|
+
var projectVite = require('./project-vite-BQj8YLI4.cjs');
|
|
10
|
+
require('./version-yEn1E2Bg.cjs');
|
|
10
11
|
require('node:async_hooks');
|
|
11
12
|
require('node:module');
|
|
12
13
|
require('path');
|
|
@@ -27,6 +28,7 @@ async function runBuildCommand(_options = {}, runtime = {}) {
|
|
|
27
28
|
const env = runtime.env ?? process.env;
|
|
28
29
|
const mode = env.HB_SDK_ENV?.trim() || undefined;
|
|
29
30
|
const skipPlatformCsp = runtimePermissionEnv.shouldSkipMiniappPlatformCspFromEnv(env);
|
|
31
|
+
runtimePermissionEnv.readMiniappPermissionsFromPackageJson(projectRoot, runtimePermissionEnv.resolveMiniappSdkVersion());
|
|
30
32
|
await fs.rm(distDir, { force: true, recursive: true });
|
|
31
33
|
try {
|
|
32
34
|
return await runtimePermissionEnv.withMiniappPlatformCspEnv(skipPlatformCsp, async () => {
|
|
@@ -91,7 +93,7 @@ async function validateBuildOutput(projectRoot, logger) {
|
|
|
91
93
|
if (hadBom) {
|
|
92
94
|
logger.warn('dist/manifest.json 包含 BOM,已自动剥离');
|
|
93
95
|
}
|
|
94
|
-
const { platforms, sdkVersion, version } = runtimeGate.validateMiniappManifestForDeploy(manifest);
|
|
96
|
+
const { platforms, sdkVersion, version, permissions } = runtimeGate.validateMiniappManifestForDeploy(manifest);
|
|
95
97
|
const allFiles = await walkDistFiles(distDir);
|
|
96
98
|
const uploadFiles = allFiles.filter((file) => runtimePermissionEnv.shouldUploadDistFile(file.relativePath));
|
|
97
99
|
const sizeError = runtimePermissionEnv.validateUploadTotalSize(uploadFiles);
|
|
@@ -103,6 +105,7 @@ async function validateBuildOutput(projectRoot, logger) {
|
|
|
103
105
|
fileCount: uploadFiles.length,
|
|
104
106
|
sdkVersion,
|
|
105
107
|
platforms,
|
|
108
|
+
...(permissions === undefined ? {} : { permissions }),
|
|
106
109
|
totalBytes: uploadFiles.reduce((total, file) => total + file.size, 0),
|
|
107
110
|
version,
|
|
108
111
|
};
|
|
@@ -1,53 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
var
|
|
3
|
+
var runtimePermissionEnv = require('./runtime-permission-env-CtL8rsjB.cjs');
|
|
4
|
+
var session = require('./session-DjBkjaF8.cjs');
|
|
5
5
|
var fs$1 = require('node:fs');
|
|
6
6
|
var fs = require('node:fs/promises');
|
|
7
7
|
var path = require('node:path');
|
|
8
8
|
|
|
9
|
-
const MANAGED_RUNTIME_PERMISSION_KEYS = new Set(['network.request']);
|
|
10
|
-
function isManagedMiniProgramRuntimePermissionKey(key) {
|
|
11
|
-
return MANAGED_RUNTIME_PERMISSION_KEYS.has(key);
|
|
12
|
-
}
|
|
13
|
-
function parseMiniProgramRuntimePermissions(snapshot) {
|
|
14
|
-
if (!isRecord(snapshot) || snapshot.schema_version !== 1 || !Array.isArray(snapshot.entries)) {
|
|
15
|
-
return { valid: false, permissions: {} };
|
|
16
|
-
}
|
|
17
|
-
if (snapshot.revision !== undefined && (typeof snapshot.revision !== 'number' || !Number.isInteger(snapshot.revision) || snapshot.revision < 0)) {
|
|
18
|
-
return { valid: false, permissions: {} };
|
|
19
|
-
}
|
|
20
|
-
const seenKeys = new Set();
|
|
21
|
-
const permissions = {};
|
|
22
|
-
for (const rawEntry of snapshot.entries) {
|
|
23
|
-
if (!isRecord(rawEntry) || typeof rawEntry.key !== 'string' || !rawEntry.key.trim()) {
|
|
24
|
-
return { valid: false, permissions: {} };
|
|
25
|
-
}
|
|
26
|
-
const key = rawEntry.key.trim();
|
|
27
|
-
if (seenKeys.has(key) || (rawEntry.status !== 'enabled' && rawEntry.status !== 'disabled') || !isRecord(rawEntry.config)) {
|
|
28
|
-
return { valid: false, permissions: {} };
|
|
29
|
-
}
|
|
30
|
-
seenKeys.add(key);
|
|
31
|
-
if (!isManagedMiniProgramRuntimePermissionKey(key))
|
|
32
|
-
continue;
|
|
33
|
-
if (key === 'network.request' && typeof rawEntry.config.useOfficialDomain !== 'boolean') {
|
|
34
|
-
return { valid: false, permissions: {} };
|
|
35
|
-
}
|
|
36
|
-
permissions[key] = {
|
|
37
|
-
key,
|
|
38
|
-
status: rawEntry.status,
|
|
39
|
-
config: { useOfficialDomain: rawEntry.config.useOfficialDomain },
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
return { valid: true, permissions };
|
|
43
|
-
}
|
|
44
|
-
function isRecord(value) {
|
|
45
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
9
|
function shouldSkipMiniappPlatformCsp(runtimePermissions) {
|
|
49
|
-
const parsed = parseMiniProgramRuntimePermissions(runtimePermissions);
|
|
50
|
-
const networkRequest = parsed.permissions
|
|
10
|
+
const parsed = runtimePermissionEnv.parseMiniProgramRuntimePermissions(runtimePermissions);
|
|
11
|
+
const networkRequest = parsed.permissions.network;
|
|
51
12
|
return parsed.valid && networkRequest?.status === 'enabled';
|
|
52
13
|
}
|
|
53
14
|
const DEFAULT_PERMISSION_STATE = {
|
|
@@ -57,8 +18,8 @@ const DEFAULT_PERMISSION_STATE = {
|
|
|
57
18
|
},
|
|
58
19
|
};
|
|
59
20
|
function createBrowserDevPermissionState(context) {
|
|
60
|
-
const parsed = parseMiniProgramRuntimePermissions(context.runtimePermissions);
|
|
61
|
-
const networkRequest = parsed.permissions
|
|
21
|
+
const parsed = runtimePermissionEnv.parseMiniProgramRuntimePermissions(context.runtimePermissions);
|
|
22
|
+
const networkRequest = parsed.permissions.network;
|
|
62
23
|
return parsed.valid && networkRequest
|
|
63
24
|
? {
|
|
64
25
|
networkRequest: {
|
|
@@ -68,25 +29,48 @@ function createBrowserDevPermissionState(context) {
|
|
|
68
29
|
}
|
|
69
30
|
: cloneDefaultPermissionState();
|
|
70
31
|
}
|
|
71
|
-
function createBrowserDevRuntimePermissions(state) {
|
|
72
|
-
return
|
|
73
|
-
schema_version: 1,
|
|
74
|
-
entries: [
|
|
75
|
-
{
|
|
76
|
-
key: 'network.request',
|
|
77
|
-
status: state.networkRequest.status,
|
|
78
|
-
config: {
|
|
79
|
-
useOfficialDomain: state.networkRequest.useOfficialDomain,
|
|
80
|
-
},
|
|
81
|
-
},
|
|
82
|
-
],
|
|
83
|
-
};
|
|
32
|
+
function createBrowserDevRuntimePermissions(snapshot, state) {
|
|
33
|
+
return createDevRuntimePermissions(snapshot, state, state.networkRequest.useOfficialDomain);
|
|
84
34
|
}
|
|
85
35
|
function cloneDefaultPermissionState() {
|
|
86
36
|
return {
|
|
87
37
|
networkRequest: { ...DEFAULT_PERMISSION_STATE.networkRequest },
|
|
88
38
|
};
|
|
89
39
|
}
|
|
40
|
+
function createDevRuntimePermissions(snapshot, state, useOfficialDomain) {
|
|
41
|
+
const parsed = runtimePermissionEnv.parseMiniProgramRuntimePermissions(snapshot);
|
|
42
|
+
if (!parsed.valid) {
|
|
43
|
+
return { schema_version: 1, entries: [] };
|
|
44
|
+
}
|
|
45
|
+
const entries = [];
|
|
46
|
+
for (const key of runtimePermissionEnv.MINI_PROGRAM_PERMISSION_KEYS) {
|
|
47
|
+
const entry = parsed.permissions[key];
|
|
48
|
+
if (!entry)
|
|
49
|
+
continue;
|
|
50
|
+
entries.push(key === 'network'
|
|
51
|
+
? {
|
|
52
|
+
key,
|
|
53
|
+
status: state.networkRequest.status,
|
|
54
|
+
config: { useOfficialDomain },
|
|
55
|
+
}
|
|
56
|
+
: {
|
|
57
|
+
key,
|
|
58
|
+
status: entry.status,
|
|
59
|
+
config: {},
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
const revision = isPlainRecord(snapshot) && typeof snapshot.revision === 'number' && Number.isInteger(snapshot.revision) && snapshot.revision >= 0
|
|
63
|
+
? snapshot.revision
|
|
64
|
+
: undefined;
|
|
65
|
+
return {
|
|
66
|
+
schema_version: 1,
|
|
67
|
+
...(revision === undefined ? {} : { revision }),
|
|
68
|
+
entries,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function isPlainRecord(value) {
|
|
72
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
|
|
73
|
+
}
|
|
90
74
|
|
|
91
75
|
function createRemoteApiClient(options) {
|
|
92
76
|
return {
|
|
@@ -401,7 +385,6 @@ exports.createBrowserDevRuntimePermissions = createBrowserDevRuntimePermissions;
|
|
|
401
385
|
exports.createLocalDevLaunchToken = createLocalDevLaunchToken;
|
|
402
386
|
exports.createRemoteApiClientForEnvironment = createRemoteApiClientForEnvironment;
|
|
403
387
|
exports.getBoundMiniProgram = getBoundMiniProgram;
|
|
404
|
-
exports.parseMiniProgramRuntimePermissions = parseMiniProgramRuntimePermissions;
|
|
405
388
|
exports.readBoundMiniProgramId = readBoundMiniProgramId;
|
|
406
389
|
exports.readBoundMiniProgramIdFromPackageJson = readBoundMiniProgramIdFromPackageJson;
|
|
407
390
|
exports.readMiniProgramProjectPackage = readMiniProgramProjectPackage;
|
|
@@ -5,7 +5,7 @@ var fs = require('node:fs/promises');
|
|
|
5
5
|
var path = require('node:path');
|
|
6
6
|
var require$$0 = require('fs');
|
|
7
7
|
var require$$1 = require('path');
|
|
8
|
-
var index = require('./index-
|
|
8
|
+
var index = require('./index-MMW2ibQm.cjs');
|
|
9
9
|
require('node:module');
|
|
10
10
|
require('os');
|
|
11
11
|
require('readline');
|
|
@@ -1238,7 +1238,7 @@ function requireEjs () {
|
|
|
1238
1238
|
|
|
1239
1239
|
var ejsExports = requireEjs();
|
|
1240
1240
|
|
|
1241
|
-
const TEMPLATE_NAME = '
|
|
1241
|
+
const TEMPLATE_NAME = 'vanilla-vite-js';
|
|
1242
1242
|
const TEMPLATE_ROOT_CANDIDATES = [
|
|
1243
1243
|
path.resolve(__dirname, 'templates', TEMPLATE_NAME),
|
|
1244
1244
|
path.resolve(__dirname, '..', 'templates', TEMPLATE_NAME),
|
|
@@ -8,17 +8,17 @@ var node_crypto = require('node:crypto');
|
|
|
8
8
|
var promises = require('node:dns/promises');
|
|
9
9
|
var node_http = require('node:http');
|
|
10
10
|
var node_module = require('node:module');
|
|
11
|
-
var context = require('./context-
|
|
11
|
+
var context = require('./context-m2W2XbL0.cjs');
|
|
12
|
+
var runtimePermissionEnv = require('./runtime-permission-env-CtL8rsjB.cjs');
|
|
12
13
|
var undici = require('undici');
|
|
13
14
|
var fs$1 = require('node:fs/promises');
|
|
14
|
-
var index = require('./index-
|
|
15
|
+
var index = require('./index-MMW2ibQm.cjs');
|
|
15
16
|
var require$$0$1 = require('crypto');
|
|
16
17
|
var require$$0 = require('buffer');
|
|
17
|
-
var session = require('./session-
|
|
18
|
+
var session = require('./session-DjBkjaF8.cjs');
|
|
18
19
|
var browser = require('./browser-RAy8e8cV.cjs');
|
|
19
|
-
var projectVite = require('./project-vite-
|
|
20
|
-
var version = require('./version-
|
|
21
|
-
var runtimePermissionEnv = require('./runtime-permission-env-CjsCe5bp.cjs');
|
|
20
|
+
var projectVite = require('./project-vite-BQj8YLI4.cjs');
|
|
21
|
+
var version = require('./version-yEn1E2Bg.cjs');
|
|
22
22
|
|
|
23
23
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
24
24
|
class Locked extends Error {
|
|
@@ -1149,11 +1149,11 @@ function validateDevSessionCreatePayload(payload, allowedMiniUrlOrigins) {
|
|
|
1149
1149
|
!allowedMiniUrlOrigins.has(miniUrlOrigin.origin)) {
|
|
1150
1150
|
throw new TypeError('mini_url_origin must be an allowed LAN origin');
|
|
1151
1151
|
}
|
|
1152
|
-
const parsedPermissions =
|
|
1152
|
+
const parsedPermissions = runtimePermissionEnv.parseMiniProgramRuntimePermissions(payload.runtime_permissions);
|
|
1153
1153
|
if (!parsedPermissions.valid) {
|
|
1154
1154
|
throw new TypeError('runtime_permissions is invalid');
|
|
1155
1155
|
}
|
|
1156
|
-
const networkPermission = parsedPermissions.permissions
|
|
1156
|
+
const networkPermission = parsedPermissions.permissions.network;
|
|
1157
1157
|
if (networkPermission?.config.useOfficialDomain !== false) {
|
|
1158
1158
|
throw new TypeError('Dev Session does not allow official domains');
|
|
1159
1159
|
}
|
|
@@ -1605,7 +1605,7 @@ async function selectAvailablePort(options) {
|
|
|
1605
1605
|
port: createPortCandidates(options.startPort, maxAttempts),
|
|
1606
1606
|
});
|
|
1607
1607
|
if (port < options.startPort || port >= options.startPort + maxAttempts) {
|
|
1608
|
-
throw new Error(
|
|
1608
|
+
throw new Error(`无法找到可用端口(${options.label}),起始端口:${options.startPort}`);
|
|
1609
1609
|
}
|
|
1610
1610
|
return port;
|
|
1611
1611
|
}
|
|
@@ -1851,8 +1851,8 @@ const DEFAULT_CHANNEL_TRANSPORT = {
|
|
|
1851
1851
|
};
|
|
1852
1852
|
const IPV4_LOOPBACK_ADDRESSES = new net.BlockList();
|
|
1853
1853
|
IPV4_LOOPBACK_ADDRESSES.addSubnet('127.0.0.0', 8, 'ipv4');
|
|
1854
|
-
const { WebSocket, WebSocketServer } = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/dev-
|
|
1855
|
-
const { parse: parseHtml } = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/dev-
|
|
1854
|
+
const { WebSocket, WebSocketServer } = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/dev-CyZuw7Yn.cjs', document.baseURI).href)))('ws');
|
|
1855
|
+
const { parse: parseHtml } = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/dev-CyZuw7Yn.cjs', document.baseURI).href)))('parse5');
|
|
1856
1856
|
const BROWSER_DEV_HOST_ROOT_CANDIDATES = [
|
|
1857
1857
|
path.resolve(__dirname, 'devtools/browser-dev-host'),
|
|
1858
1858
|
path.resolve(__dirname, '../devtools/browser-dev-host'),
|
|
@@ -2968,7 +2968,7 @@ async function listenHttpServer(server, options, getPortImpl, listen) {
|
|
|
2968
2968
|
const port = await selectAvailablePort({
|
|
2969
2969
|
getPort: getPortImpl,
|
|
2970
2970
|
host: DEV_PORT_PROBE_HOST,
|
|
2971
|
-
label: '
|
|
2971
|
+
label: '浏览器调试页服务',
|
|
2972
2972
|
maxAttempts: lastCandidate - nextCandidate + 1,
|
|
2973
2973
|
startPort: nextCandidate,
|
|
2974
2974
|
});
|
|
@@ -2987,7 +2987,7 @@ async function listenHttpServer(server, options, getPortImpl, listen) {
|
|
|
2987
2987
|
nextCandidate = port + 1;
|
|
2988
2988
|
}
|
|
2989
2989
|
}
|
|
2990
|
-
throw lastAddressInUseError ?? new Error(
|
|
2990
|
+
throw lastAddressInUseError ?? new Error(`无法找到可用浏览器调试页服务端口,起始端口:${options.port}`);
|
|
2991
2991
|
}
|
|
2992
2992
|
const listenBrowserDevHostServer = (server, options) => new Promise((resolve, reject) => {
|
|
2993
2993
|
const onError = (error) => {
|
|
@@ -5217,6 +5217,9 @@ function resolveRuntimeLogTelemetryReportId(entry) {
|
|
|
5217
5217
|
function shouldReportRuntimeLogToTelemetry(entry, options = {}) {
|
|
5218
5218
|
const minLevel = options.minLevel ?? DEFAULT_RUNTIME_LOG_TELEMETRY_MIN_LEVEL;
|
|
5219
5219
|
const ignoreMessages = options.ignoreMessages ?? [...DEFAULT_RUNTIME_LOG_TELEMETRY_IGNORE_MESSAGES];
|
|
5220
|
+
if (options.shouldReport && !options.shouldReport(entry)) {
|
|
5221
|
+
return false;
|
|
5222
|
+
}
|
|
5220
5223
|
return isRuntimeLogLevelEnabled(entry.level, minLevel) && !ignoreMessages.some((pattern) => pattern.test(entry.message));
|
|
5221
5224
|
}
|
|
5222
5225
|
/**
|
|
@@ -5518,10 +5521,14 @@ function createTelemetryRuntimeLogSink(telemetry, options = {}) {
|
|
|
5518
5521
|
severity: entry.level,
|
|
5519
5522
|
});
|
|
5520
5523
|
const { requestId, ...reportContext } = entry.context ?? {};
|
|
5524
|
+
// 调用方可在上下文里显式给出聚合指纹(例如全局错误捕获已按统一规则算好);
|
|
5525
|
+
// 显式值优先于按本条日志错误推导的指纹,避免同一错误在直接上报与 sink 转发
|
|
5526
|
+
// 两条出口下落到不同 issue。
|
|
5527
|
+
const contextFingerprint = reportContext.runtime_error_fingerprint;
|
|
5521
5528
|
currentTelemetry.reportError({
|
|
5522
5529
|
addition: {
|
|
5523
5530
|
...reportContext,
|
|
5524
|
-
runtime_error_fingerprint: entry.errorInfo?.fingerprint,
|
|
5531
|
+
runtime_error_fingerprint: typeof contextFingerprint === 'string' && contextFingerprint ? contextFingerprint : entry.errorInfo?.fingerprint,
|
|
5525
5532
|
runtime_log_domain: entry.domain,
|
|
5526
5533
|
runtime_log_id: entry.id,
|
|
5527
5534
|
runtime_log_level: entry.level,
|
|
@@ -10895,7 +10902,7 @@ function assertNodeRuntimeCapabilities(ctx, settings) {
|
|
|
10895
10902
|
function createNodeRealtimeDriverRegistry() {
|
|
10896
10903
|
const registry = createRuntimeRealtimeDriverRegistry();
|
|
10897
10904
|
registry.registerLazy('node-websocket', async () => {
|
|
10898
|
-
const mod = await Promise.resolve().then(function () { return require('./index.esm-
|
|
10905
|
+
const mod = await Promise.resolve().then(function () { return require('./index.esm-BiAaAUFC.cjs'); });
|
|
10899
10906
|
return mod.createNodeWebSocketRuntimeDriver();
|
|
10900
10907
|
});
|
|
10901
10908
|
return registry;
|
|
@@ -14908,7 +14915,7 @@ function createLaunchSnapshot(source, requestOrigin) {
|
|
|
14908
14915
|
if (storedContextUrl) {
|
|
14909
14916
|
snapshot.dev_context_url = projectStoredDevContextUrl(storedContextUrl, requestOrigin.hostname);
|
|
14910
14917
|
}
|
|
14911
|
-
if (
|
|
14918
|
+
if (runtimePermissionEnv.parseMiniProgramRuntimePermissions(source.runtimePermissions).valid && source.runtimePermissions) {
|
|
14912
14919
|
snapshot.runtime_permissions = JSON.parse(JSON.stringify(source.runtimePermissions));
|
|
14913
14920
|
}
|
|
14914
14921
|
return snapshot;
|
|
@@ -14960,7 +14967,7 @@ const DEFAULT_BROWSER_DEV_HOST_PORT = 5174;
|
|
|
14960
14967
|
const DEFAULT_DEV_CONTEXT_TIMEOUT_MS = 3000;
|
|
14961
14968
|
const DEV_LISTEN_HOST = '0.0.0.0';
|
|
14962
14969
|
const LOCAL_DEV_URL_HOST = '127.0.0.1';
|
|
14963
|
-
//
|
|
14970
|
+
// 与 Heybox H5 Vite 插件共享,让 hb-sdk 统一管理启动输出。
|
|
14964
14971
|
const MANAGED_DEV_OUTPUT_ENV = 'HEYBOX_DEV_SERVER_MANAGED_OUTPUT';
|
|
14965
14972
|
const VITE_LOG_LEVEL = 'warn';
|
|
14966
14973
|
async function runDevCommand(options, runtime = {}) {
|
|
@@ -14975,7 +14982,7 @@ async function runDevCommand(options, runtime = {}) {
|
|
|
14975
14982
|
const appPort = await selectAvailablePort({
|
|
14976
14983
|
getPort: getPortImpl,
|
|
14977
14984
|
host: DEV_LISTEN_HOST,
|
|
14978
|
-
label: 'Vite
|
|
14985
|
+
label: 'Vite 开发服务器',
|
|
14979
14986
|
startPort: options.port ?? DEFAULT_APP_PORT,
|
|
14980
14987
|
});
|
|
14981
14988
|
const resolvedOptions = {
|
|
@@ -14991,9 +14998,9 @@ async function runDevCommand(options, runtime = {}) {
|
|
|
14991
14998
|
projectRoot,
|
|
14992
14999
|
timeoutMs: runtime.devContextTimeoutMs ?? DEFAULT_DEV_CONTEXT_TIMEOUT_MS,
|
|
14993
15000
|
});
|
|
14994
|
-
const launchRuntimePermissions = context.createBrowserDevRuntimePermissions(context.createBrowserDevPermissionState(devLaunchContext));
|
|
14995
|
-
const vite = await logger
|
|
14996
|
-
const appServer = await logger
|
|
15001
|
+
const launchRuntimePermissions = context.createBrowserDevRuntimePermissions(devLaunchContext.runtimePermissions, context.createBrowserDevPermissionState(devLaunchContext));
|
|
15002
|
+
const vite = await runDevStartupTask(logger, '正在加载项目 Vite', () => projectVite.loadProjectVite(projectRoot), '已加载项目 Vite');
|
|
15003
|
+
const appServer = await runDevStartupTask(logger, '正在创建 Vite 开发服务器', () => runtimePermissionEnv.withMiniappPlatformCspEnv(context.shouldSkipMiniappPlatformCsp(devLaunchContext.runtimePermissions), () => withManagedDevOutputEnv(() => vite.createServer(createViteServerOptions(projectRoot, resolvedOptions)), runtime.env), runtime.env), '已创建 Vite 开发服务器');
|
|
14997
15004
|
const mobileDevContextUrlsByOrigin = new Map();
|
|
14998
15005
|
let currentLocalDevLaunchToken = devLaunchContext.localDevLaunchToken;
|
|
14999
15006
|
appServer.middlewares.use((request, response, next) => {
|
|
@@ -15007,12 +15014,8 @@ async function runDevCommand(options, runtime = {}) {
|
|
|
15007
15014
|
if (!handled)
|
|
15008
15015
|
next();
|
|
15009
15016
|
});
|
|
15010
|
-
await logger
|
|
15011
|
-
|
|
15012
|
-
});
|
|
15013
|
-
const appUrl = await logger.task('正在探测小程序入口', () => resolveViteAppUrl(appServer, resolvedOptions, fetchImpl), {
|
|
15014
|
-
successText: '已确定小程序入口',
|
|
15015
|
-
});
|
|
15017
|
+
await runDevStartupTask(logger, '正在启动 Vite 开发服务器', () => appServer.listen(appPort), 'Vite 开发服务器已启动');
|
|
15018
|
+
const appUrl = await runDevStartupTask(logger, '正在探测小程序入口', () => resolveViteAppUrl(appServer, resolvedOptions, fetchImpl), '已确定小程序入口');
|
|
15016
15019
|
const buildLanAddresses = () => createLanAddressCandidates({
|
|
15017
15020
|
appUrl,
|
|
15018
15021
|
devContextUrlsByMiniUrlOrigin: mobileDevContextUrlsByOrigin,
|
|
@@ -15027,7 +15030,7 @@ async function runDevCommand(options, runtime = {}) {
|
|
|
15027
15030
|
const closers = [() => appServer.close()];
|
|
15028
15031
|
let browserDevHost;
|
|
15029
15032
|
try {
|
|
15030
|
-
browserDevHost = await logger
|
|
15033
|
+
browserDevHost = await runDevStartupTask(logger, '正在启动浏览器调试页服务', () => startBrowserDevHostServer({
|
|
15031
15034
|
appUrl,
|
|
15032
15035
|
devContext: devLaunchContext,
|
|
15033
15036
|
fetchImpl,
|
|
@@ -15048,7 +15051,7 @@ async function runDevCommand(options, runtime = {}) {
|
|
|
15048
15051
|
root: runtime.browserDevHostRoot,
|
|
15049
15052
|
rootCandidates: runtime.browserDevHostRootCandidates,
|
|
15050
15053
|
runtimePermissions: devLaunchContext.runtimePermissions,
|
|
15051
|
-
}),
|
|
15054
|
+
}), '浏览器调试页服务已启动');
|
|
15052
15055
|
}
|
|
15053
15056
|
catch (error) {
|
|
15054
15057
|
await closeAll(closers);
|
|
@@ -15079,23 +15082,29 @@ async function runDevCommand(options, runtime = {}) {
|
|
|
15079
15082
|
});
|
|
15080
15083
|
closers.push(tokenSession.close);
|
|
15081
15084
|
}
|
|
15082
|
-
logger.success('
|
|
15083
|
-
logger.info(
|
|
15084
|
-
logger.info(
|
|
15085
|
-
logger.info('
|
|
15085
|
+
logger.success('本地调试已启动');
|
|
15086
|
+
logger.info(`调试页:${browserDevHost.url}`);
|
|
15087
|
+
logger.info(`小程序地址:${appUrl}`);
|
|
15088
|
+
logger.info('手机调试:请在调试页扫码');
|
|
15086
15089
|
if (options.open !== false) {
|
|
15087
15090
|
logger.debug('正在打开浏览器调试页');
|
|
15088
15091
|
void openUrl(browserDevHost.url);
|
|
15089
15092
|
}
|
|
15090
15093
|
installShutdownHandlers(closers, runtime.process ?? process);
|
|
15091
15094
|
}
|
|
15095
|
+
function runDevStartupTask(logger, text, action, successText) {
|
|
15096
|
+
if (!logger.verbose) {
|
|
15097
|
+
return action();
|
|
15098
|
+
}
|
|
15099
|
+
return logger.task(text, () => action(), { successText });
|
|
15100
|
+
}
|
|
15092
15101
|
async function loadDevLaunchContext(options) {
|
|
15093
15102
|
const miniProgramId = await context.readBoundMiniProgramId(options.projectRoot);
|
|
15094
15103
|
if (!miniProgramId) {
|
|
15095
15104
|
throw new Error('hb-sdk dev 需要绑定小程序。请先运行 hb-sdk remote create 或 hb-sdk remote bind <mini-program-id>。');
|
|
15096
15105
|
}
|
|
15097
15106
|
try {
|
|
15098
|
-
const { detail, miniProgramId: verifiedMiniProgramId } = await options.logger
|
|
15107
|
+
const { detail, miniProgramId: verifiedMiniProgramId } = await runDevStartupTask(options.logger, '正在读取远端小程序权限', () => loadRemoteDevContextWithTimeout({
|
|
15099
15108
|
fetchImpl: options.fetchImpl,
|
|
15100
15109
|
load: (fetchImpl) => context.getBoundMiniProgram({
|
|
15101
15110
|
cwd: options.projectRoot,
|
|
@@ -15103,12 +15112,16 @@ async function loadDevLaunchContext(options) {
|
|
|
15103
15112
|
fetchImpl,
|
|
15104
15113
|
}),
|
|
15105
15114
|
timeoutMs: options.timeoutMs,
|
|
15106
|
-
}),
|
|
15115
|
+
}), '已读取远端小程序权限');
|
|
15116
|
+
const localPermissions = runtimePermissionEnv.readMiniappPermissionsFromPackageJson(options.projectRoot, runtimePermissionEnv.resolveMiniappSdkVersion());
|
|
15117
|
+
if (!localPermissions.declared)
|
|
15118
|
+
options.logger.warn(runtimePermissionEnv.getMissingPermissionsWarning());
|
|
15119
|
+
const effectivePermissions = runtimePermissionEnv.composeMiniappRuntimePermissions(localPermissions, detail.runtime_permissions).runtimePermissions;
|
|
15107
15120
|
return {
|
|
15108
15121
|
context: {
|
|
15109
15122
|
source: 'remote',
|
|
15110
15123
|
miniProgramId: verifiedMiniProgramId,
|
|
15111
|
-
runtimePermissions:
|
|
15124
|
+
runtimePermissions: effectivePermissions,
|
|
15112
15125
|
},
|
|
15113
15126
|
miniProgram: createMiniProgramSummary(verifiedMiniProgramId, detail),
|
|
15114
15127
|
};
|
|
@@ -15303,25 +15316,30 @@ async function closeAll(closers) {
|
|
|
15303
15316
|
function startLocalDevLaunchTokenSession(options) {
|
|
15304
15317
|
let closed = false;
|
|
15305
15318
|
let expirationTimer;
|
|
15319
|
+
let hasReportedTokenFailure = false;
|
|
15306
15320
|
let refreshTimer;
|
|
15307
15321
|
const refresh = async () => {
|
|
15308
15322
|
try {
|
|
15309
15323
|
const issued = await issueLocalDevLaunchToken(options);
|
|
15310
15324
|
if (closed)
|
|
15311
15325
|
return;
|
|
15312
|
-
if (!issued) {
|
|
15313
|
-
schedule(options.timeoutMs);
|
|
15314
|
-
return;
|
|
15315
|
-
}
|
|
15316
15326
|
options.onToken(issued.token);
|
|
15317
15327
|
const ttl = Math.max(0, issued.expiresAt - Date.now());
|
|
15318
15328
|
scheduleExpiration(ttl);
|
|
15319
15329
|
schedule(Math.max(1_000, Math.floor(ttl * 0.8)));
|
|
15330
|
+
if (hasReportedTokenFailure) {
|
|
15331
|
+
hasReportedTokenFailure = false;
|
|
15332
|
+
options.logger.success('手机扫码调试已恢复');
|
|
15333
|
+
}
|
|
15320
15334
|
}
|
|
15321
15335
|
catch (error) {
|
|
15322
15336
|
if (closed)
|
|
15323
15337
|
return;
|
|
15324
|
-
|
|
15338
|
+
if (!hasReportedTokenFailure) {
|
|
15339
|
+
options.logger.warn('手机扫码调试凭证获取失败,将自动重试');
|
|
15340
|
+
}
|
|
15341
|
+
hasReportedTokenFailure = true;
|
|
15342
|
+
options.logger.debug(`手机扫码调试凭证获取失败,详细原因:${index.readRedactedErrorMessage(error, { verbose: true })}`);
|
|
15325
15343
|
schedule(options.timeoutMs);
|
|
15326
15344
|
}
|
|
15327
15345
|
};
|
|
@@ -15352,31 +15370,25 @@ function startLocalDevLaunchTokenSession(options) {
|
|
|
15352
15370
|
};
|
|
15353
15371
|
}
|
|
15354
15372
|
async function issueLocalDevLaunchToken(options) {
|
|
15355
|
-
|
|
15356
|
-
|
|
15357
|
-
|
|
15358
|
-
|
|
15359
|
-
|
|
15360
|
-
|
|
15361
|
-
|
|
15362
|
-
|
|
15363
|
-
|
|
15364
|
-
|
|
15365
|
-
|
|
15366
|
-
|
|
15367
|
-
|
|
15368
|
-
throw new Error('响应缺少 token');
|
|
15369
|
-
}
|
|
15370
|
-
const expiresAt = normalizeExpiresAt(issued.expires_at);
|
|
15371
|
-
if (!expiresAt || expiresAt <= Date.now()) {
|
|
15372
|
-
throw new Error('响应缺少有效 expires_at');
|
|
15373
|
-
}
|
|
15374
|
-
return { expiresAt, token };
|
|
15373
|
+
const issued = await loadRemoteDevContextWithTimeout({
|
|
15374
|
+
fetchImpl: options.fetchImpl,
|
|
15375
|
+
timeoutMs: options.timeoutMs,
|
|
15376
|
+
load: (fetchImpl) => context.createLocalDevLaunchToken({
|
|
15377
|
+
cwd: options.projectRoot,
|
|
15378
|
+
env: options.env,
|
|
15379
|
+
fetchImpl,
|
|
15380
|
+
miniProgramId: options.miniProgramId,
|
|
15381
|
+
}),
|
|
15382
|
+
});
|
|
15383
|
+
const token = typeof issued.token === 'string' ? issued.token.trim() : '';
|
|
15384
|
+
if (!token) {
|
|
15385
|
+
throw new Error('响应缺少 token');
|
|
15375
15386
|
}
|
|
15376
|
-
|
|
15377
|
-
|
|
15378
|
-
|
|
15387
|
+
const expiresAt = normalizeExpiresAt(issued.expires_at);
|
|
15388
|
+
if (!expiresAt || expiresAt <= Date.now()) {
|
|
15389
|
+
throw new Error('响应缺少有效 expires_at');
|
|
15379
15390
|
}
|
|
15391
|
+
return { expiresAt, token };
|
|
15380
15392
|
}
|
|
15381
15393
|
function normalizeExpiresAt(value) {
|
|
15382
15394
|
if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0)
|