@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.
- package/__tests__/protocol-v2-link-manager.test.js +70 -0
- package/__tests__/protocol-v2-usb-transport-base.test.js +6 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.js +39 -6
- package/dist/protocols/v2/link-manager.d.ts +1 -0
- package/dist/protocols/v2/link-manager.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/protocols/v2/link-manager.ts +46 -5
|
@@ -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
|
|
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
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
|
|
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
|
-
|
|
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 =
|
|
1308
|
-
const
|
|
1309
|
-
const
|
|
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
|
-
|
|
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;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"link-manager.d.ts","sourceRoot":"","sources":["../../../src/protocols/v2/link-manager.ts"],"names":[],"mappings":"
|
|
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.
|
|
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": "
|
|
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
|
-
|
|
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 =
|
|
69
|
-
const
|
|
70
|
-
const
|
|
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
|
-
|
|
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;
|