@heybox/hb-sdk 0.6.8 → 0.6.9-alpha.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.
Files changed (26) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/cli-chunks/{build-D4crgajb.cjs → build-9hbhrRbU.cjs} +4 -4
  3. package/dist/cli-chunks/{context-fRWWSXDj.cjs → context-7rmakzS_.cjs} +3 -3
  4. package/dist/cli-chunks/{create-BU_EFgDz.cjs → create-BkX4rtRP.cjs} +1 -1
  5. package/dist/cli-chunks/{dev-BrkChQ7v.cjs → dev-DiZGT4FQ.cjs} +6 -6
  6. package/dist/cli-chunks/{doctor-DZXVO4rL.cjs → doctor-BcpqXVRh.cjs} +1 -1
  7. package/dist/cli-chunks/{index-DxH41nsb.cjs → index-6HfcwZ_r.cjs} +2 -2
  8. package/dist/cli-chunks/{index-CSjJrnkO.cjs → index-DofTxdoX.cjs} +14 -14
  9. package/dist/cli-chunks/{login-DLVozxrs.cjs → login-DUiejZcD.cjs} +2 -2
  10. package/dist/cli-chunks/{project-vite-CwmMfCk3.cjs → project-vite-CfD8Bp1K.cjs} +1 -1
  11. package/dist/cli-chunks/{remote-CfpkkC9I.cjs → remote-A4-PKgOn.cjs} +456 -202
  12. package/dist/cli-chunks/{runtime-gate-CgN_v4Te.cjs → runtime-gate-BWlU-R4h.cjs} +1 -1
  13. package/dist/cli-chunks/{runtime-permission-env-B0jK9Rt1.cjs → runtime-permission-env-DKrhgVM3.cjs} +205 -49
  14. package/dist/cli-chunks/{session-DB7ARwm4.cjs → session-D5u3XzHN.cjs} +1 -1
  15. package/dist/cli.cjs +1 -1
  16. package/dist/index.cjs.js +1 -1
  17. package/dist/index.esm.js +1 -1
  18. package/dist/miniapp-publish.cjs.js +205 -0
  19. package/dist/miniapp-publish.esm.js +201 -1
  20. package/dist/vite.cjs.js +1 -1
  21. package/dist/vite.esm.js +1 -1
  22. package/package.json +2 -2
  23. package/skill/references/api-root.md +1 -1
  24. package/skill/references/cli.md +8 -0
  25. package/skill/skill.json +4 -4
  26. package/types/miniapp-publish/index.d.ts +77 -0
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var runtimePermissionEnv = require('./runtime-permission-env-B0jK9Rt1.cjs');
3
+ var runtimePermissionEnv = require('./runtime-permission-env-DKrhgVM3.cjs');
4
4
 
5
5
  function isValidMiniappManifestVersion(version) {
6
6
  return getMiniappManifestVersionError(version) === undefined;
@@ -2814,6 +2814,10 @@ const UPDATE_SQUARE_DISPLAY_API_PATH = '/mall/developer/user_miniprogram/square_
2814
2814
  const LIST_USER_MINIPROGRAM_VERSION_API_PATH = '/mall/developer/user_miniprogram/version/list';
2815
2815
  const FNV_OFFSET = 0x811c9dc5;
2816
2816
  const FNV_PRIME = 0x01000193;
2817
+ const MINIAPP_UPLOAD_SESSION_REF_LENGTH = 10;
2818
+ const MINIAPP_ARTIFACT_SIGNATURE_MAGIC = 'heybox-user-miniprogram-artifact\0v1\0';
2819
+ const UNPAIRED_SURROGATE_PATTERN = /[\ud800-\udbff](?![\udc00-\udfff])|(^|[^\ud800-\udbff])[\udc00-\udfff]/;
2820
+ const textEncoder = new TextEncoder();
2817
2821
  function normalizeRelativePath(relativePath) {
2818
2822
  return String(relativePath || '')
2819
2823
  .replace(/\\/g, '/')
@@ -2834,52 +2838,6 @@ function getMiniProgramUploadAlias(miniProgramId) {
2834
2838
  }
2835
2839
  return hash.toString(36);
2836
2840
  }
2837
- function getMiniappUploadKey(input) {
2838
- const alias = getMiniProgramUploadAlias(input.miniProgramId);
2839
- const normalized = normalizeRelativePath(input.relativePath);
2840
- return `/u/${alias}/${input.version}/${normalized}`;
2841
- }
2842
- function validateUploadPaths(items, options) {
2843
- const limit = options.maxLength;
2844
- const uploadKeys = new Set();
2845
- for (const item of items) {
2846
- const relativePath = String(item.relativePath ?? '');
2847
- const normalizedRelativePath = normalizeRelativePath(relativePath);
2848
- const invalidReason = getInvalidUploadPathReason(normalizedRelativePath);
2849
- if (invalidReason) {
2850
- return `构建产物文件路径不合法(${invalidReason}):${JSON.stringify(relativePath)}`;
2851
- }
2852
- const key = getMiniappUploadKey({
2853
- miniProgramId: options.miniProgramId,
2854
- version: options.version,
2855
- relativePath: normalizedRelativePath,
2856
- });
2857
- if (uploadKeys.has(key)) {
2858
- return `构建产物文件路径重复,无法上传到同一位置:${JSON.stringify(relativePath)}`;
2859
- }
2860
- uploadKeys.add(key);
2861
- if (key.length > limit) {
2862
- return `文件路径过长,请缩短构建产物文件名或目录层级:${relativePath}`;
2863
- }
2864
- }
2865
- return undefined;
2866
- }
2867
- function getInvalidUploadPathReason(relativePath) {
2868
- const segments = relativePath.replace(/\\/g, '/').split('/');
2869
- if (segments.some((segment) => !segment)) {
2870
- return '包含空路径段';
2871
- }
2872
- if (segments.some((segment) => segment === '.')) {
2873
- return '包含当前目录';
2874
- }
2875
- if (segments.some((segment) => segment === '..')) {
2876
- return '包含上级目录';
2877
- }
2878
- if (/[\u0000-\u001F\u007F]/.test(relativePath)) {
2879
- return '包含控制字符';
2880
- }
2881
- return undefined;
2882
- }
2883
2841
  function validateUploadTotalSize(items, options = {}) {
2884
2842
  const limit = options.limitBytes ?? MINIAPP_UPLOAD_TOTAL_SIZE_LIMIT_BYTES;
2885
2843
  const total = items.reduce((sum, item) => {
@@ -2907,6 +2865,202 @@ function shouldUploadDistFile(relativePath) {
2907
2865
  }
2908
2866
  return true;
2909
2867
  }
2868
+ /**
2869
+ * 校验构建产物路径和大小,并按路径 UTF-8 字节序排序。
2870
+ *
2871
+ * @param files 待处理的构建产物文件。
2872
+ * @returns 保留输入附加字段的有序文件列表。
2873
+ * @throws 路径不规范、路径重复或文件大小非法时抛出错误。
2874
+ */
2875
+ function prepareMiniappArtifactFiles(files) {
2876
+ if (files.length > 0xffffffff) {
2877
+ throw new Error('构建产物文件数量超出签名协议上限');
2878
+ }
2879
+ const usedPaths = new Set();
2880
+ const prepared = files.map(file => {
2881
+ const relativePath = normalizeMiniappArtifactRelativePath(file.relativePath);
2882
+ if (usedPaths.has(relativePath)) {
2883
+ throw new Error(`构建产物包含重复文件路径:${relativePath}`);
2884
+ }
2885
+ usedPaths.add(relativePath);
2886
+ validateArtifactFileSize(file.size, relativePath);
2887
+ return { ...file, relativePath };
2888
+ });
2889
+ return prepared.sort((left, right) => compareBytes(textEncoder.encode(left.relativePath), textEncoder.encode(right.relativePath)));
2890
+ }
2891
+ /**
2892
+ * 按服务端 session key 前缀校验最终上传路径的 UTF-8 字节长度。
2893
+ *
2894
+ * @param files 待上传的构建产物文件。
2895
+ * @param options 小程序和长度限制配置。
2896
+ * @returns 第一个超长路径的错误提示;全部合法时返回 `undefined`。
2897
+ */
2898
+ function validateMiniappUploadPathLengths(files, options) {
2899
+ const maxLength = options.maxLength ?? ACTIVITY_UPLOAD_KEY_MAX_LENGTH;
2900
+ const prefix = `/u/${getMiniProgramUploadAlias(options.miniProgramId)}/${'x'.repeat(MINIAPP_UPLOAD_SESSION_REF_LENGTH)}/`;
2901
+ const prefixLength = textEncoder.encode(prefix).length;
2902
+ const tooLongFile = files.find(file => {
2903
+ const relativePath = normalizeMiniappArtifactRelativePath(file.relativePath);
2904
+ return prefixLength + textEncoder.encode(relativePath).length > maxLength;
2905
+ });
2906
+ return tooLongFile ? `文件路径过长,请缩短构建产物文件名或目录层级:${tooLongFile.relativePath}` : undefined;
2907
+ }
2908
+ /**
2909
+ * 创建 v1 整包签名头。
2910
+ *
2911
+ * @param fileCount 参与签名的文件数量。
2912
+ * @returns 包含协议标识和文件数量的字节数组。
2913
+ * @throws 文件数量超出协议范围时抛出错误。
2914
+ */
2915
+ function createMiniappArtifactSignatureHeader(fileCount) {
2916
+ if (!Number.isInteger(fileCount) || fileCount < 0 || fileCount > 0xffffffff) {
2917
+ throw new Error('构建产物文件数量超出签名协议上限');
2918
+ }
2919
+ return concatBytes(textEncoder.encode(MINIAPP_ARTIFACT_SIGNATURE_MAGIC), encodeUint32BE(fileCount));
2920
+ }
2921
+ /**
2922
+ * 创建单个文件的 v1 framing header。
2923
+ *
2924
+ * @param file 已规范化或待校验的构建产物文件。
2925
+ * @returns 包含路径长度、路径和文件大小的字节数组。
2926
+ * @throws 文件路径或大小非法时抛出错误。
2927
+ */
2928
+ function createMiniappArtifactFileHeader(file) {
2929
+ const relativePath = normalizeMiniappArtifactRelativePath(file.relativePath);
2930
+ validateArtifactFileSize(file.size, relativePath);
2931
+ const pathBytes = textEncoder.encode(relativePath);
2932
+ return concatBytes(encodeUint32BE(pathBytes.length), pathBytes, encodeUint64BE(file.size));
2933
+ }
2934
+ /**
2935
+ * 校验服务端 session 文件映射,并计算仍需上传的文件。
2936
+ *
2937
+ * @param localFiles 本地构建产物文件。
2938
+ * @param serverFiles 服务端签发的文件映射。
2939
+ * @param uploadedKeys 服务端已确认上传成功的 key。
2940
+ * @returns 完整映射、待上传文件和已确认 key。
2941
+ * @throws 服务端映射与本地产物不一致时抛出错误。
2942
+ */
2943
+ function resolveMiniappUploadSessionFiles(localFiles, serverFiles, uploadedKeys) {
2944
+ const preparedFiles = prepareMiniappArtifactFiles(localFiles);
2945
+ if (serverFiles.length !== preparedFiles.length) {
2946
+ throw new Error(`上传会话文件数量异常:期望 ${preparedFiles.length},实际 ${serverFiles.length}`);
2947
+ }
2948
+ const mappings = new Map();
2949
+ const serverSizes = new Map();
2950
+ const knownKeys = new Set();
2951
+ for (const serverFile of serverFiles) {
2952
+ const relativePath = normalizeMiniappArtifactRelativePath(serverFile.relative_path);
2953
+ if (mappings.has(relativePath)) {
2954
+ throw new Error(`上传会话返回重复文件路径:${relativePath}`);
2955
+ }
2956
+ const key = String(serverFile.key || '');
2957
+ if (!key) {
2958
+ throw new Error(`上传会话未返回文件 key:${relativePath}`);
2959
+ }
2960
+ if (knownKeys.has(key)) {
2961
+ throw new Error(`上传会话返回重复文件 key:${relativePath}`);
2962
+ }
2963
+ const size = Number(serverFile.size);
2964
+ if (!Number.isSafeInteger(size) || size < 0) {
2965
+ throw new Error(`上传会话返回文件大小异常:${relativePath}`);
2966
+ }
2967
+ mappings.set(relativePath, key);
2968
+ serverSizes.set(relativePath, size);
2969
+ knownKeys.add(key);
2970
+ }
2971
+ const files = preparedFiles.map(file => {
2972
+ const key = mappings.get(file.relativePath);
2973
+ if (!key) {
2974
+ throw new Error(`上传会话缺少文件映射:${file.relativePath}`);
2975
+ }
2976
+ if (serverSizes.get(file.relativePath) !== file.size) {
2977
+ throw new Error(`上传会话文件大小与本地构建不一致:${file.relativePath}`);
2978
+ }
2979
+ return { file, key };
2980
+ });
2981
+ const completed = new Set();
2982
+ for (const rawKey of uploadedKeys) {
2983
+ const key = String(rawKey || '');
2984
+ if (!knownKeys.has(key)) {
2985
+ throw new Error('上传会话返回了不属于当前产物的已上传文件');
2986
+ }
2987
+ completed.add(key);
2988
+ }
2989
+ return {
2990
+ files,
2991
+ missingFiles: files.filter(file => !completed.has(file.key)),
2992
+ uploadedKeys: [...completed],
2993
+ };
2994
+ }
2995
+ function normalizeMiniappArtifactRelativePath(relativePath) {
2996
+ const value = String(relativePath || '');
2997
+ if (!value) {
2998
+ throw new Error('构建产物包含空文件路径');
2999
+ }
3000
+ if (value.includes('\\')) {
3001
+ throw new Error(`构建产物路径不能包含反斜杠:${value}`);
3002
+ }
3003
+ if (value.startsWith('/') || value.endsWith('/') || /^[a-zA-Z]:\//.test(value)) {
3004
+ throw new Error(`构建产物必须使用相对文件路径:${value}`);
3005
+ }
3006
+ if (containsControlCharacter(value) || UNPAIRED_SURROGATE_PATTERN.test(value)) {
3007
+ throw new Error(`构建产物路径包含非法字符:${value}`);
3008
+ }
3009
+ if (value.split('/').some(segment => !segment || segment === '.' || segment === '..')) {
3010
+ throw new Error(`构建产物路径包含非法目录段:${value}`);
3011
+ }
3012
+ if (value.includes(',')) {
3013
+ throw new Error(`构建产物路径不能包含逗号:${value}`);
3014
+ }
3015
+ if (value.includes('*') || value.includes('?')) {
3016
+ throw new Error(`构建产物路径不能包含通配符:${value}`);
3017
+ }
3018
+ return value;
3019
+ }
3020
+ function containsControlCharacter(value) {
3021
+ for (const character of value) {
3022
+ const code = character.charCodeAt(0);
3023
+ if (code <= 0x1f || (code >= 0x7f && code <= 0x9f)) {
3024
+ return true;
3025
+ }
3026
+ }
3027
+ return false;
3028
+ }
3029
+ function validateArtifactFileSize(size, relativePath) {
3030
+ if (!Number.isSafeInteger(size) || size < 0) {
3031
+ throw new Error(`构建产物文件大小无效:${relativePath}`);
3032
+ }
3033
+ }
3034
+ function encodeUint32BE(value) {
3035
+ const bytes = new Uint8Array(4);
3036
+ new DataView(bytes.buffer).setUint32(0, value, false);
3037
+ return bytes;
3038
+ }
3039
+ function encodeUint64BE(value) {
3040
+ const bytes = new Uint8Array(8);
3041
+ const view = new DataView(bytes.buffer);
3042
+ view.setUint32(0, Math.floor(value / 0x100000000), false);
3043
+ view.setUint32(4, value % 0x100000000, false);
3044
+ return bytes;
3045
+ }
3046
+ function concatBytes(...parts) {
3047
+ const result = new Uint8Array(parts.reduce((total, part) => total + part.length, 0));
3048
+ let offset = 0;
3049
+ for (const part of parts) {
3050
+ result.set(part, offset);
3051
+ offset += part.length;
3052
+ }
3053
+ return result;
3054
+ }
3055
+ function compareBytes(left, right) {
3056
+ const length = Math.min(left.length, right.length);
3057
+ for (let index = 0; index < length; index += 1) {
3058
+ if (left[index] !== right[index]) {
3059
+ return left[index] - right[index];
3060
+ }
3061
+ }
3062
+ return left.length - right.length;
3063
+ }
2910
3064
 
2911
3065
  const HB_SDK_RUNTIME_USE_OFFICIAL_DOMAIN_ENV = 'HB_SDK_RUNTIME_USE_OFFICIAL_DOMAIN';
2912
3066
  const HB_SDK_RUNTIME_PERMISSION_CONTEXT_ENV = 'HB_SDK_RUNTIME_PERMISSION_CONTEXT';
@@ -2974,7 +3128,6 @@ async function runMiniappPlatformCspEnvScope(skipPlatformCsp, action, env) {
2974
3128
  }
2975
3129
  }
2976
3130
 
2977
- exports.ACTIVITY_UPLOAD_KEY_MAX_LENGTH = ACTIVITY_UPLOAD_KEY_MAX_LENGTH;
2978
3131
  exports.CREATE_USER_MINIPROGRAM_API_PATH = CREATE_USER_MINIPROGRAM_API_PATH;
2979
3132
  exports.CREATE_USER_MINIPROGRAM_LEADERBOARD_API_PATH = CREATE_USER_MINIPROGRAM_LEADERBOARD_API_PATH;
2980
3133
  exports.DELETE_USER_MINIPROGRAM_LEADERBOARD_API_PATH = DELETE_USER_MINIPROGRAM_LEADERBOARD_API_PATH;
@@ -2998,14 +3151,17 @@ exports.USER_MINIPROGRAM_ACCESS_STATUS_API_PATH = USER_MINIPROGRAM_ACCESS_STATUS
2998
3151
  exports.USER_MINIPROGRAM_PREVIEW_ALLOWLIST_API_PATH = USER_MINIPROGRAM_PREVIEW_ALLOWLIST_API_PATH;
2999
3152
  exports.USER_MINIPROGRAM_VERSION_PREVIEW_INFO_API_PATH = USER_MINIPROGRAM_VERSION_PREVIEW_INFO_API_PATH;
3000
3153
  exports.WITHDRAW_USER_MINIPROGRAM_VERSION_API_PATH = WITHDRAW_USER_MINIPROGRAM_VERSION_API_PATH;
3154
+ exports.createMiniappArtifactFileHeader = createMiniappArtifactFileHeader;
3155
+ exports.createMiniappArtifactSignatureHeader = createMiniappArtifactSignatureHeader;
3001
3156
  exports.createMiniappPlatformCspEnv = createMiniappPlatformCspEnv;
3002
- exports.getMiniappUploadKey = getMiniappUploadKey;
3003
3157
  exports.normalizeRelativePath = normalizeRelativePath;
3158
+ exports.prepareMiniappArtifactFiles = prepareMiniappArtifactFiles;
3004
3159
  exports.relativePathContainsNodeModulesSegment = relativePathContainsNodeModulesSegment;
3005
3160
  exports.requireSemver = requireSemver;
3161
+ exports.resolveMiniappUploadSessionFiles = resolveMiniappUploadSessionFiles;
3006
3162
  exports.semverExports = semverExports;
3007
3163
  exports.shouldSkipMiniappPlatformCspFromEnv = shouldSkipMiniappPlatformCspFromEnv;
3008
3164
  exports.shouldUploadDistFile = shouldUploadDistFile;
3009
- exports.validateUploadPaths = validateUploadPaths;
3165
+ exports.validateMiniappUploadPathLengths = validateMiniappUploadPathLengths;
3010
3166
  exports.validateUploadTotalSize = validateUploadTotalSize;
3011
3167
  exports.withMiniappPlatformCspEnv = withMiniappPlatformCspEnv;
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./index-CSjJrnkO.cjs');
3
+ var index = require('./index-DofTxdoX.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-CSjJrnkO.cjs');
3
+ var index = require('./cli-chunks/index-DofTxdoX.cjs');
4
4
  require('node:module');
5
5
  require('node:fs');
6
6
  require('node:fs/promises');
package/dist/index.cjs.js CHANGED
@@ -603,7 +603,7 @@ function createMessageId() {
603
603
  /** 构建时替换为当前发布包的实际版本。 */
604
604
  const HB_SDK_VERSION = typeof undefined === 'string'
605
605
  ? undefined
606
- : '0.6.8';
606
+ : '0.6.9-alpha.0';
607
607
 
608
608
  /**
609
609
  * 判断未知数据是否符合小程序 bridge 消息信封。
package/dist/index.esm.js CHANGED
@@ -599,7 +599,7 @@ function createMessageId() {
599
599
  /** 构建时替换为当前发布包的实际版本。 */
600
600
  const HB_SDK_VERSION = typeof undefined === 'string'
601
601
  ? undefined
602
- : '0.6.8';
602
+ : '0.6.9-alpha.0';
603
603
 
604
604
  /**
605
605
  * 判断未知数据是否符合小程序 bridge 消息信封。
@@ -2836,6 +2836,10 @@ const UPDATE_SQUARE_DISPLAY_API_PATH = '/mall/developer/user_miniprogram/square_
2836
2836
  const LIST_USER_MINIPROGRAM_VERSION_API_PATH = '/mall/developer/user_miniprogram/version/list';
2837
2837
  const FNV_OFFSET = 0x811c9dc5;
2838
2838
  const FNV_PRIME = 0x01000193;
2839
+ const MINIAPP_UPLOAD_SESSION_REF_LENGTH = 10;
2840
+ const MINIAPP_ARTIFACT_SIGNATURE_MAGIC = 'heybox-user-miniprogram-artifact\0v1\0';
2841
+ const UNPAIRED_SURROGATE_PATTERN = /[\ud800-\udbff](?![\udc00-\udfff])|(^|[^\ud800-\udbff])[\udc00-\udfff]/;
2842
+ const textEncoder = new TextEncoder();
2839
2843
  function normalizeRelativePath(relativePath) {
2840
2844
  return String(relativePath || '')
2841
2845
  .replace(/\\/g, '/')
@@ -2929,6 +2933,202 @@ function shouldUploadDistFile(relativePath) {
2929
2933
  }
2930
2934
  return true;
2931
2935
  }
2936
+ /**
2937
+ * 校验构建产物路径和大小,并按路径 UTF-8 字节序排序。
2938
+ *
2939
+ * @param files 待处理的构建产物文件。
2940
+ * @returns 保留输入附加字段的有序文件列表。
2941
+ * @throws 路径不规范、路径重复或文件大小非法时抛出错误。
2942
+ */
2943
+ function prepareMiniappArtifactFiles(files) {
2944
+ if (files.length > 0xffffffff) {
2945
+ throw new Error('构建产物文件数量超出签名协议上限');
2946
+ }
2947
+ const usedPaths = new Set();
2948
+ const prepared = files.map(file => {
2949
+ const relativePath = normalizeMiniappArtifactRelativePath(file.relativePath);
2950
+ if (usedPaths.has(relativePath)) {
2951
+ throw new Error(`构建产物包含重复文件路径:${relativePath}`);
2952
+ }
2953
+ usedPaths.add(relativePath);
2954
+ validateArtifactFileSize(file.size, relativePath);
2955
+ return { ...file, relativePath };
2956
+ });
2957
+ return prepared.sort((left, right) => compareBytes(textEncoder.encode(left.relativePath), textEncoder.encode(right.relativePath)));
2958
+ }
2959
+ /**
2960
+ * 按服务端 session key 前缀校验最终上传路径的 UTF-8 字节长度。
2961
+ *
2962
+ * @param files 待上传的构建产物文件。
2963
+ * @param options 小程序和长度限制配置。
2964
+ * @returns 第一个超长路径的错误提示;全部合法时返回 `undefined`。
2965
+ */
2966
+ function validateMiniappUploadPathLengths(files, options) {
2967
+ const maxLength = options.maxLength ?? ACTIVITY_UPLOAD_KEY_MAX_LENGTH;
2968
+ const prefix = `/u/${getMiniProgramUploadAlias(options.miniProgramId)}/${'x'.repeat(MINIAPP_UPLOAD_SESSION_REF_LENGTH)}/`;
2969
+ const prefixLength = textEncoder.encode(prefix).length;
2970
+ const tooLongFile = files.find(file => {
2971
+ const relativePath = normalizeMiniappArtifactRelativePath(file.relativePath);
2972
+ return prefixLength + textEncoder.encode(relativePath).length > maxLength;
2973
+ });
2974
+ return tooLongFile ? `文件路径过长,请缩短构建产物文件名或目录层级:${tooLongFile.relativePath}` : undefined;
2975
+ }
2976
+ /**
2977
+ * 创建 v1 整包签名头。
2978
+ *
2979
+ * @param fileCount 参与签名的文件数量。
2980
+ * @returns 包含协议标识和文件数量的字节数组。
2981
+ * @throws 文件数量超出协议范围时抛出错误。
2982
+ */
2983
+ function createMiniappArtifactSignatureHeader(fileCount) {
2984
+ if (!Number.isInteger(fileCount) || fileCount < 0 || fileCount > 0xffffffff) {
2985
+ throw new Error('构建产物文件数量超出签名协议上限');
2986
+ }
2987
+ return concatBytes(textEncoder.encode(MINIAPP_ARTIFACT_SIGNATURE_MAGIC), encodeUint32BE(fileCount));
2988
+ }
2989
+ /**
2990
+ * 创建单个文件的 v1 framing header。
2991
+ *
2992
+ * @param file 已规范化或待校验的构建产物文件。
2993
+ * @returns 包含路径长度、路径和文件大小的字节数组。
2994
+ * @throws 文件路径或大小非法时抛出错误。
2995
+ */
2996
+ function createMiniappArtifactFileHeader(file) {
2997
+ const relativePath = normalizeMiniappArtifactRelativePath(file.relativePath);
2998
+ validateArtifactFileSize(file.size, relativePath);
2999
+ const pathBytes = textEncoder.encode(relativePath);
3000
+ return concatBytes(encodeUint32BE(pathBytes.length), pathBytes, encodeUint64BE(file.size));
3001
+ }
3002
+ /**
3003
+ * 校验服务端 session 文件映射,并计算仍需上传的文件。
3004
+ *
3005
+ * @param localFiles 本地构建产物文件。
3006
+ * @param serverFiles 服务端签发的文件映射。
3007
+ * @param uploadedKeys 服务端已确认上传成功的 key。
3008
+ * @returns 完整映射、待上传文件和已确认 key。
3009
+ * @throws 服务端映射与本地产物不一致时抛出错误。
3010
+ */
3011
+ function resolveMiniappUploadSessionFiles(localFiles, serverFiles, uploadedKeys) {
3012
+ const preparedFiles = prepareMiniappArtifactFiles(localFiles);
3013
+ if (serverFiles.length !== preparedFiles.length) {
3014
+ throw new Error(`上传会话文件数量异常:期望 ${preparedFiles.length},实际 ${serverFiles.length}`);
3015
+ }
3016
+ const mappings = new Map();
3017
+ const serverSizes = new Map();
3018
+ const knownKeys = new Set();
3019
+ for (const serverFile of serverFiles) {
3020
+ const relativePath = normalizeMiniappArtifactRelativePath(serverFile.relative_path);
3021
+ if (mappings.has(relativePath)) {
3022
+ throw new Error(`上传会话返回重复文件路径:${relativePath}`);
3023
+ }
3024
+ const key = String(serverFile.key || '');
3025
+ if (!key) {
3026
+ throw new Error(`上传会话未返回文件 key:${relativePath}`);
3027
+ }
3028
+ if (knownKeys.has(key)) {
3029
+ throw new Error(`上传会话返回重复文件 key:${relativePath}`);
3030
+ }
3031
+ const size = Number(serverFile.size);
3032
+ if (!Number.isSafeInteger(size) || size < 0) {
3033
+ throw new Error(`上传会话返回文件大小异常:${relativePath}`);
3034
+ }
3035
+ mappings.set(relativePath, key);
3036
+ serverSizes.set(relativePath, size);
3037
+ knownKeys.add(key);
3038
+ }
3039
+ const files = preparedFiles.map(file => {
3040
+ const key = mappings.get(file.relativePath);
3041
+ if (!key) {
3042
+ throw new Error(`上传会话缺少文件映射:${file.relativePath}`);
3043
+ }
3044
+ if (serverSizes.get(file.relativePath) !== file.size) {
3045
+ throw new Error(`上传会话文件大小与本地构建不一致:${file.relativePath}`);
3046
+ }
3047
+ return { file, key };
3048
+ });
3049
+ const completed = new Set();
3050
+ for (const rawKey of uploadedKeys) {
3051
+ const key = String(rawKey || '');
3052
+ if (!knownKeys.has(key)) {
3053
+ throw new Error('上传会话返回了不属于当前产物的已上传文件');
3054
+ }
3055
+ completed.add(key);
3056
+ }
3057
+ return {
3058
+ files,
3059
+ missingFiles: files.filter(file => !completed.has(file.key)),
3060
+ uploadedKeys: [...completed],
3061
+ };
3062
+ }
3063
+ function normalizeMiniappArtifactRelativePath(relativePath) {
3064
+ const value = String(relativePath || '');
3065
+ if (!value) {
3066
+ throw new Error('构建产物包含空文件路径');
3067
+ }
3068
+ if (value.includes('\\')) {
3069
+ throw new Error(`构建产物路径不能包含反斜杠:${value}`);
3070
+ }
3071
+ if (value.startsWith('/') || value.endsWith('/') || /^[a-zA-Z]:\//.test(value)) {
3072
+ throw new Error(`构建产物必须使用相对文件路径:${value}`);
3073
+ }
3074
+ if (containsControlCharacter(value) || UNPAIRED_SURROGATE_PATTERN.test(value)) {
3075
+ throw new Error(`构建产物路径包含非法字符:${value}`);
3076
+ }
3077
+ if (value.split('/').some(segment => !segment || segment === '.' || segment === '..')) {
3078
+ throw new Error(`构建产物路径包含非法目录段:${value}`);
3079
+ }
3080
+ if (value.includes(',')) {
3081
+ throw new Error(`构建产物路径不能包含逗号:${value}`);
3082
+ }
3083
+ if (value.includes('*') || value.includes('?')) {
3084
+ throw new Error(`构建产物路径不能包含通配符:${value}`);
3085
+ }
3086
+ return value;
3087
+ }
3088
+ function containsControlCharacter(value) {
3089
+ for (const character of value) {
3090
+ const code = character.charCodeAt(0);
3091
+ if (code <= 0x1f || (code >= 0x7f && code <= 0x9f)) {
3092
+ return true;
3093
+ }
3094
+ }
3095
+ return false;
3096
+ }
3097
+ function validateArtifactFileSize(size, relativePath) {
3098
+ if (!Number.isSafeInteger(size) || size < 0) {
3099
+ throw new Error(`构建产物文件大小无效:${relativePath}`);
3100
+ }
3101
+ }
3102
+ function encodeUint32BE(value) {
3103
+ const bytes = new Uint8Array(4);
3104
+ new DataView(bytes.buffer).setUint32(0, value, false);
3105
+ return bytes;
3106
+ }
3107
+ function encodeUint64BE(value) {
3108
+ const bytes = new Uint8Array(8);
3109
+ const view = new DataView(bytes.buffer);
3110
+ view.setUint32(0, Math.floor(value / 0x100000000), false);
3111
+ view.setUint32(4, value % 0x100000000, false);
3112
+ return bytes;
3113
+ }
3114
+ function concatBytes(...parts) {
3115
+ const result = new Uint8Array(parts.reduce((total, part) => total + part.length, 0));
3116
+ let offset = 0;
3117
+ for (const part of parts) {
3118
+ result.set(part, offset);
3119
+ offset += part.length;
3120
+ }
3121
+ return result;
3122
+ }
3123
+ function compareBytes(left, right) {
3124
+ const length = Math.min(left.length, right.length);
3125
+ for (let index = 0; index < length; index += 1) {
3126
+ if (left[index] !== right[index]) {
3127
+ return left[index] - right[index];
3128
+ }
3129
+ }
3130
+ return left.length - right.length;
3131
+ }
2932
3132
 
2933
3133
  exports.ACTIVITY_UPLOAD_KEY_MAX_LENGTH = ACTIVITY_UPLOAD_KEY_MAX_LENGTH;
2934
3134
  exports.CREATE_USER_MINIPROGRAM_API_PATH = CREATE_USER_MINIPROGRAM_API_PATH;
@@ -2955,11 +3155,16 @@ exports.USER_MINIPROGRAM_ACCESS_STATUS_API_PATH = USER_MINIPROGRAM_ACCESS_STATUS
2955
3155
  exports.USER_MINIPROGRAM_PREVIEW_ALLOWLIST_API_PATH = USER_MINIPROGRAM_PREVIEW_ALLOWLIST_API_PATH;
2956
3156
  exports.USER_MINIPROGRAM_VERSION_PREVIEW_INFO_API_PATH = USER_MINIPROGRAM_VERSION_PREVIEW_INFO_API_PATH;
2957
3157
  exports.WITHDRAW_USER_MINIPROGRAM_VERSION_API_PATH = WITHDRAW_USER_MINIPROGRAM_VERSION_API_PATH;
3158
+ exports.createMiniappArtifactFileHeader = createMiniappArtifactFileHeader;
3159
+ exports.createMiniappArtifactSignatureHeader = createMiniappArtifactSignatureHeader;
2958
3160
  exports.getMiniProgramUploadAlias = getMiniProgramUploadAlias;
2959
3161
  exports.getMiniappUploadKey = getMiniappUploadKey;
2960
3162
  exports.isValidVersion = isValidMiniappManifestVersion;
2961
3163
  exports.normalizeRelativePath = normalizeRelativePath;
3164
+ exports.prepareMiniappArtifactFiles = prepareMiniappArtifactFiles;
2962
3165
  exports.relativePathContainsNodeModulesSegment = relativePathContainsNodeModulesSegment;
3166
+ exports.resolveMiniappUploadSessionFiles = resolveMiniappUploadSessionFiles;
2963
3167
  exports.shouldUploadDistFile = shouldUploadDistFile;
3168
+ exports.validateMiniappUploadPathLengths = validateMiniappUploadPathLengths;
2964
3169
  exports.validateUploadPaths = validateUploadPaths;
2965
3170
  exports.validateUploadTotalSize = validateUploadTotalSize;