@onekeyfe/hd-transport-web-device 1.2.0-alpha.20 → 1.2.0-alpha.22

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.
@@ -141,7 +141,6 @@ describe('ElectronBleTransport protocol detection', () => {
141
141
  { message: 'ok' },
142
142
  { router: PROTOCOL_V2_CHANNEL_BLE_UART }
143
143
  );
144
-
145
144
  nobleBle.onNotification.mockImplementation(handler => {
146
145
  notificationHandler = handler;
147
146
  return jest.fn();
@@ -220,19 +219,21 @@ describe('ElectronBleTransport protocol detection', () => {
220
219
  const device = { id: 'named-pro2-id', name: 'OneKey Pro 2' };
221
220
  const nobleBle = createNobleBle(device);
222
221
  let notificationHandler: ((deviceId: string, data: string) => void) | undefined;
223
- const probeResponse = ProtocolV2.encodeFrame(
224
- schemas,
225
- 'Success',
226
- { message: 'ok' },
227
- { router: PROTOCOL_V2_CHANNEL_BLE_UART }
228
- );
229
222
 
230
223
  nobleBle.onNotification.mockImplementation(handler => {
231
224
  notificationHandler = handler;
232
225
  return jest.fn();
233
226
  });
227
+ let responseSeq = 0;
234
228
  nobleBle.write.mockImplementation(() => {
235
- setTimeout(() => notificationHandler?.(device.id, bytesToHex(probeResponse)), 0);
229
+ responseSeq += 1;
230
+ const response = ProtocolV2.encodeFrame(
231
+ schemas,
232
+ 'Success',
233
+ { message: 'ok' },
234
+ { router: PROTOCOL_V2_CHANNEL_BLE_UART, seq: responseSeq }
235
+ );
236
+ setTimeout(() => notificationHandler?.(device.id, bytesToHex(response)), 0);
236
237
  return Promise.resolve();
237
238
  });
238
239
  const transport = configureTransport(nobleBle);
@@ -304,19 +305,20 @@ describe('ElectronBleTransport protocol detection', () => {
304
305
  const device = { id: 'repeated-acquire-pro2-id', name: 'OneKey Pro 2' };
305
306
  const nobleBle = createNobleBle(device);
306
307
  let notificationHandler: ((deviceId: string, data: string) => void) | undefined;
307
- const response = ProtocolV2.encodeFrame(
308
- schemas,
309
- 'Success',
310
- { message: 'ok' },
311
- { router: PROTOCOL_V2_CHANNEL_BLE_UART }
312
- );
313
-
314
308
  nobleBle.onNotification.mockImplementation(handler => {
315
309
  notificationHandler = handler;
316
310
  return jest.fn();
317
311
  });
312
+ let responseSeq = 0;
318
313
  nobleBle.write.mockImplementation(() => {
319
- setTimeout(() => notificationHandler?.(device.id, bytesToHex(response)), 0);
314
+ responseSeq += 1;
315
+ const sequencedResponse = ProtocolV2.encodeFrame(
316
+ schemas,
317
+ 'Success',
318
+ { message: 'ok' },
319
+ { router: PROTOCOL_V2_CHANNEL_BLE_UART, seq: responseSeq }
320
+ );
321
+ setTimeout(() => notificationHandler?.(device.id, bytesToHex(sequencedResponse)), 0);
320
322
  return Promise.resolve();
321
323
  });
322
324
  const transport = configureTransport(nobleBle);
@@ -334,7 +336,7 @@ describe('ElectronBleTransport protocol detection', () => {
334
336
  const sentSeqs = nobleBle.write.mock.calls.map(([, hex]) =>
335
337
  Number.parseInt(hex.slice(12, 14), 16)
336
338
  );
337
- expect(sentSeqs).toEqual([1, 2]);
339
+ expect(sentSeqs).toEqual([1, 2, 3]);
338
340
  } finally {
339
341
  await transport.release(device.id);
340
342
  }
@@ -1,4 +1,8 @@
1
- import transport, { ProtocolV2FrameAssembler } from '@onekeyfe/hd-transport';
1
+ import transport, {
2
+ ProtocolV2,
3
+ ProtocolV2FrameAssembler,
4
+ ProtocolV2LinkError,
5
+ } from '@onekeyfe/hd-transport';
2
6
 
3
7
  import WebUsbTransport from '../src/webusb';
4
8
 
@@ -49,7 +53,6 @@ describe('WebUsbTransport Protocol V2 timeout recovery', () => {
49
53
  webusb.receiveProtocolV2Frame = jest.fn(() => new Promise<void>(() => {}));
50
54
  webusb.resetConnectionAfterProbe = jest.fn().mockImplementation(() => {
51
55
  webusb.protocolV2Sessions.delete(path);
52
- webusb.protocolV2ReadTimeouts.delete(path);
53
56
  webusb.protocolV2Assemblers.get(path)?.reset();
54
57
  });
55
58
 
@@ -61,28 +64,101 @@ describe('WebUsbTransport Protocol V2 timeout recovery', () => {
61
64
  expect(webusb.protocolV2Sessions.has(path)).toBe(false);
62
65
  });
63
66
 
64
- test('resets a stalled write while retaining the device sequence cursor', async () => {
67
+ test.each(['router', 'packet-source', 'ack-sequence', 'response-sequence', 'frame'] as const)(
68
+ 'invalidates cached state for typed Protocol V2 %s errors',
69
+ async code => {
70
+ const webusb = new WebUsbTransport() as any;
71
+ const path = 'pro2-webusb';
72
+ webusb.messages = transport.parseConfigure(schema);
73
+ webusb.messagesV2 = transport.parseConfigure(schema);
74
+ webusb.protocolV2Assemblers.set(path, new ProtocolV2FrameAssembler());
75
+ webusb.transferOutOnce = jest.fn().mockResolvedValue(undefined);
76
+ const recoveredResponse = ProtocolV2.encodeFrame(
77
+ { protocolV1: webusb.messages, protocolV2: webusb.messagesV2 },
78
+ 'Success',
79
+ { message: 'recovered' },
80
+ { seq: 1 }
81
+ );
82
+ webusb.receiveProtocolV2Frame = jest
83
+ .fn()
84
+ .mockRejectedValueOnce(
85
+ new ProtocolV2LinkError(code, `Protocol V2 ${code} validation failed`)
86
+ )
87
+ .mockResolvedValue(recoveredResponse);
88
+ webusb.resetConnectionAfterProbe = jest.fn().mockImplementation(() => {
89
+ webusb.protocolV2Sessions.delete(path);
90
+ webusb.protocolV2Assemblers.get(path)?.reset();
91
+ });
92
+
93
+ await expect(webusb.callProtocolV2(path, 'Ping', { message: 'mismatch' })).rejects.toThrow(
94
+ `${code} validation failed`
95
+ );
96
+
97
+ expect(webusb.resetConnectionAfterProbe).toHaveBeenCalledWith(path);
98
+ expect(webusb.protocolV2Sessions.has(path)).toBe(false);
99
+
100
+ await expect(
101
+ webusb.callProtocolV2(path, 'Ping', { message: 'after-reset' })
102
+ ).resolves.toMatchObject({
103
+ type: 'Success',
104
+ message: { message: 'recovered' },
105
+ });
106
+ }
107
+ );
108
+
109
+ test('does not discard buffered Protocol V2 frames before each call', async () => {
65
110
  const webusb = new WebUsbTransport() as any;
66
111
  const path = 'pro2-webusb';
112
+ const assembler = new ProtocolV2FrameAssembler();
113
+ const reset = jest.spyOn(assembler, 'reset');
114
+ let responseSequence = 0;
67
115
  webusb.messages = transport.parseConfigure(schema);
68
116
  webusb.messagesV2 = transport.parseConfigure(schema);
69
- webusb.protocolV2WriteTimeoutMs = 10;
70
- webusb.protocolV2Assemblers.set(path, new ProtocolV2FrameAssembler());
71
- webusb.transferOutOnce = jest.fn(() => new Promise(() => {}));
72
- webusb.receiveProtocolV2Frame = jest.fn();
73
- webusb.resetConnectionAfterProbe = jest.fn().mockImplementation(() => {
74
- webusb.protocolV2Sessions.delete(path);
75
- webusb.protocolV2ReadTimeouts.delete(path);
76
- webusb.protocolV2Assemblers.get(path)?.reset();
117
+ webusb.protocolV2Assemblers.set(path, assembler);
118
+ webusb.transferOutOnce = jest.fn().mockResolvedValue(undefined);
119
+ webusb.receiveProtocolV2Frame = jest.fn().mockImplementation(() => {
120
+ responseSequence += 1;
121
+ const response = ProtocolV2.encodeFrame(
122
+ { protocolV1: webusb.messages, protocolV2: webusb.messagesV2 },
123
+ 'Success',
124
+ { message: 'ok' },
125
+ { seq: responseSequence }
126
+ );
127
+ return Promise.resolve(response);
77
128
  });
78
129
 
79
- await expect(
80
- webusb.callProtocolV2(path, 'Ping', { message: 'timeout' }, { timeoutMs: 10 })
81
- ).rejects.toThrow('Protocol V2 write timeout after 10ms for Ping');
130
+ await webusb.callProtocolV2(path, 'Ping', { message: 'first' });
131
+ await webusb.callProtocolV2(path, 'Ping', { message: 'second' });
82
132
 
83
- expect(webusb.resetConnectionAfterProbe).toHaveBeenCalledWith(path);
84
- expect(webusb.protocolV2Sequences.has(path)).toBe(true);
85
- expect(webusb.protocolV2Sessions.has(path)).toBe(false);
86
- expect(webusb.receiveProtocolV2Frame).not.toHaveBeenCalled();
133
+ expect(reset).not.toHaveBeenCalled();
134
+ });
135
+
136
+ test('keeps queued Protocol V2 read timeouts scoped to each call', async () => {
137
+ const webusb = new WebUsbTransport() as any;
138
+ const path = 'pro2-webusb';
139
+ let responseSequence = 0;
140
+ webusb.messages = transport.parseConfigure(schema);
141
+ webusb.messagesV2 = transport.parseConfigure(schema);
142
+ webusb.transferOutOnce = jest.fn().mockResolvedValue(undefined);
143
+ webusb.receiveProtocolV2Frame = jest.fn().mockImplementation(() => {
144
+ responseSequence += 1;
145
+ return Promise.resolve(
146
+ ProtocolV2.encodeFrame(
147
+ { protocolV1: webusb.messages, protocolV2: webusb.messagesV2 },
148
+ 'Success',
149
+ { message: 'ok' },
150
+ { seq: responseSequence }
151
+ )
152
+ );
153
+ });
154
+
155
+ await Promise.all([
156
+ webusb.callProtocolV2(path, 'Ping', { message: 'long' }, { timeoutMs: 1_000 }),
157
+ webusb.callProtocolV2(path, 'Ping', { message: 'short' }, { timeoutMs: 25 }),
158
+ ]);
159
+
160
+ expect(
161
+ webusb.receiveProtocolV2Frame.mock.calls.map(([, timeoutMs]: unknown[]) => timeoutMs)
162
+ ).toEqual([1_000, 25]);
87
163
  });
88
164
  });
@@ -1 +1 @@
1
- {"version":3,"file":"electron-ble-transport.d.ts","sourceRoot":"","sources":["../src/electron-ble-transport.ts"],"names":[],"mappings":";AAoBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACnG,OAAO,KAAK,YAAY,MAAM,QAAQ,CAAC;AAIvC,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,UAAU,CAAC,EAAE,UAAU,CAAC;KACzB;CACF;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,gBAAgB,CAAC,EAAE,YAAY,CAAC;CACjC,CAAC;AAqCF,MAAM,CAAC,OAAO,OAAO,oBAAoB;IACvC,OAAO,CAAC,SAAS,CAA0D;IAE3E,OAAO,CAAC,WAAW,CAA0D;IAE7E,IAAI,SAA0B;IAE9B,UAAU,UAAS;IAEnB,UAAU,EAAE,QAAQ,CAAC,UAAU,GAAG,MAAM,CAAC,GAAG,IAAI,CAAQ;IAExD,GAAG,CAAC,EAAE,GAAG,CAAC;IAEV,OAAO,CAAC,EAAE,YAAY,CAAC;IAEvB,OAAO,CAAC,gBAAgB,CAA0B;IAElD,OAAO,CAAC,cAAc,CAAwC;IAE9D,OAAO,CAAC,mBAAmB,CAAwC;IAEnE,OAAO,CAAC,SAAS,CAAsE;IAEvF,OAAO,CAAC,YAAY,CAAoD;IAExE,OAAO,CAAC,aAAa,CAAwC;IAE7D,OAAO,CAAC,eAAe,CAAgD;IAEvE,OAAO,CAAC,eAAe,CAmBpB;IAEH,OAAO,CAAC,oBAAoB,CAAsC;IAElE,OAAO,CAAC,kBAAkB,CAAsC;IAEhE,OAAO,CAAC,kBAAkB,CAAkC;IAE5D,OAAO,CAAC,qBAAqB,CAAK;IAElC,OAAO,CAAC,oBAAoB;IA+B5B,OAAO,CAAC,kBAAkB;IA0B1B,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,YAAY;IAcxC,SAAS,CAAC,UAAU,EAAE,GAAG;IAKzB,mBAAmB,CAAC,UAAU,EAAE,GAAG;IAO7B,MAAM;IAIN,SAAS,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAqBxC,OAAO,CAAC,KAAK,EAAE,eAAe;;;;;;;;;;;IA0F9B,OAAO,CAAC,EAAE,EAAE,MAAM;IAgBxB,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,4BAA4B;IAOpC,OAAO,CAAC,kBAAkB;YAMZ,cAAc;IA8C5B,OAAO,CAAC,8BAA8B;YAgBxB,iCAAiC;YAqCjC,eAAe;YAgBf,eAAe;YAuBf,iBAAiB;YAsBjB,SAAS;IASvB,OAAO,CAAC,kBAAkB;IAwB1B,OAAO,CAAC,4BAA4B;IAkBpC,OAAO,CAAC,uBAAuB;IAS/B,OAAO,CAAC,sBAAsB;IAU9B,OAAO,CAAC,yBAAyB;IAQjC,OAAO,CAAC,qBAAqB;IAK7B,OAAO,CAAC,sBAAsB;YAShB,mBAAmB;IAiBjC,OAAO,CAAC,4BAA4B;IAgB9B,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,oBAAoB;YA2BlB,cAAc;YAuEd,cAAc;IA6B5B,OAAO,CAAC,uBAAuB;IA0C/B,OAAO,CAAC,6BAA6B;IAsCrC,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;CAGxD"}
1
+ {"version":3,"file":"electron-ble-transport.d.ts","sourceRoot":"","sources":["../src/electron-ble-transport.ts"],"names":[],"mappings":";AAoBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACnG,OAAO,KAAK,YAAY,MAAM,QAAQ,CAAC;AAIvC,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,UAAU,CAAC,EAAE,UAAU,CAAC;KACzB;CACF;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,gBAAgB,CAAC,EAAE,YAAY,CAAC;CACjC,CAAC;AAoCF,MAAM,CAAC,OAAO,OAAO,oBAAoB;IACvC,OAAO,CAAC,SAAS,CAA0D;IAE3E,OAAO,CAAC,WAAW,CAA0D;IAE7E,IAAI,SAA0B;IAE9B,UAAU,UAAS;IAEnB,UAAU,EAAE,QAAQ,CAAC,UAAU,GAAG,MAAM,CAAC,GAAG,IAAI,CAAQ;IAExD,GAAG,CAAC,EAAE,GAAG,CAAC;IAEV,OAAO,CAAC,EAAE,YAAY,CAAC;IAEvB,OAAO,CAAC,gBAAgB,CAA0B;IAElD,OAAO,CAAC,cAAc,CAAwC;IAE9D,OAAO,CAAC,mBAAmB,CAAwC;IAEnE,OAAO,CAAC,SAAS,CAAsE;IAEvF,OAAO,CAAC,YAAY,CAAoD;IAExE,OAAO,CAAC,aAAa,CAAwC;IAE7D,OAAO,CAAC,eAAe,CAAgD;IAEvE,OAAO,CAAC,eAAe,CAmBpB;IAEH,OAAO,CAAC,oBAAoB,CAAsC;IAElE,OAAO,CAAC,kBAAkB,CAAsC;IAEhE,OAAO,CAAC,kBAAkB,CAAkC;IAE5D,OAAO,CAAC,qBAAqB,CAAK;IAElC,OAAO,CAAC,oBAAoB;IA+B5B,OAAO,CAAC,kBAAkB;IA0B1B,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,YAAY;IAcxC,SAAS,CAAC,UAAU,EAAE,GAAG;IAKzB,mBAAmB,CAAC,UAAU,EAAE,GAAG;IAO7B,MAAM;IAIN,SAAS,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAqBxC,OAAO,CAAC,KAAK,EAAE,eAAe;;;;;;;;;;;IA0F9B,OAAO,CAAC,EAAE,EAAE,MAAM;IAgBxB,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,4BAA4B;IAOpC,OAAO,CAAC,kBAAkB;YAMZ,cAAc;IA+C5B,OAAO,CAAC,8BAA8B;YAgBxB,iCAAiC;YAqCjC,eAAe;YAgBf,eAAe;YAuBf,iBAAiB;YAsBjB,SAAS;IASvB,OAAO,CAAC,kBAAkB;IAwB1B,OAAO,CAAC,4BAA4B;IAkBpC,OAAO,CAAC,uBAAuB;IAS/B,OAAO,CAAC,sBAAsB;IAU9B,OAAO,CAAC,yBAAyB;IAQjC,OAAO,CAAC,qBAAqB;IAK7B,OAAO,CAAC,sBAAsB;YAShB,mBAAmB;IAiBjC,OAAO,CAAC,4BAA4B;IAgB9B,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,oBAAoB;YA2BlB,cAAc;YAuEd,cAAc;IA0B5B,OAAO,CAAC,uBAAuB;IA0C/B,OAAO,CAAC,6BAA6B;IAsCrC,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;CAGxD"}
package/dist/index.d.ts CHANGED
@@ -25,9 +25,6 @@ declare class WebUsbTransport {
25
25
  private protocolV2Sessions;
26
26
  /** Sequence cursors survive ordinary reconnects and cached session rebuilds. */
27
27
  private protocolV2Sequences;
28
- /** Read timeout for the current Protocol V2 call, consumed by cached readFrame. */
29
- private protocolV2ReadTimeouts;
30
- private protocolV2WriteTimeoutMs;
31
28
  /** Per-path USB endpoint / interface numbers (discovered from USB descriptors) */
32
29
  private deviceEndpoints;
33
30
  /**
package/dist/index.js CHANGED
@@ -70,8 +70,6 @@ class WebUsbTransport {
70
70
  this.protocolV2Assemblers = new Map();
71
71
  this.protocolV2Sessions = new Map();
72
72
  this.protocolV2Sequences = new Map();
73
- this.protocolV2ReadTimeouts = new Map();
74
- this.protocolV2WriteTimeoutMs = transport.PROTOCOL_V2_WRITE_WATCHDOG_TIMEOUT_MS;
75
73
  this.deviceEndpoints = new Map();
76
74
  this.mockSerialPaths = new WeakMap();
77
75
  this.mockSerialCounter = 0;
@@ -99,7 +97,6 @@ class WebUsbTransport {
99
97
  configureProtocolV2(signedData) {
100
98
  this.messagesV2 = parseConfigure$1(signedData);
101
99
  this.protocolV2Sessions.clear();
102
- this.protocolV2ReadTimeouts.clear();
103
100
  }
104
101
  promptDeviceAccess() {
105
102
  return __awaiter(this, void 0, void 0, function* () {
@@ -200,8 +197,11 @@ class WebUsbTransport {
200
197
  throw this.createProtocolMismatchError(expectedProtocol);
201
198
  }
202
199
  if (expectedProtocol === 'V2') {
203
- this.deviceProtocol.set(path, 'V2');
204
- return 'V2';
200
+ if (yield this.probeProtocolV2(path)) {
201
+ this.deviceProtocol.set(path, 'V2');
202
+ return 'V2';
203
+ }
204
+ throw this.createProtocolMismatchError(expectedProtocol);
205
205
  }
206
206
  const probeOrder = protocolHint === 'V2' || this.deviceProtocol.get(path) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
207
207
  for (const protocol of probeOrder) {
@@ -496,7 +496,6 @@ class WebUsbTransport {
496
496
  return __awaiter(this, void 0, void 0, function* () {
497
497
  (_a = this.protocolV2Assemblers.get(path)) === null || _a === void 0 ? void 0 : _a.reset();
498
498
  this.protocolV2Sessions.delete(path);
499
- this.protocolV2ReadTimeouts.delete(path);
500
499
  try {
501
500
  const device = yield this.findDevice(path);
502
501
  if (device.opened) {
@@ -518,7 +517,7 @@ class WebUsbTransport {
518
517
  yield this.connect(path, false);
519
518
  });
520
519
  }
521
- withProtocolReadTimeout(_path, promise, timeoutMs, protocol, onTimeout) {
520
+ withProtocolReadTimeout(path, promise, timeoutMs, protocol, onTimeout) {
522
521
  return __awaiter(this, void 0, void 0, function* () {
523
522
  let timer;
524
523
  let timedOut = false;
@@ -533,11 +532,19 @@ class WebUsbTransport {
533
532
  return yield Promise.race([
534
533
  guardedPromise,
535
534
  new Promise((_, reject) => {
536
- timer = setTimeout(() => {
535
+ timer = setTimeout(() => __awaiter(this, void 0, void 0, function* () {
537
536
  timedOut = true;
538
537
  onTimeout === null || onTimeout === void 0 ? void 0 : onTimeout();
538
+ if (protocol === 'V1') {
539
+ try {
540
+ yield this.resetConnectionAfterProbe(path);
541
+ }
542
+ catch (error) {
543
+ this.Log.debug('[WebUsbTransport] reset after Protocol V1 timeout failed:', error);
544
+ }
545
+ }
539
546
  reject(new Error(`Protocol ${protocol} read timeout after ${timeoutMs}ms`));
540
- }, timeoutMs);
547
+ }), timeoutMs);
541
548
  }),
542
549
  ]);
543
550
  }
@@ -618,7 +625,6 @@ class WebUsbTransport {
618
625
  });
619
626
  }
620
627
  callProtocolV2(path, name, data, options) {
621
- var _a;
622
628
  return __awaiter(this, void 0, void 0, function* () {
623
629
  const protocolV1Messages = this.messages;
624
630
  if (!this.messagesV2) {
@@ -641,38 +647,28 @@ class WebUsbTransport {
641
647
  },
642
648
  router: transport.PROTOCOL_V2_CHANNEL_USB,
643
649
  sequenceCursor,
644
- writeTimeoutMs: this.protocolV2WriteTimeoutMs,
645
650
  writeFrame: (frame) => this.transferOutOnce(path, frame),
646
- readFrame: () => this.receiveProtocolV2Frame(path, this.protocolV2ReadTimeouts.get(path)),
651
+ readFrame: context => this.receiveProtocolV2Frame(path, context.timeoutMs),
647
652
  logger: this.Log,
648
653
  logPrefix: 'ProtocolV2 WebUSB',
649
- createTimeoutError: (messageName, timeoutMs) => new Error(`Protocol V2 response timeout after ${timeoutMs}ms for ${messageName}`),
654
+ createTimeoutError: (messageName, timeoutMs) => new transport.ProtocolV2LinkError('response-timeout', `Protocol V2 response timeout after ${timeoutMs}ms for ${messageName}`),
650
655
  });
651
656
  this.protocolV2Sessions.set(path, session);
652
657
  }
653
- this.protocolV2ReadTimeouts.set(path, options === null || options === void 0 ? void 0 : options.timeoutMs);
654
- (_a = this.protocolV2Assemblers.get(path)) === null || _a === void 0 ? void 0 : _a.reset();
655
658
  try {
656
659
  return yield session.call(name, data, options);
657
660
  }
658
661
  catch (error) {
659
- const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
660
- if (message.includes('protocol v2 read timeout') ||
661
- message.includes('protocol v2 write timeout') ||
662
- message.includes('protocol v2 delivery timeout') ||
663
- message.includes('response timeout')) {
662
+ if (transport.isProtocolV2LinkError(error) || this.isRetryablePacketIoError(error)) {
664
663
  try {
665
664
  yield this.resetConnectionAfterProbe(path);
666
665
  }
667
666
  catch (resetError) {
668
- this.Log.debug('[WebUsbTransport] Protocol V2 timeout reset failed:', resetError);
667
+ this.Log.debug('[WebUsbTransport] Protocol V2 link reset failed:', resetError);
669
668
  }
670
669
  }
671
670
  throw error;
672
671
  }
673
- finally {
674
- this.protocolV2ReadTimeouts.delete(path);
675
- }
676
672
  });
677
673
  }
678
674
  receiveProtocolV2Frame(path, timeoutMs) {
@@ -693,12 +689,7 @@ class WebUsbTransport {
693
689
  })
694
690
  : yield transferIn;
695
691
  const bytes = new Uint8Array(this.toArrayBuffer(dataView.buffer.slice(dataView.byteOffset, dataView.byteOffset + dataView.byteLength)));
696
- try {
697
- frame = assembler.push(bytes);
698
- }
699
- catch (error) {
700
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.NetworkError, error instanceof Error ? error.message : String(error));
701
- }
692
+ frame = assembler.push(bytes);
702
693
  }
703
694
  return frame;
704
695
  });
@@ -753,7 +744,6 @@ class WebUsbTransport {
753
744
  (_b = this.protocolV2Assemblers.get(path)) === null || _b === void 0 ? void 0 : _b.reset();
754
745
  this.protocolV2Assemblers.delete(path);
755
746
  this.protocolV2Sessions.delete(path);
756
- this.protocolV2ReadTimeouts.delete(path);
757
747
  this.deviceEndpoints.delete(path);
758
748
  });
759
749
  }
@@ -769,7 +759,6 @@ function inferProtocolHintFromDeviceName(name) {
769
759
  const toBleDescriptor = (device, protocolType) => (Object.assign({ id: device.id, name: device.name, path: device.id, debug: false, commType: 'electron-ble' }, (protocolType ? { protocolType } : {})));
770
760
  const BLE_PACKET_SIZE = 192;
771
761
  const BLE_WRITE_DELAY_MS = 5;
772
- const BLE_RESPONSE_TIMEOUT_MS = 30000;
773
762
  const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
774
763
  const PROTOCOL_V2_PROBE_TIMEOUT_MS = 5000;
775
764
  class ElectronBleTransport {
@@ -1028,9 +1017,12 @@ class ElectronBleTransport {
1028
1017
  throw this.createProtocolMismatchError(expectedProtocol);
1029
1018
  }
1030
1019
  if (expectedProtocol === 'V2') {
1031
- this.deviceProtocol.set(uuid, 'V2');
1032
- (_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug(`[Electron BLE] detectProtocol: uuid=${uuid} -> V2 (expected)`);
1033
- return 'V2';
1020
+ if (yield this.probeProtocolV2(uuid)) {
1021
+ this.deviceProtocol.set(uuid, 'V2');
1022
+ (_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug(`[Electron BLE] detectProtocol: uuid=${uuid} -> V2 (expected)`);
1023
+ return 'V2';
1024
+ }
1025
+ throw this.createProtocolMismatchError(expectedProtocol);
1034
1026
  }
1035
1027
  const probeOrder = protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
1036
1028
  for (let i = 0; i < probeOrder.length; i += 1) {
@@ -1366,17 +1358,17 @@ class ElectronBleTransport {
1366
1358
  });
1367
1359
  }
1368
1360
  callProtocolV2(uuid, name, data, options) {
1369
- var _a, _b;
1361
+ var _a;
1370
1362
  return __awaiter(this, void 0, void 0, function* () {
1371
1363
  if (!this._messages || !this._messagesV2) {
1372
1364
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
1373
1365
  }
1374
- const callOptions = Object.assign(Object.assign({}, options), { timeoutMs: (_a = options === null || options === void 0 ? void 0 : options.timeoutMs) !== null && _a !== void 0 ? _a : BLE_RESPONSE_TIMEOUT_MS });
1366
+ const callOptions = options;
1375
1367
  try {
1376
1368
  return yield this.protocolV2Links.call(uuid, () => this.createProtocolV2Adapter(uuid), name, data, callOptions);
1377
1369
  }
1378
1370
  catch (e) {
1379
- (_b = this.Log) === null || _b === void 0 ? void 0 : _b.error('[Electron BLE] Protocol V2 call error:', e);
1371
+ (_a = this.Log) === null || _a === void 0 ? void 0 : _a.error('[Electron BLE] Protocol V2 call error:', e);
1380
1372
  throw e;
1381
1373
  }
1382
1374
  });
package/dist/webusb.d.ts CHANGED
@@ -14,8 +14,6 @@ export default class WebUsbTransport {
14
14
  private protocolV2Assemblers;
15
15
  private protocolV2Sessions;
16
16
  private protocolV2Sequences;
17
- private protocolV2ReadTimeouts;
18
- private protocolV2WriteTimeoutMs;
19
17
  private deviceEndpoints;
20
18
  private mockSerialPaths;
21
19
  private mockSerialCounter;
@@ -1 +1 @@
1
- {"version":3,"file":"webusb.d.ts","sourceRoot":"","sources":["../src/webusb.ts"],"names":[],"mappings":";AACA,OAAO,SAYN,MAAM,wBAAwB,CAAC;AAYhC,OAAO,KAAK,EACV,YAAY,EACZ,oBAAoB,EACpB,YAAY,EACZ,oBAAoB,EACrB,MAAM,wBAAwB,CAAC;AAqBhC,MAAM,WAAW,UAAW,SAAQ,oBAAoB;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,CAAC;IAClB,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAaD,MAAM,CAAC,OAAO,OAAO,eAAe;IAClC,QAAQ,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAGlE,UAAU,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAGpE,OAAO,CAAC,cAAc,CAAwC;IAE9D,OAAO,CAAC,mBAAmB,CAAwC;IAGnE,OAAO,CAAC,oBAAoB,CAAoD;IAGhF,OAAO,CAAC,kBAAkB,CAA6C;IAGvE,OAAO,CAAC,mBAAmB,CAAoD;IAG/E,OAAO,CAAC,sBAAsB,CAA8C;IAE5E,OAAO,CAAC,wBAAwB,CAAyC;IAGzE,OAAO,CAAC,eAAe,CAA2C;IAMlE,OAAO,CAAC,eAAe,CAA6C;IAEpE,OAAO,CAAC,iBAAiB,CAAK;IAE9B,IAAI,SAAqB;IAEzB,OAAO,UAAS;IAEhB,UAAU,UAAS;IAEnB,GAAG,CAAC,EAAE,GAAG,CAAC;IAEV,GAAG,CAAC,EAAE,GAAG,CAAC;IAMV,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAM;IAEnC,eAAe,SAAoB;IAEnC,UAAU,SAAe;IAEzB,WAAW,SAAgB;IAK3B,IAAI,CAAC,MAAM,EAAE,GAAG;IAgBhB,SAAS,CAAC,UAAU,EAAE,GAAG;IASzB,mBAAmB,CAAC,UAAU,EAAE,GAAG;IAW7B,kBAAkB;IAmBlB,SAAS;IAQf,OAAO,CAAC,aAAa;IAmBf,mBAAmB;IAgCnB,OAAO,CAAC,KAAK,EAAE,YAAY;IA0BjC,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,4BAA4B;YAOtB,cAAc;IAgDtB,UAAU,CAAC,IAAI,EAAE,MAAM;IAwBvB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;IAkB1C,OAAO,CAAC,iBAAiB;IAiCnB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;YAmCpC,eAAe;YAmBf,iBAAiB;IAezB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAIvE,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,wBAAwB;YAalB,yBAAyB;IAiCvC,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,aAAa;YASP,oBAAoB;YA2BpB,eAAe;YAaf,mBAAmB;YA2CnB,yBAAyB;YAyBzB,uBAAuB;YAmCvB,eAAe;YAaf,eAAe;IAgBvB,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,oBAAoB;YA8BlB,cAAc;YAkCd,cAAc;YAmEd,sBAAsB;IA4C9B,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;IAgD5C,OAAO,CAAC,IAAI,EAAE,MAAM;IAmB1B,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;CAGxD"}
1
+ {"version":3,"file":"webusb.d.ts","sourceRoot":"","sources":["../src/webusb.ts"],"names":[],"mappings":";AACA,OAAO,SAaN,MAAM,wBAAwB,CAAC;AAYhC,OAAO,KAAK,EACV,YAAY,EACZ,oBAAoB,EACpB,YAAY,EACZ,oBAAoB,EACrB,MAAM,wBAAwB,CAAC;AAqBhC,MAAM,WAAW,UAAW,SAAQ,oBAAoB;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,CAAC;IAClB,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAaD,MAAM,CAAC,OAAO,OAAO,eAAe;IAClC,QAAQ,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAGlE,UAAU,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAGpE,OAAO,CAAC,cAAc,CAAwC;IAE9D,OAAO,CAAC,mBAAmB,CAAwC;IAGnE,OAAO,CAAC,oBAAoB,CAAoD;IAGhF,OAAO,CAAC,kBAAkB,CAA6C;IAGvE,OAAO,CAAC,mBAAmB,CAAoD;IAG/E,OAAO,CAAC,eAAe,CAA2C;IAMlE,OAAO,CAAC,eAAe,CAA6C;IAEpE,OAAO,CAAC,iBAAiB,CAAK;IAE9B,IAAI,SAAqB;IAEzB,OAAO,UAAS;IAEhB,UAAU,UAAS;IAEnB,GAAG,CAAC,EAAE,GAAG,CAAC;IAEV,GAAG,CAAC,EAAE,GAAG,CAAC;IAMV,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAM;IAEnC,eAAe,SAAoB;IAEnC,UAAU,SAAe;IAEzB,WAAW,SAAgB;IAK3B,IAAI,CAAC,MAAM,EAAE,GAAG;IAgBhB,SAAS,CAAC,UAAU,EAAE,GAAG;IASzB,mBAAmB,CAAC,UAAU,EAAE,GAAG;IAU7B,kBAAkB;IAmBlB,SAAS;IAQf,OAAO,CAAC,aAAa;IAmBf,mBAAmB;IAgCnB,OAAO,CAAC,KAAK,EAAE,YAAY;IA0BjC,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,4BAA4B;YAOtB,cAAc;IAiDtB,UAAU,CAAC,IAAI,EAAE,MAAM;IAwBvB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;IAkB1C,OAAO,CAAC,iBAAiB;IAiCnB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;YAmCpC,eAAe;YAmBf,iBAAiB;IAezB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAIvE,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,wBAAwB;YAalB,yBAAyB;IAiCvC,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,aAAa;YASP,oBAAoB;YA2BpB,eAAe;YAaf,mBAAmB;YA2CnB,yBAAyB;YAwBzB,uBAAuB;YA0CvB,eAAe;YAaf,eAAe;IAgBvB,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,oBAAoB;YA8BlB,cAAc;YAkCd,cAAc;YA0Dd,sBAAsB;IAqC9B,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;IAgD5C,OAAO,CAAC,IAAI,EAAE,MAAM;IAkB1B,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;CAGxD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-transport-web-device",
3
- "version": "1.2.0-alpha.20",
3
+ "version": "1.2.0-alpha.22",
4
4
  "author": "OneKey",
5
5
  "homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
6
6
  "license": "MIT",
@@ -20,13 +20,13 @@
20
20
  "lint:fix": "eslint . --fix"
21
21
  },
22
22
  "dependencies": {
23
- "@onekeyfe/hd-shared": "1.2.0-alpha.20",
24
- "@onekeyfe/hd-transport": "1.2.0-alpha.20"
23
+ "@onekeyfe/hd-shared": "1.2.0-alpha.22",
24
+ "@onekeyfe/hd-transport": "1.2.0-alpha.22"
25
25
  },
26
26
  "devDependencies": {
27
- "@onekeyfe/hd-transport-electron": "1.2.0-alpha.20",
27
+ "@onekeyfe/hd-transport-electron": "1.2.0-alpha.22",
28
28
  "@types/w3c-web-usb": "^1.0.6",
29
29
  "@types/web-bluetooth": "^0.0.17"
30
30
  },
31
- "gitHead": "5fbc1ada90fd3cfda7e5ef08be368cb452018733"
31
+ "gitHead": "ce94a5ebbe141b0c15db7c68d08085ca1ddb0166"
32
32
  }
@@ -62,7 +62,6 @@ const toBleDescriptor = (
62
62
 
63
63
  const BLE_PACKET_SIZE = 192;
64
64
  const BLE_WRITE_DELAY_MS = 5;
65
- const BLE_RESPONSE_TIMEOUT_MS = 30_000;
66
65
  const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
67
66
  const PROTOCOL_V2_PROBE_TIMEOUT_MS = 5000;
68
67
 
@@ -379,11 +378,12 @@ export default class ElectronBleTransport {
379
378
  }
380
379
 
381
380
  if (expectedProtocol === 'V2') {
382
- // Skip probing when the caller explicitly confirms V2, such as reconnect after a
383
- // firmware reboot where expectedProtocol carries the previously probed result.
384
- this.deviceProtocol.set(uuid, 'V2');
385
- this.Log?.debug(`[Electron BLE] detectProtocol: uuid=${uuid} -> V2 (expected)`);
386
- return 'V2';
381
+ if (await this.probeProtocolV2(uuid)) {
382
+ this.deviceProtocol.set(uuid, 'V2');
383
+ this.Log?.debug(`[Electron BLE] detectProtocol: uuid=${uuid} -> V2 (expected)`);
384
+ return 'V2';
385
+ }
386
+ throw this.createProtocolMismatchError(expectedProtocol);
387
387
  }
388
388
 
389
389
  // Protocol must be actively probed after connection. Name, PID, and descriptors only
@@ -761,10 +761,7 @@ export default class ElectronBleTransport {
761
761
  throw ERRORS.TypedError(HardwareErrorCode.TransportNotConfigured);
762
762
  }
763
763
 
764
- const callOptions = {
765
- ...options,
766
- timeoutMs: options?.timeoutMs ?? BLE_RESPONSE_TIMEOUT_MS,
767
- };
764
+ const callOptions = options;
768
765
 
769
766
  try {
770
767
  return await this.protocolV2Links.call(
package/src/webusb.ts CHANGED
@@ -6,10 +6,11 @@ import transport, {
6
6
  PROTOCOL_V1_USB_PACKET_SIZE,
7
7
  PROTOCOL_V2_CHANNEL_USB,
8
8
  PROTOCOL_V2_FRAME_MAX_BYTES,
9
- PROTOCOL_V2_WRITE_WATCHDOG_TIMEOUT_MS,
10
9
  ProtocolV2FrameAssembler,
10
+ ProtocolV2LinkError,
11
11
  ProtocolV2SequenceCursor,
12
12
  ProtocolV2Session,
13
+ isProtocolV2LinkError,
13
14
  probeProtocolV2 as probeProtocolV2Helper,
14
15
  } from '@onekeyfe/hd-transport';
15
16
  import {
@@ -86,11 +87,6 @@ export default class WebUsbTransport {
86
87
  /** Sequence cursors survive ordinary reconnects and cached session rebuilds. */
87
88
  private protocolV2Sequences: Map<string, ProtocolV2SequenceCursor> = new Map();
88
89
 
89
- /** Read timeout for the current Protocol V2 call, consumed by cached readFrame. */
90
- private protocolV2ReadTimeouts: Map<string, number | undefined> = new Map();
91
-
92
- private protocolV2WriteTimeoutMs = PROTOCOL_V2_WRITE_WATCHDOG_TIMEOUT_MS;
93
-
94
90
  /** Per-path USB endpoint / interface numbers (discovered from USB descriptors) */
95
91
  private deviceEndpoints: Map<string, DeviceEndpoints> = new Map();
96
92
 
@@ -155,7 +151,6 @@ export default class WebUsbTransport {
155
151
  configureProtocolV2(signedData: any) {
156
152
  this.messagesV2 = parseConfigure(signedData);
157
153
  this.protocolV2Sessions.clear();
158
- this.protocolV2ReadTimeouts.clear();
159
154
  }
160
155
 
161
156
  /**
@@ -296,10 +291,11 @@ export default class WebUsbTransport {
296
291
  }
297
292
 
298
293
  if (expectedProtocol === 'V2') {
299
- // Skip probing when the caller explicitly confirms V2, such as reconnect after a
300
- // firmware reboot where expectedProtocol carries the previously probed result.
301
- this.deviceProtocol.set(path, 'V2');
302
- return 'V2';
294
+ if (await this.probeProtocolV2(path)) {
295
+ this.deviceProtocol.set(path, 'V2');
296
+ return 'V2';
297
+ }
298
+ throw this.createProtocolMismatchError(expectedProtocol);
303
299
  }
304
300
 
305
301
  // Protocol must be actively probed after connection. Name, PID, and descriptors only
@@ -638,7 +634,6 @@ export default class WebUsbTransport {
638
634
  private async resetConnectionAfterProbe(path: string) {
639
635
  this.protocolV2Assemblers.get(path)?.reset();
640
636
  this.protocolV2Sessions.delete(path);
641
- this.protocolV2ReadTimeouts.delete(path);
642
637
 
643
638
  try {
644
639
  const device = await this.findDevice(path);
@@ -661,7 +656,7 @@ export default class WebUsbTransport {
661
656
  }
662
657
 
663
658
  private async withProtocolReadTimeout<T>(
664
- _path: string,
659
+ path: string,
665
660
  promise: Promise<T>,
666
661
  timeoutMs: number,
667
662
  protocol: ProtocolType,
@@ -683,9 +678,16 @@ export default class WebUsbTransport {
683
678
  return await Promise.race([
684
679
  guardedPromise,
685
680
  new Promise<never>((_, reject) => {
686
- timer = setTimeout(() => {
681
+ timer = setTimeout(async () => {
687
682
  timedOut = true;
688
683
  onTimeout?.();
684
+ if (protocol === 'V1') {
685
+ try {
686
+ await this.resetConnectionAfterProbe(path);
687
+ } catch (error) {
688
+ this.Log.debug('[WebUsbTransport] reset after Protocol V1 timeout failed:', error);
689
+ }
690
+ }
689
691
  reject(new Error(`Protocol ${protocol} read timeout after ${timeoutMs}ms`));
690
692
  }, timeoutMs);
691
693
  }),
@@ -823,39 +825,30 @@ export default class WebUsbTransport {
823
825
  },
824
826
  router: PROTOCOL_V2_CHANNEL_USB,
825
827
  sequenceCursor,
826
- writeTimeoutMs: this.protocolV2WriteTimeoutMs,
827
828
  writeFrame: (frame: Uint8Array) => this.transferOutOnce(path, frame),
828
- readFrame: () => this.receiveProtocolV2Frame(path, this.protocolV2ReadTimeouts.get(path)),
829
+ readFrame: context => this.receiveProtocolV2Frame(path, context.timeoutMs),
829
830
  logger: this.Log,
830
831
  logPrefix: 'ProtocolV2 WebUSB',
831
832
  createTimeoutError: (messageName: string, timeoutMs: number) =>
832
- new Error(`Protocol V2 response timeout after ${timeoutMs}ms for ${messageName}`),
833
+ new ProtocolV2LinkError(
834
+ 'response-timeout',
835
+ `Protocol V2 response timeout after ${timeoutMs}ms for ${messageName}`
836
+ ),
833
837
  });
834
838
  this.protocolV2Sessions.set(path, session);
835
839
  }
836
840
 
837
- this.protocolV2ReadTimeouts.set(path, options?.timeoutMs);
838
- this.protocolV2Assemblers.get(path)?.reset();
839
841
  try {
840
842
  return await session.call(name, data, options);
841
843
  } catch (error) {
842
- const message =
843
- error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
844
- if (
845
- message.includes('protocol v2 read timeout') ||
846
- message.includes('protocol v2 write timeout') ||
847
- message.includes('protocol v2 delivery timeout') ||
848
- message.includes('response timeout')
849
- ) {
844
+ if (isProtocolV2LinkError(error) || this.isRetryablePacketIoError(error)) {
850
845
  try {
851
846
  await this.resetConnectionAfterProbe(path);
852
847
  } catch (resetError) {
853
- this.Log.debug('[WebUsbTransport] Protocol V2 timeout reset failed:', resetError);
848
+ this.Log.debug('[WebUsbTransport] Protocol V2 link reset failed:', resetError);
854
849
  }
855
850
  }
856
851
  throw error;
857
- } finally {
858
- this.protocolV2ReadTimeouts.delete(path);
859
852
  }
860
853
  }
861
854
 
@@ -888,14 +881,7 @@ export default class WebUsbTransport {
888
881
  dataView.buffer.slice(dataView.byteOffset, dataView.byteOffset + dataView.byteLength)
889
882
  )
890
883
  );
891
- try {
892
- frame = assembler.push(bytes);
893
- } catch (error) {
894
- throw ERRORS.TypedError(
895
- HardwareErrorCode.NetworkError,
896
- error instanceof Error ? error.message : String(error)
897
- );
898
- }
884
+ frame = assembler.push(bytes);
899
885
  }
900
886
  return frame;
901
887
  }
@@ -962,7 +948,6 @@ export default class WebUsbTransport {
962
948
  this.protocolV2Assemblers.get(path)?.reset();
963
949
  this.protocolV2Assemblers.delete(path);
964
950
  this.protocolV2Sessions.delete(path);
965
- this.protocolV2ReadTimeouts.delete(path);
966
951
  this.deviceEndpoints.delete(path);
967
952
  }
968
953