@heybox/hb-sdk 0.8.0-alpha.9 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/README.md +25 -19
  3. package/dist/cli-chunks/{build-DkokKVNy.cjs → build-PYCNacya.cjs} +8 -5
  4. package/dist/cli-chunks/{context-CKzZxKbF.cjs → context-m2W2XbL0.cjs} +42 -59
  5. package/dist/cli-chunks/{create-BQy5Jdmu.cjs → create-BdAg3WGA.cjs} +1 -1
  6. package/dist/cli-chunks/{dev-Dik2zr5R.cjs → dev-CyZuw7Yn.cjs} +26 -15
  7. package/dist/cli-chunks/{doctor-Byr4uwKG.cjs → doctor-DU8rCfUF.cjs} +1 -1
  8. package/dist/cli-chunks/{index-Cl0XaX8e.cjs → index-DATObqzK.cjs} +2 -2
  9. package/dist/cli-chunks/{index-BwGBr1ZA.cjs → index-MMW2ibQm.cjs} +15 -15
  10. package/dist/cli-chunks/{index.esm-BYifBABc.cjs → index.esm-BiAaAUFC.cjs} +8 -8
  11. package/dist/cli-chunks/{login-Dr9lclu3.cjs → login-B3TThMss.cjs} +2 -2
  12. package/dist/cli-chunks/{project-vite-BIPrOtRO.cjs → project-vite-BQj8YLI4.cjs} +1 -1
  13. package/dist/cli-chunks/{remote-DXxyA14a.cjs → remote-DNvI7tHH.cjs} +57 -25
  14. package/dist/cli-chunks/{runtime-gate-DFjw66kF.cjs → runtime-gate-BEFp1w_s.cjs} +11 -3
  15. package/dist/cli-chunks/{runtime-permission-env-CjsCe5bp.cjs → runtime-permission-env-CtL8rsjB.cjs} +351 -0
  16. package/dist/cli-chunks/{session-B6Mo9eYW.cjs → session-DjBkjaF8.cjs} +1 -1
  17. package/dist/cli-chunks/{skill-f90sxv28.cjs → skill-cR_wnaw2.cjs} +2 -2
  18. package/dist/cli-chunks/{version-DDB_btOG.cjs → version-yEn1E2Bg.cjs} +1 -1
  19. package/dist/cli.cjs +1 -1
  20. package/dist/devtools/browser-dev-host/assets/browser-dev-host-TzYf9L6C.js +99 -0
  21. package/dist/devtools/browser-dev-host/assets/{index-DaqmTjSF.js → index-C5MZZDa5.js} +4 -4
  22. package/dist/devtools/browser-dev-host/assets/index-P-ra4m1y.css +1 -0
  23. package/dist/devtools/browser-dev-host/assets/{workbench-state-wHgWRb7I.js → workbench-state-BwV7bm4n.js} +2 -2
  24. package/dist/devtools/browser-dev-host/index.html +3 -3
  25. package/dist/index.cjs.js +99 -1
  26. package/dist/index.esm.js +99 -1
  27. package/dist/protocol.cjs.js +189 -47
  28. package/dist/protocol.esm.js +189 -47
  29. package/dist/templates/vanilla-vite-js/package.json.ejs +2 -1
  30. package/dist/vite.cjs.js +281 -13
  31. package/dist/vite.esm.js +281 -13
  32. package/package.json +5 -5
  33. package/skill/SKILL.md +8 -9
  34. package/skill/references/api-protocol.md +1 -1
  35. package/skill/references/api-root.md +63 -21
  36. package/skill/references/cli.md +6 -6
  37. package/skill/references/examples.md +3 -3
  38. package/skill/references/recipes.md +35 -38
  39. package/skill/references/safety-boundaries.md +9 -5
  40. package/skill/skill.json +5 -5
  41. package/types/core/mini-dev-console.d.ts +36 -0
  42. package/types/miniapp-manifest/index.d.ts +1 -0
  43. package/types/miniapp-manifest/node.d.ts +3 -0
  44. package/types/miniapp-manifest/permissions.d.ts +37 -0
  45. package/types/miniapp-manifest/schema.d.ts +5 -0
  46. package/types/vite/index.d.ts +3 -1
  47. package/dist/devtools/browser-dev-host/assets/browser-dev-host-CbhA4h8z.js +0 -97
  48. package/dist/devtools/browser-dev-host/assets/index-DJFB5ySU.css +0 -1
package/dist/vite.cjs.js CHANGED
@@ -8,7 +8,30 @@ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentS
8
8
  /** 构建时替换为当前发布包的实际版本。 */
9
9
  const HB_SDK_VERSION = typeof undefined === 'string'
10
10
  ? undefined
11
- : '0.8.0-alpha.9';
11
+ : '0.8.0';
12
+
13
+ /**
14
+ * iframe 与调试台页面的开发期 console 捕获转发。
15
+ *
16
+ * 双条件门控(iframe 转发):
17
+ * 1. 构建期:`hb-sdk dev`(vite serve)注入 `false`;`hb-sdk build` /
18
+ * `vite build`(生产)永不注入或替换为 `false`,本模块被死代码消除。
19
+ * 2. 运行期:仅在 `window.name` 为 `__hb_sdk_mini_dev__` 的调试 iframe 中激活,
20
+ * 用户设备上的正式运行环境没有该标记。
21
+ *
22
+ * 无侵入 patch `console.log/debug/info/warn/error`:原样放行(DevTools 不受影响)+
23
+ * 通过 `window.parent.postMessage` 转发给调试台(CSP 的 connect-src 不约束 postMessage,
24
+ * 网络上报由调试台页面代理)。发送全程静默:失败不得回打 console,避免自触发死循环。
25
+ *
26
+ * 激活时机:由 SDK 默认单例初始化时调用(业务 `import '@heybox/hb-sdk'` 即生效);
27
+ * iframe 标记由调试台 `iframe.name` 与 vite dev 注入的无依赖 bootstrap 共同设置。
28
+ * 调试台页面自身(runtime 跑在这里,`[hb-sdk-runtime]` 日志不经过 iframe)另用
29
+ * `installMiniDevConsoleSelfCapture` 接入「日志」页签(source: 'host')。
30
+ */
31
+ const MINI_DEV_IFRAME_WINDOW_NAME = '__hb_sdk_mini_dev__';
32
+ const MINI_DEV_CONSOLE_EVENT_TYPE = 'hb-sdk:mini-dev-console';
33
+ /** 调试台页面与 iframe bootstrap 共用的已安装标记(window/console 上的属性名)。 */
34
+ const MINI_DEV_CONSOLE_INSTALL_FLAG = '__hb_sdk_mini_dev_console_installed__';
12
35
 
13
36
  var re = {exports: {}};
14
37
 
@@ -2795,6 +2818,197 @@ function requireSemver () {
2795
2818
 
2796
2819
  var semverExports = requireSemver();
2797
2820
 
2821
+ const MINI_PROGRAM_MESSAGE_NAMESPACE = 'heybox:miniprogram';
2822
+ const MINI_PROGRAM_MESSAGE_VERSION = 2;
2823
+ const MINI_PROGRAM_BRIDGE_NONCE_PARAM = 'hb_mini_bridge_nonce';
2824
+
2825
+ const MINI_PROGRAM_PERMISSION_KEYS = [
2826
+ 'userInfo',
2827
+ 'steamLibrary',
2828
+ 'share',
2829
+ 'storage',
2830
+ 'filesystem',
2831
+ 'clipboard',
2832
+ 'leaderboard',
2833
+ 'network',
2834
+ ];
2835
+ const MINI_PROGRAM_PERMISSION_CATALOG = [
2836
+ {
2837
+ key: 'userInfo',
2838
+ name: '用户信息',
2839
+ description: '登录小程序并读取当前用户的授权信息。',
2840
+ access: 'declaration',
2841
+ risk: 'medium',
2842
+ config: {},
2843
+ methods: ['auth.login', 'user.getInfo'],
2844
+ conditions: ['继续执行黑盒登录、用户授权和可信手势规则。'],
2845
+ },
2846
+ {
2847
+ key: 'steamLibrary',
2848
+ name: 'Steam 游戏库',
2849
+ description: '读取当前用户授权的 Steam 游戏库。',
2850
+ access: 'declaration',
2851
+ risk: 'high',
2852
+ config: {},
2853
+ methods: ['user.getSteamGameList'],
2854
+ conditions: ['继续执行账号绑定和数据可用性规则。'],
2855
+ },
2856
+ {
2857
+ key: 'share',
2858
+ name: '分享',
2859
+ description: '使用小程序分享、分享菜单和截图能力。',
2860
+ access: 'declaration',
2861
+ risk: 'medium',
2862
+ config: {},
2863
+ methods: ['share.copyLink', 'share.showShareMenu', 'share.screenshot'],
2864
+ conditions: ['继续执行分享参数校验和 Host 支持检查。'],
2865
+ },
2866
+ {
2867
+ key: 'storage',
2868
+ name: '键值存储',
2869
+ description: '读写按小程序隔离的简单键值数据。',
2870
+ access: 'declaration',
2871
+ risk: 'medium',
2872
+ config: {},
2873
+ methods: ['storage.getStorage', 'storage.setStorage'],
2874
+ conditions: ['仅用于简单键值存储,不包含文件系统。'],
2875
+ },
2876
+ {
2877
+ key: 'filesystem',
2878
+ name: '文件系统',
2879
+ description: '访问小程序沙盒文件和用户选择的外部文件。',
2880
+ access: 'declaration',
2881
+ risk: 'high',
2882
+ config: {},
2883
+ methods: [
2884
+ 'network.download',
2885
+ 'files.pickFiles',
2886
+ 'files.pickDirectory',
2887
+ 'files.saveFile',
2888
+ 'file.create',
2889
+ 'file.remove',
2890
+ 'file.exists',
2891
+ 'file.readText',
2892
+ 'file.writeText',
2893
+ 'file.readBytes',
2894
+ 'file.writeBytes',
2895
+ 'file.stat',
2896
+ 'directory.create',
2897
+ 'directory.remove',
2898
+ 'directory.exists',
2899
+ 'directory.list',
2900
+ ],
2901
+ conditions: ['外部文件继续要求有效的用户选择、会话句柄和匹配的读写模式。'],
2902
+ },
2903
+ {
2904
+ key: 'clipboard',
2905
+ name: '剪贴板',
2906
+ description: '向系统剪贴板写入文本。',
2907
+ access: 'declaration',
2908
+ risk: 'medium',
2909
+ config: {},
2910
+ methods: ['device.setClipboard'],
2911
+ conditions: ['继续执行文本长度和 Host 支持限制。'],
2912
+ },
2913
+ {
2914
+ key: 'leaderboard',
2915
+ name: '排行榜',
2916
+ description: '读写小程序云排行榜。',
2917
+ access: 'declaration',
2918
+ risk: 'medium',
2919
+ config: {},
2920
+ methods: [
2921
+ 'cloud.leaderboard.submit',
2922
+ 'cloud.leaderboard.getList',
2923
+ 'cloud.leaderboard.getCurrentUserEntry',
2924
+ 'cloud.leaderboard.deleteCurrentUserEntry',
2925
+ 'cloud.leaderboard.getInfo',
2926
+ ],
2927
+ conditions: ['排行榜必须已创建,用户和资源规则保持不变。'],
2928
+ },
2929
+ {
2930
+ key: 'network',
2931
+ name: '网络',
2932
+ description: '通过 network 模块请求或下载外部资源。',
2933
+ access: 'platform-approval',
2934
+ risk: 'high',
2935
+ config: {
2936
+ useOfficialDomain: {
2937
+ type: 'boolean',
2938
+ required: false,
2939
+ default: false,
2940
+ description: '是否申请访问小黑盒官方域名。',
2941
+ },
2942
+ },
2943
+ methods: ['network.request', 'network.download'],
2944
+ conditions: ['声明后仍需平台批准,并继续执行网络安全策略。'],
2945
+ },
2946
+ ];
2947
+ new Set(MINI_PROGRAM_PERMISSION_KEYS);
2948
+
2949
+ new Set(MINI_PROGRAM_PERMISSION_KEYS);
2950
+
2951
+ const MINIAPP_PERMISSION_KEYS = MINI_PROGRAM_PERMISSION_KEYS;
2952
+ function parseMiniappPermissions(value, options) {
2953
+ const sourceLabel = options.sourceLabel ?? 'package.json#heybox.permissions';
2954
+ if (!options.present) {
2955
+ if (requiresExplicitPermissions(options.sdkVersion)) {
2956
+ throw new Error(`${sourceLabel} 在 @heybox/hb-sdk ${options.sdkVersion} 中必须声明;无权限需求时请配置空对象 {}`);
2957
+ }
2958
+ return { declared: false };
2959
+ }
2960
+ if (!isRecord$1(value)) {
2961
+ throw new Error(`${sourceLabel} 必须是 JSON 对象`);
2962
+ }
2963
+ const knownKeys = new Set(MINIAPP_PERMISSION_KEYS);
2964
+ for (const key of Object.keys(value)) {
2965
+ if (!knownKeys.has(key))
2966
+ throw new Error(`${sourceLabel} 包含未知权限:${key}`);
2967
+ }
2968
+ const permissions = {};
2969
+ for (const key of MINIAPP_PERMISSION_KEYS) {
2970
+ if (!Object.prototype.hasOwnProperty.call(value, key))
2971
+ continue;
2972
+ const declaration = value[key];
2973
+ const itemLabel = `${sourceLabel}.${key}`;
2974
+ if (!isRecord$1(declaration))
2975
+ throw new Error(`${itemLabel} 必须是 JSON 对象`);
2976
+ const definition = MINI_PROGRAM_PERMISSION_CATALOG.find((item) => item.key === key);
2977
+ if (!definition)
2978
+ throw new Error(`权限目录缺少定义:${key}`);
2979
+ const allowedFields = new Set(['enabled', ...Object.keys(definition.config)]);
2980
+ for (const field of Object.keys(declaration)) {
2981
+ if (!allowedFields.has(field))
2982
+ throw new Error(`${itemLabel} 包含未知字段:${field}`);
2983
+ }
2984
+ if (typeof declaration.enabled !== 'boolean')
2985
+ throw new Error(`${itemLabel}.enabled 必须是 boolean`);
2986
+ let useOfficialDomain = false;
2987
+ if (key === 'network') {
2988
+ if (declaration.useOfficialDomain !== undefined && typeof declaration.useOfficialDomain !== 'boolean') {
2989
+ throw new Error(`${itemLabel}.useOfficialDomain 必须是 boolean`);
2990
+ }
2991
+ useOfficialDomain = declaration.useOfficialDomain === true;
2992
+ }
2993
+ if (!declaration.enabled)
2994
+ continue;
2995
+ permissions[key] = key === 'network' ? { enabled: true, useOfficialDomain } : { enabled: true };
2996
+ }
2997
+ return { declared: true, permissions: permissions };
2998
+ }
2999
+ function requiresExplicitPermissions(sdkVersion) {
3000
+ const parsed = semverExports.parse(sdkVersion);
3001
+ if (!parsed)
3002
+ throw new Error(`sdkVersion 必须是合法 SemVer:${sdkVersion}`);
3003
+ return parsed.major > 0 || parsed.minor >= 9;
3004
+ }
3005
+ function getMissingPermissionsWarning(sourceLabel = 'package.json#heybox.permissions') {
3006
+ return `${sourceLabel} 尚未声明。当前按 0.8 兼容行为构建;0.9.0 起将拒绝构建,请尽快补充权限声明(无权限需求时配置 {})。`;
3007
+ }
3008
+ function isRecord$1(value) {
3009
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
3010
+ }
3011
+
2798
3012
  const MINIAPP_PLATFORM_VALUES = ['android', 'ios', 'ohos', 'windows', 'macos', 'linux'];
2799
3013
  function validateMiniappPackageVersionForBuild(version) {
2800
3014
  return validateMiniappManifestVersion(version, 'package.json.version');
@@ -2808,7 +3022,13 @@ function validateMiniappManifestVersion(version, sourceLabel = 'manifest.version
2808
3022
  }
2809
3023
  function renderMiniappManifest(manifest) {
2810
3024
  const platforms = validateMiniappPlatforms(manifest.platforms, 'manifest.platforms');
2811
- return `${JSON.stringify({ version: manifest.version, sdkVersion: manifest.sdkVersion, platforms }, null, 2)}\n`;
3025
+ const output = {
3026
+ version: manifest.version,
3027
+ sdkVersion: manifest.sdkVersion,
3028
+ platforms,
3029
+ ...(manifest.permissions === undefined ? {} : { permissions: manifest.permissions }),
3030
+ };
3031
+ return `${JSON.stringify(output, null, 2)}\n`;
2812
3032
  }
2813
3033
  function validateMiniappPlatforms(platforms, sourceLabel = 'platforms') {
2814
3034
  if (!Array.isArray(platforms) || platforms.length === 0) {
@@ -2863,6 +3083,21 @@ function findNearestPackageJsonPath(startDir) {
2863
3083
  }
2864
3084
  }
2865
3085
  function readMiniappVersionFromPackageJson(root) {
3086
+ return readMiniappPackageJson(root, true).version;
3087
+ }
3088
+ function readMiniappPermissionsFromPackageJson(root, sdkVersion) {
3089
+ const { packageJson } = readMiniappPackageJson(root, false);
3090
+ const heybox = packageJson.heybox;
3091
+ if (heybox !== undefined && !isRecord(heybox)) {
3092
+ throw new Error('@heybox/hb-sdk 提示:package.json#heybox 必须是 JSON 对象');
3093
+ }
3094
+ const present = isRecord(heybox) && Object.prototype.hasOwnProperty.call(heybox, 'permissions');
3095
+ return parseMiniappPermissions(isRecord(heybox) ? heybox.permissions : undefined, {
3096
+ present,
3097
+ sdkVersion,
3098
+ });
3099
+ }
3100
+ function readMiniappPackageJson(root, requireVersion) {
2866
3101
  const packageJsonPath = findNearestPackageJsonPath(root);
2867
3102
  let content;
2868
3103
  try {
@@ -2878,10 +3113,13 @@ function readMiniappVersionFromPackageJson(root) {
2878
3113
  catch (error) {
2879
3114
  throw new Error(`@heybox/hb-sdk 解析 package.json 失败:${formatReason(error)}`);
2880
3115
  }
2881
- if (typeof packageJson.version !== 'string' || packageJson.version.trim() === '') {
3116
+ if (requireVersion && (typeof packageJson.version !== 'string' || packageJson.version.trim() === '')) {
2882
3117
  throw new Error('@heybox/hb-sdk 提示:package.json.version 必须是非空字符串');
2883
3118
  }
2884
- return packageJson.version.trim();
3119
+ return { packageJson, version: typeof packageJson.version === 'string' ? packageJson.version.trim() : '' };
3120
+ }
3121
+ function isRecord(value) {
3122
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
2885
3123
  }
2886
3124
  function formatReason(error) {
2887
3125
  if (error instanceof Error && error.message) {
@@ -11320,10 +11558,6 @@ function parse(html, options) {
11320
11558
  return Parser.parse(html, options);
11321
11559
  }
11322
11560
 
11323
- const MINI_PROGRAM_MESSAGE_NAMESPACE = 'heybox:miniprogram';
11324
- const MINI_PROGRAM_MESSAGE_VERSION = 2;
11325
- const MINI_PROGRAM_BRIDGE_NONCE_PARAM = 'hb_mini_bridge_nonce';
11326
-
11327
11561
  const HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
11328
11562
  const RUNTIME_GATE_MARKER = 'data-heybox-runtime-gate';
11329
11563
  const RUNTIME_GATE_STYLE_MARKER = 'data-heybox-runtime-gate-style';
@@ -11646,8 +11880,29 @@ function shouldSkipMiniappPlatformCspFromEnv(env = process.env) {
11646
11880
  return env[HB_SDK_RUNTIME_PERMISSION_CONTEXT_ENV] === VERIFIED_RUNTIME_PERMISSION_CONTEXT && env[HB_SDK_RUNTIME_USE_OFFICIAL_DOMAIN_ENV] === '1';
11647
11881
  }
11648
11882
 
11883
+ /**
11884
+ * dev serve 时在入口 HTML 最早位置注入无依赖 console 捕获 bootstrap。
11885
+ *
11886
+ * 必须内联完整 patch 逻辑而非等 SDK 模块加载:vite client、业务代码的 console 调用
11887
+ * 都可能早于 SDK 单例初始化(如 [vite] connecting)。捕获条件:
11888
+ * 1. 构建期只有 vite serve 走到本分支(生产构建无此代码,双保险之 define 常量仍注入给 SDK 侧);
11889
+ * 2. 运行期仅调试 iframe(window.name 标记由调试台 iframe.name 与本 bootstrap 设置)激活。
11890
+ * 转发经 window.parent.postMessage(CSP connect-src 不约束 postMessage),失败静默,
11891
+ * 严禁回打 console 造成自触发循环。SDK 侧 installMiniDevConsoleForwarding 安装时
11892
+ * 检测同款 INSTALL_FLAG,不会重复 patch。
11893
+ */
11894
+ function injectMiniDevConsoleBootstrap(html) {
11895
+ const bootstrap = `<script>(function(){var LEVELS=['log','debug','info','warn','error'];var FLAG='${MINI_DEV_CONSOLE_INSTALL_FLAG}';var TYPE='${MINI_DEV_CONSOLE_EVENT_TYPE}';if(window.name!=='${MINI_DEV_IFRAME_WINDOW_NAME}')window.name='${MINI_DEV_IFRAME_WINDOW_NAME}';if(window[FLAG])return;var patched=false;for(var i=0;i<LEVELS.length;i++)(function(level){var original=console[level];if(typeof original!=='function')return;console[level]=function(){try{parent.postMessage({type:TYPE,detail:{level:level,args:Array.prototype.slice.call(arguments),timestamp:Date.now()}},'*')}catch(e){}return original.apply(this,arguments)};patched=true})(LEVELS[i]);if(patched)window[FLAG]=true})();</script>`;
11896
+ const headIndex = html.indexOf('<head>');
11897
+ if (headIndex < 0) {
11898
+ return html;
11899
+ }
11900
+ const insertAt = headIndex + '<head>'.length;
11901
+ return `${html.slice(0, insertAt)}${bootstrap}${html.slice(insertAt)}`;
11902
+ }
11649
11903
  const sdkVersionPlaceholder = ['__HB_SDK', 'VERSION__'].join('_');
11650
11904
  const sdkVersionBuildConstant = ['__HB_SDK_BUILD', 'VERSION__'].join('_');
11905
+ const miniDevLoggingConstant = ['__HB_SDK_DEV', 'LOGGING__'].join('_');
11651
11906
  function miniappManifest(options) {
11652
11907
  const platforms = validateMiniappPlatforms(options?.platforms, 'miniappManifest().platforms');
11653
11908
  const skipPlatformCsp = shouldSkipMiniappPlatformCspFromEnv();
@@ -11659,12 +11914,17 @@ function miniappManifest(options) {
11659
11914
  let priorBuildError;
11660
11915
  return {
11661
11916
  name: 'heybox-miniapp-manifest',
11662
- config(config) {
11917
+ config(config, env) {
11663
11918
  assertMiniappViteBase(config.base);
11919
+ // command 在本钩子时序早于 configResolved,必须取自 env 命令而非闭包;
11920
+ // 旧调用方(测试等)未传 env 时按 build 处理。
11921
+ const isServe = env?.command === 'serve';
11664
11922
  return {
11665
11923
  ...(config.base === undefined ? { base: './' } : {}),
11666
11924
  define: {
11667
11925
  [sdkVersionBuildConstant]: JSON.stringify(resolveSdkVersion()),
11926
+ // 仅 vite serve(hb-sdk dev)注入;vite build / hb-sdk build 永不注入,捕获模块被死代码消除。
11927
+ ...(isServe ? { [miniDevLoggingConstant]: 'true' } : {}),
11668
11928
  },
11669
11929
  };
11670
11930
  },
@@ -11686,12 +11946,12 @@ function miniappManifest(options) {
11686
11946
  }
11687
11947
  },
11688
11948
  transformIndexHtml(html) {
11689
- return enforceMiniappHtmlPolicy(html, {
11949
+ const enforced = enforceMiniappHtmlPolicy(html, {
11690
11950
  ...(command === 'serve' ? { hmrWebSocketUrl } : {}),
11691
11951
  skipPlatformCsp,
11692
11952
  });
11693
- },
11694
- async closeBundle() {
11953
+ return command === 'serve' ? injectMiniDevConsoleBootstrap(enforced) : enforced;
11954
+ }, async closeBundle() {
11695
11955
  // Rollup 在 buildStart/transform 失败后仍会调用 closeBundle。
11696
11956
  // 若此时再抛「缺 index.html」等二次错误,会掩盖真实失败原因。
11697
11957
  if (priorBuildError) {
@@ -11699,6 +11959,9 @@ function miniappManifest(options) {
11699
11959
  }
11700
11960
  const version = validateMiniappPackageVersionForBuild(readMiniappVersionFromPackageJson(root));
11701
11961
  const sdkVersion = resolveSdkVersion();
11962
+ const parsedPermissions = readMiniappPermissionsFromPackageJson(root, sdkVersion);
11963
+ if (!parsedPermissions.declared)
11964
+ this.warn(getMissingPermissionsWarning());
11702
11965
  const outputRoot = path.resolve(root, outDir);
11703
11966
  const htmlFiles = collectHtmlFiles(outputRoot);
11704
11967
  const unexpectedHtmlFiles = htmlFiles.filter((file) => path.relative(outputRoot, file) !== 'index.html');
@@ -11714,7 +11977,12 @@ function miniappManifest(options) {
11714
11977
  node_fs.writeFileSync(indexHtmlPath, enforceMiniappHtmlPolicy(node_fs.readFileSync(indexHtmlPath, 'utf8'), { skipPlatformCsp }));
11715
11978
  const manifestPath = path.join(outputRoot, 'manifest.json');
11716
11979
  node_fs.mkdirSync(path.dirname(manifestPath), { recursive: true });
11717
- node_fs.writeFileSync(manifestPath, renderMiniappManifest({ version, sdkVersion, platforms }));
11980
+ node_fs.writeFileSync(manifestPath, renderMiniappManifest({
11981
+ version,
11982
+ sdkVersion,
11983
+ platforms,
11984
+ ...(parsedPermissions.permissions === undefined ? {} : { permissions: parsedPermissions.permissions }),
11985
+ }));
11718
11986
  },
11719
11987
  };
11720
11988
  }