@heybox/hb-sdk 0.4.6 → 0.4.7

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/README.md CHANGED
@@ -96,11 +96,14 @@ import { share } from '@heybox/hb-sdk';
96
96
  await share.showShareMenu({
97
97
  title: '我的小程序页面',
98
98
  desc: '来自黑盒小程序的分享',
99
- url: window.location.href,
100
99
  imageUrl: 'https://imgheybox.max-c.com/demo.png',
101
100
  });
102
101
  ```
103
102
 
103
+ 用户小程序通常不要传 `url`;宿主 runtime 会按当前小程序生成
104
+ `/tools/common_share?user_miniprogram_id=...` 分享落地页。只有确实要分享外部
105
+ HTTP(S) 页面时才显式传 `url`。
106
+
104
107
  截图分享:
105
108
 
106
109
  ```ts
@@ -336,9 +339,10 @@ try {
336
339
  5. 解析 `dist/manifest.json`,校验 `version` 为 `x.y.z`,自动剥离 BOM;普通 remote deploy 要求该版本与 precheck 使用的 `package.json.version` 一致。
337
340
  6. `--skip-build` 会先读取已有 `dist/manifest.json.version`,再调用 precheck。
338
341
  7. 遍历 `dist/` 文件,跳过 `manifest.json`、`.DS_Store`、`.map`;遇到 symlink 或 `node_modules` 直接报错。
339
- 8. 校验所有上传路径长度不超过 64
340
- 9. 4 并发上传到 COS。默认只展示上传阶段和文件总数;`--verbose` 会展示并发数、bucket / region 和逐文件结果,但不会输出 keys、签名、cookie token。任一文件失败立刻 abort,不提交审核。
341
- 10. 全部上传成功后调用 `/mall/developer/user_miniprogram/version/submit_audit`,输出提交审核成功、发布策略和可用的 preview URL;后端原始失败细节只在 `--verbose` 下展示。
342
+ 8. 校验所有上传路径长度不超过 64,并在任何上传请求发生前限制实际上传产物总大小不超过 100MiB。错误提示中使用 `100MB`,方便开发者理解。
343
+ 9. 上传信息、上传凭证和上传回调按批次执行,每批最多 50 个文件;批次串行,批内保持 4 并发上传到 COS。CLI 会校验 CDN 上传信息接口返回的 key 与本地期望 key 完全一致,异常时停止后续批次且不提交审核。
344
+ 10. 默认只展示上传阶段和文件总数,例如 `正在上传 137/244 个文件`;`--verbose` 会展示并发数、批次数、当前批次、bucket / region 和逐文件结果,但不会输出 keys、签名、cookie、pkey、token 或临时密钥。
345
+ 11. 全部上传成功后调用 `/mall/developer/user_miniprogram/version/submit_audit`,输出提交审核成功、发布策略和可用的 preview URL;后端原始失败细节只在 `--verbose` 下展示。
342
346
 
343
347
  默认 `auto_publish=false`,审核通过后使用 `hb-sdk remote versions` 查看版本状态,再用 `hb-sdk remote release <version>` 发布;使用 `--auto-publish` 时提交 `auto_publish=true`,审核通过后自动发布。未发布候选版本可通过 `hb-sdk remote allowlist add <heybox_id>` 加入预览白名单,让指定用户在广场看到;正式入口使用 `mini_program_id`,`mini_url` 仅用于本地调试。
344
348
 
@@ -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-DuwxUSkq.cjs');
8
+ var index = require('./index-13_8m0Pw.cjs');
9
9
  require('node:module');
10
10
  require('os');
11
11
  require('readline');
@@ -9,7 +9,7 @@ var node_url = require('node:url');
9
9
  var net = require('node:net');
10
10
  var node_http = require('node:http');
11
11
  var browser = require('./browser-RAy8e8cV.cjs');
12
- var index = require('./index-DuwxUSkq.cjs');
12
+ var index = require('./index-13_8m0Pw.cjs');
13
13
  require('node:process');
14
14
  require('node:buffer');
15
15
  require('node:util');
@@ -4,7 +4,7 @@ var fs$1 = require('node:fs');
4
4
  var fs = require('node:fs/promises');
5
5
  var os = require('node:os');
6
6
  var path = require('node:path');
7
- var index = require('./index-DuwxUSkq.cjs');
7
+ var index = require('./index-13_8m0Pw.cjs');
8
8
  require('node:module');
9
9
  require('path');
10
10
  require('os');
@@ -234,19 +234,19 @@ function requireArgument () {
234
234
 
235
235
  var command = {};
236
236
 
237
- const require$5 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/index-DuwxUSkq.cjs', document.baseURI).href)));
237
+ const require$5 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/index-13_8m0Pw.cjs', document.baseURI).href)));
238
238
  function __require$4() { return require$5("node:events"); }
239
239
 
240
- const require$4 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/index-DuwxUSkq.cjs', document.baseURI).href)));
240
+ const require$4 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/index-13_8m0Pw.cjs', document.baseURI).href)));
241
241
  function __require$3() { return require$4("node:child_process"); }
242
242
 
243
- const require$3 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/index-DuwxUSkq.cjs', document.baseURI).href)));
243
+ const require$3 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/index-13_8m0Pw.cjs', document.baseURI).href)));
244
244
  function __require$2() { return require$3("node:path"); }
245
245
 
246
- const require$2 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/index-DuwxUSkq.cjs', document.baseURI).href)));
246
+ const require$2 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/index-13_8m0Pw.cjs', document.baseURI).href)));
247
247
  function __require$1() { return require$2("node:fs"); }
248
248
 
249
- const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/index-DuwxUSkq.cjs', document.baseURI).href)));
249
+ const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli-chunks/index-13_8m0Pw.cjs', document.baseURI).href)));
250
250
  function __require() { return require$1("node:process"); }
251
251
 
252
252
  var help = {};
@@ -13094,7 +13094,7 @@ function readErrorMessage(error, options = {}) {
13094
13094
  }
13095
13095
 
13096
13096
  const CLI_VERSION_PLACEHOLDER = ['__HB', 'SDK', 'CLI', 'VERSION__'].join('_');
13097
- const BUILT_CLI_VERSION = '0.4.6';
13097
+ const BUILT_CLI_VERSION = '0.4.7';
13098
13098
  const PACKAGE_JSON_CANDIDATES = [
13099
13099
  path.resolve(__dirname, '..', '..', 'package.json'),
13100
13100
  path.resolve(__dirname, '..', 'package.json'),
@@ -13377,31 +13377,31 @@ function createCommandLoggerResolver(options) {
13377
13377
  };
13378
13378
  }
13379
13379
  const defaultClearLoginStatus = async (...args) => {
13380
- const { clearLoginStatus } = await Promise.resolve().then(function () { return require('./login-OqaEx-Wd.cjs'); });
13380
+ const { clearLoginStatus } = await Promise.resolve().then(function () { return require('./login-CoBqr1Bm.cjs'); });
13381
13381
  return clearLoginStatus(...args);
13382
13382
  };
13383
13383
  const defaultLoginToHeybox = async (...args) => {
13384
- const { loginToHeybox } = await Promise.resolve().then(function () { return require('./login-OqaEx-Wd.cjs'); });
13384
+ const { loginToHeybox } = await Promise.resolve().then(function () { return require('./login-CoBqr1Bm.cjs'); });
13385
13385
  return loginToHeybox(...args);
13386
13386
  };
13387
13387
  const defaultPrintLoginStatus = async (...args) => {
13388
- const { printLoginStatus } = await Promise.resolve().then(function () { return require('./login-OqaEx-Wd.cjs'); });
13388
+ const { printLoginStatus } = await Promise.resolve().then(function () { return require('./login-CoBqr1Bm.cjs'); });
13389
13389
  return printLoginStatus(...args);
13390
13390
  };
13391
13391
  const defaultRunCreateCommand = async (...args) => {
13392
- const { runCreateCommand } = await Promise.resolve().then(function () { return require('./create-BahMMgJH.cjs'); });
13392
+ const { runCreateCommand } = await Promise.resolve().then(function () { return require('./create-IOxksfE3.cjs'); });
13393
13393
  return runCreateCommand(...args);
13394
13394
  };
13395
13395
  const defaultRunDevCommand = async (...args) => {
13396
- const { runDevCommand } = await Promise.resolve().then(function () { return require('./dev-CTuXVPpU.cjs'); });
13396
+ const { runDevCommand } = await Promise.resolve().then(function () { return require('./dev-zMT1BxGs.cjs'); });
13397
13397
  return runDevCommand(...args);
13398
13398
  };
13399
13399
  const defaultRunDoctorCommand = async (...args) => {
13400
- const { runDoctorCommand } = await Promise.resolve().then(function () { return require('./doctor-9gg3ZIvt.cjs'); });
13400
+ const { runDoctorCommand } = await Promise.resolve().then(function () { return require('./doctor-ZOfNccof.cjs'); });
13401
13401
  return runDoctorCommand(...args);
13402
13402
  };
13403
13403
  const defaultRunRemoteCommand = async (...args) => {
13404
- const { runRemoteCommand } = await Promise.resolve().then(function () { return require('./remote-zX17hOT4.cjs'); }).then(function (n) { return n.remote; });
13404
+ const { runRemoteCommand } = await Promise.resolve().then(function () { return require('./remote-Cc5j-m2I.cjs'); }).then(function (n) { return n.remote; });
13405
13405
  return runRemoteCommand(...args);
13406
13406
  };
13407
13407
  function resolveStandaloneLogger(options, verbose) {
@@ -1,13 +1,13 @@
1
1
  'use strict';
2
2
 
3
- var index$2 = require('./index-DuwxUSkq.cjs');
3
+ var index$2 = require('./index-13_8m0Pw.cjs');
4
4
  var require$$0$2 = require('fs');
5
5
  var require$$2$1 = require('crypto');
6
6
  var require$$1$2 = require('path');
7
7
  var require$$0$3 = require('assert');
8
8
  var require$$4$2 = require('events');
9
9
  var require$$1$1 = require('util');
10
- var remote = require('./remote-zX17hOT4.cjs');
10
+ var remote = require('./remote-Cc5j-m2I.cjs');
11
11
  var require$$0$5 = require('net');
12
12
  var require$$0$4 = require('url');
13
13
  var require$$2$2 = require('http');
@@ -3,9 +3,9 @@
3
3
  var promises = require('node:readline/promises');
4
4
  var node_crypto = require('node:crypto');
5
5
  var node_http = require('node:http');
6
- var session = require('./session-D7lF9mpd.cjs');
6
+ var session = require('./session-BSi5YfqO.cjs');
7
7
  var browser = require('./browser-RAy8e8cV.cjs');
8
- var index = require('./index-DuwxUSkq.cjs');
8
+ var index = require('./index-13_8m0Pw.cjs');
9
9
  require('node:path');
10
10
  require('fs');
11
11
  require('constants');
@@ -1,12 +1,12 @@
1
1
  'use strict';
2
2
 
3
3
  var promises = require('node:readline/promises');
4
- var session = require('./session-D7lF9mpd.cjs');
4
+ var session = require('./session-BSi5YfqO.cjs');
5
5
  var childProcess = require('node:child_process');
6
6
  var fs = require('node:fs');
7
7
  var fs$1 = require('node:fs/promises');
8
8
  var path = require('node:path');
9
- var index = require('./index-DuwxUSkq.cjs');
9
+ var index = require('./index-13_8m0Pw.cjs');
10
10
 
11
11
  var re = {exports: {}};
12
12
 
@@ -2858,6 +2858,8 @@ function formatReason(error) {
2858
2858
 
2859
2859
  const MINIAPP_UPLOAD_SCOPE = 'activity';
2860
2860
  const ACTIVITY_UPLOAD_KEY_MAX_LENGTH = 64;
2861
+ const MINIAPP_UPLOAD_BATCH_SIZE = 50;
2862
+ const MINIAPP_UPLOAD_TOTAL_SIZE_LIMIT_BYTES = 100 * 1024 * 1024;
2861
2863
  const USER_MINIPROGRAM_ACCESS_STATUS_API_PATH = '/mall/developer/user_miniprogram/access_status';
2862
2864
  const DEVELOPER_ENTITY_LIST_API_PATH = '/mall/developer/entity/list';
2863
2865
  const DEVELOPER_ENTITY_SWITCH_API_PATH = '/mall/developer/entity/switch';
@@ -2916,6 +2918,17 @@ function validateUploadPaths(items, options) {
2916
2918
  }
2917
2919
  return undefined;
2918
2920
  }
2921
+ function validateUploadTotalSize(items, options = {}) {
2922
+ const limit = options.limitBytes ?? MINIAPP_UPLOAD_TOTAL_SIZE_LIMIT_BYTES;
2923
+ const total = items.reduce((sum, item) => {
2924
+ const size = Number(item.size);
2925
+ return sum + (Number.isFinite(size) && size > 0 ? size : 0);
2926
+ }, 0);
2927
+ if (total > limit) {
2928
+ return '构建产物总大小超过 100MB,请删除未引用资源或压缩图片后重新 deploy';
2929
+ }
2930
+ return undefined;
2931
+ }
2919
2932
  function shouldUploadDistFile(relativePath) {
2920
2933
  const normalized = normalizeRelativePath(relativePath);
2921
2934
  if (!normalized) {
@@ -2978,7 +2991,74 @@ async function runUpload(options, runtime = {}) {
2978
2991
  const postCDNUploadCallback$1 = runtime.postCDNUploadCallback ?? postCDNUploadCallback;
2979
2992
  const createReadStream = runtime.createReadStream ?? fs.createReadStream;
2980
2993
  const orderedFiles = [...options.files].sort((a, b) => a.relativePath.localeCompare(b.relativePath));
2981
- const fileInfos = orderedFiles.map((file) => ({
2994
+ const batches = chunkUploadFiles(orderedFiles, MINIAPP_UPLOAD_BATCH_SIZE);
2995
+ await logger.task(`正在上传 ${orderedFiles.length} 个文件`, async (taskContext) => {
2996
+ logger.debug(`上传并发数: ${concurrency}`);
2997
+ logger.debug(`上传批次数: ${batches.length}`);
2998
+ let completed = 0;
2999
+ for (let batchIndex = 0; batchIndex < batches.length; batchIndex += 1) {
3000
+ const batch = batches[batchIndex];
3001
+ logger.debug(`正在处理上传批次 ${batchIndex + 1}/${batches.length},文件数: ${batch.length}`);
3002
+ logger.debug('正在获取 CDN 上传信息');
3003
+ const fileInfos = createUploadFileInfos(batch, options);
3004
+ const uploadInfo = await getCDNUploadInfo$1({ session: options.session, scope: MINIAPP_UPLOAD_SCOPE, fileInfos, needCache: false }, { baseUrl: runtime.baseUrl, fetchImpl });
3005
+ logger.debug(`CDN bucket: ${uploadInfo.bucket}`);
3006
+ logger.debug(`CDN region: ${uploadInfo.region}`);
3007
+ validateUploadInfoKeys(batch, uploadInfo.keys, options);
3008
+ const indexedFiles = batch.map((file, index) => ({ file, key: uploadInfo.keys[index] }));
3009
+ const batchKeys = indexedFiles.map(({ key }) => key);
3010
+ logger.debug('正在获取 CDN 上传凭证');
3011
+ const uploadToken = await getCDNUploadToken$1({
3012
+ session: options.session,
3013
+ bucket: uploadInfo.bucket,
3014
+ keys: batchKeys,
3015
+ mimetypes: indexedFiles.map(({ file }) => file.mimeType),
3016
+ isMultipartUpload: 0,
3017
+ }, { baseUrl: runtime.baseUrl, fetchImpl });
3018
+ const cos = runtime.createCosClient ? runtime.createCosClient({ session: options.session, uploadToken }) : await createDefaultCosClient(uploadToken);
3019
+ const queue = indexedFiles.slice();
3020
+ const workers = Array.from({ length: Math.min(concurrency, indexedFiles.length) }, async () => {
3021
+ while (queue.length > 0) {
3022
+ const uploadTask = queue.shift();
3023
+ if (!uploadTask) {
3024
+ return;
3025
+ }
3026
+ try {
3027
+ await cos.putObject({
3028
+ Bucket: uploadInfo.bucket,
3029
+ Region: uploadInfo.region,
3030
+ Key: uploadTask.key,
3031
+ Body: createReadStream(uploadTask.file.absolutePath),
3032
+ ContentLength: uploadTask.file.size,
3033
+ ContentType: uploadTask.file.mimeType,
3034
+ });
3035
+ completed += 1;
3036
+ taskContext.update(`正在上传 ${completed}/${orderedFiles.length} 个文件`);
3037
+ logger.debug(`[${String(completed).padStart(2)}/${orderedFiles.length}] ok ${uploadTask.file.relativePath} (${formatSize(uploadTask.file.size)})`);
3038
+ }
3039
+ catch (error) {
3040
+ completed += 1;
3041
+ const message = readErrorMessage(error);
3042
+ logger.debug(`[${String(completed).padStart(2)}/${orderedFiles.length}] error ${uploadTask.file.relativePath} -> ${message}`);
3043
+ throw new index.CliError(`上传文件失败:${uploadTask.file.relativePath} -> ${message}`, readVerboseUploadError(uploadTask.file.relativePath, error));
3044
+ }
3045
+ }
3046
+ });
3047
+ await Promise.all(workers);
3048
+ logger.debug('正在确认 CDN 上传结果');
3049
+ await postCDNUploadCallback$1({ session: options.session, keys: batchKeys, isFinished: true }, { baseUrl: runtime.baseUrl, fetchImpl });
3050
+ }
3051
+ }, { successText: `已上传 ${orderedFiles.length} 个文件` });
3052
+ }
3053
+ function chunkUploadFiles(files, batchSize) {
3054
+ const batches = [];
3055
+ for (let index = 0; index < files.length; index += batchSize) {
3056
+ batches.push(files.slice(index, index + batchSize));
3057
+ }
3058
+ return batches;
3059
+ }
3060
+ function createUploadFileInfos(files, options) {
3061
+ return files.map((file) => ({
2982
3062
  name: file.relativePath.split('/').pop() ?? file.relativePath,
2983
3063
  mimetype: file.mimeType,
2984
3064
  fsize: file.size,
@@ -2988,56 +3068,22 @@ async function runUpload(options, runtime = {}) {
2988
3068
  relativePath: file.relativePath,
2989
3069
  }),
2990
3070
  }));
2991
- await logger.task(`正在上传 ${orderedFiles.length} 个文件`, async (taskContext) => {
2992
- logger.debug(`上传并发数: ${concurrency}`);
2993
- logger.debug('正在获取 CDN 上传信息');
2994
- const uploadInfo = await getCDNUploadInfo$1({ session: options.session, scope: MINIAPP_UPLOAD_SCOPE, fileInfos, needCache: false }, { baseUrl: runtime.baseUrl, fetchImpl });
2995
- logger.debug(`CDN bucket: ${uploadInfo.bucket}`);
2996
- logger.debug(`CDN region: ${uploadInfo.region}`);
2997
- const indexedFiles = orderedFiles.map((file, index) => ({ file, key: uploadInfo.keys[index] }));
2998
- logger.debug('正在获取 CDN 上传凭证');
2999
- const cos = runtime.createCosClient
3000
- ? runtime.createCosClient({ session: options.session })
3001
- : await createDefaultCosClient(await getCDNUploadToken$1({
3002
- session: options.session,
3003
- bucket: uploadInfo.bucket,
3004
- keys: indexedFiles.map(({ key }) => key),
3005
- mimetypes: indexedFiles.map(({ file }) => file.mimeType),
3006
- isMultipartUpload: 0,
3007
- }, { baseUrl: runtime.baseUrl, fetchImpl }));
3008
- const queue = indexedFiles.slice();
3009
- let completed = 0;
3010
- const workers = Array.from({ length: Math.min(concurrency, indexedFiles.length) }, async () => {
3011
- while (queue.length > 0) {
3012
- const uploadTask = queue.shift();
3013
- if (!uploadTask) {
3014
- return;
3015
- }
3016
- try {
3017
- await cos.putObject({
3018
- Bucket: uploadInfo.bucket,
3019
- Region: uploadInfo.region,
3020
- Key: uploadTask.key,
3021
- Body: createReadStream(uploadTask.file.absolutePath),
3022
- ContentLength: uploadTask.file.size,
3023
- ContentType: uploadTask.file.mimeType,
3024
- });
3025
- completed += 1;
3026
- taskContext.update(`正在上传 ${completed}/${indexedFiles.length} 个文件`);
3027
- logger.debug(`[${String(completed).padStart(2)}/${indexedFiles.length}] ok ${uploadTask.file.relativePath} (${formatSize(uploadTask.file.size)})`);
3028
- }
3029
- catch (error) {
3030
- completed += 1;
3031
- const message = readErrorMessage(error);
3032
- logger.debug(`[${String(completed).padStart(2)}/${indexedFiles.length}] error ${uploadTask.file.relativePath} -> ${message}`);
3033
- throw new index.CliError(`上传文件失败:${uploadTask.file.relativePath} -> ${message}`, readVerboseUploadError(uploadTask.file.relativePath, error));
3034
- }
3035
- }
3071
+ }
3072
+ function validateUploadInfoKeys(files, keys, options) {
3073
+ if (keys.length !== files.length) {
3074
+ throw new index.CliError(`CDN 上传接口返回 key 数量异常:期望 ${files.length},实际 ${keys.length}`);
3075
+ }
3076
+ for (let index$1 = 0; index$1 < files.length; index$1 += 1) {
3077
+ const expectedKey = getMiniappUploadKey({
3078
+ miniProgramId: options.miniProgramId,
3079
+ version: options.version,
3080
+ relativePath: files[index$1].relativePath,
3036
3081
  });
3037
- await Promise.all(workers);
3038
- logger.debug('正在确认 CDN 上传结果');
3039
- await postCDNUploadCallback$1({ session: options.session, keys: uploadInfo.keys, isFinished: true }, { baseUrl: runtime.baseUrl, fetchImpl });
3040
- }, { successText: `已上传 ${orderedFiles.length} 个文件` });
3082
+ const actualKey = keys[index$1];
3083
+ if (actualKey !== expectedKey) {
3084
+ throw new index.CliError(`CDN 上传接口返回 key 异常:${files[index$1].relativePath}`, [`CDN 上传接口返回 key 异常:${files[index$1].relativePath}`, `expected: ${expectedKey}`, `actual: ${actualKey}`].join('\n'));
3085
+ }
3086
+ }
3041
3087
  }
3042
3088
  async function createDefaultCosClient(uploadToken) {
3043
3089
  const COS = await loadCosConstructor();
@@ -3067,7 +3113,7 @@ async function createDefaultCosClient(uploadToken) {
3067
3113
  };
3068
3114
  }
3069
3115
  async function loadCosConstructor() {
3070
- const cosModule = await Promise.resolve().then(function () { return require('./index-CLne_LW7.cjs'); }).then(function (n) { return n.index; });
3116
+ const cosModule = await Promise.resolve().then(function () { return require('./index-2nlBRew_.cjs'); }).then(function (n) { return n.index; });
3071
3117
  return cosModule.default;
3072
3118
  }
3073
3119
  function formatSize(bytes) {
@@ -3233,6 +3279,10 @@ async function runDeployCommand(options, runtime = {}) {
3233
3279
  if (pathError) {
3234
3280
  throw new Error(pathError);
3235
3281
  }
3282
+ const sizeError = validateUploadTotalSize(files);
3283
+ if (sizeError) {
3284
+ throw new Error(sizeError);
3285
+ }
3236
3286
  logger.debug(`待上传文件数: ${files.length}`);
3237
3287
  return files;
3238
3288
  }, { successText: 'dist 上传文件校验完成' });
@@ -3249,12 +3299,12 @@ async function runDeployCommand(options, runtime = {}) {
3249
3299
  catch (error) {
3250
3300
  throw translateHeyboxDeployError(error, { projectRoot, stage: 'submitAudit', version });
3251
3301
  }
3252
- printDeploySuccess({ autoPublish, logger, miniProgramId, mode: options.successOutputMode ?? 'legacy', previewUrl: submitAuditResult.preview_url, version });
3302
+ printDeploySuccess({ autoPublish, logger, miniProgramId, mode: options.successOutputMode ?? 'legacy', protocolString: submitAuditResult.protocol_string, version });
3253
3303
  return {
3254
3304
  autoPublish,
3255
3305
  changed: true,
3256
3306
  miniProgramId,
3257
- ...(submitAuditResult.preview_url ? { previewUrl: submitAuditResult.preview_url } : {}),
3307
+ ...(submitAuditResult.protocol_string ? { protocolString: submitAuditResult.protocol_string } : {}),
3258
3308
  version,
3259
3309
  };
3260
3310
  }
@@ -3262,8 +3312,8 @@ function printDeploySuccess(options) {
3262
3312
  options.logger.success(`提交审核成功:${options.miniProgramId} ${options.version}`);
3263
3313
  if (options.mode === 'remote') {
3264
3314
  options.logger.info(`发布策略:${options.autoPublish ? '审核通过后自动发布' : '审核通过后需手动发布'}`);
3265
- if (options.previewUrl) {
3266
- options.logger.info(`Preview URL: ${options.previewUrl}`);
3315
+ if (options.protocolString) {
3316
+ options.logger.info(`Protocol: ${options.protocolString}`);
3267
3317
  options.logger.info('允许他人预览:hb-sdk remote allowlist add <heybox_id>');
3268
3318
  }
3269
3319
  options.logger.info(options.autoPublish
@@ -3272,8 +3322,8 @@ function printDeploySuccess(options) {
3272
3322
  return;
3273
3323
  }
3274
3324
  options.logger.info(`发布策略:${options.autoPublish ? '审核通过后自动发布' : '审核通过后需在开放平台手动发布'}`);
3275
- if (options.previewUrl) {
3276
- options.logger.info(`Preview URL: ${options.previewUrl}`);
3325
+ if (options.protocolString) {
3326
+ options.logger.info(`Protocol: ${options.protocolString}`);
3277
3327
  }
3278
3328
  }
3279
3329
  function findProjectRoot(startDir) {
@@ -3840,14 +3890,14 @@ async function createRemoteCommandContext(options, runtime) {
3840
3890
  function adaptRemoteApiClient(api) {
3841
3891
  return {
3842
3892
  accessStatus: () => api.getDeveloperAccessStatus(),
3843
- listMiniPrograms: ({ keyword, status }) => api.listUserMiniprograms({ keyword, status, offset: 0, limit: 200 }),
3893
+ listMiniPrograms: ({ keyword, limit = 200, offset = 0, status }) => api.listUserMiniprograms({ keyword, status, offset, limit }),
3844
3894
  createMiniProgram: ({ name, previewImageUrl }) => api.createUserMiniprogram({ name, ...(previewImageUrl ? { preview_image_url: previewImageUrl } : {}) }),
3845
3895
  updateMiniProgram: ({ miniProgramId, name, previewImageUrl }) => api.updateUserMiniprogram({ mini_program_id: miniProgramId, name, preview_image_url: previewImageUrl }),
3846
3896
  getMiniProgramDetail: (miniProgramId) => api.getUserMiniprogramDetail({ mini_program_id: miniProgramId }),
3847
3897
  getAllowlist: (miniProgramId) => api.getPreviewAllowlist({ mini_program_id: miniProgramId }),
3848
3898
  updateAllowlist: ({ miniProgramId, heyboxIds }) => api.updatePreviewAllowlist({ mini_program_id: miniProgramId, heybox_ids: heyboxIds }),
3849
3899
  listVersions: (miniProgramId) => api.listUserMiniprogramVersions({ mini_program_id: miniProgramId, offset: 0, limit: 50 }),
3850
- getPreviewInfo: ({ miniProgramId, version }) => api.getVersionPreviewInfo({ mini_program_id: miniProgramId, preview_version: version }),
3900
+ getPreviewInfo: ({ miniProgramId, version }) => api.getVersionPreviewInfo({ mini_program_id: miniProgramId, version }),
3851
3901
  listDeveloperEntities: () => api.listDeveloperEntities(),
3852
3902
  switchDeveloperEntity: ({ entityId }) => api.switchDeveloperEntity({ entity_id: entityId }),
3853
3903
  releaseVersion: ({ miniProgramId, version }) => api.releaseUserMiniprogramVersion({ mini_program_id: miniProgramId, version }),
@@ -3871,9 +3921,9 @@ async function remoteAccess(options, context, runtime) {
3871
3921
  return finishRemoteCommand(options, runtime, output);
3872
3922
  }
3873
3923
  async function remoteEntityList(options, context, runtime) {
3874
- const result = await context.api.listDeveloperEntities();
3875
- const items = normalizeDeveloperEntities(result);
3876
- const output = { changed: false, items: toCamelCaseDeep(items), total: readNumber(result.total, items.length) };
3924
+ const result = await listDeveloperEntitiesWithAccessFallback(context);
3925
+ const items = result.items;
3926
+ const output = { changed: false, items: toCamelCaseDeep(items), total: result.total };
3877
3927
  if (!options.json) {
3878
3928
  if (items.length === 0) {
3879
3929
  context.logger.info('当前账号未绑定开发者主体');
@@ -3899,18 +3949,24 @@ async function remoteEntityCurrent(options, context, runtime) {
3899
3949
  }
3900
3950
  async function remoteEntitySwitch(options, context, runtime) {
3901
3951
  const entityId = parseEntityId(options.entityId);
3902
- const entities = normalizeDeveloperEntities(await context.api.listDeveloperEntities());
3952
+ const entityList = await listDeveloperEntitiesWithAccessFallback(context);
3953
+ const entities = entityList.items;
3903
3954
  const target = entities.find((entity) => entity.entity_id === entityId);
3904
3955
  if (!target) {
3905
3956
  throw new Error(`当前账号不可切换到开发者主体 ${entityId};请先运行 hb-sdk remote entity list 查看可用主体`);
3906
3957
  }
3907
- const result = await context.api.switchDeveloperEntity({ entityId });
3958
+ const result = entityList.fromAccessFallback === true && target.is_current === true ? target : await context.api.switchDeveloperEntity({ entityId });
3908
3959
  const access = await context.api.accessStatus();
3909
3960
  const switched = normalizeDeveloperEntity(result) ?? { ...target, is_current: true };
3961
+ const switchedEntityId = normalizeEntityId(switched.entity_id);
3962
+ if (switchedEntityId === undefined) {
3963
+ throw new Error('远端响应缺少 entity_id');
3964
+ }
3965
+ const switchedOwnerHeyboxId = normalizePositiveInteger(switched.owner_heybox_id);
3910
3966
  await session.setSelectedDeveloperEntitySnapshot({
3911
- entityId: switched.entity_id,
3967
+ entityId: switchedEntityId,
3912
3968
  entityName: switched.entity_name || '--',
3913
- ...(switched.owner_heybox_id !== undefined ? { ownerHeyboxId: switched.owner_heybox_id } : {}),
3969
+ ...(switchedOwnerHeyboxId !== undefined ? { ownerHeyboxId: switchedOwnerHeyboxId } : {}),
3914
3970
  }, {
3915
3971
  cacheFile: options.cacheFile,
3916
3972
  env: options.env,
@@ -4050,7 +4106,7 @@ async function remoteVersions(options, context, runtime) {
4050
4106
  const output = { changed: false, items: toCamelCaseDeep(items), miniProgramId, total: readNumber(result.total, items.length) };
4051
4107
  if (!options.json) {
4052
4108
  for (const item of items) {
4053
- context.logger.info(`${item.version ?? '--'} status=${item.status ?? '--'} preview=${item.preview_url ?? '--'} publish=${item.publish_url ?? '--'}`);
4109
+ context.logger.info(`${item.version ?? '--'} status=${item.status ?? '--'} auto_publish=${item.auto_publish === true ? 'yes' : 'no'}`);
4054
4110
  }
4055
4111
  if (items.length === 0) {
4056
4112
  context.logger.info('暂无远端版本');
@@ -4062,10 +4118,13 @@ async function remotePreview(options, context, runtime) {
4062
4118
  const miniProgramId = requireBinding(context);
4063
4119
  const version = requireNonEmpty(options.version, 'remote preview 必须传 <version>');
4064
4120
  const result = await context.api.getPreviewInfo({ miniProgramId, version });
4065
- const previewUrl = String(result.preview_url || result.publish_url || result.page_url || '');
4066
- const output = { changed: false, miniProgramId, previewUrl, version, preview: toCamelCaseDeep(result) };
4121
+ const protocolString = String(result.protocol_string || '');
4122
+ if (!protocolString) {
4123
+ throw new Error(`版本 ${version} 暂无 protocol_string,无法打开预览`);
4124
+ }
4125
+ const output = { changed: false, miniProgramId, protocolString, version, preview: toCamelCaseDeep(result) };
4067
4126
  if (!options.json) {
4068
- context.logger.info(previewUrl ? `Preview URL: ${previewUrl}` : `版本 ${version} 暂无 preview URL`);
4127
+ context.logger.info(`Protocol: ${protocolString}`);
4069
4128
  }
4070
4129
  return finishRemoteCommand(options, runtime, output);
4071
4130
  }
@@ -4140,7 +4199,7 @@ function requireBinding(context) {
4140
4199
  return context.binding;
4141
4200
  }
4142
4201
  async function requireCurrentDeveloperEntity(context) {
4143
- const entities = normalizeDeveloperEntities(await context.api.listDeveloperEntities());
4202
+ const entities = (await listDeveloperEntitiesWithAccessFallback(context)).items;
4144
4203
  const current = entities.find((entity) => entity.is_current === true);
4145
4204
  if (!current) {
4146
4205
  throw new Error('当前账号没有服务端 current 开发者主体;请先运行 hb-sdk remote entity list 查看可用主体,并用 hb-sdk remote entity switch <entity-id> 切换');
@@ -4148,7 +4207,7 @@ async function requireCurrentDeveloperEntity(context) {
4148
4207
  return current;
4149
4208
  }
4150
4209
  async function confirmCreateWithCurrentEntity(options, context, runtime) {
4151
- const entities = normalizeDeveloperEntities(await context.api.listDeveloperEntities());
4210
+ const entities = (await listDeveloperEntitiesWithAccessFallback(context)).items;
4152
4211
  if (entities.length <= 1) {
4153
4212
  return;
4154
4213
  }
@@ -4175,24 +4234,43 @@ async function confirmCreateWithCurrentEntity(options, context, runtime) {
4175
4234
  }
4176
4235
  }
4177
4236
  async function assertMiniProgramEntityMatchesCurrent(context, detail) {
4178
- if (!isValidEntityId(detail.entity_id)) {
4237
+ const detailEntityId = normalizeEntityId(detail.entity_id);
4238
+ if (detailEntityId === undefined) {
4179
4239
  return;
4180
4240
  }
4181
4241
  const current = await requireCurrentDeveloperEntity(context);
4182
- if (current.entity_id === detail.entity_id) {
4242
+ if (current.entity_id === detailEntityId) {
4183
4243
  return;
4184
4244
  }
4185
- throw new Error(createEntityMismatchMessage({ current, target: createEntityFromMiniProgramDetail(detail) }));
4245
+ throw new Error(createEntityMismatchMessage({ current, target: createEntityFromMiniProgramDetail({ ...detail, entity_id: detailEntityId }) }));
4186
4246
  }
4187
4247
  async function assertBoundMiniProgramEntityMatchesCurrent(context, miniProgramId) {
4188
4248
  const current = await requireCurrentDeveloperEntity(context);
4189
4249
  const detail = await context.api.getMiniProgramDetail(miniProgramId);
4190
4250
  requireMiniProgramId(detail);
4191
- if (!isValidEntityId(detail.entity_id)) {
4192
- throw new Error(`远端小程序 ${miniProgramId} 详情缺少 entity_id,无法校验当前主体一致性,已停止 deploy`);
4251
+ const detailEntityId = normalizeEntityId(detail.entity_id);
4252
+ if (detailEntityId === undefined) {
4253
+ const listed = await findMiniProgramInCurrentEntityList(context, miniProgramId);
4254
+ if (listed) {
4255
+ return;
4256
+ }
4257
+ throw new Error(`远端小程序 ${miniProgramId} 详情缺少 entity_id,且当前主体列表中找不到该小程序,无法校验当前主体一致性,已停止 deploy`);
4258
+ }
4259
+ if (current.entity_id !== detailEntityId) {
4260
+ throw new Error(createEntityMismatchMessage({ current, target: createEntityFromMiniProgramDetail({ ...detail, entity_id: detailEntityId }) }));
4193
4261
  }
4194
- if (current.entity_id !== detail.entity_id) {
4195
- throw new Error(createEntityMismatchMessage({ current, target: createEntityFromMiniProgramDetail(detail) }));
4262
+ }
4263
+ async function findMiniProgramInCurrentEntityList(context, miniProgramId) {
4264
+ const pageSize = 200;
4265
+ for (let offset = 0;; offset += pageSize) {
4266
+ const result = await context.api.listMiniPrograms({ limit: pageSize, offset });
4267
+ const items = Array.isArray(result.items) ? result.items : [];
4268
+ if (items.some((item) => item.mini_program_id === miniProgramId)) {
4269
+ return true;
4270
+ }
4271
+ if (items.length < pageSize || offset + items.length >= readNumber(result.total, Number.POSITIVE_INFINITY)) {
4272
+ return false;
4273
+ }
4196
4274
  }
4197
4275
  }
4198
4276
  function createEntityMismatchMessage(options) {
@@ -4221,6 +4299,18 @@ function parseEntityId(value) {
4221
4299
  }
4222
4300
  return entityId;
4223
4301
  }
4302
+ async function listDeveloperEntitiesWithAccessFallback(context) {
4303
+ const result = await context.api.listDeveloperEntities();
4304
+ const items = normalizeDeveloperEntities(result);
4305
+ if (items.length > 0) {
4306
+ return { items, total: readNumber(result.total, items.length) };
4307
+ }
4308
+ const fallback = createEntityFromAccessStatus(await context.api.accessStatus());
4309
+ if (!fallback) {
4310
+ return { items, total: readNumber(result.total, items.length) };
4311
+ }
4312
+ return { fromAccessFallback: true, items: [fallback], total: 1 };
4313
+ }
4224
4314
  function normalizeDeveloperEntities(result) {
4225
4315
  const rawItems = Array.isArray(result.items) ? result.items : [];
4226
4316
  return rawItems.map((item) => normalizeDeveloperEntity(item)).filter((item) => item !== undefined);
@@ -4229,31 +4319,57 @@ function normalizeDeveloperEntity(value) {
4229
4319
  if (!value || typeof value !== 'object') {
4230
4320
  return undefined;
4231
4321
  }
4232
- const rawEntityId = value.entity_id;
4233
- if (!isValidEntityId(rawEntityId)) {
4322
+ const entityId = normalizeEntityId(value.entity_id);
4323
+ if (entityId === undefined) {
4234
4324
  return undefined;
4235
4325
  }
4326
+ const ownerHeyboxId = normalizePositiveInteger(value.owner_heybox_id);
4236
4327
  return {
4237
4328
  ...value,
4238
- entity_id: rawEntityId,
4329
+ entity_id: entityId,
4239
4330
  entity_name: typeof value.entity_name === 'string' ? value.entity_name : undefined,
4240
- owner_heybox_id: isValidHeyboxIdNumber(value.owner_heybox_id)
4241
- ? value.owner_heybox_id
4242
- : undefined,
4331
+ owner_heybox_id: ownerHeyboxId,
4243
4332
  is_current: value.is_current === true,
4244
4333
  };
4245
4334
  }
4246
4335
  function createEntityFromMiniProgramDetail(detail) {
4336
+ const entityId = normalizeEntityId(detail.entity_id);
4247
4337
  return {
4248
- entity_id: detail.entity_id,
4338
+ ...(entityId !== undefined ? { entity_id: entityId } : {}),
4249
4339
  entity_name: typeof detail.developer_name === 'string' ? detail.developer_name : undefined,
4250
- owner_heybox_id: detail.owner_heybox_id,
4340
+ owner_heybox_id: normalizePositiveInteger(detail.owner_heybox_id),
4251
4341
  is_current: false,
4252
4342
  };
4253
4343
  }
4344
+ function createEntityFromAccessStatus(access) {
4345
+ const entity = normalizeDeveloperEntity({
4346
+ entity_id: access.entity_id,
4347
+ entity_name: access.developer_name,
4348
+ owner_heybox_id: access.owner_heybox_id,
4349
+ is_current: true,
4350
+ });
4351
+ return entity;
4352
+ }
4254
4353
  function isValidEntityId(value) {
4255
4354
  return Number.isSafeInteger(value) && Number(value) > 0;
4256
4355
  }
4356
+ function normalizeEntityId(value) {
4357
+ return normalizePositiveInteger(value);
4358
+ }
4359
+ function normalizePositiveInteger(value) {
4360
+ if (typeof value === 'number') {
4361
+ return isValidEntityId(value) ? value : undefined;
4362
+ }
4363
+ if (typeof value !== 'string') {
4364
+ return undefined;
4365
+ }
4366
+ const normalized = value.trim();
4367
+ if (!/^[1-9]\d*$/.test(normalized)) {
4368
+ return undefined;
4369
+ }
4370
+ const parsed = Number(normalized);
4371
+ return isValidEntityId(parsed) ? parsed : undefined;
4372
+ }
4257
4373
  function requireNonEmpty(value, message) {
4258
4374
  const normalized = typeof value === 'string' ? value.trim() : '';
4259
4375
  if (!normalized) {
@@ -4377,8 +4493,11 @@ function printMiniProgramDetail(logger, detail) {
4377
4493
  logger.info(`Name: ${detail.name ?? '--'}`);
4378
4494
  logger.info(`Status: ${detail.status ?? '--'}`);
4379
4495
  logger.info(`Version: ${detail.version || detail.latest_version_status || '--'}`);
4380
- if (detail.publish_url) {
4381
- logger.info(`Publish URL: ${detail.publish_url}`);
4496
+ if (detail.page_url) {
4497
+ logger.info(`Page URL: ${detail.page_url}`);
4498
+ }
4499
+ if (detail.protocol_string) {
4500
+ logger.info(`Protocol: ${detail.protocol_string}`);
4382
4501
  }
4383
4502
  }
4384
4503
  function printDeveloperEntities(logger, entities) {
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./index-DuwxUSkq.cjs');
3
+ var index = require('./index-13_8m0Pw.cjs');
4
4
  var node_crypto = require('node:crypto');
5
5
  var path = require('node:path');
6
6
  var require$$0$2 = require('fs');
package/dist/cli.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./cli-chunks/index-DuwxUSkq.cjs');
3
+ var index = require('./cli-chunks/index-13_8m0Pw.cjs');
4
4
  require('node:module');
5
5
  require('node:fs');
6
6
  require('node:fs/promises');
@@ -3686,14 +3686,14 @@ function readScreenshotRect(value) {
3686
3686
  }
3687
3687
 
3688
3688
  /** 展示基础分享面板。 */
3689
- function showMiniProgramRuntimeShareMenu(payload, platformAdapter) {
3690
- return platformAdapter.share.showShareMenu(readMiniProgramShareMenuOptions(payload));
3689
+ function showMiniProgramRuntimeShareMenu(payload, platformAdapter, context = {}) {
3690
+ return platformAdapter.share.showShareMenu(readMiniProgramShareMenuOptions(payload, platformAdapter, context));
3691
3691
  }
3692
- function readMiniProgramShareMenuOptions(payload) {
3692
+ function readMiniProgramShareMenuOptions(payload, platformAdapter, context) {
3693
3693
  assertMiniProgramRuntimeRecord(payload, 'share.showShareMenu 参数必须是对象');
3694
3694
  const title = readMiniProgramRuntimeRequiredString(payload.title, 'share.showShareMenu title 必须是非空字符串');
3695
3695
  const desc = readMiniProgramRuntimeRequiredString(payload.desc, 'share.showShareMenu desc 必须是非空字符串');
3696
- const url = readMiniProgramRuntimeHttpUrl(payload.url, 'share.showShareMenu url 必须是 HTTP(S) URL');
3696
+ const url = readMiniProgramShareUrl(payload.url, platformAdapter, context);
3697
3697
  const imageUrl = payload.imageUrl === undefined
3698
3698
  ? undefined
3699
3699
  : readMiniProgramRuntimeHttpUrl(payload.imageUrl, 'share.showShareMenu imageUrl 必须是 HTTP(S) URL');
@@ -3709,6 +3709,33 @@ function readMiniProgramShareMenuOptions(payload) {
3709
3709
  channel,
3710
3710
  };
3711
3711
  }
3712
+ function readMiniProgramShareUrl(value, platformAdapter, context) {
3713
+ if (typeof value === 'string' && value.trim()) {
3714
+ return readMiniProgramRuntimeHttpUrl(value, 'share.showShareMenu url 必须是 HTTP(S) URL');
3715
+ }
3716
+ if (value !== undefined && value !== null && value !== '') {
3717
+ throw createMiniProgramRuntimeBridgeError('INVALID_PARAMS', 'share.showShareMenu url 必须是 HTTP(S) URL');
3718
+ }
3719
+ return createUserMiniprogramShareUrl(platformAdapter, context);
3720
+ }
3721
+ function createUserMiniprogramShareUrl(platformAdapter, context) {
3722
+ const miniProgramId = context.miniProgramId?.trim();
3723
+ if (!miniProgramId) {
3724
+ throw createMiniProgramRuntimeBridgeError('INVALID_PARAMS', 'share.showShareMenu url 必须是 HTTP(S) URL');
3725
+ }
3726
+ try {
3727
+ const shareUrl = new URL('/tools/common_share', platformAdapter.app.getCurrentHref());
3728
+ shareUrl.searchParams.set('user_miniprogram_id', miniProgramId);
3729
+ const miniProgramVersion = context.miniProgramVersion?.trim();
3730
+ if (miniProgramVersion) {
3731
+ shareUrl.searchParams.set('preview_version', miniProgramVersion);
3732
+ }
3733
+ return shareUrl.href;
3734
+ }
3735
+ catch {
3736
+ throw createMiniProgramRuntimeBridgeError('INVALID_PARAMS', 'share.showShareMenu url 必须是 HTTP(S) URL');
3737
+ }
3738
+ }
3712
3739
  function isShareChannel(value) {
3713
3740
  return (value === 'wechatSession' ||
3714
3741
  value === 'wechatTimeline' ||
@@ -2811,6 +2811,8 @@ function getMiniappManifestVersionError(version) {
2811
2811
 
2812
2812
  const MINIAPP_UPLOAD_SCOPE = 'activity';
2813
2813
  const ACTIVITY_UPLOAD_KEY_MAX_LENGTH = 64;
2814
+ const MINIAPP_UPLOAD_BATCH_SIZE = 50;
2815
+ const MINIAPP_UPLOAD_TOTAL_SIZE_LIMIT_BYTES = 100 * 1024 * 1024;
2814
2816
  const USER_MINIPROGRAM_ACCESS_STATUS_API_PATH = '/mall/developer/user_miniprogram/access_status';
2815
2817
  const DEVELOPER_ENTITY_LIST_API_PATH = '/mall/developer/entity/list';
2816
2818
  const DEVELOPER_ENTITY_SWITCH_API_PATH = '/mall/developer/entity/switch';
@@ -2869,6 +2871,17 @@ function validateUploadPaths(items, options) {
2869
2871
  }
2870
2872
  return undefined;
2871
2873
  }
2874
+ function validateUploadTotalSize(items, options = {}) {
2875
+ const limit = options.limitBytes ?? MINIAPP_UPLOAD_TOTAL_SIZE_LIMIT_BYTES;
2876
+ const total = items.reduce((sum, item) => {
2877
+ const size = Number(item.size);
2878
+ return sum + (Number.isFinite(size) && size > 0 ? size : 0);
2879
+ }, 0);
2880
+ if (total > limit) {
2881
+ return '构建产物总大小超过 100MB,请删除未引用资源或压缩图片后重新 deploy';
2882
+ }
2883
+ return undefined;
2884
+ }
2872
2885
  function shouldUploadDistFile(relativePath) {
2873
2886
  const normalized = normalizeRelativePath(relativePath);
2874
2887
  if (!normalized) {
@@ -2893,7 +2906,9 @@ exports.DEVELOPER_ENTITY_LIST_API_PATH = DEVELOPER_ENTITY_LIST_API_PATH;
2893
2906
  exports.DEVELOPER_ENTITY_SWITCH_API_PATH = DEVELOPER_ENTITY_SWITCH_API_PATH;
2894
2907
  exports.LIST_USER_MINIPROGRAM_API_PATH = LIST_USER_MINIPROGRAM_API_PATH;
2895
2908
  exports.LIST_USER_MINIPROGRAM_VERSION_API_PATH = LIST_USER_MINIPROGRAM_VERSION_API_PATH;
2909
+ exports.MINIAPP_UPLOAD_BATCH_SIZE = MINIAPP_UPLOAD_BATCH_SIZE;
2896
2910
  exports.MINIAPP_UPLOAD_SCOPE = MINIAPP_UPLOAD_SCOPE;
2911
+ exports.MINIAPP_UPLOAD_TOTAL_SIZE_LIMIT_BYTES = MINIAPP_UPLOAD_TOTAL_SIZE_LIMIT_BYTES;
2897
2912
  exports.PRECHECK_USER_MINIPROGRAM_VERSION_API_PATH = PRECHECK_USER_MINIPROGRAM_VERSION_API_PATH;
2898
2913
  exports.RELEASE_USER_MINIPROGRAM_VERSION_API_PATH = RELEASE_USER_MINIPROGRAM_VERSION_API_PATH;
2899
2914
  exports.REOPEN_USER_MINIPROGRAM_API_PATH = REOPEN_USER_MINIPROGRAM_API_PATH;
@@ -2912,3 +2927,4 @@ exports.normalizeRelativePath = normalizeRelativePath;
2912
2927
  exports.relativePathContainsNodeModulesSegment = relativePathContainsNodeModulesSegment;
2913
2928
  exports.shouldUploadDistFile = shouldUploadDistFile;
2914
2929
  exports.validateUploadPaths = validateUploadPaths;
2930
+ exports.validateUploadTotalSize = validateUploadTotalSize;
@@ -2809,6 +2809,8 @@ function getMiniappManifestVersionError(version) {
2809
2809
 
2810
2810
  const MINIAPP_UPLOAD_SCOPE = 'activity';
2811
2811
  const ACTIVITY_UPLOAD_KEY_MAX_LENGTH = 64;
2812
+ const MINIAPP_UPLOAD_BATCH_SIZE = 50;
2813
+ const MINIAPP_UPLOAD_TOTAL_SIZE_LIMIT_BYTES = 100 * 1024 * 1024;
2812
2814
  const USER_MINIPROGRAM_ACCESS_STATUS_API_PATH = '/mall/developer/user_miniprogram/access_status';
2813
2815
  const DEVELOPER_ENTITY_LIST_API_PATH = '/mall/developer/entity/list';
2814
2816
  const DEVELOPER_ENTITY_SWITCH_API_PATH = '/mall/developer/entity/switch';
@@ -2867,6 +2869,17 @@ function validateUploadPaths(items, options) {
2867
2869
  }
2868
2870
  return undefined;
2869
2871
  }
2872
+ function validateUploadTotalSize(items, options = {}) {
2873
+ const limit = options.limitBytes ?? MINIAPP_UPLOAD_TOTAL_SIZE_LIMIT_BYTES;
2874
+ const total = items.reduce((sum, item) => {
2875
+ const size = Number(item.size);
2876
+ return sum + (Number.isFinite(size) && size > 0 ? size : 0);
2877
+ }, 0);
2878
+ if (total > limit) {
2879
+ return '构建产物总大小超过 100MB,请删除未引用资源或压缩图片后重新 deploy';
2880
+ }
2881
+ return undefined;
2882
+ }
2870
2883
  function shouldUploadDistFile(relativePath) {
2871
2884
  const normalized = normalizeRelativePath(relativePath);
2872
2885
  if (!normalized) {
@@ -2884,4 +2897,4 @@ function shouldUploadDistFile(relativePath) {
2884
2897
  return true;
2885
2898
  }
2886
2899
 
2887
- export { ACTIVITY_UPLOAD_KEY_MAX_LENGTH, CREATE_USER_MINIPROGRAM_API_PATH, DETAIL_USER_MINIPROGRAM_API_PATH, DEVELOPER_ENTITY_LIST_API_PATH, DEVELOPER_ENTITY_SWITCH_API_PATH, LIST_USER_MINIPROGRAM_API_PATH, LIST_USER_MINIPROGRAM_VERSION_API_PATH, MINIAPP_UPLOAD_SCOPE, PRECHECK_USER_MINIPROGRAM_VERSION_API_PATH, RELEASE_USER_MINIPROGRAM_VERSION_API_PATH, REOPEN_USER_MINIPROGRAM_API_PATH, SUBMIT_USER_MINIPROGRAM_AUDIT_API_PATH, TAKE_DOWN_USER_MINIPROGRAM_API_PATH, UPDATE_USER_MINIPROGRAM_API_PATH, UPDATE_USER_MINIPROGRAM_PREVIEW_ALLOWLIST_API_PATH, USER_MINIPROGRAM_ACCESS_STATUS_API_PATH, USER_MINIPROGRAM_PREVIEW_ALLOWLIST_API_PATH, USER_MINIPROGRAM_VERSION_PREVIEW_INFO_API_PATH, WITHDRAW_USER_MINIPROGRAM_VERSION_API_PATH, getMiniProgramUploadAlias, getMiniappUploadKey, isValidMiniappManifestVersion as isValidVersion, normalizeRelativePath, relativePathContainsNodeModulesSegment, shouldUploadDistFile, validateUploadPaths };
2900
+ export { ACTIVITY_UPLOAD_KEY_MAX_LENGTH, CREATE_USER_MINIPROGRAM_API_PATH, DETAIL_USER_MINIPROGRAM_API_PATH, DEVELOPER_ENTITY_LIST_API_PATH, DEVELOPER_ENTITY_SWITCH_API_PATH, LIST_USER_MINIPROGRAM_API_PATH, LIST_USER_MINIPROGRAM_VERSION_API_PATH, MINIAPP_UPLOAD_BATCH_SIZE, MINIAPP_UPLOAD_SCOPE, MINIAPP_UPLOAD_TOTAL_SIZE_LIMIT_BYTES, PRECHECK_USER_MINIPROGRAM_VERSION_API_PATH, RELEASE_USER_MINIPROGRAM_VERSION_API_PATH, REOPEN_USER_MINIPROGRAM_API_PATH, SUBMIT_USER_MINIPROGRAM_AUDIT_API_PATH, TAKE_DOWN_USER_MINIPROGRAM_API_PATH, UPDATE_USER_MINIPROGRAM_API_PATH, UPDATE_USER_MINIPROGRAM_PREVIEW_ALLOWLIST_API_PATH, USER_MINIPROGRAM_ACCESS_STATUS_API_PATH, USER_MINIPROGRAM_PREVIEW_ALLOWLIST_API_PATH, USER_MINIPROGRAM_VERSION_PREVIEW_INFO_API_PATH, WITHDRAW_USER_MINIPROGRAM_VERSION_API_PATH, getMiniProgramUploadAlias, getMiniappUploadKey, isValidMiniappManifestVersion as isValidVersion, normalizeRelativePath, relativePathContainsNodeModulesSegment, shouldUploadDistFile, validateUploadPaths, validateUploadTotalSize };
@@ -33,7 +33,6 @@ import hbSDK from '@heybox/hb-sdk';
33
33
  await hbSDK.share.showShareMenu({
34
34
  title: '<%= projectName %>',
35
35
  desc: '分享描述',
36
- url: window.location.href,
37
36
  });
38
37
 
39
38
  await hbSDK.storage.setStorage({
@@ -46,3 +45,5 @@ const response = await hbSDK.network.request({
46
45
  url: 'https://jsonplaceholder.typicode.com/todos/1',
47
46
  });
48
47
  ```
48
+
49
+ 用户小程序通常不要传 `url`;宿主 runtime 会自动生成当前小程序的通用分享页。只有确实要分享外部 HTTP(S) 页面时才显式传 `url`。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heybox/hb-sdk",
3
- "version": "0.4.6",
3
+ "version": "0.4.7",
4
4
  "description": "",
5
5
  "exports": {
6
6
  ".": {
package/skill/SKILL.md CHANGED
@@ -52,7 +52,7 @@ Apply these instructions when writing, reviewing, or debugging code that consume
52
52
  4. Use `hb-sdk remote entity list`, `hb-sdk remote entity current`, and `hb-sdk remote entity switch <entity-id>` to inspect or change the developer platform server-side current entity before remote management commands.
53
53
  5. Use `hb-sdk remote create --name <name>` to create a remote mini-program under the server-side current entity and bind the returned id into `package.json.heybox.miniProgramId`; use `hb-sdk remote bind <mini-program-id>` to bind an existing remote mini-program after current-entity manageability is verified.
54
54
  6. Use `hb-sdk remote info`, `hb-sdk remote update`, `hb-sdk remote list`, `hb-sdk remote access`, `hb-sdk remote versions`, `hb-sdk remote preview <version>`, and `hb-sdk remote allowlist ...` for read/basic/preview management workflows. Discovery commands show the server-side current entity scope; write commands target the current project binding rather than arbitrary ids.
55
- 7. Use `hb-sdk remote deploy --release-note <text>` to build, upload, and submit the current project for audit. It reads `package.json.heybox.miniProgramId`, validates that the bound mini-program belongs to the server-side current entity before precheck/build/upload, prechecks `package.json.version` before build, runs the project's `build` script via the package manager auto-detected by lockfile, uploads `dist/` to CDN (skipping `manifest.json`, `.DS_Store`, `.map`), then calls the submit-audit API.
55
+ 7. Use `hb-sdk remote deploy --release-note <text>` to build, upload, and submit the current project for audit. It reads `package.json.heybox.miniProgramId`, validates that the bound mini-program belongs to the server-side current entity before precheck/build/upload, prechecks `package.json.version` before build, runs the project's `build` script via the package manager auto-detected by lockfile, uploads `dist/` to CDN (skipping `manifest.json`, `.DS_Store`, `.map`), then calls the submit-audit API. The deploy upload pipeline rejects actual upload artifacts over 100MiB before any upload request, processes CDN upload metadata and callback confirmation at 50 files per batch, keeps 4-way upload concurrency within each batch, and validates CDN-returned keys exactly against the local expected upload keys.
56
56
  8. Use `hb-sdk remote deploy --skip-build --release-note <text>` only when the `dist/` directory is already prepared by an upstream CI stage. In this mode the CLI reads `dist/manifest.json.version` before precheck. Missing `dist/manifest.json` or `dist/index.html` aborts the run.
57
57
  9. Always provide a concise release note before deploy. Non-interactive environments must pass `--release-note`; interactive terminals may prompt for it. The default is manual release after approval with `hb-sdk remote release <version>`; use `--auto-publish` when the audited version should automatically release after approval.
58
58
  10. Use `hb-sdk remote release <version>`, `hb-sdk remote withdraw <version>`, `hb-sdk remote take-down`, and `hb-sdk remote reopen` for dangerous remote changes. Interactive terminals should confirm after showing enough context; non-interactive environments must pass `--yes`.
@@ -19,7 +19,7 @@
19
19
  ## Package metadata
20
20
 
21
21
  - Package: `@heybox/hb-sdk`
22
- - Version at generation time: `0.4.6`
22
+ - Version at generation time: `0.4.7`
23
23
  - Public root export: `@heybox/hb-sdk`
24
24
  - Protocol export: `@heybox/hb-sdk/protocol`
25
25
  - Vite plugin export: `@heybox/hb-sdk/vite`
@@ -341,11 +341,14 @@ import { share } from '@heybox/hb-sdk';
341
341
  await share.showShareMenu({
342
342
  title: '我的小程序页面',
343
343
  desc: '来自黑盒小程序的分享',
344
- url: window.location.href,
345
344
  imageUrl: 'https://imgheybox.max-c.com/demo.png',
346
345
  });
347
346
  ```
348
347
 
348
+ 用户小程序通常不要传 `url`;宿主 runtime 会按当前小程序生成
349
+ `/tools/common_share?user_miniprogram_id=...` 分享落地页。只有确实要分享外部
350
+ HTTP(S) 页面时才显式传 `url`。
351
+
349
352
  截图分享:
350
353
 
351
354
  ```ts
@@ -147,8 +147,10 @@ HB_SDK_ALLOW_UNSAFE_API_BASE_URL=1 hb-sdk remote deploy --api-base-url http://12
147
147
  7. 解析 `dist/manifest.json`,自动剥离 BOM,并校验 `version` 是合法 SemVer:允许 prerelease,例如 `1.2.3-rc.1`;拒绝 build metadata,例如 `1.2.3+build.1`;拒绝 `0.0.0`。
148
148
  8. 普通 remote deploy 的 `dist/manifest.json.version` 必须与预检使用的 `package.json.version` 一致,否则失败且不上传。
149
149
  9. 遍历 `dist/` 文件,过滤掉 `manifest.json`、`.DS_Store`、`*.map`;遇到 symbolic link 或 `node_modules` 路径直接报错。
150
- 10. 4 并发把文件上传到 CDN,默认只展示上传阶段和文件总数;`--verbose` 会展示并发数、bucket / region 和逐文件结果,但不会输出 keys、签名、cookie 或 token。
151
- 11. 全部上传成功后调用提交审核接口,CLI 输出提交审核成功、发布策略和可用的 preview URL。
150
+ 10. 校验上传路径长度不超过 64,并在任何上传请求发生前限制实际上传产物总大小不超过 100MiB。错误提示中使用 `100MB`,方便开发者理解。
151
+ 11. 上传信息、上传凭证和上传回调按批次执行,每批最多 50 个文件;批次串行,批内保持 4 并发上传到 CDN。CLI 会校验 CDN 上传信息接口返回的 key 与本地期望 key 完全一致,异常时停止后续批次且不提交审核。
152
+ 12. 默认只展示上传阶段和文件总数,例如 `正在上传 137/244 个文件`;`--verbose` 会展示并发数、批次数、当前批次、bucket / region 和逐文件结果,但不会输出 keys、签名、cookie、pkey、token 或临时密钥。
153
+ 13. 全部上传成功后调用提交审核接口,CLI 输出提交审核成功、发布策略和可用的 preview URL。
152
154
 
153
155
  `mini_program_id` 没有 CLI flag,必须落在 `package.json` 里:
154
156
 
@@ -367,7 +369,6 @@ import hbSDK from '@heybox/hb-sdk';
367
369
  await hbSDK.share.showShareMenu({
368
370
  title: '<%= projectName %>',
369
371
  desc: '分享描述',
370
- url: window.location.href,
371
372
  });
372
373
 
373
374
  await hbSDK.storage.setStorage({
@@ -380,4 +381,6 @@ const response = await hbSDK.network.request({
380
381
  url: 'https://jsonplaceholder.typicode.com/todos/1',
381
382
  });
382
383
  ```
384
+
385
+ 用户小程序通常不要传 `url`;宿主 runtime 会自动生成当前小程序的通用分享页。只有确实要分享外部 HTTP(S) 页面时才显式传 `url`。
383
386
  ````
package/skill/skill.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "hb-sdk",
3
- "skillVersion": "0.4.6+skill.1c7149a95a0b",
3
+ "skillVersion": "0.4.7+skill.c42e9bccd99b",
4
4
  "sdk": {
5
5
  "package": "@heybox/hb-sdk",
6
- "version": "0.4.6",
7
- "compatibility": "0.4.6"
6
+ "version": "0.4.7",
7
+ "compatibility": "0.4.7"
8
8
  },
9
9
  "source": "https://open.xiaoheihe.cn/agent-skills/hb-sdk",
10
- "integrity": "sha256-1c7149a95a0b5d0ffeaa479456186dab5f3bc06865fd5c28a975682381267811"
10
+ "integrity": "sha256-c42e9bccd99b7a9f6119a18ad0bd8bd9040176bdcc99134b0c6f29aa19e9aa59"
11
11
  }
@@ -1,5 +1,7 @@
1
1
  export declare const MINIAPP_UPLOAD_SCOPE = "activity";
2
2
  export declare const ACTIVITY_UPLOAD_KEY_MAX_LENGTH = 64;
3
+ export declare const MINIAPP_UPLOAD_BATCH_SIZE = 50;
4
+ export declare const MINIAPP_UPLOAD_TOTAL_SIZE_LIMIT_BYTES: number;
3
5
  export declare const USER_MINIPROGRAM_ACCESS_STATUS_API_PATH = "/mall/developer/user_miniprogram/access_status";
4
6
  export declare const DEVELOPER_ENTITY_LIST_API_PATH = "/mall/developer/entity/list";
5
7
  export declare const DEVELOPER_ENTITY_SWITCH_API_PATH = "/mall/developer/entity/switch";
@@ -36,4 +38,12 @@ export interface ValidateUploadPathsOptions {
36
38
  maxLength?: number;
37
39
  }
38
40
  export declare function validateUploadPaths(items: UploadPathItem[], options: ValidateUploadPathsOptions): string | undefined;
41
+ export interface UploadSizeItem {
42
+ relativePath: string;
43
+ size: number;
44
+ }
45
+ export interface ValidateUploadTotalSizeOptions {
46
+ limitBytes?: number;
47
+ }
48
+ export declare function validateUploadTotalSize(items: UploadSizeItem[], options?: ValidateUploadTotalSizeOptions): "构建产物总大小超过 100MB,请删除未引用资源或压缩图片后重新 deploy" | undefined;
39
49
  export declare function shouldUploadDistFile(relativePath: string): boolean;
@@ -16,8 +16,8 @@ export interface MiniProgramShowShareMenuOptions {
16
16
  title: string;
17
17
  /** 分享描述。 */
18
18
  desc: string;
19
- /** 分享落地页 URL */
20
- url: string;
19
+ /** 分享落地页 URL;不传时由宿主 runtime 生成当前小程序的通用分享页。 */
20
+ url?: string;
21
21
  /** 分享缩略图 URL。 */
22
22
  imageUrl?: string;
23
23
  /** 指定分享渠道;不传则由客户端展示默认分享面板。 */