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

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.
@@ -1,4 +1,5 @@
1
1
  import transport, { PROTOCOL_V2_CHANNEL_BLE_UART, bytesToHex } from '@onekeyfe/hd-transport';
2
+ import { HardwareErrorCode } from '@onekeyfe/hd-shared';
2
3
 
3
4
  import ElectronBleTransport from '../src/electron-ble-transport';
4
5
 
@@ -245,6 +246,95 @@ describe('ElectronBleTransport protocol detection', () => {
245
246
  );
246
247
  expect(nobleBle.write).toHaveBeenCalledTimes(1);
247
248
  expect(transport.getProtocolType(device.id)).toBe('V2');
249
+ await expect(transport.call(device.id, 'Ping', { message: 'after-probe' })).resolves.toEqual({
250
+ type: 'Success',
251
+ message: { message: 'ok' },
252
+ });
253
+ const sentSeqs = nobleBle.write.mock.calls.map(([, hex]) =>
254
+ Number.parseInt(hex.slice(12, 14), 16)
255
+ );
256
+ expect(sentSeqs).toEqual([1, 2]);
257
+ } finally {
258
+ await transport.release(device.id);
259
+ }
260
+ });
261
+
262
+ test('rejects the active Protocol V2 reader when pairing is rejected', async () => {
263
+ const device = { id: 'pairing-rejected-pro2-id', name: 'OneKey Pro 2' };
264
+ const nobleBle = createNobleBle(device);
265
+ let notificationHandler: ((deviceId: string, data: string) => void) | undefined;
266
+ let pairingRejected = false;
267
+ const probeResponse = ProtocolV2.encodeFrame(
268
+ schemas,
269
+ 'Success',
270
+ { message: 'ok' },
271
+ { router: PROTOCOL_V2_CHANNEL_BLE_UART }
272
+ );
273
+
274
+ nobleBle.onNotification.mockImplementation(handler => {
275
+ notificationHandler = handler;
276
+ return jest.fn();
277
+ });
278
+ nobleBle.write.mockImplementation(() => {
279
+ setTimeout(
280
+ () =>
281
+ notificationHandler?.(
282
+ device.id,
283
+ pairingRejected ? 'PAIRING_REJECTED' : bytesToHex(probeResponse)
284
+ ),
285
+ 0
286
+ );
287
+ return Promise.resolve();
288
+ });
289
+ const transport = configureTransport(nobleBle);
290
+
291
+ try {
292
+ await transport.acquire({ uuid: device.id });
293
+ pairingRejected = true;
294
+
295
+ await expect(
296
+ transport.call(device.id, 'Ping', { message: 'pairing' }, { timeoutMs: 50 })
297
+ ).rejects.toMatchObject({ errorCode: HardwareErrorCode.BleDeviceBondedCanceled });
298
+ } finally {
299
+ await transport.release(device.id);
300
+ }
301
+ });
302
+
303
+ test('rebuilds the active link when Core acquires the same device again', async () => {
304
+ const device = { id: 'repeated-acquire-pro2-id', name: 'OneKey Pro 2' };
305
+ const nobleBle = createNobleBle(device);
306
+ 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
+ nobleBle.onNotification.mockImplementation(handler => {
315
+ notificationHandler = handler;
316
+ return jest.fn();
317
+ });
318
+ nobleBle.write.mockImplementation(() => {
319
+ setTimeout(() => notificationHandler?.(device.id, bytesToHex(response)), 0);
320
+ return Promise.resolve();
321
+ });
322
+ const transport = configureTransport(nobleBle);
323
+
324
+ try {
325
+ await transport.acquire({ uuid: device.id });
326
+ await transport.acquire({ uuid: device.id, expectedProtocol: 'V2' });
327
+ await expect(
328
+ transport.call(device.id, 'Ping', { message: 'after-reacquire' })
329
+ ).resolves.toEqual({
330
+ type: 'Success',
331
+ message: { message: 'ok' },
332
+ });
333
+
334
+ const sentSeqs = nobleBle.write.mock.calls.map(([, hex]) =>
335
+ Number.parseInt(hex.slice(12, 14), 16)
336
+ );
337
+ expect(sentSeqs).toEqual([1, 2]);
248
338
  } finally {
249
339
  await transport.release(device.id);
250
340
  }
@@ -28,8 +28,7 @@ export default class ElectronBleTransport {
28
28
  private v2Assemblers;
29
29
  private v2FrameQueues;
30
30
  private v2FramePromises;
31
- private activeProtocolV2Call;
32
- private nextProtocolV2CallToken;
31
+ private protocolV2Links;
33
32
  private notificationCleanups;
34
33
  private disconnectCleanups;
35
34
  private notificationTokens;
@@ -69,12 +68,13 @@ export default class ElectronBleTransport {
69
68
  private resolveProtocolV2Frame;
70
69
  private rejectAllProtocolV2Frames;
71
70
  private resetProtocolV2Frames;
72
- private isActiveProtocolV2Call;
71
+ private rejectProtocolV2Frames;
73
72
  private readProtocolV2Frame;
74
73
  private handleProtocolV1Notification;
75
74
  call(uuid: string, name: string, data: Record<string, unknown>, options?: TransportCallOptions): Promise<import("@onekeyfe/hd-transport").MessageFromOneKey>;
76
75
  private callProtocolV1;
77
76
  private callProtocolV2;
77
+ private createProtocolV2Adapter;
78
78
  private processProtocolV1Notification;
79
79
  getProtocolType(path: string): ProtocolType | undefined;
80
80
  }
@@ -1 +1 @@
1
- {"version":3,"file":"electron-ble-transport.d.ts","sourceRoot":"","sources":["../src/electron-ble-transport.ts"],"names":[],"mappings":";AAmBA,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;AAevC,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,oBAAoB,CAAgD;IAE5E,OAAO,CAAC,uBAAuB,CAAK;IAEpC,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;IAK7B,MAAM;IAIN,SAAS,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAqBxC,OAAO,CAAC,KAAK,EAAE,eAAe;;;;;;;;;;;IAuF9B,OAAO,CAAC,EAAE,EAAE,MAAM;IAexB,OAAO,CAAC,2BAA2B;IAOnC,OAAO,CAAC,4BAA4B;IAOpC,OAAO,CAAC,kBAAkB;YAMZ,cAAc;IA+C5B,OAAO,CAAC,8BAA8B;YAgBxB,iCAAiC;YAoCjC,eAAe;YAgBf,eAAe;YAuBf,iBAAiB;YAsBjB,SAAS;IASvB,OAAO,CAAC,kBAAkB;IAuB1B,OAAO,CAAC,4BAA4B;IA2BpC,OAAO,CAAC,uBAAuB;IAS/B,OAAO,CAAC,sBAAsB;IAU9B,OAAO,CAAC,yBAAyB;IAQjC,OAAO,CAAC,qBAAqB;IAK7B,OAAO,CAAC,sBAAsB;YAIhB,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;YA+BlB,cAAc;YAuEd,cAAc;IAmF5B,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":";AAmBA,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;AAevC,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;IAQ7B,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;YA+BlB,cAAc;YAuEd,cAAc;IA6B5B,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
@@ -93,8 +93,7 @@ declare class ElectronBleTransport {
93
93
  private v2Assemblers;
94
94
  private v2FrameQueues;
95
95
  private v2FramePromises;
96
- private activeProtocolV2Call;
97
- private nextProtocolV2CallToken;
96
+ private protocolV2Links;
98
97
  private notificationCleanups;
99
98
  private disconnectCleanups;
100
99
  private notificationTokens;
@@ -134,12 +133,13 @@ declare class ElectronBleTransport {
134
133
  private resolveProtocolV2Frame;
135
134
  private rejectAllProtocolV2Frames;
136
135
  private resetProtocolV2Frames;
137
- private isActiveProtocolV2Call;
136
+ private rejectProtocolV2Frames;
138
137
  private readProtocolV2Frame;
139
138
  private handleProtocolV1Notification;
140
139
  call(uuid: string, name: string, data: Record<string, unknown>, options?: TransportCallOptions): Promise<_onekeyfe_hd_transport.MessageFromOneKey>;
141
140
  private callProtocolV1;
142
141
  private callProtocolV2;
142
+ private createProtocolV2Adapter;
143
143
  private processProtocolV1Notification;
144
144
  getProtocolType(path: string): ProtocolType | undefined;
145
145
  }
package/dist/index.js CHANGED
@@ -781,8 +781,27 @@ class ElectronBleTransport {
781
781
  this.v2Assemblers = new Map();
782
782
  this.v2FrameQueues = new Map();
783
783
  this.v2FramePromises = new Map();
784
- this.activeProtocolV2Call = null;
785
- this.nextProtocolV2CallToken = 1;
784
+ this.protocolV2Links = new transport.ProtocolV2LinkManager({
785
+ getSchemas: () => {
786
+ if (!this._messages || !this._messagesV2) {
787
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
788
+ }
789
+ return {
790
+ protocolV1: this._messages,
791
+ protocolV2: this._messagesV2,
792
+ };
793
+ },
794
+ classifyError: () => 'link-fatal',
795
+ onLinkInvalidated: (uuid, reason) => __awaiter(this, void 0, void 0, function* () {
796
+ var _a, _b;
797
+ (_a = this.v2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
798
+ this.rejectProtocolV2Frames(uuid, new Error(reason));
799
+ (_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug('[Electron BLE] Protocol V2 link invalidated:', uuid, reason);
800
+ if (reason.startsWith('Protocol V2 link-fatal error:')) {
801
+ yield this.release(uuid);
802
+ }
803
+ }),
804
+ });
786
805
  this.notificationCleanups = new Map();
787
806
  this.disconnectCleanups = new Map();
788
807
  this.notificationTokens = new Map();
@@ -818,15 +837,14 @@ class ElectronBleTransport {
818
837
  throw error;
819
838
  }
820
839
  cleanupDeviceState(deviceId) {
821
- var _a;
840
+ this.protocolV2Links
841
+ .invalidateLink(deviceId, 'Electron BLE device state cleaned')
842
+ .catch(error => { var _a; return (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] link cleanup failed:', error); });
822
843
  this.connectedDevices.delete(deviceId);
823
844
  this.deviceProtocol.delete(deviceId);
824
845
  this.v1Buffers.delete(deviceId);
825
846
  this.v2Assemblers.delete(deviceId);
826
847
  this.resetProtocolV2Frames(deviceId);
827
- if (((_a = this.activeProtocolV2Call) === null || _a === void 0 ? void 0 : _a.uuid) === deviceId) {
828
- this.activeProtocolV2Call = null;
829
- }
830
848
  this.notificationTokens.delete(deviceId);
831
849
  const notifyCleanup = this.notificationCleanups.get(deviceId);
832
850
  if (notifyCleanup) {
@@ -855,6 +873,9 @@ class ElectronBleTransport {
855
873
  configureProtocolV2(signedData) {
856
874
  var _a;
857
875
  this._messagesV2 = parseConfigure(signedData);
876
+ this.protocolV2Links
877
+ .invalidateAllLinks('Protocol V2 schema reconfigured')
878
+ .catch(error => { var _a; return (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] schema link cleanup failed:', error); });
858
879
  (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] Protocol V2 schema configured');
859
880
  }
860
881
  listen() {
@@ -893,12 +914,14 @@ class ElectronBleTransport {
893
914
  if (!uuid) {
894
915
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleRequiredUUID);
895
916
  }
917
+ if (this.connectedDevices.has(uuid)) {
918
+ yield this.release(uuid);
919
+ }
896
920
  if (forceCleanRunPromise && this.runPromise) {
897
921
  const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
898
922
  this.runPromise.reject(error);
899
923
  this.rejectAllProtocolV2Frames(error);
900
924
  this.runPromise = null;
901
- this.activeProtocolV2Call = null;
902
925
  }
903
926
  try {
904
927
  if (!((_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle)) {
@@ -966,6 +989,7 @@ class ElectronBleTransport {
966
989
  var _a, _b;
967
990
  return __awaiter(this, void 0, void 0, function* () {
968
991
  try {
992
+ yield this.protocolV2Links.invalidateLink(id, 'Electron BLE transport released');
969
993
  if (this.connectedDevices.has(id)) {
970
994
  if ((_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle) {
971
995
  yield window.desktopApi.nobleBle.unsubscribe(id);
@@ -1039,14 +1063,12 @@ class ElectronBleTransport {
1039
1063
  });
1040
1064
  }
1041
1065
  resetProbeStateAfterProtocolProbe(uuid, protocol) {
1042
- var _a, _b, _c, _d, _e, _f, _g, _h;
1066
+ var _a, _b, _c, _d, _e, _f, _g;
1043
1067
  return __awaiter(this, void 0, void 0, function* () {
1068
+ yield this.protocolV2Links.invalidateLink(uuid, `Reset notify state after Protocol ${protocol} probe`);
1044
1069
  this.v1Buffers.set(uuid, { buffer: [], bufferLength: 0 });
1045
1070
  (_a = this.v2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1046
1071
  this.resetProtocolV2Frames(uuid);
1047
- if (((_b = this.activeProtocolV2Call) === null || _b === void 0 ? void 0 : _b.uuid) === uuid) {
1048
- this.activeProtocolV2Call = null;
1049
- }
1050
1072
  if (this.runPromise) {
1051
1073
  const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
1052
1074
  this.runPromise.reject(error);
@@ -1059,16 +1081,16 @@ class ElectronBleTransport {
1059
1081
  }
1060
1082
  this.notificationTokens.delete(uuid);
1061
1083
  try {
1062
- yield ((_d = (_c = window.desktopApi) === null || _c === void 0 ? void 0 : _c.nobleBle) === null || _d === void 0 ? void 0 : _d.unsubscribe(uuid));
1084
+ yield ((_c = (_b = window.desktopApi) === null || _b === void 0 ? void 0 : _b.nobleBle) === null || _c === void 0 ? void 0 : _c.unsubscribe(uuid));
1063
1085
  }
1064
1086
  catch (error) {
1065
- (_e = this.Log) === null || _e === void 0 ? void 0 : _e.debug(`[Electron BLE] unsubscribe after Protocol ${protocol} probe failed:`, error);
1087
+ (_d = this.Log) === null || _d === void 0 ? void 0 : _d.debug(`[Electron BLE] unsubscribe after Protocol ${protocol} probe failed:`, error);
1066
1088
  }
1067
1089
  try {
1068
- yield ((_g = (_f = window.desktopApi) === null || _f === void 0 ? void 0 : _f.nobleBle) === null || _g === void 0 ? void 0 : _g.subscribe(uuid));
1090
+ yield ((_f = (_e = window.desktopApi) === null || _e === void 0 ? void 0 : _e.nobleBle) === null || _f === void 0 ? void 0 : _f.subscribe(uuid));
1069
1091
  }
1070
1092
  catch (error) {
1071
- (_h = this.Log) === null || _h === void 0 ? void 0 : _h.debug(`[Electron BLE] resubscribe after Protocol ${protocol} probe failed:`, error);
1093
+ (_g = this.Log) === null || _g === void 0 ? void 0 : _g.debug(`[Electron BLE] resubscribe after Protocol ${protocol} probe failed:`, error);
1072
1094
  throw error;
1073
1095
  }
1074
1096
  const cleanup = this.createNotificationSubscription(uuid);
@@ -1151,10 +1173,12 @@ class ElectronBleTransport {
1151
1173
  var _a, _b;
1152
1174
  if (hexData === 'PAIRING_REJECTED') {
1153
1175
  (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] Pairing rejection detected for device:', deviceId);
1154
- if (this.runPromise) {
1155
- const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceBondedCanceled);
1176
+ const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceBondedCanceled);
1177
+ if (this.deviceProtocol.get(deviceId) === 'V2') {
1178
+ this.rejectProtocolV2Frames(deviceId, error);
1179
+ }
1180
+ else if (this.runPromise) {
1156
1181
  this.runPromise.reject(error);
1157
- this.rejectAllProtocolV2Frames(error);
1158
1182
  }
1159
1183
  return;
1160
1184
  }
@@ -1170,13 +1194,8 @@ class ElectronBleTransport {
1170
1194
  this.handleProtocolV1Notification(deviceId, hexData);
1171
1195
  }
1172
1196
  handleProtocolV2Notification(deviceId, hexData) {
1173
- var _a, _b, _c;
1197
+ var _a;
1174
1198
  try {
1175
- if (!this.runPromise || ((_a = this.activeProtocolV2Call) === null || _a === void 0 ? void 0 : _a.uuid) !== deviceId) {
1176
- (_b = this.v2Assemblers.get(deviceId)) === null || _b === void 0 ? void 0 : _b.reset();
1177
- this.resetProtocolV2Frames(deviceId);
1178
- return;
1179
- }
1180
1199
  const bytes = transport.hexToBytes(hexData);
1181
1200
  if (bytes.length === 0)
1182
1201
  return;
@@ -1188,12 +1207,9 @@ class ElectronBleTransport {
1188
1207
  }
1189
1208
  }
1190
1209
  catch (error) {
1191
- (_c = this.Log) === null || _c === void 0 ? void 0 : _c.error('[Electron BLE] Protocol V2 notification error:', error);
1192
- if (this.runPromise) {
1193
- const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
1194
- this.runPromise.reject(notifyError);
1195
- this.rejectAllProtocolV2Frames(notifyError);
1196
- }
1210
+ (_a = this.Log) === null || _a === void 0 ? void 0 : _a.error('[Electron BLE] Protocol V2 notification error:', error);
1211
+ const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
1212
+ this.rejectProtocolV2Frames(deviceId, notifyError);
1197
1213
  }
1198
1214
  }
1199
1215
  getProtocolV2FrameQueue(uuid) {
@@ -1224,9 +1240,13 @@ class ElectronBleTransport {
1224
1240
  this.v2FrameQueues.delete(uuid);
1225
1241
  this.v2FramePromises.delete(uuid);
1226
1242
  }
1227
- isActiveProtocolV2Call(uuid, token) {
1228
- var _a;
1229
- return ((_a = this.activeProtocolV2Call) === null || _a === void 0 ? void 0 : _a.uuid) === uuid && this.activeProtocolV2Call.token === token;
1243
+ rejectProtocolV2Frames(uuid, error) {
1244
+ this.v2FrameQueues.delete(uuid);
1245
+ const framePromise = this.v2FramePromises.get(uuid);
1246
+ if (framePromise) {
1247
+ this.v2FramePromises.delete(uuid);
1248
+ framePromise.reject(error);
1249
+ }
1230
1250
  }
1231
1251
  readProtocolV2Frame(uuid) {
1232
1252
  return __awaiter(this, void 0, void 0, function* () {
@@ -1349,76 +1369,60 @@ class ElectronBleTransport {
1349
1369
  });
1350
1370
  }
1351
1371
  callProtocolV2(uuid, name, data, options) {
1352
- var _a, _b, _c, _d, _e;
1372
+ var _a, _b;
1353
1373
  return __awaiter(this, void 0, void 0, function* () {
1354
1374
  if (!this._messages || !this._messagesV2) {
1355
1375
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
1356
1376
  }
1357
- const forceRun = name === 'Initialize' || name === 'Cancel' || name === 'Ping';
1358
- if (this.runPromise) {
1359
- if (!forceRun) {
1360
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportCallInProgress);
1361
- }
1362
- const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
1363
- this.runPromise.reject(error);
1364
- this.rejectAllProtocolV2Frames(error);
1365
- this.runPromise = null;
1366
- this.activeProtocolV2Call = null;
1367
- }
1368
- const runPromise = hdShared.createDeferred();
1369
- runPromise.promise.catch(() => undefined);
1370
- this.runPromise = runPromise;
1371
- const callToken = this.nextProtocolV2CallToken++;
1372
- this.activeProtocolV2Call = { uuid, token: callToken };
1373
- (_a = this.v2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1374
- this.resetProtocolV2Frames(uuid);
1375
- let completed = false;
1376
- 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 });
1377
+ 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 });
1377
1378
  try {
1378
- const session = new transport.ProtocolV2Session({
1379
- schemas: {
1380
- protocolV1: this._messages,
1381
- protocolV2: this._messagesV2,
1382
- },
1383
- router: transport.PROTOCOL_V2_CHANNEL_BLE_UART,
1384
- writeFrame: (frame) => this.writeWithChunking(uuid, transport.bytesToHex(frame)),
1385
- readFrame: () => __awaiter(this, void 0, void 0, function* () {
1386
- const rxFrame = yield this.readProtocolV2Frame(uuid);
1387
- if (!(rxFrame instanceof Uint8Array)) {
1388
- throw new Error('Response is not Uint8Array');
1389
- }
1390
- return rxFrame;
1391
- }),
1392
- logger: this.Log,
1393
- logPrefix: 'ProtocolV2 BLE',
1394
- createTimeoutError: (_messageName, timeout) => hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, `BLE response timeout after ${timeout}ms for ${name}`),
1395
- });
1396
- const result = yield session.call(name, data, callOptions);
1397
- completed = true;
1398
- return result;
1379
+ return yield this.protocolV2Links.call(uuid, () => this.createProtocolV2Adapter(uuid), name, data, callOptions);
1399
1380
  }
1400
1381
  catch (e) {
1401
- if (this.isActiveProtocolV2Call(uuid, callToken)) {
1402
- (_c = this.v2Assemblers.get(uuid)) === null || _c === void 0 ? void 0 : _c.reset();
1403
- this.resetProtocolV2Frames(uuid);
1404
- }
1405
- (_d = this.Log) === null || _d === void 0 ? void 0 : _d.error('[Electron BLE] Protocol V2 call error:', e);
1382
+ (_b = this.Log) === null || _b === void 0 ? void 0 : _b.error('[Electron BLE] Protocol V2 call error:', e);
1406
1383
  throw e;
1407
1384
  }
1408
- finally {
1409
- if (this.isActiveProtocolV2Call(uuid, callToken)) {
1410
- if (!completed) {
1411
- (_e = this.v2Assemblers.get(uuid)) === null || _e === void 0 ? void 0 : _e.reset();
1412
- }
1413
- this.resetProtocolV2Frames(uuid);
1414
- this.activeProtocolV2Call = null;
1415
- }
1416
- if (this.runPromise === runPromise) {
1417
- this.runPromise = null;
1418
- }
1419
- }
1420
1385
  });
1421
1386
  }
1387
+ createProtocolV2Adapter(uuid) {
1388
+ var _a;
1389
+ const generation = (_a = this.notificationTokens.get(uuid)) !== null && _a !== void 0 ? _a : 0;
1390
+ const assertCurrentGeneration = () => {
1391
+ if (this.notificationTokens.get(uuid) !== generation) {
1392
+ throw new Error(`Protocol V2 notification generation changed for ${uuid}`);
1393
+ }
1394
+ };
1395
+ return {
1396
+ router: transport.PROTOCOL_V2_CHANNEL_BLE_UART,
1397
+ generation,
1398
+ prepareCall: () => {
1399
+ var _a;
1400
+ assertCurrentGeneration();
1401
+ (_a = this.v2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1402
+ this.resetProtocolV2Frames(uuid);
1403
+ },
1404
+ writeFrame: (frame) => {
1405
+ assertCurrentGeneration();
1406
+ return this.writeWithChunking(uuid, transport.bytesToHex(frame));
1407
+ },
1408
+ readFrame: () => __awaiter(this, void 0, void 0, function* () {
1409
+ assertCurrentGeneration();
1410
+ const rxFrame = yield this.readProtocolV2Frame(uuid);
1411
+ if (!(rxFrame instanceof Uint8Array)) {
1412
+ throw new Error('Response is not Uint8Array');
1413
+ }
1414
+ return rxFrame;
1415
+ }),
1416
+ reset: (reason) => {
1417
+ var _a;
1418
+ (_a = this.v2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1419
+ this.rejectProtocolV2Frames(uuid, new Error(reason));
1420
+ },
1421
+ logger: this.Log,
1422
+ logPrefix: 'ProtocolV2 BLE',
1423
+ createTimeoutError: (messageName, timeout) => hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleTimeoutError, `BLE response timeout after ${timeout}ms for ${messageName}`),
1424
+ };
1425
+ }
1422
1426
  processProtocolV1Notification(deviceId, hexData) {
1423
1427
  try {
1424
1428
  if (typeof hexData !== 'string') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-transport-web-device",
3
- "version": "1.2.0-alpha.8",
3
+ "version": "1.2.0-alpha.9",
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.8",
24
- "@onekeyfe/hd-transport": "1.2.0-alpha.8"
23
+ "@onekeyfe/hd-shared": "1.2.0-alpha.9",
24
+ "@onekeyfe/hd-transport": "1.2.0-alpha.9"
25
25
  },
26
26
  "devDependencies": {
27
- "@onekeyfe/hd-transport-electron": "1.2.0-alpha.8",
27
+ "@onekeyfe/hd-transport-electron": "1.2.0-alpha.9",
28
28
  "@types/w3c-web-usb": "^1.0.6",
29
29
  "@types/web-bluetooth": "^0.0.17"
30
30
  },
31
- "gitHead": "d2b1323e5ad9c373e72d4d092f35c9f274c1fd74"
31
+ "gitHead": "f90ef9921b742f68cce9f4e7a0425e45e8ae7ec9"
32
32
  }
@@ -3,7 +3,7 @@ import transport, {
3
3
  PROTOCOL_V1_MESSAGE_HEADER_SIZE,
4
4
  PROTOCOL_V2_CHANNEL_BLE_UART,
5
5
  ProtocolV2FrameAssembler,
6
- ProtocolV2Session,
6
+ ProtocolV2LinkManager,
7
7
  bytesToHex,
8
8
  hexToBytes,
9
9
  probeProtocolV2 as probeProtocolV2Helper,
@@ -111,9 +111,26 @@ export default class ElectronBleTransport {
111
111
 
112
112
  private v2FramePromises: Map<string, Deferred<Uint8Array>> = new Map();
113
113
 
114
- private activeProtocolV2Call: { uuid: string; token: number } | null = null;
115
-
116
- private nextProtocolV2CallToken = 1;
114
+ private protocolV2Links = new ProtocolV2LinkManager<string>({
115
+ getSchemas: () => {
116
+ if (!this._messages || !this._messagesV2) {
117
+ throw ERRORS.TypedError(HardwareErrorCode.TransportNotConfigured);
118
+ }
119
+ return {
120
+ protocolV1: this._messages,
121
+ protocolV2: this._messagesV2,
122
+ };
123
+ },
124
+ classifyError: () => 'link-fatal',
125
+ onLinkInvalidated: async (uuid, reason) => {
126
+ this.v2Assemblers.get(uuid)?.reset();
127
+ this.rejectProtocolV2Frames(uuid, new Error(reason));
128
+ this.Log?.debug('[Electron BLE] Protocol V2 link invalidated:', uuid, reason);
129
+ if (reason.startsWith('Protocol V2 link-fatal error:')) {
130
+ await this.release(uuid);
131
+ }
132
+ },
133
+ });
117
134
 
118
135
  private notificationCleanups: Map<string, () => void> = new Map();
119
136
 
@@ -155,6 +172,9 @@ export default class ElectronBleTransport {
155
172
  }
156
173
 
157
174
  private cleanupDeviceState(deviceId: string): void {
175
+ this.protocolV2Links
176
+ .invalidateLink(deviceId, 'Electron BLE device state cleaned')
177
+ .catch(error => this.Log?.debug('[Electron BLE] link cleanup failed:', error));
158
178
  this.connectedDevices.delete(deviceId);
159
179
  this.deviceProtocol.delete(deviceId);
160
180
  // Keep deviceProtocolHints — it's inferred from device name (e.g. "Pro 2" → V2)
@@ -162,9 +182,6 @@ export default class ElectronBleTransport {
162
182
  this.v1Buffers.delete(deviceId);
163
183
  this.v2Assemblers.delete(deviceId);
164
184
  this.resetProtocolV2Frames(deviceId);
165
- if (this.activeProtocolV2Call?.uuid === deviceId) {
166
- this.activeProtocolV2Call = null;
167
- }
168
185
  this.notificationTokens.delete(deviceId);
169
186
 
170
187
  const notifyCleanup = this.notificationCleanups.get(deviceId);
@@ -201,6 +218,9 @@ export default class ElectronBleTransport {
201
218
 
202
219
  configureProtocolV2(signedData: any) {
203
220
  this._messagesV2 = parseConfigure(signedData);
221
+ this.protocolV2Links
222
+ .invalidateAllLinks('Protocol V2 schema reconfigured')
223
+ .catch(error => this.Log?.debug('[Electron BLE] schema link cleanup failed:', error));
204
224
  this.Log?.debug('[Electron BLE] Protocol V2 schema configured');
205
225
  }
206
226
 
@@ -236,12 +256,15 @@ export default class ElectronBleTransport {
236
256
  throw ERRORS.TypedError(HardwareErrorCode.BleRequiredUUID);
237
257
  }
238
258
 
259
+ if (this.connectedDevices.has(uuid)) {
260
+ await this.release(uuid);
261
+ }
262
+
239
263
  if (forceCleanRunPromise && this.runPromise) {
240
264
  const error = ERRORS.TypedError(HardwareErrorCode.BleForceCleanRunPromise);
241
265
  this.runPromise.reject(error);
242
266
  this.rejectAllProtocolV2Frames(error);
243
267
  this.runPromise = null;
244
- this.activeProtocolV2Call = null;
245
268
  }
246
269
 
247
270
  try {
@@ -318,6 +341,7 @@ export default class ElectronBleTransport {
318
341
 
319
342
  async release(id: string) {
320
343
  try {
344
+ await this.protocolV2Links.invalidateLink(id, 'Electron BLE transport released');
321
345
  if (this.connectedDevices.has(id)) {
322
346
  if (window.desktopApi?.nobleBle) {
323
347
  await window.desktopApi.nobleBle.unsubscribe(id);
@@ -415,12 +439,13 @@ export default class ElectronBleTransport {
415
439
  }
416
440
 
417
441
  private async resetProbeStateAfterProtocolProbe(uuid: string, protocol: ProtocolType) {
442
+ await this.protocolV2Links.invalidateLink(
443
+ uuid,
444
+ `Reset notify state after Protocol ${protocol} probe`
445
+ );
418
446
  this.v1Buffers.set(uuid, { buffer: [], bufferLength: 0 });
419
447
  this.v2Assemblers.get(uuid)?.reset();
420
448
  this.resetProtocolV2Frames(uuid);
421
- if (this.activeProtocolV2Call?.uuid === uuid) {
422
- this.activeProtocolV2Call = null;
423
- }
424
449
  if (this.runPromise) {
425
450
  const error = ERRORS.TypedError(HardwareErrorCode.BleForceCleanRunPromise);
426
451
  this.runPromise.reject(error);
@@ -523,10 +548,11 @@ export default class ElectronBleTransport {
523
548
  private handleNotification(deviceId: string, hexData: string): void {
524
549
  if (hexData === 'PAIRING_REJECTED') {
525
550
  this.Log?.debug('[Electron BLE] Pairing rejection detected for device:', deviceId);
526
- if (this.runPromise) {
527
- const error = ERRORS.TypedError(HardwareErrorCode.BleDeviceBondedCanceled);
551
+ const error = ERRORS.TypedError(HardwareErrorCode.BleDeviceBondedCanceled);
552
+ if (this.deviceProtocol.get(deviceId) === 'V2') {
553
+ this.rejectProtocolV2Frames(deviceId, error);
554
+ } else if (this.runPromise) {
528
555
  this.runPromise.reject(error);
529
- this.rejectAllProtocolV2Frames(error);
530
556
  }
531
557
  return;
532
558
  }
@@ -545,12 +571,6 @@ export default class ElectronBleTransport {
545
571
 
546
572
  private handleProtocolV2Notification(deviceId: string, hexData: string): void {
547
573
  try {
548
- if (!this.runPromise || this.activeProtocolV2Call?.uuid !== deviceId) {
549
- this.v2Assemblers.get(deviceId)?.reset();
550
- this.resetProtocolV2Frames(deviceId);
551
- return;
552
- }
553
-
554
574
  const bytes = hexToBytes(hexData);
555
575
  if (bytes.length === 0) return;
556
576
 
@@ -562,11 +582,8 @@ export default class ElectronBleTransport {
562
582
  }
563
583
  } catch (error) {
564
584
  this.Log?.error('[Electron BLE] Protocol V2 notification error:', error);
565
- if (this.runPromise) {
566
- const notifyError = ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError);
567
- this.runPromise.reject(notifyError);
568
- this.rejectAllProtocolV2Frames(notifyError);
569
- }
585
+ const notifyError = ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError);
586
+ this.rejectProtocolV2Frames(deviceId, notifyError);
570
587
  }
571
588
  }
572
589
 
@@ -602,8 +619,13 @@ export default class ElectronBleTransport {
602
619
  this.v2FramePromises.delete(uuid);
603
620
  }
604
621
 
605
- private isActiveProtocolV2Call(uuid: string, token: number) {
606
- return this.activeProtocolV2Call?.uuid === uuid && this.activeProtocolV2Call.token === token;
622
+ private rejectProtocolV2Frames(uuid: string, error: Error) {
623
+ this.v2FrameQueues.delete(uuid);
624
+ const framePromise = this.v2FramePromises.get(uuid);
625
+ if (framePromise) {
626
+ this.v2FramePromises.delete(uuid);
627
+ framePromise.reject(error);
628
+ }
607
629
  }
608
630
 
609
631
  private async readProtocolV2Frame(uuid: string) {
@@ -755,79 +777,67 @@ export default class ElectronBleTransport {
755
777
  throw ERRORS.TypedError(HardwareErrorCode.TransportNotConfigured);
756
778
  }
757
779
 
758
- const forceRun = name === 'Initialize' || name === 'Cancel' || name === 'Ping';
759
- if (this.runPromise) {
760
- if (!forceRun) {
761
- throw ERRORS.TypedError(HardwareErrorCode.TransportCallInProgress);
762
- }
763
- const error = ERRORS.TypedError(HardwareErrorCode.BleForceCleanRunPromise);
764
- this.runPromise.reject(error);
765
- this.rejectAllProtocolV2Frames(error);
766
- this.runPromise = null;
767
- this.activeProtocolV2Call = null;
768
- }
769
-
770
- const runPromise = createDeferred<Uint8Array | string>();
771
- runPromise.promise.catch(() => undefined);
772
- this.runPromise = runPromise;
773
- const callToken = this.nextProtocolV2CallToken++;
774
- this.activeProtocolV2Call = { uuid, token: callToken };
775
- this.v2Assemblers.get(uuid)?.reset();
776
- this.resetProtocolV2Frames(uuid);
777
- let completed = false;
778
780
  const callOptions = {
779
781
  ...options,
780
782
  timeoutMs: options?.timeoutMs ?? BLE_RESPONSE_TIMEOUT_MS,
781
783
  };
782
784
 
783
785
  try {
784
- const session = new ProtocolV2Session({
785
- schemas: {
786
- protocolV1: this._messages,
787
- protocolV2: this._messagesV2,
788
- },
789
- router: PROTOCOL_V2_CHANNEL_BLE_UART,
790
- writeFrame: (frame: Uint8Array) => this.writeWithChunking(uuid, bytesToHex(frame)),
791
- readFrame: async () => {
792
- const rxFrame = await this.readProtocolV2Frame(uuid);
793
- if (!(rxFrame instanceof Uint8Array)) {
794
- throw new Error('Response is not Uint8Array');
795
- }
796
- return rxFrame;
797
- },
798
- logger: this.Log,
799
- logPrefix: 'ProtocolV2 BLE',
800
- createTimeoutError: (_messageName: string, timeout: number) =>
801
- ERRORS.TypedError(
802
- HardwareErrorCode.BleTimeoutError,
803
- `BLE response timeout after ${timeout}ms for ${name}`
804
- ),
805
- });
806
-
807
- const result = await session.call(name, data, callOptions);
808
- completed = true;
809
- return result;
786
+ return await this.protocolV2Links.call(
787
+ uuid,
788
+ () => this.createProtocolV2Adapter(uuid),
789
+ name,
790
+ data,
791
+ callOptions
792
+ );
810
793
  } catch (e) {
811
- if (this.isActiveProtocolV2Call(uuid, callToken)) {
812
- this.v2Assemblers.get(uuid)?.reset();
813
- this.resetProtocolV2Frames(uuid);
814
- }
815
794
  this.Log?.error('[Electron BLE] Protocol V2 call error:', e);
816
795
  throw e;
817
- } finally {
818
- if (this.isActiveProtocolV2Call(uuid, callToken)) {
819
- if (!completed) {
820
- this.v2Assemblers.get(uuid)?.reset();
821
- }
822
- this.resetProtocolV2Frames(uuid);
823
- this.activeProtocolV2Call = null;
824
- }
825
- if (this.runPromise === runPromise) {
826
- this.runPromise = null;
827
- }
828
796
  }
829
797
  }
830
798
 
799
+ private createProtocolV2Adapter(uuid: string) {
800
+ const generation = this.notificationTokens.get(uuid) ?? 0;
801
+ const assertCurrentGeneration = () => {
802
+ if (this.notificationTokens.get(uuid) !== generation) {
803
+ throw new Error(`Protocol V2 notification generation changed for ${uuid}`);
804
+ }
805
+ };
806
+
807
+ return {
808
+ router: PROTOCOL_V2_CHANNEL_BLE_UART,
809
+ generation,
810
+ prepareCall: () => {
811
+ assertCurrentGeneration();
812
+ this.v2Assemblers.get(uuid)?.reset();
813
+ this.resetProtocolV2Frames(uuid);
814
+ },
815
+ writeFrame: (frame: Uint8Array) => {
816
+ assertCurrentGeneration();
817
+ return this.writeWithChunking(uuid, bytesToHex(frame));
818
+ },
819
+ readFrame: async () => {
820
+ assertCurrentGeneration();
821
+ const rxFrame = await this.readProtocolV2Frame(uuid);
822
+ if (!(rxFrame instanceof Uint8Array)) {
823
+ throw new Error('Response is not Uint8Array');
824
+ }
825
+ return rxFrame;
826
+ },
827
+ reset: (reason: string) => {
828
+ this.v2Assemblers.get(uuid)?.reset();
829
+ this.rejectProtocolV2Frames(uuid, new Error(reason));
830
+ },
831
+ logger: this.Log,
832
+ logPrefix: 'ProtocolV2 BLE',
833
+ createTimeoutError: (messageName: string, timeout: number) =>
834
+ ERRORS.TypedError(
835
+ HardwareErrorCode.BleTimeoutError,
836
+ `BLE response timeout after ${timeout}ms for ${messageName}`
837
+ ),
838
+ };
839
+ }
840
+
831
841
  private processProtocolV1Notification(deviceId: string, hexData: string): PacketProcessResult {
832
842
  try {
833
843
  if (typeof hexData !== 'string') {