@onekeyfe/hd-core 1.2.0-alpha.130 → 1.2.0-alpha.132

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.
@@ -800,7 +800,13 @@ describe('DeviceCommands cancellation', () => {
800
800
  try {
801
801
  const commands = createCommands();
802
802
  commands.disposed = false;
803
+ commands.mainId = 'main-id';
803
804
  commands.callPromise = new Promise(() => {});
805
+ const disconnect = jest.fn().mockResolvedValue(undefined);
806
+ commands.transport = {
807
+ name: 'ReactNativeBleTransport',
808
+ disconnect,
809
+ } as any;
804
810
  const dispose = jest.fn().mockResolvedValue(undefined);
805
811
  commands.dispose = dispose;
806
812
 
@@ -810,6 +816,7 @@ describe('DeviceCommands cancellation', () => {
810
816
 
811
817
  await expect(cancellation).resolves.toBeUndefined();
812
818
  expect(dispose).toHaveBeenCalledWith(true);
819
+ expect(disconnect).toHaveBeenCalledWith('main-id');
813
820
  expect(commands.callPromise).toBeUndefined();
814
821
  } finally {
815
822
  jest.useRealTimers();
@@ -1,7 +1,7 @@
1
1
  import { EDeviceType, ERRORS, HardwareErrorCode, createDeferred } from '@onekeyfe/hd-shared';
2
2
  import { DeviceType, TRANSPORT_EVENT } from '@onekeyfe/hd-transport';
3
3
 
4
- import { initConnector, initCore } from '../src/core';
4
+ import { initConnector, initCore, isMissingDetectedProtocolV2Error } from '../src/core';
5
5
  import { DataManager } from '../src/data-manager';
6
6
  import TransportManager from '../src/data-manager/TransportManager';
7
7
  import { Device } from '../src/device/Device';
@@ -82,6 +82,23 @@ describe('public device lifecycle events', () => {
82
82
  expect(DevicePool.emitter.listenerCount(DEVICE.DISCONNECT)).toBe(1);
83
83
  });
84
84
 
85
+ test('isolates pending cancellation cleanup by connect id', () => {
86
+ core = initCore();
87
+ const context = (core as any).getCoreContext();
88
+ const firstCleanup = createDeferred<void>();
89
+ const replacementCleanup = createDeferred<void>();
90
+
91
+ context.setPrePendingCallPromise('device-a', firstCleanup.promise);
92
+
93
+ expect(context.getPrePendingCallPromise('device-a')).toBe(firstCleanup.promise);
94
+ expect(context.getPrePendingCallPromise('device-b')).toBeUndefined();
95
+
96
+ context.setPrePendingCallPromise('device-a', replacementCleanup.promise);
97
+ context.removePrePendingCallPromise('device-a', firstCleanup.promise);
98
+
99
+ expect(context.getPrePendingCallPromise('device-a')).toBe(replacementCleanup.promise);
100
+ });
101
+
85
102
  test('keeps shared device lifecycle listeners across a device cache reset', () => {
86
103
  jest.spyOn(DataManager, 'getSettings').mockReturnValue('react-native' as never);
87
104
  core = initCore();
@@ -234,6 +251,22 @@ describe('public device lifecycle events', () => {
234
251
  expect(device.getProtocol()).toBe('V2');
235
252
  });
236
253
 
254
+ test.each([
255
+ ['V2', true],
256
+ ['V1', false],
257
+ ] as const)(
258
+ 'retries a missing detected protocol only for an explicit Protocol %s connection',
259
+ (connectProtocol, expected) => {
260
+ const method = { payload: { connectProtocol } } as never;
261
+ const error = {
262
+ errorCode: HardwareErrorCode.RuntimeError,
263
+ message: 'Device protocol has not been detected for ble-id',
264
+ };
265
+
266
+ expect(isMissingDetectedProtocolV2Error(method, error)).toBe(expected);
267
+ }
268
+ );
269
+
237
270
  test('converts an internal transport disconnect into a public KnownDevice snapshot', () => {
238
271
  jest.spyOn(DataManager, 'getSettings').mockReturnValue('react-native' as never);
239
272
  core = initCore();
@@ -361,8 +361,8 @@ describe('DeviceSettings protocol routing', () => {
361
361
  expect(getDeviceState).toHaveBeenCalledWith({ refreshSections: ['settings'] });
362
362
  });
363
363
 
364
- it('rejects a successful Pro2 page response when the hardware did not reach the target', async () => {
365
- const { device, getDeviceState } = createDevice({ protocol: 'V2' });
364
+ it('does not overwrite Pro2 passphrase state when refreshed state does not match', async () => {
365
+ const { device, getDeviceState, updateState } = createDevice({ protocol: 'V2' });
366
366
  getDeviceState.mockResolvedValue({
367
367
  status: { passphraseProtection: false },
368
368
  });
@@ -376,14 +376,12 @@ describe('DeviceSettings protocol routing', () => {
376
376
  method.init();
377
377
  (method as any).device = device;
378
378
 
379
- await expect(method.run()).rejects.toMatchObject({
380
- errorCode: HardwareErrorCode.RuntimeError,
381
- message: 'Protocol V2 passphrase setting did not reach the requested value.',
382
- });
379
+ await expect(method.run()).resolves.toEqual({ message: 'Success' });
380
+ expect(updateState).not.toHaveBeenCalled();
383
381
  });
384
382
 
385
- it('accepts a locked Pro2 as confirmation after disabling passphrase', async () => {
386
- const { device, getDeviceState } = createDevice({ protocol: 'V2' });
383
+ it('keeps the previous Pro2 passphrase state when refreshed state is unavailable after locking', async () => {
384
+ const { device, getDeviceState, updateState } = createDevice({ protocol: 'V2' });
387
385
  getDeviceState
388
386
  .mockResolvedValueOnce({
389
387
  status: { unlocked: true, passphraseProtection: true },
@@ -402,6 +400,7 @@ describe('DeviceSettings protocol routing', () => {
402
400
  (method as any).device = device;
403
401
 
404
402
  await expect(method.run()).resolves.toEqual({ message: 'Success' });
403
+ expect(updateState).not.toHaveBeenCalled();
405
404
  });
406
405
 
407
406
  it('keeps Protocol V1 passphrase settings on ApplySettings', async () => {
@@ -1 +1 @@
1
- {"version":3,"file":"DeviceSettings.d.ts","sourceRoot":"","sources":["../../../src/api/device/DeviceSettings.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAgB3C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AA0D5D,MAAM,CAAC,OAAO,OAAO,cAAe,SAAQ,UAAU,CAAC,aAAa,CAAC;IACnE,qBAAqB;IAIrB,IAAI;IA0EJ,eAAe;;;;;;;IAWT,GAAG;CAwIV"}
1
+ {"version":3,"file":"DeviceSettings.d.ts","sourceRoot":"","sources":["../../../src/api/device/DeviceSettings.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAgB3C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AA0D5D,MAAM,CAAC,OAAO,OAAO,cAAe,SAAQ,UAAU,CAAC,aAAa,CAAC;IACnE,qBAAqB;IAIrB,IAAI;IA0EJ,eAAe;;;;;;;IAWT,GAAG;CA2HV"}
@@ -5,8 +5,10 @@ import DeviceConnector from '../device/DeviceConnector';
5
5
  import type { ConnectSettings } from '../types';
6
6
  import type { CoreMessage } from '../events';
7
7
  import type { LowlevelTransportSharedPlugin } from '@onekeyfe/hd-transport';
8
+ import type { BaseMethod } from '../api/BaseMethod';
8
9
  export type CoreContext = ReturnType<Core['getCoreContext']>;
9
10
  export declare const callAPI: (context: CoreContext, message: CoreMessage) => Promise<any>;
11
+ export declare function isMissingDetectedProtocolV2Error(method: BaseMethod, error: unknown): boolean;
10
12
  export declare const cancel: (context: CoreContext, connectId?: string) => void;
11
13
  export declare const onDeviceButtonHandler: (__0_0: Device, __0_1: import("../events").DeviceButtonRequestPayload) => void;
12
14
  export default class Core extends EventEmitter {
@@ -14,7 +16,7 @@ export default class Core extends EventEmitter {
14
16
  readonly sdkInstanceId: string;
15
17
  private requestQueue;
16
18
  private disposePromise?;
17
- private prePendingCallPromise;
19
+ private prePendingCallPromises;
18
20
  private methodSynchronize;
19
21
  constructor();
20
22
  private getCoreContext;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":";AACA,OAAO,YAAY,MAAM,QAAQ,CAAC;AAoClC,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,EACV,6BAA6B,EAG9B,MAAM,wBAAwB,CAAC;AAWhC,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC;AAmE7D,eAAO,MAAM,OAAO,YAAmB,WAAW,WAAW,WAAW,iBAoFvE,CAAC;AAg5BF,eAAO,MAAM,MAAM,YAAa,WAAW,cAAc,MAAM,SAkF9D,CAAC;AAmGF,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,qBAAqB,CAA4B;IAEzD,OAAO,CAAC,iBAAiB,CAAoB;;IAS7C,OAAO,CAAC,cAAc;IAoBhB,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;AAoClC,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,EACV,6BAA6B,EAG9B,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAWpD,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC;AAmE7D,eAAO,MAAM,OAAO,YAAmB,WAAW,WAAW,WAAW,iBAoFvE,CAAC;AA2qBF,wBAAgB,gCAAgC,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,WAQlF;AAsPD,eAAO,MAAM,MAAM,YAAa,WAAW,cAAc,MAAM,SAqF9D,CAAC;AAmGF,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;IA6BhB,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 +1 @@
1
- {"version":3,"file":"DeviceCommands.d.ts","sourceRoot":"","sources":["../../src/device/DeviceCommands.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"DeviceCommands.d.ts","sourceRoot":"","sources":["../../src/device/DeviceCommands.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1093,7 +1093,7 @@ declare class Core extends EventEmitter {
1093
1093
  readonly sdkInstanceId: string;
1094
1094
  private requestQueue;
1095
1095
  private disposePromise?;
1096
- private prePendingCallPromise;
1096
+ private prePendingCallPromises;
1097
1097
  private methodSynchronize;
1098
1098
  constructor();
1099
1099
  private getCoreContext;
package/dist/index.js CHANGED
@@ -43679,6 +43679,7 @@ class DeviceCommands {
43679
43679
  }
43680
43680
  const activeCall = this.callPromise;
43681
43681
  let timer;
43682
+ let timedOut = false;
43682
43683
  const cancellation = (() => __awaiter(this, void 0, void 0, function* () {
43683
43684
  yield this.dispose(true);
43684
43685
  yield (activeCall === null || activeCall === void 0 ? void 0 : activeCall.catch(() => undefined));
@@ -43687,13 +43688,23 @@ class DeviceCommands {
43687
43688
  yield Promise.race([
43688
43689
  cancellation,
43689
43690
  new Promise(resolve => {
43690
- timer = setTimeout(resolve, 10 * 1000);
43691
+ timer = setTimeout(() => {
43692
+ timedOut = true;
43693
+ resolve();
43694
+ }, 10 * 1000);
43691
43695
  }),
43692
43696
  ]);
43693
43697
  }
43694
43698
  finally {
43695
43699
  if (timer)
43696
43700
  clearTimeout(timer);
43701
+ if (timedOut &&
43702
+ this.transport.name === 'ReactNativeBleTransport' &&
43703
+ this.transport.disconnect) {
43704
+ yield this.transport.disconnect(this.mainId).catch(error => {
43705
+ Log$h.debug('BLE cancellation timeout disconnect error (ignored)', error);
43706
+ });
43707
+ }
43697
43708
  if (this.callPromise === activeCall) {
43698
43709
  this.callPromise = undefined;
43699
43710
  }
@@ -48515,14 +48526,7 @@ class DeviceSettings extends BaseMethod {
48515
48526
  const res = yield this.device.commands.typedCall('DeviceSettingsPageShow', 'Success', {
48516
48527
  page: hdTransport.DeviceSettingsPage.DevicePassphrase,
48517
48528
  });
48518
- const updated = yield refreshStatusAndSettings();
48519
- const lockedAfterDisabling = requestedPassphrase === false &&
48520
- current.status.unlocked === true &&
48521
- updated.status.unlocked === false;
48522
- if (updated.status.passphraseProtection !== requestedPassphrase &&
48523
- !lockedAfterDisabling) {
48524
- throw hdShared.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Protocol V2 passphrase setting did not reach the requested value.');
48525
- }
48529
+ yield refreshStatusAndSettings();
48526
48530
  return res.message;
48527
48531
  }
48528
48532
  if (requestedAirgap !== undefined) {
@@ -65014,6 +65018,7 @@ const createUiProgressMessageFilter = (intervalMs = DEFAULT_UI_PROGRESS_INTERVAL
65014
65018
 
65015
65019
  const Log = getLogger(exports.LoggerNames.Core);
65016
65020
  const PRE_INITIALIZE_TTL_MS = 60 * 1000;
65021
+ const PRE_PENDING_CALL_TIMEOUT_MS = 15 * 1000;
65017
65022
  const preWarmInflight = new Map();
65018
65023
  const preWarmDoneAt = new Map();
65019
65024
  function hasDeriveCardano(method) {
@@ -65169,23 +65174,40 @@ const handlePreWarmSignal = (context, message, method) => __awaiter(void 0, void
65169
65174
  }
65170
65175
  }
65171
65176
  });
65172
- const waitForPendingPromise = (getPrePendingCallPromise, removePrePendingCallPromise) => __awaiter(void 0, void 0, void 0, function* () {
65173
- const pendingPromise = getPrePendingCallPromise();
65177
+ const waitForPendingPromise = (connectId, getPrePendingCallPromise, removePrePendingCallPromise) => __awaiter(void 0, void 0, void 0, function* () {
65178
+ const pendingPromise = getPrePendingCallPromise(connectId);
65174
65179
  if (pendingPromise) {
65175
65180
  Log.debug('pre pending call promise before call method, wait for it');
65181
+ let timer;
65182
+ let timedOut = false;
65176
65183
  try {
65177
- yield pendingPromise;
65184
+ yield Promise.race([
65185
+ pendingPromise,
65186
+ new Promise(resolve => {
65187
+ timer = setTimeout(() => {
65188
+ timedOut = true;
65189
+ resolve();
65190
+ }, PRE_PENDING_CALL_TIMEOUT_MS);
65191
+ }),
65192
+ ]);
65178
65193
  }
65179
65194
  catch (error) {
65180
65195
  }
65181
- removePrePendingCallPromise === null || removePrePendingCallPromise === void 0 ? void 0 : removePrePendingCallPromise(pendingPromise);
65196
+ finally {
65197
+ if (timer)
65198
+ clearTimeout(timer);
65199
+ removePrePendingCallPromise === null || removePrePendingCallPromise === void 0 ? void 0 : removePrePendingCallPromise(connectId, pendingPromise);
65200
+ }
65201
+ if (timedOut) {
65202
+ Log.warn('pre pending call promise timed out before call method', { connectId });
65203
+ }
65182
65204
  Log.debug('pre pending call promise before call method done');
65183
65205
  }
65184
65206
  });
65185
65207
  const onCallDevice = (context, message, method) => __awaiter(void 0, void 0, void 0, function* () {
65186
- var _d, _e, _f, _g, _h;
65208
+ var _d, _e, _f, _g, _h, _j, _k;
65187
65209
  let messageResponse;
65188
- const { requestQueue, getPrePendingCallPromise, setPrePendingCallPromise } = context;
65210
+ const { requestQueue, getPrePendingCallPromise, removePrePendingCallPromise } = context;
65189
65211
  updateMethodRequestContext(method, { status: 'running' });
65190
65212
  const normalizePassphraseState = (s) => s || '';
65191
65213
  const connectStateChange = normalizePassphraseState(preConnectCache.passphraseState) !==
@@ -65200,7 +65222,7 @@ const onCallDevice = (context, message, method) => __awaiter(void 0, void 0, voi
65200
65222
  if (method.connectId) {
65201
65223
  yield context.waitForCallbackTasks(method.connectId);
65202
65224
  }
65203
- yield waitForPendingPromise(getPrePendingCallPromise, setPrePendingCallPromise);
65225
+ yield waitForPendingPromise((_d = method.connectId) !== null && _d !== void 0 ? _d : '', getPrePendingCallPromise, removePrePendingCallPromise);
65204
65226
  const task = requestQueue.createTask(method);
65205
65227
  let preWarmCallbackTask;
65206
65228
  if (method.isPreWarmSignal && method.connectId) {
@@ -65209,9 +65231,9 @@ const onCallDevice = (context, message, method) => __awaiter(void 0, void 0, voi
65209
65231
  }
65210
65232
  let device;
65211
65233
  try {
65212
- const connectId = (_d = method.connectId) !== null && _d !== void 0 ? _d : '';
65234
+ const connectId = (_e = method.connectId) !== null && _e !== void 0 ? _e : '';
65213
65235
  const pollingId = pollingManager.start(connectId);
65214
- device = yield ensureConnected(context, method, connectId, pollingId, (_e = task.abortController) === null || _e === void 0 ? void 0 : _e.signal);
65236
+ device = yield ensureConnected(context, method, connectId, pollingId, (_f = task.abortController) === null || _f === void 0 ? void 0 : _f.signal);
65215
65237
  }
65216
65238
  catch (e) {
65217
65239
  preWarmCallbackTask === null || preWarmCallbackTask === void 0 ? void 0 : preWarmCallbackTask.resolve();
@@ -65226,17 +65248,17 @@ const onCallDevice = (context, message, method) => __awaiter(void 0, void 0, voi
65226
65248
  requestQueue.releaseTask(method.responseID);
65227
65249
  return createResponseMessage(method.responseID, false, { error: e });
65228
65250
  }
65229
- if ((_f = method.payload) === null || _f === void 0 ? void 0 : _f.onlyConnectBleDevice) {
65251
+ if ((_g = method.payload) === null || _g === void 0 ? void 0 : _g.onlyConnectBleDevice) {
65230
65252
  preWarmCallbackTask === null || preWarmCallbackTask === void 0 ? void 0 : preWarmCallbackTask.resolve();
65231
65253
  Log.debug('Call API - only connect ble device: ', device === null || device === void 0 ? void 0 : device.mainId);
65232
65254
  return createResponseMessage(method.responseID, true, null);
65233
65255
  }
65234
65256
  Log.debug('Call API - setDevice: ', device.mainId);
65235
- (_g = method.setDevice) === null || _g === void 0 ? void 0 : _g.call(method, device);
65257
+ (_h = method.setDevice) === null || _h === void 0 ? void 0 : _h.call(method, device);
65236
65258
  method.context = context;
65237
65259
  updateMethodRequestContext(method, {
65238
65260
  deviceInstanceId: device.instanceId,
65239
- commandsInstanceId: (_h = device.commands) === null || _h === void 0 ? void 0 : _h.instanceId,
65261
+ commandsInstanceId: (_j = device.commands) === null || _j === void 0 ? void 0 : _j.instanceId,
65240
65262
  });
65241
65263
  const activeRequests = getActiveRequestsByDeviceInstance(device.instanceId);
65242
65264
  if (activeRequests.length > 0) {
@@ -65265,14 +65287,14 @@ const onCallDevice = (context, message, method) => __awaiter(void 0, void 0, voi
65265
65287
  if (method.connectId) {
65266
65288
  yield context.waitForCallbackTasks(method.connectId, preWarmCallbackTask);
65267
65289
  }
65268
- yield waitForPendingPromise(getPrePendingCallPromise, setPrePendingCallPromise);
65290
+ yield waitForPendingPromise((_k = method.connectId) !== null && _k !== void 0 ? _k : '', getPrePendingCallPromise, removePrePendingCallPromise);
65269
65291
  const inner = () => __awaiter(void 0, void 0, void 0, function* () {
65270
- var _j, _k;
65292
+ var _l, _m;
65271
65293
  method.assertProtocolSupported(device.getProtocol(), device.getCurrentFirmwareType());
65272
65294
  protocolV2Operation = device.isProtocolV2();
65273
65295
  const versionRange = device.getCurrentMethodVersionRange(type => method.getVersionRange()[type]);
65274
- const currentFirmwareVersion = (_j = device.getCurrentFirmwareVersionString()) !== null && _j !== void 0 ? _j : '0.0.0';
65275
- const currentBleVersion = (_k = device.getCurrentBLEFirmwareVersionString()) !== null && _k !== void 0 ? _k : '0.0.0';
65296
+ const currentFirmwareVersion = (_l = device.getCurrentFirmwareVersionString()) !== null && _l !== void 0 ? _l : '0.0.0';
65297
+ const currentBleVersion = (_m = device.getCurrentBLEFirmwareVersionString()) !== null && _m !== void 0 ? _m : '0.0.0';
65276
65298
  const deviceFirmwareType = device.getCurrentFirmwareType();
65277
65299
  let newVersionStatus;
65278
65300
  if (device.features && !device.isProtocolV2()) {
@@ -65350,7 +65372,7 @@ const onCallDevice = (context, message, method) => __awaiter(void 0, void 0, voi
65350
65372
  }
65351
65373
  },
65352
65374
  prepare: () => __awaiter(void 0, void 0, void 0, function* () {
65353
- var _l, _m, _o, _p, _q;
65375
+ var _o, _p, _q, _r, _s;
65354
65376
  if (method.deviceId && method.checkDeviceId && !deviceIdCheckedDuringUnlockPreflight) {
65355
65377
  const isSameDeviceID = yield checkLiveDeviceId(device, method.deviceId);
65356
65378
  if (!isSameDeviceID) {
@@ -65370,7 +65392,7 @@ const onCallDevice = (context, message, method) => __awaiter(void 0, void 0, voi
65370
65392
  require: support.require,
65371
65393
  });
65372
65394
  }
65373
- const passphraseStateSafety = yield device.checkPassphraseStateSafety((_l = method.payload) === null || _l === void 0 ? void 0 : _l.passphraseState, (_m = method.payload) === null || _m === void 0 ? void 0 : _m.useEmptyPassphrase, (_o = method.payload) === null || _o === void 0 ? void 0 : _o.skipPassphraseCheck, hasDeriveCardano(method));
65395
+ const passphraseStateSafety = yield device.checkPassphraseStateSafety((_o = method.payload) === null || _o === void 0 ? void 0 : _o.passphraseState, (_p = method.payload) === null || _p === void 0 ? void 0 : _p.useEmptyPassphrase, (_q = method.payload) === null || _q === void 0 ? void 0 : _q.skipPassphraseCheck, hasDeriveCardano(method));
65374
65396
  checkPassphraseEnableState(method, device.features);
65375
65397
  if (!passphraseStateSafety) {
65376
65398
  DevicePool.clearDeviceCache(method.payload.connectId);
@@ -65388,7 +65410,7 @@ const onCallDevice = (context, message, method) => __awaiter(void 0, void 0, voi
65388
65410
  ? e
65389
65411
  : hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'open safety check failed.');
65390
65412
  }
65391
- (_q = (_p = method.device) === null || _p === void 0 ? void 0 : _p.commands) === null || _q === void 0 ? void 0 : _q.checkDisposed();
65413
+ (_s = (_r = method.device) === null || _r === void 0 ? void 0 : _r.commands) === null || _s === void 0 ? void 0 : _s.checkDisposed();
65392
65414
  }),
65393
65415
  });
65394
65416
  messageResponse = createResponseMessage(method.responseID, true, response);
@@ -65584,9 +65606,10 @@ function isRetryableBleProtocolV2ProbeError(method, error) {
65584
65606
  message.includes('expected V2') &&
65585
65607
  message.includes('did not respond to expected protocol'));
65586
65608
  }
65587
- function isMissingDetectedProtocolError(error) {
65609
+ function isMissingDetectedProtocolV2Error(method, error) {
65588
65610
  const typedError = error;
65589
- return ((typedError === null || typedError === void 0 ? void 0 : typedError.errorCode) === hdShared.HardwareErrorCode.RuntimeError &&
65611
+ return (method.payload.connectProtocol === 'V2' &&
65612
+ (typedError === null || typedError === void 0 ? void 0 : typedError.errorCode) === hdShared.HardwareErrorCode.RuntimeError &&
65590
65613
  typeof typedError.message === 'string' &&
65591
65614
  typedError.message.includes('Device protocol has not been detected'));
65592
65615
  }
@@ -65624,7 +65647,7 @@ function connectDeviceForBle(method, device, retryCount = 0) {
65624
65647
  }
65625
65648
  }
65626
65649
  catch (err) {
65627
- const requiresColdReconnect = isMissingDetectedProtocolError(err);
65650
+ const requiresColdReconnect = isMissingDetectedProtocolV2Error(method, err);
65628
65651
  if ((hdShared.ERROR_CODES_REQUIRE_DISCONNECT.includes(err.errorCode) || requiresColdReconnect) &&
65629
65652
  device.mainId &&
65630
65653
  device.deviceConnector) {
@@ -65656,7 +65679,7 @@ const ensureConnected = (_context, method, connectId, pollingId, abortSignal) =>
65656
65679
  Log.debug(`EnsureConnected function start, MAX_RETRY_COUNT=${MAX_RETRY_COUNT}, POLL_INTERVAL_TIME=${POLL_INTERVAL_TIME} `);
65657
65680
  const poll = (time = POLL_INTERVAL_TIME) => __awaiter(void 0, void 0, void 0, function* () {
65658
65681
  return new Promise((resolve, reject) => __awaiter(void 0, void 0, void 0, function* () {
65659
- var _r;
65682
+ var _t;
65660
65683
  const abort = () => {
65661
65684
  if (abortSignal && abortSignal.aborted) {
65662
65685
  if (timer) {
@@ -65771,7 +65794,7 @@ const ensureConnected = (_context, method, connectId, pollingId, abortSignal) =>
65771
65794
  clearTimeout(timer);
65772
65795
  }
65773
65796
  Log.debug('EnsureConnected get to max try count, will return: ', tryCount);
65774
- if (DataManager.isBrowserWebUsb(env) && !((_r = method.payload) === null || _r === void 0 ? void 0 : _r.skipWebDevicePrompt)) {
65797
+ if (DataManager.isBrowserWebUsb(env) && !((_t = method.payload) === null || _t === void 0 ? void 0 : _t.skipWebDevicePrompt)) {
65775
65798
  postMessage(createUiMessage(UI_REQUEST.WEB_DEVICE_PROMPT_ACCESS_PERMISSION));
65776
65799
  reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.WebDeviceNotFoundOrNeedsPermission));
65777
65800
  }
@@ -65789,7 +65812,7 @@ const ensureConnected = (_context, method, connectId, pollingId, abortSignal) =>
65789
65812
  return poll();
65790
65813
  });
65791
65814
  const cancel = (context, connectId) => {
65792
- var _a, _b;
65815
+ var _a, _b, _c;
65793
65816
  const { requestQueue, setPrePendingCallPromise } = context;
65794
65817
  if (connectId) {
65795
65818
  try {
@@ -65803,7 +65826,7 @@ const cancel = (context, connectId) => {
65803
65826
  if (task && ((_a = task.method) === null || _a === void 0 ? void 0 : _a.device)) {
65804
65827
  if (!canceledDevices.includes(task.method.device)) {
65805
65828
  const { device } = task.method;
65806
- setPrePendingCallPromise(device === null || device === void 0 ? void 0 : device.interruptionFromUser());
65829
+ setPrePendingCallPromise((_b = task.method.connectId) !== null && _b !== void 0 ? _b : connectId, device.interruptionFromUser());
65807
65830
  canceledDevices.push(device);
65808
65831
  }
65809
65832
  requestQueue.rejectRequest(requestId, hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.CallQueueActionCancelled));
@@ -65823,7 +65846,7 @@ const cancel = (context, connectId) => {
65823
65846
  for (const requestId of requestQueue.getRequestTasksId()) {
65824
65847
  const task = requestQueue.getTask(requestId);
65825
65848
  Log.debug('Cancel Api connect task: ', task);
65826
- if (task && ((_b = task.method) === null || _b === void 0 ? void 0 : _b.device)) {
65849
+ if (task && ((_c = task.method) === null || _c === void 0 ? void 0 : _c.device)) {
65827
65850
  if (!canceledDevices.includes(task.method.device)) {
65828
65851
  const { device } = task.method;
65829
65852
  device === null || device === void 0 ? void 0 : device.interruptionFromUser();
@@ -66012,6 +66035,7 @@ class Core extends events.exports {
66012
66035
  constructor() {
66013
66036
  super();
66014
66037
  this.requestQueue = new RequestQueue();
66038
+ this.prePendingCallPromises = new Map();
66015
66039
  this.methodSynchronize = getSynchronize();
66016
66040
  this.tracingContext = createSdkTracingContext();
66017
66041
  this.sdkInstanceId = this.tracingContext.sdkInstanceId;
@@ -66023,9 +66047,18 @@ class Core extends events.exports {
66023
66047
  tracingContext: this.tracingContext,
66024
66048
  requestQueue: this.requestQueue,
66025
66049
  methodSynchronize: this.methodSynchronize,
66026
- getPrePendingCallPromise: () => this.prePendingCallPromise,
66027
- setPrePendingCallPromise: (promise) => {
66028
- this.prePendingCallPromise = promise;
66050
+ getPrePendingCallPromise: (connectId) => this.prePendingCallPromises.get(connectId),
66051
+ setPrePendingCallPromise: (connectId, promise) => {
66052
+ if (!promise) {
66053
+ this.prePendingCallPromises.delete(connectId);
66054
+ return;
66055
+ }
66056
+ this.prePendingCallPromises.set(connectId, promise);
66057
+ },
66058
+ removePrePendingCallPromise: (connectId, promise) => {
66059
+ if (this.prePendingCallPromises.get(connectId) === promise) {
66060
+ this.prePendingCallPromises.delete(connectId);
66061
+ }
66029
66062
  },
66030
66063
  registerCallbackTask: (connectId, callbackPromise) => {
66031
66064
  this.requestQueue.registerPendingCallbackTask(connectId, callbackPromise);
@@ -66119,7 +66152,7 @@ class Core extends events.exports {
66119
66152
  preWarmInflight.clear();
66120
66153
  preWarmDoneAt.clear();
66121
66154
  preConnectCache = { passphraseState: undefined };
66122
- this.prePendingCallPromise = undefined;
66155
+ this.prePendingCallPromises.clear();
66123
66156
  this.removeAllListeners();
66124
66157
  cleanupSdkInstance(this.sdkInstanceId);
66125
66158
  if (_core === this)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-core",
3
- "version": "1.2.0-alpha.130",
3
+ "version": "1.2.0-alpha.132",
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.0-alpha.130",
29
- "@onekeyfe/hd-transport": "1.2.0-alpha.130",
28
+ "@onekeyfe/hd-shared": "1.2.0-alpha.132",
29
+ "@onekeyfe/hd-transport": "1.2.0-alpha.132",
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": "287003096bc4df3a5824f2bea77e50f7fdc20565"
49
+ "gitHead": "e02f784ed4bd26e3efee4592c0c286f0d740425e"
50
50
  }
@@ -200,20 +200,7 @@ export default class DeviceSettings extends BaseMethod<ApplySettings> {
200
200
  const res = await this.device.commands.typedCall('DeviceSettingsPageShow', 'Success', {
201
201
  page: DeviceSettingsPage.DevicePassphrase,
202
202
  });
203
- const updated = await refreshStatusAndSettings();
204
- const lockedAfterDisabling =
205
- requestedPassphrase === false &&
206
- current.status.unlocked === true &&
207
- updated.status.unlocked === false;
208
- if (
209
- updated.status.passphraseProtection !== requestedPassphrase &&
210
- !lockedAfterDisabling
211
- ) {
212
- throw TypedError(
213
- HardwareErrorCode.RuntimeError,
214
- 'Protocol V2 passphrase setting did not reach the requested value.'
215
- );
216
- }
203
+ await refreshStatusAndSettings();
217
204
  return res.message;
218
205
  }
219
206
  if (requestedAirgap !== undefined) {
package/src/core/index.ts CHANGED
@@ -83,6 +83,7 @@ import type { BaseMethod } from '../api/BaseMethod';
83
83
 
84
84
  const Log = getLogger(LoggerNames.Core);
85
85
  const PRE_INITIALIZE_TTL_MS = 60 * 1000;
86
+ const PRE_PENDING_CALL_TIMEOUT_MS = 15 * 1000;
86
87
 
87
88
  // Dedup/coalesce state for "pre-warm signal" methods (isPreWarmSignal),
88
89
  // keyed by getPreWarmKey(): coalesce in-flight, skip if warmed within TTL.
@@ -297,18 +298,34 @@ const handlePreWarmSignal = async (
297
298
  };
298
299
 
299
300
  const waitForPendingPromise = async (
300
- getPrePendingCallPromise: () => Promise<void> | undefined,
301
- removePrePendingCallPromise?: (promise: Promise<void> | undefined) => void
301
+ connectId: string,
302
+ getPrePendingCallPromise: (connectId: string) => Promise<void> | undefined,
303
+ removePrePendingCallPromise?: (connectId: string, promise: Promise<void>) => void
302
304
  ) => {
303
- const pendingPromise = getPrePendingCallPromise();
305
+ const pendingPromise = getPrePendingCallPromise(connectId);
304
306
  if (pendingPromise) {
305
307
  Log.debug('pre pending call promise before call method, wait for it');
308
+ let timer: ReturnType<typeof setTimeout> | undefined;
309
+ let timedOut = false;
306
310
  try {
307
- await pendingPromise;
311
+ await Promise.race([
312
+ pendingPromise,
313
+ new Promise<void>(resolve => {
314
+ timer = setTimeout(() => {
315
+ timedOut = true;
316
+ resolve();
317
+ }, PRE_PENDING_CALL_TIMEOUT_MS);
318
+ }),
319
+ ]);
308
320
  } catch (error) {
309
- // Cancellation is best-effort, but its teardown must settle before reuse.
321
+ // Cancellation is best-effort; the transport teardown owns recovery.
322
+ } finally {
323
+ if (timer) clearTimeout(timer);
324
+ removePrePendingCallPromise?.(connectId, pendingPromise);
325
+ }
326
+ if (timedOut) {
327
+ Log.warn('pre pending call promise timed out before call method', { connectId });
310
328
  }
311
- removePrePendingCallPromise?.(pendingPromise);
312
329
  Log.debug('pre pending call promise before call method done');
313
330
  }
314
331
  };
@@ -320,7 +337,7 @@ const onCallDevice = async (
320
337
  ): Promise<any> => {
321
338
  let messageResponse: any;
322
339
 
323
- const { requestQueue, getPrePendingCallPromise, setPrePendingCallPromise } = context;
340
+ const { requestQueue, getPrePendingCallPromise, removePrePendingCallPromise } = context;
324
341
 
325
342
  updateMethodRequestContext(method, { status: 'running' });
326
343
 
@@ -348,7 +365,11 @@ const onCallDevice = async (
348
365
  await context.waitForCallbackTasks(method.connectId);
349
366
  }
350
367
 
351
- await waitForPendingPromise(getPrePendingCallPromise, setPrePendingCallPromise);
368
+ await waitForPendingPromise(
369
+ method.connectId ?? '',
370
+ getPrePendingCallPromise,
371
+ removePrePendingCallPromise
372
+ );
352
373
 
353
374
  const task = requestQueue.createTask(method);
354
375
 
@@ -447,7 +468,11 @@ const onCallDevice = async (
447
468
  await context.waitForCallbackTasks(method.connectId, preWarmCallbackTask);
448
469
  }
449
470
 
450
- await waitForPendingPromise(getPrePendingCallPromise, setPrePendingCallPromise);
471
+ await waitForPendingPromise(
472
+ method.connectId ?? '',
473
+ getPrePendingCallPromise,
474
+ removePrePendingCallPromise
475
+ );
451
476
 
452
477
  const inner = async (): Promise<void> => {
453
478
  // Protocol is established from an active device response during acquire/initialize.
@@ -899,9 +924,10 @@ function isRetryableBleProtocolV2ProbeError(method: BaseMethod, error: unknown)
899
924
  );
900
925
  }
901
926
 
902
- function isMissingDetectedProtocolError(error: unknown) {
927
+ export function isMissingDetectedProtocolV2Error(method: BaseMethod, error: unknown) {
903
928
  const typedError = error as { errorCode?: unknown; message?: unknown };
904
929
  return (
930
+ method.payload.connectProtocol === 'V2' &&
905
931
  typedError?.errorCode === HardwareErrorCode.RuntimeError &&
906
932
  typeof typedError.message === 'string' &&
907
933
  typedError.message.includes('Device protocol has not been detected')
@@ -945,7 +971,7 @@ async function connectDeviceForBle(method: BaseMethod, device: Device, retryCoun
945
971
  DevicePool.emitter.emit(DEVICE.CONNECT, device);
946
972
  }
947
973
  } catch (err) {
948
- const requiresColdReconnect = isMissingDetectedProtocolError(err);
974
+ const requiresColdReconnect = isMissingDetectedProtocolV2Error(method, err);
949
975
  // Device.run()'s REQUIRE_DISCONNECT handling never sees acquire/initialize
950
976
  // failures, so with keep-alive a wedged link would be reused by every retry
951
977
  // (field case: Initialize timing out at 25s per attempt, forever). Drop it
@@ -1181,7 +1207,10 @@ export const cancel = (context: CoreContext, connectId?: string) => {
1181
1207
  if (task && task.method?.device) {
1182
1208
  if (!canceledDevices.includes(task.method.device)) {
1183
1209
  const { device } = task.method;
1184
- setPrePendingCallPromise(device?.interruptionFromUser());
1210
+ setPrePendingCallPromise(
1211
+ task.method.connectId ?? connectId,
1212
+ device.interruptionFromUser()
1213
+ );
1185
1214
  canceledDevices.push(device);
1186
1215
  }
1187
1216
  requestQueue.rejectRequest(
@@ -1535,7 +1564,7 @@ export default class Core extends EventEmitter {
1535
1564
  private disposePromise?: Promise<void>;
1536
1565
 
1537
1566
  // background task
1538
- private prePendingCallPromise: Promise<void> | undefined;
1567
+ private prePendingCallPromises = new Map<string, Promise<void>>();
1539
1568
 
1540
1569
  private methodSynchronize = getSynchronize();
1541
1570
 
@@ -1552,9 +1581,18 @@ export default class Core extends EventEmitter {
1552
1581
  tracingContext: this.tracingContext,
1553
1582
  requestQueue: this.requestQueue,
1554
1583
  methodSynchronize: this.methodSynchronize,
1555
- getPrePendingCallPromise: () => this.prePendingCallPromise,
1556
- setPrePendingCallPromise: (promise: Promise<void> | undefined) => {
1557
- this.prePendingCallPromise = promise;
1584
+ getPrePendingCallPromise: (connectId: string) => this.prePendingCallPromises.get(connectId),
1585
+ setPrePendingCallPromise: (connectId: string, promise?: Promise<void>) => {
1586
+ if (!promise) {
1587
+ this.prePendingCallPromises.delete(connectId);
1588
+ return;
1589
+ }
1590
+ this.prePendingCallPromises.set(connectId, promise);
1591
+ },
1592
+ removePrePendingCallPromise: (connectId: string, promise: Promise<void>) => {
1593
+ if (this.prePendingCallPromises.get(connectId) === promise) {
1594
+ this.prePendingCallPromises.delete(connectId);
1595
+ }
1558
1596
  },
1559
1597
  // callback 任务管理
1560
1598
  registerCallbackTask: (connectId: string, callbackPromise: Deferred<any>) => {
@@ -1669,7 +1707,7 @@ export default class Core extends EventEmitter {
1669
1707
  preWarmInflight.clear();
1670
1708
  preWarmDoneAt.clear();
1671
1709
  preConnectCache = { passphraseState: undefined };
1672
- this.prePendingCallPromise = undefined;
1710
+ this.prePendingCallPromises.clear();
1673
1711
  this.removeAllListeners();
1674
1712
  cleanupSdkInstance(this.sdkInstanceId);
1675
1713
  if (_core === this) _core = undefined;
@@ -264,6 +264,7 @@ export class DeviceCommands {
264
264
  }
265
265
  const activeCall = this.callPromise;
266
266
  let timer: ReturnType<typeof setTimeout> | undefined;
267
+ let timedOut = false;
267
268
  const cancellation = (async () => {
268
269
  await this.dispose(true);
269
270
  await activeCall?.catch(() => undefined);
@@ -273,11 +274,23 @@ export class DeviceCommands {
273
274
  await Promise.race([
274
275
  cancellation,
275
276
  new Promise<void>(resolve => {
276
- timer = setTimeout(resolve, 10 * 1000);
277
+ timer = setTimeout(() => {
278
+ timedOut = true;
279
+ resolve();
280
+ }, 10 * 1000);
277
281
  }),
278
282
  ]);
279
283
  } finally {
280
284
  if (timer) clearTimeout(timer);
285
+ if (
286
+ timedOut &&
287
+ this.transport.name === 'ReactNativeBleTransport' &&
288
+ this.transport.disconnect
289
+ ) {
290
+ await this.transport.disconnect(this.mainId).catch(error => {
291
+ Log.debug('BLE cancellation timeout disconnect error (ignored)', error);
292
+ });
293
+ }
281
294
  if (this.callPromise === activeCall) {
282
295
  this.callPromise = undefined;
283
296
  }