@heybox/hb-sdk 0.7.0-alpha.5 → 0.7.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 (41) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +17 -16
  3. package/dist/cli-chunks/{build-35mI_IwM.cjs → build-CsXk21Yi.cjs} +2 -2
  4. package/dist/cli-chunks/{context-DysC5qrh.cjs → context-ClUhQYbA.cjs} +1 -1
  5. package/dist/cli-chunks/{create-BJSQzUeZ.cjs → create-vadbYtVj.cjs} +1 -1
  6. package/dist/cli-chunks/{dev-DiGtxt4q.cjs → dev-Dkk4qHsv.cjs} +5 -5
  7. package/dist/cli-chunks/{doctor-DWWVwcd7.cjs → doctor-DzgWYWKU.cjs} +1 -1
  8. package/dist/cli-chunks/{index-k5wJ6TMJ.cjs → index-BMddcNGi.cjs} +1 -1
  9. package/dist/cli-chunks/{index-CgCp6jGG.cjs → index-EYRMHb10.cjs} +14 -14
  10. package/dist/cli-chunks/{login-CzkMdcXM.cjs → login-5TM5_o8q.cjs} +2 -2
  11. package/dist/cli-chunks/{project-vite-TBMlJw8y.cjs → project-vite-eJ89lO8_.cjs} +1 -1
  12. package/dist/cli-chunks/{remote-DE9DIxqw.cjs → remote-DxiHjrI9.cjs} +4 -4
  13. package/dist/cli-chunks/{session--DdaUkcq.cjs → session-DYEm6ALx.cjs} +1 -1
  14. package/dist/cli.cjs +1 -1
  15. package/dist/devtools/mock-host/main.js +43 -23
  16. package/dist/index.cjs.js +108 -68
  17. package/dist/index.esm.js +107 -68
  18. package/dist/protocol.cjs.js +19 -0
  19. package/dist/protocol.esm.js +19 -1
  20. package/dist/templates/vue3-vite-ts/src/App.vue +29 -19
  21. package/dist/templates/vue3-vite-ts/src/__tests__/App.spec.ts +82 -9
  22. package/dist/vite.cjs.js +1 -1
  23. package/dist/vite.esm.js +1 -1
  24. package/package.json +1 -1
  25. package/skill/SKILL.md +8 -7
  26. package/skill/references/api-protocol.md +6 -2
  27. package/skill/references/api-root.md +65 -19
  28. package/skill/references/examples.md +30 -9
  29. package/skill/references/recipes.md +48 -26
  30. package/skill/references/safety-boundaries.md +3 -1
  31. package/skill/references/smoke-evaluation.md +3 -1
  32. package/skill/scripts/sync-references.mjs +31 -9
  33. package/skill/skill.json +4 -4
  34. package/types/core/client.d.ts +15 -9
  35. package/types/core/handshake-state.d.ts +24 -0
  36. package/types/core/sdk.d.ts +6 -17
  37. package/types/core/singleton.d.ts +5 -7
  38. package/types/index.d.ts +5 -3
  39. package/types/protocol/trusted-user-gesture.d.ts +6 -0
  40. package/types/protocol/types.d.ts +1 -1
  41. package/types/protocol.d.ts +1 -0
package/dist/index.cjs.js CHANGED
@@ -433,6 +433,43 @@ class MiniProgramEventBus {
433
433
  }
434
434
  }
435
435
 
436
+ /** 保存握手终态,并向订阅者同步派发唯一一次状态迁移。 */
437
+ class MiniProgramSDKHandshakeStateStore {
438
+ state = Object.freeze({ status: 'connecting' });
439
+ handlers = new Set();
440
+ getState() {
441
+ return this.state;
442
+ }
443
+ subscribe(handler) {
444
+ this.handlers.add(handler);
445
+ this.notify(handler, this.state);
446
+ return () => {
447
+ this.handlers.delete(handler);
448
+ };
449
+ }
450
+ clear() {
451
+ this.handlers.clear();
452
+ }
453
+ settle(state) {
454
+ if (this.state.status !== 'connecting') {
455
+ return;
456
+ }
457
+ const immutableState = Object.freeze(state.status === 'ready'
458
+ ? { status: 'ready' }
459
+ : { status: 'failed', error: state.error });
460
+ this.state = immutableState;
461
+ [...this.handlers].forEach((handler) => this.notify(handler, immutableState));
462
+ }
463
+ notify(handler, state) {
464
+ try {
465
+ handler(state);
466
+ }
467
+ catch {
468
+ // 订阅者异常不能阻断握手状态落定或其他订阅者。
469
+ }
470
+ }
471
+ }
472
+
436
473
  const historyObservers = new WeakMap();
437
474
  /** 共享同一个 History patch,并在最后一个订阅者退出时精确恢复原属性。 */
438
475
  function subscribeHistoryChanges(history, listener) {
@@ -603,7 +640,7 @@ function createMessageId() {
603
640
  /** 构建时替换为当前发布包的实际版本。 */
604
641
  const HB_SDK_VERSION = typeof undefined === 'string'
605
642
  ? undefined
606
- : '0.7.0-alpha.5';
643
+ : '0.7.0';
607
644
 
608
645
  /**
609
646
  * 判断未知数据是否符合小程序 bridge 消息信封。
@@ -642,11 +679,12 @@ class MiniProgramBridgeClient {
642
679
  };
643
680
  unsubscribeHistoryChanges;
644
681
  started = false;
645
- readySettled = false;
646
- readyPromise;
647
- resolveReady;
648
- rejectReady;
649
- readyTimer;
682
+ handshakeState = new MiniProgramSDKHandshakeStateStore();
683
+ handshakeSettled = false;
684
+ handshakePromise;
685
+ resolveHandshake;
686
+ rejectHandshake;
687
+ handshakeTimer;
650
688
  handshakeRetryTimer;
651
689
  destroyed = false;
652
690
  runtimeUnavailable = false;
@@ -659,16 +697,24 @@ class MiniProgramBridgeClient {
659
697
  this.targetOrigin = options.targetOrigin || readParentOrigin(this.selfWindow) || '*';
660
698
  this.nonce = options.nonce || readBridgeNonce(this.selfWindow);
661
699
  this.handleMessage = this.onMessage.bind(this);
662
- this.readyPromise = new Promise((resolve, reject) => {
663
- this.resolveReady = resolve;
664
- this.rejectReady = reject;
700
+ this.handshakePromise = new Promise((resolve, reject) => {
701
+ this.resolveHandshake = resolve;
702
+ this.rejectHandshake = reject;
665
703
  });
666
- this.readyPromise.catch(() => undefined);
704
+ this.handshakePromise.catch(() => undefined);
667
705
  this.ensureStarted();
668
706
  }
669
707
  /** 等待父容器握手完成。 */
670
- ready() {
671
- return this.readyPromise;
708
+ waitForHandshake() {
709
+ return this.handshakePromise;
710
+ }
711
+ /** 获取当前握手状态。 */
712
+ getHandshakeState() {
713
+ return this.handshakeState.getState();
714
+ }
715
+ /** 订阅握手状态,并立即接收当前状态。 */
716
+ onHandshakeStateChange(handler) {
717
+ return this.handshakeState.subscribe(handler);
672
718
  }
673
719
  /** 注册小程序事件监听。 */
674
720
  on(eventName, handler) {
@@ -686,7 +732,7 @@ class MiniProgramBridgeClient {
686
732
  if (this.runtimeUnavailable) {
687
733
  throw this.getRuntimeUnavailableError();
688
734
  }
689
- await this.ready();
735
+ await this.waitForHandshake();
690
736
  if (this.destroyed) {
691
737
  throw createSDKError('SDK_DESTROYED', '小程序 SDK 已销毁');
692
738
  }
@@ -735,16 +781,17 @@ class MiniProgramBridgeClient {
735
781
  this.unsubscribeHistoryChanges?.();
736
782
  this.unsubscribeHistoryChanges = undefined;
737
783
  const error = createSDKError('SDK_DESTROYED', '小程序 SDK 已销毁');
738
- this.failReady(error);
784
+ this.failHandshake(error);
785
+ this.handshakeState.clear();
739
786
  this.rejectAllPending(error);
740
787
  }
741
788
  ensureStarted() {
742
789
  if (this.destroyed) {
743
- this.failReady(createSDKError('SDK_DESTROYED', '小程序 SDK 已销毁'));
790
+ this.failHandshake(createSDKError('SDK_DESTROYED', '小程序 SDK 已销毁'));
744
791
  return;
745
792
  }
746
793
  if (this.runtimeUnavailable) {
747
- this.failReady(this.getRuntimeUnavailableError());
794
+ this.failHandshake(this.getRuntimeUnavailableError());
748
795
  return;
749
796
  }
750
797
  if (this.started) {
@@ -752,11 +799,11 @@ class MiniProgramBridgeClient {
752
799
  }
753
800
  this.started = true;
754
801
  if (!this.selfWindow || !this.targetWindow) {
755
- this.failReady(createSDKError('NOT_IN_IFRAME', '当前页面不在小程序沙盒 iframe 中'));
802
+ this.failHandshake(createSDKError('NOT_IN_IFRAME', '当前页面不在小程序沙盒 iframe 中'));
756
803
  return;
757
804
  }
758
805
  if (!this.nonce) {
759
- this.failReady(createSDKError('MISSING_NONCE', '缺少小程序沙盒通信标识'));
806
+ this.failHandshake(createSDKError('MISSING_NONCE', '缺少小程序沙盒通信标识'));
760
807
  return;
761
808
  }
762
809
  this.selfWindow.addEventListener('message', this.handleMessage);
@@ -767,8 +814,8 @@ class MiniProgramBridgeClient {
767
814
  this.unsubscribeHistoryChanges = subscribeHistoryChanges(this.selfWindow.history, () => {
768
815
  this.postLocationReport('history');
769
816
  });
770
- this.readyTimer = setTimeout(() => {
771
- this.failReady(createSDKError('READY_TIMEOUT', '小程序沙盒握手超时'));
817
+ this.handshakeTimer = setTimeout(() => {
818
+ this.failHandshake(createSDKError('READY_TIMEOUT', '小程序沙盒握手超时'));
772
819
  }, this.timeout);
773
820
  try {
774
821
  this.postHandshake();
@@ -777,14 +824,14 @@ class MiniProgramBridgeClient {
777
824
  this.postHandshake();
778
825
  }
779
826
  catch (error) {
780
- this.failReady(error instanceof HbMiniProgramSDKError
827
+ this.failHandshake(error instanceof HbMiniProgramSDKError
781
828
  ? error
782
829
  : createSDKError('HANDSHAKE_FAILED', '小程序沙盒握手发送失败', error));
783
830
  }
784
831
  }, HANDSHAKE_RETRY_INTERVAL);
785
832
  }
786
833
  catch (error) {
787
- this.failReady(error instanceof HbMiniProgramSDKError
834
+ this.failHandshake(error instanceof HbMiniProgramSDKError
788
835
  ? error
789
836
  : createSDKError('HANDSHAKE_FAILED', '小程序沙盒握手发送失败', error));
790
837
  }
@@ -881,7 +928,7 @@ class MiniProgramBridgeClient {
881
928
  this.markRuntimeUnavailable();
882
929
  }
883
930
  else if (eventName === 'ready' && !this.runtimeUnavailable) {
884
- this.resolveReadyOnce();
931
+ this.resolveHandshakeOnce();
885
932
  }
886
933
  else if (eventName === 'show' && !this.runtimeUnavailable) {
887
934
  this.postLocationReport('resume');
@@ -910,26 +957,28 @@ class MiniProgramBridgeClient {
910
957
  }
911
958
  this.targetWindow.postMessage(message, this.targetOrigin);
912
959
  }
913
- resolveReadyOnce() {
914
- if (this.readySettled) {
960
+ resolveHandshakeOnce() {
961
+ if (this.handshakeSettled) {
915
962
  return;
916
963
  }
917
- this.readySettled = true;
918
- this.clearReadyTimers();
919
- this.resolveReady();
964
+ this.handshakeSettled = true;
965
+ this.clearHandshakeTimers();
966
+ this.handshakeState.settle({ status: 'ready' });
967
+ this.resolveHandshake();
920
968
  }
921
- failReady(error) {
922
- if (this.readySettled) {
969
+ failHandshake(error) {
970
+ if (this.handshakeSettled) {
923
971
  return;
924
972
  }
925
- this.readySettled = true;
926
- this.clearReadyTimers();
927
- this.rejectReady(error);
928
- }
929
- clearReadyTimers() {
930
- if (this.readyTimer) {
931
- clearTimeout(this.readyTimer);
932
- this.readyTimer = undefined;
973
+ this.handshakeSettled = true;
974
+ this.clearHandshakeTimers();
975
+ this.handshakeState.settle({ status: 'failed', error });
976
+ this.rejectHandshake(error);
977
+ }
978
+ clearHandshakeTimers() {
979
+ if (this.handshakeTimer) {
980
+ clearTimeout(this.handshakeTimer);
981
+ this.handshakeTimer = undefined;
933
982
  }
934
983
  if (this.handshakeRetryTimer) {
935
984
  clearInterval(this.handshakeRetryTimer);
@@ -942,8 +991,8 @@ class MiniProgramBridgeClient {
942
991
  }
943
992
  const error = this.getRuntimeUnavailableError();
944
993
  this.runtimeUnavailable = true;
945
- this.clearReadyTimers();
946
- this.failReady(error);
994
+ this.clearHandshakeTimers();
995
+ this.failHandshake(error);
947
996
  this.rejectAllPending(error);
948
997
  }
949
998
  getRuntimeUnavailableError() {
@@ -1597,16 +1646,7 @@ function createNavigationModule(requester) {
1597
1646
  * 多数业务页直接使用默认单例即可;只有在测试、多实例或需要定制运行参数时,
1598
1647
  * 才建议显式创建独立实例。
1599
1648
  *
1600
- * @example
1601
- * ```ts
1602
- * import { createMiniProgramSDK } from '@heybox/hb-sdk'
1603
- *
1604
- * const sdk = createMiniProgramSDK({
1605
- * timeout: 15000,
1606
- * })
1607
- *
1608
- * await sdk.ready()
1609
- * ```
1649
+ * 能力调用会自动等待与父容器完成握手。
1610
1650
  */
1611
1651
  class MiniProgramSDK {
1612
1652
  client;
@@ -1643,14 +1683,13 @@ class MiniProgramSDK {
1643
1683
  this.navigation = createNavigationModule(this.client);
1644
1684
  this.cloud = createCloudModule(this.client);
1645
1685
  }
1646
- /**
1647
- * 等待 SDK 与父容器完成握手。
1648
- *
1649
- * @returns 当 bridge 握手成功后 resolve。
1650
- * @throws {HbMiniProgramSDKError} 当当前页面不在 iframe 中、缺少 nonce 或握手超时时抛出。
1651
- */
1652
- ready() {
1653
- return this.client.ready();
1686
+ /** 获取当前握手状态。 */
1687
+ getHandshakeState() {
1688
+ return this.client.getHandshakeState();
1689
+ }
1690
+ /** 订阅握手状态,并立即接收当前状态。 */
1691
+ onHandshakeStateChange(handler) {
1692
+ return this.client.onHandshakeStateChange(handler);
1654
1693
  }
1655
1694
  /**
1656
1695
  * 注册小程序生命周期或业务事件。
@@ -1711,14 +1750,13 @@ function getDefaultSDK() {
1711
1750
  }
1712
1751
  return defaultSDK;
1713
1752
  }
1714
- /**
1715
- * 等待默认 SDK 实例与父容器完成握手。
1716
- *
1717
- * @returns 当默认 SDK 单例与父容器握手成功后 resolve。
1718
- * @throws {HbMiniProgramSDKError} 当当前页面不在 iframe 中、缺少 nonce 或握手超时时抛出。
1719
- */
1720
- function ready() {
1721
- return getDefaultSDK().ready();
1753
+ /** 获取默认 SDK 实例的当前握手状态。 */
1754
+ function getHandshakeState() {
1755
+ return getDefaultSDK().getHandshakeState();
1756
+ }
1757
+ /** 订阅默认 SDK 实例的握手状态,并立即接收当前状态。 */
1758
+ function onHandshakeStateChange(handler) {
1759
+ return getDefaultSDK().onHandshakeStateChange(handler);
1722
1760
  }
1723
1761
  /**
1724
1762
  * 注册默认 SDK 实例的事件监听。
@@ -1799,7 +1837,8 @@ const cloud = {
1799
1837
  };
1800
1838
 
1801
1839
  const hbSDK = {
1802
- ready,
1840
+ getHandshakeState,
1841
+ onHandshakeStateChange,
1803
1842
  on,
1804
1843
  off,
1805
1844
  auth,
@@ -1820,11 +1859,12 @@ exports.auth = auth;
1820
1859
  exports.cloud = cloud;
1821
1860
  exports.default = hbSDK;
1822
1861
  exports.device = device;
1862
+ exports.getHandshakeState = getHandshakeState;
1823
1863
  exports.navigation = navigation;
1824
1864
  exports.network = network;
1825
1865
  exports.off = off;
1826
1866
  exports.on = on;
1827
- exports.ready = ready;
1867
+ exports.onHandshakeStateChange = onHandshakeStateChange;
1828
1868
  exports.share = share;
1829
1869
  exports.storage = storage;
1830
1870
  exports.ui = ui;
package/dist/index.esm.js CHANGED
@@ -429,6 +429,43 @@ class MiniProgramEventBus {
429
429
  }
430
430
  }
431
431
 
432
+ /** 保存握手终态,并向订阅者同步派发唯一一次状态迁移。 */
433
+ class MiniProgramSDKHandshakeStateStore {
434
+ state = Object.freeze({ status: 'connecting' });
435
+ handlers = new Set();
436
+ getState() {
437
+ return this.state;
438
+ }
439
+ subscribe(handler) {
440
+ this.handlers.add(handler);
441
+ this.notify(handler, this.state);
442
+ return () => {
443
+ this.handlers.delete(handler);
444
+ };
445
+ }
446
+ clear() {
447
+ this.handlers.clear();
448
+ }
449
+ settle(state) {
450
+ if (this.state.status !== 'connecting') {
451
+ return;
452
+ }
453
+ const immutableState = Object.freeze(state.status === 'ready'
454
+ ? { status: 'ready' }
455
+ : { status: 'failed', error: state.error });
456
+ this.state = immutableState;
457
+ [...this.handlers].forEach((handler) => this.notify(handler, immutableState));
458
+ }
459
+ notify(handler, state) {
460
+ try {
461
+ handler(state);
462
+ }
463
+ catch {
464
+ // 订阅者异常不能阻断握手状态落定或其他订阅者。
465
+ }
466
+ }
467
+ }
468
+
432
469
  const historyObservers = new WeakMap();
433
470
  /** 共享同一个 History patch,并在最后一个订阅者退出时精确恢复原属性。 */
434
471
  function subscribeHistoryChanges(history, listener) {
@@ -599,7 +636,7 @@ function createMessageId() {
599
636
  /** 构建时替换为当前发布包的实际版本。 */
600
637
  const HB_SDK_VERSION = typeof undefined === 'string'
601
638
  ? undefined
602
- : '0.7.0-alpha.5';
639
+ : '0.7.0';
603
640
 
604
641
  /**
605
642
  * 判断未知数据是否符合小程序 bridge 消息信封。
@@ -638,11 +675,12 @@ class MiniProgramBridgeClient {
638
675
  };
639
676
  unsubscribeHistoryChanges;
640
677
  started = false;
641
- readySettled = false;
642
- readyPromise;
643
- resolveReady;
644
- rejectReady;
645
- readyTimer;
678
+ handshakeState = new MiniProgramSDKHandshakeStateStore();
679
+ handshakeSettled = false;
680
+ handshakePromise;
681
+ resolveHandshake;
682
+ rejectHandshake;
683
+ handshakeTimer;
646
684
  handshakeRetryTimer;
647
685
  destroyed = false;
648
686
  runtimeUnavailable = false;
@@ -655,16 +693,24 @@ class MiniProgramBridgeClient {
655
693
  this.targetOrigin = options.targetOrigin || readParentOrigin(this.selfWindow) || '*';
656
694
  this.nonce = options.nonce || readBridgeNonce(this.selfWindow);
657
695
  this.handleMessage = this.onMessage.bind(this);
658
- this.readyPromise = new Promise((resolve, reject) => {
659
- this.resolveReady = resolve;
660
- this.rejectReady = reject;
696
+ this.handshakePromise = new Promise((resolve, reject) => {
697
+ this.resolveHandshake = resolve;
698
+ this.rejectHandshake = reject;
661
699
  });
662
- this.readyPromise.catch(() => undefined);
700
+ this.handshakePromise.catch(() => undefined);
663
701
  this.ensureStarted();
664
702
  }
665
703
  /** 等待父容器握手完成。 */
666
- ready() {
667
- return this.readyPromise;
704
+ waitForHandshake() {
705
+ return this.handshakePromise;
706
+ }
707
+ /** 获取当前握手状态。 */
708
+ getHandshakeState() {
709
+ return this.handshakeState.getState();
710
+ }
711
+ /** 订阅握手状态,并立即接收当前状态。 */
712
+ onHandshakeStateChange(handler) {
713
+ return this.handshakeState.subscribe(handler);
668
714
  }
669
715
  /** 注册小程序事件监听。 */
670
716
  on(eventName, handler) {
@@ -682,7 +728,7 @@ class MiniProgramBridgeClient {
682
728
  if (this.runtimeUnavailable) {
683
729
  throw this.getRuntimeUnavailableError();
684
730
  }
685
- await this.ready();
731
+ await this.waitForHandshake();
686
732
  if (this.destroyed) {
687
733
  throw createSDKError('SDK_DESTROYED', '小程序 SDK 已销毁');
688
734
  }
@@ -731,16 +777,17 @@ class MiniProgramBridgeClient {
731
777
  this.unsubscribeHistoryChanges?.();
732
778
  this.unsubscribeHistoryChanges = undefined;
733
779
  const error = createSDKError('SDK_DESTROYED', '小程序 SDK 已销毁');
734
- this.failReady(error);
780
+ this.failHandshake(error);
781
+ this.handshakeState.clear();
735
782
  this.rejectAllPending(error);
736
783
  }
737
784
  ensureStarted() {
738
785
  if (this.destroyed) {
739
- this.failReady(createSDKError('SDK_DESTROYED', '小程序 SDK 已销毁'));
786
+ this.failHandshake(createSDKError('SDK_DESTROYED', '小程序 SDK 已销毁'));
740
787
  return;
741
788
  }
742
789
  if (this.runtimeUnavailable) {
743
- this.failReady(this.getRuntimeUnavailableError());
790
+ this.failHandshake(this.getRuntimeUnavailableError());
744
791
  return;
745
792
  }
746
793
  if (this.started) {
@@ -748,11 +795,11 @@ class MiniProgramBridgeClient {
748
795
  }
749
796
  this.started = true;
750
797
  if (!this.selfWindow || !this.targetWindow) {
751
- this.failReady(createSDKError('NOT_IN_IFRAME', '当前页面不在小程序沙盒 iframe 中'));
798
+ this.failHandshake(createSDKError('NOT_IN_IFRAME', '当前页面不在小程序沙盒 iframe 中'));
752
799
  return;
753
800
  }
754
801
  if (!this.nonce) {
755
- this.failReady(createSDKError('MISSING_NONCE', '缺少小程序沙盒通信标识'));
802
+ this.failHandshake(createSDKError('MISSING_NONCE', '缺少小程序沙盒通信标识'));
756
803
  return;
757
804
  }
758
805
  this.selfWindow.addEventListener('message', this.handleMessage);
@@ -763,8 +810,8 @@ class MiniProgramBridgeClient {
763
810
  this.unsubscribeHistoryChanges = subscribeHistoryChanges(this.selfWindow.history, () => {
764
811
  this.postLocationReport('history');
765
812
  });
766
- this.readyTimer = setTimeout(() => {
767
- this.failReady(createSDKError('READY_TIMEOUT', '小程序沙盒握手超时'));
813
+ this.handshakeTimer = setTimeout(() => {
814
+ this.failHandshake(createSDKError('READY_TIMEOUT', '小程序沙盒握手超时'));
768
815
  }, this.timeout);
769
816
  try {
770
817
  this.postHandshake();
@@ -773,14 +820,14 @@ class MiniProgramBridgeClient {
773
820
  this.postHandshake();
774
821
  }
775
822
  catch (error) {
776
- this.failReady(error instanceof HbMiniProgramSDKError
823
+ this.failHandshake(error instanceof HbMiniProgramSDKError
777
824
  ? error
778
825
  : createSDKError('HANDSHAKE_FAILED', '小程序沙盒握手发送失败', error));
779
826
  }
780
827
  }, HANDSHAKE_RETRY_INTERVAL);
781
828
  }
782
829
  catch (error) {
783
- this.failReady(error instanceof HbMiniProgramSDKError
830
+ this.failHandshake(error instanceof HbMiniProgramSDKError
784
831
  ? error
785
832
  : createSDKError('HANDSHAKE_FAILED', '小程序沙盒握手发送失败', error));
786
833
  }
@@ -877,7 +924,7 @@ class MiniProgramBridgeClient {
877
924
  this.markRuntimeUnavailable();
878
925
  }
879
926
  else if (eventName === 'ready' && !this.runtimeUnavailable) {
880
- this.resolveReadyOnce();
927
+ this.resolveHandshakeOnce();
881
928
  }
882
929
  else if (eventName === 'show' && !this.runtimeUnavailable) {
883
930
  this.postLocationReport('resume');
@@ -906,26 +953,28 @@ class MiniProgramBridgeClient {
906
953
  }
907
954
  this.targetWindow.postMessage(message, this.targetOrigin);
908
955
  }
909
- resolveReadyOnce() {
910
- if (this.readySettled) {
956
+ resolveHandshakeOnce() {
957
+ if (this.handshakeSettled) {
911
958
  return;
912
959
  }
913
- this.readySettled = true;
914
- this.clearReadyTimers();
915
- this.resolveReady();
960
+ this.handshakeSettled = true;
961
+ this.clearHandshakeTimers();
962
+ this.handshakeState.settle({ status: 'ready' });
963
+ this.resolveHandshake();
916
964
  }
917
- failReady(error) {
918
- if (this.readySettled) {
965
+ failHandshake(error) {
966
+ if (this.handshakeSettled) {
919
967
  return;
920
968
  }
921
- this.readySettled = true;
922
- this.clearReadyTimers();
923
- this.rejectReady(error);
924
- }
925
- clearReadyTimers() {
926
- if (this.readyTimer) {
927
- clearTimeout(this.readyTimer);
928
- this.readyTimer = undefined;
969
+ this.handshakeSettled = true;
970
+ this.clearHandshakeTimers();
971
+ this.handshakeState.settle({ status: 'failed', error });
972
+ this.rejectHandshake(error);
973
+ }
974
+ clearHandshakeTimers() {
975
+ if (this.handshakeTimer) {
976
+ clearTimeout(this.handshakeTimer);
977
+ this.handshakeTimer = undefined;
929
978
  }
930
979
  if (this.handshakeRetryTimer) {
931
980
  clearInterval(this.handshakeRetryTimer);
@@ -938,8 +987,8 @@ class MiniProgramBridgeClient {
938
987
  }
939
988
  const error = this.getRuntimeUnavailableError();
940
989
  this.runtimeUnavailable = true;
941
- this.clearReadyTimers();
942
- this.failReady(error);
990
+ this.clearHandshakeTimers();
991
+ this.failHandshake(error);
943
992
  this.rejectAllPending(error);
944
993
  }
945
994
  getRuntimeUnavailableError() {
@@ -1593,16 +1642,7 @@ function createNavigationModule(requester) {
1593
1642
  * 多数业务页直接使用默认单例即可;只有在测试、多实例或需要定制运行参数时,
1594
1643
  * 才建议显式创建独立实例。
1595
1644
  *
1596
- * @example
1597
- * ```ts
1598
- * import { createMiniProgramSDK } from '@heybox/hb-sdk'
1599
- *
1600
- * const sdk = createMiniProgramSDK({
1601
- * timeout: 15000,
1602
- * })
1603
- *
1604
- * await sdk.ready()
1605
- * ```
1645
+ * 能力调用会自动等待与父容器完成握手。
1606
1646
  */
1607
1647
  class MiniProgramSDK {
1608
1648
  client;
@@ -1639,14 +1679,13 @@ class MiniProgramSDK {
1639
1679
  this.navigation = createNavigationModule(this.client);
1640
1680
  this.cloud = createCloudModule(this.client);
1641
1681
  }
1642
- /**
1643
- * 等待 SDK 与父容器完成握手。
1644
- *
1645
- * @returns 当 bridge 握手成功后 resolve。
1646
- * @throws {HbMiniProgramSDKError} 当当前页面不在 iframe 中、缺少 nonce 或握手超时时抛出。
1647
- */
1648
- ready() {
1649
- return this.client.ready();
1682
+ /** 获取当前握手状态。 */
1683
+ getHandshakeState() {
1684
+ return this.client.getHandshakeState();
1685
+ }
1686
+ /** 订阅握手状态,并立即接收当前状态。 */
1687
+ onHandshakeStateChange(handler) {
1688
+ return this.client.onHandshakeStateChange(handler);
1650
1689
  }
1651
1690
  /**
1652
1691
  * 注册小程序生命周期或业务事件。
@@ -1707,14 +1746,13 @@ function getDefaultSDK() {
1707
1746
  }
1708
1747
  return defaultSDK;
1709
1748
  }
1710
- /**
1711
- * 等待默认 SDK 实例与父容器完成握手。
1712
- *
1713
- * @returns 当默认 SDK 单例与父容器握手成功后 resolve。
1714
- * @throws {HbMiniProgramSDKError} 当当前页面不在 iframe 中、缺少 nonce 或握手超时时抛出。
1715
- */
1716
- function ready() {
1717
- return getDefaultSDK().ready();
1749
+ /** 获取默认 SDK 实例的当前握手状态。 */
1750
+ function getHandshakeState() {
1751
+ return getDefaultSDK().getHandshakeState();
1752
+ }
1753
+ /** 订阅默认 SDK 实例的握手状态,并立即接收当前状态。 */
1754
+ function onHandshakeStateChange(handler) {
1755
+ return getDefaultSDK().onHandshakeStateChange(handler);
1718
1756
  }
1719
1757
  /**
1720
1758
  * 注册默认 SDK 实例的事件监听。
@@ -1795,7 +1833,8 @@ const cloud = {
1795
1833
  };
1796
1834
 
1797
1835
  const hbSDK = {
1798
- ready,
1836
+ getHandshakeState,
1837
+ onHandshakeStateChange,
1799
1838
  on,
1800
1839
  off,
1801
1840
  auth,
@@ -1810,4 +1849,4 @@ const hbSDK = {
1810
1849
  cloud,
1811
1850
  };
1812
1851
 
1813
- export { HbMiniProgramNetworkError, HbMiniProgramSDKError, auth, cloud, hbSDK as default, device, navigation, network, off, on, ready, share, storage, ui, user, viewport };
1852
+ export { HbMiniProgramNetworkError, HbMiniProgramSDKError, auth, cloud, hbSDK as default, device, getHandshakeState, navigation, network, off, on, onHandshakeStateChange, share, storage, ui, user, viewport };
@@ -32,6 +32,24 @@ function isMiniProgramBridgeMessage(value) {
32
32
  typeof message.type === 'string');
33
33
  }
34
34
 
35
+ function readBrowserEnvironment() {
36
+ try {
37
+ return {
38
+ activeElement: globalThis.document?.activeElement ?? null,
39
+ userActivation: globalThis.navigator?.userActivation,
40
+ };
41
+ }
42
+ catch {
43
+ return { activeElement: null };
44
+ }
45
+ }
46
+ /** 按当前浏览器消息、用户激活和目标 iframe 焦点生成请求级可信手势快照。 */
47
+ function computeTrustedIframeUserGesture(event, iframe, environment = readBrowserEnvironment()) {
48
+ return (event.isTrusted === true
49
+ && environment.userActivation?.isActive === true
50
+ && environment.activeElement === iframe);
51
+ }
52
+
35
53
  const MANAGED_RUNTIME_PERMISSION_KEYS = new Set(['network.request']);
36
54
  /**
37
55
  * 判断 permission key 是否由 Runtime 权限快照管理。
@@ -469,6 +487,7 @@ exports.USER_GET_STEAM_GAME_LIST_METHOD = USER_GET_STEAM_GAME_LIST_METHOD;
469
487
  exports.USER_REVOKE_AUTHORIZATION_METHOD = USER_REVOKE_AUTHORIZATION_METHOD;
470
488
  exports.VIEWPORT_GET_WINDOW_INFO_METHOD = VIEWPORT_GET_WINDOW_INFO_METHOD;
471
489
  exports.VIEWPORT_SET_NAVIGATION_BAR_STYLE_METHOD = VIEWPORT_SET_NAVIGATION_BAR_STYLE_METHOD;
490
+ exports.computeTrustedIframeUserGesture = computeTrustedIframeUserGesture;
472
491
  exports.isManagedMiniProgramRuntimePermissionKey = isManagedMiniProgramRuntimePermissionKey;
473
492
  exports.isMiniProgramBridgeMessage = isMiniProgramBridgeMessage;
474
493
  exports.isOfficialMiniProgramNetworkUrl = isOfficialMiniProgramNetworkUrl;