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

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
@@ -54,8 +54,22 @@ const PACKET_IO_RETRY_DELAY = 300;
54
54
  const PROTOCOL_V1_PROBE_TIMEOUT = 5000;
55
55
  const PROTOCOL_V2_PROBE_TIMEOUT = 1000;
56
56
  const EXPECTED_PROTOCOL_V2_PROBE_ATTEMPTS = 2;
57
- function inferProtocolHintFromDeviceName$1(name) {
58
- return /\bpro\s*2\b/i.test(name !== null && name !== void 0 ? name : '') ? 'V2' : undefined;
57
+ let activeWebUsbTransport;
58
+ let usbDisconnectListenerAttached = false;
59
+ function registerActiveWebUsbTransport(instance, usb) {
60
+ activeWebUsbTransport = instance;
61
+ if (usbDisconnectListenerAttached)
62
+ return;
63
+ usbDisconnectListenerAttached = true;
64
+ usb.addEventListener('disconnect', event => {
65
+ if (!event.device)
66
+ return;
67
+ const path = hdShared.resolveOneKeyUsbDevicePath(event.device);
68
+ if (!path)
69
+ return;
70
+ activeWebUsbTransport === null || activeWebUsbTransport === void 0 ? void 0 : activeWebUsbTransport.markProtocolStale(path);
71
+ activeWebUsbTransport === null || activeWebUsbTransport === void 0 ? void 0 : activeWebUsbTransport.emitDeviceDisconnect(path, event.device);
72
+ });
59
73
  }
60
74
  class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
61
75
  constructor() {
@@ -65,7 +79,11 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
65
79
  logPrefix: 'ProtocolV2 WebUSB',
66
80
  });
67
81
  this.deviceProtocol = new Map();
82
+ this.confirmedDeviceProtocols = new Map();
68
83
  this.deviceProtocolHints = new Map();
84
+ this.staleProtocolPaths = new Set();
85
+ this.acquiredPaths = new Set();
86
+ this.probedDeviceObjects = new Map();
69
87
  this.deviceEndpoints = new Map();
70
88
  this.name = 'WebUsbTransport';
71
89
  this.stopped = false;
@@ -75,13 +93,27 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
75
93
  this.endpointId = ENDPOINT_ID;
76
94
  this.interfaceId = INTERFACE_ID;
77
95
  }
78
- init(logger) {
96
+ init(logger, emitter) {
79
97
  this.Log = logger;
98
+ this.emitter = emitter;
80
99
  const { usb } = navigator;
81
100
  if (!usb) {
82
101
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'WebUSB is not supported by current browsers');
83
102
  }
84
103
  this.usb = usb;
104
+ registerActiveWebUsbTransport(this, usb);
105
+ }
106
+ emitDeviceDisconnect(path, device) {
107
+ var _a, _b;
108
+ (_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit(transport.TRANSPORT_EVENT.DEVICE_DISCONNECT, {
109
+ name: (_b = device === null || device === void 0 ? void 0 : device.productName) !== null && _b !== void 0 ? _b : '',
110
+ id: path,
111
+ connectId: path,
112
+ });
113
+ }
114
+ markProtocolStale(path) {
115
+ this.staleProtocolPaths.add(path);
116
+ this.probedDeviceObjects.delete(path);
85
117
  }
86
118
  configure(signedData) {
87
119
  const messages = parseConfigure$1(signedData);
@@ -129,26 +161,29 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
129
161
  const devices = yield this.usb.getDevices();
130
162
  const onekeyDevices = devices.filter(dev => {
131
163
  const isOneKey = hdShared.ONEKEY_WEBUSB_FILTER.some((desc) => dev.vendorId === desc.vendorId && dev.productId === desc.productId);
132
- const hasSerialNumber = typeof dev.serialNumber === 'string' && dev.serialNumber.length > 0;
133
- return isOneKey && hasSerialNumber && !hdShared.isKnownTrezorWebUsbDevice(dev);
164
+ return isOneKey && !hdShared.isKnownTrezorWebUsbDevice(dev);
134
165
  });
135
- this.deviceList = onekeyDevices.map(device => {
136
- const path = device.serialNumber;
137
- const protocolHint = inferProtocolHintFromDeviceName$1(device.productName);
166
+ this.deviceList = onekeyDevices.flatMap(device => {
167
+ const path = hdShared.resolveOneKeyUsbDevicePath(device);
168
+ if (!path)
169
+ return [];
170
+ const protocolHint = hdShared.inferProtocolHintFromUsbId(device.vendorId, device.productId);
138
171
  if (protocolHint) {
139
172
  this.deviceProtocolHints.set(path, protocolHint);
140
173
  }
141
- return {
142
- path,
143
- device,
144
- commType: 'webusb',
145
- };
174
+ return [
175
+ {
176
+ path,
177
+ device,
178
+ commType: 'webusb',
179
+ },
180
+ ];
146
181
  });
147
182
  return this.deviceList;
148
183
  });
149
184
  }
150
185
  acquire(input) {
151
- var _a, _b, _c, _d;
186
+ var _a, _b, _c, _d, _e, _f, _g;
152
187
  return __awaiter(this, void 0, void 0, function* () {
153
188
  if (!input.path)
154
189
  return;
@@ -156,18 +191,63 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
156
191
  yield this.rotateProtocolV2UsbGeneration(input.path, 'WebUSB transport acquired');
157
192
  yield this.closeOpenDevice(input.path);
158
193
  yield this.connect((_a = input.path) !== null && _a !== void 0 ? _a : '', true);
159
- const deviceName = (_b = this.deviceList.find(device => device.path === input.path)) === null || _b === void 0 ? void 0 : _b.device.productName;
160
- const protocolHint = input.expectedProtocol
161
- ? undefined
162
- : (_d = (_c = input.protocolHint) !== null && _c !== void 0 ? _c : this.deviceProtocolHints.get(input.path)) !== null && _d !== void 0 ? _d : inferProtocolHintFromDeviceName$1(deviceName);
163
- if (protocolHint) {
164
- this.deviceProtocolHints.set(input.path, protocolHint);
194
+ if (input.skipProtocolProbe) {
195
+ if (!input.expectedProtocol) {
196
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'skipProtocolProbe requires an expected protocol');
197
+ }
198
+ if (this.confirmedDeviceProtocols.get(input.path) !== input.expectedProtocol) {
199
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'skipProtocolProbe requires a previously confirmed protocol for this WebUSB endpoint');
200
+ }
201
+ this.staleProtocolPaths.delete(input.path);
202
+ this.deviceProtocol.set(input.path, input.expectedProtocol);
203
+ const currentDevice = (_b = this.deviceList.find(d => d.path === input.path)) === null || _b === void 0 ? void 0 : _b.device;
204
+ if (currentDevice) {
205
+ this.probedDeviceObjects.set(input.path, currentDevice);
206
+ }
207
+ }
208
+ else if (input.forceProtocolDetection) {
209
+ this.staleProtocolPaths.delete(input.path);
210
+ this.deviceProtocol.delete(input.path);
211
+ this.deviceProtocolHints.delete(input.path);
212
+ }
213
+ if (!input.skipProtocolProbe && this.staleProtocolPaths.has(input.path)) {
214
+ this.staleProtocolPaths.delete(input.path);
215
+ this.deviceProtocol.delete(input.path);
216
+ }
217
+ if (!input.skipProtocolProbe && this.deviceProtocol.has(input.path)) {
218
+ const currentDevice = (_c = this.deviceList.find(d => d.path === input.path)) === null || _c === void 0 ? void 0 : _c.device;
219
+ if (!currentDevice || currentDevice !== this.probedDeviceObjects.get(input.path)) {
220
+ this.deviceProtocol.delete(input.path);
221
+ }
165
222
  }
166
- yield this.detectProtocol(input.path, input.expectedProtocol, protocolHint);
223
+ const cachedProtocol = this.deviceProtocol.get(input.path);
224
+ if (!input.skipProtocolProbe &&
225
+ cachedProtocol &&
226
+ input.expectedProtocol &&
227
+ cachedProtocol !== input.expectedProtocol) {
228
+ this.deviceProtocol.delete(input.path);
229
+ }
230
+ if (!this.deviceProtocol.has(input.path)) {
231
+ const usbDevice = (_d = this.deviceList.find(device => device.path === input.path)) === null || _d === void 0 ? void 0 : _d.device;
232
+ const protocolHint = input.expectedProtocol
233
+ ? undefined
234
+ : (_f = (_e = input.protocolHint) !== null && _e !== void 0 ? _e : this.deviceProtocolHints.get(input.path)) !== null && _f !== void 0 ? _f : hdShared.inferProtocolHintFromUsbId(usbDevice === null || usbDevice === void 0 ? void 0 : usbDevice.vendorId, usbDevice === null || usbDevice === void 0 ? void 0 : usbDevice.productId);
235
+ if (protocolHint) {
236
+ this.deviceProtocolHints.set(input.path, protocolHint);
237
+ }
238
+ const detectedProtocol = yield this.detectProtocol(input.path, input.expectedProtocol, protocolHint);
239
+ this.confirmedDeviceProtocols.set(input.path, detectedProtocol);
240
+ const probedDevice = (_g = this.deviceList.find(d => d.path === input.path)) === null || _g === void 0 ? void 0 : _g.device;
241
+ if (probedDevice) {
242
+ this.probedDeviceObjects.set(input.path, probedDevice);
243
+ }
244
+ }
245
+ this.acquiredPaths.add(input.path);
167
246
  return yield Promise.resolve(input.path);
168
247
  }
169
248
  catch (e) {
170
249
  this.Log.debug('acquire error: ', e instanceof Error ? `${e.name}: ${e.message}` : String(e));
250
+ this.acquiredPaths.delete(input.path);
171
251
  yield this.closeOpenDevice(input.path);
172
252
  throw e;
173
253
  }
@@ -177,7 +257,7 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
177
257
  return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
178
258
  }
179
259
  createProtocolProbeTimeoutError(expected, attempts) {
180
- return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Protocol ${expected} probe timeout after ${attempts} attempts`);
260
+ return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceInitializeFailed, `Protocol ${expected} probe timeout after ${attempts} attempts`);
181
261
  }
182
262
  createProtocolDetectionError() {
183
263
  return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound);
@@ -351,8 +431,18 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
351
431
  }
352
432
  });
353
433
  }
434
+ assertAcquired(path) {
435
+ if (!this.acquiredPaths.has(path)) {
436
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device is not acquired for ${path}`);
437
+ }
438
+ }
354
439
  post(session, name, data) {
355
440
  return __awaiter(this, void 0, void 0, function* () {
441
+ this.assertAcquired(session);
442
+ if (this.deviceProtocol.get(session) === 'V2') {
443
+ yield this.sendProtocolV2UsbFlowControl(session, name, data);
444
+ return;
445
+ }
356
446
  yield this.call(session, name, data);
357
447
  });
358
448
  }
@@ -381,6 +471,7 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
381
471
  var _a;
382
472
  return __awaiter(this, void 0, void 0, function* () {
383
473
  this.Log.debug(`[WebUsbTransport] transfer${direction} failed, retry ${attempt}/${PACKET_IO_MAX_RETRIES}: ${this.getErrorMessage(error)}`);
474
+ this.staleProtocolPaths.add(path);
384
475
  yield hdShared.wait(attempt * PACKET_IO_RETRY_DELAY);
385
476
  try {
386
477
  const currentDevice = yield this.findDevice(path);
@@ -595,6 +686,7 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
595
686
  if (this.messages == null) {
596
687
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
597
688
  }
689
+ this.assertAcquired(path);
598
690
  const device = yield this.findDevice(path);
599
691
  if (!device) {
600
692
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound);
@@ -603,9 +695,6 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
603
695
  if (!protocol) {
604
696
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol has not been detected for ${path}`);
605
697
  }
606
- if (!transport.shouldSuppressHighVolumeCallLog(name)) {
607
- this.Log.debug('transport call', transport.createTransportCallLog(name, protocol, data));
608
- }
609
698
  if (protocol === 'V2') {
610
699
  return this.callProtocolV2(path, name, data, options);
611
700
  }
@@ -677,10 +766,9 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
677
766
  }
678
767
  release(path) {
679
768
  return __awaiter(this, void 0, void 0, function* () {
769
+ this.acquiredPaths.delete(path);
680
770
  yield this.invalidateProtocolV2UsbLink(path, 'WebUSB transport released');
681
771
  yield this.closeOpenDevice(path);
682
- this.deviceProtocol.delete(path);
683
- this.deviceProtocolHints.delete(path);
684
772
  this.deviceEndpoints.delete(path);
685
773
  });
686
774
  }
@@ -734,14 +822,11 @@ function resolveBlePacketCapacity(mtu, maximumPacketCapacity, fallbackPacketCapa
734
822
  }
735
823
 
736
824
  const { parseConfigure, ProtocolV1, check } = transport__default["default"];
737
- function inferProtocolHintFromDeviceName(name) {
738
- return /\bpro\s*2\b/i.test(name !== null && name !== void 0 ? name : '') ? 'V2' : undefined;
739
- }
740
825
  const toBleDescriptor = (device, protocolType) => (Object.assign({ id: device.id, name: device.name, path: device.id, debug: false, commType: 'electron-ble' }, (protocolType ? { protocolType } : {})));
741
826
  const BLE_PACKET_SIZE_FALLBACK = 192;
742
827
  const BLE_PACKET_SIZE_MAXIMUM = 244;
743
828
  const BLE_WRITE_DELAY_MS = 5;
744
- const PROTOCOL_PROBE_TIMEOUT_MS = 1000;
829
+ const PROTOCOL_PROBE_TIMEOUT_MS = 3000;
745
830
  const PROTOCOL_V2_PROBE_TIMEOUT_MS = 5000;
746
831
  class ElectronBleTransport {
747
832
  constructor() {
@@ -752,6 +837,7 @@ class ElectronBleTransport {
752
837
  this.connectedDevices = new Set();
753
838
  this.deviceProtocol = new Map();
754
839
  this.deviceProtocolHints = new Map();
840
+ this.confirmedProtocolV2 = new Set();
755
841
  this.deviceMtus = new Map();
756
842
  this.devicePacketCapacities = new Map();
757
843
  this.v1Buffers = new Map();
@@ -782,11 +868,32 @@ class ElectronBleTransport {
782
868
  this.warnedMissingRelease = false;
783
869
  this.notificationCleanups = new Map();
784
870
  this.mtuCleanups = new Map();
785
- this.disconnectCleanups = new Map();
786
871
  this.notificationTokens = new Map();
787
872
  this.nextNotificationToken = 1;
788
873
  }
789
- handleBluetoothError(error) {
874
+ toStaleBondError(error) {
875
+ var _a;
876
+ if (hdShared.isBleStaleBondHardwareError(error)) {
877
+ return error;
878
+ }
879
+ const errorMessage = error && typeof error === 'object' && 'message' in error
880
+ ? String((_a = error.message) !== null && _a !== void 0 ? _a : '')
881
+ : String(error !== null && error !== void 0 ? error : '');
882
+ if (!hdShared.isBleStaleBondErrorText(errorMessage)) {
883
+ return null;
884
+ }
885
+ const normalizedErrorMessage = errorMessage.toLowerCase();
886
+ return hdShared.ERRORS.TypedError(normalizedErrorMessage.includes('peer removed pairing information')
887
+ ? hdShared.HardwareErrorCode.BlePeerRemovedPairingInformation
888
+ : hdShared.HardwareErrorCode.BleDeviceBondError, errorMessage);
889
+ }
890
+ handleBluetoothError(error, mapProtocolV2StaleBond = false) {
891
+ if (mapProtocolV2StaleBond) {
892
+ const staleBondError = this.toStaleBondError(error);
893
+ if (staleBondError) {
894
+ throw staleBondError;
895
+ }
896
+ }
790
897
  if (error && typeof error === 'object') {
791
898
  if ('code' in error) {
792
899
  if (error.code === hdShared.HardwareErrorCode.BlePoweredOff) {
@@ -842,11 +949,6 @@ class ElectronBleTransport {
842
949
  mtuCleanup();
843
950
  this.mtuCleanups.delete(deviceId);
844
951
  }
845
- const disconnectCleanup = this.disconnectCleanups.get(deviceId);
846
- if (disconnectCleanup) {
847
- disconnectCleanup();
848
- this.disconnectCleanups.delete(deviceId);
849
- }
850
952
  }
851
953
  init(logger, emitter) {
852
954
  var _a, _b;
@@ -855,8 +957,26 @@ class ElectronBleTransport {
855
957
  if (!((_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle)) {
856
958
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Noble BLE API is not available. Please ensure you are running in Electron with Noble support.');
857
959
  }
960
+ this.subscribeHostDisconnects();
858
961
  (_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug('[Electron BLE] Transport initialized');
859
962
  }
963
+ subscribeHostDisconnects() {
964
+ var _a, _b, _c;
965
+ (_a = this.hostDisconnectCleanup) === null || _a === void 0 ? void 0 : _a.call(this);
966
+ this.hostDisconnectCleanup = (_c = (_b = window.desktopApi) === null || _b === void 0 ? void 0 : _b.nobleBle) === null || _c === void 0 ? void 0 : _c.onDeviceDisconnected((disconnectedDevice) => {
967
+ var _a, _b, _c;
968
+ const uuid = disconnectedDevice === null || disconnectedDevice === void 0 ? void 0 : disconnectedDevice.id;
969
+ if (!uuid)
970
+ return;
971
+ this.cleanupDeviceState(uuid);
972
+ (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] Device link dropped:', uuid, (_b = disconnectedDevice.reason) !== null && _b !== void 0 ? _b : hdShared.EBleDisconnectReason.DeviceDisconnected);
973
+ (_c = this.emitter) === null || _c === void 0 ? void 0 : _c.emit(transport.TRANSPORT_EVENT.DEVICE_DISCONNECT, {
974
+ name: disconnectedDevice.name,
975
+ id: uuid,
976
+ connectId: uuid,
977
+ });
978
+ });
979
+ }
860
980
  configure(signedData) {
861
981
  this._messages = parseConfigure(signedData);
862
982
  this.configured = true;
@@ -891,10 +1011,6 @@ class ElectronBleTransport {
891
1011
  (_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug(`[Electron BLE] enumerate found ${devices.length} device(s):`);
892
1012
  for (const dev of devices) {
893
1013
  (_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug(`[Electron BLE] id="${dev.id}" name="${dev.name}"`);
894
- const protocolHint = inferProtocolHintFromDeviceName(dev.name);
895
- if (protocolHint) {
896
- this.deviceProtocolHints.set(dev.id, protocolHint);
897
- }
898
1014
  }
899
1015
  return devices.map(device => toBleDescriptor(device));
900
1016
  }
@@ -905,9 +1021,12 @@ class ElectronBleTransport {
905
1021
  });
906
1022
  }
907
1023
  acquire(input) {
908
- var _a, _b, _c, _d, _e, _f, _g, _h;
1024
+ var _a, _b, _c, _d, _e, _f, _g;
909
1025
  return __awaiter(this, void 0, void 0, function* () {
910
1026
  const { uuid, forceCleanRunPromise, expectedProtocol } = input;
1027
+ const shouldMapProtocolV2StaleBond = expectedProtocol
1028
+ ? expectedProtocol === 'V2'
1029
+ : this.confirmedProtocolV2.has(uuid);
911
1030
  if (!uuid) {
912
1031
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleRequiredUUID);
913
1032
  }
@@ -930,7 +1049,7 @@ class ElectronBleTransport {
930
1049
  }
931
1050
  const protocolHint = expectedProtocol
932
1051
  ? undefined
933
- : (_c = (_b = input.protocolHint) !== null && _b !== void 0 ? _b : this.deviceProtocolHints.get(uuid)) !== null && _c !== void 0 ? _c : inferProtocolHintFromDeviceName(device.name);
1052
+ : (_b = input.protocolHint) !== null && _b !== void 0 ? _b : this.deviceProtocolHints.get(uuid);
934
1053
  if (protocolHint) {
935
1054
  this.deviceProtocolHints.set(uuid, protocolHint);
936
1055
  }
@@ -939,7 +1058,7 @@ class ElectronBleTransport {
939
1058
  this.connectedDevices.add(uuid);
940
1059
  }
941
1060
  catch (error) {
942
- this.handleBluetoothError(error);
1061
+ this.handleBluetoothError(error, shouldMapProtocolV2StaleBond);
943
1062
  }
944
1063
  const mtuCleanup = this.createMtuSubscription(uuid);
945
1064
  if (mtuCleanup) {
@@ -947,44 +1066,35 @@ class ElectronBleTransport {
947
1066
  }
948
1067
  this.v1Buffers.set(uuid, { buffer: [], bufferLength: 0 });
949
1068
  this.v2Assemblers.set(uuid, new transport.ProtocolV2FrameAssembler(transport.PROTOCOL_V2_BLE_FRAME_MAX_BYTES));
950
- yield window.desktopApi.nobleBle.subscribe(uuid);
1069
+ try {
1070
+ yield window.desktopApi.nobleBle.subscribe(uuid);
1071
+ }
1072
+ catch (error) {
1073
+ this.handleBluetoothError(error, shouldMapProtocolV2StaleBond);
1074
+ }
951
1075
  yield this.refreshBlePacketCapacity(uuid);
952
1076
  const cleanup = this.createNotificationSubscription(uuid);
953
1077
  this.notificationCleanups.set(uuid, cleanup);
954
- const connectionToken = this.notificationTokens.get(uuid);
955
1078
  const protocolType = yield this.detectProtocol(uuid, expectedProtocol, protocolHint);
956
1079
  if (protocolType === 'V2') {
957
- (_d = this.Log) === null || _d === void 0 ? void 0 : _d.debug('[Electron BLE] Protocol V2 write configured', {
1080
+ (_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug('[Electron BLE] Protocol V2 write configured', {
958
1081
  writeMode: 'withoutResponse',
959
1082
  negotiatedMtu: this.deviceMtus.get(uuid),
960
- packetCapacity: (_e = this.devicePacketCapacities.get(uuid)) !== null && _e !== void 0 ? _e : BLE_PACKET_SIZE_FALLBACK,
1083
+ packetCapacity: (_d = this.devicePacketCapacities.get(uuid)) !== null && _d !== void 0 ? _d : BLE_PACKET_SIZE_FALLBACK,
961
1084
  });
962
1085
  }
963
- const disconnectCleanup = window.desktopApi.nobleBle.onDeviceDisconnected((disconnectedDevice) => {
964
- var _a;
965
- if (disconnectedDevice.id === uuid &&
966
- this.notificationTokens.get(uuid) === connectionToken) {
967
- this.cleanupDeviceState(uuid);
968
- (_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit(transport.TRANSPORT_EVENT.DEVICE_DISCONNECT, {
969
- name: disconnectedDevice.name,
970
- id: disconnectedDevice.id,
971
- connectId: disconnectedDevice.id,
972
- });
973
- }
974
- });
975
- this.disconnectCleanups.set(uuid, disconnectCleanup);
976
1086
  return Object.assign(Object.assign({}, toBleDescriptor({ id: device.id, name: device.name }, protocolType)), { uuid });
977
1087
  }
978
1088
  catch (error) {
979
- (_f = this.Log) === null || _f === void 0 ? void 0 : _f.error('[Electron BLE] acquire failed:', error);
1089
+ (_e = this.Log) === null || _e === void 0 ? void 0 : _e.error('[Electron BLE] acquire failed:', error);
980
1090
  try {
981
- if (((_g = window.desktopApi) === null || _g === void 0 ? void 0 : _g.nobleBle) && this.connectedDevices.has(uuid)) {
1091
+ if (((_f = window.desktopApi) === null || _f === void 0 ? void 0 : _f.nobleBle) && this.connectedDevices.has(uuid)) {
982
1092
  yield window.desktopApi.nobleBle.unsubscribe(uuid);
983
1093
  yield window.desktopApi.nobleBle.disconnect(uuid);
984
1094
  }
985
1095
  }
986
1096
  catch (cleanupError) {
987
- (_h = this.Log) === null || _h === void 0 ? void 0 : _h.debug('[Electron BLE] acquire cleanup failed:', cleanupError);
1097
+ (_g = this.Log) === null || _g === void 0 ? void 0 : _g.debug('[Electron BLE] acquire cleanup failed:', cleanupError);
988
1098
  }
989
1099
  this.cleanupDeviceState(uuid);
990
1100
  throw error;
@@ -1051,8 +1161,9 @@ class ElectronBleTransport {
1051
1161
  }
1052
1162
  });
1053
1163
  }
1054
- createProtocolMismatchError(expected) {
1055
- return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
1164
+ createProtocolMismatchError(expected, uuid) {
1165
+ const isStaleV2Bond = expected === 'V2' && this.confirmedProtocolV2.has(uuid);
1166
+ return hdShared.ERRORS.TypedError(isStaleV2Bond ? hdShared.HardwareErrorCode.BleDeviceBondError : hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
1056
1167
  }
1057
1168
  createProtocolDetectionError() {
1058
1169
  return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, 'Unable to detect BLE protocol: device did not respond to Protocol V1 GetFeatures or Protocol V2 Ping');
@@ -1066,20 +1177,18 @@ class ElectronBleTransport {
1066
1177
  var _a, _b, _c;
1067
1178
  return __awaiter(this, void 0, void 0, function* () {
1068
1179
  if (expectedProtocol === 'V1') {
1069
- if (yield this.probeProtocolV1(uuid)) {
1070
- this.deviceProtocol.set(uuid, 'V1');
1071
- (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[Electron BLE] detectProtocol: uuid=${uuid} -> V1 (expected)`);
1072
- return 'V1';
1073
- }
1074
- throw this.createProtocolMismatchError(expectedProtocol);
1180
+ this.deviceProtocol.set(uuid, 'V1');
1181
+ (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[Electron BLE] detectProtocol: uuid=${uuid} -> V1 (expected, no probe)`);
1182
+ return 'V1';
1075
1183
  }
1076
1184
  if (expectedProtocol === 'V2') {
1077
1185
  if (yield this.probeProtocolV2(uuid)) {
1078
1186
  this.deviceProtocol.set(uuid, 'V2');
1187
+ this.confirmedProtocolV2.add(uuid);
1079
1188
  (_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug(`[Electron BLE] detectProtocol: uuid=${uuid} -> V2 (expected)`);
1080
1189
  return 'V2';
1081
1190
  }
1082
- throw this.createProtocolMismatchError(expectedProtocol);
1191
+ throw this.createProtocolMismatchError(expectedProtocol, uuid);
1083
1192
  }
1084
1193
  const probeOrder = protocolHint === 'V2' || this.deviceProtocol.get(uuid) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
1085
1194
  for (let i = 0; i < probeOrder.length; i += 1) {
@@ -1090,6 +1199,9 @@ class ElectronBleTransport {
1090
1199
  const detected = protocol === 'V1' ? yield this.probeProtocolV1(uuid) : yield this.probeProtocolV2(uuid);
1091
1200
  if (detected) {
1092
1201
  this.deviceProtocol.set(uuid, protocol);
1202
+ if (protocol === 'V2') {
1203
+ this.confirmedProtocolV2.add(uuid);
1204
+ }
1093
1205
  (_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug(`[Electron BLE] detectProtocol: uuid=${uuid} -> ${protocol}`);
1094
1206
  return protocol;
1095
1207
  }
@@ -1113,7 +1225,7 @@ class ElectronBleTransport {
1113
1225
  });
1114
1226
  }
1115
1227
  resetProbeStateAfterProtocolProbe(uuid, protocol) {
1116
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
1228
+ var _a;
1117
1229
  return __awaiter(this, void 0, void 0, function* () {
1118
1230
  yield this.protocolV2Links.invalidateLink(uuid, `Reset notify state after Protocol ${protocol} probe`);
1119
1231
  this.v1Buffers.set(uuid, { buffer: [], bufferLength: 0 });
@@ -1125,37 +1237,10 @@ class ElectronBleTransport {
1125
1237
  this.runPromise = null;
1126
1238
  this.runPromiseDeviceId = null;
1127
1239
  }
1128
- const notifyCleanup = this.notificationCleanups.get(uuid);
1129
- if (notifyCleanup) {
1130
- notifyCleanup();
1131
- this.notificationCleanups.delete(uuid);
1132
- }
1133
- this.notificationTokens.delete(uuid);
1134
- try {
1135
- yield ((_c = (_b = window.desktopApi) === null || _b === void 0 ? void 0 : _b.nobleBle) === null || _c === void 0 ? void 0 : _c.unsubscribe(uuid));
1136
- }
1137
- catch (error) {
1138
- (_d = this.Log) === null || _d === void 0 ? void 0 : _d.debug(`[Electron BLE] unsubscribe after Protocol ${protocol} probe failed:`, error);
1139
- }
1140
- try {
1141
- yield ((_f = (_e = window.desktopApi) === null || _e === void 0 ? void 0 : _e.nobleBle) === null || _f === void 0 ? void 0 : _f.disconnect(uuid));
1142
- }
1143
- catch (error) {
1144
- (_g = this.Log) === null || _g === void 0 ? void 0 : _g.debug(`[Electron BLE] disconnect after Protocol ${protocol} probe failed:`, error);
1145
- }
1146
- this.connectedDevices.delete(uuid);
1147
- try {
1148
- yield ((_j = (_h = window.desktopApi) === null || _h === void 0 ? void 0 : _h.nobleBle) === null || _j === void 0 ? void 0 : _j.connect(uuid));
1149
- this.connectedDevices.add(uuid);
1150
- yield ((_l = (_k = window.desktopApi) === null || _k === void 0 ? void 0 : _k.nobleBle) === null || _l === void 0 ? void 0 : _l.subscribe(uuid));
1151
- yield this.refreshBlePacketCapacity(uuid);
1152
- }
1153
- catch (error) {
1154
- (_m = this.Log) === null || _m === void 0 ? void 0 : _m.debug(`[Electron BLE] reconnect after Protocol ${protocol} probe failed:`, error);
1155
- throw error;
1240
+ if (!this.notificationCleanups.has(uuid)) {
1241
+ const cleanup = this.createNotificationSubscription(uuid);
1242
+ this.notificationCleanups.set(uuid, cleanup);
1156
1243
  }
1157
- const cleanup = this.createNotificationSubscription(uuid);
1158
- this.notificationCleanups.set(uuid, cleanup);
1159
1244
  });
1160
1245
  }
1161
1246
  probeProtocolV1(uuid) {
@@ -1172,6 +1257,9 @@ class ElectronBleTransport {
1172
1257
  catch (error) {
1173
1258
  this.clearProbeProtocol(uuid, 'V1');
1174
1259
  (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] Protocol V1 GetFeatures probe failed:', error);
1260
+ if (transport.isProtocolV2LinkDisabledError(error)) {
1261
+ throw error;
1262
+ }
1175
1263
  return false;
1176
1264
  }
1177
1265
  });
@@ -1194,6 +1282,7 @@ class ElectronBleTransport {
1194
1282
  (_a = this.v2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1195
1283
  this.resetProtocolV2Frames(uuid);
1196
1284
  },
1285
+ shouldRethrow: error => hdShared.isBleStaleBondHardwareError(error) || transport.isProtocolV2LinkDisabledError(error),
1197
1286
  });
1198
1287
  if (!detected) {
1199
1288
  this.clearProbeProtocol(uuid, 'V2');
@@ -1208,7 +1297,16 @@ class ElectronBleTransport {
1208
1297
  if (!nobleBle) {
1209
1298
  throw new Error('Noble BLE API not available');
1210
1299
  }
1211
- yield nobleBle.write(uuid, hexData, { pacingDelayMs: 0 });
1300
+ try {
1301
+ yield nobleBle.write(uuid, hexData, { pacingDelayMs: 0 });
1302
+ }
1303
+ catch (error) {
1304
+ const staleBondError = this.toStaleBondError(error);
1305
+ if (staleBondError) {
1306
+ throw staleBondError;
1307
+ }
1308
+ throw error;
1309
+ }
1212
1310
  });
1213
1311
  }
1214
1312
  refreshBlePacketCapacity(uuid) {
@@ -1277,8 +1375,27 @@ class ElectronBleTransport {
1277
1375
  this.handleProtocolV2Notification(deviceId, hexData);
1278
1376
  return;
1279
1377
  }
1378
+ const linkDisabledError = this.readProtocolV2LinkDisabledFailure(deviceId, hexData);
1379
+ if (linkDisabledError) {
1380
+ if (this.runPromise && this.runPromiseDeviceId === deviceId) {
1381
+ this.runPromise.reject(linkDisabledError);
1382
+ }
1383
+ return;
1384
+ }
1280
1385
  this.handleProtocolV1Notification(deviceId, hexData);
1281
1386
  }
1387
+ readProtocolV2LinkDisabledFailure(deviceId, hexData) {
1388
+ if (!this._messages || !this._messagesV2)
1389
+ return undefined;
1390
+ const assembler = this.v2Assemblers.get(deviceId);
1391
+ if (!assembler)
1392
+ return undefined;
1393
+ return transport.detectProtocolV2LinkDisabledError({
1394
+ schemas: { protocolV1: this._messages, protocolV2: this._messagesV2 },
1395
+ assembler,
1396
+ bytes: transport.hexToBytes(hexData),
1397
+ });
1398
+ }
1282
1399
  handleProtocolV2Notification(deviceId, hexData) {
1283
1400
  var _a;
1284
1401
  try {
@@ -1362,7 +1479,6 @@ class ElectronBleTransport {
1362
1479
  }
1363
1480
  }
1364
1481
  call(uuid, name, data, options) {
1365
- var _a;
1366
1482
  return __awaiter(this, void 0, void 0, function* () {
1367
1483
  if (!this._messages) {
1368
1484
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
@@ -1374,15 +1490,21 @@ class ElectronBleTransport {
1374
1490
  if (!protocol) {
1375
1491
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol has not been detected for ${uuid}`);
1376
1492
  }
1377
- if (!transport.shouldSuppressHighVolumeCallLog(name)) {
1378
- (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('transport call', transport.createTransportCallLog(name, protocol, data));
1379
- }
1380
1493
  if (protocol === 'V2') {
1381
1494
  return this.callProtocolV2(uuid, name, data, options);
1382
1495
  }
1383
1496
  return this.callProtocolV1(uuid, name, data, options);
1384
1497
  });
1385
1498
  }
1499
+ post(uuid, name, data) {
1500
+ return __awaiter(this, void 0, void 0, function* () {
1501
+ if (this.deviceProtocol.get(uuid) === 'V2') {
1502
+ yield this.protocolV2Links.sendFlowControl(uuid, () => this.createProtocolV2Adapter(uuid), name, data);
1503
+ return;
1504
+ }
1505
+ yield this.callProtocolV1(uuid, name, data);
1506
+ });
1507
+ }
1386
1508
  callProtocolV1(uuid, name, data, options) {
1387
1509
  var _a, _b;
1388
1510
  return __awaiter(this, void 0, void 0, function* () {