@onekeyfe/hd-transport-electron 1.2.2-alpha.14 → 1.2.2-alpha.17

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.
@@ -32,7 +32,7 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
32
32
 
33
33
  function initNobleBleSupport(webContents) {
34
34
  return __awaiter(this, void 0, void 0, function* () {
35
- const { setupNobleBleHandlers } = yield Promise.resolve().then(function () { return require('./noble-ble-handler-de8d60e9.js'); });
35
+ const { setupNobleBleHandlers } = yield Promise.resolve().then(function () { return require('./noble-ble-handler-0b455c4f.js'); });
36
36
  setupNobleBleHandlers(webContents);
37
37
  });
38
38
  }
package/dist/index.d.ts CHANGED
@@ -37,6 +37,17 @@ interface Logger {
37
37
  interface NobleBleWriteOptions {
38
38
  pacingDelayMs?: number;
39
39
  }
40
+ type NobleBleIpcErrorPayload = {
41
+ name: string;
42
+ message: string;
43
+ errorCode: number;
44
+ params?: unknown;
45
+ };
46
+ type NobleBleIpcErrorResponse = {
47
+ type: 'NobleBleIpcError';
48
+ success: false;
49
+ error: NobleBleIpcErrorPayload;
50
+ };
40
51
  interface NobleBleAPI {
41
52
  enumerate: () => Promise<{
42
53
  id: string;
@@ -82,4 +93,4 @@ interface DesktopAPI {
82
93
 
83
94
  declare function initNobleBleSupport(webContents: WebContents): Promise<void>;
84
95
 
85
- export { CharacteristicPair, DesktopAPI, DeviceInfo, Logger, NobleBleAPI, NobleBleWriteOptions, NobleModule, initNobleBleSupport };
96
+ export { CharacteristicPair, DesktopAPI, DeviceInfo, Logger, NobleBleAPI, NobleBleIpcErrorPayload, NobleBleIpcErrorResponse, NobleBleWriteOptions, NobleModule, initNobleBleSupport };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C,cAAc,SAAS,CAAC;AAGxB,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAEzF,wBAAsB,mBAAmB,CAAC,WAAW,EAAE,WAAW,iBAGjE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C,cAAc,SAAS,CAAC;AAGxB,YAAY,EACV,UAAU,EACV,WAAW,EACX,uBAAuB,EACvB,wBAAwB,EACxB,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAE7B,wBAAsB,mBAAmB,CAAC,WAAW,EAAE,WAAW,iBAGjE"}
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var index = require('./index-ef6298f9.js');
5
+ var index = require('./index-d51c7da6.js');
6
6
 
7
7
 
8
8
 
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./index-ef6298f9.js');
3
+ var index = require('./index-d51c7da6.js');
4
4
  var hdShared = require('@onekeyfe/hd-shared');
5
5
  var pRetry = require('p-retry');
6
6
 
@@ -102,7 +102,8 @@ let noble = null;
102
102
  let logger = null;
103
103
  function createNobleBleConnectionError(error, messagePrefix = '') {
104
104
  const errorMessage = error.message;
105
- if (hdShared.isBleStaleBondErrorText(errorMessage)) {
105
+ const isInvalidMacOsBond = error.nativeErrorCode === 14 && error.nativeErrorDomain === 'CBErrorDomain';
106
+ if (isInvalidMacOsBond) {
106
107
  const nativeErrorMessage = `${messagePrefix}${errorMessage}`;
107
108
  return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleBondInvalid, `${hdShared.HardwareErrorCodeMessage[hdShared.HardwareErrorCode.BleBondInvalid]} (${nativeErrorMessage})`, {
108
109
  nativeErrorMessage,
@@ -110,6 +111,29 @@ function createNobleBleConnectionError(error, messagePrefix = '') {
110
111
  }
111
112
  return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, `${messagePrefix}${errorMessage}`);
112
113
  }
114
+ function createNobleBleIpcErrorResponse(error) {
115
+ const candidate = error;
116
+ const errorCode = typeof (candidate === null || candidate === void 0 ? void 0 : candidate.errorCode) === 'number' ? candidate.errorCode : hdShared.HardwareErrorCode.UnknownError;
117
+ const message = typeof (candidate === null || candidate === void 0 ? void 0 : candidate.message) === 'string' ? candidate.message : String(error !== null && error !== void 0 ? error : 'Unknown error');
118
+ const name = typeof (candidate === null || candidate === void 0 ? void 0 : candidate.name) === 'string' ? candidate.name : 'Error';
119
+ let params;
120
+ if ((candidate === null || candidate === void 0 ? void 0 : candidate.params) !== undefined) {
121
+ try {
122
+ const serializedParams = JSON.stringify(candidate.params);
123
+ params = serializedParams === undefined ? undefined : JSON.parse(serializedParams);
124
+ }
125
+ catch (_a) {
126
+ params = undefined;
127
+ }
128
+ }
129
+ return {
130
+ type: 'NobleBleIpcError',
131
+ success: false,
132
+ error: Object.assign({ name,
133
+ message,
134
+ errorCode }, (params !== undefined ? { params } : {})),
135
+ };
136
+ }
113
137
  const bluetoothState = {
114
138
  available: false,
115
139
  unsupported: false,
@@ -1351,7 +1375,14 @@ function setupNobleBleHandlers(webContents) {
1351
1375
  const { ipcMain } = require('electron');
1352
1376
  const handle = (channel, listener) => {
1353
1377
  ipcMain.removeHandler(channel);
1354
- ipcMain.handle(channel, listener);
1378
+ ipcMain.handle(channel, (...args) => index.__awaiter(this, void 0, void 0, function* () {
1379
+ try {
1380
+ return yield Promise.resolve(listener(...args));
1381
+ }
1382
+ catch (error) {
1383
+ return createNobleBleIpcErrorResponse(error);
1384
+ }
1385
+ }));
1355
1386
  };
1356
1387
  safeLog(logger, 'info', 'Setting up Noble BLE IPC handlers');
1357
1388
  handle(hdShared.EOneKeyBleMessageKeys.NOBLE_BLE_ENUMERATE, () => index.__awaiter(this, void 0, void 0, function* () {
@@ -1472,5 +1503,6 @@ function setupNobleBleHandlers(webContents) {
1472
1503
  }
1473
1504
 
1474
1505
  exports.createNobleBleConnectionError = createNobleBleConnectionError;
1506
+ exports.createNobleBleIpcErrorResponse = createNobleBleIpcErrorResponse;
1475
1507
  exports.resolveNobleBleWritePacingDelay = resolveNobleBleWritePacingDelay;
1476
1508
  exports.setupNobleBleHandlers = setupNobleBleHandlers;
@@ -1,6 +1,12 @@
1
1
  import type { WebContents } from 'electron';
2
- import type { NobleBleWriteOptions } from './types/desktop-api';
3
- export declare function createNobleBleConnectionError(error: Error, messagePrefix?: string): import("@onekeyfe/hd-shared").HardwareError;
2
+ import type { NobleBleIpcErrorResponse, NobleBleWriteOptions } from './types/desktop-api';
3
+ type NobleBleNativeError = Error & {
4
+ nativeErrorCode?: number;
5
+ nativeErrorDomain?: string;
6
+ };
7
+ export declare function createNobleBleConnectionError(error: NobleBleNativeError, messagePrefix?: string): import("@onekeyfe/hd-shared").HardwareError;
8
+ export declare function createNobleBleIpcErrorResponse(error: unknown): NobleBleIpcErrorResponse;
4
9
  export declare function resolveNobleBleWritePacingDelay(options?: NobleBleWriteOptions): number;
5
10
  export declare function setupNobleBleHandlers(webContents: WebContents): void;
11
+ export {};
6
12
  //# sourceMappingURL=noble-ble-handler.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"noble-ble-handler.d.ts","sourceRoot":"","sources":["../src/noble-ble-handler.ts"],"names":[],"mappings":"AAmCA,OAAO,KAAK,EAA+B,WAAW,EAAE,MAAM,UAAU,CAAC;AAEzE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAOhE,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,KAAK,EAAE,aAAa,SAAK,+CAc7E;AA+ED,wBAAgB,+BAA+B,CAAC,OAAO,CAAC,EAAE,oBAAoB,UAI7E;AAuuDD,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,CAkMpE"}
1
+ {"version":3,"file":"noble-ble-handler.d.ts","sourceRoot":"","sources":["../src/noble-ble-handler.ts"],"names":[],"mappings":"AAkCA,OAAO,KAAK,EAA+B,WAAW,EAAE,MAAM,UAAU,CAAC;AAEzE,OAAO,KAAK,EAAE,wBAAwB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAO1F,KAAK,mBAAmB,GAAG,KAAK,GAAG;IACjC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,mBAAmB,EAAE,aAAa,SAAK,+CAgB3F;AAED,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,OAAO,GAAG,wBAAwB,CAgCvF;AA+ED,wBAAgB,+BAA+B,CAAC,OAAO,CAAC,EAAE,oBAAoB,UAI7E;AAuuDD,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,CAwMpE"}
@@ -2,6 +2,17 @@ import type { EBleDisconnectReason } from '@onekeyfe/hd-shared';
2
2
  export interface NobleBleWriteOptions {
3
3
  pacingDelayMs?: number;
4
4
  }
5
+ export type NobleBleIpcErrorPayload = {
6
+ name: string;
7
+ message: string;
8
+ errorCode: number;
9
+ params?: unknown;
10
+ };
11
+ export type NobleBleIpcErrorResponse = {
12
+ type: 'NobleBleIpcError';
13
+ success: false;
14
+ error: NobleBleIpcErrorPayload;
15
+ };
5
16
  export interface NobleBleAPI {
6
17
  enumerate: () => Promise<{
7
18
  id: string;
@@ -1 +1 @@
1
- {"version":3,"file":"desktop-api.d.ts","sourceRoot":"","sources":["../../src/types/desktop-api.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAGhE,MAAM,WAAW,oBAAoB;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC,CAAC;IACzD,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IACxF,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAGzC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,oBAAoB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrF,cAAc,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;IACnF,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;IAOvF,oBAAoB,EAAE,CACpB,QAAQ,EAAE,CAAC,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,oBAAoB,CAAA;KAAE,KAAK,IAAI,KACpF,MAAM,IAAI,CAAC;IAChB,iBAAiB,EAAE,MAAM,OAAO,CAAC;QAC/B,SAAS,EAAE,OAAO,CAAC;QACnB,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,EAAE,OAAO,CAAC;QACrB,WAAW,EAAE,OAAO,CAAC;KACtB,CAAC,CAAC;CACJ;AAGD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,WAAW,CAAC;CACxB"}
1
+ {"version":3,"file":"desktop-api.d.ts","sourceRoot":"","sources":["../../src/types/desktop-api.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAGhE,MAAM,WAAW,oBAAoB;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,MAAM,uBAAuB,GAAG;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,IAAI,EAAE,kBAAkB,CAAC;IACzB,OAAO,EAAE,KAAK,CAAC;IACf,KAAK,EAAE,uBAAuB,CAAC;CAChC,CAAC;AAEF,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC,CAAC;IACzD,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IACxF,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAGzC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,oBAAoB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrF,cAAc,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;IACnF,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;IAOvF,oBAAoB,EAAE,CACpB,QAAQ,EAAE,CAAC,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,oBAAoB,CAAA;KAAE,KAAK,IAAI,KACpF,MAAM,IAAI,CAAC;IAChB,iBAAiB,EAAE,MAAM,OAAO,CAAC;QAC/B,SAAS,EAAE,OAAO,CAAC;QACnB,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,EAAE,OAAO,CAAC;QACrB,WAAW,EAAE,OAAO,CAAC;KACtB,CAAC,CAAC;CACJ;AAGD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,WAAW,CAAC;CACxB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-transport-electron",
3
- "version": "1.2.2-alpha.14",
3
+ "version": "1.2.2-alpha.17",
4
4
  "author": "OneKey",
5
5
  "homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
6
6
  "license": "MIT",
@@ -25,9 +25,9 @@
25
25
  "electron-log": ">=4.0.0"
26
26
  },
27
27
  "dependencies": {
28
- "@onekeyfe/hd-core": "1.2.2-alpha.14",
29
- "@onekeyfe/hd-shared": "1.2.2-alpha.14",
30
- "@onekeyfe/hd-transport": "1.2.2-alpha.14",
28
+ "@onekeyfe/hd-core": "1.2.2-alpha.17",
29
+ "@onekeyfe/hd-shared": "1.2.2-alpha.17",
30
+ "@onekeyfe/hd-transport": "1.2.2-alpha.17",
31
31
  "@stoprocent/noble": "2.3.16",
32
32
  "p-retry": "^4.6.2"
33
33
  },
@@ -36,5 +36,5 @@
36
36
  "electron": "^25.0.0",
37
37
  "typescript": "^5.3.3"
38
38
  },
39
- "gitHead": "1b3bf0576a53bedbc5f4a82308c48e11ffc00b16"
39
+ "gitHead": "b6c48b842d1c325dcd5ce42c03a07ee4377002dd"
40
40
  }
@@ -31,37 +31,90 @@ describe('Electron Noble BLE device discovery', () => {
31
31
  expect(NOBLE_BLE_SUBSCRIBE_TIMEOUT_MS).toBe(10_000);
32
32
  });
33
33
 
34
- test('maps macOS stale pairing failures without reclassifying generic connection errors', async () => {
34
+ test('maps structured macOS stale pairing failures without parsing localized text', async () => {
35
35
  const { createNobleBleConnectionError } = await import('../noble-ble-handler');
36
36
 
37
37
  const staleBondError = createNobleBleConnectionError(
38
- new Error('CBErrorDomain:14 Peer removed pairing information on the device side')
38
+ Object.assign(new Error('Peer removed pairing information on the device side'), {
39
+ nativeErrorCode: 14,
40
+ nativeErrorDomain: 'CBErrorDomain',
41
+ })
39
42
  );
40
43
  expect(staleBondError).toMatchObject({
41
44
  errorCode: HardwareErrorCode.BleBondInvalid,
42
45
  params: {
43
- nativeErrorMessage: 'CBErrorDomain:14 Peer removed pairing information on the device side',
46
+ nativeErrorMessage: 'Peer removed pairing information on the device side',
44
47
  },
45
48
  });
46
- expect(staleBondError.message).toContain(
47
- 'CBErrorDomain:14 Peer removed pairing information on the device side'
48
- );
49
+ expect(staleBondError.message).toContain('Peer removed pairing information on the device side');
49
50
  expect(createNobleBleConnectionError(new Error('Encryption is insufficient'))).toMatchObject({
50
- errorCode: HardwareErrorCode.BleBondInvalid,
51
- params: {
52
- nativeErrorMessage: 'Encryption is insufficient',
53
- },
51
+ errorCode: HardwareErrorCode.BleConnectedError,
54
52
  });
55
53
  expect(
56
- createNobleBleConnectionError(new Error('CBATTErrorDomain:14 localized native message'))
54
+ createNobleBleConnectionError(
55
+ Object.assign(new Error('localized native message'), {
56
+ nativeErrorCode: 14,
57
+ nativeErrorDomain: 'CBATTErrorDomain',
58
+ })
59
+ )
57
60
  ).toMatchObject({
58
- errorCode: HardwareErrorCode.BleBondInvalid,
61
+ errorCode: HardwareErrorCode.BleConnectedError,
59
62
  });
60
63
  expect(createNobleBleConnectionError(new Error('connection failed'))).toMatchObject({
61
64
  errorCode: HardwareErrorCode.BleConnectedError,
62
65
  });
63
66
  });
64
67
 
68
+ test('serializes HardwareError fields for the Noble IPC boundary', async () => {
69
+ const { createNobleBleIpcErrorResponse } = await import('../noble-ble-handler');
70
+
71
+ expect(
72
+ createNobleBleIpcErrorResponse({
73
+ name: 'HardwareError',
74
+ message: 'Bluetooth pairing information is no longer valid',
75
+ errorCode: HardwareErrorCode.BleBondInvalid,
76
+ params: { nativeErrorMessage: 'native message' },
77
+ })
78
+ ).toEqual({
79
+ type: 'NobleBleIpcError',
80
+ success: false,
81
+ error: {
82
+ name: 'HardwareError',
83
+ message: 'Bluetooth pairing information is no longer valid',
84
+ errorCode: HardwareErrorCode.BleBondInvalid,
85
+ params: { nativeErrorMessage: 'native message' },
86
+ },
87
+ });
88
+
89
+ expect(createNobleBleIpcErrorResponse(new Error('untyped failure'))).toEqual({
90
+ type: 'NobleBleIpcError',
91
+ success: false,
92
+ error: {
93
+ name: 'Error',
94
+ message: 'untyped failure',
95
+ errorCode: HardwareErrorCode.UnknownError,
96
+ },
97
+ });
98
+
99
+ const circularParams: { self?: unknown } = {};
100
+ circularParams.self = circularParams;
101
+ expect(
102
+ createNobleBleIpcErrorResponse({
103
+ message: 'failure with unsafe params',
104
+ errorCode: HardwareErrorCode.BleConnectedError,
105
+ params: circularParams,
106
+ })
107
+ ).toEqual({
108
+ type: 'NobleBleIpcError',
109
+ success: false,
110
+ error: {
111
+ name: 'Error',
112
+ message: 'failure with unsafe params',
113
+ errorCode: HardwareErrorCode.BleConnectedError,
114
+ },
115
+ });
116
+ });
117
+
65
118
  test('keeps safe pacing by default and allows an explicit high-throughput bypass', async () => {
66
119
  const { resolveNobleBleWritePacingDelay } = await import('../noble-ble-handler');
67
120
 
@@ -207,7 +260,15 @@ describe('Electron Noble BLE device discovery', () => {
207
260
  expect(peripheral.connect).not.toHaveBeenCalled();
208
261
 
209
262
  stopScanningCallback?.();
210
- await expect(connectPromise).rejects.toThrow('expected test connection failure');
263
+ await expect(connectPromise).resolves.toEqual({
264
+ type: 'NobleBleIpcError',
265
+ success: false,
266
+ error: {
267
+ name: 'HardwareError',
268
+ message: 'expected test connection failure',
269
+ errorCode: HardwareErrorCode.BleConnectedError,
270
+ },
271
+ });
211
272
  expect(peripheral.connect).toHaveBeenCalledTimes(1);
212
273
  });
213
274
 
@@ -232,7 +293,7 @@ describe('Electron Noble BLE device discovery', () => {
232
293
 
233
294
  peripheral.connect(connectCallback);
234
295
  const nativeError = new Error(
235
- 'CBATTErrorDomain:14 Peer removed pairing information on the device side'
296
+ 'CBErrorDomain:14 Peer removed pairing information on the device side'
236
297
  );
237
298
  bindings.emit('disconnect', 'aabbccdd', nativeError);
238
299
 
package/src/index.ts CHANGED
@@ -3,7 +3,13 @@ import type { WebContents } from 'electron';
3
3
  export * from './types';
4
4
 
5
5
  // Export Desktop API types for other packages to reuse
6
- export type { DesktopAPI, NobleBleAPI, NobleBleWriteOptions } from './types/desktop-api';
6
+ export type {
7
+ DesktopAPI,
8
+ NobleBleAPI,
9
+ NobleBleIpcErrorPayload,
10
+ NobleBleIpcErrorResponse,
11
+ NobleBleWriteOptions,
12
+ } from './types/desktop-api';
7
13
 
8
14
  export async function initNobleBleSupport(webContents: WebContents) {
9
15
  const { setupNobleBleHandlers } = await import('./noble-ble-handler');
@@ -16,7 +16,6 @@ import {
16
16
  ONEKEY_WRITE_CHARACTERISTIC_UUID,
17
17
  createKnownBleUuidAliases,
18
18
  hasOnekeyCommunicationService,
19
- isBleStaleBondErrorText,
20
19
  isBleStaleBondHardwareError,
21
20
  isOnekeyBluetoothDevice,
22
21
  isPro2FamilyBleName,
@@ -35,16 +34,23 @@ import {
35
34
 
36
35
  import type { IpcMain, IpcMainInvokeEvent, WebContents } from 'electron';
37
36
  import type { Characteristic, Peripheral, Service } from '@stoprocent/noble';
38
- import type { NobleBleWriteOptions } from './types/desktop-api';
37
+ import type { NobleBleIpcErrorResponse, NobleBleWriteOptions } from './types/desktop-api';
39
38
  import type { CharacteristicPair, DeviceInfo, Logger, NobleModule } from './types/noble-extended';
40
39
 
41
40
  // Noble will be dynamically imported to avoid bundling issues
42
41
  let noble: NobleModule | null = null;
43
42
  let logger: Logger | null = null;
44
43
 
45
- export function createNobleBleConnectionError(error: Error, messagePrefix = '') {
44
+ type NobleBleNativeError = Error & {
45
+ nativeErrorCode?: number;
46
+ nativeErrorDomain?: string;
47
+ };
48
+
49
+ export function createNobleBleConnectionError(error: NobleBleNativeError, messagePrefix = '') {
46
50
  const errorMessage = error.message;
47
- if (isBleStaleBondErrorText(errorMessage)) {
51
+ const isInvalidMacOsBond =
52
+ error.nativeErrorCode === 14 && error.nativeErrorDomain === 'CBErrorDomain';
53
+ if (isInvalidMacOsBond) {
48
54
  const nativeErrorMessage = `${messagePrefix}${errorMessage}`;
49
55
  return ERRORS.TypedError(
50
56
  HardwareErrorCode.BleBondInvalid,
@@ -58,6 +64,40 @@ export function createNobleBleConnectionError(error: Error, messagePrefix = '')
58
64
  return ERRORS.TypedError(HardwareErrorCode.BleConnectedError, `${messagePrefix}${errorMessage}`);
59
65
  }
60
66
 
67
+ export function createNobleBleIpcErrorResponse(error: unknown): NobleBleIpcErrorResponse {
68
+ const candidate = error as {
69
+ errorCode?: unknown;
70
+ message?: unknown;
71
+ name?: unknown;
72
+ params?: unknown;
73
+ };
74
+ const errorCode =
75
+ typeof candidate?.errorCode === 'number' ? candidate.errorCode : HardwareErrorCode.UnknownError;
76
+ const message =
77
+ typeof candidate?.message === 'string' ? candidate.message : String(error ?? 'Unknown error');
78
+ const name = typeof candidate?.name === 'string' ? candidate.name : 'Error';
79
+ let params: unknown;
80
+ if (candidate?.params !== undefined) {
81
+ try {
82
+ const serializedParams = JSON.stringify(candidate.params);
83
+ params = serializedParams === undefined ? undefined : JSON.parse(serializedParams);
84
+ } catch {
85
+ params = undefined;
86
+ }
87
+ }
88
+
89
+ return {
90
+ type: 'NobleBleIpcError',
91
+ success: false,
92
+ error: {
93
+ name,
94
+ message,
95
+ errorCode,
96
+ ...(params !== undefined ? { params } : {}),
97
+ },
98
+ };
99
+ }
100
+
61
101
  // Bluetooth state management
62
102
  const bluetoothState: {
63
103
  available: boolean;
@@ -1919,7 +1959,13 @@ export function setupNobleBleHandlers(webContents: WebContents): void {
1919
1959
  // Electron throws on duplicate channels and setup re-runs on soft restart.
1920
1960
  const handle: IpcMain['handle'] = (channel, listener) => {
1921
1961
  ipcMain.removeHandler(channel);
1922
- ipcMain.handle(channel, listener);
1962
+ ipcMain.handle(channel, async (...args) => {
1963
+ try {
1964
+ return await Promise.resolve(listener(...args));
1965
+ } catch (error) {
1966
+ return createNobleBleIpcErrorResponse(error);
1967
+ }
1968
+ });
1923
1969
  };
1924
1970
 
1925
1971
  safeLog(logger, 'info', 'Setting up Noble BLE IPC handlers');
@@ -10,6 +10,19 @@ export interface NobleBleWriteOptions {
10
10
  pacingDelayMs?: number;
11
11
  }
12
12
 
13
+ export type NobleBleIpcErrorPayload = {
14
+ name: string;
15
+ message: string;
16
+ errorCode: number;
17
+ params?: unknown;
18
+ };
19
+
20
+ export type NobleBleIpcErrorResponse = {
21
+ type: 'NobleBleIpcError';
22
+ success: false;
23
+ error: NobleBleIpcErrorPayload;
24
+ };
25
+
13
26
  export interface NobleBleAPI {
14
27
  enumerate: () => Promise<{ id: string; name: string }[]>;
15
28
  getDevice: (uuid: string) => Promise<{ id: string; name: string; mtu?: number } | null>;