@onekeyfe/hd-transport 1.2.0-alpha.24 → 1.2.0-alpha.25
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 +45 -0
- package/__tests__/protocol-v2-usb-transport-base.test.js +36 -2
- package/dist/index.d.ts +5 -1
- package/dist/index.js +47 -15
- package/dist/protocols/v2/errors.d.ts +1 -1
- package/dist/protocols/v2/errors.d.ts.map +1 -1
- package/dist/protocols/v2/link-manager.d.ts +3 -0
- package/dist/protocols/v2/link-manager.d.ts.map +1 -1
- package/dist/protocols/v2/usb-transport-base.d.ts +1 -0
- package/dist/protocols/v2/usb-transport-base.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/protocols/v2/errors.ts +2 -0
- package/src/protocols/v2/link-manager.ts +28 -2
- package/src/protocols/v2/usb-transport-base.ts +28 -9
|
@@ -341,4 +341,49 @@ describe('ProtocolV2LinkManager', () => {
|
|
|
341
341
|
await expect(call).rejects.toThrow('device disconnected');
|
|
342
342
|
expect(adapter.readFrame).not.toHaveBeenCalled();
|
|
343
343
|
});
|
|
344
|
+
|
|
345
|
+
test('rejects calls queued before their link generation is invalidated', async () => {
|
|
346
|
+
let releaseWrite;
|
|
347
|
+
let markWriteStarted;
|
|
348
|
+
const writeStarted = new Promise(resolve => {
|
|
349
|
+
markWriteStarted = resolve;
|
|
350
|
+
});
|
|
351
|
+
const writeBlocked = new Promise(resolve => {
|
|
352
|
+
releaseWrite = resolve;
|
|
353
|
+
});
|
|
354
|
+
const success = ProtocolV2.encodeFrame(
|
|
355
|
+
schemas,
|
|
356
|
+
'Success',
|
|
357
|
+
{ message: 'ok' },
|
|
358
|
+
{ router: 1, packetSrc: 0, seq: 1 }
|
|
359
|
+
);
|
|
360
|
+
const adapter = {
|
|
361
|
+
router: 1,
|
|
362
|
+
generation: 1,
|
|
363
|
+
prepareCall: jest.fn(),
|
|
364
|
+
writeFrame: jest.fn(async () => {
|
|
365
|
+
markWriteStarted();
|
|
366
|
+
await writeBlocked;
|
|
367
|
+
}),
|
|
368
|
+
readFrame: jest.fn(() => Promise.resolve(success)),
|
|
369
|
+
reset: jest.fn(),
|
|
370
|
+
};
|
|
371
|
+
const createAdapter = jest.fn(() => adapter);
|
|
372
|
+
const manager = new ProtocolV2LinkManager({
|
|
373
|
+
getSchemas: () => schemas,
|
|
374
|
+
classifyError: () => 'recoverable',
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
const activeCall = manager.call('device-a', createAdapter, 'Ping', { message: 'active' });
|
|
378
|
+
await writeStarted;
|
|
379
|
+
const queuedCall = manager.call('device-a', createAdapter, 'Ping', { message: 'queued' });
|
|
380
|
+
await manager.invalidateLink('device-a', 'device released');
|
|
381
|
+
releaseWrite();
|
|
382
|
+
|
|
383
|
+
await expect(activeCall).rejects.toThrow('device released');
|
|
384
|
+
await expect(queuedCall).rejects.toThrow('device released');
|
|
385
|
+
expect(createAdapter).toHaveBeenCalledTimes(1);
|
|
386
|
+
expect(adapter.writeFrame).toHaveBeenCalledTimes(1);
|
|
387
|
+
expect(adapter.readFrame).not.toHaveBeenCalled();
|
|
388
|
+
});
|
|
344
389
|
});
|
|
@@ -65,6 +65,7 @@ class FakeUsbTransport extends ProtocolV2UsbTransportBase {
|
|
|
65
65
|
this.nextReadError = undefined;
|
|
66
66
|
this.writeBlock = undefined;
|
|
67
67
|
this.readBlock = undefined;
|
|
68
|
+
this.coalesceNextResponses = false;
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
callDevice(key, message, timeoutMs) {
|
|
@@ -118,6 +119,10 @@ class FakeUsbTransport extends ProtocolV2UsbTransportBase {
|
|
|
118
119
|
return { started, release };
|
|
119
120
|
}
|
|
120
121
|
|
|
122
|
+
coalesceNextTwoResponses() {
|
|
123
|
+
this.coalesceNextResponses = true;
|
|
124
|
+
}
|
|
125
|
+
|
|
121
126
|
getProtocolV2UsbSchemas() {
|
|
122
127
|
return schemas;
|
|
123
128
|
}
|
|
@@ -135,8 +140,22 @@ class FakeUsbTransport extends ProtocolV2UsbTransportBase {
|
|
|
135
140
|
{ message: 'ok' },
|
|
136
141
|
{ router: PROTOCOL_V2_CHANNEL_USB, seq }
|
|
137
142
|
);
|
|
138
|
-
|
|
139
|
-
|
|
143
|
+
if (this.coalesceNextResponses) {
|
|
144
|
+
this.coalesceNextResponses = false;
|
|
145
|
+
const nextResponse = ProtocolV2.encodeFrame(
|
|
146
|
+
schemas,
|
|
147
|
+
'Success',
|
|
148
|
+
{ message: 'ok' },
|
|
149
|
+
{ router: PROTOCOL_V2_CHANNEL_USB, seq: Number(seq) + 1 }
|
|
150
|
+
);
|
|
151
|
+
const packet = new Uint8Array(response.length + nextResponse.length);
|
|
152
|
+
packet.set(response);
|
|
153
|
+
packet.set(nextResponse, response.length);
|
|
154
|
+
this.packetQueues.set(key, [packet]);
|
|
155
|
+
} else {
|
|
156
|
+
const splitAt = Math.min(4, response.length);
|
|
157
|
+
this.packetQueues.set(key, [response.slice(0, splitAt), response.slice(splitAt)]);
|
|
158
|
+
}
|
|
140
159
|
|
|
141
160
|
const block = this.writeBlock;
|
|
142
161
|
if (block) {
|
|
@@ -215,6 +234,21 @@ describe('ProtocolV2UsbTransportBase', () => {
|
|
|
215
234
|
]);
|
|
216
235
|
});
|
|
217
236
|
|
|
237
|
+
test('keeps a coalesced response buffered for the next call', async () => {
|
|
238
|
+
const transport = new FakeUsbTransport();
|
|
239
|
+
await transport.rotate('device-a');
|
|
240
|
+
transport.coalesceNextTwoResponses();
|
|
241
|
+
|
|
242
|
+
await transport.callDevice('device-a', 'first');
|
|
243
|
+
await transport.callDevice('device-a', 'second');
|
|
244
|
+
|
|
245
|
+
expect(transport.readCounts.get('device-a')).toBe(1);
|
|
246
|
+
expect(transport.sentSeqs).toEqual([
|
|
247
|
+
['device-a', 1],
|
|
248
|
+
['device-a', 2],
|
|
249
|
+
]);
|
|
250
|
+
});
|
|
251
|
+
|
|
218
252
|
test('does not block a different USB path', async () => {
|
|
219
253
|
const transport = new FakeUsbTransport();
|
|
220
254
|
await transport.rotate('device-a');
|
package/dist/index.d.ts
CHANGED
|
@@ -131,7 +131,7 @@ declare function isAckFrame(data: Uint8Array): boolean;
|
|
|
131
131
|
*/
|
|
132
132
|
declare function decodeFrame(data: Uint8Array): ProtoV2Frame;
|
|
133
133
|
|
|
134
|
-
type ProtocolV2LinkErrorCode = 'response-timeout' | 'router' | 'packet-source' | 'ack-sequence' | 'response-sequence' | 'frame';
|
|
134
|
+
type ProtocolV2LinkErrorCode = 'response-timeout' | 'io' | 'generation' | 'router' | 'packet-source' | 'ack-sequence' | 'response-sequence' | 'frame';
|
|
135
135
|
declare class ProtocolV2LinkError extends Error {
|
|
136
136
|
readonly code: ProtocolV2LinkErrorCode;
|
|
137
137
|
readonly cause?: unknown;
|
|
@@ -6668,6 +6668,8 @@ declare class ProtocolV2LinkManager<Key> {
|
|
|
6668
6668
|
private readonly links;
|
|
6669
6669
|
private readonly sequences;
|
|
6670
6670
|
private readonly callQueues;
|
|
6671
|
+
private readonly generations;
|
|
6672
|
+
private readonly invalidationReasons;
|
|
6671
6673
|
private readonly options;
|
|
6672
6674
|
constructor(options: ProtocolV2LinkManagerOptions<Key>);
|
|
6673
6675
|
call(key: Key, createAdapter: () => ProtocolV2LinkAdapter, name: string, data: Record<string, unknown>, options?: TransportCallOptions): Promise<MessageFromOneKey>;
|
|
@@ -6677,6 +6679,7 @@ declare class ProtocolV2LinkManager<Key> {
|
|
|
6677
6679
|
private getOrCreateLink;
|
|
6678
6680
|
private executeCall;
|
|
6679
6681
|
private clearSettledCallQueue;
|
|
6682
|
+
private assertCallGeneration;
|
|
6680
6683
|
}
|
|
6681
6684
|
|
|
6682
6685
|
type ProtocolV2UsbTransportBaseOptions = {
|
|
@@ -6706,6 +6709,7 @@ declare abstract class ProtocolV2UsbTransportBase<Key> {
|
|
|
6706
6709
|
private createProtocolV2UsbAdapter;
|
|
6707
6710
|
private getProtocolV2UsbAssembler;
|
|
6708
6711
|
private createProtocolV2UsbCancellation;
|
|
6712
|
+
private createProtocolV2UsbIoError;
|
|
6709
6713
|
}
|
|
6710
6714
|
|
|
6711
6715
|
declare function info(res: any): {
|
package/dist/index.js
CHANGED
|
@@ -1055,12 +1055,18 @@ class ProtocolV2LinkManager {
|
|
|
1055
1055
|
this.links = new Map();
|
|
1056
1056
|
this.sequences = new Map();
|
|
1057
1057
|
this.callQueues = new Map();
|
|
1058
|
+
this.generations = new Map();
|
|
1059
|
+
this.invalidationReasons = new Map();
|
|
1058
1060
|
this.options = options;
|
|
1059
1061
|
}
|
|
1060
1062
|
call(key, createAdapter, name, data, options) {
|
|
1061
|
-
var _a;
|
|
1062
|
-
const
|
|
1063
|
-
const
|
|
1063
|
+
var _a, _b;
|
|
1064
|
+
const generation = (_a = this.generations.get(key)) !== null && _a !== void 0 ? _a : 0;
|
|
1065
|
+
const run = () => {
|
|
1066
|
+
this.assertCallGeneration(key, generation);
|
|
1067
|
+
return this.executeCall(key, createAdapter, name, data, options);
|
|
1068
|
+
};
|
|
1069
|
+
const previous = (_b = this.callQueues.get(key)) !== null && _b !== void 0 ? _b : Promise.resolve();
|
|
1064
1070
|
const result = previous.then(run, run);
|
|
1065
1071
|
const queue = result.catch(() => undefined);
|
|
1066
1072
|
this.callQueues.set(key, queue);
|
|
@@ -1070,20 +1076,24 @@ class ProtocolV2LinkManager {
|
|
|
1070
1076
|
return result;
|
|
1071
1077
|
}
|
|
1072
1078
|
invalidateLink(key, reason) {
|
|
1073
|
-
var _a, _b;
|
|
1079
|
+
var _a, _b, _c;
|
|
1074
1080
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1081
|
+
const generation = ((_a = this.generations.get(key)) !== null && _a !== void 0 ? _a : 0) + 1;
|
|
1082
|
+
this.generations.set(key, generation);
|
|
1083
|
+
this.invalidationReasons.set(key, { generation, reason });
|
|
1075
1084
|
const link = this.links.get(key);
|
|
1076
1085
|
if (!link)
|
|
1077
1086
|
return;
|
|
1078
1087
|
this.links.delete(key);
|
|
1079
1088
|
link.state.invalidatedReason = reason;
|
|
1080
1089
|
yield link.adapter.reset(reason);
|
|
1081
|
-
yield ((
|
|
1090
|
+
yield ((_c = (_b = this.options).onLinkInvalidated) === null || _c === void 0 ? void 0 : _c.call(_b, key, reason));
|
|
1082
1091
|
});
|
|
1083
1092
|
}
|
|
1084
1093
|
invalidateAllLinks(reason) {
|
|
1085
1094
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1086
|
-
|
|
1095
|
+
const keys = new Set([...this.links.keys(), ...this.callQueues.keys()]);
|
|
1096
|
+
yield Promise.all(Array.from(keys, key => this.invalidateLink(key, reason)));
|
|
1087
1097
|
});
|
|
1088
1098
|
}
|
|
1089
1099
|
dispose(reason) {
|
|
@@ -1158,6 +1168,17 @@ class ProtocolV2LinkManager {
|
|
|
1158
1168
|
this.callQueues.delete(key);
|
|
1159
1169
|
}
|
|
1160
1170
|
}
|
|
1171
|
+
assertCallGeneration(key, generation) {
|
|
1172
|
+
var _a;
|
|
1173
|
+
const currentGeneration = (_a = this.generations.get(key)) !== null && _a !== void 0 ? _a : 0;
|
|
1174
|
+
if (currentGeneration === generation)
|
|
1175
|
+
return;
|
|
1176
|
+
const invalidation = this.invalidationReasons.get(key);
|
|
1177
|
+
const reason = (invalidation === null || invalidation === void 0 ? void 0 : invalidation.generation) === currentGeneration
|
|
1178
|
+
? invalidation.reason
|
|
1179
|
+
: 'Protocol V2 link generation changed';
|
|
1180
|
+
throw new ProtocolV2LinkError('generation', reason);
|
|
1181
|
+
}
|
|
1161
1182
|
}
|
|
1162
1183
|
|
|
1163
1184
|
class ProtocolV2UsbTransportBase {
|
|
@@ -1168,7 +1189,7 @@ class ProtocolV2UsbTransportBase {
|
|
|
1168
1189
|
this.protocolV2UsbOptions = options;
|
|
1169
1190
|
this.protocolV2UsbLinks = new ProtocolV2LinkManager({
|
|
1170
1191
|
getSchemas: () => this.getProtocolV2UsbSchemas(),
|
|
1171
|
-
classifyError: ()
|
|
1192
|
+
classifyError: error => (isProtocolV2LinkError(error) ? 'link-fatal' : 'recoverable'),
|
|
1172
1193
|
onLinkInvalidated: (key, reason) => __awaiter(this, void 0, void 0, function* () {
|
|
1173
1194
|
var _a;
|
|
1174
1195
|
(_a = this.protocolV2UsbAssemblers.get(key)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
@@ -1218,10 +1239,10 @@ class ProtocolV2UsbTransportBase {
|
|
|
1218
1239
|
}
|
|
1219
1240
|
const assertCurrentGeneration = () => {
|
|
1220
1241
|
if (cancellation.reason) {
|
|
1221
|
-
throw new
|
|
1242
|
+
throw new ProtocolV2LinkError('generation', cancellation.reason);
|
|
1222
1243
|
}
|
|
1223
1244
|
if (this.protocolV2UsbGenerations.get(key) !== generation) {
|
|
1224
|
-
throw new
|
|
1245
|
+
throw new ProtocolV2LinkError('generation', 'Protocol V2 USB connection generation changed');
|
|
1225
1246
|
}
|
|
1226
1247
|
};
|
|
1227
1248
|
return {
|
|
@@ -1230,11 +1251,15 @@ class ProtocolV2UsbTransportBase {
|
|
|
1230
1251
|
generation,
|
|
1231
1252
|
prepareCall: () => {
|
|
1232
1253
|
assertCurrentGeneration();
|
|
1233
|
-
this.getProtocolV2UsbAssembler(key).reset();
|
|
1234
1254
|
},
|
|
1235
1255
|
writeFrame: (frame, context) => __awaiter(this, void 0, void 0, function* () {
|
|
1236
1256
|
assertCurrentGeneration();
|
|
1237
|
-
|
|
1257
|
+
try {
|
|
1258
|
+
yield this.writeProtocolV2UsbPacket(key, frame, context);
|
|
1259
|
+
}
|
|
1260
|
+
catch (error) {
|
|
1261
|
+
throw this.createProtocolV2UsbIoError('write', error);
|
|
1262
|
+
}
|
|
1238
1263
|
assertCurrentGeneration();
|
|
1239
1264
|
}),
|
|
1240
1265
|
readFrame: (context) => __awaiter(this, void 0, void 0, function* () {
|
|
@@ -1242,13 +1267,15 @@ class ProtocolV2UsbTransportBase {
|
|
|
1242
1267
|
const assembler = this.getProtocolV2UsbAssembler(key);
|
|
1243
1268
|
let frame = assembler.push(new Uint8Array(0));
|
|
1244
1269
|
while (!frame) {
|
|
1245
|
-
const packetRead = this.readProtocolV2UsbPacket(key, context)
|
|
1246
|
-
packet
|
|
1247
|
-
|
|
1270
|
+
const packetRead = this.readProtocolV2UsbPacket(key, context)
|
|
1271
|
+
.then(packet => ({ packet }))
|
|
1272
|
+
.catch(error => {
|
|
1273
|
+
throw this.createProtocolV2UsbIoError('read', error);
|
|
1274
|
+
});
|
|
1248
1275
|
const cancelled = cancellation.promise.then(reason => ({ reason }));
|
|
1249
1276
|
const result = yield Promise.race([packetRead, cancelled]);
|
|
1250
1277
|
if ('reason' in result) {
|
|
1251
|
-
throw new
|
|
1278
|
+
throw new ProtocolV2LinkError('generation', result.reason);
|
|
1252
1279
|
}
|
|
1253
1280
|
assertCurrentGeneration();
|
|
1254
1281
|
frame = assembler.push(result.packet);
|
|
@@ -1289,6 +1316,11 @@ class ProtocolV2UsbTransportBase {
|
|
|
1289
1316
|
};
|
|
1290
1317
|
return cancellation;
|
|
1291
1318
|
}
|
|
1319
|
+
createProtocolV2UsbIoError(operation, error) {
|
|
1320
|
+
if (isProtocolV2LinkError(error))
|
|
1321
|
+
return error;
|
|
1322
|
+
return new ProtocolV2LinkError('io', `Protocol V2 USB ${operation} failed: ${getErrorMessage(error) || 'unknown error'}`, error);
|
|
1323
|
+
}
|
|
1292
1324
|
}
|
|
1293
1325
|
|
|
1294
1326
|
const TRANSPORT_EVENT = {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type ProtocolV2LinkErrorCode = 'response-timeout' | 'router' | 'packet-source' | 'ack-sequence' | 'response-sequence' | 'frame';
|
|
1
|
+
export type ProtocolV2LinkErrorCode = 'response-timeout' | 'io' | 'generation' | 'router' | 'packet-source' | 'ack-sequence' | 'response-sequence' | 'frame';
|
|
2
2
|
export declare class ProtocolV2LinkError extends Error {
|
|
3
3
|
readonly code: ProtocolV2LinkErrorCode;
|
|
4
4
|
readonly cause?: unknown;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/protocols/v2/errors.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,uBAAuB,GAC/B,kBAAkB,GAClB,QAAQ,GACR,eAAe,GACf,cAAc,GACd,mBAAmB,GACnB,OAAO,CAAC;AAEZ,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,IAAI,EAAE,uBAAuB,CAAC;IAEvC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;gBAEb,IAAI,EAAE,uBAAuB,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO;CAM5E;AAED,eAAO,MAAM,qBAAqB,UAAW,OAAO,iCACd,CAAC"}
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/protocols/v2/errors.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,uBAAuB,GAC/B,kBAAkB,GAClB,IAAI,GACJ,YAAY,GACZ,QAAQ,GACR,eAAe,GACf,cAAc,GACd,mBAAmB,GACnB,OAAO,CAAC;AAEZ,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,IAAI,EAAE,uBAAuB,CAAC;IAEvC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;gBAEb,IAAI,EAAE,uBAAuB,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO;CAM5E;AAED,eAAO,MAAM,qBAAqB,UAAW,OAAO,iCACd,CAAC"}
|
|
@@ -22,6 +22,8 @@ export declare class ProtocolV2LinkManager<Key> {
|
|
|
22
22
|
private readonly links;
|
|
23
23
|
private readonly sequences;
|
|
24
24
|
private readonly callQueues;
|
|
25
|
+
private readonly generations;
|
|
26
|
+
private readonly invalidationReasons;
|
|
25
27
|
private readonly options;
|
|
26
28
|
constructor(options: ProtocolV2LinkManagerOptions<Key>);
|
|
27
29
|
call(key: Key, createAdapter: () => ProtocolV2LinkAdapter, name: string, data: Record<string, unknown>, options?: TransportCallOptions): Promise<MessageFromOneKey>;
|
|
@@ -31,5 +33,6 @@ export declare class ProtocolV2LinkManager<Key> {
|
|
|
31
33
|
private getOrCreateLink;
|
|
32
34
|
private executeCall;
|
|
33
35
|
private clearSettledCallQueue;
|
|
36
|
+
private assertCallGeneration;
|
|
34
37
|
}
|
|
35
38
|
//# 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":"
|
|
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,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;IAmBvB,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAcvD,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKjD,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM5C,OAAO,CAAC,eAAe;YA+CT,WAAW;IAkBzB,OAAO,CAAC,qBAAqB;IAM7B,OAAO,CAAC,oBAAoB;CAW7B"}
|
|
@@ -27,5 +27,6 @@ export declare abstract class ProtocolV2UsbTransportBase<Key> {
|
|
|
27
27
|
private createProtocolV2UsbAdapter;
|
|
28
28
|
private getProtocolV2UsbAssembler;
|
|
29
29
|
private createProtocolV2UsbCancellation;
|
|
30
|
+
private createProtocolV2UsbIoError;
|
|
30
31
|
}
|
|
31
32
|
//# sourceMappingURL=usb-transport-base.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usb-transport-base.d.ts","sourceRoot":"","sources":["../../../src/protocols/v2/usb-transport-base.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"usb-transport-base.d.ts","sourceRoot":"","sources":["../../../src/protocols/v2/usb-transport-base.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;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AASF,8BAAsB,0BAA0B,CAAC,GAAG;IAClD,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAA6B;IAEhE,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAA4C;IAEpF,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAA0B;IAEnE,OAAO,CAAC,QAAQ,CAAC,0BAA0B,CAA6C;IAExF,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAoC;IAEzE,SAAS,aAAa,OAAO,EAAE,iCAAiC;IAahE,SAAS,CAAC,QAAQ,CAAC,uBAAuB,IAAI,iBAAiB;IAE/D,SAAS,CAAC,QAAQ,CAAC,sBAAsB,IAAI,wBAAwB,CAAC,QAAQ,CAAC;IAE/E,SAAS,CAAC,QAAQ,CAAC,wBAAwB,CACzC,GAAG,EAAE,GAAG,EACR,KAAK,EAAE,UAAU,EACjB,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,IAAI,CAAC;IAEhB,SAAS,CAAC,QAAQ,CAAC,uBAAuB,CACxC,GAAG,EAAE,GAAG,EACR,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,UAAU,CAAC;IAEtB,SAAS,CAAC,QAAQ,CAAC,4BAA4B,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAExF,SAAS,CAAC,QAAQ,CAAC,+BAA+B,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,KAAK;IAE1F,SAAS,CAAC,8BAA8B,CAAC,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI;cAI1E,6BAA6B,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAYxF,SAAS,CAAC,iBAAiB,CACzB,GAAG,EAAE,GAAG,EACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CAAC,iBAAiB,CAAC;IAU7B,SAAS,CAAC,2BAA2B,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI9E,SAAS,CAAC,+BAA+B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;cAIxD,yBAAyB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOxE,OAAO,CAAC,0BAA0B;IAsElC,OAAO,CAAC,yBAAyB;IAQjC,OAAO,CAAC,+BAA+B;IAgBvC,OAAO,CAAC,0BAA0B;CAQnC"}
|
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.25",
|
|
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": "20bb98b8530c509299b9e109d2b5ada7c6a6d034"
|
|
32
32
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ProtocolV2SequenceCursor } from './sequence-cursor';
|
|
2
|
+
import { ProtocolV2LinkError } from './errors';
|
|
2
3
|
import { ProtocolV2Session, getErrorMessage } from './session';
|
|
3
4
|
|
|
4
5
|
import type { MessageFromOneKey, TransportCallOptions } from '../../types';
|
|
@@ -40,6 +41,10 @@ export class ProtocolV2LinkManager<Key> {
|
|
|
40
41
|
|
|
41
42
|
private readonly callQueues = new Map<Key, Promise<unknown>>();
|
|
42
43
|
|
|
44
|
+
private readonly generations = new Map<Key, number>();
|
|
45
|
+
|
|
46
|
+
private readonly invalidationReasons = new Map<Key, { generation: number; reason: string }>();
|
|
47
|
+
|
|
43
48
|
private readonly options: ProtocolV2LinkManagerOptions<Key>;
|
|
44
49
|
|
|
45
50
|
constructor(options: ProtocolV2LinkManagerOptions<Key>) {
|
|
@@ -53,7 +58,11 @@ export class ProtocolV2LinkManager<Key> {
|
|
|
53
58
|
data: Record<string, unknown>,
|
|
54
59
|
options?: TransportCallOptions
|
|
55
60
|
): Promise<MessageFromOneKey> {
|
|
56
|
-
const
|
|
61
|
+
const generation = this.generations.get(key) ?? 0;
|
|
62
|
+
const run = () => {
|
|
63
|
+
this.assertCallGeneration(key, generation);
|
|
64
|
+
return this.executeCall(key, createAdapter, name, data, options);
|
|
65
|
+
};
|
|
57
66
|
const previous = this.callQueues.get(key) ?? Promise.resolve();
|
|
58
67
|
const result = previous.then(run, run);
|
|
59
68
|
const queue = result.catch(() => undefined);
|
|
@@ -68,6 +77,10 @@ export class ProtocolV2LinkManager<Key> {
|
|
|
68
77
|
}
|
|
69
78
|
|
|
70
79
|
async invalidateLink(key: Key, reason: string): Promise<void> {
|
|
80
|
+
const generation = (this.generations.get(key) ?? 0) + 1;
|
|
81
|
+
this.generations.set(key, generation);
|
|
82
|
+
this.invalidationReasons.set(key, { generation, reason });
|
|
83
|
+
|
|
71
84
|
const link = this.links.get(key);
|
|
72
85
|
if (!link) return;
|
|
73
86
|
|
|
@@ -78,7 +91,8 @@ export class ProtocolV2LinkManager<Key> {
|
|
|
78
91
|
}
|
|
79
92
|
|
|
80
93
|
async invalidateAllLinks(reason: string): Promise<void> {
|
|
81
|
-
|
|
94
|
+
const keys = new Set([...this.links.keys(), ...this.callQueues.keys()]);
|
|
95
|
+
await Promise.all(Array.from(keys, key => this.invalidateLink(key, reason)));
|
|
82
96
|
}
|
|
83
97
|
|
|
84
98
|
async dispose(reason: string): Promise<void> {
|
|
@@ -157,4 +171,16 @@ export class ProtocolV2LinkManager<Key> {
|
|
|
157
171
|
this.callQueues.delete(key);
|
|
158
172
|
}
|
|
159
173
|
}
|
|
174
|
+
|
|
175
|
+
private assertCallGeneration(key: Key, generation: number) {
|
|
176
|
+
const currentGeneration = this.generations.get(key) ?? 0;
|
|
177
|
+
if (currentGeneration === generation) return;
|
|
178
|
+
|
|
179
|
+
const invalidation = this.invalidationReasons.get(key);
|
|
180
|
+
const reason =
|
|
181
|
+
invalidation?.generation === currentGeneration
|
|
182
|
+
? invalidation.reason
|
|
183
|
+
: 'Protocol V2 link generation changed';
|
|
184
|
+
throw new ProtocolV2LinkError('generation', reason);
|
|
185
|
+
}
|
|
160
186
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { ProtocolV2FrameAssembler } from './frame-assembler';
|
|
2
|
+
import { ProtocolV2LinkError, isProtocolV2LinkError } from './errors';
|
|
2
3
|
import { ProtocolV2LinkManager } from './link-manager';
|
|
4
|
+
import { getErrorMessage } from './session';
|
|
3
5
|
|
|
4
6
|
import type { MessageFromOneKey, TransportCallOptions } from '../../types';
|
|
5
7
|
import type { ProtocolV2CallContext, ProtocolV2Schemas, ProtocolV2SessionOptions } from './session';
|
|
@@ -32,7 +34,7 @@ export abstract class ProtocolV2UsbTransportBase<Key> {
|
|
|
32
34
|
this.protocolV2UsbOptions = options;
|
|
33
35
|
this.protocolV2UsbLinks = new ProtocolV2LinkManager<Key>({
|
|
34
36
|
getSchemas: () => this.getProtocolV2UsbSchemas(),
|
|
35
|
-
classifyError: ()
|
|
37
|
+
classifyError: error => (isProtocolV2LinkError(error) ? 'link-fatal' : 'recoverable'),
|
|
36
38
|
onLinkInvalidated: async (key, reason) => {
|
|
37
39
|
this.protocolV2UsbAssemblers.get(key)?.reset();
|
|
38
40
|
await this.resetProtocolV2UsbNativeLink(key, reason);
|
|
@@ -118,10 +120,13 @@ export abstract class ProtocolV2UsbTransportBase<Key> {
|
|
|
118
120
|
|
|
119
121
|
const assertCurrentGeneration = () => {
|
|
120
122
|
if (cancellation.reason) {
|
|
121
|
-
throw new
|
|
123
|
+
throw new ProtocolV2LinkError('generation', cancellation.reason);
|
|
122
124
|
}
|
|
123
125
|
if (this.protocolV2UsbGenerations.get(key) !== generation) {
|
|
124
|
-
throw new
|
|
126
|
+
throw new ProtocolV2LinkError(
|
|
127
|
+
'generation',
|
|
128
|
+
'Protocol V2 USB connection generation changed'
|
|
129
|
+
);
|
|
125
130
|
}
|
|
126
131
|
};
|
|
127
132
|
|
|
@@ -131,11 +136,14 @@ export abstract class ProtocolV2UsbTransportBase<Key> {
|
|
|
131
136
|
generation,
|
|
132
137
|
prepareCall: () => {
|
|
133
138
|
assertCurrentGeneration();
|
|
134
|
-
this.getProtocolV2UsbAssembler(key).reset();
|
|
135
139
|
},
|
|
136
140
|
writeFrame: async (frame: Uint8Array, context: ProtocolV2CallContext) => {
|
|
137
141
|
assertCurrentGeneration();
|
|
138
|
-
|
|
142
|
+
try {
|
|
143
|
+
await this.writeProtocolV2UsbPacket(key, frame, context);
|
|
144
|
+
} catch (error) {
|
|
145
|
+
throw this.createProtocolV2UsbIoError('write', error);
|
|
146
|
+
}
|
|
139
147
|
assertCurrentGeneration();
|
|
140
148
|
},
|
|
141
149
|
readFrame: async (context: ProtocolV2CallContext) => {
|
|
@@ -143,13 +151,15 @@ export abstract class ProtocolV2UsbTransportBase<Key> {
|
|
|
143
151
|
const assembler = this.getProtocolV2UsbAssembler(key);
|
|
144
152
|
let frame = assembler.push(new Uint8Array(0));
|
|
145
153
|
while (!frame) {
|
|
146
|
-
const packetRead = this.readProtocolV2UsbPacket(key, context)
|
|
147
|
-
packet
|
|
148
|
-
|
|
154
|
+
const packetRead = this.readProtocolV2UsbPacket(key, context)
|
|
155
|
+
.then(packet => ({ packet }))
|
|
156
|
+
.catch(error => {
|
|
157
|
+
throw this.createProtocolV2UsbIoError('read', error);
|
|
158
|
+
});
|
|
149
159
|
const cancelled = cancellation.promise.then(reason => ({ reason }));
|
|
150
160
|
const result = await Promise.race([packetRead, cancelled]);
|
|
151
161
|
if ('reason' in result) {
|
|
152
|
-
throw new
|
|
162
|
+
throw new ProtocolV2LinkError('generation', result.reason);
|
|
153
163
|
}
|
|
154
164
|
assertCurrentGeneration();
|
|
155
165
|
frame = assembler.push(result.packet);
|
|
@@ -191,4 +201,13 @@ export abstract class ProtocolV2UsbTransportBase<Key> {
|
|
|
191
201
|
};
|
|
192
202
|
return cancellation;
|
|
193
203
|
}
|
|
204
|
+
|
|
205
|
+
private createProtocolV2UsbIoError(operation: 'read' | 'write', error: unknown) {
|
|
206
|
+
if (isProtocolV2LinkError(error)) return error;
|
|
207
|
+
return new ProtocolV2LinkError(
|
|
208
|
+
'io',
|
|
209
|
+
`Protocol V2 USB ${operation} failed: ${getErrorMessage(error) || 'unknown error'}`,
|
|
210
|
+
error
|
|
211
|
+
);
|
|
212
|
+
}
|
|
194
213
|
}
|