@onekeyfe/hd-transport 1.2.0-alpha.175 → 1.2.0-alpha.176

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.
@@ -317,6 +317,76 @@ describe('ProtocolV2LinkManager', () => {
317
317
  ]);
318
318
  });
319
319
 
320
+ test('times out a queued call without sending it after the active call settles', async () => {
321
+ let releaseActiveRead;
322
+ let markActiveReadStarted;
323
+ const activeReadStarted = new Promise(resolve => {
324
+ markActiveReadStarted = resolve;
325
+ });
326
+ const activeReadBlocked = new Promise(resolve => {
327
+ releaseActiveRead = resolve;
328
+ });
329
+ const sentSeqs = [];
330
+ const success = ProtocolV2.encodeFrame(
331
+ schemas,
332
+ 'Success',
333
+ { message: 'ok' },
334
+ { router: 1, packetSrc: 0, seq: 1 }
335
+ );
336
+ let requestSeq = 0;
337
+ let readCount = 0;
338
+ const adapter = {
339
+ router: 1,
340
+ generation: 1,
341
+ prepareCall: jest.fn(),
342
+ writeFrame: jest.fn(frame => {
343
+ [, , , , , , requestSeq] = frame;
344
+ sentSeqs.push(requestSeq);
345
+ return Promise.resolve();
346
+ }),
347
+ readFrame: jest.fn(async () => {
348
+ readCount += 1;
349
+ if (readCount === 1) {
350
+ markActiveReadStarted();
351
+ await activeReadBlocked;
352
+ }
353
+ return rewriteSeq(success, requestSeq);
354
+ }),
355
+ reset: jest.fn(),
356
+ createTimeoutError: (name, timeoutMs) =>
357
+ new Error(`response timeout after ${timeoutMs}ms for ${name}`),
358
+ };
359
+ const manager = new ProtocolV2LinkManager({
360
+ getSchemas: () => schemas,
361
+ classifyError: () => 'recoverable',
362
+ });
363
+ const createAdapter = jest.fn(() => adapter);
364
+
365
+ const activeCall = manager.call('device-a', createAdapter, 'Ping', { message: 'active' });
366
+ await activeReadStarted;
367
+ const queuedCall = manager.call(
368
+ 'device-a',
369
+ createAdapter,
370
+ 'Ping',
371
+ { message: 'queued' },
372
+ { timeoutMs: 20 }
373
+ );
374
+
375
+ await expect(queuedCall).rejects.toThrow('response timeout after 20ms for Ping');
376
+ expect(sentSeqs).toEqual([1]);
377
+
378
+ releaseActiveRead();
379
+ await activeCall;
380
+ await expect(
381
+ manager.call('device-a', createAdapter, 'Ping', { message: 'after-timeout' })
382
+ ).resolves.toEqual({
383
+ type: 'Success',
384
+ message: { message: 'ok' },
385
+ });
386
+
387
+ expect(sentSeqs).toEqual([1, 2]);
388
+ });
389
+
320
390
  test('writes flow control while the active call is waiting for its response', async () => {
321
391
  const sentSeqs = [];
322
392
  let releaseRead;
@@ -217,7 +217,7 @@ describe('ProtocolV2UsbTransportBase', () => {
217
217
  expect(transport.nativeResets).toEqual([['device-a', 'USB reconnected']]);
218
218
  });
219
219
 
220
- test('passes each queued call its own timeout context', async () => {
220
+ test('passes each queued call its remaining timeout context', async () => {
221
221
  const transport = new FakeUsbTransport();
222
222
  await transport.rotate('device-a');
223
223
 
@@ -226,12 +226,14 @@ describe('ProtocolV2UsbTransportBase', () => {
226
226
  transport.callDevice('device-a', 'second', 222),
227
227
  ]);
228
228
 
229
- expect(transport.readContexts).toEqual([
229
+ expect(transport.readContexts.slice(0, 2)).toEqual([
230
230
  ['device-a', 111],
231
231
  ['device-a', 111],
232
- ['device-a', 222],
233
- ['device-a', 222],
234
232
  ]);
233
+ const secondCallTimeouts = transport.readContexts.slice(2).map(([, timeoutMs]) => timeoutMs);
234
+ expect(secondCallTimeouts[0]).toBeGreaterThan(0);
235
+ expect(secondCallTimeouts[0]).toBeLessThanOrEqual(222);
236
+ expect(secondCallTimeouts[1]).toBe(secondCallTimeouts[0]);
235
237
  });
236
238
 
237
239
  test('keeps a coalesced response buffered for the next call', async () => {
package/dist/index.d.ts CHANGED
@@ -6756,6 +6756,7 @@ declare class ProtocolV2LinkManager<Key> {
6756
6756
  private getOrCreateLink;
6757
6757
  private executeCall;
6758
6758
  private clearSettledCallQueue;
6759
+ private createQueuedCallTimeoutError;
6759
6760
  private assertCallGeneration;
6760
6761
  }
6761
6762
 
package/dist/index.js CHANGED
@@ -1298,17 +1298,43 @@ class ProtocolV2LinkManager {
1298
1298
  this.options = options;
1299
1299
  }
1300
1300
  call(key, createAdapter, name, data, options) {
1301
- var _a, _b;
1301
+ var _a;
1302
1302
  const generation = (_a = this.generations.get(key)) !== null && _a !== void 0 ? _a : 0;
1303
+ const previousQueue = this.callQueues.get(key);
1304
+ const timeoutMs = (options === null || options === void 0 ? void 0 : options.timeoutMs) && options.timeoutMs > 0
1305
+ ? options.timeoutMs
1306
+ : PROTOCOL_V2_DEFAULT_RESPONSE_TIMEOUT_MS;
1307
+ const queuedAt = Date.now();
1308
+ let queueTimeout;
1309
+ let queueTimeoutError;
1310
+ const timeoutWhileQueued = new Promise((_, reject) => {
1311
+ queueTimeout = setTimeout(() => {
1312
+ queueTimeout = undefined;
1313
+ queueTimeoutError = this.createQueuedCallTimeoutError(key, name, timeoutMs);
1314
+ reject(queueTimeoutError);
1315
+ }, timeoutMs);
1316
+ });
1303
1317
  const run = () => {
1318
+ if (queueTimeout) {
1319
+ clearTimeout(queueTimeout);
1320
+ queueTimeout = undefined;
1321
+ }
1322
+ if (queueTimeoutError) {
1323
+ throw queueTimeoutError;
1324
+ }
1304
1325
  this.assertCallGeneration(key, generation);
1305
- return this.executeCall(key, createAdapter, name, data, options);
1326
+ const remainingTimeoutMs = previousQueue ? timeoutMs - (Date.now() - queuedAt) : timeoutMs;
1327
+ if (remainingTimeoutMs <= 0) {
1328
+ throw this.createQueuedCallTimeoutError(key, name, timeoutMs);
1329
+ }
1330
+ return this.executeCall(key, createAdapter, name, data, Object.assign(Object.assign({}, options), { timeoutMs: remainingTimeoutMs }));
1306
1331
  };
1307
- const previous = (_b = this.callQueues.get(key)) !== null && _b !== void 0 ? _b : Promise.resolve();
1308
- const result = previous.then(run, run);
1309
- const queue = result.catch(() => undefined);
1332
+ const previous = previousQueue !== null && previousQueue !== void 0 ? previousQueue : Promise.resolve();
1333
+ const execution = previous.then(run, run);
1334
+ const result = Promise.race([execution, timeoutWhileQueued]);
1335
+ const queue = execution.catch(() => undefined);
1310
1336
  this.callQueues.set(key, queue);
1311
- result
1337
+ execution
1312
1338
  .then(() => this.clearSettledCallQueue(key, queue), () => this.clearSettledCallQueue(key, queue))
1313
1339
  .catch(() => undefined);
1314
1340
  return result;
@@ -1433,6 +1459,13 @@ class ProtocolV2LinkManager {
1433
1459
  this.callQueues.delete(key);
1434
1460
  }
1435
1461
  }
1462
+ createQueuedCallTimeoutError(key, name, timeoutMs) {
1463
+ var _a;
1464
+ const adapterTimeoutError = (_a = this.links.get(key)) === null || _a === void 0 ? void 0 : _a.adapter.createTimeoutError;
1465
+ return adapterTimeoutError
1466
+ ? adapterTimeoutError(name, timeoutMs)
1467
+ : new ProtocolV2LinkError('response-timeout', `Protocol V2 call timeout after ${timeoutMs}ms while queued for ${name}`);
1468
+ }
1436
1469
  assertCallGeneration(key, generation) {
1437
1470
  var _a;
1438
1471
  const currentGeneration = (_a = this.generations.get(key)) !== null && _a !== void 0 ? _a : 0;
@@ -35,6 +35,7 @@ export declare class ProtocolV2LinkManager<Key> {
35
35
  private getOrCreateLink;
36
36
  private executeCall;
37
37
  private clearSettledCallQueue;
38
+ private createQueuedCallTimeoutError;
38
39
  private assertCallGeneration;
39
40
  }
40
41
  //# sourceMappingURL=link-manager.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"link-manager.d.ts","sourceRoot":"","sources":["../../../src/protocols/v2/link-manager.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAC3E,OAAO,KAAK,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AAEpG,MAAM,MAAM,iCAAiC,GAAG,YAAY,GAAG,aAAa,CAAC;AAE7E,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAClE,UAAU,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E,SAAS,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAC/D,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC5C,MAAM,CAAC,EAAE,wBAAwB,CAAC,QAAQ,CAAC,CAAC;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kBAAkB,CAAC,EAAE,wBAAwB,CAAC,oBAAoB,CAAC,CAAC;CACrE;AAED,MAAM,MAAM,4BAA4B,CAAC,GAAG,IAAI;IAC9C,UAAU,EAAE,MAAM,iBAAiB,CAAC;IACpC,aAAa,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,iCAAiC,CAAC;IACrE,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CACxE,CAAC;AAUF,qBAAa,qBAAqB,CAAC,GAAG;IACpC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAkC;IAExD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA4C;IAEtE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAoC;IAE/D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA0B;IAEtD,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAA0D;IAE9F,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAiC;IAE/D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoC;gBAEhD,OAAO,EAAE,4BAA4B,CAAC,GAAG,CAAC;IAItD,IAAI,CACF,GAAG,EAAE,GAAG,EACR,aAAa,EAAE,MAAM,qBAAqB,EAC1C,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CAAC,iBAAiB,CAAC;IAmB7B,eAAe,CACb,GAAG,EAAE,GAAG,EACR,aAAa,EAAE,MAAM,qBAAqB,EAC1C,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,iBAAiB,CAAC;IAMvB,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA8BvD,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IASjD,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAO5C,OAAO,CAAC,eAAe;YA+CT,WAAW;IAkBzB,OAAO,CAAC,qBAAqB;IAM7B,OAAO,CAAC,oBAAoB;CAW7B"}
1
+ {"version":3,"file":"link-manager.d.ts","sourceRoot":"","sources":["../../../src/protocols/v2/link-manager.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAC3E,OAAO,KAAK,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AAEpG,MAAM,MAAM,iCAAiC,GAAG,YAAY,GAAG,aAAa,CAAC;AAE7E,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAClE,UAAU,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E,SAAS,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAC/D,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC5C,MAAM,CAAC,EAAE,wBAAwB,CAAC,QAAQ,CAAC,CAAC;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kBAAkB,CAAC,EAAE,wBAAwB,CAAC,oBAAoB,CAAC,CAAC;CACrE;AAED,MAAM,MAAM,4BAA4B,CAAC,GAAG,IAAI;IAC9C,UAAU,EAAE,MAAM,iBAAiB,CAAC;IACpC,aAAa,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,iCAAiC,CAAC;IACrE,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CACxE,CAAC;AAUF,qBAAa,qBAAqB,CAAC,GAAG;IACpC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAkC;IAExD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA4C;IAEtE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAoC;IAE/D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA0B;IAEtD,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAA0D;IAE9F,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAiC;IAE/D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoC;gBAEhD,OAAO,EAAE,4BAA4B,CAAC,GAAG,CAAC;IAItD,IAAI,CACF,GAAG,EAAE,GAAG,EACR,aAAa,EAAE,MAAM,qBAAqB,EAC1C,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CAAC,iBAAiB,CAAC;IAiD7B,eAAe,CACb,GAAG,EAAE,GAAG,EACR,aAAa,EAAE,MAAM,qBAAqB,EAC1C,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,iBAAiB,CAAC;IAMvB,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA8BvD,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IASjD,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAO5C,OAAO,CAAC,eAAe;YA+CT,WAAW;IAkBzB,OAAO,CAAC,qBAAqB;IAM7B,OAAO,CAAC,4BAA4B;IAUpC,OAAO,CAAC,oBAAoB;CAW7B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-transport",
3
- "version": "1.2.0-alpha.175",
3
+ "version": "1.2.0-alpha.176",
4
4
  "description": "Transport layer abstractions and utilities for OneKey hardware SDK.",
5
5
  "author": "OneKey",
6
6
  "homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
@@ -28,5 +28,5 @@
28
28
  "long": "^4.0.0",
29
29
  "protobufjs": "^6.11.2"
30
30
  },
31
- "gitHead": "b2cb2451bd3a56b219d18e0078597b316dbc3ba7"
31
+ "gitHead": "a5152148317effe811cde8adecdfe75c28e82657"
32
32
  }
@@ -1,6 +1,7 @@
1
1
  import { ProtocolV2SequenceCursor } from './sequence-cursor';
2
2
  import { ProtocolV2LinkError } from './errors';
3
3
  import { ProtocolV2Session, getErrorMessage } from './session';
4
+ import { PROTOCOL_V2_DEFAULT_RESPONSE_TIMEOUT_MS } from '../../constants';
4
5
 
5
6
  import type { MessageFromOneKey, TransportCallOptions } from '../../types';
6
7
  import type { ProtocolV2CallContext, ProtocolV2Schemas, ProtocolV2SessionOptions } from './session';
@@ -61,15 +62,45 @@ export class ProtocolV2LinkManager<Key> {
61
62
  options?: TransportCallOptions
62
63
  ): Promise<MessageFromOneKey> {
63
64
  const generation = this.generations.get(key) ?? 0;
65
+ const previousQueue = this.callQueues.get(key);
66
+ const timeoutMs =
67
+ options?.timeoutMs && options.timeoutMs > 0
68
+ ? options.timeoutMs
69
+ : PROTOCOL_V2_DEFAULT_RESPONSE_TIMEOUT_MS;
70
+ const queuedAt = Date.now();
71
+ let queueTimeout: ReturnType<typeof setTimeout> | undefined;
72
+ let queueTimeoutError: Error | undefined;
73
+ const timeoutWhileQueued = new Promise<never>((_, reject) => {
74
+ queueTimeout = setTimeout(() => {
75
+ queueTimeout = undefined;
76
+ queueTimeoutError = this.createQueuedCallTimeoutError(key, name, timeoutMs);
77
+ reject(queueTimeoutError);
78
+ }, timeoutMs);
79
+ });
64
80
  const run = () => {
81
+ if (queueTimeout) {
82
+ clearTimeout(queueTimeout);
83
+ queueTimeout = undefined;
84
+ }
85
+ if (queueTimeoutError) {
86
+ throw queueTimeoutError;
87
+ }
65
88
  this.assertCallGeneration(key, generation);
66
- return this.executeCall(key, createAdapter, name, data, options);
89
+ const remainingTimeoutMs = previousQueue ? timeoutMs - (Date.now() - queuedAt) : timeoutMs;
90
+ if (remainingTimeoutMs <= 0) {
91
+ throw this.createQueuedCallTimeoutError(key, name, timeoutMs);
92
+ }
93
+ return this.executeCall(key, createAdapter, name, data, {
94
+ ...options,
95
+ timeoutMs: remainingTimeoutMs,
96
+ });
67
97
  };
68
- const previous = this.callQueues.get(key) ?? Promise.resolve();
69
- const result = previous.then(run, run);
70
- const queue = result.catch(() => undefined);
98
+ const previous = previousQueue ?? Promise.resolve();
99
+ const execution = previous.then(run, run);
100
+ const result = Promise.race([execution, timeoutWhileQueued]);
101
+ const queue = execution.catch(() => undefined);
71
102
  this.callQueues.set(key, queue);
72
- result
103
+ execution
73
104
  .then(
74
105
  () => this.clearSettledCallQueue(key, queue),
75
106
  () => this.clearSettledCallQueue(key, queue)
@@ -206,6 +237,16 @@ export class ProtocolV2LinkManager<Key> {
206
237
  }
207
238
  }
208
239
 
240
+ private createQueuedCallTimeoutError(key: Key, name: string, timeoutMs: number): Error {
241
+ const adapterTimeoutError = this.links.get(key)?.adapter.createTimeoutError;
242
+ return adapterTimeoutError
243
+ ? adapterTimeoutError(name, timeoutMs)
244
+ : new ProtocolV2LinkError(
245
+ 'response-timeout',
246
+ `Protocol V2 call timeout after ${timeoutMs}ms while queued for ${name}`
247
+ );
248
+ }
249
+
209
250
  private assertCallGeneration(key: Key, generation: number) {
210
251
  const currentGeneration = this.generations.get(key) ?? 0;
211
252
  if (currentGeneration === generation) return;