@onekeyfe/hd-transport-web-device 1.2.8-alpha.127 → 1.2.8-alpha.129

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.
@@ -286,6 +286,192 @@ describe('ElectronBleTransport protocol detection', () => {
286
286
  expect(nobleBle.unsubscribe).not.toHaveBeenCalled();
287
287
  });
288
288
 
289
+ test.each([false, true])(
290
+ 'forwards the existing keepSession=%s release intent to native',
291
+ async keepSession => {
292
+ const device = { id: 'release-intent-pro2-id', name: 'OneKey Pro 2' };
293
+ const release = jest.fn().mockResolvedValue(undefined);
294
+ const nobleBle = { ...createNobleBle(device), release };
295
+ echoProtocolV2(nobleBle, device.id);
296
+ const bleTransport = configureTransport(nobleBle);
297
+ await bleTransport.acquire({ uuid: device.id, expectedProtocol: 'V2' });
298
+
299
+ await bleTransport.release(device.id, false, keepSession);
300
+
301
+ expect(release).toHaveBeenCalledWith(device.id, keepSession);
302
+ expect(nobleBle.disconnect).not.toHaveBeenCalled();
303
+ expect(nobleBle.unsubscribe).not.toHaveBeenCalled();
304
+ }
305
+ );
306
+
307
+ test.each(['Pro 2', 'Neo'])(
308
+ 'reuses the connection, subscription and V2 sequence for %s after release',
309
+ async name => {
310
+ const device = { id: 'retained-v2-id', name };
311
+ const release = jest.fn().mockResolvedValue(undefined);
312
+ const nobleBle = { ...createNobleBle(device), release };
313
+ echoProtocolV2(nobleBle, device.id);
314
+ const bleTransport = configureTransport(nobleBle);
315
+ await bleTransport.acquire({ uuid: device.id, expectedProtocol: 'V2' });
316
+ await bleTransport.call(device.id, 'Ping', { message: 'before-release' });
317
+ const notifyCleanup = nobleBle.onNotification.mock.results[0].value;
318
+
319
+ try {
320
+ await bleTransport.release(device.id, false, true);
321
+ expect(bleTransport.getProtocolType(device.id)).toBe('V2');
322
+ expect(notifyCleanup).not.toHaveBeenCalled();
323
+ await expect(
324
+ bleTransport.acquire({ uuid: device.id, expectedProtocol: 'V2' })
325
+ ).resolves.toMatchObject({ uuid: device.id, protocolType: 'V2' });
326
+ await expect(
327
+ bleTransport.call(device.id, 'Ping', { message: 'after-release' })
328
+ ).resolves.toMatchObject({ type: 'Success' });
329
+
330
+ expect(nobleBle.connect).toHaveBeenCalledTimes(1);
331
+ expect(nobleBle.subscribe).toHaveBeenCalledTimes(1);
332
+ expect(nobleBle.onNotification).toHaveBeenCalledTimes(1);
333
+ expect(nobleBle.onMtuChanged).toHaveBeenCalledTimes(1);
334
+ expect(release).toHaveBeenCalledWith(device.id, true);
335
+ expect(
336
+ nobleBle.write.mock.calls.map(([, hex]) => Number.parseInt(hex.slice(12, 14), 16))
337
+ ).toEqual([1, 2, 3]);
338
+ } finally {
339
+ await bleTransport.disconnect(device.id);
340
+ }
341
+ expect(notifyCleanup).toHaveBeenCalledTimes(1);
342
+ expect(nobleBle.disconnect).toHaveBeenCalledWith(device.id);
343
+ }
344
+ );
345
+
346
+ test.each(['ordinary release', 'onclose', 'forced probe'])(
347
+ 'does not retain renderer state for %s',
348
+ async mode => {
349
+ const device = { id: 'release-fallback-id', name: 'Pro 2' };
350
+ const nobleBle = createNobleBle(device);
351
+ echoProtocolV2(nobleBle, device.id);
352
+ const bleTransport = configureTransport(nobleBle);
353
+ await bleTransport.acquire({ uuid: device.id, expectedProtocol: 'V2' });
354
+ const notifyCleanup = nobleBle.onNotification.mock.results[0].value;
355
+ await bleTransport.release(device.id, mode === 'onclose', mode !== 'ordinary release');
356
+
357
+ const input = {
358
+ uuid: device.id,
359
+ expectedProtocol: 'V2' as const,
360
+ forceProtocolDetection: mode === 'forced probe',
361
+ };
362
+ await bleTransport.acquire(input);
363
+
364
+ expect(notifyCleanup).toHaveBeenCalledTimes(1);
365
+ expect(nobleBle.connect).toHaveBeenCalledTimes(2);
366
+ expect(nobleBle.subscribe).toHaveBeenCalledTimes(2);
367
+ await bleTransport.disconnect(device.id);
368
+ }
369
+ );
370
+
371
+ test.each([false, true])(
372
+ 'rebuilds a retained link after a disconnect, including during lookup: %s',
373
+ async duringLookup => {
374
+ const device = { id: 'retained-drop-id', name: 'Pro 2' };
375
+ const nobleBle = createNobleBle(device);
376
+ echoProtocolV2(nobleBle, device.id);
377
+ const bleTransport = configureTransport(nobleBle);
378
+ await bleTransport.acquire({ uuid: device.id, expectedProtocol: 'V2' });
379
+ await bleTransport.release(device.id, false, true);
380
+ const disconnectHandler = nobleBle.onDeviceDisconnected.mock.calls[0][0];
381
+ const notifyCleanup = nobleBle.onNotification.mock.results[0].value;
382
+ const drop = () =>
383
+ disconnectHandler({ ...device, reason: EBleDisconnectReason.DeviceDisconnected });
384
+ if (duringLookup) {
385
+ nobleBle.getDevice.mockImplementationOnce(() => {
386
+ drop();
387
+ return Promise.resolve(device);
388
+ });
389
+ } else {
390
+ drop();
391
+ }
392
+
393
+ await bleTransport.acquire({ uuid: device.id, expectedProtocol: 'V2' });
394
+ await expect(bleTransport.call(device.id, 'Ping', {})).resolves.toMatchObject({
395
+ type: 'Success',
396
+ });
397
+
398
+ expect(notifyCleanup).toHaveBeenCalledTimes(1);
399
+ expect(nobleBle.connect).toHaveBeenCalledTimes(2);
400
+ expect(nobleBle.subscribe).toHaveBeenCalledTimes(2);
401
+ await bleTransport.disconnect(device.id);
402
+ }
403
+ );
404
+
405
+ test('disconnects a reused link on timeout without replaying the call', async () => {
406
+ const device = { id: 'retained-timeout-id', name: 'Pro 2' };
407
+ const nobleBle = createNobleBle(device);
408
+ echoProtocolV2(nobleBle, device.id);
409
+ const bleTransport = configureTransport(nobleBle);
410
+ await bleTransport.acquire({ uuid: device.id, expectedProtocol: 'V2' });
411
+ await bleTransport.release(device.id, false, true);
412
+ await bleTransport.acquire({ uuid: device.id, expectedProtocol: 'V2' });
413
+ nobleBle.write.mockImplementationOnce(() => Promise.resolve());
414
+
415
+ await expect(bleTransport.call(device.id, 'Ping', {}, { timeoutMs: 10 })).rejects.toMatchObject(
416
+ {
417
+ errorCode: HardwareErrorCode.BleTimeoutError,
418
+ }
419
+ );
420
+
421
+ expect(nobleBle.write).toHaveBeenCalledTimes(2);
422
+ expect(nobleBle.unsubscribe).toHaveBeenCalledWith(device.id);
423
+ expect(nobleBle.disconnect).toHaveBeenCalledWith(device.id);
424
+ expect(bleTransport.getProtocolType(device.id)).toBeUndefined();
425
+ await bleTransport.acquire({ uuid: device.id, expectedProtocol: 'V2' });
426
+ expect(nobleBle.subscribe).toHaveBeenCalledTimes(2);
427
+ await bleTransport.disconnect(device.id);
428
+ });
429
+
430
+ test('invalidates a retained link when native release fails', async () => {
431
+ const device = { id: 'retained-release-failure-id', name: 'Pro 2' };
432
+ const release = jest.fn().mockRejectedValueOnce(new Error('IPC release failed'));
433
+ const nobleBle = { ...createNobleBle(device), release };
434
+ echoProtocolV2(nobleBle, device.id);
435
+ const bleTransport = configureTransport(nobleBle);
436
+ await bleTransport.acquire({ uuid: device.id, expectedProtocol: 'V2' });
437
+ const notifyCleanup = nobleBle.onNotification.mock.results[0].value;
438
+
439
+ await bleTransport.release(device.id, false, true);
440
+
441
+ expect(bleTransport.getProtocolType(device.id)).toBeUndefined();
442
+ expect(notifyCleanup).toHaveBeenCalledTimes(1);
443
+ await bleTransport.acquire({ uuid: device.id, expectedProtocol: 'V2' });
444
+ expect(nobleBle.subscribe).toHaveBeenCalledTimes(2);
445
+ await bleTransport.disconnect(device.id);
446
+ });
447
+
448
+ test('still cancels an in-flight V2 write when release requests retention', async () => {
449
+ const device = { id: 'active-release-id', name: 'Pro 2' };
450
+ const nobleBle = createNobleBle(device);
451
+ echoProtocolV2(nobleBle, device.id);
452
+ const bleTransport = configureTransport(nobleBle);
453
+ await bleTransport.acquire({ uuid: device.id, expectedProtocol: 'V2' });
454
+ const started = createDeferred<void>();
455
+ const write = createDeferred<void>();
456
+ nobleBle.write.mockImplementationOnce(() => {
457
+ started.resolve();
458
+ return write.promise;
459
+ });
460
+ const result = bleTransport.call(device.id, 'Ping', {}).catch(error => error);
461
+ await started.promise;
462
+
463
+ try {
464
+ const releasing = bleTransport.release(device.id, false, true);
465
+ write.resolve();
466
+ await releasing;
467
+ await expect(result).resolves.toMatchObject({ message: 'Electron BLE transport released' });
468
+ expect(bleTransport.getProtocolType(device.id)).toBeUndefined();
469
+ } finally {
470
+ write.resolve();
471
+ await bleTransport.disconnect(device.id);
472
+ }
473
+ });
474
+
289
475
  test('forceNative disconnects a device the renderer no longer tracks', async () => {
290
476
  const device = { id: 'presumed-dead-id', name: 'OneKey Pro 2' };
291
477
  const nobleBle = createNobleBle(device);
@@ -488,6 +674,10 @@ describe('ElectronBleTransport protocol detection', () => {
488
674
  })
489
675
  );
490
676
  expect(transport.getProtocolType(device.id)).toBe('V1');
677
+ const notifyCleanup = nobleBle.onNotification.mock.results[0].value;
678
+ await transport.release(device.id, false, true);
679
+ expect(transport.getProtocolType(device.id)).toBeUndefined();
680
+ expect(notifyCleanup).toHaveBeenCalledTimes(1);
491
681
  await expect(transport.acquire({ uuid: device.id, expectedProtocol: 'V1' })).resolves.toEqual(
492
682
  expect.objectContaining({
493
683
  uuid: device.id,
@@ -25,6 +25,8 @@ export default class ElectronBleTransport {
25
25
  Log?: any;
26
26
  emitter?: EventEmitter;
27
27
  private connectedDevices;
28
+ private retainedDevices;
29
+ private protocolV2CallCounts;
28
30
  private acquiringDevices;
29
31
  private deviceProtocol;
30
32
  private deviceProtocolHints;
@@ -62,7 +64,7 @@ export default class ElectronBleTransport {
62
64
  bleColorCode?: string | undefined;
63
65
  protocolType?: ProtocolType | undefined;
64
66
  }>;
65
- release(id: string, _onclose?: boolean, keepSession?: boolean): Promise<void>;
67
+ release(id: string, onclose?: boolean, keepSession?: boolean): Promise<void>;
66
68
  disconnect(id: string): Promise<void>;
67
69
  private releaseNative;
68
70
  private releaseLogical;
@@ -1 +1 @@
1
- {"version":3,"file":"electron-ble-transport.d.ts","sourceRoot":"","sources":["../src/electron-ble-transport.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"electron-ble-transport.d.ts","sourceRoot":"","sources":["../src/electron-ble-transport.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -255,6 +255,8 @@ declare class ElectronBleTransport {
255
255
  Log?: any;
256
256
  emitter?: EventEmitter;
257
257
  private connectedDevices;
258
+ private retainedDevices;
259
+ private protocolV2CallCounts;
258
260
  /**
259
261
  * Devices whose acquire() is still in flight.
260
262
  *
@@ -313,7 +315,7 @@ declare class ElectronBleTransport {
313
315
  bleColorCode?: string | undefined;
314
316
  protocolType?: ProtocolType | undefined;
315
317
  }>;
316
- release(id: string, _onclose?: boolean, keepSession?: boolean): Promise<void>;
318
+ release(id: string, onclose?: boolean, keepSession?: boolean): Promise<void>;
317
319
  disconnect(id: string): Promise<void>;
318
320
  private releaseNative;
319
321
  private releaseLogical;
package/dist/index.js CHANGED
@@ -1532,6 +1532,8 @@ class ElectronBleTransport {
1532
1532
  this.runPromise = null;
1533
1533
  this.runPromiseDeviceId = null;
1534
1534
  this.connectedDevices = new Set();
1535
+ this.retainedDevices = new Set();
1536
+ this.protocolV2CallCounts = new Map();
1535
1537
  this.acquiringDevices = new Set();
1536
1538
  this.deviceProtocol = new Map();
1537
1539
  this.deviceProtocolHints = new Map();
@@ -1611,6 +1613,7 @@ class ElectronBleTransport {
1611
1613
  .invalidateLink(deviceId, 'Electron BLE device state cleaned')
1612
1614
  .catch(error => { var _a; return (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] link cleanup failed:', error); });
1613
1615
  this.connectedDevices.delete(deviceId);
1616
+ this.retainedDevices.delete(deviceId);
1614
1617
  this.deviceProtocol.delete(deviceId);
1615
1618
  this.deviceMtus.delete(deviceId);
1616
1619
  this.devicePacketCapacities.delete(deviceId);
@@ -1707,11 +1710,18 @@ class ElectronBleTransport {
1707
1710
  acquire(input) {
1708
1711
  var _a, _b, _c, _d, _e, _f, _g;
1709
1712
  return __awaiter(this, void 0, void 0, function* () {
1710
- const { uuid, forceCleanRunPromise, expectedProtocol } = input;
1713
+ const { uuid, forceCleanRunPromise, expectedProtocol, forceProtocolDetection } = input;
1711
1714
  if (!uuid) {
1712
1715
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleRequiredUUID);
1713
1716
  }
1714
- if (this.connectedDevices.has(uuid)) {
1717
+ const retainedToken = this.retainedDevices.has(uuid) &&
1718
+ !forceProtocolDetection &&
1719
+ (!expectedProtocol || expectedProtocol === 'V2') &&
1720
+ this.deviceProtocol.get(uuid) === 'V2'
1721
+ ? this.notificationTokens.get(uuid)
1722
+ : undefined;
1723
+ this.retainedDevices.delete(uuid);
1724
+ if (this.connectedDevices.has(uuid) && retainedToken === undefined) {
1715
1725
  yield this.release(uuid);
1716
1726
  }
1717
1727
  if (forceCleanRunPromise && this.runPromise) {
@@ -1729,6 +1739,12 @@ class ElectronBleTransport {
1729
1739
  if (!device) {
1730
1740
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound, `Device ${uuid} not found`);
1731
1741
  }
1742
+ if (retainedToken !== undefined &&
1743
+ this.notificationTokens.get(uuid) === retainedToken &&
1744
+ this.connectedDevices.has(uuid) &&
1745
+ this.deviceProtocol.get(uuid) === 'V2') {
1746
+ return Object.assign(Object.assign({}, toBleDescriptor({ id: device.id, name: device.name }, 'V2')), { uuid });
1747
+ }
1732
1748
  const protocolHint = expectedProtocol
1733
1749
  ? undefined
1734
1750
  : (_b = input.protocolHint) !== null && _b !== void 0 ? _b : this.deviceProtocolHints.get(uuid);
@@ -1792,12 +1808,19 @@ class ElectronBleTransport {
1792
1808
  }
1793
1809
  });
1794
1810
  }
1795
- release(id, _onclose, keepSession) {
1811
+ release(id, onclose, keepSession) {
1796
1812
  var _a;
1797
1813
  return __awaiter(this, void 0, void 0, function* () {
1798
1814
  try {
1799
- yield this.protocolV2Links.invalidateLink(id, 'Electron BLE transport released');
1800
- yield this.releaseLogical(id, keepSession);
1815
+ const retainLink = keepSession === true &&
1816
+ !onclose &&
1817
+ this.connectedDevices.has(id) &&
1818
+ this.deviceProtocol.get(id) === 'V2' &&
1819
+ !this.protocolV2CallCounts.has(id);
1820
+ if (!retainLink) {
1821
+ yield this.protocolV2Links.invalidateLink(id, 'Electron BLE transport released');
1822
+ }
1823
+ yield this.releaseLogical(id, keepSession, retainLink);
1801
1824
  }
1802
1825
  catch (error) {
1803
1826
  (_a = this.Log) === null || _a === void 0 ? void 0 : _a.error('[Electron BLE] release failed:', error);
@@ -1834,13 +1857,18 @@ class ElectronBleTransport {
1834
1857
  }
1835
1858
  });
1836
1859
  }
1837
- releaseLogical(id, keepSession) {
1860
+ releaseLogical(id, keepSession, retainLink = false) {
1838
1861
  var _a, _b, _c, _d;
1839
1862
  return __awaiter(this, void 0, void 0, function* () {
1840
1863
  try {
1841
1864
  if (!this.connectedDevices.has(id))
1842
1865
  return;
1843
- this.cleanupDeviceState(id);
1866
+ if (retainLink) {
1867
+ this.retainedDevices.add(id);
1868
+ }
1869
+ else {
1870
+ this.cleanupDeviceState(id);
1871
+ }
1844
1872
  const release = (_b = (_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle) === null || _b === void 0 ? void 0 : _b.release;
1845
1873
  if (!release) {
1846
1874
  if (!this.warnedMissingRelease) {
@@ -2272,19 +2300,27 @@ class ElectronBleTransport {
2272
2300
  });
2273
2301
  }
2274
2302
  callProtocolV2(uuid, name, data, options) {
2275
- var _a;
2303
+ var _a, _b, _c;
2276
2304
  return __awaiter(this, void 0, void 0, function* () {
2277
2305
  if (!this._messages || !this._messagesV2) {
2278
2306
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
2279
2307
  }
2280
2308
  const callOptions = options;
2309
+ this.protocolV2CallCounts.set(uuid, ((_a = this.protocolV2CallCounts.get(uuid)) !== null && _a !== void 0 ? _a : 0) + 1);
2281
2310
  try {
2282
2311
  return yield this.protocolV2Links.call(uuid, () => this.createProtocolV2Adapter(uuid), name, data, callOptions);
2283
2312
  }
2284
2313
  catch (e) {
2285
- (_a = this.Log) === null || _a === void 0 ? void 0 : _a.error('[Electron BLE] Protocol V2 call error:', e);
2314
+ (_b = this.Log) === null || _b === void 0 ? void 0 : _b.error('[Electron BLE] Protocol V2 call error:', e);
2286
2315
  throw e;
2287
2316
  }
2317
+ finally {
2318
+ const remaining = ((_c = this.protocolV2CallCounts.get(uuid)) !== null && _c !== void 0 ? _c : 1) - 1;
2319
+ if (remaining > 0)
2320
+ this.protocolV2CallCounts.set(uuid, remaining);
2321
+ else
2322
+ this.protocolV2CallCounts.delete(uuid);
2323
+ }
2288
2324
  });
2289
2325
  }
2290
2326
  createProtocolV2Adapter(uuid) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-transport-web-device",
3
- "version": "1.2.8-alpha.127",
3
+ "version": "1.2.8-alpha.129",
4
4
  "author": "OneKey",
5
5
  "homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
6
6
  "license": "MIT",
@@ -21,13 +21,13 @@
21
21
  "lint:fix": "eslint . --fix"
22
22
  },
23
23
  "dependencies": {
24
- "@onekeyfe/hd-shared": "1.2.8-alpha.127",
25
- "@onekeyfe/hd-transport": "1.2.8-alpha.127"
24
+ "@onekeyfe/hd-shared": "1.2.8-alpha.129",
25
+ "@onekeyfe/hd-transport": "1.2.8-alpha.129"
26
26
  },
27
27
  "devDependencies": {
28
- "@onekeyfe/hd-transport-electron": "1.2.8-alpha.127",
28
+ "@onekeyfe/hd-transport-electron": "1.2.8-alpha.129",
29
29
  "@types/w3c-web-usb": "^1.0.6",
30
30
  "@types/web-bluetooth": "^0.0.17"
31
31
  },
32
- "gitHead": "1fdcafbc25357d5f4372dc8f89f8c97e561fd153"
32
+ "gitHead": "e340d9447b375a9d79afd737bec50ff8c5a46aba"
33
33
  }
@@ -26,6 +26,7 @@ import { resolveBlePacketCapacity } from './ble-packet-capacity';
26
26
  import type { Deferred } from '@onekeyfe/hd-shared';
27
27
  import type { DesktopAPI, NobleBleIpcErrorResponse } from '@onekeyfe/hd-transport-electron';
28
28
  import type {
29
+ AcquireInput,
29
30
  OneKeyDeviceInfo,
30
31
  ProtocolType,
31
32
  ProtocolV2CallContext,
@@ -117,6 +118,10 @@ export default class ElectronBleTransport {
117
118
 
118
119
  private connectedDevices: Set<string> = new Set();
119
120
 
121
+ private retainedDevices: Set<string> = new Set();
122
+
123
+ private protocolV2CallCounts: Map<string, number> = new Map();
124
+
120
125
  /**
121
126
  * Devices whose acquire() is still in flight.
122
127
  *
@@ -234,6 +239,7 @@ export default class ElectronBleTransport {
234
239
  .invalidateLink(deviceId, 'Electron BLE device state cleaned')
235
240
  .catch(error => this.Log?.debug('[Electron BLE] link cleanup failed:', error));
236
241
  this.connectedDevices.delete(deviceId);
242
+ this.retainedDevices.delete(deviceId);
237
243
  this.deviceProtocol.delete(deviceId);
238
244
  this.deviceMtus.delete(deviceId);
239
245
  this.devicePacketCapacities.delete(deviceId);
@@ -358,13 +364,22 @@ export default class ElectronBleTransport {
358
364
  }
359
365
 
360
366
  async acquire(input: BleAcquireInput) {
361
- const { uuid, forceCleanRunPromise, expectedProtocol } = input;
367
+ const { uuid, forceCleanRunPromise, expectedProtocol, forceProtocolDetection }: AcquireInput =
368
+ input;
362
369
 
363
370
  if (!uuid) {
364
371
  throw ERRORS.TypedError(HardwareErrorCode.BleRequiredUUID);
365
372
  }
366
373
 
367
- if (this.connectedDevices.has(uuid)) {
374
+ const retainedToken =
375
+ this.retainedDevices.has(uuid) &&
376
+ !forceProtocolDetection &&
377
+ (!expectedProtocol || expectedProtocol === 'V2') &&
378
+ this.deviceProtocol.get(uuid) === 'V2'
379
+ ? this.notificationTokens.get(uuid)
380
+ : undefined;
381
+ this.retainedDevices.delete(uuid);
382
+ if (this.connectedDevices.has(uuid) && retainedToken === undefined) {
368
383
  await this.release(uuid);
369
384
  }
370
385
 
@@ -385,6 +400,18 @@ export default class ElectronBleTransport {
385
400
  if (!device) {
386
401
  throw ERRORS.TypedError(HardwareErrorCode.DeviceNotFound, `Device ${uuid} not found`);
387
402
  }
403
+ // A disconnect during the IPC lookup invalidates the retained notification generation.
404
+ if (
405
+ retainedToken !== undefined &&
406
+ this.notificationTokens.get(uuid) === retainedToken &&
407
+ this.connectedDevices.has(uuid) &&
408
+ this.deviceProtocol.get(uuid) === 'V2'
409
+ ) {
410
+ return {
411
+ ...toBleDescriptor({ id: device.id, name: device.name }, 'V2'),
412
+ uuid,
413
+ };
414
+ }
388
415
  const protocolHint = expectedProtocol
389
416
  ? undefined
390
417
  : input.protocolHint ?? this.deviceProtocolHints.get(uuid);
@@ -452,10 +479,18 @@ export default class ElectronBleTransport {
452
479
  }
453
480
  }
454
481
 
455
- async release(id: string, _onclose?: boolean, keepSession?: boolean) {
482
+ async release(id: string, onclose?: boolean, keepSession?: boolean) {
456
483
  try {
457
- await this.protocolV2Links.invalidateLink(id, 'Electron BLE transport released');
458
- await this.releaseLogical(id, keepSession);
484
+ const retainLink =
485
+ keepSession === true &&
486
+ !onclose &&
487
+ this.connectedDevices.has(id) &&
488
+ this.deviceProtocol.get(id) === 'V2' &&
489
+ !this.protocolV2CallCounts.has(id);
490
+ if (!retainLink) {
491
+ await this.protocolV2Links.invalidateLink(id, 'Electron BLE transport released');
492
+ }
493
+ await this.releaseLogical(id, keepSession, retainLink);
459
494
  } catch (error) {
460
495
  this.Log?.error('[Electron BLE] release failed:', error);
461
496
  this.cleanupDeviceState(id);
@@ -500,12 +535,15 @@ export default class ElectronBleTransport {
500
535
  }
501
536
  }
502
537
 
503
- // Logical release: link and subscription stay up for the next call. Renderer
504
- // listeners are still torn down, or fresh ones would double-process packets.
505
- private async releaseLogical(id: string, keepSession?: boolean) {
538
+ // Retained V2 links reuse their renderer listeners; legacy releases remove them.
539
+ private async releaseLogical(id: string, keepSession?: boolean, retainLink = false) {
506
540
  try {
507
541
  if (!this.connectedDevices.has(id)) return;
508
- this.cleanupDeviceState(id);
542
+ if (retainLink) {
543
+ this.retainedDevices.add(id);
544
+ } else {
545
+ this.cleanupDeviceState(id);
546
+ }
509
547
 
510
548
  const release = window.desktopApi?.nobleBle?.release;
511
549
  if (!release) {
@@ -1016,6 +1054,7 @@ export default class ElectronBleTransport {
1016
1054
 
1017
1055
  const callOptions = options;
1018
1056
 
1057
+ this.protocolV2CallCounts.set(uuid, (this.protocolV2CallCounts.get(uuid) ?? 0) + 1);
1019
1058
  try {
1020
1059
  return await this.protocolV2Links.call(
1021
1060
  uuid,
@@ -1027,6 +1066,10 @@ export default class ElectronBleTransport {
1027
1066
  } catch (e) {
1028
1067
  this.Log?.error('[Electron BLE] Protocol V2 call error:', e);
1029
1068
  throw e;
1069
+ } finally {
1070
+ const remaining = (this.protocolV2CallCounts.get(uuid) ?? 1) - 1;
1071
+ if (remaining > 0) this.protocolV2CallCounts.set(uuid, remaining);
1072
+ else this.protocolV2CallCounts.delete(uuid);
1030
1073
  }
1031
1074
  }
1032
1075