@heybox/hb-sdk 0.9.0-alpha.0 → 0.9.0-alpha.10
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 +70 -0
- package/README.md +16 -1
- package/dist/cli-chunks/{build-Dv5vWsBh.cjs → build-CT6E9KRd.cjs} +5 -5
- package/dist/cli-chunks/{context-B15FzPf9.cjs → context--dJRx4eH.cjs} +2 -2
- package/dist/cli-chunks/{create-BtZUsxJd.cjs → create-D6SiqyyQ.cjs} +1 -1
- package/dist/cli-chunks/{dev-tAThflC6.cjs → dev-CoWE5lyA.cjs} +228 -19
- package/dist/cli-chunks/{doctor-PE_MhGBX.cjs → doctor-IQ5wPkWD.cjs} +1 -1
- package/dist/cli-chunks/{index-Dd_eGaDz.cjs → index-CYxcLlON.cjs} +15 -15
- package/dist/cli-chunks/{index-CeA8xI0T.cjs → index-CnOLdO_-.cjs} +2 -2
- package/dist/cli-chunks/{index.esm-Bw3R5BAL.cjs → index.esm-07KOgDkI.cjs} +7 -7
- package/dist/cli-chunks/{login-5FPaFRi0.cjs → login-BszmxuLI.cjs} +2 -2
- package/dist/cli-chunks/{project-vite-ckDtbYtt.cjs → project-vite-tMo1gavS.cjs} +1 -1
- package/dist/cli-chunks/{remote-DCHqFvmB.cjs → remote-2oOP8M5C.cjs} +83 -17
- package/dist/cli-chunks/{runtime-gate-DkTbgx4d.cjs → runtime-gate-CNBf6Zkh.cjs} +8 -1
- package/dist/cli-chunks/{runtime-permission-env-DR89buKQ.cjs → runtime-permission-env-GH_vWEVx.cjs} +86 -57
- package/dist/cli-chunks/{session-BiT-5VHK.cjs → session-DrhTp-x-.cjs} +1 -1
- package/dist/cli-chunks/{skill-C5EoUf2T.cjs → skill-NfC1c0iQ.cjs} +2 -2
- package/dist/cli-chunks/{version-CJ7rfbl-.cjs → version-DA6v9R62.cjs} +1 -1
- package/dist/cli.cjs +1 -1
- package/dist/devtools/browser-dev-host/assets/browser-dev-host-C3c3yud2.js +102 -0
- package/dist/devtools/browser-dev-host/assets/desktop-app-launch-CHZKVQmQ.js +6 -0
- package/dist/devtools/browser-dev-host/assets/{index-0Ac39Njs.css → index-B5aFVoao.css} +1 -1
- package/dist/devtools/browser-dev-host/assets/{index-B1ZPOArZ.js → index-BmiQPZvl.js} +4 -4
- package/dist/devtools/browser-dev-host/index.html +3 -3
- package/dist/index.cjs.js +10 -3
- package/dist/index.esm.js +10 -3
- package/dist/miniapp-publish.cjs.js +121 -57
- package/dist/miniapp-publish.esm.js +121 -57
- package/dist/vite.cjs.js +33 -4
- package/dist/vite.esm.js +33 -4
- package/docs/runtime-host-migration.md +3 -1
- package/package.json +7 -7
- package/skill/skill.json +4 -4
- package/types/core/singleton.d.ts +1 -1
- package/types/miniapp-manifest/basic-info-browser.d.ts +29 -1
- package/types/miniapp-manifest/basic-info.d.ts +23 -2
- package/types/miniapp-manifest/category.d.ts +9 -2
- package/types/miniapp-manifest/companion-types.d.ts +25 -0
- package/types/miniapp-manifest/permissions.d.ts +12 -0
- package/types/miniapp-manifest/runtime-gate.d.ts +7 -0
- package/types/miniapp-manifest/schema.d.ts +23 -0
- package/types/miniapp-publish/index.d.ts +23 -51
- package/types/modules/auth/index.d.ts +5 -1
- package/types/modules/companion/index.d.ts +27 -34
- package/types/modules/navigation/index.d.ts +8 -0
- package/types/modules/network/index.d.ts +12 -2
- package/types/modules/user/index.d.ts +12 -1
- package/types/modules/user/steam-game-list.d.ts +2 -2
- package/types/modules/viewport/index.d.ts +21 -1
- package/types/vite/index.d.ts +5 -0
- package/dist/devtools/browser-dev-host/assets/browser-dev-host-Cu0OUKXf.js +0 -102
- package/dist/devtools/browser-dev-host/assets/desktop-app-launch-2BQ4uwPZ.js +0 -6
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<meta name="theme-color" content="#f4f5f7" />
|
|
7
7
|
<title>小程序工坊调试台</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
-
<link rel="modulepreload" crossorigin href="/assets/desktop-app-launch-
|
|
10
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-BmiQPZvl.js"></script>
|
|
9
|
+
<link rel="modulepreload" crossorigin href="/assets/desktop-app-launch-CHZKVQmQ.js">
|
|
10
|
+
<link rel="stylesheet" crossorigin href="/assets/index-B5aFVoao.css">
|
|
11
11
|
</head>
|
|
12
12
|
<body>
|
|
13
13
|
<div id="app"></div>
|
package/dist/index.cjs.js
CHANGED
|
@@ -1342,7 +1342,7 @@ function createMessageId() {
|
|
|
1342
1342
|
/** 构建时替换为当前发布包的实际版本。 */
|
|
1343
1343
|
const HB_SDK_VERSION = typeof undefined === 'string'
|
|
1344
1344
|
? undefined
|
|
1345
|
-
: '0.9.0-alpha.
|
|
1345
|
+
: '0.9.0-alpha.10';
|
|
1346
1346
|
|
|
1347
1347
|
const RUNTIME_MODES = new Set(['production', 'preview', 'development', 'unknown']);
|
|
1348
1348
|
const OPERATING_SYSTEM_NAMES = new Set(['android', 'ios', 'ohos', 'windows', 'macos', 'linux', 'unknown']);
|
|
@@ -1531,6 +1531,11 @@ class MiniProgramBridgeClient {
|
|
|
1531
1531
|
resolve: resolve,
|
|
1532
1532
|
reject,
|
|
1533
1533
|
isOperation: !useTimeout,
|
|
1534
|
+
cancelOnTimeout: method === NAVIGATION_OPEN_APP_PAGE_METHOD &&
|
|
1535
|
+
!!payload &&
|
|
1536
|
+
typeof payload === 'object' &&
|
|
1537
|
+
'target' in payload &&
|
|
1538
|
+
payload.target === 'mini_program',
|
|
1534
1539
|
cancelOnAbort: options.cancelOnAbort !== false,
|
|
1535
1540
|
...(options.onProgress ? { onProgress: options.onProgress } : {}),
|
|
1536
1541
|
...(options.signal ? { signal: options.signal } : {}),
|
|
@@ -1541,6 +1546,8 @@ class MiniProgramBridgeClient {
|
|
|
1541
1546
|
if (!active) {
|
|
1542
1547
|
return;
|
|
1543
1548
|
}
|
|
1549
|
+
if (active.cancelOnTimeout)
|
|
1550
|
+
this.sendCancel(id);
|
|
1544
1551
|
this.cleanupPending(active);
|
|
1545
1552
|
this.pendingRequests.delete(id);
|
|
1546
1553
|
reject(createSDKError('REQUEST_TIMEOUT', `${method} 调用超时`));
|
|
@@ -1779,7 +1786,7 @@ class MiniProgramBridgeClient {
|
|
|
1779
1786
|
}
|
|
1780
1787
|
sendCancel(id) {
|
|
1781
1788
|
const pending = this.pendingRequests.get(id);
|
|
1782
|
-
if (!pending
|
|
1789
|
+
if (!pending || (!pending.isOperation && !pending.cancelOnTimeout) || pending.cancelSent) {
|
|
1783
1790
|
return;
|
|
1784
1791
|
}
|
|
1785
1792
|
pending.cancelSent = true;
|
|
@@ -5639,7 +5646,7 @@ function onHandshakeStateChange(handler) {
|
|
|
5639
5646
|
* - `heybox_app_login_change`:`{ isHeyboxAppLoggedIn }`;
|
|
5640
5647
|
* - `user_info_authorization_change`:
|
|
5641
5648
|
* [UserInfoAuthorization](/reference/symbols/root/interfaces/UserInfoAuthorization),包含 `identity` 与
|
|
5642
|
-
* `profile`
|
|
5649
|
+
* `profile` 两项授权状态,支持 Steam 账号能力时还可能包含 `steam_account`;
|
|
5643
5650
|
* - `companion.session.output`:`{ sessionId, controllerGeneration, stream, sequence, data }`;
|
|
5644
5651
|
* - `companion.session.exit`:`{ sessionId, exit }`;
|
|
5645
5652
|
* - `companion.session.ownershipLost`:`{ sessionId, controllerGeneration }`。
|
package/dist/index.esm.js
CHANGED
|
@@ -1338,7 +1338,7 @@ function createMessageId() {
|
|
|
1338
1338
|
/** 构建时替换为当前发布包的实际版本。 */
|
|
1339
1339
|
const HB_SDK_VERSION = typeof undefined === 'string'
|
|
1340
1340
|
? undefined
|
|
1341
|
-
: '0.9.0-alpha.
|
|
1341
|
+
: '0.9.0-alpha.10';
|
|
1342
1342
|
|
|
1343
1343
|
const RUNTIME_MODES = new Set(['production', 'preview', 'development', 'unknown']);
|
|
1344
1344
|
const OPERATING_SYSTEM_NAMES = new Set(['android', 'ios', 'ohos', 'windows', 'macos', 'linux', 'unknown']);
|
|
@@ -1527,6 +1527,11 @@ class MiniProgramBridgeClient {
|
|
|
1527
1527
|
resolve: resolve,
|
|
1528
1528
|
reject,
|
|
1529
1529
|
isOperation: !useTimeout,
|
|
1530
|
+
cancelOnTimeout: method === NAVIGATION_OPEN_APP_PAGE_METHOD &&
|
|
1531
|
+
!!payload &&
|
|
1532
|
+
typeof payload === 'object' &&
|
|
1533
|
+
'target' in payload &&
|
|
1534
|
+
payload.target === 'mini_program',
|
|
1530
1535
|
cancelOnAbort: options.cancelOnAbort !== false,
|
|
1531
1536
|
...(options.onProgress ? { onProgress: options.onProgress } : {}),
|
|
1532
1537
|
...(options.signal ? { signal: options.signal } : {}),
|
|
@@ -1537,6 +1542,8 @@ class MiniProgramBridgeClient {
|
|
|
1537
1542
|
if (!active) {
|
|
1538
1543
|
return;
|
|
1539
1544
|
}
|
|
1545
|
+
if (active.cancelOnTimeout)
|
|
1546
|
+
this.sendCancel(id);
|
|
1540
1547
|
this.cleanupPending(active);
|
|
1541
1548
|
this.pendingRequests.delete(id);
|
|
1542
1549
|
reject(createSDKError('REQUEST_TIMEOUT', `${method} 调用超时`));
|
|
@@ -1775,7 +1782,7 @@ class MiniProgramBridgeClient {
|
|
|
1775
1782
|
}
|
|
1776
1783
|
sendCancel(id) {
|
|
1777
1784
|
const pending = this.pendingRequests.get(id);
|
|
1778
|
-
if (!pending
|
|
1785
|
+
if (!pending || (!pending.isOperation && !pending.cancelOnTimeout) || pending.cancelSent) {
|
|
1779
1786
|
return;
|
|
1780
1787
|
}
|
|
1781
1788
|
pending.cancelSent = true;
|
|
@@ -5635,7 +5642,7 @@ function onHandshakeStateChange(handler) {
|
|
|
5635
5642
|
* - `heybox_app_login_change`:`{ isHeyboxAppLoggedIn }`;
|
|
5636
5643
|
* - `user_info_authorization_change`:
|
|
5637
5644
|
* [UserInfoAuthorization](/reference/symbols/root/interfaces/UserInfoAuthorization),包含 `identity` 与
|
|
5638
|
-
* `profile`
|
|
5645
|
+
* `profile` 两项授权状态,支持 Steam 账号能力时还可能包含 `steam_account`;
|
|
5639
5646
|
* - `companion.session.output`:`{ sessionId, controllerGeneration, stream, sequence, data }`;
|
|
5640
5647
|
* - `companion.session.exit`:`{ sessionId, exit }`;
|
|
5641
5648
|
* - `companion.session.ownershipLost`:`{ sessionId, controllerGeneration }`。
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* 校验项目配置或产物中的小程序类别。
|
|
5
|
+
*
|
|
6
|
+
* @param value - 待校验值,只接受 `tool`、`game`、`other` 或 `undefined`;不做 trim 或大小写转换。
|
|
7
|
+
* @param sourceLabel - 错误消息中的字段来源,默认 `package.json#heybox.category`。
|
|
8
|
+
* @returns 合法类别原值;输入 `undefined` 时返回 `undefined`,由发布流程另行检查是否必填。
|
|
9
|
+
* @throws `Error` 输入为其他值(包括 null 或空字符串)时抛出带字段来源的错误。
|
|
10
|
+
*/
|
|
4
11
|
function parseMiniappCategory(value, sourceLabel = 'package.json#heybox.category') {
|
|
5
12
|
if (value === undefined)
|
|
6
13
|
return undefined;
|
|
@@ -2796,7 +2803,14 @@ var semverExports = requireSemver();
|
|
|
2796
2803
|
|
|
2797
2804
|
const BASIC_INFO_MAX_BYTES = 10 * 1024 * 1024;
|
|
2798
2805
|
const BASIC_INFO_MAX_PIXELS = 40_000_000;
|
|
2799
|
-
/**
|
|
2806
|
+
/**
|
|
2807
|
+
* 规范化并校验小程序展示名称。
|
|
2808
|
+
*
|
|
2809
|
+
* @param raw - 原始名称,必须是字符串。
|
|
2810
|
+
* @returns NFKC 规范化、去除首尾 Unicode White_Space 后的名称;不折叠规范化后的内部空白。
|
|
2811
|
+
* @throws `Error` 输入不是字符串,或规范化后不是 1–40 个 Unicode code point、不含文字或数字,或包含控制字符、
|
|
2812
|
+
* 格式字符、行/段分隔符以及 HTML 特殊字符 `<`、`>`、`&`、双引号或单引号时抛出。
|
|
2813
|
+
*/
|
|
2800
2814
|
function normalizeBasicInfoName(raw) {
|
|
2801
2815
|
if (typeof raw !== 'string')
|
|
2802
2816
|
throw new Error('heybox.name 必须是字符串');
|
|
@@ -2817,7 +2831,17 @@ function validateBasicInfoPath(raw) {
|
|
|
2817
2831
|
throw new Error('资料图片仅支持 JPEG、PNG、GIF、WebP');
|
|
2818
2832
|
return raw;
|
|
2819
2833
|
}
|
|
2820
|
-
/**
|
|
2834
|
+
/**
|
|
2835
|
+
* 校验项目声明或 Manifest 中的小程序展示资料。
|
|
2836
|
+
*
|
|
2837
|
+
* @param value - `package.json#heybox` 或 Manifest 对象,读取 `name`、`icon`、`coverImages`。
|
|
2838
|
+
* @param sdkVersion - 产物 SDK 版本,默认 `0.8.2`;用于判断历史产物是否允许缺失全部资料。
|
|
2839
|
+
* @returns 名称规范化后的新资料对象;仅版本早于 `0.8.2` 且三项均无自有属性时返回 `undefined`。
|
|
2840
|
+
* @throws `Error` SDK 版本非法、资料字段不齐、名称/图片路径非法,或封面不是 1–5 个互不重复的路径时抛出。
|
|
2841
|
+
* @remarks
|
|
2842
|
+
* 图片路径必须使用 `/` 分隔的非空相对路径,扩展名为 JPEG、PNG、GIF 或 WebP;拒绝 URL、点段、
|
|
2843
|
+
* 控制字符和构建/缓存等保留目录。此函数只校验声明,不读取文件,也不验证图片字节、尺寸或存在性。
|
|
2844
|
+
*/
|
|
2821
2845
|
function parseMiniappBasicInfo(value, sdkVersion = '0.8.2') {
|
|
2822
2846
|
sdkVersion = sdkVersion.trim();
|
|
2823
2847
|
if (!semverExports.valid(sdkVersion) || sdkVersion.startsWith('v') || sdkVersion.includes('+'))
|
|
@@ -3220,6 +3244,16 @@ function validateMiniappManifestVersion(version, sourceLabel = 'manifest.version
|
|
|
3220
3244
|
}
|
|
3221
3245
|
return String(version).trim();
|
|
3222
3246
|
}
|
|
3247
|
+
/**
|
|
3248
|
+
* 校验发布 Manifest 并返回规范化后的已知字段。
|
|
3249
|
+
*
|
|
3250
|
+
* @param manifest - 从构建产物读取的 Manifest,图片和 Companion 路径相对构建目录。
|
|
3251
|
+
* @returns 新对象:规范化版本、名称、平台与游戏绑定,省略空绑定及未声明的可选字段;不保留未知字段。
|
|
3252
|
+
* @throws `Error` 版本、平台、资料、权限、窗口、游戏绑定或生成的 Companion 声明非法,以及类别缺失时抛出。
|
|
3253
|
+
* @remarks
|
|
3254
|
+
* 不修改输入对象,不执行文件 I/O;不会验证图片/归档是否存在、内容摘要是否匹配,也不查询远端发布资格。
|
|
3255
|
+
* 成功仅表示 Manifest 字段通过校验,不能替代文件检查与服务端发布预检。
|
|
3256
|
+
*/
|
|
3223
3257
|
function validateMiniappManifestForDeploy(manifest) {
|
|
3224
3258
|
const version = validateMiniappManifestVersion(manifest.version, 'manifest.version');
|
|
3225
3259
|
const sdkVersion = validateSdkVersion(manifest.sdkVersion, 'manifest.sdkVersion');
|
|
@@ -3252,6 +3286,7 @@ function validateGeneratedCompanions(value) {
|
|
|
3252
3286
|
return undefined;
|
|
3253
3287
|
if (!isRecord$1(value) || Object.keys(value).length === 0)
|
|
3254
3288
|
throw new Error('manifest.companions 必须是非空对象');
|
|
3289
|
+
let descriptorCount = 0;
|
|
3255
3290
|
for (const [alias, definition] of Object.entries(value)) {
|
|
3256
3291
|
if (!/^[a-z][a-z0-9-]{0,31}$/.test(alias) ||
|
|
3257
3292
|
!isRecord$1(definition) ||
|
|
@@ -3265,6 +3300,9 @@ function validateGeneratedCompanions(value) {
|
|
|
3265
3300
|
Object.keys(definition.targets).length === 0)
|
|
3266
3301
|
throw new Error(`manifest.companions.${alias} 不合法`);
|
|
3267
3302
|
for (const [target, artifact] of Object.entries(definition.targets)) {
|
|
3303
|
+
descriptorCount += 1;
|
|
3304
|
+
if (descriptorCount > 64)
|
|
3305
|
+
throw new Error('manifest.companions 最多允许 64 个 target 描述符');
|
|
3268
3306
|
if (isRecord$1(artifact) && artifact.elevation === 'required') {
|
|
3269
3307
|
throw new Error(`manifest.companions.${alias}.targets.${target}.elevation V1 仅支持 none,暂不支持管理员权限启动`);
|
|
3270
3308
|
}
|
|
@@ -3552,7 +3590,35 @@ function readBasicInfoImageHeader(bytes) {
|
|
|
3552
3590
|
// 脚手架开发占位图;构建可用,提审前必须替换。
|
|
3553
3591
|
const BASIC_INFO_PLACEHOLDER_SHA256 = 'd14e54b30f5e6d1bcbf51267bc82b06a0af7c333f2945bcc16b4e1adc3b123dd';
|
|
3554
3592
|
|
|
3555
|
-
/**
|
|
3593
|
+
/**
|
|
3594
|
+
* 在浏览器中校验构建产物的展示资料图片,并创建预览 URL。
|
|
3595
|
+
*
|
|
3596
|
+
* @param manifest - 构建产物 Manifest,必须包含合法的 `sdkVersion`。
|
|
3597
|
+
* @param files - 构建文件列表;`relativePath` 必须精确匹配 Manifest 中的图片路径。
|
|
3598
|
+
* @returns 名称、图标 `icon_url` 和按原顺序排列的 `cover_image_urls`;允许缺失资料的历史产物返回 `undefined`。
|
|
3599
|
+
* @throws `Error` 资料非法、图片缺失/为空/超过 10 MiB、格式与扩展名不符、超过 4000 万像素、
|
|
3600
|
+
* icon 不是 200×200,或仍使用脚手架占位图片时拒绝;浏览器解码和摘要计算失败也会拒绝。
|
|
3601
|
+
* @remarks
|
|
3602
|
+
* 需要浏览器提供 `crypto.subtle`、`createImageBitmap` 和 object URL API;不生成或回写 Manifest。
|
|
3603
|
+
* 失败时会释放本次已创建的 URL;成功后所有 URL 由调用方在预览结束或替换时释放。
|
|
3604
|
+
* @example
|
|
3605
|
+
* ```ts
|
|
3606
|
+
* import { previewMiniappBasicInfo } from '@heybox/hb-sdk/miniapp-publish';
|
|
3607
|
+
*
|
|
3608
|
+
* const preview = await previewMiniappBasicInfo(manifest, files);
|
|
3609
|
+
* if (preview) {
|
|
3610
|
+
* try {
|
|
3611
|
+
* const image = new Image();
|
|
3612
|
+
* image.src = preview.icon_url;
|
|
3613
|
+
* await image.decode();
|
|
3614
|
+
* document.body.append(image);
|
|
3615
|
+
* } finally {
|
|
3616
|
+
* // 已加载的图像可继续显示;不再需要从这些 URL 加载时释放。
|
|
3617
|
+
* [preview.icon_url, ...preview.cover_image_urls].forEach(URL.revokeObjectURL);
|
|
3618
|
+
* }
|
|
3619
|
+
* }
|
|
3620
|
+
* ```
|
|
3621
|
+
*/
|
|
3556
3622
|
async function previewMiniappBasicInfo(manifest, files) {
|
|
3557
3623
|
const profile = parseMiniappBasicInfo(manifest, String(manifest.sdkVersion ?? ''));
|
|
3558
3624
|
if (!profile)
|
|
@@ -11848,6 +11914,13 @@ function parse(html, options) {
|
|
|
11848
11914
|
const RUNTIME_GATE_MARKER = 'data-heybox-runtime-gate';
|
|
11849
11915
|
const RUNTIME_GATE_STYLE_MARKER = 'data-heybox-runtime-gate-style';
|
|
11850
11916
|
const RUNTIME_GATE_BOOTSTRAP_MARKER = 'data-heybox-runtime-gate-bootstrap';
|
|
11917
|
+
/**
|
|
11918
|
+
* 检查 HTML 是否包含 Runtime Gate 的三个结构标记。
|
|
11919
|
+
*
|
|
11920
|
+
* @param html - 待检查的完整 HTML 文本,使用 HTML parser 解析。
|
|
11921
|
+
* @returns Gate、隐藏样式及 bootstrap 标记均出现时返回 `true`,否则返回 `false`。
|
|
11922
|
+
* @remarks 只识别标记,不验证脚本内容、标记所在节点或宿主握手;不能据此判断页面安全或实际可运行。
|
|
11923
|
+
*/
|
|
11851
11924
|
function hasMiniappRuntimeGate(html) {
|
|
11852
11925
|
const document = parse(html);
|
|
11853
11926
|
let hasGate = false;
|
|
@@ -11939,30 +12012,13 @@ const textEncoder = new TextEncoder();
|
|
|
11939
12012
|
/**
|
|
11940
12013
|
* 将扫描到的相对路径转换为统一的 `/` 分隔形式。
|
|
11941
12014
|
*
|
|
11942
|
-
* @param relativePath - 待整理的构建产物路径。
|
|
11943
|
-
*
|
|
11944
|
-
* @returns 仅统一分隔符形态的新字符串:所有 `\\` 转为 `/`,所有前导 `/` 被删除,其余连续
|
|
11945
|
-
* `/` 合并为一个。空字符串以及运行时传入的 `undefined`、`null`、`false`、`0`、`NaN` 等
|
|
11946
|
-
* falsy 值返回 `''`;truthy 非字符串按标准 `String(value)` 结果继续整理。
|
|
12015
|
+
* @param relativePath - 待整理的构建产物路径。
|
|
12016
|
+
* @returns 反斜杠转为 `/`、删除前导 `/`、合并连续 `/` 后的新字符串;空输入返回空字符串。
|
|
11947
12017
|
*
|
|
11948
12018
|
* @remarks
|
|
11949
|
-
*
|
|
11950
|
-
*
|
|
11951
|
-
*
|
|
11952
|
-
* `etc/passwd`,`C:\\temp\\app.js` 会变成 `C:/temp/app.js`,`assets/../secret/` 仍保留点段
|
|
11953
|
-
* 和尾随 slash;调用方不能根据返回值推断原输入是安全相对路径。
|
|
11954
|
-
*
|
|
11955
|
-
* SDK 的 build/deploy 流程会把扫描路径先用本函数统一分隔符,再分别使用
|
|
11956
|
-
* {@link relativePathContainsNodeModulesSegment} 排除完整 `node_modules` 目录段、用
|
|
11957
|
-
* {@link shouldUploadDistFile} 过滤根 `manifest.json` / `.DS_Store` / source map。`hb-sdk build`
|
|
11958
|
-
* 随后校验大小预算;`hb-sdk remote deploy` 还会在签名和上传前通过
|
|
11959
|
-
* `validateUploadPaths()`、`prepareMiniappArtifactFiles()` 或
|
|
11960
|
-
* `validateMiniappUploadPathLengths()` 执行真正的空路径、点段、控制字符、重复和长度校验。
|
|
11961
|
-
* 这些步骤不能由本函数单独替代;`shouldUploadDistFile()` 也不会自行检查 `node_modules`。
|
|
11962
|
-
*
|
|
11963
|
-
* 不要把本函数与 `@heybox/hb-sdk/protocol` 的 `isMiniProgramRelativePath()` 混用:协议 guard
|
|
11964
|
-
* 不做转换,只接受已经规范的非空字符串,并拒绝前导/尾随 `/`、反斜杠、空 segment、`.`、
|
|
11965
|
-
* `..` 与控制字符;本函数则会改写部分非法形态并保留其余不安全内容。
|
|
12019
|
+
* 仅整理文本格式,不做路径安全校验:保留点段、控制字符、盘符和尾随 `/`,不 trim 或 URL decode。
|
|
12020
|
+
* 上传前仍需校验路径安全、唯一性与长度。协议 `isMiniProgramRelativePath()` 则只校验、不改写输入。
|
|
12021
|
+
* JavaScript 非字符串输入按 `String(relativePath || '')` 转换,falsy 值回退为空串。
|
|
11966
12022
|
*
|
|
11967
12023
|
* @example
|
|
11968
12024
|
* ```ts
|
|
@@ -11970,8 +12026,6 @@ const textEncoder = new TextEncoder();
|
|
|
11970
12026
|
*
|
|
11971
12027
|
* normalizeRelativePath('///dist\\assets//app.js'); // 'dist/assets/app.js'
|
|
11972
12028
|
* normalizeRelativePath('assets/../secret///'); // 'assets/../secret/',仍需安全校验
|
|
11973
|
-
* normalizeRelativePath(123 as unknown as string); // '123',JavaScript 运行时强制转换
|
|
11974
|
-
* normalizeRelativePath(0 as unknown as string); // '',falsy 值先回退为空串
|
|
11975
12029
|
* ```
|
|
11976
12030
|
*
|
|
11977
12031
|
* @see {@link relativePathContainsNodeModulesSegment}
|
|
@@ -11987,40 +12041,21 @@ function normalizeRelativePath(relativePath) {
|
|
|
11987
12041
|
/**
|
|
11988
12042
|
* 判断路径中是否包含大小写不敏感的完整 `node_modules` 目录段。
|
|
11989
12043
|
*
|
|
11990
|
-
* @param relativePath -
|
|
11991
|
-
*
|
|
11992
|
-
* @returns 至少一个非空段大小写不敏感地等于完整 `node_modules` 时返回 `true`;空路径、
|
|
11993
|
-
* 空段及 `node_modules.txt`、`node_modules-old`、`@node_modules` 等相似名称返回 `false`。
|
|
12044
|
+
* @param relativePath - 构建产物路径,先按 {@link normalizeRelativePath} 整理分隔符再分段比较。
|
|
12045
|
+
* @returns 任意目录段大小写不敏感地等于 `node_modules` 时返回 `true`;空路径和相似名称返回 `false`。
|
|
11994
12046
|
*
|
|
11995
12047
|
* @remarks
|
|
11996
|
-
*
|
|
11997
|
-
* `node_modules
|
|
11998
|
-
*
|
|
11999
|
-
*
|
|
12000
|
-
* 这是同步、无副作用的**单一 predicate**,只回答“规范化后的文本是否包含该完整段”。它
|
|
12001
|
-
* 不 trim segment、不进行 URL/percent decode、Unicode normalize 或文件系统解析。例如
|
|
12002
|
-
* `node_modules%2Fpkg/index.js`、`%6eode_modules/pkg.js` 和 `node_modules.txt` 都不会命中;调用方
|
|
12003
|
-
* 不应传 URL,也不能把未命中解释为路径安全。
|
|
12004
|
-
*
|
|
12005
|
-
* 本函数不会拒绝或消解 `.` / `..`、绝对路径、Windows drive prefix、控制字符、空路径、
|
|
12006
|
-
* symlink、重复路径或超长路径。`assets/../node_modules/pkg.js` 仍会命中,因为点段保持原样;
|
|
12007
|
-
* 不含 `node_modules` 的危险路径则仍可能返回 `false`。签名或上传前必须继续执行对应的上传
|
|
12008
|
-
* 路径规范、唯一性和长度校验。
|
|
12009
|
-
*
|
|
12010
|
-
* SDK build/deploy 会在文件扫描后用本函数拒绝整个 `node_modules` 树,再用
|
|
12011
|
-
* {@link shouldUploadDistFile} 排除根 `manifest.json`、`.DS_Store` 和 source map。build 随后
|
|
12012
|
-
* 校验大小预算;deploy 还会执行上传路径安全、唯一性、长度与签名校验。这些步骤互不替代:`shouldUploadDistFile()` 本身不检查
|
|
12013
|
-
* `node_modules`,本函数也不决定普通文件是否应该上传。
|
|
12048
|
+
* 只检查目录段文本,不 trim、URL decode 或解析文件系统。未命中不表示路径安全;点段仍保留。
|
|
12049
|
+
* SDK 用它排除 `node_modules` 树,再用 {@link shouldUploadDistFile} 过滤其他不上传的文件;
|
|
12050
|
+
* 路径安全、唯一性、长度与上传大小需分别校验。
|
|
12014
12051
|
*
|
|
12015
12052
|
* @example
|
|
12016
12053
|
* ```ts
|
|
12017
12054
|
* import { relativePathContainsNodeModulesSegment } from '@heybox/hb-sdk/miniapp-publish';
|
|
12018
12055
|
*
|
|
12019
12056
|
* relativePathContainsNodeModulesSegment('dist\\NODE_MODULES/pkg/index.js'); // true
|
|
12020
|
-
* relativePathContainsNodeModulesSegment('dist//vendor///node_modules/'); // true
|
|
12021
12057
|
* relativePathContainsNodeModulesSegment('dist/node_modules.txt'); // false
|
|
12022
12058
|
* relativePathContainsNodeModulesSegment('dist/node_modules%2Fpkg.js'); // false,不做 URL decode
|
|
12023
|
-
* relativePathContainsNodeModulesSegment('assets/../secret.js'); // false,但这不代表路径安全
|
|
12024
12059
|
* ```
|
|
12025
12060
|
*
|
|
12026
12061
|
* @see [normalizeRelativePath](/reference/symbols/miniapp-publish/functions/normalizeRelativePath)
|
|
@@ -12400,8 +12435,9 @@ function createMiniappArtifactFileHeader(file) {
|
|
|
12400
12435
|
* 校验服务端 session 文件映射,并计算仍需上传的文件。
|
|
12401
12436
|
*
|
|
12402
12437
|
* @param localFiles - 本地构建产物 receipt;除 `relativePath` 和 `size` 外,每项必须包含 64 位小写十六进制 `sha256`。
|
|
12403
|
-
* @param serverFiles -
|
|
12438
|
+
* @param serverFiles - 服务端签发的会话文件映射;与复用的 Companion 合起来必须覆盖本地产物。
|
|
12404
12439
|
* @param uploadedKeys - 服务端已确认上传成功的 key;每个值都必须属于当前会话,重复值会被去重。
|
|
12440
|
+
* @param reusedCompanionFiles - 已验证 Blob 引用;仅允许当前产物中的 Companion 文件。
|
|
12405
12441
|
* @returns
|
|
12406
12442
|
* 包含三个字段的对象:`files` 是按路径 UTF-8 字节序排列的全部 `{ file, key }` 映射,
|
|
12407
12443
|
* `missingFiles` 是其中尚未确认上传的映射,`uploadedKeys` 是去重后的已上传 key。
|
|
@@ -12417,15 +12453,38 @@ function createMiniappArtifactFileHeader(file) {
|
|
|
12417
12453
|
* sha256: 'a'.repeat(64),
|
|
12418
12454
|
* }],
|
|
12419
12455
|
* [],
|
|
12456
|
+
* [],
|
|
12420
12457
|
* );
|
|
12421
12458
|
*
|
|
12422
12459
|
* console.log(resolved.missingFiles.length); // 1
|
|
12423
12460
|
* ```
|
|
12424
12461
|
*/
|
|
12425
|
-
function resolveMiniappUploadSessionFiles(localFiles, serverFiles, uploadedKeys) {
|
|
12462
|
+
function resolveMiniappUploadSessionFiles(localFiles, serverFiles, uploadedKeys, reusedCompanionFiles) {
|
|
12426
12463
|
const preparedFiles = prepareMiniappArtifactFiles(localFiles);
|
|
12427
|
-
if (serverFiles.length !== preparedFiles.length) {
|
|
12428
|
-
throw new Error(`上传会话文件数量异常:期望 ${preparedFiles.length},实际 ${serverFiles.length}`);
|
|
12464
|
+
if (serverFiles.length + reusedCompanionFiles.length !== preparedFiles.length) {
|
|
12465
|
+
throw new Error(`上传会话文件数量异常:期望 ${preparedFiles.length},实际 ${serverFiles.length + reusedCompanionFiles.length}`);
|
|
12466
|
+
}
|
|
12467
|
+
const localByPath = new Map(preparedFiles.map((file) => [file.relativePath, file]));
|
|
12468
|
+
const reused = new Map();
|
|
12469
|
+
for (const reference of reusedCompanionFiles) {
|
|
12470
|
+
const relativePath = normalizeMiniappArtifactRelativePath(reference.relative_path);
|
|
12471
|
+
const local = localByPath.get(relativePath);
|
|
12472
|
+
if (!relativePath.startsWith('companions/') || !local) {
|
|
12473
|
+
throw new Error(`上传会话返回了不属于当前 Companion 产物的 Blob 引用:${relativePath}`);
|
|
12474
|
+
}
|
|
12475
|
+
if (reused.has(relativePath)) {
|
|
12476
|
+
throw new Error(`上传会话返回重复 Blob 引用:${relativePath}`);
|
|
12477
|
+
}
|
|
12478
|
+
if (!Number.isSafeInteger(reference.size) || reference.size < 0 || reference.size !== local.size) {
|
|
12479
|
+
throw new Error(`上传会话 Blob 文件大小与本地构建不一致:${relativePath}`);
|
|
12480
|
+
}
|
|
12481
|
+
if (!('sha256' in local) || !/^[a-f0-9]{64}$/.test(reference.sha256) || reference.sha256 !== local.sha256) {
|
|
12482
|
+
throw new Error(`上传会话 Blob 文件 SHA-256 与本地构建不一致:${relativePath}`);
|
|
12483
|
+
}
|
|
12484
|
+
if (typeof reference.blob_id !== 'string' || !reference.blob_id.trim()) {
|
|
12485
|
+
throw new Error(`上传会话 Blob 引用 ID 无效:${relativePath}`);
|
|
12486
|
+
}
|
|
12487
|
+
reused.set(relativePath, reference);
|
|
12429
12488
|
}
|
|
12430
12489
|
const mappings = new Map();
|
|
12431
12490
|
const serverSizes = new Map();
|
|
@@ -12436,6 +12495,9 @@ function resolveMiniappUploadSessionFiles(localFiles, serverFiles, uploadedKeys)
|
|
|
12436
12495
|
if (mappings.has(relativePath)) {
|
|
12437
12496
|
throw new Error(`上传会话返回重复文件路径:${relativePath}`);
|
|
12438
12497
|
}
|
|
12498
|
+
if (reused.has(relativePath)) {
|
|
12499
|
+
throw new Error(`上传会话同时返回文件 key 和 Blob 引用:${relativePath}`);
|
|
12500
|
+
}
|
|
12439
12501
|
const key = String(serverFile.key || '');
|
|
12440
12502
|
if (!key) {
|
|
12441
12503
|
throw new Error(`上传会话未返回文件 key:${relativePath}`);
|
|
@@ -12456,7 +12518,8 @@ function resolveMiniappUploadSessionFiles(localFiles, serverFiles, uploadedKeys)
|
|
|
12456
12518
|
serverHashes.set(relativePath, sha256);
|
|
12457
12519
|
knownKeys.add(key);
|
|
12458
12520
|
}
|
|
12459
|
-
const
|
|
12521
|
+
const sessionFiles = preparedFiles.filter((file) => !reused.has(file.relativePath));
|
|
12522
|
+
const files = sessionFiles.map((file) => {
|
|
12460
12523
|
const key = mappings.get(file.relativePath);
|
|
12461
12524
|
if (!key) {
|
|
12462
12525
|
throw new Error(`上传会话缺少文件映射:${file.relativePath}`);
|
|
@@ -12481,6 +12544,7 @@ function resolveMiniappUploadSessionFiles(localFiles, serverFiles, uploadedKeys)
|
|
|
12481
12544
|
files,
|
|
12482
12545
|
missingFiles: files.filter((file) => !completed.has(file.key)),
|
|
12483
12546
|
uploadedKeys: [...completed],
|
|
12547
|
+
reusedCompanionFiles: [...reused.values()],
|
|
12484
12548
|
};
|
|
12485
12549
|
}
|
|
12486
12550
|
/**
|