@heybox/hb-sdk 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -112,10 +112,12 @@ sdk.destroy();
112
112
  SDK 与父容器之间通过 `postMessage` 通信,消息会带上固定命名空间、协议版本与 iframe 实例 nonce。SDK 内部负责:
113
113
 
114
114
  - 从 URL query 读取 `hb_mini_bridge_nonce`,也支持通过 `MiniProgramSDKOptions.nonce` 显式传入。
115
- - 向父容器发送 `sdk.handshake` 握手消息,并等待 `ready` 事件。
115
+ - 向父容器发送 `sdk.handshake` 握手消息;如果还没有收到 `ready` 事件,会在总超时窗口内短间隔重试。
116
116
  - 为每次能力调用生成请求 ID,匹配父容器返回的 response。
117
117
  - 过滤非小程序消息、非当前 nonce 消息以及非目标父窗口消息。
118
- - 处理请求超时、握手超时和销毁后的未完成请求。
118
+ - 处理请求超时、握手总超时和销毁后的未完成请求。
119
+
120
+ 父容器应幂等响应握手:重复 `sdk.handshake` 只代表 SDK 在补偿早期消息丢失,父容器可以重复派发 `ready`,但不应重复执行一次性启动副作用。
119
121
 
120
122
  ### 标准错误
121
123
 
@@ -430,6 +432,7 @@ sdk.destroy();
430
432
 
431
433
  - SDK 只能在父容器创建的小程序 iframe 沙盒中正常完成握手;非 iframe 环境会抛出 `NOT_IN_IFRAME`。
432
434
  - 父容器必须在小程序 URL 上注入 `hb_mini_bridge_nonce`,否则会抛出 `MISSING_NONCE`。
435
+ - 父容器需要幂等响应 `sdk.handshake`;SDK 会在收到 `ready` 前自动重试握手,不引用 SDK 的普通页面不会触发握手。
433
436
  - 调用模块能力前会自动等待 `ready()`,但业务仍建议在页面启动阶段显式 `await ready()`,便于集中处理握手失败。
434
437
  - `user.getInfo()` 只查询登录态,不会主动唤起登录。
435
438
  - `user.login()` 只返回公开用户资料,不返回 token、cookie 或私有凭据。
package/dist/index.cjs.js CHANGED
@@ -112,6 +112,7 @@ function isMiniProgramBridgeMessage(value) {
112
112
  }
113
113
 
114
114
  const DEFAULT_TIMEOUT = 10000;
115
+ const HANDSHAKE_RETRY_INTERVAL = 250;
115
116
  /** 底层 bridge client,负责握手、请求响应、事件分发与超时清理。 */
116
117
  class MiniProgramBridgeClient {
117
118
  timeout;
@@ -127,6 +128,8 @@ class MiniProgramBridgeClient {
127
128
  resolveReady;
128
129
  rejectReady;
129
130
  readyTimer;
131
+ handshakeRetryTimer;
132
+ destroyed = false;
130
133
  constructor(options = {}) {
131
134
  this.timeout = options.timeout || DEFAULT_TIMEOUT;
132
135
  this.selfWindow = options.selfWindow || getGlobalWindow();
@@ -189,11 +192,17 @@ class MiniProgramBridgeClient {
189
192
  }
190
193
  /** 销毁 SDK 实例,并拒绝尚未完成的请求。 */
191
194
  destroy() {
195
+ this.destroyed = true;
192
196
  this.selfWindow?.removeEventListener('message', this.handleMessage);
193
- this.clearReadyTimer();
194
- this.rejectAllPending(createSDKError('SDK_DESTROYED', '小程序 SDK 已销毁'));
197
+ const error = createSDKError('SDK_DESTROYED', '小程序 SDK 已销毁');
198
+ this.failReady(error);
199
+ this.rejectAllPending(error);
195
200
  }
196
201
  ensureStarted() {
202
+ if (this.destroyed) {
203
+ this.failReady(createSDKError('SDK_DESTROYED', '小程序 SDK 已销毁'));
204
+ return;
205
+ }
197
206
  if (this.started) {
198
207
  return;
199
208
  }
@@ -210,6 +219,22 @@ class MiniProgramBridgeClient {
210
219
  this.readyTimer = setTimeout(() => {
211
220
  this.failReady(createSDKError('READY_TIMEOUT', '小程序沙盒握手超时'));
212
221
  }, this.timeout);
222
+ try {
223
+ this.postHandshake();
224
+ this.handshakeRetryTimer = setInterval(() => {
225
+ this.postHandshake();
226
+ }, HANDSHAKE_RETRY_INTERVAL);
227
+ }
228
+ catch (error) {
229
+ this.failReady(error instanceof HbMiniProgramSDKError
230
+ ? error
231
+ : createSDKError('HANDSHAKE_FAILED', '小程序沙盒握手发送失败', error));
232
+ }
233
+ }
234
+ postHandshake() {
235
+ if (!this.selfWindow) {
236
+ throw createSDKError('NOT_IN_IFRAME', '当前页面不在小程序沙盒 iframe 中');
237
+ }
213
238
  this.postMessage({
214
239
  namespace: MINI_PROGRAM_MESSAGE_NAMESPACE,
215
240
  version: MINI_PROGRAM_MESSAGE_VERSION,
@@ -272,7 +297,7 @@ class MiniProgramBridgeClient {
272
297
  return;
273
298
  }
274
299
  this.readySettled = true;
275
- this.clearReadyTimer();
300
+ this.clearReadyTimers();
276
301
  this.resolveReady();
277
302
  }
278
303
  failReady(error) {
@@ -280,15 +305,18 @@ class MiniProgramBridgeClient {
280
305
  return;
281
306
  }
282
307
  this.readySettled = true;
283
- this.clearReadyTimer();
308
+ this.clearReadyTimers();
284
309
  this.rejectReady(error);
285
310
  }
286
- clearReadyTimer() {
287
- if (!this.readyTimer) {
288
- return;
311
+ clearReadyTimers() {
312
+ if (this.readyTimer) {
313
+ clearTimeout(this.readyTimer);
314
+ this.readyTimer = undefined;
315
+ }
316
+ if (this.handshakeRetryTimer) {
317
+ clearInterval(this.handshakeRetryTimer);
318
+ this.handshakeRetryTimer = undefined;
289
319
  }
290
- clearTimeout(this.readyTimer);
291
- this.readyTimer = undefined;
292
320
  }
293
321
  rejectAllPending(error) {
294
322
  this.pendingRequests.forEach((pending) => {
package/dist/index.esm.js CHANGED
@@ -108,6 +108,7 @@ function isMiniProgramBridgeMessage(value) {
108
108
  }
109
109
 
110
110
  const DEFAULT_TIMEOUT = 10000;
111
+ const HANDSHAKE_RETRY_INTERVAL = 250;
111
112
  /** 底层 bridge client,负责握手、请求响应、事件分发与超时清理。 */
112
113
  class MiniProgramBridgeClient {
113
114
  timeout;
@@ -123,6 +124,8 @@ class MiniProgramBridgeClient {
123
124
  resolveReady;
124
125
  rejectReady;
125
126
  readyTimer;
127
+ handshakeRetryTimer;
128
+ destroyed = false;
126
129
  constructor(options = {}) {
127
130
  this.timeout = options.timeout || DEFAULT_TIMEOUT;
128
131
  this.selfWindow = options.selfWindow || getGlobalWindow();
@@ -185,11 +188,17 @@ class MiniProgramBridgeClient {
185
188
  }
186
189
  /** 销毁 SDK 实例,并拒绝尚未完成的请求。 */
187
190
  destroy() {
191
+ this.destroyed = true;
188
192
  this.selfWindow?.removeEventListener('message', this.handleMessage);
189
- this.clearReadyTimer();
190
- this.rejectAllPending(createSDKError('SDK_DESTROYED', '小程序 SDK 已销毁'));
193
+ const error = createSDKError('SDK_DESTROYED', '小程序 SDK 已销毁');
194
+ this.failReady(error);
195
+ this.rejectAllPending(error);
191
196
  }
192
197
  ensureStarted() {
198
+ if (this.destroyed) {
199
+ this.failReady(createSDKError('SDK_DESTROYED', '小程序 SDK 已销毁'));
200
+ return;
201
+ }
193
202
  if (this.started) {
194
203
  return;
195
204
  }
@@ -206,6 +215,22 @@ class MiniProgramBridgeClient {
206
215
  this.readyTimer = setTimeout(() => {
207
216
  this.failReady(createSDKError('READY_TIMEOUT', '小程序沙盒握手超时'));
208
217
  }, this.timeout);
218
+ try {
219
+ this.postHandshake();
220
+ this.handshakeRetryTimer = setInterval(() => {
221
+ this.postHandshake();
222
+ }, HANDSHAKE_RETRY_INTERVAL);
223
+ }
224
+ catch (error) {
225
+ this.failReady(error instanceof HbMiniProgramSDKError
226
+ ? error
227
+ : createSDKError('HANDSHAKE_FAILED', '小程序沙盒握手发送失败', error));
228
+ }
229
+ }
230
+ postHandshake() {
231
+ if (!this.selfWindow) {
232
+ throw createSDKError('NOT_IN_IFRAME', '当前页面不在小程序沙盒 iframe 中');
233
+ }
209
234
  this.postMessage({
210
235
  namespace: MINI_PROGRAM_MESSAGE_NAMESPACE,
211
236
  version: MINI_PROGRAM_MESSAGE_VERSION,
@@ -268,7 +293,7 @@ class MiniProgramBridgeClient {
268
293
  return;
269
294
  }
270
295
  this.readySettled = true;
271
- this.clearReadyTimer();
296
+ this.clearReadyTimers();
272
297
  this.resolveReady();
273
298
  }
274
299
  failReady(error) {
@@ -276,15 +301,18 @@ class MiniProgramBridgeClient {
276
301
  return;
277
302
  }
278
303
  this.readySettled = true;
279
- this.clearReadyTimer();
304
+ this.clearReadyTimers();
280
305
  this.rejectReady(error);
281
306
  }
282
- clearReadyTimer() {
283
- if (!this.readyTimer) {
284
- return;
307
+ clearReadyTimers() {
308
+ if (this.readyTimer) {
309
+ clearTimeout(this.readyTimer);
310
+ this.readyTimer = undefined;
311
+ }
312
+ if (this.handshakeRetryTimer) {
313
+ clearInterval(this.handshakeRetryTimer);
314
+ this.handshakeRetryTimer = undefined;
285
315
  }
286
- clearTimeout(this.readyTimer);
287
- this.readyTimer = undefined;
288
316
  }
289
317
  rejectAllPending(error) {
290
318
  this.pendingRequests.forEach((pending) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heybox/hb-sdk",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "",
5
5
  "exports": {
6
6
  ".": {
@@ -30,6 +30,8 @@ export declare class MiniProgramBridgeClient implements MiniProgramRequester {
30
30
  private resolveReady;
31
31
  private rejectReady;
32
32
  private readyTimer?;
33
+ private handshakeRetryTimer?;
34
+ private destroyed;
33
35
  constructor(options?: MiniProgramSDKOptions);
34
36
  /** 等待父容器握手完成。 */
35
37
  ready(): Promise<void>;
@@ -42,12 +44,13 @@ export declare class MiniProgramBridgeClient implements MiniProgramRequester {
42
44
  /** 销毁 SDK 实例,并拒绝尚未完成的请求。 */
43
45
  destroy(): void;
44
46
  private ensureStarted;
47
+ private postHandshake;
45
48
  private onMessage;
46
49
  private handleEvent;
47
50
  private handleResponse;
48
51
  private postMessage;
49
52
  private resolveReadyOnce;
50
53
  private failReady;
51
- private clearReadyTimer;
54
+ private clearReadyTimers;
52
55
  private rejectAllPending;
53
56
  }