@onekeyfe/hd-transport-web-device 1.2.0-alpha.154 → 1.2.0-alpha.155
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/__tests__/electron-ble-transport.test.ts +125 -13
- package/dist/electron-ble-transport.d.ts +2 -1
- package/dist/electron-ble-transport.d.ts.map +1 -1
- package/dist/index.d.ts +23 -3
- package/dist/index.js +29 -21
- package/dist/webusb.d.ts +5 -1
- package/dist/webusb.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/electron-ble-transport.ts +49 -25
- package/src/webusb.ts +22 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import transport, { PROTOCOL_V2_CHANNEL_BLE_UART, bytesToHex } from '@onekeyfe/hd-transport';
|
|
2
|
-
import { HardwareErrorCode, createDeferred } from '@onekeyfe/hd-shared';
|
|
2
|
+
import { EBleDisconnectReason, HardwareErrorCode, createDeferred } from '@onekeyfe/hd-shared';
|
|
3
3
|
import EventEmitter from 'events';
|
|
4
4
|
|
|
5
5
|
import ElectronBleTransport from '../src/electron-ble-transport';
|
|
@@ -134,6 +134,30 @@ const configureTransport = (
|
|
|
134
134
|
return transport;
|
|
135
135
|
};
|
|
136
136
|
|
|
137
|
+
/**
|
|
138
|
+
* Wire the mock so acquire()'s Protocol V2 probe gets an answer. Without it
|
|
139
|
+
* detectProtocol throws and the test never reaches the disconnect behaviour.
|
|
140
|
+
*/
|
|
141
|
+
const echoProtocolV2 = (nobleBle: ReturnType<typeof createNobleBle>, deviceId: string) => {
|
|
142
|
+
let notificationHandler: ((id: string, data: string) => void) | undefined;
|
|
143
|
+
nobleBle.onNotification.mockImplementation(handler => {
|
|
144
|
+
notificationHandler = handler;
|
|
145
|
+
return jest.fn();
|
|
146
|
+
});
|
|
147
|
+
let responseSeq = 0;
|
|
148
|
+
nobleBle.write.mockImplementation(() => {
|
|
149
|
+
responseSeq += 1;
|
|
150
|
+
const response = ProtocolV2.encodeFrame(
|
|
151
|
+
schemas,
|
|
152
|
+
'Success',
|
|
153
|
+
{ message: 'ok' },
|
|
154
|
+
{ router: PROTOCOL_V2_CHANNEL_BLE_UART, seq: responseSeq }
|
|
155
|
+
);
|
|
156
|
+
setTimeout(() => notificationHandler?.(deviceId, bytesToHex(response)), 0);
|
|
157
|
+
return Promise.resolve();
|
|
158
|
+
});
|
|
159
|
+
};
|
|
160
|
+
|
|
137
161
|
describe('ElectronBleTransport protocol detection', () => {
|
|
138
162
|
afterEach(() => {
|
|
139
163
|
delete (global as any).window;
|
|
@@ -624,21 +648,19 @@ describe('ElectronBleTransport protocol detection', () => {
|
|
|
624
648
|
}
|
|
625
649
|
});
|
|
626
650
|
|
|
627
|
-
test('
|
|
628
|
-
|
|
651
|
+
test('subscribes to host disconnects once for the transport lifetime', async () => {
|
|
652
|
+
// The previous design registered a listener inside every acquire() and
|
|
653
|
+
// overwrote the cleanup entry without disposing the old one, so each
|
|
654
|
+
// acquire leaked a live handler and a delayed event from a superseded
|
|
655
|
+
// connection could tear down the current one. One transport-lifetime
|
|
656
|
+
// subscription removes that whole class of bug.
|
|
657
|
+
const device = { id: 'single-subscription-pro2-id', name: 'OneKey Pro 2' };
|
|
629
658
|
const nobleBle = createNobleBle(device);
|
|
630
659
|
let notificationHandler: ((deviceId: string, data: string) => void) | undefined;
|
|
631
|
-
const disconnectHandlers: Array<
|
|
632
|
-
(disconnectedDevice: { id: string; name: string | null }) => void
|
|
633
|
-
> = [];
|
|
634
660
|
nobleBle.onNotification.mockImplementation(handler => {
|
|
635
661
|
notificationHandler = handler;
|
|
636
662
|
return jest.fn();
|
|
637
663
|
});
|
|
638
|
-
nobleBle.onDeviceDisconnected.mockImplementation(handler => {
|
|
639
|
-
disconnectHandlers.push(handler);
|
|
640
|
-
return jest.fn();
|
|
641
|
-
});
|
|
642
664
|
let responseSeq = 0;
|
|
643
665
|
nobleBle.write.mockImplementation(() => {
|
|
644
666
|
responseSeq += 1;
|
|
@@ -654,14 +676,15 @@ describe('ElectronBleTransport protocol detection', () => {
|
|
|
654
676
|
const bleTransport = configureTransport(nobleBle);
|
|
655
677
|
|
|
656
678
|
try {
|
|
679
|
+
expect(nobleBle.onDeviceDisconnected).toHaveBeenCalledTimes(1);
|
|
680
|
+
|
|
657
681
|
await bleTransport.acquire({ uuid: device.id, expectedProtocol: 'V2' });
|
|
658
682
|
await bleTransport.acquire({ uuid: device.id, expectedProtocol: 'V2' });
|
|
659
683
|
|
|
660
|
-
|
|
661
|
-
|
|
684
|
+
expect(nobleBle.onDeviceDisconnected).toHaveBeenCalledTimes(1);
|
|
662
685
|
expect(bleTransport.getProtocolType(device.id)).toBe('V2');
|
|
663
686
|
await expect(
|
|
664
|
-
bleTransport.call(device.id, 'Ping', { message: 'after-
|
|
687
|
+
bleTransport.call(device.id, 'Ping', { message: 'after-reacquire' })
|
|
665
688
|
).resolves.toEqual({
|
|
666
689
|
type: 'Success',
|
|
667
690
|
message: { message: 'ok' },
|
|
@@ -671,6 +694,95 @@ describe('ElectronBleTransport protocol detection', () => {
|
|
|
671
694
|
}
|
|
672
695
|
});
|
|
673
696
|
|
|
697
|
+
test('reports a device that drops after a logical release (OK-60486)', async () => {
|
|
698
|
+
// A logical release keeps the native link alive for the keep-alive window.
|
|
699
|
+
// A drop during that window must still reach consumers, or the UI keeps
|
|
700
|
+
// showing the device as connected forever.
|
|
701
|
+
const device = { id: 'idle-drop-pro2-id', name: 'OneKey Pro 2' };
|
|
702
|
+
const nobleBle = createNobleBle(device);
|
|
703
|
+
const emitter = new EventEmitter();
|
|
704
|
+
let disconnectHandler:
|
|
705
|
+
| ((d: { id: string; name: string; reason?: EBleDisconnectReason }) => void)
|
|
706
|
+
| undefined;
|
|
707
|
+
nobleBle.onDeviceDisconnected.mockImplementation(handler => {
|
|
708
|
+
disconnectHandler = handler;
|
|
709
|
+
return jest.fn();
|
|
710
|
+
});
|
|
711
|
+
echoProtocolV2(nobleBle, device.id);
|
|
712
|
+
const transportDisconnect = jest.fn();
|
|
713
|
+
emitter.on('transport-device-disconnect', transportDisconnect);
|
|
714
|
+
const bleTransport = configureTransport(nobleBle, emitter);
|
|
715
|
+
|
|
716
|
+
await bleTransport.acquire({ uuid: device.id, expectedProtocol: 'V2' });
|
|
717
|
+
await bleTransport.release(device.id);
|
|
718
|
+
|
|
719
|
+
disconnectHandler?.({ ...device, reason: EBleDisconnectReason.DeviceDisconnected });
|
|
720
|
+
|
|
721
|
+
expect(transportDisconnect).toHaveBeenCalledWith({
|
|
722
|
+
id: device.id,
|
|
723
|
+
connectId: device.id,
|
|
724
|
+
name: device.name,
|
|
725
|
+
});
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
test('reports an idle keep-alive release as a device disconnect', async () => {
|
|
729
|
+
// The main process reclaims idle links on its own timer. That is still a
|
|
730
|
+
// closed link, and consumers track link liveness, so it is reported like
|
|
731
|
+
// any other drop. Nothing reconnects on its own, so the state settles once
|
|
732
|
+
// until the user acts.
|
|
733
|
+
const device = { id: 'keep-alive-pro2-id', name: 'OneKey Pro 2' };
|
|
734
|
+
const nobleBle = createNobleBle(device);
|
|
735
|
+
const emitter = new EventEmitter();
|
|
736
|
+
let disconnectHandler:
|
|
737
|
+
| ((d: { id: string; name: string; reason?: EBleDisconnectReason }) => void)
|
|
738
|
+
| undefined;
|
|
739
|
+
nobleBle.onDeviceDisconnected.mockImplementation(handler => {
|
|
740
|
+
disconnectHandler = handler;
|
|
741
|
+
return jest.fn();
|
|
742
|
+
});
|
|
743
|
+
echoProtocolV2(nobleBle, device.id);
|
|
744
|
+
const transportDisconnect = jest.fn();
|
|
745
|
+
emitter.on('transport-device-disconnect', transportDisconnect);
|
|
746
|
+
const bleTransport = configureTransport(nobleBle, emitter);
|
|
747
|
+
|
|
748
|
+
await bleTransport.acquire({ uuid: device.id, expectedProtocol: 'V2' });
|
|
749
|
+
disconnectHandler?.({ ...device, reason: EBleDisconnectReason.IdleKeepAlive });
|
|
750
|
+
|
|
751
|
+
expect(transportDisconnect).toHaveBeenCalledWith({
|
|
752
|
+
id: device.id,
|
|
753
|
+
connectId: device.id,
|
|
754
|
+
name: device.name,
|
|
755
|
+
});
|
|
756
|
+
// The link really is gone, so cached link state must be dropped too.
|
|
757
|
+
expect(bleTransport.getProtocolType(device.id)).toBeUndefined();
|
|
758
|
+
});
|
|
759
|
+
|
|
760
|
+
test('treats a disconnect with no reason as a real device drop', async () => {
|
|
761
|
+
// An older host bridge does not send `reason`; defaulting to "device left"
|
|
762
|
+
// preserves the pre-existing behaviour rather than silently ignoring it.
|
|
763
|
+
const device = { id: 'legacy-host-pro2-id', name: 'OneKey Pro 2' };
|
|
764
|
+
const nobleBle = createNobleBle(device);
|
|
765
|
+
const emitter = new EventEmitter();
|
|
766
|
+
let disconnectHandler: ((d: { id: string; name: string }) => void) | undefined;
|
|
767
|
+
nobleBle.onDeviceDisconnected.mockImplementation(handler => {
|
|
768
|
+
disconnectHandler = handler;
|
|
769
|
+
return jest.fn();
|
|
770
|
+
});
|
|
771
|
+
echoProtocolV2(nobleBle, device.id);
|
|
772
|
+
const transportDisconnect = jest.fn();
|
|
773
|
+
emitter.on('transport-device-disconnect', transportDisconnect);
|
|
774
|
+
const bleTransport = configureTransport(nobleBle, emitter);
|
|
775
|
+
|
|
776
|
+
await bleTransport.acquire({ uuid: device.id, expectedProtocol: 'V2' });
|
|
777
|
+
disconnectHandler?.(device);
|
|
778
|
+
|
|
779
|
+
expect(transportDisconnect).toHaveBeenCalledWith({
|
|
780
|
+
id: device.id,
|
|
781
|
+
connectId: device.id,
|
|
782
|
+
name: device.name,
|
|
783
|
+
});
|
|
784
|
+
});
|
|
785
|
+
|
|
674
786
|
test('preserves the active Protocol V2 link when the same schema is configured again', async () => {
|
|
675
787
|
const device = { id: 'stable-schema-pro2-id', name: 'OneKey Pro 2' };
|
|
676
788
|
const nobleBle = createNobleBle(device);
|
|
@@ -38,12 +38,13 @@ export default class ElectronBleTransport {
|
|
|
38
38
|
private warnedMissingRelease;
|
|
39
39
|
private notificationCleanups;
|
|
40
40
|
private mtuCleanups;
|
|
41
|
-
private
|
|
41
|
+
private hostDisconnectCleanup?;
|
|
42
42
|
private notificationTokens;
|
|
43
43
|
private nextNotificationToken;
|
|
44
44
|
private handleBluetoothError;
|
|
45
45
|
private cleanupDeviceState;
|
|
46
46
|
init(logger: any, emitter?: EventEmitter): void;
|
|
47
|
+
private subscribeHostDisconnects;
|
|
47
48
|
configure(signedData: any): void;
|
|
48
49
|
configureProtocolV2(signedData: any): void;
|
|
49
50
|
listen(): Promise<OneKeyDeviceInfo[]>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"electron-ble-transport.d.ts","sourceRoot":"","sources":["../src/electron-ble-transport.ts"],"names":[],"mappings":";
|
|
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
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as _onekeyfe_hd_transport from '@onekeyfe/hd-transport';
|
|
2
2
|
import _onekeyfe_hd_transport__default, { ProtocolV2UsbTransportBase, AcquireInput, TransportCallOptions, ProtocolV2Schemas, ProtocolV2CallContext, ProtocolType, OneKeyDeviceInfoBase, OneKeyDeviceInfo } from '@onekeyfe/hd-transport';
|
|
3
|
+
import EventEmitter from 'events';
|
|
3
4
|
import { Deferred } from '@onekeyfe/hd-shared';
|
|
4
5
|
import { DesktopAPI } from '@onekeyfe/hd-transport-electron';
|
|
5
|
-
import EventEmitter from 'events';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Device information with path and WebUSB device instance
|
|
@@ -42,6 +42,7 @@ declare class WebUsbTransport extends ProtocolV2UsbTransportBase<string> {
|
|
|
42
42
|
configured: boolean;
|
|
43
43
|
Log?: any;
|
|
44
44
|
usb?: USB;
|
|
45
|
+
emitter?: EventEmitter;
|
|
45
46
|
/**
|
|
46
47
|
* Cached list of connected devices
|
|
47
48
|
* This is essential for maintaining device references between operations
|
|
@@ -54,7 +55,12 @@ declare class WebUsbTransport extends ProtocolV2UsbTransportBase<string> {
|
|
|
54
55
|
/**
|
|
55
56
|
* Initialize WebUSB transport
|
|
56
57
|
*/
|
|
57
|
-
init(logger: any): void;
|
|
58
|
+
init(logger: any, emitter?: EventEmitter): void;
|
|
59
|
+
/**
|
|
60
|
+
* Announce that a USB device left. Called from the module-scoped disconnect
|
|
61
|
+
* listener, which is why it is public rather than inlined.
|
|
62
|
+
*/
|
|
63
|
+
emitDeviceDisconnect(path: string, device?: USBDevice): void;
|
|
58
64
|
/**
|
|
59
65
|
* Protocol type is a property of the physical device keyed by USB serial number.
|
|
60
66
|
* It can only change across a device reboot (e.g. normal ↔ bootloader mode), and
|
|
@@ -223,12 +229,26 @@ declare class ElectronBleTransport {
|
|
|
223
229
|
private warnedMissingRelease;
|
|
224
230
|
private notificationCleanups;
|
|
225
231
|
private mtuCleanups;
|
|
226
|
-
|
|
232
|
+
/**
|
|
233
|
+
* Transport-lifetime subscription to host BLE disconnects.
|
|
234
|
+
*
|
|
235
|
+
* This must NOT be scoped to acquire()/release(): a logical release keeps the
|
|
236
|
+
* native link alive for the keep-alive window, so a device that drops while
|
|
237
|
+
* idle would otherwise go unobserved and consumers would never learn it left
|
|
238
|
+
* (OK-60486).
|
|
239
|
+
*/
|
|
240
|
+
private hostDisconnectCleanup?;
|
|
227
241
|
private notificationTokens;
|
|
228
242
|
private nextNotificationToken;
|
|
229
243
|
private handleBluetoothError;
|
|
230
244
|
private cleanupDeviceState;
|
|
231
245
|
init(logger: any, emitter?: EventEmitter): void;
|
|
246
|
+
/**
|
|
247
|
+
* One host subscription for the whole transport lifetime. init() can run
|
|
248
|
+
* again after an SDK reset, so drop the previous listener first rather than
|
|
249
|
+
* stacking duplicates.
|
|
250
|
+
*/
|
|
251
|
+
private subscribeHostDisconnects;
|
|
232
252
|
configure(signedData: any): void;
|
|
233
253
|
configureProtocolV2(signedData: any): void;
|
|
234
254
|
listen(): Promise<OneKeyDeviceInfo[]>;
|
package/dist/index.js
CHANGED
|
@@ -68,6 +68,7 @@ function registerActiveWebUsbTransport(instance, usb) {
|
|
|
68
68
|
if (!path)
|
|
69
69
|
return;
|
|
70
70
|
activeWebUsbTransport === null || activeWebUsbTransport === void 0 ? void 0 : activeWebUsbTransport.markProtocolStale(path);
|
|
71
|
+
activeWebUsbTransport === null || activeWebUsbTransport === void 0 ? void 0 : activeWebUsbTransport.emitDeviceDisconnect(path, event.device);
|
|
71
72
|
});
|
|
72
73
|
}
|
|
73
74
|
class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
@@ -91,8 +92,9 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
91
92
|
this.endpointId = ENDPOINT_ID;
|
|
92
93
|
this.interfaceId = INTERFACE_ID;
|
|
93
94
|
}
|
|
94
|
-
init(logger) {
|
|
95
|
+
init(logger, emitter) {
|
|
95
96
|
this.Log = logger;
|
|
97
|
+
this.emitter = emitter;
|
|
96
98
|
const { usb } = navigator;
|
|
97
99
|
if (!usb) {
|
|
98
100
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'WebUSB is not supported by current browsers');
|
|
@@ -100,6 +102,14 @@ class WebUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
|
100
102
|
this.usb = usb;
|
|
101
103
|
registerActiveWebUsbTransport(this, usb);
|
|
102
104
|
}
|
|
105
|
+
emitDeviceDisconnect(path, device) {
|
|
106
|
+
var _a, _b;
|
|
107
|
+
(_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit(transport.TRANSPORT_EVENT.DEVICE_DISCONNECT, {
|
|
108
|
+
name: (_b = device === null || device === void 0 ? void 0 : device.productName) !== null && _b !== void 0 ? _b : '',
|
|
109
|
+
id: path,
|
|
110
|
+
connectId: path,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
103
113
|
markProtocolStale(path) {
|
|
104
114
|
this.staleProtocolPaths.add(path);
|
|
105
115
|
this.probedDeviceObjects.delete(path);
|
|
@@ -839,7 +849,6 @@ class ElectronBleTransport {
|
|
|
839
849
|
this.warnedMissingRelease = false;
|
|
840
850
|
this.notificationCleanups = new Map();
|
|
841
851
|
this.mtuCleanups = new Map();
|
|
842
|
-
this.disconnectCleanups = new Map();
|
|
843
852
|
this.notificationTokens = new Map();
|
|
844
853
|
this.nextNotificationToken = 1;
|
|
845
854
|
}
|
|
@@ -899,11 +908,6 @@ class ElectronBleTransport {
|
|
|
899
908
|
mtuCleanup();
|
|
900
909
|
this.mtuCleanups.delete(deviceId);
|
|
901
910
|
}
|
|
902
|
-
const disconnectCleanup = this.disconnectCleanups.get(deviceId);
|
|
903
|
-
if (disconnectCleanup) {
|
|
904
|
-
disconnectCleanup();
|
|
905
|
-
this.disconnectCleanups.delete(deviceId);
|
|
906
|
-
}
|
|
907
911
|
}
|
|
908
912
|
init(logger, emitter) {
|
|
909
913
|
var _a, _b;
|
|
@@ -912,8 +916,26 @@ class ElectronBleTransport {
|
|
|
912
916
|
if (!((_a = window.desktopApi) === null || _a === void 0 ? void 0 : _a.nobleBle)) {
|
|
913
917
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Noble BLE API is not available. Please ensure you are running in Electron with Noble support.');
|
|
914
918
|
}
|
|
919
|
+
this.subscribeHostDisconnects();
|
|
915
920
|
(_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug('[Electron BLE] Transport initialized');
|
|
916
921
|
}
|
|
922
|
+
subscribeHostDisconnects() {
|
|
923
|
+
var _a, _b, _c;
|
|
924
|
+
(_a = this.hostDisconnectCleanup) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
925
|
+
this.hostDisconnectCleanup = (_c = (_b = window.desktopApi) === null || _b === void 0 ? void 0 : _b.nobleBle) === null || _c === void 0 ? void 0 : _c.onDeviceDisconnected((disconnectedDevice) => {
|
|
926
|
+
var _a, _b, _c;
|
|
927
|
+
const uuid = disconnectedDevice === null || disconnectedDevice === void 0 ? void 0 : disconnectedDevice.id;
|
|
928
|
+
if (!uuid)
|
|
929
|
+
return;
|
|
930
|
+
this.cleanupDeviceState(uuid);
|
|
931
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[Electron BLE] Device link dropped:', uuid, (_b = disconnectedDevice.reason) !== null && _b !== void 0 ? _b : hdShared.EBleDisconnectReason.DeviceDisconnected);
|
|
932
|
+
(_c = this.emitter) === null || _c === void 0 ? void 0 : _c.emit(transport.TRANSPORT_EVENT.DEVICE_DISCONNECT, {
|
|
933
|
+
name: disconnectedDevice.name,
|
|
934
|
+
id: uuid,
|
|
935
|
+
connectId: uuid,
|
|
936
|
+
});
|
|
937
|
+
});
|
|
938
|
+
}
|
|
917
939
|
configure(signedData) {
|
|
918
940
|
this._messages = parseConfigure(signedData);
|
|
919
941
|
this.configured = true;
|
|
@@ -1004,7 +1026,6 @@ class ElectronBleTransport {
|
|
|
1004
1026
|
yield this.refreshBlePacketCapacity(uuid);
|
|
1005
1027
|
const cleanup = this.createNotificationSubscription(uuid);
|
|
1006
1028
|
this.notificationCleanups.set(uuid, cleanup);
|
|
1007
|
-
const connectionToken = this.notificationTokens.get(uuid);
|
|
1008
1029
|
const protocolType = yield this.detectProtocol(uuid, expectedProtocol, protocolHint);
|
|
1009
1030
|
if (protocolType === 'V2') {
|
|
1010
1031
|
(_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug('[Electron BLE] Protocol V2 write configured', {
|
|
@@ -1013,19 +1034,6 @@ class ElectronBleTransport {
|
|
|
1013
1034
|
packetCapacity: (_d = this.devicePacketCapacities.get(uuid)) !== null && _d !== void 0 ? _d : BLE_PACKET_SIZE_FALLBACK,
|
|
1014
1035
|
});
|
|
1015
1036
|
}
|
|
1016
|
-
const disconnectCleanup = window.desktopApi.nobleBle.onDeviceDisconnected((disconnectedDevice) => {
|
|
1017
|
-
var _a;
|
|
1018
|
-
if (disconnectedDevice.id === uuid &&
|
|
1019
|
-
this.notificationTokens.get(uuid) === connectionToken) {
|
|
1020
|
-
this.cleanupDeviceState(uuid);
|
|
1021
|
-
(_a = this.emitter) === null || _a === void 0 ? void 0 : _a.emit(transport.TRANSPORT_EVENT.DEVICE_DISCONNECT, {
|
|
1022
|
-
name: disconnectedDevice.name,
|
|
1023
|
-
id: disconnectedDevice.id,
|
|
1024
|
-
connectId: disconnectedDevice.id,
|
|
1025
|
-
});
|
|
1026
|
-
}
|
|
1027
|
-
});
|
|
1028
|
-
this.disconnectCleanups.set(uuid, disconnectCleanup);
|
|
1029
1037
|
return Object.assign(Object.assign({}, toBleDescriptor({ id: device.id, name: device.name }, protocolType)), { uuid });
|
|
1030
1038
|
}
|
|
1031
1039
|
catch (error) {
|
package/dist/webusb.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/// <reference types="w3c-web-usb" />
|
|
2
|
+
/// <reference types="node" />
|
|
2
3
|
import transport, { ProtocolV2UsbTransportBase } from '@onekeyfe/hd-transport';
|
|
4
|
+
import type EventEmitter from 'events';
|
|
3
5
|
import type { AcquireInput, OneKeyDeviceInfoBase, ProtocolType, ProtocolV2CallContext, ProtocolV2Schemas, TransportCallOptions } from '@onekeyfe/hd-transport';
|
|
4
6
|
export interface DeviceInfo extends OneKeyDeviceInfoBase {
|
|
5
7
|
path: string;
|
|
@@ -21,12 +23,14 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
21
23
|
configured: boolean;
|
|
22
24
|
Log?: any;
|
|
23
25
|
usb?: USB;
|
|
26
|
+
emitter?: EventEmitter;
|
|
24
27
|
deviceList: Array<DeviceInfo>;
|
|
25
28
|
configurationId: number;
|
|
26
29
|
endpointId: number;
|
|
27
30
|
interfaceId: number;
|
|
28
31
|
constructor();
|
|
29
|
-
init(logger: any): void;
|
|
32
|
+
init(logger: any, emitter?: EventEmitter): void;
|
|
33
|
+
emitDeviceDisconnect(path: string, device?: USBDevice): void;
|
|
30
34
|
markProtocolStale(path: string): void;
|
|
31
35
|
configure(signedData: any): void;
|
|
32
36
|
configureProtocolV2(signedData: any): void;
|
package/dist/webusb.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webusb.d.ts","sourceRoot":"","sources":["../src/webusb.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"webusb.d.ts","sourceRoot":"","sources":["../src/webusb.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hd-transport-web-device",
|
|
3
|
-
"version": "1.2.0-alpha.
|
|
3
|
+
"version": "1.2.0-alpha.155",
|
|
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.0-alpha.
|
|
25
|
-
"@onekeyfe/hd-transport": "1.2.0-alpha.
|
|
24
|
+
"@onekeyfe/hd-shared": "1.2.0-alpha.155",
|
|
25
|
+
"@onekeyfe/hd-transport": "1.2.0-alpha.155"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@onekeyfe/hd-transport-electron": "1.2.0-alpha.
|
|
28
|
+
"@onekeyfe/hd-transport-electron": "1.2.0-alpha.155",
|
|
29
29
|
"@types/w3c-web-usb": "^1.0.6",
|
|
30
30
|
"@types/web-bluetooth": "^0.0.17"
|
|
31
31
|
},
|
|
32
|
-
"gitHead": "
|
|
32
|
+
"gitHead": "922cb4e29b2d89511c9b67c25840294335cb8005"
|
|
33
33
|
}
|
|
@@ -11,6 +11,7 @@ import transport, {
|
|
|
11
11
|
writeProtocolV2BleFrame,
|
|
12
12
|
} from '@onekeyfe/hd-transport';
|
|
13
13
|
import {
|
|
14
|
+
EBleDisconnectReason,
|
|
14
15
|
ERRORS,
|
|
15
16
|
HardwareErrorCode,
|
|
16
17
|
HardwareErrorCodeMessage,
|
|
@@ -144,7 +145,15 @@ export default class ElectronBleTransport {
|
|
|
144
145
|
|
|
145
146
|
private mtuCleanups: Map<string, () => void> = new Map();
|
|
146
147
|
|
|
147
|
-
|
|
148
|
+
/**
|
|
149
|
+
* Transport-lifetime subscription to host BLE disconnects.
|
|
150
|
+
*
|
|
151
|
+
* This must NOT be scoped to acquire()/release(): a logical release keeps the
|
|
152
|
+
* native link alive for the keep-alive window, so a device that drops while
|
|
153
|
+
* idle would otherwise go unobserved and consumers would never learn it left
|
|
154
|
+
* (OK-60486).
|
|
155
|
+
*/
|
|
156
|
+
private hostDisconnectCleanup?: () => void;
|
|
148
157
|
|
|
149
158
|
private notificationTokens: Map<string, number> = new Map();
|
|
150
159
|
|
|
@@ -212,12 +221,6 @@ export default class ElectronBleTransport {
|
|
|
212
221
|
mtuCleanup();
|
|
213
222
|
this.mtuCleanups.delete(deviceId);
|
|
214
223
|
}
|
|
215
|
-
|
|
216
|
-
const disconnectCleanup = this.disconnectCleanups.get(deviceId);
|
|
217
|
-
if (disconnectCleanup) {
|
|
218
|
-
disconnectCleanup();
|
|
219
|
-
this.disconnectCleanups.delete(deviceId);
|
|
220
|
-
}
|
|
221
224
|
}
|
|
222
225
|
|
|
223
226
|
init(logger: any, emitter?: EventEmitter) {
|
|
@@ -231,9 +234,48 @@ export default class ElectronBleTransport {
|
|
|
231
234
|
);
|
|
232
235
|
}
|
|
233
236
|
|
|
237
|
+
this.subscribeHostDisconnects();
|
|
238
|
+
|
|
234
239
|
this.Log?.debug('[Electron BLE] Transport initialized');
|
|
235
240
|
}
|
|
236
241
|
|
|
242
|
+
/**
|
|
243
|
+
* One host subscription for the whole transport lifetime. init() can run
|
|
244
|
+
* again after an SDK reset, so drop the previous listener first rather than
|
|
245
|
+
* stacking duplicates.
|
|
246
|
+
*/
|
|
247
|
+
private subscribeHostDisconnects() {
|
|
248
|
+
this.hostDisconnectCleanup?.();
|
|
249
|
+
this.hostDisconnectCleanup = window.desktopApi?.nobleBle?.onDeviceDisconnected(
|
|
250
|
+
(disconnectedDevice: { id: string; name: string; reason?: EBleDisconnectReason }) => {
|
|
251
|
+
const uuid = disconnectedDevice?.id;
|
|
252
|
+
if (!uuid) return;
|
|
253
|
+
|
|
254
|
+
// The link is gone, so renderer-side state must go with it; the next
|
|
255
|
+
// acquire reconnects from scratch.
|
|
256
|
+
this.cleanupDeviceState(uuid);
|
|
257
|
+
|
|
258
|
+
// Every link drop is reported, including the main process reclaiming
|
|
259
|
+
// an idle link on its keep-alive timer: consumers track whether a BLE
|
|
260
|
+
// link is live, not whether the peripheral is theoretically in range,
|
|
261
|
+
// and a link we closed ourselves is still a closed link. Nothing
|
|
262
|
+
// reconnects on its own, so this settles once until the user acts.
|
|
263
|
+
// `reason` is carried for diagnostics only — behaviour is uniform.
|
|
264
|
+
this.Log?.debug(
|
|
265
|
+
'[Electron BLE] Device link dropped:',
|
|
266
|
+
uuid,
|
|
267
|
+
disconnectedDevice.reason ?? EBleDisconnectReason.DeviceDisconnected
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
this.emitter?.emit(TRANSPORT_EVENT.DEVICE_DISCONNECT, {
|
|
271
|
+
name: disconnectedDevice.name,
|
|
272
|
+
id: uuid,
|
|
273
|
+
connectId: uuid,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
|
|
237
279
|
configure(signedData: any) {
|
|
238
280
|
this._messages = parseConfigure(signedData);
|
|
239
281
|
this.configured = true;
|
|
@@ -330,7 +372,6 @@ export default class ElectronBleTransport {
|
|
|
330
372
|
|
|
331
373
|
const cleanup = this.createNotificationSubscription(uuid);
|
|
332
374
|
this.notificationCleanups.set(uuid, cleanup);
|
|
333
|
-
const connectionToken = this.notificationTokens.get(uuid);
|
|
334
375
|
|
|
335
376
|
const protocolType = await this.detectProtocol(uuid, expectedProtocol, protocolHint);
|
|
336
377
|
if (protocolType === 'V2') {
|
|
@@ -341,23 +382,6 @@ export default class ElectronBleTransport {
|
|
|
341
382
|
});
|
|
342
383
|
}
|
|
343
384
|
|
|
344
|
-
const disconnectCleanup = window.desktopApi.nobleBle.onDeviceDisconnected(
|
|
345
|
-
(disconnectedDevice: any) => {
|
|
346
|
-
if (
|
|
347
|
-
disconnectedDevice.id === uuid &&
|
|
348
|
-
this.notificationTokens.get(uuid) === connectionToken
|
|
349
|
-
) {
|
|
350
|
-
this.cleanupDeviceState(uuid);
|
|
351
|
-
this.emitter?.emit(TRANSPORT_EVENT.DEVICE_DISCONNECT, {
|
|
352
|
-
name: disconnectedDevice.name,
|
|
353
|
-
id: disconnectedDevice.id,
|
|
354
|
-
connectId: disconnectedDevice.id,
|
|
355
|
-
});
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
);
|
|
359
|
-
this.disconnectCleanups.set(uuid, disconnectCleanup);
|
|
360
|
-
|
|
361
385
|
return {
|
|
362
386
|
...toBleDescriptor({ id: device.id, name: device.name }, protocolType),
|
|
363
387
|
uuid,
|
package/src/webusb.ts
CHANGED
|
@@ -8,6 +8,7 @@ import transport, {
|
|
|
8
8
|
PROTOCOL_V2_FRAME_MAX_BYTES,
|
|
9
9
|
ProtocolV2LinkError,
|
|
10
10
|
ProtocolV2UsbTransportBase,
|
|
11
|
+
TRANSPORT_EVENT,
|
|
11
12
|
probeProtocolV2 as probeProtocolV2Helper,
|
|
12
13
|
} from '@onekeyfe/hd-transport';
|
|
13
14
|
import {
|
|
@@ -21,6 +22,7 @@ import {
|
|
|
21
22
|
} from '@onekeyfe/hd-shared';
|
|
22
23
|
import ByteBuffer from 'bytebuffer';
|
|
23
24
|
|
|
25
|
+
import type EventEmitter from 'events';
|
|
24
26
|
import type {
|
|
25
27
|
AcquireInput,
|
|
26
28
|
OneKeyDeviceInfoBase,
|
|
@@ -87,6 +89,10 @@ function registerActiveWebUsbTransport(instance: WebUsbTransport, usb: USB) {
|
|
|
87
89
|
const path = resolveOneKeyUsbDevicePath(event.device);
|
|
88
90
|
if (!path) return;
|
|
89
91
|
activeWebUsbTransport?.markProtocolStale(path);
|
|
92
|
+
// WebUSB has no device-list poller behind it, so this event is the only
|
|
93
|
+
// signal that the device is gone. Without it consumers never see a
|
|
94
|
+
// DEVICE.DISCONNECT for USB (OK-60486).
|
|
95
|
+
activeWebUsbTransport?.emitDeviceDisconnect(path, event.device);
|
|
90
96
|
});
|
|
91
97
|
}
|
|
92
98
|
|
|
@@ -134,6 +140,8 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
134
140
|
|
|
135
141
|
usb?: USB;
|
|
136
142
|
|
|
143
|
+
emitter?: EventEmitter;
|
|
144
|
+
|
|
137
145
|
/**
|
|
138
146
|
* Cached list of connected devices
|
|
139
147
|
* This is essential for maintaining device references between operations
|
|
@@ -157,8 +165,9 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
157
165
|
/**
|
|
158
166
|
* Initialize WebUSB transport
|
|
159
167
|
*/
|
|
160
|
-
init(logger: any) {
|
|
168
|
+
init(logger: any, emitter?: EventEmitter) {
|
|
161
169
|
this.Log = logger;
|
|
170
|
+
this.emitter = emitter;
|
|
162
171
|
|
|
163
172
|
const { usb } = navigator;
|
|
164
173
|
if (!usb) {
|
|
@@ -171,6 +180,18 @@ export default class WebUsbTransport extends ProtocolV2UsbTransportBase<string>
|
|
|
171
180
|
registerActiveWebUsbTransport(this, usb);
|
|
172
181
|
}
|
|
173
182
|
|
|
183
|
+
/**
|
|
184
|
+
* Announce that a USB device left. Called from the module-scoped disconnect
|
|
185
|
+
* listener, which is why it is public rather than inlined.
|
|
186
|
+
*/
|
|
187
|
+
emitDeviceDisconnect(path: string, device?: USBDevice) {
|
|
188
|
+
this.emitter?.emit(TRANSPORT_EVENT.DEVICE_DISCONNECT, {
|
|
189
|
+
name: device?.productName ?? '',
|
|
190
|
+
id: path,
|
|
191
|
+
connectId: path,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
174
195
|
/**
|
|
175
196
|
* Protocol type is a property of the physical device keyed by USB serial number.
|
|
176
197
|
* It can only change across a device reboot (e.g. normal ↔ bootloader mode), and
|