@onekeyfe/hd-transport-web-device 1.1.27-alpha.30 → 1.1.27-alpha.32

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.
@@ -23,12 +23,17 @@ export default class ElectronAutoBleTransport {
23
23
  emitter?: EventEmitter;
24
24
  private connectedDevices;
25
25
  private deviceProtocol;
26
+ private deviceProtocolHints;
26
27
  private v1Buffers;
27
28
  private v2Assemblers;
28
- private v2FrameQueue;
29
- private v2FramePromise;
29
+ private v2FrameQueues;
30
+ private v2FramePromises;
31
+ private activeProtocolV2Call;
32
+ private nextProtocolV2CallToken;
30
33
  private notificationCleanups;
31
34
  private disconnectCleanups;
35
+ private notificationTokens;
36
+ private nextNotificationToken;
32
37
  private handleBluetoothError;
33
38
  private cleanupDeviceState;
34
39
  init(logger: any, emitter?: EventEmitter): void;
@@ -50,15 +55,19 @@ export default class ElectronAutoBleTransport {
50
55
  release(id: string): Promise<void>;
51
56
  private createProtocolMismatchError;
52
57
  private detectProtocol;
58
+ private createNotificationSubscription;
59
+ private resetProbeStateAfterProtocolProbe;
53
60
  private probeProtocolV1;
54
61
  private probeProtocolV2;
55
62
  private writeWithChunking;
56
63
  private writeWithRetry;
57
64
  private handleNotification;
58
65
  private handleProtocolV2Notification;
66
+ private getProtocolV2FrameQueue;
59
67
  private resolveProtocolV2Frame;
60
- private rejectProtocolV2Frame;
68
+ private rejectAllProtocolV2Frames;
61
69
  private resetProtocolV2Frames;
70
+ private isActiveProtocolV2Call;
62
71
  private readProtocolV2Frame;
63
72
  private handleProtocolV1Notification;
64
73
  call(uuid: string, name: string, data: Record<string, unknown>, options?: TransportCallOptions): Promise<import("@onekeyfe/hd-transport").MessageFromOneKey>;
@@ -1 +1 @@
1
- {"version":3,"file":"electron-auto-ble-transport.d.ts","sourceRoot":"","sources":["../src/electron-auto-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;AAWvC,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;AAyCF,MAAM,CAAC,OAAO,OAAO,wBAAwB;IAC3C,OAAO,CAAC,SAAS,CAA0D;IAE3E,OAAO,CAAC,WAAW,CAA0D;IAE7E,IAAI,SAA8B;IAElC,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,SAAS,CAAsE;IAEvF,OAAO,CAAC,YAAY,CAAoD;IAExE,OAAO,CAAC,YAAY,CAAoB;IAExC,OAAO,CAAC,cAAc,CAAqC;IAE3D,OAAO,CAAC,oBAAoB,CAAsC;IAElE,OAAO,CAAC,kBAAkB,CAAsC;IAEhE,OAAO,CAAC,oBAAoB;IA+B5B,OAAO,CAAC,kBAAkB;IAmB1B,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;IAiBxC,OAAO,CAAC,KAAK,EAAE,eAAe;;;;;;;;;;;IAqF9B,OAAO,CAAC,EAAE,EAAE,MAAM;IAexB,OAAO,CAAC,2BAA2B;YAOrB,cAAc;YAqCd,eAAe;YAef,eAAe;YAmBf,iBAAiB;YAsBjB,cAAc;IA4B5B,OAAO,CAAC,kBAAkB;IAmB1B,OAAO,CAAC,4BAA4B;IA6BpC,OAAO,CAAC,sBAAsB;IAS9B,OAAO,CAAC,qBAAqB;IAQ7B,OAAO,CAAC,qBAAqB;YAKf,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;YAyBlB,cAAc;YAuEd,cAAc;IA2E5B,OAAO,CAAC,6BAA6B;IAgDrC,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY;CAG5C"}
1
+ {"version":3,"file":"electron-auto-ble-transport.d.ts","sourceRoot":"","sources":["../src/electron-auto-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;AAWvC,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;AAsCF,MAAM,CAAC,OAAO,OAAO,wBAAwB;IAC3C,OAAO,CAAC,SAAS,CAA0D;IAE3E,OAAO,CAAC,WAAW,CAA0D;IAE7E,IAAI,SAA8B;IAElC,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;IAyB1B,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;YAOrB,cAAc;IAgD5B,OAAO,CAAC,8BAA8B;YAgBxB,iCAAiC;YAoCjC,eAAe;YAef,eAAe;YAmBf,iBAAiB;YAsBjB,cAAc;IA4B5B,OAAO,CAAC,kBAAkB;IAmB1B,OAAO,CAAC,4BAA4B;IA6BpC,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;YAyBlB,cAAc;YAuEd,cAAc;IAmF5B,OAAO,CAAC,6BAA6B;IAgDrC,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY;CAG5C"}
package/dist/index.d.ts CHANGED
@@ -13,6 +13,7 @@ declare class WebUsbTransport {
13
13
  messages: ReturnType<typeof _onekeyfe_hd_transport__default.parseConfigure> | undefined;
14
14
  messagesV2: ReturnType<typeof _onekeyfe_hd_transport__default.parseConfigure> | undefined;
15
15
  private deviceProtocol;
16
+ private deviceProtocolHints;
16
17
  private protocolV2Assemblers;
17
18
  private deviceEndpoints;
18
19
  name: string;
@@ -118,12 +119,17 @@ declare class ElectronAutoBleTransport {
118
119
  emitter?: EventEmitter;
119
120
  private connectedDevices;
120
121
  private deviceProtocol;
122
+ private deviceProtocolHints;
121
123
  private v1Buffers;
122
124
  private v2Assemblers;
123
- private v2FrameQueue;
124
- private v2FramePromise;
125
+ private v2FrameQueues;
126
+ private v2FramePromises;
127
+ private activeProtocolV2Call;
128
+ private nextProtocolV2CallToken;
125
129
  private notificationCleanups;
126
130
  private disconnectCleanups;
131
+ private notificationTokens;
132
+ private nextNotificationToken;
127
133
  private handleBluetoothError;
128
134
  private cleanupDeviceState;
129
135
  init(logger: any, emitter?: EventEmitter): void;
@@ -145,15 +151,19 @@ declare class ElectronAutoBleTransport {
145
151
  release(id: string): Promise<void>;
146
152
  private createProtocolMismatchError;
147
153
  private detectProtocol;
154
+ private createNotificationSubscription;
155
+ private resetProbeStateAfterProtocolProbe;
148
156
  private probeProtocolV1;
149
157
  private probeProtocolV2;
150
158
  private writeWithChunking;
151
159
  private writeWithRetry;
152
160
  private handleNotification;
153
161
  private handleProtocolV2Notification;
162
+ private getProtocolV2FrameQueue;
154
163
  private resolveProtocolV2Frame;
155
- private rejectProtocolV2Frame;
164
+ private rejectAllProtocolV2Frames;
156
165
  private resetProtocolV2Frames;
166
+ private isActiveProtocolV2Call;
157
167
  private readProtocolV2Frame;
158
168
  private handleProtocolV1Notification;
159
169
  call(uuid: string, name: string, data: Record<string, unknown>, options?: TransportCallOptions): Promise<_onekeyfe_hd_transport.MessageFromOneKey>;
package/dist/index.js CHANGED
@@ -61,12 +61,13 @@ function shouldBlockWebUsbCallDataLog(name) {
61
61
  const normalized = name.replace(/[_\s-]/g, '');
62
62
  return transport.LogBlockCommand.has(name) || WEBUSB_FILE_WRITE_LOG_BLOCK_PATTERN.test(normalized);
63
63
  }
64
- function inferProtocolTypeFromDeviceName$1(name) {
64
+ function inferProtocolHintFromDeviceName$1(name) {
65
65
  return /\bpro\s*2\b/i.test(name !== null && name !== void 0 ? name : '') ? 'V2' : undefined;
66
66
  }
67
67
  class WebUsbTransport {
68
68
  constructor() {
69
69
  this.deviceProtocol = new Map();
70
+ this.deviceProtocolHints = new Map();
70
71
  this.protocolV2Assemblers = new Map();
71
72
  this.deviceEndpoints = new Map();
72
73
  this.name = 'WebUsbTransport';
@@ -125,12 +126,18 @@ class WebUsbTransport {
125
126
  const hasSerialNumber = typeof dev.serialNumber === 'string' && dev.serialNumber.length > 0;
126
127
  return isOneKey && hasSerialNumber;
127
128
  });
128
- this.deviceList = onekeyDevices.map(device => ({
129
- path: device.serialNumber,
130
- device,
131
- commType: 'webusb',
132
- protocolType: inferProtocolTypeFromDeviceName$1(device.productName),
133
- }));
129
+ this.deviceList = onekeyDevices.map(device => {
130
+ const path = device.serialNumber;
131
+ const protocolHint = inferProtocolHintFromDeviceName$1(device.productName);
132
+ if (protocolHint) {
133
+ this.deviceProtocolHints.set(path, protocolHint);
134
+ }
135
+ return {
136
+ path,
137
+ device,
138
+ commType: 'webusb',
139
+ };
140
+ });
134
141
  for (const dev of onekeyDevices) {
135
142
  this.Log.debug(`[WebUSB] Device: name="${dev.productName}" serial="${dev.serialNumber}" ` +
136
143
  `VID=0x${dev.vendorId.toString(16)} PID=0x${dev.productId.toString(16)}`);
@@ -139,13 +146,20 @@ class WebUsbTransport {
139
146
  });
140
147
  }
141
148
  acquire(input) {
142
- var _a;
149
+ var _a, _b, _c;
143
150
  return __awaiter(this, void 0, void 0, function* () {
144
151
  if (!input.path)
145
152
  return;
146
153
  try {
147
154
  yield this.connect((_a = input.path) !== null && _a !== void 0 ? _a : '', true);
148
- yield this.detectProtocol(input.path, input.expectedProtocol);
155
+ const deviceName = (_b = this.deviceList.find(device => device.path === input.path)) === null || _b === void 0 ? void 0 : _b.device.productName;
156
+ const protocolHint = input.expectedProtocol
157
+ ? undefined
158
+ : (_c = this.deviceProtocolHints.get(input.path)) !== null && _c !== void 0 ? _c : inferProtocolHintFromDeviceName$1(deviceName);
159
+ if (protocolHint) {
160
+ this.deviceProtocolHints.set(input.path, protocolHint);
161
+ }
162
+ yield this.detectProtocol(input.path, input.expectedProtocol, protocolHint);
149
163
  return yield Promise.resolve(input.path);
150
164
  }
151
165
  catch (e) {
@@ -157,8 +171,7 @@ class WebUsbTransport {
157
171
  createProtocolMismatchError(expected) {
158
172
  return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
159
173
  }
160
- detectProtocol(path, expectedProtocol) {
161
- var _a;
174
+ detectProtocol(path, expectedProtocol, protocolHint) {
162
175
  return __awaiter(this, void 0, void 0, function* () {
163
176
  if (expectedProtocol === 'V1') {
164
177
  if (yield this.probeProtocolV1(path)) {
@@ -169,15 +182,6 @@ class WebUsbTransport {
169
182
  throw this.createProtocolMismatchError(expectedProtocol);
170
183
  }
171
184
  if (expectedProtocol === 'V2') {
172
- if (this.deviceProtocol.get(path) === 'V2') {
173
- this.Log.debug(`[WebUsbTransport] detectProtocol: path=${path} -> V2 (cached expected)`);
174
- return 'V2';
175
- }
176
- if (((_a = this.deviceList.find(device => device.path === path)) === null || _a === void 0 ? void 0 : _a.protocolType) === 'V2') {
177
- this.deviceProtocol.set(path, 'V2');
178
- this.Log.debug(`[WebUsbTransport] detectProtocol: path=${path} -> V2 (descriptor)`);
179
- return 'V2';
180
- }
181
185
  if (yield this.probeProtocolV2(path)) {
182
186
  this.deviceProtocol.set(path, 'V2');
183
187
  this.Log.debug(`[WebUsbTransport] detectProtocol: path=${path} -> V2 (expected)`);
@@ -185,6 +189,11 @@ class WebUsbTransport {
185
189
  }
186
190
  throw this.createProtocolMismatchError(expectedProtocol);
187
191
  }
192
+ if (protocolHint === 'V2' && (yield this.probeProtocolV2(path))) {
193
+ this.deviceProtocol.set(path, 'V2');
194
+ this.Log.debug(`[WebUsbTransport] detectProtocol: path=${path} -> V2 (hint)`);
195
+ return 'V2';
196
+ }
188
197
  if (this.deviceProtocol.get(path) === 'V2' && (yield this.probeProtocolV2(path))) {
189
198
  this.deviceProtocol.set(path, 'V2');
190
199
  this.Log.debug(`[WebUsbTransport] detectProtocol: path=${path} -> V2 (cached)`);
@@ -647,13 +656,16 @@ class WebUsbTransport {
647
656
  });
648
657
  }
649
658
  release(path) {
650
- var _a;
659
+ var _a, _b;
651
660
  return __awaiter(this, void 0, void 0, function* () {
652
661
  const device = yield this.findDevice(path);
653
662
  const endpoints = this.deviceEndpoints.get(path);
654
663
  const ifaceNum = (_a = endpoints === null || endpoints === void 0 ? void 0 : endpoints.interfaceNumber) !== null && _a !== void 0 ? _a : this.interfaceId;
655
664
  yield device.releaseInterface(ifaceNum);
656
665
  yield device.close();
666
+ this.deviceProtocol.delete(path);
667
+ this.deviceProtocolHints.delete(path);
668
+ (_b = this.protocolV2Assemblers.get(path)) === null || _b === void 0 ? void 0 : _b.reset();
657
669
  this.protocolV2Assemblers.delete(path);
658
670
  this.deviceEndpoints.delete(path);
659
671
  });
@@ -963,13 +975,10 @@ function shouldSuppressHighVolumeCallLog(name) {
963
975
  return FILE_WRITE_LOG_BLOCK_PATTERN.test(normalized);
964
976
  }
965
977
  const { parseConfigure, ProtocolV1, check } = transport__default["default"];
966
- function inferProtocolTypeFromDeviceName(name) {
978
+ function inferProtocolHintFromDeviceName(name) {
967
979
  return /\bpro\s*2\b/i.test(name !== null && name !== void 0 ? name : '') ? 'V2' : undefined;
968
980
  }
969
- const toBleDescriptor = (device, protocolType) => {
970
- const resolvedProtocolType = protocolType !== null && protocolType !== void 0 ? protocolType : inferProtocolTypeFromDeviceName(device.name);
971
- return Object.assign({ id: device.id, name: device.name, path: device.id, debug: false, commType: 'electron-ble' }, (resolvedProtocolType ? { protocolType: resolvedProtocolType } : {}));
972
- };
981
+ const toBleDescriptor = (device, protocolType) => (Object.assign({ id: device.id, name: device.name, path: device.id, debug: false, commType: 'electron-ble' }, (protocolType ? { protocolType } : {})));
973
982
  const BLE_PACKET_SIZE = 192;
974
983
  const BLE_WRITE_DELAY_MS = 5;
975
984
  const BLE_WRITE_MAX_RETRIES = 3;
@@ -983,12 +992,17 @@ class ElectronAutoBleTransport {
983
992
  this.runPromise = null;
984
993
  this.connectedDevices = new Set();
985
994
  this.deviceProtocol = new Map();
995
+ this.deviceProtocolHints = new Map();
986
996
  this.v1Buffers = new Map();
987
997
  this.v2Assemblers = new Map();
988
- this.v2FrameQueue = [];
989
- this.v2FramePromise = null;
998
+ this.v2FrameQueues = new Map();
999
+ this.v2FramePromises = new Map();
1000
+ this.activeProtocolV2Call = null;
1001
+ this.nextProtocolV2CallToken = 1;
990
1002
  this.notificationCleanups = new Map();
991
1003
  this.disconnectCleanups = new Map();
1004
+ this.notificationTokens = new Map();
1005
+ this.nextNotificationToken = 1;
992
1006
  }
993
1007
  handleBluetoothError(error) {
994
1008
  if (error && typeof error === 'object') {
@@ -1020,10 +1034,17 @@ class ElectronAutoBleTransport {
1020
1034
  throw error;
1021
1035
  }
1022
1036
  cleanupDeviceState(deviceId) {
1037
+ var _a;
1023
1038
  this.connectedDevices.delete(deviceId);
1024
1039
  this.deviceProtocol.delete(deviceId);
1040
+ this.deviceProtocolHints.delete(deviceId);
1025
1041
  this.v1Buffers.delete(deviceId);
1026
1042
  this.v2Assemblers.delete(deviceId);
1043
+ this.resetProtocolV2Frames(deviceId);
1044
+ if (((_a = this.activeProtocolV2Call) === null || _a === void 0 ? void 0 : _a.uuid) === deviceId) {
1045
+ this.activeProtocolV2Call = null;
1046
+ }
1047
+ this.notificationTokens.delete(deviceId);
1027
1048
  const notifyCleanup = this.notificationCleanups.get(deviceId);
1028
1049
  if (notifyCleanup) {
1029
1050
  notifyCleanup();
@@ -1069,6 +1090,10 @@ class ElectronAutoBleTransport {
1069
1090
  (_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug(`[Auto BLE] enumerate found ${devices.length} device(s):`);
1070
1091
  for (const dev of devices) {
1071
1092
  (_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug(`[Auto BLE] id="${dev.id}" name="${dev.name}"`);
1093
+ const protocolHint = inferProtocolHintFromDeviceName(dev.name);
1094
+ if (protocolHint) {
1095
+ this.deviceProtocolHints.set(dev.id, protocolHint);
1096
+ }
1072
1097
  }
1073
1098
  return devices.map(device => toBleDescriptor(device));
1074
1099
  }
@@ -1079,7 +1104,7 @@ class ElectronAutoBleTransport {
1079
1104
  });
1080
1105
  }
1081
1106
  acquire(input) {
1082
- var _a, _b, _c, _d, _e;
1107
+ var _a, _b, _c, _d, _e, _f;
1083
1108
  return __awaiter(this, void 0, void 0, function* () {
1084
1109
  const { uuid, forceCleanRunPromise, expectedProtocol } = input;
1085
1110
  if (!uuid) {
@@ -1088,7 +1113,9 @@ class ElectronAutoBleTransport {
1088
1113
  if (forceCleanRunPromise && this.runPromise) {
1089
1114
  const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
1090
1115
  this.runPromise.reject(error);
1091
- this.rejectProtocolV2Frame(error);
1116
+ this.rejectAllProtocolV2Frames(error);
1117
+ this.runPromise = null;
1118
+ this.activeProtocolV2Call = null;
1092
1119
  }
1093
1120
  try {
1094
1121
  if (!((_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle)) {
@@ -1098,6 +1125,12 @@ class ElectronAutoBleTransport {
1098
1125
  if (!device) {
1099
1126
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound, `Device ${uuid} not found`);
1100
1127
  }
1128
+ const protocolHint = expectedProtocol
1129
+ ? undefined
1130
+ : (_b = this.deviceProtocolHints.get(uuid)) !== null && _b !== void 0 ? _b : inferProtocolHintFromDeviceName(device.name);
1131
+ if (protocolHint) {
1132
+ this.deviceProtocolHints.set(uuid, protocolHint);
1133
+ }
1101
1134
  try {
1102
1135
  yield window.desktopApi.nobleBle.connect(uuid);
1103
1136
  this.connectedDevices.add(uuid);
@@ -1108,11 +1141,7 @@ class ElectronAutoBleTransport {
1108
1141
  this.v1Buffers.set(uuid, { buffer: [], bufferLength: 0 });
1109
1142
  this.v2Assemblers.set(uuid, new transport.ProtocolV2FrameAssembler());
1110
1143
  yield window.desktopApi.nobleBle.subscribe(uuid);
1111
- const cleanup = window.desktopApi.nobleBle.onNotification((deviceId, data) => {
1112
- if (deviceId === uuid) {
1113
- this.handleNotification(uuid, data);
1114
- }
1115
- });
1144
+ const cleanup = this.createNotificationSubscription(uuid);
1116
1145
  this.notificationCleanups.set(uuid, cleanup);
1117
1146
  const disconnectCleanup = window.desktopApi.nobleBle.onDeviceDisconnected((disconnectedDevice) => {
1118
1147
  var _a;
@@ -1126,8 +1155,8 @@ class ElectronAutoBleTransport {
1126
1155
  }
1127
1156
  });
1128
1157
  this.disconnectCleanups.set(uuid, disconnectCleanup);
1129
- const protocolType = yield this.detectProtocol(uuid, expectedProtocol);
1130
- (_b = this.emitter) === null || _b === void 0 ? void 0 : _b.emit('device-connect', {
1158
+ const protocolType = yield this.detectProtocol(uuid, expectedProtocol, protocolHint);
1159
+ (_c = this.emitter) === null || _c === void 0 ? void 0 : _c.emit('device-connect', {
1131
1160
  name: device.name,
1132
1161
  id: device.id,
1133
1162
  connectId: device.id,
@@ -1135,15 +1164,15 @@ class ElectronAutoBleTransport {
1135
1164
  return Object.assign(Object.assign({}, toBleDescriptor({ id: device.id, name: device.name }, protocolType)), { uuid });
1136
1165
  }
1137
1166
  catch (error) {
1138
- (_c = this.Log) === null || _c === void 0 ? void 0 : _c.error('[Auto BLE] acquire failed:', error);
1167
+ (_d = this.Log) === null || _d === void 0 ? void 0 : _d.error('[Auto BLE] acquire failed:', error);
1139
1168
  try {
1140
- if (((_d = window.desktopApi) === null || _d === void 0 ? void 0 : _d.nobleBle) && this.connectedDevices.has(uuid)) {
1169
+ if (((_e = window.desktopApi) === null || _e === void 0 ? void 0 : _e.nobleBle) && this.connectedDevices.has(uuid)) {
1141
1170
  yield window.desktopApi.nobleBle.unsubscribe(uuid);
1142
1171
  yield window.desktopApi.nobleBle.disconnect(uuid);
1143
1172
  }
1144
1173
  }
1145
1174
  catch (cleanupError) {
1146
- (_e = this.Log) === null || _e === void 0 ? void 0 : _e.debug('[Auto BLE] acquire cleanup failed:', cleanupError);
1175
+ (_f = this.Log) === null || _f === void 0 ? void 0 : _f.debug('[Auto BLE] acquire cleanup failed:', cleanupError);
1147
1176
  }
1148
1177
  this.cleanupDeviceState(uuid);
1149
1178
  throw error;
@@ -1171,8 +1200,8 @@ class ElectronAutoBleTransport {
1171
1200
  createProtocolMismatchError(expected) {
1172
1201
  return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol mismatch: expected ${expected}, but device did not respond to expected protocol`);
1173
1202
  }
1174
- detectProtocol(uuid, expectedProtocol) {
1175
- var _a, _b, _c, _d;
1203
+ detectProtocol(uuid, expectedProtocol, protocolHint) {
1204
+ var _a, _b, _c, _d, _e;
1176
1205
  return __awaiter(this, void 0, void 0, function* () {
1177
1206
  if (expectedProtocol === 'V1') {
1178
1207
  if (yield this.probeProtocolV1(uuid)) {
@@ -1190,20 +1219,80 @@ class ElectronAutoBleTransport {
1190
1219
  }
1191
1220
  throw this.createProtocolMismatchError(expectedProtocol);
1192
1221
  }
1222
+ if (protocolHint === 'V2' && (yield this.probeProtocolV2(uuid))) {
1223
+ this.deviceProtocol.set(uuid, 'V2');
1224
+ (_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug(`[Auto BLE] detectProtocol: uuid=${uuid} -> V2 (hint)`);
1225
+ return 'V2';
1226
+ }
1193
1227
  if (this.deviceProtocol.get(uuid) === 'V2' && (yield this.probeProtocolV2(uuid))) {
1194
1228
  this.deviceProtocol.set(uuid, 'V2');
1195
- (_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug(`[Auto BLE] detectProtocol: uuid=${uuid} -> V2 (cached)`);
1229
+ (_d = this.Log) === null || _d === void 0 ? void 0 : _d.debug(`[Auto BLE] detectProtocol: uuid=${uuid} -> V2 (cached)`);
1196
1230
  return 'V2';
1197
1231
  }
1198
1232
  let protocol = 'V1';
1199
- if (!(yield this.probeProtocolV1(uuid)) && (yield this.probeProtocolV2(uuid))) {
1200
- protocol = 'V2';
1233
+ const protocolV1Detected = yield this.probeProtocolV1(uuid);
1234
+ if (!protocolV1Detected) {
1235
+ yield this.resetProbeStateAfterProtocolProbe(uuid, 'V1');
1236
+ if (yield this.probeProtocolV2(uuid)) {
1237
+ protocol = 'V2';
1238
+ }
1201
1239
  }
1202
1240
  this.deviceProtocol.set(uuid, protocol);
1203
- (_d = this.Log) === null || _d === void 0 ? void 0 : _d.debug(`[Auto BLE] detectProtocol: uuid=${uuid} -> ${protocol}`);
1241
+ (_e = this.Log) === null || _e === void 0 ? void 0 : _e.debug(`[Auto BLE] detectProtocol: uuid=${uuid} -> ${protocol}`);
1204
1242
  return protocol;
1205
1243
  });
1206
1244
  }
1245
+ createNotificationSubscription(uuid) {
1246
+ var _a;
1247
+ if (!((_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle)) {
1248
+ throw new Error('Noble BLE API not available');
1249
+ }
1250
+ const notificationToken = this.nextNotificationToken;
1251
+ this.nextNotificationToken += 1;
1252
+ this.notificationTokens.set(uuid, notificationToken);
1253
+ return window.desktopApi.nobleBle.onNotification((deviceId, data) => {
1254
+ if (deviceId === uuid && this.notificationTokens.get(uuid) === notificationToken) {
1255
+ this.handleNotification(uuid, data);
1256
+ }
1257
+ });
1258
+ }
1259
+ resetProbeStateAfterProtocolProbe(uuid, protocol) {
1260
+ var _a, _b, _c, _d, _e, _f, _g, _h;
1261
+ return __awaiter(this, void 0, void 0, function* () {
1262
+ this.v1Buffers.set(uuid, { buffer: [], bufferLength: 0 });
1263
+ (_a = this.v2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1264
+ this.resetProtocolV2Frames(uuid);
1265
+ if (((_b = this.activeProtocolV2Call) === null || _b === void 0 ? void 0 : _b.uuid) === uuid) {
1266
+ this.activeProtocolV2Call = null;
1267
+ }
1268
+ if (this.runPromise) {
1269
+ const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
1270
+ this.runPromise.reject(error);
1271
+ this.runPromise = null;
1272
+ }
1273
+ const notifyCleanup = this.notificationCleanups.get(uuid);
1274
+ if (notifyCleanup) {
1275
+ notifyCleanup();
1276
+ this.notificationCleanups.delete(uuid);
1277
+ }
1278
+ this.notificationTokens.delete(uuid);
1279
+ try {
1280
+ yield ((_d = (_c = window.desktopApi) === null || _c === void 0 ? void 0 : _c.nobleBle) === null || _d === void 0 ? void 0 : _d.unsubscribe(uuid));
1281
+ }
1282
+ catch (error) {
1283
+ (_e = this.Log) === null || _e === void 0 ? void 0 : _e.debug(`[Auto BLE] unsubscribe after Protocol ${protocol} probe failed:`, error);
1284
+ }
1285
+ try {
1286
+ yield ((_g = (_f = window.desktopApi) === null || _f === void 0 ? void 0 : _f.nobleBle) === null || _g === void 0 ? void 0 : _g.subscribe(uuid));
1287
+ }
1288
+ catch (error) {
1289
+ (_h = this.Log) === null || _h === void 0 ? void 0 : _h.debug(`[Auto BLE] resubscribe after Protocol ${protocol} probe failed:`, error);
1290
+ throw error;
1291
+ }
1292
+ const cleanup = this.createNotificationSubscription(uuid);
1293
+ this.notificationCleanups.set(uuid, cleanup);
1294
+ });
1295
+ }
1207
1296
  probeProtocolV1(uuid) {
1208
1297
  var _a;
1209
1298
  return __awaiter(this, void 0, void 0, function* () {
@@ -1237,7 +1326,7 @@ class ElectronAutoBleTransport {
1237
1326
  onProbeFailed: () => {
1238
1327
  var _a;
1239
1328
  (_a = this.v2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1240
- this.resetProtocolV2Frames();
1329
+ this.resetProtocolV2Frames(uuid);
1241
1330
  },
1242
1331
  });
1243
1332
  });
@@ -1292,7 +1381,7 @@ class ElectronAutoBleTransport {
1292
1381
  if (this.runPromise) {
1293
1382
  const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleDeviceBondedCanceled);
1294
1383
  this.runPromise.reject(error);
1295
- this.rejectProtocolV2Frame(error);
1384
+ this.rejectAllProtocolV2Frames(error);
1296
1385
  }
1297
1386
  return;
1298
1387
  }
@@ -1304,11 +1393,11 @@ class ElectronAutoBleTransport {
1304
1393
  this.handleProtocolV1Notification(deviceId, hexData);
1305
1394
  }
1306
1395
  handleProtocolV2Notification(deviceId, hexData) {
1307
- var _a, _b;
1396
+ var _a, _b, _c;
1308
1397
  try {
1309
- if (!this.runPromise) {
1310
- (_a = this.v2Assemblers.get(deviceId)) === null || _a === void 0 ? void 0 : _a.reset();
1311
- this.resetProtocolV2Frames();
1398
+ if (!this.runPromise || ((_a = this.activeProtocolV2Call) === null || _a === void 0 ? void 0 : _a.uuid) !== deviceId) {
1399
+ (_b = this.v2Assemblers.get(deviceId)) === null || _b === void 0 ? void 0 : _b.reset();
1400
+ this.resetProtocolV2Frames(deviceId);
1312
1401
  return;
1313
1402
  }
1314
1403
  const bytes = transport.hexToBytes(hexData);
@@ -1319,52 +1408,65 @@ class ElectronAutoBleTransport {
1319
1408
  return;
1320
1409
  let frameData = assembler.push(bytes);
1321
1410
  while (frameData) {
1322
- this.resolveProtocolV2Frame(frameData);
1411
+ this.resolveProtocolV2Frame(deviceId, frameData);
1323
1412
  frameData = assembler.push(new Uint8Array(0));
1324
1413
  }
1325
1414
  }
1326
1415
  catch (error) {
1327
- (_b = this.Log) === null || _b === void 0 ? void 0 : _b.error('[Auto BLE] Protocol V2 notification error:', error);
1416
+ (_c = this.Log) === null || _c === void 0 ? void 0 : _c.error('[Auto BLE] Protocol V2 notification error:', error);
1328
1417
  if (this.runPromise) {
1329
1418
  const notifyError = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleWriteCharacteristicError);
1330
1419
  this.runPromise.reject(notifyError);
1331
- this.rejectProtocolV2Frame(notifyError);
1420
+ this.rejectAllProtocolV2Frames(notifyError);
1332
1421
  }
1333
1422
  }
1334
1423
  }
1335
- resolveProtocolV2Frame(frame) {
1336
- if (this.v2FramePromise) {
1337
- this.v2FramePromise.resolve(frame);
1338
- this.v2FramePromise = null;
1424
+ getProtocolV2FrameQueue(uuid) {
1425
+ let queue = this.v2FrameQueues.get(uuid);
1426
+ if (!queue) {
1427
+ queue = [];
1428
+ this.v2FrameQueues.set(uuid, queue);
1429
+ }
1430
+ return queue;
1431
+ }
1432
+ resolveProtocolV2Frame(uuid, frame) {
1433
+ const framePromise = this.v2FramePromises.get(uuid);
1434
+ if (framePromise) {
1435
+ framePromise.resolve(frame);
1436
+ this.v2FramePromises.delete(uuid);
1339
1437
  return;
1340
1438
  }
1341
- this.v2FrameQueue.push(frame);
1439
+ this.getProtocolV2FrameQueue(uuid).push(frame);
1342
1440
  }
1343
- rejectProtocolV2Frame(error) {
1344
- this.v2FrameQueue = [];
1345
- if (this.v2FramePromise) {
1346
- this.v2FramePromise.reject(error);
1347
- this.v2FramePromise = null;
1441
+ rejectAllProtocolV2Frames(error) {
1442
+ this.v2FrameQueues.clear();
1443
+ for (const framePromise of this.v2FramePromises.values()) {
1444
+ framePromise.reject(error);
1348
1445
  }
1446
+ this.v2FramePromises.clear();
1349
1447
  }
1350
- resetProtocolV2Frames() {
1351
- this.v2FrameQueue = [];
1352
- this.v2FramePromise = null;
1448
+ resetProtocolV2Frames(uuid) {
1449
+ this.v2FrameQueues.delete(uuid);
1450
+ this.v2FramePromises.delete(uuid);
1353
1451
  }
1354
- readProtocolV2Frame() {
1452
+ isActiveProtocolV2Call(uuid, token) {
1453
+ var _a;
1454
+ return ((_a = this.activeProtocolV2Call) === null || _a === void 0 ? void 0 : _a.uuid) === uuid && this.activeProtocolV2Call.token === token;
1455
+ }
1456
+ readProtocolV2Frame(uuid) {
1355
1457
  return __awaiter(this, void 0, void 0, function* () {
1356
- const queuedFrame = this.v2FrameQueue.shift();
1458
+ const queuedFrame = this.getProtocolV2FrameQueue(uuid).shift();
1357
1459
  if (queuedFrame) {
1358
1460
  return queuedFrame;
1359
1461
  }
1360
1462
  const framePromise = hdShared.createDeferred();
1361
- this.v2FramePromise = framePromise;
1463
+ this.v2FramePromises.set(uuid, framePromise);
1362
1464
  try {
1363
1465
  return yield framePromise.promise;
1364
1466
  }
1365
1467
  finally {
1366
- if (this.v2FramePromise === framePromise) {
1367
- this.v2FramePromise = null;
1468
+ if (this.v2FramePromises.get(uuid) === framePromise) {
1469
+ this.v2FramePromises.delete(uuid);
1368
1470
  }
1369
1471
  }
1370
1472
  });
@@ -1481,14 +1583,17 @@ class ElectronAutoBleTransport {
1481
1583
  }
1482
1584
  const error = hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleForceCleanRunPromise);
1483
1585
  this.runPromise.reject(error);
1484
- this.rejectProtocolV2Frame(error);
1586
+ this.rejectAllProtocolV2Frames(error);
1485
1587
  this.runPromise = null;
1588
+ this.activeProtocolV2Call = null;
1486
1589
  }
1487
1590
  const runPromise = hdShared.createDeferred();
1488
1591
  runPromise.promise.catch(() => undefined);
1489
1592
  this.runPromise = runPromise;
1593
+ const callToken = this.nextProtocolV2CallToken++;
1594
+ this.activeProtocolV2Call = { uuid, token: callToken };
1490
1595
  (_a = this.v2Assemblers.get(uuid)) === null || _a === void 0 ? void 0 : _a.reset();
1491
- this.resetProtocolV2Frames();
1596
+ this.resetProtocolV2Frames(uuid);
1492
1597
  let completed = false;
1493
1598
  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 });
1494
1599
  try {
@@ -1500,7 +1605,7 @@ class ElectronAutoBleTransport {
1500
1605
  router: transport.PROTOCOL_V2_CHANNEL_BLE_UART,
1501
1606
  writeFrame: (frame) => this.writeWithChunking(uuid, transport.bytesToHex(frame)),
1502
1607
  readFrame: () => __awaiter(this, void 0, void 0, function* () {
1503
- const rxFrame = yield this.readProtocolV2Frame();
1608
+ const rxFrame = yield this.readProtocolV2Frame(uuid);
1504
1609
  if (!(rxFrame instanceof Uint8Array)) {
1505
1610
  throw new Error('Response is not Uint8Array');
1506
1611
  }
@@ -1515,16 +1620,21 @@ class ElectronAutoBleTransport {
1515
1620
  return result;
1516
1621
  }
1517
1622
  catch (e) {
1518
- (_c = this.v2Assemblers.get(uuid)) === null || _c === void 0 ? void 0 : _c.reset();
1519
- this.resetProtocolV2Frames();
1623
+ if (this.isActiveProtocolV2Call(uuid, callToken)) {
1624
+ (_c = this.v2Assemblers.get(uuid)) === null || _c === void 0 ? void 0 : _c.reset();
1625
+ this.resetProtocolV2Frames(uuid);
1626
+ }
1520
1627
  (_d = this.Log) === null || _d === void 0 ? void 0 : _d.error('[Auto BLE] Protocol V2 call error:', e);
1521
1628
  throw e;
1522
1629
  }
1523
1630
  finally {
1524
- if (!completed) {
1525
- (_e = this.v2Assemblers.get(uuid)) === null || _e === void 0 ? void 0 : _e.reset();
1631
+ if (this.isActiveProtocolV2Call(uuid, callToken)) {
1632
+ if (!completed) {
1633
+ (_e = this.v2Assemblers.get(uuid)) === null || _e === void 0 ? void 0 : _e.reset();
1634
+ }
1635
+ this.resetProtocolV2Frames(uuid);
1636
+ this.activeProtocolV2Call = null;
1526
1637
  }
1527
- this.resetProtocolV2Frames();
1528
1638
  if (this.runPromise === runPromise) {
1529
1639
  this.runPromise = null;
1530
1640
  }
package/dist/webusb.d.ts CHANGED
@@ -10,6 +10,7 @@ export default class WebUsbTransport {
10
10
  messages: ReturnType<typeof transport.parseConfigure> | undefined;
11
11
  messagesV2: ReturnType<typeof transport.parseConfigure> | undefined;
12
12
  private deviceProtocol;
13
+ private deviceProtocolHints;
13
14
  private protocolV2Assemblers;
14
15
  private deviceEndpoints;
15
16
  name: string;
@@ -1 +1 @@
1
- {"version":3,"file":"webusb.d.ts","sourceRoot":"","sources":["../src/webusb.ts"],"names":[],"mappings":";AACA,OAAO,SAWN,MAAM,wBAAwB,CAAC;AAIhC,OAAO,KAAK,EACV,YAAY,EACZ,oBAAoB,EACpB,YAAY,EACZ,oBAAoB,EACrB,MAAM,wBAAwB,CAAC;AAiChC,MAAM,WAAW,UAAW,SAAQ,oBAAoB;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,CAAC;IAClB,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAaD,MAAM,CAAC,OAAO,OAAO,eAAe;IAClC,QAAQ,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAGlE,UAAU,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAGpE,OAAO,CAAC,cAAc,CAAwC;IAG9D,OAAO,CAAC,oBAAoB,CAAoD;IAGhF,OAAO,CAAC,eAAe,CAA2C;IAElE,IAAI,SAAqB;IAEzB,OAAO,UAAS;IAEhB,UAAU,UAAS;IAEnB,GAAG,CAAC,EAAE,GAAG,CAAC;IAEV,GAAG,CAAC,EAAE,GAAG,CAAC;IAMV,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAM;IAEnC,eAAe,SAAoB;IAEnC,UAAU,SAAe;IAEzB,WAAW,SAAgB;IAK3B,IAAI,CAAC,MAAM,EAAE,GAAG;IAgBhB,SAAS,CAAC,UAAU,EAAE,GAAG;IASzB,mBAAmB,CAAC,UAAU,EAAE,GAAG;IAU7B,kBAAkB;IAmBlB,SAAS;IAQT,mBAAmB;IAiCnB,OAAO,CAAC,KAAK,EAAE,YAAY;IAiBjC,OAAO,CAAC,2BAA2B;YAOrB,cAAc;IAiDtB,UAAU,CAAC,IAAI,EAAE,MAAM;IAwBvB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;IAkB1C,OAAO,CAAC,iBAAiB;IAiCnB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;IAgC5C,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAIvE,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,wBAAwB;YAalB,yBAAyB;IAiCvC,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,aAAa;YASP,oBAAoB;YAoCpB,mBAAmB;YA2CnB,yBAAyB;YAuBzB,uBAAuB;YA4CvB,eAAe;YAcf,eAAe;IAiBvB,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,oBAAoB;YA4BlB,cAAc;YAkCd,cAAc;YAmCd,sBAAsB;IA4C9B,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;IAgD5C,OAAO,CAAC,IAAI,EAAE,MAAM;IAc1B,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY;CAG5C"}
1
+ {"version":3,"file":"webusb.d.ts","sourceRoot":"","sources":["../src/webusb.ts"],"names":[],"mappings":";AACA,OAAO,SAWN,MAAM,wBAAwB,CAAC;AAIhC,OAAO,KAAK,EACV,YAAY,EACZ,oBAAoB,EACpB,YAAY,EACZ,oBAAoB,EACrB,MAAM,wBAAwB,CAAC;AAiChC,MAAM,WAAW,UAAW,SAAQ,oBAAoB;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,CAAC;IAClB,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAaD,MAAM,CAAC,OAAO,OAAO,eAAe;IAClC,QAAQ,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAGlE,UAAU,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAGpE,OAAO,CAAC,cAAc,CAAwC;IAE9D,OAAO,CAAC,mBAAmB,CAAwC;IAGnE,OAAO,CAAC,oBAAoB,CAAoD;IAGhF,OAAO,CAAC,eAAe,CAA2C;IAElE,IAAI,SAAqB;IAEzB,OAAO,UAAS;IAEhB,UAAU,UAAS;IAEnB,GAAG,CAAC,EAAE,GAAG,CAAC;IAEV,GAAG,CAAC,EAAE,GAAG,CAAC;IAMV,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAM;IAEnC,eAAe,SAAoB;IAEnC,UAAU,SAAe;IAEzB,WAAW,SAAgB;IAK3B,IAAI,CAAC,MAAM,EAAE,GAAG;IAgBhB,SAAS,CAAC,UAAU,EAAE,GAAG;IASzB,mBAAmB,CAAC,UAAU,EAAE,GAAG;IAU7B,kBAAkB;IAmBlB,SAAS;IAQT,mBAAmB;IAwCnB,OAAO,CAAC,KAAK,EAAE,YAAY;IAyBjC,OAAO,CAAC,2BAA2B;YAOrB,cAAc;IA+CtB,UAAU,CAAC,IAAI,EAAE,MAAM;IAwBvB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;IAkB1C,OAAO,CAAC,iBAAiB;IAiCnB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;IAgC5C,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAIvE,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,wBAAwB;YAalB,yBAAyB;IAiCvC,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,aAAa;YASP,oBAAoB;YAoCpB,mBAAmB;YA2CnB,yBAAyB;YAuBzB,uBAAuB;YA4CvB,eAAe;YAcf,eAAe;IAiBvB,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,oBAAoB;YA4BlB,cAAc;YAkCd,cAAc;YAmCd,sBAAsB;IA4C9B,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;IAgD5C,OAAO,CAAC,IAAI,EAAE,MAAM;IAiB1B,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY;CAG5C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-transport-web-device",
3
- "version": "1.1.27-alpha.30",
3
+ "version": "1.1.27-alpha.32",
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.1.27-alpha.30",
24
- "@onekeyfe/hd-transport": "1.1.27-alpha.30"
23
+ "@onekeyfe/hd-shared": "1.1.27-alpha.32",
24
+ "@onekeyfe/hd-transport": "1.1.27-alpha.32"
25
25
  },
26
26
  "devDependencies": {
27
- "@onekeyfe/hd-transport-electron": "1.1.27-alpha.30",
27
+ "@onekeyfe/hd-transport-electron": "1.1.27-alpha.32",
28
28
  "@types/w3c-web-usb": "^1.0.6",
29
29
  "@types/web-bluetooth": "^0.0.17"
30
30
  },
31
- "gitHead": "f1e3a93e766463007436eaa9aff4a271ffd8830c"
31
+ "gitHead": "04f92052a18dcf17e4a347f3003b4c4bbf062681"
32
32
  }
@@ -49,25 +49,22 @@ interface PacketProcessResult {
49
49
  error?: string;
50
50
  }
51
51
 
52
- function inferProtocolTypeFromDeviceName(name?: string | null): ProtocolType | undefined {
52
+ function inferProtocolHintFromDeviceName(name?: string | null): ProtocolType | undefined {
53
53
  return /\bpro\s*2\b/i.test(name ?? '') ? 'V2' : undefined;
54
54
  }
55
55
 
56
56
  const toBleDescriptor = (
57
57
  device: { id: string; name: string | null },
58
58
  protocolType?: ProtocolType
59
- ): OneKeyDeviceInfo => {
60
- const resolvedProtocolType = protocolType ?? inferProtocolTypeFromDeviceName(device.name);
61
-
62
- return {
59
+ ): OneKeyDeviceInfo =>
60
+ ({
63
61
  id: device.id,
64
62
  name: device.name,
65
63
  path: device.id,
66
64
  debug: false,
67
65
  commType: 'electron-ble',
68
- ...(resolvedProtocolType ? { protocolType: resolvedProtocolType } : {}),
69
- } as OneKeyDeviceInfo;
70
- };
66
+ ...(protocolType ? { protocolType } : {}),
67
+ } as OneKeyDeviceInfo);
71
68
 
72
69
  const BLE_PACKET_SIZE = 192;
73
70
  const BLE_WRITE_DELAY_MS = 5;
@@ -101,18 +98,28 @@ export default class ElectronAutoBleTransport {
101
98
 
102
99
  private deviceProtocol: Map<string, ProtocolType> = new Map();
103
100
 
101
+ private deviceProtocolHints: Map<string, ProtocolType> = new Map();
102
+
104
103
  private v1Buffers: Map<string, { buffer: number[]; bufferLength: number }> = new Map();
105
104
 
106
105
  private v2Assemblers: Map<string, ProtocolV2FrameAssembler> = new Map();
107
106
 
108
- private v2FrameQueue: Uint8Array[] = [];
107
+ private v2FrameQueues: Map<string, Uint8Array[]> = new Map();
108
+
109
+ private v2FramePromises: Map<string, Deferred<Uint8Array>> = new Map();
109
110
 
110
- private v2FramePromise: Deferred<Uint8Array> | null = null;
111
+ private activeProtocolV2Call: { uuid: string; token: number } | null = null;
112
+
113
+ private nextProtocolV2CallToken = 1;
111
114
 
112
115
  private notificationCleanups: Map<string, () => void> = new Map();
113
116
 
114
117
  private disconnectCleanups: Map<string, () => void> = new Map();
115
118
 
119
+ private notificationTokens: Map<string, number> = new Map();
120
+
121
+ private nextNotificationToken = 1;
122
+
116
123
  private handleBluetoothError(error: any): never {
117
124
  if (error && typeof error === 'object') {
118
125
  if ('code' in error) {
@@ -147,8 +154,14 @@ export default class ElectronAutoBleTransport {
147
154
  private cleanupDeviceState(deviceId: string): void {
148
155
  this.connectedDevices.delete(deviceId);
149
156
  this.deviceProtocol.delete(deviceId);
157
+ this.deviceProtocolHints.delete(deviceId);
150
158
  this.v1Buffers.delete(deviceId);
151
159
  this.v2Assemblers.delete(deviceId);
160
+ this.resetProtocolV2Frames(deviceId);
161
+ if (this.activeProtocolV2Call?.uuid === deviceId) {
162
+ this.activeProtocolV2Call = null;
163
+ }
164
+ this.notificationTokens.delete(deviceId);
152
165
 
153
166
  const notifyCleanup = this.notificationCleanups.get(deviceId);
154
167
  if (notifyCleanup) {
@@ -200,6 +213,10 @@ export default class ElectronAutoBleTransport {
200
213
  this.Log?.debug(`[Auto BLE] enumerate found ${devices.length} device(s):`);
201
214
  for (const dev of devices) {
202
215
  this.Log?.debug(`[Auto BLE] id="${dev.id}" name="${dev.name}"`);
216
+ const protocolHint = inferProtocolHintFromDeviceName(dev.name);
217
+ if (protocolHint) {
218
+ this.deviceProtocolHints.set(dev.id, protocolHint);
219
+ }
203
220
  }
204
221
  return devices.map(device => toBleDescriptor(device));
205
222
  } catch (error) {
@@ -218,7 +235,9 @@ export default class ElectronAutoBleTransport {
218
235
  if (forceCleanRunPromise && this.runPromise) {
219
236
  const error = ERRORS.TypedError(HardwareErrorCode.BleForceCleanRunPromise);
220
237
  this.runPromise.reject(error);
221
- this.rejectProtocolV2Frame(error);
238
+ this.rejectAllProtocolV2Frames(error);
239
+ this.runPromise = null;
240
+ this.activeProtocolV2Call = null;
222
241
  }
223
242
 
224
243
  try {
@@ -230,6 +249,12 @@ export default class ElectronAutoBleTransport {
230
249
  if (!device) {
231
250
  throw ERRORS.TypedError(HardwareErrorCode.DeviceNotFound, `Device ${uuid} not found`);
232
251
  }
252
+ const protocolHint = expectedProtocol
253
+ ? undefined
254
+ : this.deviceProtocolHints.get(uuid) ?? inferProtocolHintFromDeviceName(device.name);
255
+ if (protocolHint) {
256
+ this.deviceProtocolHints.set(uuid, protocolHint);
257
+ }
233
258
 
234
259
  try {
235
260
  await window.desktopApi.nobleBle.connect(uuid);
@@ -243,13 +268,7 @@ export default class ElectronAutoBleTransport {
243
268
 
244
269
  await window.desktopApi.nobleBle.subscribe(uuid);
245
270
 
246
- const cleanup = window.desktopApi.nobleBle.onNotification(
247
- (deviceId: string, data: string) => {
248
- if (deviceId === uuid) {
249
- this.handleNotification(uuid, data);
250
- }
251
- }
252
- );
271
+ const cleanup = this.createNotificationSubscription(uuid);
253
272
  this.notificationCleanups.set(uuid, cleanup);
254
273
 
255
274
  const disconnectCleanup = window.desktopApi.nobleBle.onDeviceDisconnected(
@@ -266,7 +285,7 @@ export default class ElectronAutoBleTransport {
266
285
  );
267
286
  this.disconnectCleanups.set(uuid, disconnectCleanup);
268
287
 
269
- const protocolType = await this.detectProtocol(uuid, expectedProtocol);
288
+ const protocolType = await this.detectProtocol(uuid, expectedProtocol, protocolHint);
270
289
 
271
290
  this.emitter?.emit('device-connect', {
272
291
  name: device.name,
@@ -317,7 +336,8 @@ export default class ElectronAutoBleTransport {
317
336
 
318
337
  private async detectProtocol(
319
338
  uuid: string,
320
- expectedProtocol?: ProtocolType
339
+ expectedProtocol?: ProtocolType,
340
+ protocolHint?: ProtocolType
321
341
  ): Promise<ProtocolType> {
322
342
  if (expectedProtocol === 'V1') {
323
343
  if (await this.probeProtocolV1(uuid)) {
@@ -337,6 +357,12 @@ export default class ElectronAutoBleTransport {
337
357
  throw this.createProtocolMismatchError(expectedProtocol);
338
358
  }
339
359
 
360
+ if (protocolHint === 'V2' && (await this.probeProtocolV2(uuid))) {
361
+ this.deviceProtocol.set(uuid, 'V2');
362
+ this.Log?.debug(`[Auto BLE] detectProtocol: uuid=${uuid} -> V2 (hint)`);
363
+ return 'V2';
364
+ }
365
+
340
366
  if (this.deviceProtocol.get(uuid) === 'V2' && (await this.probeProtocolV2(uuid))) {
341
367
  this.deviceProtocol.set(uuid, 'V2');
342
368
  this.Log?.debug(`[Auto BLE] detectProtocol: uuid=${uuid} -> V2 (cached)`);
@@ -344,14 +370,70 @@ export default class ElectronAutoBleTransport {
344
370
  }
345
371
 
346
372
  let protocol: ProtocolType = 'V1';
347
- if (!(await this.probeProtocolV1(uuid)) && (await this.probeProtocolV2(uuid))) {
348
- protocol = 'V2';
373
+ const protocolV1Detected = await this.probeProtocolV1(uuid);
374
+ if (!protocolV1Detected) {
375
+ await this.resetProbeStateAfterProtocolProbe(uuid, 'V1');
376
+ if (await this.probeProtocolV2(uuid)) {
377
+ protocol = 'V2';
378
+ }
349
379
  }
350
380
  this.deviceProtocol.set(uuid, protocol);
351
381
  this.Log?.debug(`[Auto BLE] detectProtocol: uuid=${uuid} -> ${protocol}`);
352
382
  return protocol;
353
383
  }
354
384
 
385
+ private createNotificationSubscription(uuid: string) {
386
+ if (!window.desktopApi?.nobleBle) {
387
+ throw new Error('Noble BLE API not available');
388
+ }
389
+
390
+ const notificationToken = this.nextNotificationToken;
391
+ this.nextNotificationToken += 1;
392
+ this.notificationTokens.set(uuid, notificationToken);
393
+
394
+ return window.desktopApi.nobleBle.onNotification((deviceId: string, data: string) => {
395
+ if (deviceId === uuid && this.notificationTokens.get(uuid) === notificationToken) {
396
+ this.handleNotification(uuid, data);
397
+ }
398
+ });
399
+ }
400
+
401
+ private async resetProbeStateAfterProtocolProbe(uuid: string, protocol: ProtocolType) {
402
+ this.v1Buffers.set(uuid, { buffer: [], bufferLength: 0 });
403
+ this.v2Assemblers.get(uuid)?.reset();
404
+ this.resetProtocolV2Frames(uuid);
405
+ if (this.activeProtocolV2Call?.uuid === uuid) {
406
+ this.activeProtocolV2Call = null;
407
+ }
408
+ if (this.runPromise) {
409
+ const error = ERRORS.TypedError(HardwareErrorCode.BleForceCleanRunPromise);
410
+ this.runPromise.reject(error);
411
+ this.runPromise = null;
412
+ }
413
+
414
+ const notifyCleanup = this.notificationCleanups.get(uuid);
415
+ if (notifyCleanup) {
416
+ notifyCleanup();
417
+ this.notificationCleanups.delete(uuid);
418
+ }
419
+ this.notificationTokens.delete(uuid);
420
+
421
+ try {
422
+ await window.desktopApi?.nobleBle?.unsubscribe(uuid);
423
+ } catch (error) {
424
+ this.Log?.debug(`[Auto BLE] unsubscribe after Protocol ${protocol} probe failed:`, error);
425
+ }
426
+ try {
427
+ await window.desktopApi?.nobleBle?.subscribe(uuid);
428
+ } catch (error) {
429
+ this.Log?.debug(`[Auto BLE] resubscribe after Protocol ${protocol} probe failed:`, error);
430
+ throw error;
431
+ }
432
+
433
+ const cleanup = this.createNotificationSubscription(uuid);
434
+ this.notificationCleanups.set(uuid, cleanup);
435
+ }
436
+
355
437
  private async probeProtocolV1(uuid: string) {
356
438
  if (!this._messages) {
357
439
  return false;
@@ -381,7 +463,7 @@ export default class ElectronAutoBleTransport {
381
463
  logPrefix: 'ProtocolV2 BLE',
382
464
  onProbeFailed: () => {
383
465
  this.v2Assemblers.get(uuid)?.reset();
384
- this.resetProtocolV2Frames();
466
+ this.resetProtocolV2Frames(uuid);
385
467
  },
386
468
  });
387
469
  }
@@ -442,7 +524,7 @@ export default class ElectronAutoBleTransport {
442
524
  if (this.runPromise) {
443
525
  const error = ERRORS.TypedError(HardwareErrorCode.BleDeviceBondedCanceled);
444
526
  this.runPromise.reject(error);
445
- this.rejectProtocolV2Frame(error);
527
+ this.rejectAllProtocolV2Frames(error);
446
528
  }
447
529
  return;
448
530
  }
@@ -457,9 +539,9 @@ export default class ElectronAutoBleTransport {
457
539
 
458
540
  private handleProtocolV2Notification(deviceId: string, hexData: string): void {
459
541
  try {
460
- if (!this.runPromise) {
542
+ if (!this.runPromise || this.activeProtocolV2Call?.uuid !== deviceId) {
461
543
  this.v2Assemblers.get(deviceId)?.reset();
462
- this.resetProtocolV2Frames();
544
+ this.resetProtocolV2Frames(deviceId);
463
545
  return;
464
546
  }
465
547
 
@@ -471,7 +553,7 @@ export default class ElectronAutoBleTransport {
471
553
 
472
554
  let frameData = assembler.push(bytes);
473
555
  while (frameData) {
474
- this.resolveProtocolV2Frame(frameData);
556
+ this.resolveProtocolV2Frame(deviceId, frameData);
475
557
  frameData = assembler.push(new Uint8Array(0));
476
558
  }
477
559
  } catch (error) {
@@ -479,46 +561,60 @@ export default class ElectronAutoBleTransport {
479
561
  if (this.runPromise) {
480
562
  const notifyError = ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError);
481
563
  this.runPromise.reject(notifyError);
482
- this.rejectProtocolV2Frame(notifyError);
564
+ this.rejectAllProtocolV2Frames(notifyError);
483
565
  }
484
566
  }
485
567
  }
486
568
 
487
- private resolveProtocolV2Frame(frame: Uint8Array) {
488
- if (this.v2FramePromise) {
489
- this.v2FramePromise.resolve(frame);
490
- this.v2FramePromise = null;
569
+ private getProtocolV2FrameQueue(uuid: string) {
570
+ let queue = this.v2FrameQueues.get(uuid);
571
+ if (!queue) {
572
+ queue = [];
573
+ this.v2FrameQueues.set(uuid, queue);
574
+ }
575
+ return queue;
576
+ }
577
+
578
+ private resolveProtocolV2Frame(uuid: string, frame: Uint8Array) {
579
+ const framePromise = this.v2FramePromises.get(uuid);
580
+ if (framePromise) {
581
+ framePromise.resolve(frame);
582
+ this.v2FramePromises.delete(uuid);
491
583
  return;
492
584
  }
493
- this.v2FrameQueue.push(frame);
585
+ this.getProtocolV2FrameQueue(uuid).push(frame);
494
586
  }
495
587
 
496
- private rejectProtocolV2Frame(error: Error) {
497
- this.v2FrameQueue = [];
498
- if (this.v2FramePromise) {
499
- this.v2FramePromise.reject(error);
500
- this.v2FramePromise = null;
588
+ private rejectAllProtocolV2Frames(error: Error) {
589
+ this.v2FrameQueues.clear();
590
+ for (const framePromise of this.v2FramePromises.values()) {
591
+ framePromise.reject(error);
501
592
  }
593
+ this.v2FramePromises.clear();
594
+ }
595
+
596
+ private resetProtocolV2Frames(uuid: string) {
597
+ this.v2FrameQueues.delete(uuid);
598
+ this.v2FramePromises.delete(uuid);
502
599
  }
503
600
 
504
- private resetProtocolV2Frames() {
505
- this.v2FrameQueue = [];
506
- this.v2FramePromise = null;
601
+ private isActiveProtocolV2Call(uuid: string, token: number) {
602
+ return this.activeProtocolV2Call?.uuid === uuid && this.activeProtocolV2Call.token === token;
507
603
  }
508
604
 
509
- private async readProtocolV2Frame() {
510
- const queuedFrame = this.v2FrameQueue.shift();
605
+ private async readProtocolV2Frame(uuid: string) {
606
+ const queuedFrame = this.getProtocolV2FrameQueue(uuid).shift();
511
607
  if (queuedFrame) {
512
608
  return queuedFrame;
513
609
  }
514
610
 
515
611
  const framePromise = createDeferred<Uint8Array>();
516
- this.v2FramePromise = framePromise;
612
+ this.v2FramePromises.set(uuid, framePromise);
517
613
  try {
518
614
  return await framePromise.promise;
519
615
  } finally {
520
- if (this.v2FramePromise === framePromise) {
521
- this.v2FramePromise = null;
616
+ if (this.v2FramePromises.get(uuid) === framePromise) {
617
+ this.v2FramePromises.delete(uuid);
522
618
  }
523
619
  }
524
620
  }
@@ -656,15 +752,18 @@ export default class ElectronAutoBleTransport {
656
752
  }
657
753
  const error = ERRORS.TypedError(HardwareErrorCode.BleForceCleanRunPromise);
658
754
  this.runPromise.reject(error);
659
- this.rejectProtocolV2Frame(error);
755
+ this.rejectAllProtocolV2Frames(error);
660
756
  this.runPromise = null;
757
+ this.activeProtocolV2Call = null;
661
758
  }
662
759
 
663
760
  const runPromise = createDeferred<Uint8Array | string>();
664
761
  runPromise.promise.catch(() => undefined);
665
762
  this.runPromise = runPromise;
763
+ const callToken = this.nextProtocolV2CallToken++;
764
+ this.activeProtocolV2Call = { uuid, token: callToken };
666
765
  this.v2Assemblers.get(uuid)?.reset();
667
- this.resetProtocolV2Frames();
766
+ this.resetProtocolV2Frames(uuid);
668
767
  let completed = false;
669
768
  const callOptions = {
670
769
  ...options,
@@ -680,7 +779,7 @@ export default class ElectronAutoBleTransport {
680
779
  router: PROTOCOL_V2_CHANNEL_BLE_UART,
681
780
  writeFrame: (frame: Uint8Array) => this.writeWithChunking(uuid, bytesToHex(frame)),
682
781
  readFrame: async () => {
683
- const rxFrame = await this.readProtocolV2Frame();
782
+ const rxFrame = await this.readProtocolV2Frame(uuid);
684
783
  if (!(rxFrame instanceof Uint8Array)) {
685
784
  throw new Error('Response is not Uint8Array');
686
785
  }
@@ -699,15 +798,20 @@ export default class ElectronAutoBleTransport {
699
798
  completed = true;
700
799
  return result;
701
800
  } catch (e) {
702
- this.v2Assemblers.get(uuid)?.reset();
703
- this.resetProtocolV2Frames();
801
+ if (this.isActiveProtocolV2Call(uuid, callToken)) {
802
+ this.v2Assemblers.get(uuid)?.reset();
803
+ this.resetProtocolV2Frames(uuid);
804
+ }
704
805
  this.Log?.error('[Auto BLE] Protocol V2 call error:', e);
705
806
  throw e;
706
807
  } finally {
707
- if (!completed) {
708
- this.v2Assemblers.get(uuid)?.reset();
808
+ if (this.isActiveProtocolV2Call(uuid, callToken)) {
809
+ if (!completed) {
810
+ this.v2Assemblers.get(uuid)?.reset();
811
+ }
812
+ this.resetProtocolV2Frames(uuid);
813
+ this.activeProtocolV2Call = null;
709
814
  }
710
- this.resetProtocolV2Frames();
711
815
  if (this.runPromise === runPromise) {
712
816
  this.runPromise = null;
713
817
  }
package/src/webusb.ts CHANGED
@@ -45,7 +45,7 @@ function shouldBlockWebUsbCallDataLog(name: string) {
45
45
  return LogBlockCommand.has(name) || WEBUSB_FILE_WRITE_LOG_BLOCK_PATTERN.test(normalized);
46
46
  }
47
47
 
48
- function inferProtocolTypeFromDeviceName(name?: string | null): ProtocolType | undefined {
48
+ function inferProtocolHintFromDeviceName(name?: string | null): ProtocolType | undefined {
49
49
  return /\bpro\s*2\b/i.test(name ?? '') ? 'V2' : undefined;
50
50
  }
51
51
 
@@ -78,6 +78,8 @@ export default class WebUsbTransport {
78
78
  /** Per-path protocol type detected by active wire-level probe. */
79
79
  private deviceProtocol: Map<string, ProtocolType> = new Map();
80
80
 
81
+ private deviceProtocolHints: Map<string, ProtocolType> = new Map();
82
+
81
83
  /** 按设备缓存 Protocol V2 frame assembler,保留同一次读取里多出来的后续 frame。 */
82
84
  private protocolV2Assemblers: Map<string, ProtocolV2FrameAssembler> = new Map();
83
85
 
@@ -183,12 +185,19 @@ export default class WebUsbTransport {
183
185
  return isOneKey && hasSerialNumber;
184
186
  });
185
187
 
186
- this.deviceList = onekeyDevices.map(device => ({
187
- path: device.serialNumber as string,
188
- device,
189
- commType: 'webusb',
190
- protocolType: inferProtocolTypeFromDeviceName(device.productName),
191
- }));
188
+ this.deviceList = onekeyDevices.map(device => {
189
+ const path = device.serialNumber as string;
190
+ const protocolHint = inferProtocolHintFromDeviceName(device.productName);
191
+ if (protocolHint) {
192
+ this.deviceProtocolHints.set(path, protocolHint);
193
+ }
194
+
195
+ return {
196
+ path,
197
+ device,
198
+ commType: 'webusb',
199
+ };
200
+ });
192
201
 
193
202
  // Debug: log all discovered devices. Protocol is detected after acquire via wire probe.
194
203
  for (const dev of onekeyDevices) {
@@ -208,7 +217,15 @@ export default class WebUsbTransport {
208
217
  if (!input.path) return;
209
218
  try {
210
219
  await this.connect(input.path ?? '', true);
211
- await this.detectProtocol(input.path, input.expectedProtocol);
220
+ const deviceName = this.deviceList.find(device => device.path === input.path)?.device
221
+ .productName;
222
+ const protocolHint = input.expectedProtocol
223
+ ? undefined
224
+ : this.deviceProtocolHints.get(input.path) ?? inferProtocolHintFromDeviceName(deviceName);
225
+ if (protocolHint) {
226
+ this.deviceProtocolHints.set(input.path, protocolHint);
227
+ }
228
+ await this.detectProtocol(input.path, input.expectedProtocol, protocolHint);
212
229
  return await Promise.resolve(input.path);
213
230
  } catch (e) {
214
231
  this.Log.debug('acquire error: ', e instanceof Error ? `${e.name}: ${e.message}` : String(e));
@@ -230,7 +247,8 @@ export default class WebUsbTransport {
230
247
 
231
248
  private async detectProtocol(
232
249
  path: string,
233
- expectedProtocol?: ProtocolType
250
+ expectedProtocol?: ProtocolType,
251
+ protocolHint?: ProtocolType
234
252
  ): Promise<ProtocolType> {
235
253
  if (expectedProtocol === 'V1') {
236
254
  if (await this.probeProtocolV1(path)) {
@@ -242,15 +260,6 @@ export default class WebUsbTransport {
242
260
  }
243
261
 
244
262
  if (expectedProtocol === 'V2') {
245
- if (this.deviceProtocol.get(path) === 'V2') {
246
- this.Log.debug(`[WebUsbTransport] detectProtocol: path=${path} -> V2 (cached expected)`);
247
- return 'V2';
248
- }
249
- if (this.deviceList.find(device => device.path === path)?.protocolType === 'V2') {
250
- this.deviceProtocol.set(path, 'V2');
251
- this.Log.debug(`[WebUsbTransport] detectProtocol: path=${path} -> V2 (descriptor)`);
252
- return 'V2';
253
- }
254
263
  if (await this.probeProtocolV2(path)) {
255
264
  this.deviceProtocol.set(path, 'V2');
256
265
  this.Log.debug(`[WebUsbTransport] detectProtocol: path=${path} -> V2 (expected)`);
@@ -259,6 +268,12 @@ export default class WebUsbTransport {
259
268
  throw this.createProtocolMismatchError(expectedProtocol);
260
269
  }
261
270
 
271
+ if (protocolHint === 'V2' && (await this.probeProtocolV2(path))) {
272
+ this.deviceProtocol.set(path, 'V2');
273
+ this.Log.debug(`[WebUsbTransport] detectProtocol: path=${path} -> V2 (hint)`);
274
+ return 'V2';
275
+ }
276
+
262
277
  if (this.deviceProtocol.get(path) === 'V2' && (await this.probeProtocolV2(path))) {
263
278
  this.deviceProtocol.set(path, 'V2');
264
279
  this.Log.debug(`[WebUsbTransport] detectProtocol: path=${path} -> V2 (cached)`);
@@ -703,8 +718,8 @@ export default class WebUsbTransport {
703
718
  /**
704
719
  * Send/receive a single call over Protocol V2 (0x5A framing).
705
720
  *
706
- * Encoding: protobuf message → 2-byte LE msgType + pb bytes → Protocol V2 frame
707
- * Decoding: Protocol V2 frame → msgType + pb bytes → protobuf message
721
+ * Encoding: protobuf message → 2-byte LE messageTypeId + pb bytes → Protocol V2 frame
722
+ * Decoding: Protocol V2 frame → messageTypeId + pb bytes → protobuf message
708
723
  */
709
724
  private async callProtocolV2(
710
725
  path: string,
@@ -839,6 +854,9 @@ export default class WebUsbTransport {
839
854
  const ifaceNum = endpoints?.interfaceNumber ?? this.interfaceId;
840
855
  await device.releaseInterface(ifaceNum);
841
856
  await device.close();
857
+ this.deviceProtocol.delete(path);
858
+ this.deviceProtocolHints.delete(path);
859
+ this.protocolV2Assemblers.get(path)?.reset();
842
860
  this.protocolV2Assemblers.delete(path);
843
861
  this.deviceEndpoints.delete(path);
844
862
  }