@onekeyfe/hd-transport-web-device 1.2.0-alpha.3 → 1.2.0-alpha.30

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/dist/index.js CHANGED
@@ -41,6 +41,14 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
41
41
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
42
42
  };
43
43
 
44
+ const HIGH_VOLUME_CALLS = new Set(['FileWrite', 'FilesystemFileWrite', 'EmmcFileWrite']);
45
+ function shouldSuppressHighVolumeCallLog(name) {
46
+ return HIGH_VOLUME_CALLS.has(name);
47
+ }
48
+ function createTransportCallLog(name, protocol) {
49
+ return { name, protocol };
50
+ }
51
+
44
52
  const { parseConfigure: parseConfigure$1, check: check$1, ProtocolV1: ProtocolV1$1 } = transport__default["default"];
45
53
  const CONFIGURATION_ID = 1;
46
54
  const INTERFACE_ID = 0;
@@ -52,29 +60,19 @@ const HEADER_LENGTH = transport.PROTOCOL_V1_MESSAGE_HEADER_SIZE;
52
60
  const PACKET_IO_MAX_RETRIES = 3;
53
61
  const PACKET_IO_RETRY_DELAY = 300;
54
62
  const PROTOCOL_PROBE_TIMEOUT = 1000;
55
- const WEBUSB_FILE_WRITE_LOG_BLOCK_PATTERN = /(?:^|[^a-z])(?:raw)?(?:filesystem|emmc)?filewrite$/i;
56
- function shouldSuppressWebUsbCallLog(name) {
57
- const normalized = name.replace(/[_\s-]/g, '');
58
- return WEBUSB_FILE_WRITE_LOG_BLOCK_PATTERN.test(normalized);
59
- }
60
- function isLogBlockCommand$1(name) {
61
- var _a, _b;
62
- return (_b = (_a = transport.LogBlockCommand === null || transport.LogBlockCommand === void 0 ? void 0 : transport.LogBlockCommand.has) === null || _a === void 0 ? void 0 : _a.call(transport.LogBlockCommand, name)) !== null && _b !== void 0 ? _b : false;
63
- }
64
- function shouldBlockWebUsbCallDataLog(name) {
65
- const normalized = name.replace(/[_\s-]/g, '');
66
- return isLogBlockCommand$1(name) || WEBUSB_FILE_WRITE_LOG_BLOCK_PATTERN.test(normalized);
67
- }
63
+ const EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS = 2;
68
64
  function inferProtocolHintFromDeviceName$1(name) {
69
65
  return /\bpro\s*2\b/i.test(name !== null && name !== void 0 ? name : '') ? 'V2' : undefined;
70
66
  }
71
- class WebUsbTransport {
67
+ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
72
68
  constructor() {
69
+ super({
70
+ router: transport.PROTOCOL_V2_CHANNEL_USB,
71
+ maxFrameBytes: transport.PROTOCOL_V2_FRAME_MAX_BYTES,
72
+ logPrefix: 'ProtocolV2 WebUSB',
73
+ });
73
74
  this.deviceProtocol = new Map();
74
75
  this.deviceProtocolHints = new Map();
75
- this.protocolV2Assemblers = new Map();
76
- this.protocolV2Sessions = new Map();
77
- this.protocolV2ReadTimeouts = new Map();
78
76
  this.deviceEndpoints = new Map();
79
77
  this.mockSerialPaths = new WeakMap();
80
78
  this.mockSerialCounter = 0;
@@ -101,10 +99,17 @@ class WebUsbTransport {
101
99
  }
102
100
  configureProtocolV2(signedData) {
103
101
  var _a;
102
+ const schemaSource = typeof signedData === 'string'
103
+ ? signedData
104
+ : (_a = JSON.stringify(signedData)) !== null && _a !== void 0 ? _a : String(signedData);
105
+ if (schemaSource === this.protocolV2SchemaSource)
106
+ return;
107
+ const hadProtocolV2Schema = this.protocolV2SchemaSource !== undefined;
104
108
  this.messagesV2 = parseConfigure$1(signedData);
105
- this.protocolV2Sessions.clear();
106
- this.protocolV2ReadTimeouts.clear();
107
- (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[WebUsbTransport] Protocol V2 schema configured');
109
+ this.protocolV2SchemaSource = schemaSource;
110
+ if (!hadProtocolV2Schema)
111
+ return;
112
+ this.invalidateAllProtocolV2UsbLinks('Protocol V2 schema reconfigured').catch(error => { var _a; return (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[WebUsbTransport] schema link cleanup failed:', error); });
108
113
  }
109
114
  promptDeviceAccess() {
110
115
  return __awaiter(this, void 0, void 0, function* () {
@@ -145,7 +150,10 @@ class WebUsbTransport {
145
150
  if (!this.usb)
146
151
  return [];
147
152
  const devices = yield this.usb.getDevices();
148
- const onekeyDevices = devices.filter(dev => hdShared.ONEKEY_WEBUSB_FILTER.some(desc => dev.vendorId === desc.vendorId && dev.productId === desc.productId));
153
+ const onekeyDevices = devices.filter(dev => {
154
+ const isOneKey = hdShared.ONEKEY_WEBUSB_FILTER.some((desc) => dev.vendorId === desc.vendorId && dev.productId === desc.productId);
155
+ return isOneKey && !hdShared.isKnownTrezorWebUsbDevice(dev);
156
+ });
149
157
  this.deviceList = onekeyDevices.map(device => {
150
158
  const path = this.getDevicePath(device);
151
159
  const protocolHint = inferProtocolHintFromDeviceName$1(device.productName);
@@ -158,10 +166,6 @@ class WebUsbTransport {
158
166
  commType: 'webusb',
159
167
  };
160
168
  });
161
- for (const dev of onekeyDevices) {
162
- this.Log.debug(`[WebUSB] Device: name="${dev.productName}" serial="${dev.serialNumber}" ` +
163
- `VID=0x${dev.vendorId.toString(16)} PID=0x${dev.productId.toString(16)}`);
164
- }
165
169
  return this.deviceList;
166
170
  });
167
171
  }
@@ -171,6 +175,7 @@ class WebUsbTransport {
171
175
  if (!input.path)
172
176
  return;
173
177
  try {
178
+ yield this.rotateProtocolV2UsbGeneration(input.path, 'WebUSB transport acquired');
174
179
  yield this.closeOpenDevice(input.path);
175
180
  yield this.connect((_a = input.path) !== null && _a !== void 0 ? _a : '', true);
176
181
  const deviceName = (_b = this.deviceList.find(device => device.path === input.path)) === null || _b === void 0 ? void 0 : _b.device.productName;
@@ -192,32 +197,45 @@ class WebUsbTransport {
192
197
  createProtocolMismatchError(expected) {
193
198
  return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
194
199
  }
200
+ createProtocolProbeTimeoutError(expected, attempts) {
201
+ return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol ${expected} probe timeout after ${attempts} attempts`);
202
+ }
195
203
  createProtocolDetectionError() {
196
204
  return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Unable to detect USB protocol: device did not respond to Protocol V1 Initialize or Protocol V2 Ping');
197
205
  }
198
206
  detectProtocol(path, expectedProtocol, protocolHint) {
207
+ var _a;
199
208
  return __awaiter(this, void 0, void 0, function* () {
200
209
  if (expectedProtocol === 'V1') {
201
210
  if (yield this.probeProtocolV1(path)) {
202
211
  this.deviceProtocol.set(path, 'V1');
203
- this.Log.debug(`[WebUsbTransport] detectProtocol: path=${path} -> V1 (expected)`);
204
212
  return 'V1';
205
213
  }
214
+ yield this.resetConnectionAfterProbe(path);
206
215
  throw this.createProtocolMismatchError(expectedProtocol);
207
216
  }
208
217
  if (expectedProtocol === 'V2') {
209
- this.deviceProtocol.set(path, 'V2');
210
- this.Log.debug(`[WebUsbTransport] detectProtocol: path=${path} -> V2 (expected)`);
211
- return 'V2';
218
+ for (let attempt = 1; attempt <= EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS; attempt += 1) {
219
+ if (yield this.probeProtocolV2(path)) {
220
+ this.deviceProtocol.set(path, 'V2');
221
+ return 'V2';
222
+ }
223
+ yield this.resetConnectionAfterProbe(path);
224
+ if (attempt < EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS) {
225
+ (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[WebUsbTransport] Protocol V2 probe timed out, retrying ${attempt + 1}/${EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS}`);
226
+ }
227
+ }
228
+ this.deviceProtocol.delete(path);
229
+ throw this.createProtocolProbeTimeoutError(expectedProtocol, EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS);
212
230
  }
213
231
  const probeOrder = protocolHint === 'V2' || this.deviceProtocol.get(path) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
214
232
  for (const protocol of probeOrder) {
215
233
  const detected = protocol === 'V1' ? yield this.probeProtocolV1(path) : yield this.probeProtocolV2(path);
216
234
  if (detected) {
217
235
  this.deviceProtocol.set(path, protocol);
218
- this.Log.debug(`[WebUsbTransport] detectProtocol: path=${path} -> ${protocol}`);
219
236
  return protocol;
220
237
  }
238
+ yield this.resetConnectionAfterProbe(path);
221
239
  }
222
240
  this.deviceProtocol.delete(path);
223
241
  throw this.createProtocolDetectionError();
@@ -284,10 +302,9 @@ class WebUsbTransport {
284
302
  };
285
303
  }
286
304
  connectToDevice(path, first) {
287
- var _a, _b;
305
+ var _a;
288
306
  return __awaiter(this, void 0, void 0, function* () {
289
307
  let device = yield this.findDevice(path);
290
- this.Log.debug('[WebUsbTransport] connecting to device:', device.productName, 'PID:', device.productId);
291
308
  if (!device.opened) {
292
309
  yield device.open();
293
310
  }
@@ -309,33 +326,30 @@ class WebUsbTransport {
309
326
  }
310
327
  const endpoints = this.discoverEndpoints(device);
311
328
  this.deviceEndpoints.set(path, endpoints);
312
- (_b = this.protocolV2Assemblers.get(path)) === null || _b === void 0 ? void 0 : _b.reset();
313
- this.protocolV2Assemblers.set(path, new transport.ProtocolV2FrameAssembler(transport.PROTOCOL_V2_FRAME_MAX_BYTES));
314
329
  yield device.claimInterface(endpoints.interfaceNumber);
315
330
  yield this.clearEndpointHalt(device, 'in', endpoints.endpointIn);
316
331
  yield this.clearEndpointHalt(device, 'out', endpoints.endpointOut);
317
332
  });
318
333
  }
319
334
  closeOpenDevice(path) {
320
- var _a, _b, _c, _d, _e;
335
+ var _a, _b, _c, _d;
321
336
  return __awaiter(this, void 0, void 0, function* () {
322
- (_a = this.protocolV2Assemblers.get(path)) === null || _a === void 0 ? void 0 : _a.reset();
323
- const current = (_b = this.deviceList.find(device => device.path === path)) === null || _b === void 0 ? void 0 : _b.device;
337
+ const current = (_a = this.deviceList.find(device => device.path === path)) === null || _a === void 0 ? void 0 : _a.device;
324
338
  if (!(current === null || current === void 0 ? void 0 : current.opened))
325
339
  return;
326
340
  const endpoints = this.deviceEndpoints.get(path);
327
- const ifaceNum = (_c = endpoints === null || endpoints === void 0 ? void 0 : endpoints.interfaceNumber) !== null && _c !== void 0 ? _c : this.interfaceId;
341
+ const ifaceNum = (_b = endpoints === null || endpoints === void 0 ? void 0 : endpoints.interfaceNumber) !== null && _b !== void 0 ? _b : this.interfaceId;
328
342
  try {
329
343
  yield current.releaseInterface(ifaceNum);
330
344
  }
331
345
  catch (error) {
332
- (_d = this.Log) === null || _d === void 0 ? void 0 : _d.debug('[WebUsbTransport] releaseInterface before reconnect failed:', error);
346
+ (_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug('[WebUsbTransport] releaseInterface before reconnect failed:', error);
333
347
  }
334
348
  try {
335
349
  yield current.close();
336
350
  }
337
351
  catch (error) {
338
- (_e = this.Log) === null || _e === void 0 ? void 0 : _e.debug('[WebUsbTransport] close before reconnect failed:', error);
352
+ (_d = this.Log) === null || _d === void 0 ? void 0 : _d.debug('[WebUsbTransport] close before reconnect failed:', error);
339
353
  }
340
354
  });
341
355
  }
@@ -420,19 +434,11 @@ class WebUsbTransport {
420
434
  return copied.buffer;
421
435
  }
422
436
  transferOutWithRetry(path, packet) {
423
- var _a;
424
437
  return __awaiter(this, void 0, void 0, function* () {
425
438
  let lastError;
426
439
  for (let attempt = 1; attempt <= PACKET_IO_MAX_RETRIES; attempt += 1) {
427
440
  try {
428
- const device = yield this.findDevice(path);
429
- if (!device.opened) {
430
- yield this.connect(path, false);
431
- }
432
- const endpoints = this.deviceEndpoints.get(path);
433
- const endpointOut = (_a = endpoints === null || endpoints === void 0 ? void 0 : endpoints.endpointOut) !== null && _a !== void 0 ? _a : this.endpointId;
434
- const transferBuffer = this.toArrayBuffer(packet.buffer.slice(packet.byteOffset, packet.byteOffset + packet.byteLength));
435
- yield device.transferOut(endpointOut, transferBuffer);
441
+ yield this.transferOutOnce(path, packet);
436
442
  return;
437
443
  }
438
444
  catch (error) {
@@ -453,6 +459,19 @@ class WebUsbTransport {
453
459
  throw lastError;
454
460
  });
455
461
  }
462
+ transferOutOnce(path, packet) {
463
+ var _a;
464
+ return __awaiter(this, void 0, void 0, function* () {
465
+ const device = yield this.findDevice(path);
466
+ if (!device.opened) {
467
+ yield this.connect(path, false);
468
+ }
469
+ const endpoints = this.deviceEndpoints.get(path);
470
+ const endpointOut = (_a = endpoints === null || endpoints === void 0 ? void 0 : endpoints.endpointOut) !== null && _a !== void 0 ? _a : this.endpointId;
471
+ const transferBuffer = this.toArrayBuffer(packet.buffer.slice(packet.byteOffset, packet.byteOffset + packet.byteLength));
472
+ yield device.transferOut(endpointOut, transferBuffer);
473
+ });
474
+ }
456
475
  transferInWithRetry(path, length, cancelToken) {
457
476
  var _a;
458
477
  return __awaiter(this, void 0, void 0, function* () {
@@ -492,17 +511,28 @@ class WebUsbTransport {
492
511
  throw lastError;
493
512
  });
494
513
  }
514
+ transferInOnce(path, length) {
515
+ var _a;
516
+ return __awaiter(this, void 0, void 0, function* () {
517
+ const device = yield this.findDevice(path);
518
+ if (!device.opened) {
519
+ throw new Error('USBDevice is not open for transferIn');
520
+ }
521
+ const endpoints = this.deviceEndpoints.get(path);
522
+ const endpointIn = (_a = endpoints === null || endpoints === void 0 ? void 0 : endpoints.endpointIn) !== null && _a !== void 0 ? _a : this.endpointId;
523
+ const result = yield device.transferIn(endpointIn, length);
524
+ return this.getTransferInData(result);
525
+ });
526
+ }
495
527
  resetConnectionAfterProbe(path) {
496
- var _a, _b;
528
+ var _a;
497
529
  return __awaiter(this, void 0, void 0, function* () {
498
- (_a = this.protocolV2Assemblers.get(path)) === null || _a === void 0 ? void 0 : _a.reset();
499
- this.protocolV2Sessions.delete(path);
500
- this.protocolV2ReadTimeouts.delete(path);
530
+ yield this.rotateProtocolV2UsbGeneration(path, 'WebUSB protocol probe reset');
501
531
  try {
502
532
  const device = yield this.findDevice(path);
503
533
  if (device.opened) {
504
534
  const endpoints = this.deviceEndpoints.get(path);
505
- const ifaceNum = (_b = endpoints === null || endpoints === void 0 ? void 0 : endpoints.interfaceNumber) !== null && _b !== void 0 ? _b : this.interfaceId;
535
+ const ifaceNum = (_a = endpoints === null || endpoints === void 0 ? void 0 : endpoints.interfaceNumber) !== null && _a !== void 0 ? _a : this.interfaceId;
506
536
  try {
507
537
  yield device.releaseInterface(ifaceNum);
508
538
  }
@@ -537,6 +567,14 @@ class WebUsbTransport {
537
567
  timer = setTimeout(() => __awaiter(this, void 0, void 0, function* () {
538
568
  timedOut = true;
539
569
  onTimeout === null || onTimeout === void 0 ? void 0 : onTimeout();
570
+ if (protocol === 'V1') {
571
+ try {
572
+ yield this.resetConnectionAfterProbe(path);
573
+ }
574
+ catch (error) {
575
+ this.Log.debug('[WebUsbTransport] reset after Protocol V1 timeout failed:', error);
576
+ }
577
+ }
540
578
  reject(new Error(`Protocol ${protocol} read timeout after ${timeoutMs}ms`));
541
579
  }), timeoutMs);
542
580
  }),
@@ -557,8 +595,7 @@ class WebUsbTransport {
557
595
  yield this.callProtocolV1(path, 'Initialize', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT });
558
596
  return true;
559
597
  }
560
- catch (error) {
561
- this.Log.debug('[WebUsbTransport] Protocol V1 Initialize probe failed:', error);
598
+ catch (_error) {
562
599
  return false;
563
600
  }
564
601
  });
@@ -573,7 +610,6 @@ class WebUsbTransport {
573
610
  timeoutMs: PROTOCOL_PROBE_TIMEOUT,
574
611
  logger: this.Log,
575
612
  logPrefix: 'ProtocolV2 WebUSB',
576
- onProbeFailed: () => this.resetConnectionAfterProbe(path),
577
613
  });
578
614
  });
579
615
  }
@@ -590,12 +626,8 @@ class WebUsbTransport {
590
626
  if (!protocol) {
591
627
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol has not been detected for ${path}`);
592
628
  }
593
- if (shouldSuppressWebUsbCallLog(name)) ;
594
- else if (shouldBlockWebUsbCallDataLog(name)) {
595
- this.Log.debug('call-', ' name: ', name, ' protocol: ', protocol);
596
- }
597
- else {
598
- this.Log.debug('call-', ' name: ', name, ' data: ', data, ' protocol: ', protocol);
629
+ if (!shouldSuppressHighVolumeCallLog(name)) {
630
+ this.Log.debug('transport call', createTransportCallLog(name, protocol));
599
631
  }
600
632
  if (protocol === 'V2') {
601
633
  return this.callProtocolV2(path, name, data, options);
@@ -625,67 +657,8 @@ class WebUsbTransport {
625
657
  });
626
658
  }
627
659
  callProtocolV2(path, name, data, options) {
628
- var _a;
629
660
  return __awaiter(this, void 0, void 0, function* () {
630
- const protocolV1Messages = this.messages;
631
- if (!this.messagesV2) {
632
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured, 'Protocol V2 schema not configured');
633
- }
634
- if (!protocolV1Messages) {
635
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
636
- }
637
- let session = this.protocolV2Sessions.get(path);
638
- if (!session) {
639
- session = new transport.ProtocolV2Session({
640
- schemas: {
641
- protocolV1: protocolV1Messages,
642
- protocolV2: this.messagesV2,
643
- },
644
- router: transport.PROTOCOL_V2_CHANNEL_USB,
645
- writeFrame: (frame) => this.transferOutWithRetry(path, frame),
646
- readFrame: () => this.receiveProtocolV2Frame(path, this.protocolV2ReadTimeouts.get(path)),
647
- logger: this.Log,
648
- logPrefix: 'ProtocolV2 WebUSB',
649
- createTimeoutError: (messageName, timeoutMs) => new Error(`Protocol V2 response timeout after ${timeoutMs}ms for ${messageName}`),
650
- });
651
- this.protocolV2Sessions.set(path, session);
652
- }
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
- try {
656
- return yield session.call(name, data, options);
657
- }
658
- finally {
659
- this.protocolV2ReadTimeouts.delete(path);
660
- }
661
- });
662
- }
663
- receiveProtocolV2Frame(path, timeoutMs) {
664
- return __awaiter(this, void 0, void 0, function* () {
665
- let assembler = this.protocolV2Assemblers.get(path);
666
- if (!assembler) {
667
- assembler = new transport.ProtocolV2FrameAssembler(transport.PROTOCOL_V2_FRAME_MAX_BYTES);
668
- this.protocolV2Assemblers.set(path, assembler);
669
- }
670
- let frame = assembler.push(new Uint8Array(0));
671
- const deadline = timeoutMs ? Date.now() + timeoutMs : undefined;
672
- while (!frame) {
673
- const cancelToken = { cancelled: false };
674
- const transferIn = this.transferInWithRetry(path, transport.PROTOCOL_V2_FRAME_MAX_BYTES, cancelToken);
675
- const dataView = deadline
676
- ? yield this.withProtocolReadTimeout(path, transferIn, Math.max(deadline - Date.now(), 1), 'V2', () => {
677
- cancelToken.cancelled = true;
678
- })
679
- : yield transferIn;
680
- const bytes = new Uint8Array(this.toArrayBuffer(dataView.buffer.slice(dataView.byteOffset, dataView.byteOffset + dataView.byteLength)));
681
- try {
682
- frame = assembler.push(bytes);
683
- }
684
- catch (error) {
685
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.NetworkError, error instanceof Error ? error.message : String(error));
686
- }
687
- }
688
- return frame;
661
+ return this.callProtocolV2Usb(path, name, data, options);
689
662
  });
690
663
  }
691
664
  receiveData(path, timeoutMs) {
@@ -726,34 +699,55 @@ class WebUsbTransport {
726
699
  });
727
700
  }
728
701
  release(path) {
729
- var _a, _b;
730
702
  return __awaiter(this, void 0, void 0, function* () {
731
- const device = yield this.findDevice(path);
732
- const endpoints = this.deviceEndpoints.get(path);
733
- const ifaceNum = (_a = endpoints === null || endpoints === void 0 ? void 0 : endpoints.interfaceNumber) !== null && _a !== void 0 ? _a : this.interfaceId;
734
- yield device.releaseInterface(ifaceNum);
735
- yield device.close();
703
+ yield this.invalidateProtocolV2UsbLink(path, 'WebUSB transport released');
704
+ yield this.closeOpenDevice(path);
736
705
  this.deviceProtocol.delete(path);
737
706
  this.deviceProtocolHints.delete(path);
738
- (_b = this.protocolV2Assemblers.get(path)) === null || _b === void 0 ? void 0 : _b.reset();
739
- this.protocolV2Assemblers.delete(path);
740
707
  this.deviceEndpoints.delete(path);
741
708
  });
742
709
  }
710
+ getProtocolV2UsbSchemas() {
711
+ if (!this.messages || !this.messagesV2) {
712
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
713
+ }
714
+ return {
715
+ protocolV1: this.messages,
716
+ protocolV2: this.messagesV2,
717
+ };
718
+ }
719
+ getProtocolV2UsbLogger() {
720
+ return this.Log;
721
+ }
722
+ writeProtocolV2UsbPacket(path, frame, _context) {
723
+ return __awaiter(this, void 0, void 0, function* () {
724
+ yield this.transferOutOnce(path, frame);
725
+ });
726
+ }
727
+ readProtocolV2UsbPacket(path, _context) {
728
+ return __awaiter(this, void 0, void 0, function* () {
729
+ const dataView = yield this.transferInOnce(path, transport.PROTOCOL_V2_FRAME_MAX_BYTES);
730
+ return new Uint8Array(this.toArrayBuffer(dataView.buffer.slice(dataView.byteOffset, dataView.byteOffset + dataView.byteLength)));
731
+ });
732
+ }
733
+ resetProtocolV2UsbNativeLink(path, _reason) {
734
+ return __awaiter(this, void 0, void 0, function* () {
735
+ yield this.closeOpenDevice(path);
736
+ });
737
+ }
738
+ onProtocolV2UsbLinkInvalidated(path, reason) {
739
+ var _a;
740
+ this.deviceProtocol.delete(path);
741
+ (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[WebUsbTransport] Protocol V2 link invalidated: ${path}`, reason);
742
+ }
743
+ createProtocolV2UsbTimeoutError(name, timeoutMs) {
744
+ return new transport.ProtocolV2LinkError('response-timeout', `Protocol V2 response timeout after ${timeoutMs}ms for ${name}`);
745
+ }
743
746
  getProtocolType(path) {
744
747
  return this.deviceProtocol.get(path);
745
748
  }
746
749
  }
747
750
 
748
- const FILE_WRITE_LOG_BLOCK_PATTERN = /(?:^|[^a-z])(?:raw)?(?:filesystem|emmc)?filewrite$/i;
749
- function shouldSuppressHighVolumeCallLog(name) {
750
- const normalized = name.replace(/[_\s-]/g, '');
751
- return FILE_WRITE_LOG_BLOCK_PATTERN.test(normalized);
752
- }
753
- function isLogBlockCommand(name) {
754
- var _a, _b;
755
- return (_b = (_a = transport.LogBlockCommand === null || transport.LogBlockCommand === void 0 ? void 0 : transport.LogBlockCommand.has) === null || _a === void 0 ? void 0 : _a.call(transport.LogBlockCommand, name)) !== null && _b !== void 0 ? _b : false;
756
- }
757
751
  const { parseConfigure, ProtocolV1, check } = transport__default["default"];
758
752
  function inferProtocolHintFromDeviceName(name) {
759
753
  return /\bpro\s*2\b/i.test(name !== null && name !== void 0 ? name : '') ? 'V2' : undefined;
@@ -761,9 +755,6 @@ function inferProtocolHintFromDeviceName(name) {
761
755
  const toBleDescriptor = (device, protocolType) => (Object.assign({ id: device.id, name: device.name, path: device.id, debug: false, commType: 'electron-ble' }, (protocolType ? { protocolType } : {})));
762
756
  const BLE_PACKET_SIZE = 192;
763
757
  const BLE_WRITE_DELAY_MS = 5;
764
- const BLE_WRITE_MAX_RETRIES = 3;
765
- const BLE_WRITE_RETRY_DELAY_MS = 300;
766
- const BLE_RESPONSE_TIMEOUT_MS = 30000;
767
758
  const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
768
759
  const PROTOCOL_V2_PROBE_TIMEOUT_MS = 5000;
769
760
  class ElectronBleTransport {
@@ -778,8 +769,27 @@ class ElectronBleTransport {
778
769
  this.v2Assemblers = new Map();
779
770
  this.v2FrameQueues = new Map();
780
771
  this.v2FramePromises = new Map();
781
- this.activeProtocolV2Call = null;
782
- this.nextProtocolV2CallToken = 1;
772
+ this.protocolV2Links = new transport.ProtocolV2LinkManager({
773
+ getSchemas: () => {
774
+ if (!this._messages || !this._messagesV2) {
775
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
776
+ }
777
+ return {
778
+ protocolV1: this._messages,
779
+ protocolV2: this._messagesV2,
780
+ };
781
+ },
782
+ classifyError: () => 'link-fatal',
783
+ onLinkInvalidated: (uuid, reason) => __awaiter(this, void 0, void 0, function* () {
784
+ var _a, _b;
785
+ (_a = this.v2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
786
+ this.rejectProtocolV2Frames(uuid, new Error(reason));
787
+ (_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug('[Electron BLE] Protocol V2 link invalidated:', uuid, reason);
788
+ if (reason.startsWith('Protocol V2 link-fatal error:')) {
789
+ yield this.release(uuid);
790
+ }
791
+ }),
792
+ });
783
793
  this.notificationCleanups = new Map();
784
794
  this.disconnectCleanups = new Map();
785
795
  this.notificationTokens = new Map();
@@ -815,15 +825,14 @@ class ElectronBleTransport {
815
825
  throw error;
816
826
  }
817
827
  cleanupDeviceState(deviceId) {
818
- var _a;
828
+ this.protocolV2Links
829
+ .invalidateLink(deviceId, 'Electron BLE device state cleaned')
830
+ .catch(error => { var _a; return (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] link cleanup failed:', error); });
819
831
  this.connectedDevices.delete(deviceId);
820
832
  this.deviceProtocol.delete(deviceId);
821
833
  this.v1Buffers.delete(deviceId);
822
834
  this.v2Assemblers.delete(deviceId);
823
835
  this.resetProtocolV2Frames(deviceId);
824
- if (((_a = this.activeProtocolV2Call) === null || _a === void 0 ? void 0 : _a.uuid) === deviceId) {
825
- this.activeProtocolV2Call = null;
826
- }
827
836
  this.notificationTokens.delete(deviceId);
828
837
  const notifyCleanup = this.notificationCleanups.get(deviceId);
829
838
  if (notifyCleanup) {
@@ -850,9 +859,18 @@ class ElectronBleTransport {
850
859
  this.configured = true;
851
860
  }
852
861
  configureProtocolV2(signedData) {
853
- var _a;
862
+ const configuration = typeof signedData === 'string' ? signedData : JSON.stringify(signedData);
863
+ if (this.protocolV2SchemaConfiguration === configuration) {
864
+ return;
865
+ }
866
+ const isReconfiguration = this.protocolV2SchemaConfiguration !== undefined;
854
867
  this._messagesV2 = parseConfigure(signedData);
855
- (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] Protocol V2 schema configured');
868
+ this.protocolV2SchemaConfiguration = configuration;
869
+ if (isReconfiguration) {
870
+ this.protocolV2Links
871
+ .invalidateAllLinks('Protocol V2 schema reconfigured')
872
+ .catch(error => { var _a; return (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] schema link cleanup failed:', error); });
873
+ }
856
874
  }
857
875
  listen() {
858
876
  return __awaiter(this, void 0, void 0, function* () {
@@ -884,18 +902,20 @@ class ElectronBleTransport {
884
902
  });
885
903
  }
886
904
  acquire(input) {
887
- var _a, _b, _c, _d, _e, _f;
905
+ var _a, _b, _c, _d, _e;
888
906
  return __awaiter(this, void 0, void 0, function* () {
889
907
  const { uuid, forceCleanRunPromise, expectedProtocol } = input;
890
908
  if (!uuid) {
891
909
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleRequiredUUID);
892
910
  }
911
+ if (this.connectedDevices.has(uuid)) {
912
+ yield this.release(uuid);
913
+ }
893
914
  if (forceCleanRunPromise && this.runPromise) {
894
915
  const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
895
916
  this.runPromise.reject(error);
896
917
  this.rejectAllProtocolV2Frames(error);
897
918
  this.runPromise = null;
898
- this.activeProtocolV2Call = null;
899
919
  }
900
920
  try {
901
921
  if (!((_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle)) {
@@ -919,7 +939,7 @@ class ElectronBleTransport {
919
939
  this.handleBluetoothError(error);
920
940
  }
921
941
  this.v1Buffers.set(uuid, { buffer: [], bufferLength: 0 });
922
- this.v2Assemblers.set(uuid, new transport.ProtocolV2FrameAssembler());
942
+ this.v2Assemblers.set(uuid, new transport.ProtocolV2FrameAssembler(transport.PROTOCOL_V2_BLE_FRAME_MAX_BYTES));
923
943
  yield window.desktopApi.nobleBle.subscribe(uuid);
924
944
  const cleanup = this.createNotificationSubscription(uuid);
925
945
  this.notificationCleanups.set(uuid, cleanup);
@@ -927,7 +947,7 @@ class ElectronBleTransport {
927
947
  var _a;
928
948
  if (disconnectedDevice.id === uuid) {
929
949
  this.cleanupDeviceState(uuid);
930
- (_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit('device-disconnect', {
950
+ (_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit(transport.TRANSPORT_EVENT.DEVICE_DISCONNECT, {
931
951
  name: disconnectedDevice.name,
932
952
  id: disconnectedDevice.id,
933
953
  connectId: disconnectedDevice.id,
@@ -936,23 +956,18 @@ class ElectronBleTransport {
936
956
  });
937
957
  this.disconnectCleanups.set(uuid, disconnectCleanup);
938
958
  const protocolType = yield this.detectProtocol(uuid, expectedProtocol, protocolHint);
939
- (_c = this.emitter) === null || _c === void 0 ? void 0 : _c.emit('device-connect', {
940
- name: device.name,
941
- id: device.id,
942
- connectId: device.id,
943
- });
944
959
  return Object.assign(Object.assign({}, toBleDescriptor({ id: device.id, name: device.name }, protocolType)), { uuid });
945
960
  }
946
961
  catch (error) {
947
- (_d = this.Log) === null || _d === void 0 ? void 0 : _d.error('[Electron BLE] acquire failed:', error);
962
+ (_c = this.Log) === null || _c === void 0 ? void 0 : _c.error('[Electron BLE] acquire failed:', error);
948
963
  try {
949
- if (((_e = window.desktopApi) === null || _e === void 0 ? void 0 : _e.nobleBle) && this.connectedDevices.has(uuid)) {
964
+ if (((_d = window.desktopApi) === null || _d === void 0 ? void 0 : _d.nobleBle) && this.connectedDevices.has(uuid)) {
950
965
  yield window.desktopApi.nobleBle.unsubscribe(uuid);
951
966
  yield window.desktopApi.nobleBle.disconnect(uuid);
952
967
  }
953
968
  }
954
969
  catch (cleanupError) {
955
- (_f = this.Log) === null || _f === void 0 ? void 0 : _f.debug('[Electron BLE] acquire cleanup failed:', cleanupError);
970
+ (_e = this.Log) === null || _e === void 0 ? void 0 : _e.debug('[Electron BLE] acquire cleanup failed:', cleanupError);
956
971
  }
957
972
  this.cleanupDeviceState(uuid);
958
973
  throw error;
@@ -963,6 +978,7 @@ class ElectronBleTransport {
963
978
  var _a, _b;
964
979
  return __awaiter(this, void 0, void 0, function* () {
965
980
  try {
981
+ yield this.protocolV2Links.invalidateLink(id, 'Electron BLE transport released');
966
982
  if (this.connectedDevices.has(id)) {
967
983
  if ((_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle) {
968
984
  yield window.desktopApi.nobleBle.unsubscribe(id);
@@ -1000,9 +1016,12 @@ class ElectronBleTransport {
1000
1016
  throw this.createProtocolMismatchError(expectedProtocol);
1001
1017
  }
1002
1018
  if (expectedProtocol === 'V2') {
1003
- this.deviceProtocol.set(uuid, 'V2');
1004
- (_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug(`[Electron BLE] detectProtocol: uuid=${uuid} -> V2 (expected)`);
1005
- return 'V2';
1019
+ if (yield this.probeProtocolV2(uuid)) {
1020
+ this.deviceProtocol.set(uuid, 'V2');
1021
+ (_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug(`[Electron BLE] detectProtocol: uuid=${uuid} -> V2 (expected)`);
1022
+ return 'V2';
1023
+ }
1024
+ throw this.createProtocolMismatchError(expectedProtocol);
1006
1025
  }
1007
1026
  const probeOrder = protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
1008
1027
  for (let i = 0; i < probeOrder.length; i += 1) {
@@ -1036,14 +1055,12 @@ class ElectronBleTransport {
1036
1055
  });
1037
1056
  }
1038
1057
  resetProbeStateAfterProtocolProbe(uuid, protocol) {
1039
- var _a, _b, _c, _d, _e, _f, _g, _h;
1058
+ var _a, _b, _c, _d, _e, _f, _g;
1040
1059
  return __awaiter(this, void 0, void 0, function* () {
1060
+ yield this.protocolV2Links.invalidateLink(uuid, `Reset notify state after Protocol ${protocol} probe`);
1041
1061
  this.v1Buffers.set(uuid, { buffer: [], bufferLength: 0 });
1042
1062
  (_a = this.v2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1043
1063
  this.resetProtocolV2Frames(uuid);
1044
- if (((_b = this.activeProtocolV2Call) === null || _b === void 0 ? void 0 : _b.uuid) === uuid) {
1045
- this.activeProtocolV2Call = null;
1046
- }
1047
1064
  if (this.runPromise) {
1048
1065
  const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
1049
1066
  this.runPromise.reject(error);
@@ -1056,16 +1073,16 @@ class ElectronBleTransport {
1056
1073
  }
1057
1074
  this.notificationTokens.delete(uuid);
1058
1075
  try {
1059
- yield ((_d = (_c = window.desktopApi) === null || _c === void 0 ? void 0 : _c.nobleBle) === null || _d === void 0 ? void 0 : _d.unsubscribe(uuid));
1076
+ yield ((_c = (_b = window.desktopApi) === null || _b === void 0 ? void 0 : _b.nobleBle) === null || _c === void 0 ? void 0 : _c.unsubscribe(uuid));
1060
1077
  }
1061
1078
  catch (error) {
1062
- (_e = this.Log) === null || _e === void 0 ? void 0 : _e.debug(`[Electron BLE] unsubscribe after Protocol ${protocol} probe failed:`, error);
1079
+ (_d = this.Log) === null || _d === void 0 ? void 0 : _d.debug(`[Electron BLE] unsubscribe after Protocol ${protocol} probe failed:`, error);
1063
1080
  }
1064
1081
  try {
1065
- yield ((_g = (_f = window.desktopApi) === null || _f === void 0 ? void 0 : _f.nobleBle) === null || _g === void 0 ? void 0 : _g.subscribe(uuid));
1082
+ yield ((_f = (_e = window.desktopApi) === null || _e === void 0 ? void 0 : _e.nobleBle) === null || _f === void 0 ? void 0 : _f.subscribe(uuid));
1066
1083
  }
1067
1084
  catch (error) {
1068
- (_h = this.Log) === null || _h === void 0 ? void 0 : _h.debug(`[Electron BLE] resubscribe after Protocol ${protocol} probe failed:`, error);
1085
+ (_g = this.Log) === null || _g === void 0 ? void 0 : _g.debug(`[Electron BLE] resubscribe after Protocol ${protocol} probe failed:`, error);
1069
1086
  throw error;
1070
1087
  }
1071
1088
  const cleanup = this.createNotificationSubscription(uuid);
@@ -1115,57 +1132,39 @@ class ElectronBleTransport {
1115
1132
  return detected;
1116
1133
  });
1117
1134
  }
1118
- writeWithChunking(uuid, hexData) {
1119
- return __awaiter(this, void 0, void 0, function* () {
1120
- const totalBytes = hexData.length / 2;
1121
- if (totalBytes <= BLE_PACKET_SIZE) {
1122
- yield hdShared.wait(BLE_WRITE_DELAY_MS);
1123
- yield this.writeWithRetry(uuid, hexData);
1124
- return;
1125
- }
1126
- for (let offset = 0; offset < hexData.length;) {
1127
- const chunkHexLen = Math.min(BLE_PACKET_SIZE * 2, hexData.length - offset);
1128
- const chunkHex = hexData.substring(offset, offset + chunkHexLen);
1129
- offset += chunkHexLen;
1130
- yield this.writeWithRetry(uuid, chunkHex);
1131
- if (offset < hexData.length) {
1132
- yield hdShared.wait(BLE_WRITE_DELAY_MS);
1133
- }
1134
- }
1135
- });
1136
- }
1137
- writeWithRetry(uuid, hexData) {
1138
- var _a, _b, _c;
1135
+ writeOnce(uuid, hexData) {
1136
+ var _a;
1139
1137
  return __awaiter(this, void 0, void 0, function* () {
1140
- let lastError;
1141
1138
  const nobleBle = (_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle;
1142
1139
  if (!nobleBle) {
1143
1140
  throw new Error('Noble BLE API not available');
1144
1141
  }
1145
- for (let attempt = 1; attempt <= BLE_WRITE_MAX_RETRIES; attempt++) {
1146
- try {
1147
- yield nobleBle.write(uuid, hexData);
1148
- return;
1149
- }
1150
- catch (error) {
1151
- lastError = error;
1152
- (_b = this.Log) === null || _b === void 0 ? void 0 : _b.error(`[Electron BLE] write failed (attempt ${attempt}/${BLE_WRITE_MAX_RETRIES}):`, error);
1153
- if (attempt < BLE_WRITE_MAX_RETRIES) {
1154
- yield hdShared.wait(BLE_WRITE_RETRY_DELAY_MS);
1155
- }
1156
- }
1157
- }
1158
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError, `BLE write failed after ${BLE_WRITE_MAX_RETRIES} attempts: ${(_c = lastError === null || lastError === void 0 ? void 0 : lastError.message) !== null && _c !== void 0 ? _c : lastError}`);
1142
+ yield nobleBle.write(uuid, hexData);
1143
+ });
1144
+ }
1145
+ writeProtocolV2Frame(uuid, frame, context, assertCurrentGeneration) {
1146
+ return transport.writeProtocolV2BleFrame({
1147
+ frame,
1148
+ packetCapacity: BLE_PACKET_SIZE,
1149
+ assertActive: assertCurrentGeneration,
1150
+ signal: context.signal,
1151
+ abortMessage: `Protocol V2 BLE write aborted for ${context.messageName}`,
1152
+ initialDelayMs: frame.length <= BLE_PACKET_SIZE ? BLE_WRITE_DELAY_MS : 0,
1153
+ burstSize: 1,
1154
+ burstPauseMs: BLE_WRITE_DELAY_MS,
1155
+ writePacket: packet => this.writeOnce(uuid, transport.bytesToHex(packet)),
1159
1156
  });
1160
1157
  }
1161
1158
  handleNotification(deviceId, hexData) {
1162
1159
  var _a, _b;
1163
1160
  if (hexData === 'PAIRING_REJECTED') {
1164
1161
  (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] Pairing rejection detected for device:', deviceId);
1165
- if (this.runPromise) {
1166
- const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceBondedCanceled);
1162
+ const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceBondedCanceled);
1163
+ if (this.deviceProtocol.get(deviceId) === 'V2') {
1164
+ this.rejectProtocolV2Frames(deviceId, error);
1165
+ }
1166
+ else if (this.runPromise) {
1167
1167
  this.runPromise.reject(error);
1168
- this.rejectAllProtocolV2Frames(error);
1169
1168
  }
1170
1169
  return;
1171
1170
  }
@@ -1181,13 +1180,8 @@ class ElectronBleTransport {
1181
1180
  this.handleProtocolV1Notification(deviceId, hexData);
1182
1181
  }
1183
1182
  handleProtocolV2Notification(deviceId, hexData) {
1184
- var _a, _b, _c;
1183
+ var _a;
1185
1184
  try {
1186
- if (!this.runPromise || ((_a = this.activeProtocolV2Call) === null || _a === void 0 ? void 0 : _a.uuid) !== deviceId) {
1187
- (_b = this.v2Assemblers.get(deviceId)) === null || _b === void 0 ? void 0 : _b.reset();
1188
- this.resetProtocolV2Frames(deviceId);
1189
- return;
1190
- }
1191
1185
  const bytes = transport.hexToBytes(hexData);
1192
1186
  if (bytes.length === 0)
1193
1187
  return;
@@ -1199,12 +1193,9 @@ class ElectronBleTransport {
1199
1193
  }
1200
1194
  }
1201
1195
  catch (error) {
1202
- (_c = this.Log) === null || _c === void 0 ? void 0 : _c.error('[Electron BLE] Protocol V2 notification error:', error);
1203
- if (this.runPromise) {
1204
- const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
1205
- this.runPromise.reject(notifyError);
1206
- this.rejectAllProtocolV2Frames(notifyError);
1207
- }
1196
+ (_a = this.Log) === null || _a === void 0 ? void 0 : _a.error('[Electron BLE] Protocol V2 notification error:', error);
1197
+ const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
1198
+ this.rejectProtocolV2Frames(deviceId, notifyError);
1208
1199
  }
1209
1200
  }
1210
1201
  getProtocolV2FrameQueue(uuid) {
@@ -1235,9 +1226,13 @@ class ElectronBleTransport {
1235
1226
  this.v2FrameQueues.delete(uuid);
1236
1227
  this.v2FramePromises.delete(uuid);
1237
1228
  }
1238
- isActiveProtocolV2Call(uuid, token) {
1239
- var _a;
1240
- return ((_a = this.activeProtocolV2Call) === null || _a === void 0 ? void 0 : _a.uuid) === uuid && this.activeProtocolV2Call.token === token;
1229
+ rejectProtocolV2Frames(uuid, error) {
1230
+ this.v2FrameQueues.delete(uuid);
1231
+ const framePromise = this.v2FramePromises.get(uuid);
1232
+ if (framePromise) {
1233
+ this.v2FramePromises.delete(uuid);
1234
+ framePromise.reject(error);
1235
+ }
1241
1236
  }
1242
1237
  readProtocolV2Frame(uuid) {
1243
1238
  return __awaiter(this, void 0, void 0, function* () {
@@ -1272,7 +1267,7 @@ class ElectronBleTransport {
1272
1267
  }
1273
1268
  }
1274
1269
  call(uuid, name, data, options) {
1275
- var _a, _b;
1270
+ var _a;
1276
1271
  return __awaiter(this, void 0, void 0, function* () {
1277
1272
  if (!this._messages) {
1278
1273
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
@@ -1284,12 +1279,8 @@ class ElectronBleTransport {
1284
1279
  if (!protocol) {
1285
1280
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol has not been detected for ${uuid}`);
1286
1281
  }
1287
- if (shouldSuppressHighVolumeCallLog(name)) ;
1288
- else if (isLogBlockCommand(name)) {
1289
- (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] call', 'name:', name, 'protocol:', protocol);
1290
- }
1291
- else {
1292
- (_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug('[Electron BLE] call', 'name:', name, 'data:', data, 'protocol:', protocol);
1282
+ if (!shouldSuppressHighVolumeCallLog(name)) {
1283
+ (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('transport call', createTransportCallLog(name, protocol));
1293
1284
  }
1294
1285
  if (protocol === 'V2') {
1295
1286
  return this.callProtocolV2(uuid, name, data, options);
@@ -1360,76 +1351,58 @@ class ElectronBleTransport {
1360
1351
  });
1361
1352
  }
1362
1353
  callProtocolV2(uuid, name, data, options) {
1363
- var _a, _b, _c, _d, _e;
1354
+ var _a;
1364
1355
  return __awaiter(this, void 0, void 0, function* () {
1365
1356
  if (!this._messages || !this._messagesV2) {
1366
1357
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
1367
1358
  }
1368
- const forceRun = name === 'Initialize' || name === 'Cancel' || name === 'Ping';
1369
- if (this.runPromise) {
1370
- if (!forceRun) {
1371
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportCallInProgress);
1372
- }
1373
- const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
1374
- this.runPromise.reject(error);
1375
- this.rejectAllProtocolV2Frames(error);
1376
- this.runPromise = null;
1377
- this.activeProtocolV2Call = null;
1378
- }
1379
- const runPromise = hdShared.createDeferred();
1380
- runPromise.promise.catch(() => undefined);
1381
- this.runPromise = runPromise;
1382
- const callToken = this.nextProtocolV2CallToken++;
1383
- this.activeProtocolV2Call = { uuid, token: callToken };
1384
- (_a = this.v2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1385
- this.resetProtocolV2Frames(uuid);
1386
- let completed = false;
1387
- const callOptions = Object.assign(Object.assign({}, options), { timeoutMs: (_b = options === null || options === void 0 ? void 0 : options.timeoutMs) !== null && _b !== void 0 ? _b : BLE_RESPONSE_TIMEOUT_MS });
1359
+ const callOptions = options;
1388
1360
  try {
1389
- const session = new transport.ProtocolV2Session({
1390
- schemas: {
1391
- protocolV1: this._messages,
1392
- protocolV2: this._messagesV2,
1393
- },
1394
- router: transport.PROTOCOL_V2_CHANNEL_BLE_UART,
1395
- writeFrame: (frame) => this.writeWithChunking(uuid, transport.bytesToHex(frame)),
1396
- readFrame: () => __awaiter(this, void 0, void 0, function* () {
1397
- const rxFrame = yield this.readProtocolV2Frame(uuid);
1398
- if (!(rxFrame instanceof Uint8Array)) {
1399
- throw new Error('Response is not Uint8Array');
1400
- }
1401
- return rxFrame;
1402
- }),
1403
- logger: this.Log,
1404
- logPrefix: 'ProtocolV2 BLE',
1405
- createTimeoutError: (_messageName, timeout) => hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, `BLE response timeout after ${timeout}ms for ${name}`),
1406
- });
1407
- const result = yield session.call(name, data, callOptions);
1408
- completed = true;
1409
- return result;
1361
+ return yield this.protocolV2Links.call(uuid, () => this.createProtocolV2Adapter(uuid), name, data, callOptions);
1410
1362
  }
1411
1363
  catch (e) {
1412
- if (this.isActiveProtocolV2Call(uuid, callToken)) {
1413
- (_c = this.v2Assemblers.get(uuid)) === null || _c === void 0 ? void 0 : _c.reset();
1414
- this.resetProtocolV2Frames(uuid);
1415
- }
1416
- (_d = this.Log) === null || _d === void 0 ? void 0 : _d.error('[Electron BLE] Protocol V2 call error:', e);
1364
+ (_a = this.Log) === null || _a === void 0 ? void 0 : _a.error('[Electron BLE] Protocol V2 call error:', e);
1417
1365
  throw e;
1418
1366
  }
1419
- finally {
1420
- if (this.isActiveProtocolV2Call(uuid, callToken)) {
1421
- if (!completed) {
1422
- (_e = this.v2Assemblers.get(uuid)) === null || _e === void 0 ? void 0 : _e.reset();
1423
- }
1424
- this.resetProtocolV2Frames(uuid);
1425
- this.activeProtocolV2Call = null;
1426
- }
1427
- if (this.runPromise === runPromise) {
1428
- this.runPromise = null;
1429
- }
1430
- }
1431
1367
  });
1432
1368
  }
1369
+ createProtocolV2Adapter(uuid) {
1370
+ var _a;
1371
+ const generation = (_a = this.notificationTokens.get(uuid)) !== null && _a !== void 0 ? _a : 0;
1372
+ const assertCurrentGeneration = () => {
1373
+ if (this.notificationTokens.get(uuid) !== generation) {
1374
+ throw new Error(`Protocol V2 notification generation changed for ${uuid}`);
1375
+ }
1376
+ };
1377
+ return {
1378
+ router: transport.PROTOCOL_V2_CHANNEL_BLE_UART,
1379
+ maxFrameBytes: transport.PROTOCOL_V2_BLE_FRAME_MAX_BYTES,
1380
+ generation,
1381
+ prepareCall: () => {
1382
+ var _a;
1383
+ assertCurrentGeneration();
1384
+ (_a = this.v2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1385
+ this.resetProtocolV2Frames(uuid);
1386
+ },
1387
+ writeFrame: (frame, context) => this.writeProtocolV2Frame(uuid, frame, context, assertCurrentGeneration),
1388
+ readFrame: () => __awaiter(this, void 0, void 0, function* () {
1389
+ assertCurrentGeneration();
1390
+ const rxFrame = yield this.readProtocolV2Frame(uuid);
1391
+ if (!(rxFrame instanceof Uint8Array)) {
1392
+ throw new Error('Response is not Uint8Array');
1393
+ }
1394
+ return rxFrame;
1395
+ }),
1396
+ reset: (reason) => {
1397
+ var _a;
1398
+ (_a = this.v2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1399
+ this.rejectProtocolV2Frames(uuid, new Error(reason));
1400
+ },
1401
+ logger: this.Log,
1402
+ logPrefix: 'ProtocolV2 BLE',
1403
+ createTimeoutError: (messageName, timeout) => hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, `BLE response timeout after ${timeout}ms for ${messageName}`),
1404
+ };
1405
+ }
1433
1406
  processProtocolV1Notification(deviceId, hexData) {
1434
1407
  try {
1435
1408
  if (typeof hexData !== 'string') {