@onekeyfe/hd-core 1.2.2-alpha.112 → 1.2.2-alpha.113

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.
@@ -4,6 +4,8 @@ import { initConnector, initCore } from '../src/core';
4
4
  import { DataManager } from '../src/data-manager';
5
5
  import TransportManager from '../src/data-manager/TransportManager';
6
6
  import { IFRAME } from '../src/events';
7
+ import SearchDevices from '../src/api/SearchDevices';
8
+ import { DeviceList } from '../src/device/DeviceList';
7
9
 
8
10
  jest.mock('../src/data/config', () => ({
9
11
  getSDKVersion: jest.fn(() => '1.0.0-test'),
@@ -24,6 +26,75 @@ describe('Core 错误输出边界', () => {
24
26
  jest.restoreAllMocks();
25
27
  });
26
28
 
29
+ test.each([
30
+ ['webusb', false],
31
+ ['desktop-webusb', false],
32
+ ['desktop-webusb', true],
33
+ ] as const)(
34
+ '%s waits for discovery without masking initialization or cancellation (cancel=%s)',
35
+ async (env, shouldCancel) => {
36
+ jest.spyOn(DataManager, 'getSettings').mockReturnValue(env as never);
37
+ const error = ERRORS.TypedError(HardwareErrorCode.DeviceInitializeFailed, 'probe failed');
38
+ let finishSearch!: () => void;
39
+ let searchStarted!: () => void;
40
+ const started = new Promise<void>(resolve => {
41
+ searchStarted = resolve;
42
+ });
43
+ const search = jest.spyOn(SearchDevices.prototype, 'run').mockImplementation(async () => {
44
+ searchStarted();
45
+ await new Promise<void>(resolve => {
46
+ finishSearch = resolve;
47
+ });
48
+ return [];
49
+ });
50
+ const initialize = jest
51
+ .spyOn(DeviceList.prototype, 'getDeviceLists')
52
+ .mockRejectedValue(error);
53
+ const core = initCore();
54
+ initConnector();
55
+ try {
56
+ const discovery = core.handleMessage({
57
+ id: 10,
58
+ type: IFRAME.CALL,
59
+ payload: { method: 'searchDevices' },
60
+ } as never);
61
+ await started;
62
+ const request = core.handleMessage({
63
+ id: 11,
64
+ type: IFRAME.CALL,
65
+ payload: { method: 'getDeviceState', connectId: 'serial-V2' },
66
+ } as never);
67
+ await new Promise(resolve => {
68
+ setTimeout(resolve, 0);
69
+ });
70
+ expect(initialize).not.toHaveBeenCalled();
71
+ if (shouldCancel) {
72
+ await core.handleMessage({
73
+ type: IFRAME.CANCEL,
74
+ payload: { connectId: 'serial-V2' },
75
+ } as never);
76
+ }
77
+ finishSearch();
78
+ await expect(discovery).resolves.toMatchObject({ success: true });
79
+ const expectedError = shouldCancel
80
+ ? ERRORS.TypedError(HardwareErrorCode.CallQueueActionCancelled)
81
+ : error;
82
+ await expect(request).resolves.toMatchObject({
83
+ success: false,
84
+ payload: { code: expectedError.errorCode, error: expectedError.message },
85
+ });
86
+ await new Promise(resolve => {
87
+ setTimeout(resolve, 0);
88
+ });
89
+ expect(search).toHaveBeenCalledTimes(1);
90
+ expect(initialize).toHaveBeenCalledTimes(shouldCancel ? 0 : 1);
91
+ } finally {
92
+ finishSearch?.();
93
+ await core.dispose();
94
+ }
95
+ }
96
+ );
97
+
27
98
  test.each([
28
99
  HardwareErrorCode.BleDeviceNotBonded,
29
100
  HardwareErrorCode.BleDeviceBondedCanceled,
@@ -362,12 +362,12 @@ describe('public device lifecycle events', () => {
362
362
  }
363
363
  );
364
364
 
365
- test('keeps the cleanup barrier when its deadline expires', async () => {
365
+ test('clears the cleanup barrier when its deadline expires', async () => {
366
366
  const realSetTimeout = setTimeout;
367
367
  jest
368
368
  .spyOn(global, 'setTimeout')
369
369
  .mockImplementation((callback, delay, ...args) =>
370
- realSetTimeout(callback, delay === 15_000 ? 0 : delay, ...args)
370
+ realSetTimeout(callback, delay === 5_000 ? 0 : delay, ...args)
371
371
  );
372
372
  {
373
373
  jest.spyOn(DataManager, 'getSettings').mockReturnValue('react-native' as never);
@@ -382,13 +382,13 @@ describe('public device lifecycle events', () => {
382
382
  type: IFRAME.CALL,
383
383
  payload: { method: 'getDeviceState', connectId: 'draining-device', connectProtocol: 'V2' },
384
384
  } as CoreMessage);
385
- await expect(result).resolves.toMatchObject({
386
- success: false,
387
- payload: { code: HardwareErrorCode.DeviceBusy },
388
- });
389
- expect(acquire).not.toHaveBeenCalled();
390
- expect(context.getPrePendingCallPromise('draining-device')).toBe(gate.promise);
385
+ setImmediate(() => cancel(context, 'draining-device'));
386
+ await expect(result).resolves.toBeDefined();
391
387
  gate.resolve();
388
+ await new Promise(resolve => {
389
+ setImmediate(resolve);
390
+ });
391
+ expect(context.getPrePendingCallPromise('draining-device')).toBeUndefined();
392
392
  }
393
393
  });
394
394
 
@@ -17,15 +17,16 @@ jest.mock('../src/data-manager', () => ({
17
17
  jest.mock('../src/device/DevicePool', () => ({
18
18
  DevicePool: {
19
19
  getDevices: jest.fn(),
20
+ getDeviceByPath: jest.fn(),
20
21
  },
21
22
  }));
22
23
 
23
24
  const transportManagerMock: { default: { configure: jest.Mock } } = jest.requireMock(
24
25
  '../src/data-manager/TransportManager'
25
26
  );
26
- const devicePoolMock: { DevicePool: { getDevices: jest.Mock } } = jest.requireMock(
27
- '../src/device/DevicePool'
28
- );
27
+ const devicePoolMock: {
28
+ DevicePool: { getDevices: jest.Mock; getDeviceByPath: jest.Mock };
29
+ } = jest.requireMock('../src/device/DevicePool');
29
30
  const dataManagerMock: {
30
31
  DataManager: {
31
32
  getSettings: jest.Mock;
@@ -40,8 +41,43 @@ describe('SearchDevices', () => {
40
41
  beforeEach(() => {
41
42
  jest.clearAllMocks();
42
43
  mockIsBleConnect.mockReturnValue(false);
44
+ dataManagerMock.DataManager.getSettings.mockReturnValue('webusb');
43
45
  });
44
46
 
47
+ test.each(['webusb', 'desktop-webusb'])(
48
+ '%s discovery leaves active V1/V2 requests and their schemas untouched',
49
+ async env => {
50
+ dataManagerMock.DataManager.getSettings.mockReturnValue(env);
51
+ const devices = ['V1', 'V2'].map(protocol => ({
52
+ features: { protocol },
53
+ toMessageObject: () => ({ connectId: `serial-${protocol}` }),
54
+ }));
55
+ devicePoolMock.DevicePool.getDeviceByPath.mockImplementation(
56
+ (path: string) => devices[['usb-V1', 'usb-V2'].indexOf(path)]
57
+ );
58
+ const method = new SearchDevices({
59
+ id: 1,
60
+ payload: { method: 'searchDevices' },
61
+ } as never);
62
+ method.init();
63
+ method.context = {
64
+ requestQueue: { getRequestTasksId: () => [2] },
65
+ } as never;
66
+ method.connector = {
67
+ enumerate: jest.fn().mockResolvedValue({
68
+ descriptors: [{ path: 'usb-V1' }, { path: 'usb-V2' }, { path: 'not-initialized' }],
69
+ }),
70
+ } as never;
71
+
72
+ await expect(method.run()).resolves.toEqual([
73
+ { connectId: 'serial-V1' },
74
+ { connectId: 'serial-V2' },
75
+ ]);
76
+ expect(mockGetDevices).not.toHaveBeenCalled();
77
+ expect(mockConfigureTransport).not.toHaveBeenCalled();
78
+ }
79
+ );
80
+
45
81
  test('搜索忽略调用方协议并主动探测,单个无响应设备不阻断后续结果', async () => {
46
82
  const unresponsiveDescriptor = {
47
83
  path: 'stale-usb-device',
@@ -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,MAAM,2BAA2B,CAAC;AAI7D,MAAM,CAAC,OAAO,OAAO,aAAc,SAAQ,UAAU;IACnD,SAAS,CAAC,EAAE,eAAe,CAAC;IAE5B,IAAI;IAME,GAAG;;;;;;;;;;;;;;;CA+DV"}
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,MAAM,2BAA2B,CAAC;AAI7D,MAAM,CAAC,OAAO,OAAO,aAAc,SAAQ,UAAU;IACnD,SAAS,CAAC,EAAE,eAAe,CAAC;IAE5B,IAAI;IAME,GAAG;;;;;;;;;;;;;;;CA0EV"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":";AACA,OAAO,YAAY,MAAM,QAAQ,CAAC;AAClC,OAAO,EAEL,KAAK,6BAA6B,EAElC,KAAK,YAAY,EAIlB,MAAM,wBAAwB,CAAC;AAiChC,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAsB1C,OAAO,eAAe,MAAM,2BAA2B,CAAC;AAYxD,OAAO,KAAK,EAAE,eAAe,EAAyB,MAAM,UAAU,CAAC;AACvE,OAAO,KAAK,EAAE,WAAW,EAAmD,MAAM,WAAW,CAAC;AAI9F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAYpD,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC;AA0E7D,eAAO,MAAM,OAAO,YAAmB,WAAW,WAAW,WAAW,iBAoFvE,CAAC;AAyFF,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAY3F;AAomBD,wBAAgB,kCAAkC,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,WAWpF;AAED,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,WAoB/E;AAED,wBAAgB,gCAAgC,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,WAQlF;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,WAEzD;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,OAAO,WAI3D;AAiKD,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,UAAU,GAAG,YAAY,GAAG,SAAS,CAMtF;AA+LD,eAAO,MAAM,MAAM,YAAa,WAAW,cAAc,MAAM,SA4G9D,CAAC;AA8GF,eAAO,MAAM,qBAAqB,gFAejC,CAAC;AAiLF,MAAM,CAAC,OAAO,OAAO,IAAK,SAAQ,YAAY;IAC5C,OAAO,CAAC,cAAc,CAAoB;IAE1C,SAAgB,aAAa,EAAE,MAAM,CAAC;IAEtC,OAAO,CAAC,YAAY,CAAsB;IAE1C,OAAO,CAAC,cAAc,CAAC,CAAgB;IAGvC,OAAO,CAAC,sBAAsB,CAAoC;IAElE,OAAO,CAAC,iBAAiB,CAAoB;;IAS7C,OAAO,CAAC,cAAc;IAuChB,aAAa,CAAC,OAAO,EAAE,WAAW;IAuExC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;YAOV,gBAAgB;CAiC/B;AAED,eAAO,MAAM,QAAQ,YAIpB,CAAC;AAEF,eAAO,MAAM,aAAa,uBAYzB,CAAC;AAMF,eAAO,MAAM,IAAI,aACL,eAAe,aACd,GAAG,WACL,6BAA6B,8BAiBvC,CAAC;AAEF,eAAO,MAAM,eAAe;SAKrB,eAAe,CAAC,KAAK,CAAC;eAChB,GAAG;;UASf,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":";AACA,OAAO,YAAY,MAAM,QAAQ,CAAC;AAClC,OAAO,EAEL,KAAK,6BAA6B,EAElC,KAAK,YAAY,EAIlB,MAAM,wBAAwB,CAAC;AAiChC,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAsB1C,OAAO,eAAe,MAAM,2BAA2B,CAAC;AAYxD,OAAO,KAAK,EAAE,eAAe,EAAyB,MAAM,UAAU,CAAC;AACvE,OAAO,KAAK,EAAE,WAAW,EAAmD,MAAM,WAAW,CAAC;AAI9F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAYpD,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC;AA0E7D,eAAO,MAAM,OAAO,YAAmB,WAAW,WAAW,WAAW,iBA0FvE,CAAC;AAyFF,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAY3F;AA8mBD,wBAAgB,kCAAkC,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,WAWpF;AAED,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,WAoB/E;AAED,wBAAgB,gCAAgC,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,WAQlF;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,WAEzD;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,OAAO,WAI3D;AAiKD,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,UAAU,GAAG,YAAY,GAAG,SAAS,CAMtF;AAwMD,eAAO,MAAM,MAAM,YAAa,WAAW,cAAc,MAAM,SA4G9D,CAAC;AA8GF,eAAO,MAAM,qBAAqB,gFAejC,CAAC;AAiLF,MAAM,CAAC,OAAO,OAAO,IAAK,SAAQ,YAAY;IAC5C,OAAO,CAAC,cAAc,CAAoB;IAE1C,SAAgB,aAAa,EAAE,MAAM,CAAC;IAEtC,OAAO,CAAC,YAAY,CAAsB;IAE1C,OAAO,CAAC,cAAc,CAAC,CAAgB;IAGvC,OAAO,CAAC,sBAAsB,CAAoC;IAElE,OAAO,CAAC,iBAAiB,CAAoB;;IAS7C,OAAO,CAAC,cAAc;IAuChB,aAAa,CAAC,OAAO,EAAE,WAAW;IAuExC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;YAOV,gBAAgB;CAiC/B;AAED,eAAO,MAAM,QAAQ,YAIpB,CAAC;AAEF,eAAO,MAAM,aAAa,uBAYzB,CAAC;AAMF,eAAO,MAAM,IAAI,aACL,eAAe,aACd,GAAG,WACL,6BAA6B,8BAiBvC,CAAC;AAEF,eAAO,MAAM,eAAe;SAKrB,eAAe,CAAC,KAAK,CAAC;eAChB,GAAG;;UASf,CAAC"}
package/dist/index.js CHANGED
@@ -47270,26 +47270,35 @@ class SearchDevices extends BaseMethod {
47270
47270
  this.skipForceUpdateCheck = true;
47271
47271
  }
47272
47272
  run() {
47273
- var _a, _b, _c, _d, _e;
47273
+ var _a, _b, _c, _d, _e, _f, _g;
47274
47274
  return __awaiter(this, void 0, void 0, function* () {
47275
- yield TransportManager.configure();
47276
- const deviceDiff = yield ((_a = this.connector) === null || _a === void 0 ? void 0 : _a.enumerate());
47277
- const devicesDescriptor = (_b = deviceDiff === null || deviceDiff === void 0 ? void 0 : deviceDiff.descriptors) !== null && _b !== void 0 ? _b : [];
47278
47275
  const env = DataManager.getSettings('env');
47276
+ const useCachedWebUsbDevices = (env === 'webusb' || env === 'desktop-webusb') &&
47277
+ ((_b = (_a = this.context) === null || _a === void 0 ? void 0 : _a.requestQueue.getRequestTasksId().length) !== null && _b !== void 0 ? _b : 0) > 0;
47278
+ if (!useCachedWebUsbDevices)
47279
+ yield TransportManager.configure();
47280
+ const deviceDiff = yield ((_c = this.connector) === null || _c === void 0 ? void 0 : _c.enumerate());
47281
+ const devicesDescriptor = (_d = deviceDiff === null || deviceDiff === void 0 ? void 0 : deviceDiff.descriptors) !== null && _d !== void 0 ? _d : [];
47279
47282
  if (DataManager.isBleConnect(env)) {
47280
47283
  const devices = [];
47281
47284
  const seenIds = new Set();
47282
47285
  for (const device of devicesDescriptor) {
47283
- const lowerId = (_c = device.id) === null || _c === void 0 ? void 0 : _c.toLowerCase();
47286
+ const lowerId = (_e = device.id) === null || _e === void 0 ? void 0 : _e.toLowerCase();
47284
47287
  if (!seenIds.has(lowerId)) {
47285
47288
  seenIds.add(lowerId);
47286
- const rawBleName = (_e = (_d = device.name) !== null && _d !== void 0 ? _d : device.localName) !== null && _e !== void 0 ? _e : '';
47289
+ const rawBleName = (_g = (_f = device.name) !== null && _f !== void 0 ? _f : device.localName) !== null && _g !== void 0 ? _g : '';
47287
47290
  const bleName = hdShared.canonicalizePro2BleAdvertisementName(rawBleName);
47288
47291
  devices.push(Object.assign(Object.assign({}, device), { connectId: device.id, serialNo: null, uuid: '', deviceId: null, name: bleName || device.name, deviceType: getDeviceTypeByBleName(bleName) }));
47289
47292
  }
47290
47293
  }
47291
47294
  return devices;
47292
47295
  }
47296
+ if (useCachedWebUsbDevices) {
47297
+ return devicesDescriptor.flatMap(descriptor => {
47298
+ const device = DevicePool.getDeviceByPath(descriptor.path);
47299
+ return (device === null || device === void 0 ? void 0 : device.features) ? [device.toMessageObject()] : [];
47300
+ });
47301
+ }
47293
47302
  const deviceList = [];
47294
47303
  for (const descriptor of devicesDescriptor) {
47295
47304
  try {
@@ -65850,7 +65859,7 @@ const createUiProgressMessageFilter = (intervalMs = DEFAULT_UI_PROGRESS_INTERVAL
65850
65859
 
65851
65860
  const Log = getLogger(exports.LoggerNames.Core);
65852
65861
  const PRE_INITIALIZE_TTL_MS = 60 * 1000;
65853
- const PRE_PENDING_CALL_TIMEOUT_MS = 15 * 1000;
65862
+ const PRE_PENDING_CALL_TIMEOUT_MS = 5 * 1000;
65854
65863
  const PRO2_USB_SIGNING_COOLDOWN_MS = 1000;
65855
65864
  const preWarmInflight = new Map();
65856
65865
  const preWarmDoneAt = new Map();
@@ -65930,6 +65939,7 @@ const callAPI = (context, message) => __awaiter(void 0, void 0, void 0, function
65930
65939
  }
65931
65940
  };
65932
65941
  (_a = method.setContext) === null || _a === void 0 ? void 0 : _a.call(method, context);
65942
+ method.context = context;
65933
65943
  method.requestContext = createRequestContext(method.responseID, method.name, {
65934
65944
  sdkInstanceId: context.sdkInstanceId,
65935
65945
  connectId: method.connectId,
@@ -65947,7 +65957,11 @@ const callAPI = (context, message) => __awaiter(void 0, void 0, void 0, function
65947
65957
  if (!method.useDevice) {
65948
65958
  updateMethodRequestContext(method, { status: 'running' });
65949
65959
  try {
65950
- const response = yield method.run();
65960
+ const env = DataManager.getSettings('env');
65961
+ const response = method.name === 'searchDevices' &&
65962
+ (DataManager.isBrowserWebUsb(env) || DataManager.isDesktopWebUsb(env))
65963
+ ? yield context.methodSynchronize(() => method.run(), 'webusb-discovery')
65964
+ : yield method.run();
65951
65965
  completeMethodRequestContext(method);
65952
65966
  return createResponseMessage(method.responseID, true, response);
65953
65967
  }
@@ -66018,20 +66032,25 @@ const waitForPendingPromise = (connectId, getPrePendingCallPromise, removePrePen
66018
66032
  if (pendingPromise) {
66019
66033
  Log.debug('pre pending call promise before call method, wait for it');
66020
66034
  let timer;
66035
+ let timedOut = false;
66021
66036
  try {
66022
66037
  yield Promise.race([
66023
66038
  pendingPromise,
66024
- new Promise((_, reject) => {
66039
+ new Promise(resolve => {
66025
66040
  timer = setTimeout(() => {
66026
- reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceBusy, 'Previous device cancellation is still draining'));
66041
+ timedOut = true;
66042
+ resolve();
66027
66043
  }, PRE_PENDING_CALL_TIMEOUT_MS);
66028
66044
  }),
66029
66045
  ]);
66030
- removePrePendingCallPromise === null || removePrePendingCallPromise === void 0 ? void 0 : removePrePendingCallPromise(connectId, pendingPromise);
66031
66046
  }
66032
66047
  finally {
66033
66048
  if (timer)
66034
66049
  clearTimeout(timer);
66050
+ removePrePendingCallPromise === null || removePrePendingCallPromise === void 0 ? void 0 : removePrePendingCallPromise(connectId, pendingPromise);
66051
+ }
66052
+ if (timedOut) {
66053
+ Log.warn('pre pending call promise timed out before call method', { connectId });
66035
66054
  }
66036
66055
  Log.debug('pre pending call promise before call method done');
66037
66056
  }
@@ -66079,7 +66098,12 @@ const onCallDevice = (context, message, method) => __awaiter(void 0, void 0, voi
66079
66098
  context.registerCallbackTask(method.connectId, preWarmCallbackTask);
66080
66099
  }
66081
66100
  const pollingId = pollingManager.start(connectId);
66082
- device = yield ensureConnected(context, method, connectId, pollingId, method.abortSignal);
66101
+ const env = DataManager.getSettings('env');
66102
+ const connect = () => ensureConnected(context, method, connectId, pollingId, method.abortSignal);
66103
+ device =
66104
+ DataManager.isBrowserWebUsb(env) || DataManager.isDesktopWebUsb(env)
66105
+ ? yield requestQueue.waitForTask(task, () => context.methodSynchronize(connect, 'webusb-discovery'))
66106
+ : yield connect();
66083
66107
  if ((_e = method.abortSignal) === null || _e === void 0 ? void 0 : _e.aborted) {
66084
66108
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallQueueActionCancelled);
66085
66109
  }
@@ -66667,6 +66691,15 @@ const ensureConnected = (_context, method, connectId, pollingId, abortSignal) =>
66667
66691
  if (error.errorCode === hdShared.HardwareErrorCode.TransportNotConfigured) {
66668
66692
  yield TransportManager.configure();
66669
66693
  }
66694
+ else {
66695
+ const env = DataManager.getSettings('env');
66696
+ if (DataManager.isBrowserWebUsb(env) || DataManager.isDesktopWebUsb(env)) {
66697
+ if (timer)
66698
+ clearTimeout(timer);
66699
+ reject(error);
66700
+ return;
66701
+ }
66702
+ }
66670
66703
  }
66671
66704
  if (abort()) {
66672
66705
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-core",
3
- "version": "1.2.2-alpha.112",
3
+ "version": "1.2.2-alpha.113",
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.112",
29
- "@onekeyfe/hd-transport": "1.2.2-alpha.112",
28
+ "@onekeyfe/hd-shared": "1.2.2-alpha.113",
29
+ "@onekeyfe/hd-transport": "1.2.2-alpha.113",
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": "3b31cf365ea4042a5fb2e78dd7ede9a2d9d96e04"
49
+ "gitHead": "32601b82b280b9ec97161cb4206a15cf9cd72881"
50
50
  }
@@ -20,12 +20,16 @@ export default class SearchDevices extends BaseMethod {
20
20
  }
21
21
 
22
22
  async run() {
23
- await TransportManager.configure();
23
+ const env = DataManager.getSettings('env');
24
+ const useCachedWebUsbDevices =
25
+ (env === 'webusb' || env === 'desktop-webusb') &&
26
+ (this.context?.requestQueue.getRequestTasksId().length ?? 0) > 0;
27
+ // Core serializes WebUSB discovery with connection setup. A registered
28
+ // business request owns the connection, including its initialization phase.
29
+ if (!useCachedWebUsbDevices) await TransportManager.configure();
24
30
  const deviceDiff = await this.connector?.enumerate();
25
31
  const devicesDescriptor = deviceDiff?.descriptors ?? [];
26
32
 
27
- const env = DataManager.getSettings('env');
28
-
29
33
  /**
30
34
  * No need to call features during Bluetooth scaning
31
35
  * to avoid device pairing
@@ -56,6 +60,13 @@ export default class SearchDevices extends BaseMethod {
56
60
  return devices;
57
61
  }
58
62
 
63
+ if (useCachedWebUsbDevices) {
64
+ return devicesDescriptor.flatMap(descriptor => {
65
+ const device = DevicePool.getDeviceByPath(descriptor.path);
66
+ return device?.features ? [device.toMessageObject()] : [];
67
+ });
68
+ }
69
+
59
70
  const deviceList = [];
60
71
  for (const descriptor of devicesDescriptor) {
61
72
  try {
package/src/core/index.ts CHANGED
@@ -84,7 +84,7 @@ import type { BaseMethod } from '../api/BaseMethod';
84
84
 
85
85
  const Log = getLogger(LoggerNames.Core);
86
86
  const PRE_INITIALIZE_TTL_MS = 60 * 1000;
87
- const PRE_PENDING_CALL_TIMEOUT_MS = 15 * 1000;
87
+ const PRE_PENDING_CALL_TIMEOUT_MS = 5 * 1000;
88
88
  const PRO2_USB_SIGNING_COOLDOWN_MS = 1000;
89
89
 
90
90
  // Dedup/coalesce state for "pre-warm signal" methods (isPreWarmSignal),
@@ -183,6 +183,7 @@ export const callAPI = async (context: CoreContext, message: CoreMessage) => {
183
183
  }
184
184
  };
185
185
  method.setContext?.(context);
186
+ method.context = context;
186
187
 
187
188
  method.requestContext = createRequestContext(method.responseID, method.name, {
188
189
  sdkInstanceId: context.sdkInstanceId,
@@ -203,7 +204,12 @@ export const callAPI = async (context: CoreContext, message: CoreMessage) => {
203
204
  if (!method.useDevice) {
204
205
  updateMethodRequestContext(method, { status: 'running' });
205
206
  try {
206
- const response = await method.run();
207
+ const env = DataManager.getSettings('env');
208
+ const response =
209
+ method.name === 'searchDevices' &&
210
+ (DataManager.isBrowserWebUsb(env) || DataManager.isDesktopWebUsb(env))
211
+ ? await context.methodSynchronize(() => method.run(), 'webusb-discovery')
212
+ : await method.run();
207
213
  completeMethodRequestContext(method);
208
214
  return createResponseMessage(method.responseID, true, response);
209
215
  } catch (error) {
@@ -315,25 +321,25 @@ const waitForPendingPromise = async (
315
321
  if (pendingPromise) {
316
322
  Log.debug('pre pending call promise before call method, wait for it');
317
323
  let timer: ReturnType<typeof setTimeout> | undefined;
324
+ let timedOut = false;
318
325
  try {
319
326
  await Promise.race([
320
327
  pendingPromise,
321
- new Promise<void>((_, reject) => {
328
+ new Promise<void>(resolve => {
322
329
  timer = setTimeout(() => {
323
- reject(
324
- ERRORS.TypedError(
325
- HardwareErrorCode.DeviceBusy,
326
- 'Previous device cancellation is still draining'
327
- )
328
- );
330
+ timedOut = true;
331
+ resolve();
329
332
  }, PRE_PENDING_CALL_TIMEOUT_MS);
330
333
  }),
331
334
  ]);
332
- // A deadline is not evidence that old I/O is safe to reuse. Keep the
333
- // barrier on failure; a later call may proceed only after cleanup settles.
334
- removePrePendingCallPromise?.(connectId, pendingPromise);
335
335
  } finally {
336
336
  if (timer) clearTimeout(timer);
337
+ // Match the legacy behavior: a stuck cleanup must not permanently poison
338
+ // this connectId. The next request will reacquire the device if needed.
339
+ removePrePendingCallPromise?.(connectId, pendingPromise);
340
+ }
341
+ if (timedOut) {
342
+ Log.warn('pre pending call promise timed out before call method', { connectId });
337
343
  }
338
344
  Log.debug('pre pending call promise before call method done');
339
345
  }
@@ -411,7 +417,17 @@ const onCallDevice = async (
411
417
  * Polling to ensure successful connection
412
418
  */
413
419
  const pollingId = pollingManager.start(connectId);
414
- device = await ensureConnected(context, method, connectId, pollingId, method.abortSignal);
420
+ const env = DataManager.getSettings('env');
421
+ const connect = () =>
422
+ ensureConnected(context, method, connectId, pollingId, method.abortSignal);
423
+ // Discovery may acquire USB endpoints. Finish it before initializing a public
424
+ // request; once registered, that request makes discovery use cached state only.
425
+ device =
426
+ DataManager.isBrowserWebUsb(env) || DataManager.isDesktopWebUsb(env)
427
+ ? await requestQueue.waitForTask(task, () =>
428
+ context.methodSynchronize(connect, 'webusb-discovery')
429
+ )
430
+ : await connect();
415
431
  if (method.abortSignal?.aborted) {
416
432
  throw ERRORS.TypedError(HardwareErrorCode.CallQueueActionCancelled);
417
433
  }
@@ -1255,6 +1271,15 @@ const ensureConnected = async (
1255
1271
  }
1256
1272
  if (error.errorCode === HardwareErrorCode.TransportNotConfigured) {
1257
1273
  await TransportManager.configure();
1274
+ } else {
1275
+ const env = DataManager.getSettings('env');
1276
+ if (DataManager.isBrowserWebUsb(env) || DataManager.isDesktopWebUsb(env)) {
1277
+ // Enumeration succeeded far enough to attempt initialization. Do not
1278
+ // replace its failure with a lookup against the now-empty DeviceList.
1279
+ if (timer) clearTimeout(timer);
1280
+ reject(error);
1281
+ return;
1282
+ }
1258
1283
  }
1259
1284
  }
1260
1285