@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 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 { CORE_EVENT, initCore } from '@onekeyfe/hd-core';
5
- import { createDeferred } from '@onekeyfe/hd-shared';
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 (_d) {
741
- switch (_d.label) {
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
- _d.label = 1;
769
+ _e.label = 1;
746
770
  case 1:
747
- _d.trys.push([1, 3, , 4]);
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 = _d.sent();
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 = _d.sent();
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.2",
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.2",
25
- "@onekeyfe/hd-shared": "1.2.8-alpha.2",
26
- "@onekeyfe/hd-transport-emulator": "1.2.8-alpha.2",
27
- "@onekeyfe/hd-transport-http": "1.2.8-alpha.2",
28
- "@onekeyfe/hd-transport-lowlevel": "1.2.8-alpha.2",
29
- "@onekeyfe/hd-transport-usb": "1.2.8-alpha.2",
30
- "@onekeyfe/hd-transport-web-device": "1.2.8-alpha.2"
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": "74ac64464df3806ef44ad54d313378754c3eaed2"
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;