@onekeyfe/hd-core 1.2.2-alpha.118 → 1.2.2-alpha.119

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.
@@ -106,9 +106,13 @@ describe('SearchDevices', () => {
106
106
  }
107
107
  );
108
108
 
109
- test('owned WebUSB cache miss still reports a SearchDevice without probing', async () => {
110
- mockGetDeviceByPath.mockReturnValue(undefined);
111
- const usbDevice = { vendorId: 0x1209, productId: 0x4f4c };
109
+ const BOOTLOADER_PATH = '00000000000000000000000000000000';
110
+
111
+ const runOwnedSearch = (
112
+ path: string,
113
+ ownerDevice?: unknown,
114
+ usbHandles: unknown[] = [{ vendorId: 0x1209, productId: 0x4f4c }]
115
+ ) => {
112
116
  const method = new SearchDevices({
113
117
  id: 1,
114
118
  payload: { method: 'searchDevices' },
@@ -117,34 +121,95 @@ describe('SearchDevices', () => {
117
121
  method.context = {
118
122
  requestQueue: {
119
123
  getRequestTasksId: () => [2],
120
- getRequestTasksIdByConnectId: (connectId: string) => (connectId === 'serial-V2' ? [2] : []),
124
+ getRequestTasksIdByConnectId: (connectId: string) => (connectId === path ? [2] : []),
125
+ getTask: (requestId: number) =>
126
+ requestId === 2 ? { method: { device: ownerDevice } } : undefined,
121
127
  },
122
128
  } as never;
123
129
  method.connector = {
124
130
  enumerate: jest.fn().mockResolvedValue({
125
- descriptors: [
126
- {
127
- path: 'serial-V2',
128
- device: usbDevice,
129
- commType: 'webusb',
130
- },
131
- ],
131
+ descriptors: usbHandles.map(device => ({ path, device, commType: 'webusb' })),
132
132
  }),
133
133
  } as never;
134
+ return method.run();
135
+ };
134
136
 
135
- await expect(method.run()).resolves.toEqual([
136
- {
137
- connectId: 'serial-V2',
138
- uuid: 'serial-V2',
139
- serialNo: 'serial-V2',
140
- deviceId: null,
141
- deviceType: 'unknown',
142
- name: 'serial-V2',
143
- commType: 'webusb',
144
- },
137
+ const createOwnerDevice = (path: string, usbHandle: unknown, serialNo: string) => ({
138
+ mainId: path,
139
+ features: { serial_no: serialNo },
140
+ originalDescriptor: { path, device: usbHandle },
141
+ toMessageObject: () => ({ connectId: serialNo, serialNo, uuid: serialNo }),
142
+ });
143
+
144
+ test.each(['serial-V2', 'usb-1209-4f4c-onekey', BOOTLOADER_PATH])(
145
+ 'owned WebUSB cache miss on %s reports the path without inventing an identity or probing',
146
+ async path => {
147
+ mockGetDeviceByPath.mockReturnValue(undefined);
148
+
149
+ await expect(runOwnedSearch(path)).resolves.toEqual([
150
+ {
151
+ connectId: path,
152
+ uuid: '',
153
+ serialNo: null,
154
+ deviceId: null,
155
+ deviceType: 'unknown',
156
+ name: path,
157
+ commType: 'webusb',
158
+ },
159
+ ]);
160
+ expect(mockGetDevices).not.toHaveBeenCalled();
161
+ expect(mockConfigureTransport).not.toHaveBeenCalled();
162
+ }
163
+ );
164
+
165
+ test('owned WebUSB cache miss reports the owning request device identity', async () => {
166
+ mockGetDeviceByPath.mockReturnValue(undefined);
167
+ const usbHandle = { vendorId: 0x1209, productId: 0x4f4c };
168
+ const ownerDevice = createOwnerDevice(BOOTLOADER_PATH, usbHandle, 'PRO2SERIAL');
169
+
170
+ await expect(runOwnedSearch(BOOTLOADER_PATH, ownerDevice, [usbHandle])).resolves.toEqual([
171
+ { connectId: 'PRO2SERIAL', serialNo: 'PRO2SERIAL', uuid: 'PRO2SERIAL' },
145
172
  ]);
146
173
  expect(mockGetDevices).not.toHaveBeenCalled();
147
- expect(mockConfigureTransport).not.toHaveBeenCalled();
174
+ });
175
+
176
+ test('owned WebUSB cache miss ignores an owning request device bound to another path', async () => {
177
+ mockGetDeviceByPath.mockReturnValue(undefined);
178
+ const usbHandle = { vendorId: 0x1209, productId: 0x4f4c };
179
+ const ownerDevice = createOwnerDevice('other-path', usbHandle, 'OTHERSERIAL');
180
+
181
+ await expect(runOwnedSearch('usb-1209-4f4c-onekey', ownerDevice, [usbHandle])).resolves.toEqual(
182
+ [expect.objectContaining({ connectId: 'usb-1209-4f4c-onekey', uuid: '', serialNo: null })]
183
+ );
184
+ });
185
+
186
+ test('a second device on the shared bootloader path does not take the owner identity', async () => {
187
+ mockGetDeviceByPath.mockReturnValue(undefined);
188
+ const ownerHandle = { vendorId: 0x1209, productId: 0x4f4c };
189
+ const otherHandle = { vendorId: 0x1209, productId: 0x4f4c };
190
+ const ownerDevice = createOwnerDevice(BOOTLOADER_PATH, ownerHandle, 'MINISERIAL');
191
+
192
+ await expect(
193
+ runOwnedSearch(BOOTLOADER_PATH, ownerDevice, [ownerHandle, otherHandle])
194
+ ).resolves.toEqual([
195
+ { connectId: 'MINISERIAL', serialNo: 'MINISERIAL', uuid: 'MINISERIAL' },
196
+ expect.objectContaining({ connectId: BOOTLOADER_PATH, uuid: '', serialNo: null }),
197
+ ]);
198
+ });
199
+
200
+ test('a device swapped onto the bound bootloader path does not take the owner identity', async () => {
201
+ mockGetDeviceByPath.mockReturnValue(undefined);
202
+ const ownerDevice = createOwnerDevice(
203
+ BOOTLOADER_PATH,
204
+ { vendorId: 0x1209, productId: 0x4f4c },
205
+ 'MINISERIAL'
206
+ );
207
+
208
+ await expect(
209
+ runOwnedSearch(BOOTLOADER_PATH, ownerDevice, [{ vendorId: 0x1209, productId: 0x4f4c }])
210
+ ).resolves.toEqual([
211
+ expect.objectContaining({ connectId: BOOTLOADER_PATH, uuid: '', serialNo: null }),
212
+ ]);
148
213
  });
149
214
 
150
215
  test('searchDevices resolves empty when WebUSB bring-up is unavailable', async () => {
@@ -1 +1 @@
1
- {"version":3,"file":"SearchDevices.d.ts","sourceRoot":"","sources":["../../src/api/SearchDevices.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAM1C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,eAAe,MAAM,2BAA2B,CAAC;AAuC7D,MAAM,CAAC,OAAO,OAAO,aAAc,SAAQ,UAAU;IACnD,SAAS,CAAC,EAAE,eAAe,CAAC;IAE5B,IAAI;IAME,GAAG,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;CAsFrC"}
1
+ {"version":3,"file":"SearchDevices.d.ts","sourceRoot":"","sources":["../../src/api/SearchDevices.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAM1C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,eAAe,MAAM,2BAA2B,CAAC;AAyE7D,MAAM,CAAC,OAAO,OAAO,aAAc,SAAQ,UAAU;IACnD,SAAS,CAAC,EAAE,eAAe,CAAC;IAE5B,IAAI;IAME,GAAG,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;CAuFrC"}
package/dist/index.js CHANGED
@@ -47277,18 +47277,38 @@ class PreInitialize extends BaseMethod {
47277
47277
  }
47278
47278
 
47279
47279
  const Log$e = getLogger(exports.LoggerNames.DevicePool);
47280
+ const getDescriptorKeys = (descriptor) => [descriptor.path, descriptor.id].filter((key) => typeof key === 'string' && key.length > 0);
47280
47281
  const isOwnedByActiveWebUsbRequest = (descriptor, requestQueue) => {
47281
47282
  if (!requestQueue)
47282
47283
  return false;
47283
- const keys = [descriptor.path, descriptor.id].filter((key) => typeof key === 'string' && key.length > 0);
47284
- return keys.some(key => requestQueue.getRequestTasksIdByConnectId(key).length > 0);
47284
+ return getDescriptorKeys(descriptor).some(key => requestQueue.getRequestTasksIdByConnectId(key).length > 0);
47285
+ };
47286
+ const getUsbHandle = (descriptor) => descriptor === null || descriptor === void 0 ? void 0 : descriptor.device;
47287
+ const getOwningRequestDevice = (descriptor, requestQueue) => {
47288
+ var _a, _b;
47289
+ if (!requestQueue)
47290
+ return undefined;
47291
+ const usbHandle = getUsbHandle(descriptor);
47292
+ if (usbHandle === undefined)
47293
+ return undefined;
47294
+ for (const key of getDescriptorKeys(descriptor)) {
47295
+ for (const requestId of requestQueue.getRequestTasksIdByConnectId(key)) {
47296
+ const device = (_a = requestQueue.getTask(requestId)) === null || _a === void 0 ? void 0 : _a.method.device;
47297
+ if ((device === null || device === void 0 ? void 0 : device.features) &&
47298
+ (device.mainId === key || ((_b = device.originalDescriptor) === null || _b === void 0 ? void 0 : _b.path) === key) &&
47299
+ getUsbHandle(device.originalDescriptor) === usbHandle) {
47300
+ return device;
47301
+ }
47302
+ }
47303
+ }
47304
+ return undefined;
47285
47305
  };
47286
47306
  const toSearchDeviceFromDescriptor = (descriptor) => {
47287
47307
  const connectId = descriptor.path || descriptor.id || null;
47288
47308
  return {
47289
47309
  connectId,
47290
- uuid: connectId !== null && connectId !== void 0 ? connectId : '',
47291
- serialNo: connectId,
47310
+ uuid: '',
47311
+ serialNo: null,
47292
47312
  deviceId: null,
47293
47313
  deviceType: hdShared.EDeviceType.Unknown,
47294
47314
  name: descriptor.name || connectId || '',
@@ -47342,7 +47362,8 @@ class SearchDevices extends BaseMethod {
47342
47362
  for (const descriptor of devicesDescriptor) {
47343
47363
  if (hasActiveWebUsbRequest && isOwnedByActiveWebUsbRequest(descriptor, requestQueue)) {
47344
47364
  const cached = DevicePool.getDeviceByPath(descriptor.path);
47345
- const message = (cached === null || cached === void 0 ? void 0 : cached.features) ? toSearchDevice(cached) : null;
47365
+ const known = (cached === null || cached === void 0 ? void 0 : cached.features) ? cached : getOwningRequestDevice(descriptor, requestQueue);
47366
+ const message = known ? toSearchDevice(known) : null;
47346
47367
  deviceList.push(message !== null && message !== void 0 ? message : toSearchDeviceFromDescriptor(descriptor));
47347
47368
  }
47348
47369
  else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-core",
3
- "version": "1.2.2-alpha.118",
3
+ "version": "1.2.2-alpha.119",
4
4
  "description": "Core processes and APIs for communicating with OneKey hardware devices.",
5
5
  "author": "OneKey",
6
6
  "homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
@@ -25,8 +25,8 @@
25
25
  "url": "https://github.com/OneKeyHQ/hardware-js-sdk/issues"
26
26
  },
27
27
  "dependencies": {
28
- "@onekeyfe/hd-shared": "1.2.2-alpha.118",
29
- "@onekeyfe/hd-transport": "1.2.2-alpha.118",
28
+ "@onekeyfe/hd-shared": "1.2.2-alpha.119",
29
+ "@onekeyfe/hd-transport": "1.2.2-alpha.119",
30
30
  "axios": "1.15.2",
31
31
  "bignumber.js": "^9.0.2",
32
32
  "buffer": "^6.0.3",
@@ -46,5 +46,5 @@
46
46
  "@types/w3c-web-usb": "^1.0.10",
47
47
  "@types/web-bluetooth": "^0.0.21"
48
48
  },
49
- "gitHead": "f73c3ec61e0ffe97c1d38b18058a18e169a72217"
49
+ "gitHead": "222ec4a49467e77a73766a07227069d76d9116f6"
50
50
  }
@@ -15,25 +15,59 @@ const Log = getLogger(LoggerNames.DevicePool);
15
15
 
16
16
  type RequestQueueLookup = {
17
17
  getRequestTasksIdByConnectId: (connectId: string) => number[];
18
+ getTask: (requestId: number) => { method: { device?: Device } } | undefined;
18
19
  };
19
20
 
21
+ const getDescriptorKeys = (descriptor: DeviceDescriptor) =>
22
+ [descriptor.path, descriptor.id].filter(
23
+ (key): key is string => typeof key === 'string' && key.length > 0
24
+ );
25
+
20
26
  const isOwnedByActiveWebUsbRequest = (
21
27
  descriptor: DeviceDescriptor,
22
28
  requestQueue?: RequestQueueLookup
23
29
  ) => {
24
30
  if (!requestQueue) return false;
25
- const keys = [descriptor.path, descriptor.id].filter(
26
- (key): key is string => typeof key === 'string' && key.length > 0
31
+ return getDescriptorKeys(descriptor).some(
32
+ key => requestQueue.getRequestTasksIdByConnectId(key).length > 0
27
33
  );
28
- return keys.some(key => requestQueue.getRequestTasksIdByConnectId(key).length > 0);
29
34
  };
30
35
 
36
+ const getUsbHandle = (descriptor?: DeviceDescriptor) =>
37
+ (descriptor as { device?: unknown } | undefined)?.device;
38
+
39
+ const getOwningRequestDevice = (
40
+ descriptor: DeviceDescriptor,
41
+ requestQueue?: RequestQueueLookup
42
+ ) => {
43
+ if (!requestQueue) return undefined;
44
+ const usbHandle = getUsbHandle(descriptor);
45
+ // A path alone can be shared (the all-zero bootloader path, a synthesized serial-less
46
+ // path), so the owner's identity is only reported for the USB handle it actually bound.
47
+ if (usbHandle === undefined) return undefined;
48
+ for (const key of getDescriptorKeys(descriptor)) {
49
+ for (const requestId of requestQueue.getRequestTasksIdByConnectId(key)) {
50
+ const device = requestQueue.getTask(requestId)?.method.device;
51
+ if (
52
+ device?.features &&
53
+ (device.mainId === key || device.originalDescriptor?.path === key) &&
54
+ getUsbHandle(device.originalDescriptor) === usbHandle
55
+ ) {
56
+ return device;
57
+ }
58
+ }
59
+ }
60
+ return undefined;
61
+ };
62
+
63
+ // A USB path is a routing key (the USB serial, a synthesized usb-vid-pid-name, or the
64
+ // bootloader placeholder), not a hardware identity, so it never fills uuid/serialNo.
31
65
  const toSearchDeviceFromDescriptor = (descriptor: DeviceDescriptor): SearchDevice => {
32
66
  const connectId = descriptor.path || descriptor.id || null;
33
67
  return {
34
68
  connectId,
35
- uuid: connectId ?? '',
36
- serialNo: connectId,
69
+ uuid: '',
70
+ serialNo: null,
37
71
  deviceId: null,
38
72
  deviceType: EDeviceType.Unknown,
39
73
  name: descriptor.name || connectId || '',
@@ -108,9 +142,10 @@ export default class SearchDevices extends BaseMethod {
108
142
  for (const descriptor of devicesDescriptor) {
109
143
  if (hasActiveWebUsbRequest && isOwnedByActiveWebUsbRequest(descriptor, requestQueue)) {
110
144
  const cached = DevicePool.getDeviceByPath(descriptor.path);
111
- const message = cached?.features ? toSearchDevice(cached) : null;
112
- // Do not probe a path an in-flight request already owns. A cache miss
113
- // (passphrase switch / initSession) is still a connected device.
145
+ const known = cached?.features ? cached : getOwningRequestDevice(descriptor, requestQueue);
146
+ const message = known ? toSearchDevice(known) : null;
147
+ // Do not probe a path an in-flight request already owns. A cache miss is still a
148
+ // connected device: report the owning request's device, or the path with no identity.
114
149
  deviceList.push(message ?? toSearchDeviceFromDescriptor(descriptor));
115
150
  } else {
116
151
  try {