@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.
@@ -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
- const splitAt = Math.min(4, response.length);
139
- this.packetQueues.set(key, [response.slice(0, splitAt), response.slice(splitAt)]);
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 run = () => this.executeCall(key, createAdapter, name, data, options);
1063
- const previous = (_a = this.callQueues.get(key)) !== null && _a !== void 0 ? _a : Promise.resolve();
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 ((_b = (_a = this.options).onLinkInvalidated) === null || _b === void 0 ? void 0 : _b.call(_a, key, reason));
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
- yield Promise.all(Array.from(this.links.keys(), key => this.invalidateLink(key, reason)));
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: () => 'link-fatal',
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 Error(cancellation.reason);
1242
+ throw new ProtocolV2LinkError('generation', cancellation.reason);
1222
1243
  }
1223
1244
  if (this.protocolV2UsbGenerations.get(key) !== generation) {
1224
- throw new Error('Protocol V2 USB connection generation changed');
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
- yield this.writeProtocolV2UsbPacket(key, frame, context);
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).then(packet => ({
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 Error(result.reason);
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":"AAGA,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,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;IAevB,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUvD,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIjD,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM5C,OAAO,CAAC,eAAe;YA+CT,WAAW;IAkBzB,OAAO,CAAC,qBAAqB;CAK9B"}
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":"AAGA,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;IA8DlC,OAAO,CAAC,yBAAyB;IAQjC,OAAO,CAAC,+BAA+B;CAexC"}
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.24",
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": "43663337d94350430cc507c74d6eec5a076bd4e9"
31
+ "gitHead": "20bb98b8530c509299b9e109d2b5ada7c6a6d034"
32
32
  }
@@ -1,5 +1,7 @@
1
1
  export type ProtocolV2LinkErrorCode =
2
2
  | 'response-timeout'
3
+ | 'io'
4
+ | 'generation'
3
5
  | 'router'
4
6
  | 'packet-source'
5
7
  | 'ack-sequence'
@@ -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 run = () => this.executeCall(key, createAdapter, name, data, options);
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
- await Promise.all(Array.from(this.links.keys(), key => this.invalidateLink(key, reason)));
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: () => 'link-fatal',
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 Error(cancellation.reason);
123
+ throw new ProtocolV2LinkError('generation', cancellation.reason);
122
124
  }
123
125
  if (this.protocolV2UsbGenerations.get(key) !== generation) {
124
- throw new Error('Protocol V2 USB connection generation changed');
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
- await this.writeProtocolV2UsbPacket(key, frame, context);
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).then(packet => ({
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 Error(result.reason);
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
  }