@heybox/hb-sdk 0.6.3 → 0.6.4

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.
@@ -7,6 +7,58 @@ const MINI_PROGRAM_BRIDGE_NONCE_PARAM = 'hb_mini_bridge_nonce';
7
7
  /** SDK 向 Runtime 报告 CSP 违规的内部方法名。 */
8
8
  const SDK_CSP_VIOLATION_METHOD = 'sdk.csp.violation';
9
9
 
10
+ const MANAGED_RUNTIME_PERMISSION_KEYS = new Set(['network.request']);
11
+ /**
12
+ * 判断 permission key 是否由 Runtime 权限快照管理。
13
+ *
14
+ * @param key 待判断的 permission key。
15
+ * @returns 该 key 需要读取 Runtime 权限快照时返回 `true`。
16
+ */
17
+ function isManagedMiniProgramRuntimePermissionKey(key) {
18
+ return MANAGED_RUNTIME_PERMISSION_KEYS.has(key);
19
+ }
20
+ /**
21
+ * 解析服务端权限快照;格式不完整时整份快照失效并 fail closed。
22
+ *
23
+ * @param snapshot 待校验的服务端权限快照。
24
+ * @returns 解析状态和通过校验的受管权限。
25
+ */
26
+ function parseMiniProgramRuntimePermissions(snapshot) {
27
+ if (!isRecord$2(snapshot) || snapshot.schema_version !== 1 || !Array.isArray(snapshot.entries)) {
28
+ return { valid: false, permissions: {} };
29
+ }
30
+ if (snapshot.revision !== undefined && (typeof snapshot.revision !== 'number' || !Number.isInteger(snapshot.revision) || snapshot.revision < 0)) {
31
+ return { valid: false, permissions: {} };
32
+ }
33
+ const seenKeys = new Set();
34
+ const permissions = {};
35
+ for (const rawEntry of snapshot.entries) {
36
+ if (!isRecord$2(rawEntry) || typeof rawEntry.key !== 'string' || !rawEntry.key.trim()) {
37
+ return { valid: false, permissions: {} };
38
+ }
39
+ const key = rawEntry.key.trim();
40
+ if (seenKeys.has(key) || (rawEntry.status !== 'enabled' && rawEntry.status !== 'disabled') || !isRecord$2(rawEntry.config)) {
41
+ return { valid: false, permissions: {} };
42
+ }
43
+ seenKeys.add(key);
44
+ if (!isManagedMiniProgramRuntimePermissionKey(key)) {
45
+ continue;
46
+ }
47
+ if (key === 'network.request' && typeof rawEntry.config.useOfficialDomain !== 'boolean') {
48
+ return { valid: false, permissions: {} };
49
+ }
50
+ permissions[key] = {
51
+ key,
52
+ status: rawEntry.status,
53
+ config: { useOfficialDomain: rawEntry.config.useOfficialDomain },
54
+ };
55
+ }
56
+ return { valid: true, permissions };
57
+ }
58
+ function isRecord$2(value) {
59
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
60
+ }
61
+
10
62
  /**
11
63
  * 登录授权能力方法名。
12
64
  *
@@ -3445,6 +3497,91 @@ function formatBlockedResource(payload) {
3445
3497
  return '未知资源';
3446
3498
  }
3447
3499
 
3500
+ const DEFAULT_PERMISSION_STATE = {
3501
+ networkRequest: {
3502
+ status: 'disabled',
3503
+ useOfficialDomain: false,
3504
+ },
3505
+ };
3506
+ function createMiniProgramMockPermissionState(context) {
3507
+ const parsed = parseMiniProgramRuntimePermissions(context.runtimePermissions);
3508
+ const networkRequest = parsed.permissions['network.request'];
3509
+ return parsed.valid && networkRequest
3510
+ ? {
3511
+ networkRequest: {
3512
+ status: networkRequest.status,
3513
+ useOfficialDomain: networkRequest.config.useOfficialDomain === true,
3514
+ },
3515
+ }
3516
+ : cloneDefaultPermissionState();
3517
+ }
3518
+ function createMiniProgramMockRuntimePermissions(state) {
3519
+ return {
3520
+ schema_version: 1,
3521
+ entries: [
3522
+ {
3523
+ key: 'network.request',
3524
+ status: state.networkRequest.status,
3525
+ config: {
3526
+ useOfficialDomain: state.networkRequest.useOfficialDomain,
3527
+ },
3528
+ },
3529
+ ],
3530
+ };
3531
+ }
3532
+ function createMiniProgramDevReleaseDiagnostics(context, localState) {
3533
+ if (context.source === 'anonymous') {
3534
+ return [
3535
+ {
3536
+ code: 'REMOTE_CONTEXT_UNAVAILABLE',
3537
+ severity: 'warning',
3538
+ message: `未获取到线上权限配置,本地模拟不代表上线状态${context.warning ? `:${context.warning}` : ''}`,
3539
+ },
3540
+ ];
3541
+ }
3542
+ const parsedRemote = parseMiniProgramRuntimePermissions(context.runtimePermissions);
3543
+ if (!parsedRemote.valid) {
3544
+ return [
3545
+ {
3546
+ code: 'REMOTE_PERMISSIONS_INVALID',
3547
+ severity: 'warning',
3548
+ message: '线上权限配置不可用,本地模拟不代表上线状态。',
3549
+ },
3550
+ ];
3551
+ }
3552
+ const remoteNetworkRequest = parsedRemote.permissions['network.request'];
3553
+ const remoteState = remoteNetworkRequest
3554
+ ? {
3555
+ status: remoteNetworkRequest.status,
3556
+ useOfficialDomain: remoteNetworkRequest.config.useOfficialDomain === true,
3557
+ }
3558
+ : DEFAULT_PERMISSION_STATE.networkRequest;
3559
+ if (localState.networkRequest.status === 'enabled' && remoteState.status !== 'enabled') {
3560
+ return [
3561
+ {
3562
+ code: 'NETWORK_REQUEST_DISABLED_ONLINE',
3563
+ severity: 'warning',
3564
+ message: '当前本地允许 network.request,但线上权限已关闭;上线后请求会返回 PERMISSION_DENIED。',
3565
+ },
3566
+ ];
3567
+ }
3568
+ if (localState.networkRequest.status === 'enabled' && localState.networkRequest.useOfficialDomain && !remoteState.useOfficialDomain) {
3569
+ return [
3570
+ {
3571
+ code: 'OFFICIAL_DOMAIN_DISABLED_ONLINE',
3572
+ severity: 'warning',
3573
+ message: '当前本地允许访问小黑盒官方域名,但线上权限未开放;上线后相关请求会返回 PERMISSION_DENIED。',
3574
+ },
3575
+ ];
3576
+ }
3577
+ return [];
3578
+ }
3579
+ function cloneDefaultPermissionState() {
3580
+ return {
3581
+ networkRequest: { ...DEFAULT_PERMISSION_STATE.networkRequest },
3582
+ };
3583
+ }
3584
+
3448
3585
  async function getMiniProgramRuntimeUserInfo(platformAdapter) {
3449
3586
  const userId = await resolveCurrentUserId(platformAdapter);
3450
3587
  if (!userId) {
@@ -5031,46 +5168,11 @@ function isOfficialMiniProgramNetworkUrl(value) {
5031
5168
  }
5032
5169
  }
5033
5170
 
5034
- const MANAGED_RUNTIME_PERMISSION_KEYS = new Set(['network.request']);
5035
- /** 解析服务端权限快照;格式不完整时整份快照失效并 fail closed。 */
5036
- function parseMiniProgramRuntimePermissions(snapshot) {
5037
- if (!isRecord$1(snapshot) || snapshot.schema_version !== 1 || !Array.isArray(snapshot.entries)) {
5038
- return { valid: false, permissions: {} };
5039
- }
5040
- if (snapshot.revision !== undefined &&
5041
- (typeof snapshot.revision !== 'number' || !Number.isInteger(snapshot.revision) || snapshot.revision < 0)) {
5042
- return { valid: false, permissions: {} };
5043
- }
5044
- const seenKeys = new Set();
5045
- const permissions = {};
5046
- for (const rawEntry of snapshot.entries) {
5047
- if (!isRecord$1(rawEntry) || typeof rawEntry.key !== 'string' || !rawEntry.key.trim()) {
5048
- return { valid: false, permissions: {} };
5049
- }
5050
- const key = rawEntry.key.trim();
5051
- if (seenKeys.has(key) || (rawEntry.status !== 'enabled' && rawEntry.status !== 'disabled') || !isRecord$1(rawEntry.config)) {
5052
- return { valid: false, permissions: {} };
5053
- }
5054
- seenKeys.add(key);
5055
- if (!MANAGED_RUNTIME_PERMISSION_KEYS.has(key)) {
5056
- continue;
5057
- }
5058
- if (key === 'network.request' && typeof rawEntry.config.useOfficialDomain !== 'boolean') {
5059
- return { valid: false, permissions: {} };
5060
- }
5061
- permissions[key] = {
5062
- key,
5063
- status: rawEntry.status,
5064
- config: { useOfficialDomain: rawEntry.config.useOfficialDomain },
5065
- };
5066
- }
5067
- return { valid: true, permissions };
5068
- }
5069
5171
  /** 创建只依赖启动快照的纯权限 evaluator,供正式 Runtime 与 Mock Host 共用。 */
5070
5172
  function createMiniProgramRuntimePermissionEvaluator(snapshot) {
5071
5173
  const parsed = parseMiniProgramRuntimePermissions(snapshot);
5072
5174
  return (permissionKey, payload) => {
5073
- if (!MANAGED_RUNTIME_PERMISSION_KEYS.has(permissionKey)) {
5175
+ if (!isManagedMiniProgramRuntimePermissionKey(permissionKey)) {
5074
5176
  return { allowed: true };
5075
5177
  }
5076
5178
  const entry = parsed.valid ? parsed.permissions[permissionKey] : undefined;
@@ -6306,6 +6408,7 @@ const elements = {
6306
6408
  cspViolations: queryElement('#csp-violations'),
6307
6409
  debugPageCopyStatus: queryElement('#debug-page-copy-status'),
6308
6410
  debugPageUrl: queryElement('#debug-page-url'),
6411
+ devContextStatus: queryElement('#dev-context-status'),
6309
6412
  device: queryElement('#device'),
6310
6413
  logs: queryElement('#logs'),
6311
6414
  macAppButton: queryElement('#mac-app-button'),
@@ -6315,6 +6418,9 @@ const elements = {
6315
6418
  mobileQrImage: queryElement('#mobile-qr-image'),
6316
6419
  mobileQrStatus: queryElement('#mobile-qr-status'),
6317
6420
  nicknameInput: queryElement('#nickname-input'),
6421
+ networkPermissionSelect: queryElement('#network-permission-select'),
6422
+ releaseDiagnostics: queryElement('#release-diagnostics'),
6423
+ resetPermissionsButton: queryElement('#reset-permissions-button'),
6318
6424
  storageSnapshot: queryElement('#storage-snapshot'),
6319
6425
  userStatus: queryElement('#user-status'),
6320
6426
  };
@@ -6325,13 +6431,15 @@ if (!miniUrl) {
6325
6431
  }
6326
6432
  const miniProgramUrl = miniUrl;
6327
6433
  const bootstrap = await loadMockHostBootstrap();
6328
- const runtimePermissions = bootstrap.runtimePermissions;
6434
+ const devContext = bootstrap.devContext;
6435
+ const initialPermissionState = createMiniProgramMockPermissionState(devContext);
6436
+ let permissionState = clonePermissionState(initialPermissionState);
6329
6437
  const iframe = document.createElement('iframe');
6330
6438
  iframe.src = appendNonce(miniProgramUrl, nonce);
6331
6439
  iframe.allow = 'clipboard-read; clipboard-write';
6332
6440
  elements.device.appendChild(iframe);
6333
6441
  elements.miniUrl.textContent = miniProgramUrl;
6334
- const runtime = new MiniProgramMockRuntime(createBrowserMockRuntimePlatformAdapter({
6442
+ const platformAdapter = createBrowserMockRuntimePlatformAdapter({
6335
6443
  iframe,
6336
6444
  getCurrentHref: () => location.href,
6337
6445
  getCurrentUser: () => currentUser,
@@ -6353,12 +6461,11 @@ const runtime = new MiniProgramMockRuntime(createBrowserMockRuntimePlatformAdapt
6353
6461
  storage.set(key, value);
6354
6462
  updateStorageSnapshot();
6355
6463
  },
6356
- }), {
6357
- onAuthChange: (result) => postEvent('authChange', result),
6358
- runtimePermissions,
6359
6464
  });
6465
+ let runtime = createRuntime();
6360
6466
  updateUserStatus();
6361
6467
  updateStorageSnapshot();
6468
+ updatePermissionControls();
6362
6469
  window.addEventListener('message', handleMessage);
6363
6470
  window.addEventListener('beforeunload', () => {
6364
6471
  postEvent('unload', { timestamp: Date.now() });
@@ -6386,7 +6493,42 @@ elements.copyDebugPageUrlButton.addEventListener('click', () => {
6386
6493
  elements.mobileLanSelect.addEventListener('change', () => {
6387
6494
  void updateMobileQrCode();
6388
6495
  });
6496
+ elements.networkPermissionSelect.addEventListener('change', () => {
6497
+ permissionState.networkRequest.status =
6498
+ elements.networkPermissionSelect.value === 'enabled' ? 'enabled' : 'disabled';
6499
+ rebuildRuntimeWithPermissions();
6500
+ });
6501
+ elements.resetPermissionsButton.addEventListener('click', () => {
6502
+ permissionState = clonePermissionState(initialPermissionState);
6503
+ rebuildRuntimeWithPermissions();
6504
+ });
6389
6505
  const mobileAppQrReady = setupMobileAppQr();
6506
+ function createRuntime() {
6507
+ return new MiniProgramMockRuntime(platformAdapter, {
6508
+ onAuthChange: (result) => postEvent('authChange', result),
6509
+ runtimePermissions: createMiniProgramMockRuntimePermissions(permissionState),
6510
+ });
6511
+ }
6512
+ function rebuildRuntimeWithPermissions() {
6513
+ runtime = createRuntime();
6514
+ updatePermissionControls();
6515
+ }
6516
+ function updatePermissionControls() {
6517
+ elements.devContextStatus.textContent =
6518
+ devContext.source === 'remote'
6519
+ ? `已连接线上配置${devContext.miniProgramId ? ` · ${devContext.miniProgramId}` : ''}`
6520
+ : '匿名本地沙箱';
6521
+ elements.networkPermissionSelect.value = permissionState.networkRequest.status;
6522
+ const diagnostics = createMiniProgramDevReleaseDiagnostics(devContext, permissionState);
6523
+ elements.releaseDiagnostics.innerHTML = diagnostics
6524
+ .map((diagnostic) => `<li>${escapeHtml(diagnostic.message)}</li>`)
6525
+ .join('');
6526
+ }
6527
+ function clonePermissionState(state) {
6528
+ return {
6529
+ networkRequest: { ...state.networkRequest },
6530
+ };
6531
+ }
6390
6532
  function handleMessage(event) {
6391
6533
  if (event.source !== iframe.contentWindow || !isBridgeMessage(event.data)) {
6392
6534
  return;
@@ -6599,10 +6741,17 @@ async function loadMockHostBootstrap() {
6599
6741
  });
6600
6742
  const payload = await response.json();
6601
6743
  if (!isRecord(payload)) {
6602
- return { lanAddresses: [] };
6744
+ return {
6745
+ devContext: {
6746
+ source: 'anonymous',
6747
+ warning: 'dev context 响应格式无效。',
6748
+ },
6749
+ lanAddresses: [],
6750
+ };
6603
6751
  }
6604
6752
  return {
6605
6753
  defaultLanAddressId: typeof payload.defaultLanAddressId === 'string' ? payload.defaultLanAddressId : undefined,
6754
+ devContext: readDevContext(payload.devContext, payload.runtimePermissions),
6606
6755
  lanAddresses: Array.isArray(payload.lanAddresses) ? payload.lanAddresses.filter(isLanAddress) : [],
6607
6756
  macAppProtocol: typeof payload.macAppProtocol === 'string' ? payload.macAppProtocol : undefined,
6608
6757
  nativeAppLaunchUnavailableReason: typeof payload.nativeAppLaunchUnavailableReason === 'string'
@@ -6613,11 +6762,33 @@ async function loadMockHostBootstrap() {
6613
6762
  }
6614
6763
  catch {
6615
6764
  return {
6765
+ devContext: {
6766
+ source: 'anonymous',
6767
+ warning: '真机调试配置加载失败,请重启 hb-sdk dev 后重试。',
6768
+ },
6616
6769
  lanAddresses: [],
6617
6770
  nativeAppLaunchUnavailableReason: '真机调试配置加载失败,请重启 hb-sdk dev 后重试。',
6618
6771
  };
6619
6772
  }
6620
6773
  }
6774
+ function readDevContext(value, legacyRuntimePermissions) {
6775
+ if (isRecord(value) && value.source === 'remote') {
6776
+ return {
6777
+ source: 'remote',
6778
+ miniProgramId: typeof value.miniProgramId === 'string' ? value.miniProgramId : undefined,
6779
+ runtimePermissions: value.runtimePermissions ?? legacyRuntimePermissions,
6780
+ };
6781
+ }
6782
+ if (isRecord(value) && value.source === 'anonymous') {
6783
+ return {
6784
+ source: 'anonymous',
6785
+ warning: typeof value.warning === 'string' ? value.warning : undefined,
6786
+ };
6787
+ }
6788
+ return legacyRuntimePermissions === undefined
6789
+ ? { source: 'anonymous', warning: '未获取到 dev context。' }
6790
+ : { source: 'remote', runtimePermissions: legacyRuntimePermissions };
6791
+ }
6621
6792
  function setupMobileLanSelect() {
6622
6793
  elements.mobileLanSelect.innerHTML = '';
6623
6794
  lanAddresses.forEach((address) => {
package/dist/index.cjs.js CHANGED
@@ -325,7 +325,7 @@ function createMessageId() {
325
325
  /** 构建时替换为当前发布包的实际版本。 */
326
326
  const HB_SDK_VERSION = typeof undefined === 'string'
327
327
  ? undefined
328
- : '0.6.3';
328
+ : '0.6.4';
329
329
 
330
330
  /**
331
331
  * 判断未知数据是否符合小程序 bridge 消息信封。
package/dist/index.esm.js CHANGED
@@ -321,7 +321,7 @@ function createMessageId() {
321
321
  /** 构建时替换为当前发布包的实际版本。 */
322
322
  const HB_SDK_VERSION = typeof undefined === 'string'
323
323
  ? undefined
324
- : '0.6.3';
324
+ : '0.6.4';
325
325
 
326
326
  /**
327
327
  * 判断未知数据是否符合小程序 bridge 消息信封。
@@ -32,6 +32,58 @@ function isMiniProgramBridgeMessage(value) {
32
32
  typeof message.type === 'string');
33
33
  }
34
34
 
35
+ const MANAGED_RUNTIME_PERMISSION_KEYS = new Set(['network.request']);
36
+ /**
37
+ * 判断 permission key 是否由 Runtime 权限快照管理。
38
+ *
39
+ * @param key 待判断的 permission key。
40
+ * @returns 该 key 需要读取 Runtime 权限快照时返回 `true`。
41
+ */
42
+ function isManagedMiniProgramRuntimePermissionKey(key) {
43
+ return MANAGED_RUNTIME_PERMISSION_KEYS.has(key);
44
+ }
45
+ /**
46
+ * 解析服务端权限快照;格式不完整时整份快照失效并 fail closed。
47
+ *
48
+ * @param snapshot 待校验的服务端权限快照。
49
+ * @returns 解析状态和通过校验的受管权限。
50
+ */
51
+ function parseMiniProgramRuntimePermissions(snapshot) {
52
+ if (!isRecord(snapshot) || snapshot.schema_version !== 1 || !Array.isArray(snapshot.entries)) {
53
+ return { valid: false, permissions: {} };
54
+ }
55
+ if (snapshot.revision !== undefined && (typeof snapshot.revision !== 'number' || !Number.isInteger(snapshot.revision) || snapshot.revision < 0)) {
56
+ return { valid: false, permissions: {} };
57
+ }
58
+ const seenKeys = new Set();
59
+ const permissions = {};
60
+ for (const rawEntry of snapshot.entries) {
61
+ if (!isRecord(rawEntry) || typeof rawEntry.key !== 'string' || !rawEntry.key.trim()) {
62
+ return { valid: false, permissions: {} };
63
+ }
64
+ const key = rawEntry.key.trim();
65
+ if (seenKeys.has(key) || (rawEntry.status !== 'enabled' && rawEntry.status !== 'disabled') || !isRecord(rawEntry.config)) {
66
+ return { valid: false, permissions: {} };
67
+ }
68
+ seenKeys.add(key);
69
+ if (!isManagedMiniProgramRuntimePermissionKey(key)) {
70
+ continue;
71
+ }
72
+ if (key === 'network.request' && typeof rawEntry.config.useOfficialDomain !== 'boolean') {
73
+ return { valid: false, permissions: {} };
74
+ }
75
+ permissions[key] = {
76
+ key,
77
+ status: rawEntry.status,
78
+ config: { useOfficialDomain: rawEntry.config.useOfficialDomain },
79
+ };
80
+ }
81
+ return { valid: true, permissions };
82
+ }
83
+ function isRecord(value) {
84
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
85
+ }
86
+
35
87
  /**
36
88
  * 登录授权能力方法名。
37
89
  *
@@ -364,4 +416,6 @@ exports.USER_GET_PLATFORM_ACCOUNT_OVERVIEW_METHOD = USER_GET_PLATFORM_ACCOUNT_OV
364
416
  exports.USER_GET_STEAM_GAME_LIST_METHOD = USER_GET_STEAM_GAME_LIST_METHOD;
365
417
  exports.VIEWPORT_GET_WINDOW_INFO_METHOD = VIEWPORT_GET_WINDOW_INFO_METHOD;
366
418
  exports.VIEWPORT_SET_NAVIGATION_BAR_STYLE_METHOD = VIEWPORT_SET_NAVIGATION_BAR_STYLE_METHOD;
419
+ exports.isManagedMiniProgramRuntimePermissionKey = isManagedMiniProgramRuntimePermissionKey;
367
420
  exports.isMiniProgramBridgeMessage = isMiniProgramBridgeMessage;
421
+ exports.parseMiniProgramRuntimePermissions = parseMiniProgramRuntimePermissions;
@@ -30,6 +30,58 @@ function isMiniProgramBridgeMessage(value) {
30
30
  typeof message.type === 'string');
31
31
  }
32
32
 
33
+ const MANAGED_RUNTIME_PERMISSION_KEYS = new Set(['network.request']);
34
+ /**
35
+ * 判断 permission key 是否由 Runtime 权限快照管理。
36
+ *
37
+ * @param key 待判断的 permission key。
38
+ * @returns 该 key 需要读取 Runtime 权限快照时返回 `true`。
39
+ */
40
+ function isManagedMiniProgramRuntimePermissionKey(key) {
41
+ return MANAGED_RUNTIME_PERMISSION_KEYS.has(key);
42
+ }
43
+ /**
44
+ * 解析服务端权限快照;格式不完整时整份快照失效并 fail closed。
45
+ *
46
+ * @param snapshot 待校验的服务端权限快照。
47
+ * @returns 解析状态和通过校验的受管权限。
48
+ */
49
+ function parseMiniProgramRuntimePermissions(snapshot) {
50
+ if (!isRecord(snapshot) || snapshot.schema_version !== 1 || !Array.isArray(snapshot.entries)) {
51
+ return { valid: false, permissions: {} };
52
+ }
53
+ if (snapshot.revision !== undefined && (typeof snapshot.revision !== 'number' || !Number.isInteger(snapshot.revision) || snapshot.revision < 0)) {
54
+ return { valid: false, permissions: {} };
55
+ }
56
+ const seenKeys = new Set();
57
+ const permissions = {};
58
+ for (const rawEntry of snapshot.entries) {
59
+ if (!isRecord(rawEntry) || typeof rawEntry.key !== 'string' || !rawEntry.key.trim()) {
60
+ return { valid: false, permissions: {} };
61
+ }
62
+ const key = rawEntry.key.trim();
63
+ if (seenKeys.has(key) || (rawEntry.status !== 'enabled' && rawEntry.status !== 'disabled') || !isRecord(rawEntry.config)) {
64
+ return { valid: false, permissions: {} };
65
+ }
66
+ seenKeys.add(key);
67
+ if (!isManagedMiniProgramRuntimePermissionKey(key)) {
68
+ continue;
69
+ }
70
+ if (key === 'network.request' && typeof rawEntry.config.useOfficialDomain !== 'boolean') {
71
+ return { valid: false, permissions: {} };
72
+ }
73
+ permissions[key] = {
74
+ key,
75
+ status: rawEntry.status,
76
+ config: { useOfficialDomain: rawEntry.config.useOfficialDomain },
77
+ };
78
+ }
79
+ return { valid: true, permissions };
80
+ }
81
+ function isRecord(value) {
82
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
83
+ }
84
+
33
85
  /**
34
86
  * 登录授权能力方法名。
35
87
  *
@@ -327,4 +379,4 @@ const MINI_PROGRAM_PROTOCOL_CAPABILITIES = [
327
379
  },
328
380
  ];
329
381
 
330
- export { AUTH_LOGIN_METHOD, CLOUD_LEADERBOARD_DELETE_CURRENT_USER_ENTRY_METHOD, CLOUD_LEADERBOARD_GET_CURRENT_USER_ENTRY_METHOD, CLOUD_LEADERBOARD_GET_INFO_METHOD, CLOUD_LEADERBOARD_GET_LIST_METHOD, CLOUD_LEADERBOARD_SUBMIT_METHOD, DEVICE_SET_CLIPBOARD_METHOD, DEVICE_VIBRATE_METHOD, MINI_PROGRAM_BRIDGE_NONCE_PARAM, MINI_PROGRAM_MESSAGE_NAMESPACE, MINI_PROGRAM_MESSAGE_VERSION, MINI_PROGRAM_PROTOCOL_CAPABILITIES, NAVIGATION_CLOSE_METHOD, NAVIGATION_OPEN_APP_PAGE_METHOD, NAVIGATION_RELOAD_METHOD, NETWORK_REQUEST_METHOD, RUNTIME_LOCATION_PROBE_METHOD, SDK_CSP_VIOLATION_METHOD, SDK_HANDSHAKE_METHOD, SDK_LOCATION_REPORT_METHOD, SHARE_SCREENSHOT_METHOD, SHARE_SHOW_SHARE_MENU_METHOD, STORAGE_GET_STORAGE_METHOD, STORAGE_SET_STORAGE_METHOD, UI_HIDE_LOADING_METHOD, UI_SHOW_LOADING_METHOD, UI_SHOW_TOAST_METHOD, USER_GET_CURRENT_USER_DETAIL_METHOD, USER_GET_CURRENT_USER_PROFILE_METHOD, USER_GET_INFO_METHOD, USER_GET_PLATFORM_ACCOUNT_INFO_METHOD, USER_GET_PLATFORM_ACCOUNT_OVERVIEW_METHOD, USER_GET_STEAM_GAME_LIST_METHOD, VIEWPORT_GET_WINDOW_INFO_METHOD, VIEWPORT_SET_NAVIGATION_BAR_STYLE_METHOD, isMiniProgramBridgeMessage };
382
+ export { AUTH_LOGIN_METHOD, CLOUD_LEADERBOARD_DELETE_CURRENT_USER_ENTRY_METHOD, CLOUD_LEADERBOARD_GET_CURRENT_USER_ENTRY_METHOD, CLOUD_LEADERBOARD_GET_INFO_METHOD, CLOUD_LEADERBOARD_GET_LIST_METHOD, CLOUD_LEADERBOARD_SUBMIT_METHOD, DEVICE_SET_CLIPBOARD_METHOD, DEVICE_VIBRATE_METHOD, MINI_PROGRAM_BRIDGE_NONCE_PARAM, MINI_PROGRAM_MESSAGE_NAMESPACE, MINI_PROGRAM_MESSAGE_VERSION, MINI_PROGRAM_PROTOCOL_CAPABILITIES, NAVIGATION_CLOSE_METHOD, NAVIGATION_OPEN_APP_PAGE_METHOD, NAVIGATION_RELOAD_METHOD, NETWORK_REQUEST_METHOD, RUNTIME_LOCATION_PROBE_METHOD, SDK_CSP_VIOLATION_METHOD, SDK_HANDSHAKE_METHOD, SDK_LOCATION_REPORT_METHOD, SHARE_SCREENSHOT_METHOD, SHARE_SHOW_SHARE_MENU_METHOD, STORAGE_GET_STORAGE_METHOD, STORAGE_SET_STORAGE_METHOD, UI_HIDE_LOADING_METHOD, UI_SHOW_LOADING_METHOD, UI_SHOW_TOAST_METHOD, USER_GET_CURRENT_USER_DETAIL_METHOD, USER_GET_CURRENT_USER_PROFILE_METHOD, USER_GET_INFO_METHOD, USER_GET_PLATFORM_ACCOUNT_INFO_METHOD, USER_GET_PLATFORM_ACCOUNT_OVERVIEW_METHOD, USER_GET_STEAM_GAME_LIST_METHOD, VIEWPORT_GET_WINDOW_INFO_METHOD, VIEWPORT_SET_NAVIGATION_BAR_STYLE_METHOD, isManagedMiniProgramRuntimePermissionKey, isMiniProgramBridgeMessage, parseMiniProgramRuntimePermissions };
package/dist/vite.cjs.js CHANGED
@@ -7,7 +7,7 @@ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentS
7
7
  /** 构建时替换为当前发布包的实际版本。 */
8
8
  const HB_SDK_VERSION = typeof undefined === 'string'
9
9
  ? undefined
10
- : '0.6.3';
10
+ : '0.6.4';
11
11
 
12
12
  var re = {exports: {}};
13
13
 
package/dist/vite.esm.js CHANGED
@@ -4,7 +4,7 @@ import path from 'node:path';
4
4
  /** 构建时替换为当前发布包的实际版本。 */
5
5
  const HB_SDK_VERSION = typeof undefined === 'string'
6
6
  ? undefined
7
- : '0.6.3';
7
+ : '0.6.4';
8
8
 
9
9
  var re = {exports: {}};
10
10
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heybox/hb-sdk",
3
- "version": "0.6.3",
3
+ "version": "0.6.4",
4
4
  "sideEffects": [
5
5
  "./src/index.ts",
6
6
  "./src/core/singleton.ts",
@@ -92,7 +92,7 @@
92
92
  "vue": "^2.7.16",
93
93
  "vite": "^8.0.12",
94
94
  "vitest": "^3.2.4",
95
- "@heybox/hb-api": "~1.25.1"
95
+ "@heybox/hb-api": "~1.25.3"
96
96
  },
97
97
  "publishConfig": {
98
98
  "registry": "https://registry.npmjs.org/",
package/skill/SKILL.md CHANGED
@@ -49,7 +49,7 @@ Apply these instructions when writing, reviewing, or debugging code that consume
49
49
  ## Step 5: Use CLI workflows
50
50
 
51
51
  1. Use `hb-sdk create <project-name>` to scaffold a standalone external mini-program template.
52
- 2. Use `hb-sdk dev` for local browser debugging through the built-in mock runtime host. The project must bind `package.json.heybox.miniProgramId` and the CLI must be logged in so dev can load the real Runtime permission snapshot before starting Vite.
52
+ 2. Use `hb-sdk dev` for local browser, Mac App, or mobile App debugging through the built-in mock runtime host. Vite and native dev-shell entrypoints only require the local `mini_url`: a missing project binding, CLI login, remote dev-context failure, or the 3-second context timeout must fall back to an anonymous local sandbox instead of blocking startup. Anonymous mode denies managed capabilities by default. When a verified remote dev context is available, the Mock Host uses its Runtime permission snapshot as the initial local simulation and reports local-versus-online permission differences.
53
53
  3. Use `hb-sdk remote ...` for developer-owned remote mini-program management. Top-level `hb-sdk deploy` has been hard-cut and must not be recommended as a compatibility alias.
54
54
  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.
55
55
  5. Use `hb-sdk remote create` 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. CLI project configuration contains only `package.json.heybox.miniProgramId`; mini-program name, icon, and cover images are maintained on the Open platform version-publish flow. On first submit without prior approved profile, the server injects a default name and default images.
@@ -64,7 +64,7 @@ Apply these instructions when writing, reviewing, or debugging code that consume
64
64
  14. Add `--verbose` / `-v` only when diagnosing failures; default CLI errors are intentionally concise, while verbose output includes backend envelope, HTTP status, trace fields, raw body, or original submit-audit failure details.
65
65
  15. Use `--json` for script consumption of `hb-sdk remote` commands. With `--json`, stdout must contain exactly one JSON object; progress, warnings, update reminders, and verbose diagnostics must not pollute stdout.
66
66
  16. Do not expect custom base URLs to affect `hb-sdk doctor`, npm latest checks, or mock-host `network.request()`.
67
- 17. Use the Mock runtime host's "在 Mac 版 APP 中启动" button for Mac App debugging, or the "Mobile App" QR code after selecting a LAN interface for phone App debugging; the phone must be on the same LAN and use a Heybox App version that supports the mini-program dev shell.
67
+ 17. Use the Mock runtime host's "在 Mac 版 APP 中启动" button for Mac App debugging, or the "Mobile App" QR code after selecting a LAN interface for phone App debugging; these entrypoints remain available in anonymous mode. The phone must be on the same LAN and use a Heybox App version that supports the mini-program dev shell.
68
68
  18. Use `--port`, `--mock-port`, and `--no-open` when the default Vite/mock ports or browser opening behavior need to be controlled.
69
69
  19. Use `hb-sdk login`, `hb-sdk login status`, and `hb-sdk login clear` only for the CLI's own Heybox auth cache. Keep `hb-sdk login` top-level; it is not a remote mini-program command.
70
70
  20. Treat `selectedEntity` in the CLI auth cache as a non-authoritative hint snapshot only. Every remote command must use the server-side current entity as the source of truth.
@@ -93,6 +93,8 @@ For CLI and local development:
93
93
  2. Do not use `hb-sdk login` as a workaround for iframe SDK authentication.
94
94
  3. Do not bypass `hb-sdk dev` by adding a second browser mock host.
95
95
  4. Keep the Vite `miniappManifest()` plugin enabled so builds only start in a compatible APP Runtime. Normal deploy always runs the project build.
96
+ 5. Keep Mock Host permission overrides in devtools-only memory. They must not rebuild the iframe, interrupt Vite HMR, mutate online permissions, or be passed through URL query parameters.
97
+ 6. Treat remote dev context as optional diagnostics and initialization data. Do not use a public mini-program detail request as a dev startup gate.
96
98
 
97
99
  For host/runtime/protocol-maintenance code:
98
100
 
@@ -31,6 +31,13 @@ export {
31
31
  SDK_LOCATION_REPORT_METHOD,
32
32
  } from './protocol/constants';
33
33
  export { isMiniProgramBridgeMessage } from './protocol/guards';
34
+ export { isManagedMiniProgramRuntimePermissionKey, parseMiniProgramRuntimePermissions } from './protocol/runtime-permissions';
35
+ export type {
36
+ MiniProgramRuntimePermissionEntry,
37
+ MiniProgramRuntimePermissionStatus,
38
+ MiniProgramRuntimePermissionsSnapshot,
39
+ ParsedMiniProgramRuntimePermissions,
40
+ } from './protocol/runtime-permissions';
34
41
  export type {
35
42
  MiniProgramBridgeError,
36
43
  MiniProgramBridgeMessage,
@@ -139,11 +146,7 @@ export type {
139
146
  MiniProgramShareChannel,
140
147
  MiniProgramShowShareMenuOptions,
141
148
  } from './modules/share';
142
- export type {
143
- GetStoragePayload,
144
- GetStorageResult,
145
- SetStoragePayload,
146
- } from './modules/storage';
149
+ export type { GetStoragePayload, GetStorageResult, SetStoragePayload } from './modules/storage';
147
150
  export type {
148
151
  GetWindowInfoPayload,
149
152
  GetWindowInfoResult,
@@ -173,13 +176,7 @@ export type {
173
176
  ShowToastPayload,
174
177
  ShowToastResult,
175
178
  } from './modules/ui';
176
- export type {
177
- MiniProgramVibrateIntensity,
178
- SetClipboardPayload,
179
- SetClipboardResult,
180
- VibratePayload,
181
- VibrateResult,
182
- } from './modules/device';
179
+ export type { MiniProgramVibrateIntensity, SetClipboardPayload, SetClipboardResult, VibratePayload, VibrateResult } from './modules/device';
183
180
  export type {
184
181
  ClosePayload,
185
182
  CloseResult,
@@ -210,18 +207,22 @@ Reference 由 `@heybox/hb-sdk` 的公开导出与源码注释自动生成,不
210
207
 
211
208
  | 导出面 | 说明 |
212
209
  | --- | --- |
213
- | [Root API](api-root.md) | 来自 `src/index.ts` 的默认导出、命名导出与公开能力。 |
214
- | [Protocol API](#public-protocol-entrypoint) | 来自 `src/protocol.ts` 的协议常量、消息类型与 method 契约。 |
210
+ | [Root API](api-root.md) | `@heybox/hb-sdk` 的默认导出、命名导出与公开能力。 |
211
+ | [Protocol API](#public-protocol-entrypoint) | `@heybox/hb-sdk/protocol` 的协议常量、消息类型与 method 契约。 |
212
+ | [Miniapp Publish API](https://open.xiaoheihe.cn/docs/hb_sdk/reference/miniapp-publish/) | `@heybox/hb-sdk/miniapp-publish` 的构建产物发布前的公开校验工具。 |
213
+ | [Vite API](https://open.xiaoheihe.cn/docs/hb_sdk/reference/vite/) | `@heybox/hb-sdk/vite` 的Vite 工坊小程序插件。 |
215
214
 
216
215
  ## 查询建议
217
216
 
218
217
  - 想查业务接入路径:先看 [Guide](recipes.md)。
219
- - 想查导出符号:从 Root API 或 Protocol API 进入对应分类页。
218
+ - 想查导出符号:从上方对应公开入口进入分类页。
220
219
  - 想看场景化用法:优先看 Guide / Recipes 页面中的“进一步阅读”。
221
220
 
222
221
  ## 统计
223
222
 
224
223
  | 导出面 | Classes | Functions | Interfaces | Types | Constants |
225
224
  | --- | ---: | ---: | ---: | ---: | ---: |
226
- | Root API | 2 | 3 | 66 | 57 | 4 |
227
- | Protocol API | 0 | 1 | 43 | 68 | 35 |
225
+ | Root API | 2 | 3 | 66 | 57 | 0 |
226
+ | Protocol API | 0 | 3 | 43 | 67 | 32 |
227
+ | Miniapp Publish API | 0 | 5 | 2 | 0 | 0 |
228
+ | Vite API | 0 | 1 | 0 | 0 | 0 |
@@ -19,7 +19,7 @@
19
19
  ## Package metadata
20
20
 
21
21
  - Package: `@heybox/hb-sdk`
22
- - Version at generation time: `0.6.3`
22
+ - Version at generation time: `0.6.4`
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`
@@ -450,7 +450,9 @@ SDK 需要在黑盒小程序 iframe 容器内运行。父容器会为页面注
450
450
  npm run dev
451
451
  ```
452
452
 
453
- 调试页会通过 iframe 加载本地页面并补齐小程序 bridge 环境;浏览器 Mock mock host 的同源只读 bootstrap 接口读取服务端保留的 Runtime 权限快照,URL query 不能提供或覆盖权限。项目已绑定 `heybox.miniProgramId` 时,可以点击调试页里的「在 Mac APP 中启动」按钮,或在「Mobile App」区域选择局域网网卡后用手机小黑盒 APP 扫码;真机 dev shell 只携带绑定的小程序 ID 和本地页面地址,H5 宿主按 ID 从公开详情接口读取可信 Runtime 权限快照。项目未绑定时仍可使用默认拒绝受管能力的浏览器 Mock,但 Mac 和手机真机入口会明确提示先运行 `hb-sdk remote create` `hb-sdk remote bind <mini-program-id>`,并保持不可用。真实容器加载开发 `mini_url` 前会提示“即将打开未经验证的开发网页。该页面可能由本机或局域网服务提供,请确认来源可信后继续。”,用户确认后才继续加载。Codex、VSCode 等内嵌浏览器可能无法唤起系统 APP;遇到这种情况时,请在系统浏览器中打开同一个调试页后重试。
453
+ 调试页会通过 iframe 加载本地页面并补齐小程序 bridge 环境。`hb-sdk dev` 的基础启动只依赖本地页面地址:即使项目未绑定、CLI 未登录或远端暂时不可用,浏览器 Mock、Mac 启动协议和手机二维码也会继续生成,真机 dev shell 以匿名本地沙箱加载 `mini_url`,不会把公开 `detail` 查询作为启动门禁。项目已绑定时会限时 3 秒读取远端 dev context;成功后 Mock Host 用真实 Runtime 权限快照初始化本地模拟,失败或超时则显示脱敏警告,并默认拒绝 `network.request` 等受管能力。需要定位降级原因时可使用 `hb-sdk dev --verbose`;详细错误只写入本地调试日志,其中 URL 用户名、密码和敏感 query/hash 会被遮蔽,不会进入 LAN bootstrap。
454
+
455
+ Mock Host 可以在内存中切换 devtools-only 的 `network.request` 权限,不会重建 iframe,因此不影响 Vite HMR;官方域名权限只读取线上快照,本地设置不会修改线上权限。调试页会对比已读取的线上快照,提示本地放开但上线后会返回 `PERMISSION_DENIED` 的差异。权限快照只通过 mock host 的同源只读 bootstrap 接口传递,URL query 不能提供或覆盖权限。真实容器加载开发 `mini_url` 前会提示“即将打开未经验证的开发网页。该页面可能由本机或局域网服务提供,请确认来源可信后继续。”,用户确认后才继续加载。Codex、VSCode 等内嵌浏览器可能无法唤起系统 APP;遇到这种情况时,请在系统浏览器中打开同一个调试页后重试。
454
456
 
455
457
  在未使用脚手架的 Vite 项目中,可以把命令加到 `package.json`:
456
458