@onekeyfe/hd-common-connect-sdk 1.2.8-alpha.2 → 1.2.8-alpha.20
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/README.md +27 -0
- package/__tests__/device-state-events.test.ts +62 -5
- package/dist/index.d.ts +6 -1
- package/dist/index.js +35 -7
- package/package.json +9 -9
- package/src/index.ts +36 -0
package/README.md
CHANGED
|
@@ -28,6 +28,33 @@ function init() {
|
|
|
28
28
|
}
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
+
## Desktop USB identity discovery
|
|
32
|
+
|
|
33
|
+
`probeUsbDeviceIdentities({ commType: 'webusb' | 'bridge' })` is a named export for
|
|
34
|
+
choosing a Desktop connection before switching the active SDK. It does not require
|
|
35
|
+
SDK initialization, dispose Core, change BLE settings, or open a wallet session.
|
|
36
|
+
Concurrent probes for the same mode share one pending request.
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
import { probeUsbDeviceIdentities } from '@onekeyfe/hd-common-connect-sdk';
|
|
40
|
+
|
|
41
|
+
const devices = await probeUsbDeviceIdentities({ commType: 'webusb' });
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Each result contains `path`, `serialNo`, `deviceId`, `deviceType`, `connectProtocol`,
|
|
45
|
+
and `commType`. `path` locates the USB endpoint; compare `serialNo` with the target's
|
|
46
|
+
physical identity, or `deviceId` for legacy devices without a physical serial.
|
|
47
|
+
Missing identities remain `null`; a descriptor path is never substituted for them.
|
|
48
|
+
The protocol comes from an active device response. V1 uses read-only Features;
|
|
49
|
+
V2 uses hardware DeviceInfo and does not query a wallet session.
|
|
50
|
+
|
|
51
|
+
Busy, ambiguous, disconnected, or unverified endpoints are omitted. An active
|
|
52
|
+
WebUSB request's identity can be reused only for the exact USB handle it owns.
|
|
53
|
+
Discovery releases only handles it acquired and does not replace the active SDK's
|
|
54
|
+
disconnect observer. An empty result is not proof that the device is absent.
|
|
55
|
+
Keep the selected transport fixed during a hardware operation; reevaluate it
|
|
56
|
+
between operations. Firmware updates must separately validate their live target.
|
|
57
|
+
|
|
31
58
|
## Docs
|
|
32
59
|
|
|
33
60
|
Documentation is available [Hardware SDK Getting Started](https://developer.onekey.so/en/hardware-sdk/getting-started)
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import { readFileSync } from 'fs';
|
|
2
2
|
import { resolve } from 'path';
|
|
3
3
|
import EventEmitter from 'events';
|
|
4
|
-
import {
|
|
5
|
-
|
|
4
|
+
import {
|
|
5
|
+
CORE_EVENT,
|
|
6
|
+
UI_REQUEST,
|
|
7
|
+
initCore,
|
|
8
|
+
probeUsbDeviceIdentitiesWithTransport,
|
|
9
|
+
} from '@onekeyfe/hd-core';
|
|
10
|
+
import { HardwareErrorCode, createDeferred } from '@onekeyfe/hd-shared';
|
|
11
|
+
import HttpTransport from '@onekeyfe/hd-transport-http';
|
|
6
12
|
|
|
7
|
-
import sdkExport, { messagePromises } from '../src';
|
|
13
|
+
import sdkExport, { messagePromises, probeUsbDeviceIdentities } from '../src';
|
|
8
14
|
|
|
9
15
|
jest.mock('../../core/src/data/config', () => ({
|
|
10
16
|
getSDKVersion: jest.fn(() => '1.0.0-test'),
|
|
@@ -15,9 +21,10 @@ jest.mock('@onekeyfe/hd-core', () => ({
|
|
|
15
21
|
__esModule: true,
|
|
16
22
|
default: (options: unknown) => options,
|
|
17
23
|
initCore: jest.fn(),
|
|
24
|
+
probeUsbDeviceIdentitiesWithTransport: jest.fn(),
|
|
18
25
|
}));
|
|
19
|
-
jest.mock('@onekeyfe/hd-transport-http', () => ({}));
|
|
20
|
-
jest.mock('@onekeyfe/hd-transport-web-device', () => ({}));
|
|
26
|
+
jest.mock('@onekeyfe/hd-transport-http', () => ({ __esModule: true, default: jest.fn() }));
|
|
27
|
+
jest.mock('@onekeyfe/hd-transport-web-device', () => ({ WebUsbTransport: jest.fn() }));
|
|
21
28
|
jest.mock('@onekeyfe/hd-transport-lowlevel', () => ({}));
|
|
22
29
|
jest.mock('@onekeyfe/hd-transport-emulator', () => ({}));
|
|
23
30
|
|
|
@@ -56,6 +63,17 @@ describe('SDK request cleanup', () => {
|
|
|
56
63
|
await sdk.dispose();
|
|
57
64
|
});
|
|
58
65
|
|
|
66
|
+
test.each([
|
|
67
|
+
[HardwareErrorCode.BlePoweredOff, UI_REQUEST.BLUETOOTH_POWERED_OFF],
|
|
68
|
+
[HardwareErrorCode.BleUnsupported, UI_REQUEST.BLUETOOTH_UNSUPPORTED],
|
|
69
|
+
[HardwareErrorCode.BleLocationError, UI_REQUEST.LOCATION_PERMISSION],
|
|
70
|
+
])('emits the adapter UI request for error %s', async (code, type) => {
|
|
71
|
+
const response = { success: false, payload: { code, error: 'Adapter unavailable' } };
|
|
72
|
+
core.handleMessage.mockResolvedValueOnce(response);
|
|
73
|
+
await expect(sdk.call({ method: 'searchDevices' })).resolves.toEqual(response);
|
|
74
|
+
expect(core.handleMessage).toHaveBeenNthCalledWith(2, expect.objectContaining({ type }));
|
|
75
|
+
});
|
|
76
|
+
|
|
59
77
|
test('removes only the completed request when calls settle out of order', async () => {
|
|
60
78
|
const first = createDeferred<unknown>();
|
|
61
79
|
const second = createDeferred<unknown>();
|
|
@@ -71,6 +89,45 @@ describe('SDK request cleanup', () => {
|
|
|
71
89
|
expect(Object.keys(messagePromises)).toHaveLength(0);
|
|
72
90
|
});
|
|
73
91
|
|
|
92
|
+
test.each(['webusb'] as const)(
|
|
93
|
+
'probes %s without disposing an outstanding BLE request and coalesces concurrent probes',
|
|
94
|
+
async commType => {
|
|
95
|
+
const pending = createDeferred<unknown>();
|
|
96
|
+
const discovery = createDeferred<never[]>();
|
|
97
|
+
core.handleMessage.mockReturnValueOnce(pending.promise);
|
|
98
|
+
const request = sdk.call({ method: 'getDeviceState' });
|
|
99
|
+
jest
|
|
100
|
+
.mocked(probeUsbDeviceIdentitiesWithTransport)
|
|
101
|
+
.mockReset()
|
|
102
|
+
.mockReturnValueOnce(discovery.promise);
|
|
103
|
+
const initializationCount = jest.mocked(initCore).mock.calls.length;
|
|
104
|
+
const first = probeUsbDeviceIdentities({ commType });
|
|
105
|
+
const second = probeUsbDeviceIdentities({ commType });
|
|
106
|
+
expect(second).toBe(first);
|
|
107
|
+
expect(probeUsbDeviceIdentitiesWithTransport).toHaveBeenCalledTimes(1);
|
|
108
|
+
discovery.resolve([]);
|
|
109
|
+
await first;
|
|
110
|
+
expect(core.dispose).not.toHaveBeenCalled();
|
|
111
|
+
expect(initCore).toHaveBeenCalledTimes(initializationCount);
|
|
112
|
+
expect(core.listenerCount(CORE_EVENT)).toBe(1);
|
|
113
|
+
expect(Object.keys(messagePromises)).toHaveLength(1);
|
|
114
|
+
pending.resolve({ success: true, payload: {} });
|
|
115
|
+
await expect(request).resolves.toMatchObject({ success: true });
|
|
116
|
+
expect(Object.keys(messagePromises)).toHaveLength(0);
|
|
117
|
+
}
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
test('rejects the withdrawn Bridge probe without creating a transport or affecting BLE', async () => {
|
|
121
|
+
jest.mocked(HttpTransport).mockClear();
|
|
122
|
+
jest.mocked(probeUsbDeviceIdentitiesWithTransport).mockClear();
|
|
123
|
+
await expect(probeUsbDeviceIdentities({ commType: 'bridge' })).rejects.toMatchObject({
|
|
124
|
+
errorCode: HardwareErrorCode.CallMethodInvalidParameter,
|
|
125
|
+
});
|
|
126
|
+
expect(HttpTransport).not.toHaveBeenCalled();
|
|
127
|
+
expect(probeUsbDeviceIdentitiesWithTransport).not.toHaveBeenCalled();
|
|
128
|
+
expect(core.dispose).not.toHaveBeenCalled();
|
|
129
|
+
});
|
|
130
|
+
|
|
74
131
|
test('waits for Core disposal and does not retain old listeners after reinitialization', async () => {
|
|
75
132
|
const cleanup = createDeferred<void>();
|
|
76
133
|
core.dispose.mockImplementationOnce(async () => {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import * as _onekeyfe_hd_core from '@onekeyfe/hd-core';
|
|
2
|
+
import { UsbDeviceIdentity } from '@onekeyfe/hd-core';
|
|
2
3
|
import { Deferred } from '@onekeyfe/hd-shared';
|
|
3
4
|
|
|
5
|
+
/** Identify WebUSB without disposing the current SDK, changing settings, or opening a wallet. */
|
|
6
|
+
declare const probeUsbDeviceIdentities: ({ commType, }: {
|
|
7
|
+
commType: UsbDeviceIdentity['commType'];
|
|
8
|
+
}) => Promise<UsbDeviceIdentity[]>;
|
|
4
9
|
declare const messagePromises: {
|
|
5
10
|
[key: number]: Deferred<any>;
|
|
6
11
|
};
|
|
7
12
|
declare const HardwareCommonConnectSdk: _onekeyfe_hd_core.CoreApi;
|
|
8
13
|
|
|
9
|
-
export { HardwareCommonConnectSdk as default, messagePromises };
|
|
14
|
+
export { HardwareCommonConnectSdk as default, messagePromises, probeUsbDeviceIdentities };
|
package/dist/index.js
CHANGED
|
@@ -607,6 +607,30 @@ var getTransport = function (env) { return __awaiter(void 0, void 0, void 0, fun
|
|
|
607
607
|
}); };
|
|
608
608
|
var _core;
|
|
609
609
|
var _settings = HardwareSdk.parseConnectSettings();
|
|
610
|
+
var usbIdentityProbeTransports = {};
|
|
611
|
+
var usbIdentityProbes = {};
|
|
612
|
+
/** Identify WebUSB without disposing the current SDK, changing settings, or opening a wallet. */
|
|
613
|
+
var probeUsbDeviceIdentities = function (_a) {
|
|
614
|
+
var commType = _a.commType;
|
|
615
|
+
if (commType !== 'webusb') {
|
|
616
|
+
return Promise.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallMethodInvalidParameter));
|
|
617
|
+
}
|
|
618
|
+
var pending = usbIdentityProbes[commType];
|
|
619
|
+
if (pending)
|
|
620
|
+
return pending;
|
|
621
|
+
var transport = usbIdentityProbeTransports[commType];
|
|
622
|
+
if (!transport) {
|
|
623
|
+
transport = new hdTransportWebDevice.WebUsbTransport(true);
|
|
624
|
+
usbIdentityProbeTransports[commType] = transport;
|
|
625
|
+
}
|
|
626
|
+
var probe = HardwareSdk.probeUsbDeviceIdentitiesWithTransport(transport, commType, function (descriptor) {
|
|
627
|
+
return _core === null || _core === void 0 ? void 0 : _core.getOwnedUsbDeviceIdentity(descriptor);
|
|
628
|
+
}).finally(function () {
|
|
629
|
+
delete usbIdentityProbes[commType];
|
|
630
|
+
});
|
|
631
|
+
usbIdentityProbes[commType] = probe;
|
|
632
|
+
return probe;
|
|
633
|
+
};
|
|
610
634
|
var _messageID = 0;
|
|
611
635
|
var messagePromises = {};
|
|
612
636
|
var dispose = function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
@@ -736,18 +760,18 @@ var init = function (settings, _, plugin) { return __awaiter(void 0, void 0, voi
|
|
|
736
760
|
}); };
|
|
737
761
|
var call = function (params) { return __awaiter(void 0, void 0, void 0, function () {
|
|
738
762
|
var blockLog, response, error_2;
|
|
739
|
-
var _a, _b, _c;
|
|
740
|
-
return __generator(this, function (
|
|
741
|
-
switch (
|
|
763
|
+
var _a, _b, _c, _d;
|
|
764
|
+
return __generator(this, function (_e) {
|
|
765
|
+
switch (_e.label) {
|
|
742
766
|
case 0:
|
|
743
767
|
blockLog = HardwareSdk.getLogBlockLabel(params);
|
|
744
768
|
Log.debug('call: ', blockLog !== null && blockLog !== void 0 ? blockLog : params);
|
|
745
|
-
|
|
769
|
+
_e.label = 1;
|
|
746
770
|
case 1:
|
|
747
|
-
|
|
771
|
+
_e.trys.push([1, 3, , 4]);
|
|
748
772
|
return [4 /*yield*/, postMessage({ event: HardwareSdk.IFRAME.CALL, type: HardwareSdk.IFRAME.CALL, payload: params })];
|
|
749
773
|
case 2:
|
|
750
|
-
response =
|
|
774
|
+
response = _e.sent();
|
|
751
775
|
if (response) {
|
|
752
776
|
Log.debug('response: ', blockLog ? '[REDACTED]' : response);
|
|
753
777
|
if (!response.success) {
|
|
@@ -760,12 +784,15 @@ var call = function (params) { return __awaiter(void 0, void 0, void 0, function
|
|
|
760
784
|
if (((_c = response.payload) === null || _c === void 0 ? void 0 : _c.code) === hdShared.HardwareErrorCode.BlePermissionError) {
|
|
761
785
|
postMessage(HardwareSdk.createUiMessage(HardwareSdk.UI_REQUEST.BLUETOOTH_PERMISSION), false);
|
|
762
786
|
}
|
|
787
|
+
if (((_d = response.payload) === null || _d === void 0 ? void 0 : _d.code) === hdShared.HardwareErrorCode.BleLocationError) {
|
|
788
|
+
postMessage(HardwareSdk.createUiMessage(HardwareSdk.UI_REQUEST.LOCATION_PERMISSION), false);
|
|
789
|
+
}
|
|
763
790
|
}
|
|
764
791
|
return [2 /*return*/, response];
|
|
765
792
|
}
|
|
766
793
|
return [2 /*return*/, HardwareSdk.createErrorMessage(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallMethodNotResponse))];
|
|
767
794
|
case 3:
|
|
768
|
-
error_2 =
|
|
795
|
+
error_2 = _e.sent();
|
|
769
796
|
Log.error('__call error: ', error_2);
|
|
770
797
|
return [2 /*return*/, HardwareSdk.createErrorMessage(error_2)];
|
|
771
798
|
case 4: return [2 /*return*/];
|
|
@@ -787,3 +814,4 @@ var HardwareCommonConnectSdk = HardwareSdk__default["default"]({
|
|
|
787
814
|
|
|
788
815
|
exports["default"] = HardwareCommonConnectSdk;
|
|
789
816
|
exports.messagePromises = messagePromises;
|
|
817
|
+
exports.probeUsbDeviceIdentities = probeUsbDeviceIdentities;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hd-common-connect-sdk",
|
|
3
|
-
"version": "1.2.8-alpha.
|
|
3
|
+
"version": "1.2.8-alpha.20",
|
|
4
4
|
"author": "OneKey",
|
|
5
5
|
"homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
|
|
6
6
|
"license": "ISC",
|
|
@@ -21,13 +21,13 @@
|
|
|
21
21
|
"lint:fix": "eslint . --fix"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@onekeyfe/hd-core": "1.2.8-alpha.
|
|
25
|
-
"@onekeyfe/hd-shared": "1.2.8-alpha.
|
|
26
|
-
"@onekeyfe/hd-transport-emulator": "1.2.8-alpha.
|
|
27
|
-
"@onekeyfe/hd-transport-http": "1.2.8-alpha.
|
|
28
|
-
"@onekeyfe/hd-transport-lowlevel": "1.2.8-alpha.
|
|
29
|
-
"@onekeyfe/hd-transport-usb": "1.2.8-alpha.
|
|
30
|
-
"@onekeyfe/hd-transport-web-device": "1.2.8-alpha.
|
|
24
|
+
"@onekeyfe/hd-core": "1.2.8-alpha.20",
|
|
25
|
+
"@onekeyfe/hd-shared": "1.2.8-alpha.20",
|
|
26
|
+
"@onekeyfe/hd-transport-emulator": "1.2.8-alpha.20",
|
|
27
|
+
"@onekeyfe/hd-transport-http": "1.2.8-alpha.20",
|
|
28
|
+
"@onekeyfe/hd-transport-lowlevel": "1.2.8-alpha.20",
|
|
29
|
+
"@onekeyfe/hd-transport-usb": "1.2.8-alpha.20",
|
|
30
|
+
"@onekeyfe/hd-transport-web-device": "1.2.8-alpha.20"
|
|
31
31
|
},
|
|
32
|
-
"gitHead": "
|
|
32
|
+
"gitHead": "973f059fe93a58fdcf098ddb32ba63f621a2c78e"
|
|
33
33
|
}
|
package/src/index.ts
CHANGED
|
@@ -17,6 +17,7 @@ import HardwareSdk, {
|
|
|
17
17
|
getLogger,
|
|
18
18
|
initCore,
|
|
19
19
|
parseConnectSettings,
|
|
20
|
+
probeUsbDeviceIdentitiesWithTransport,
|
|
20
21
|
setLoggerPostMessage,
|
|
21
22
|
} from '@onekeyfe/hd-core';
|
|
22
23
|
import { ERRORS, HardwareErrorCode, createDeferred } from '@onekeyfe/hd-shared';
|
|
@@ -32,6 +33,7 @@ import type {
|
|
|
32
33
|
CoreMessage,
|
|
33
34
|
LowLevelCoreApi,
|
|
34
35
|
UiResponseEvent,
|
|
36
|
+
UsbDeviceIdentity,
|
|
35
37
|
} from '@onekeyfe/hd-core';
|
|
36
38
|
import type { LowlevelTransportSharedPlugin } from '@onekeyfe/hd-transport';
|
|
37
39
|
|
|
@@ -57,6 +59,37 @@ const getTransport = async (env: ConnectSettings['env']) => {
|
|
|
57
59
|
let _core: Core | undefined;
|
|
58
60
|
let _settings = parseConnectSettings();
|
|
59
61
|
|
|
62
|
+
const usbIdentityProbeTransports: Partial<Record<UsbDeviceIdentity['commType'], WebUsbTransport>> =
|
|
63
|
+
{};
|
|
64
|
+
const usbIdentityProbes: Partial<
|
|
65
|
+
Record<UsbDeviceIdentity['commType'], Promise<UsbDeviceIdentity[]>>
|
|
66
|
+
> = {};
|
|
67
|
+
|
|
68
|
+
/** Identify WebUSB without disposing the current SDK, changing settings, or opening a wallet. */
|
|
69
|
+
export const probeUsbDeviceIdentities = ({
|
|
70
|
+
commType,
|
|
71
|
+
}: {
|
|
72
|
+
commType: UsbDeviceIdentity['commType'];
|
|
73
|
+
}): Promise<UsbDeviceIdentity[]> => {
|
|
74
|
+
if (commType !== 'webusb') {
|
|
75
|
+
return Promise.reject(ERRORS.TypedError(HardwareErrorCode.CallMethodInvalidParameter));
|
|
76
|
+
}
|
|
77
|
+
const pending = usbIdentityProbes[commType];
|
|
78
|
+
if (pending) return pending;
|
|
79
|
+
let transport = usbIdentityProbeTransports[commType];
|
|
80
|
+
if (!transport) {
|
|
81
|
+
transport = new WebUsbTransport(true);
|
|
82
|
+
usbIdentityProbeTransports[commType] = transport;
|
|
83
|
+
}
|
|
84
|
+
const probe = probeUsbDeviceIdentitiesWithTransport(transport, commType, descriptor =>
|
|
85
|
+
_core?.getOwnedUsbDeviceIdentity(descriptor)
|
|
86
|
+
).finally(() => {
|
|
87
|
+
delete usbIdentityProbes[commType];
|
|
88
|
+
});
|
|
89
|
+
usbIdentityProbes[commType] = probe;
|
|
90
|
+
return probe;
|
|
91
|
+
};
|
|
92
|
+
|
|
60
93
|
let _messageID = 0;
|
|
61
94
|
export const messagePromises: { [key: number]: Deferred<any> } = {};
|
|
62
95
|
|
|
@@ -188,6 +221,9 @@ const call = async (params: any) => {
|
|
|
188
221
|
if (response.payload?.code === HardwareErrorCode.BlePermissionError) {
|
|
189
222
|
postMessage(createUiMessage(UI_REQUEST.BLUETOOTH_PERMISSION), false);
|
|
190
223
|
}
|
|
224
|
+
if (response.payload?.code === HardwareErrorCode.BleLocationError) {
|
|
225
|
+
postMessage(createUiMessage(UI_REQUEST.LOCATION_PERMISSION), false);
|
|
226
|
+
}
|
|
191
227
|
}
|
|
192
228
|
|
|
193
229
|
return response;
|