@onekeyfe/hd-transport-electron 1.2.8-alpha.4 → 1.2.8-alpha.5

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.
@@ -55,13 +55,13 @@ function invokeNobleBleIpc(request) {
55
55
 
56
56
  function initNobleBleSupport(webContents) {
57
57
  return __awaiter(this, void 0, void 0, function* () {
58
- const { setupNobleBleHandlers } = yield Promise.resolve().then(function () { return require('./noble-ble-handler-d0991fa0.js'); });
58
+ const { setupNobleBleHandlers } = yield Promise.resolve().then(function () { return require('./noble-ble-handler-804f0a41.js'); });
59
59
  setupNobleBleHandlers(webContents);
60
60
  });
61
61
  }
62
62
  function disposeNobleBleSupport(releaseNoble) {
63
63
  return __awaiter(this, void 0, void 0, function* () {
64
- const handler = yield Promise.resolve().then(function () { return require('./noble-ble-handler-d0991fa0.js'); });
64
+ const handler = yield Promise.resolve().then(function () { return require('./noble-ble-handler-804f0a41.js'); });
65
65
  yield handler.disposeNobleBleSupport(releaseNoble);
66
66
  });
67
67
  }
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-e2e3d2ba.js');
5
+ var index = require('./index-2231e562.js');
6
6
 
7
7
 
8
8
 
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./index-e2e3d2ba.js');
3
+ var index = require('./index-2231e562.js');
4
4
  var hdShared = require('@onekeyfe/hd-shared');
5
5
  var pRetry = require('p-retry');
6
6
 
@@ -123,13 +123,6 @@ function assertBleActive() {
123
123
  }
124
124
  function createNobleBleConnectionError(error, messagePrefix = '') {
125
125
  const errorMessage = error.message;
126
- const isInvalidMacOsBond = error.nativeErrorCode === 14 && error.nativeErrorDomain === 'CBErrorDomain';
127
- if (isInvalidMacOsBond) {
128
- const nativeErrorMessage = `${messagePrefix}${errorMessage}`;
129
- return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleBondInvalid, `${hdShared.HardwareErrorCodeMessage[hdShared.HardwareErrorCode.BleBondInvalid]} (${nativeErrorMessage})`, {
130
- nativeErrorMessage,
131
- });
132
- }
133
126
  return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleConnectedError, `${messagePrefix}${errorMessage}`);
134
127
  }
135
128
  function createNobleBleIpcErrorResponse(error) {
@@ -383,8 +376,18 @@ function updateBluetoothState(state) {
383
376
  function initializeNoble() {
384
377
  return index.__awaiter(this, void 0, void 0, function* () {
385
378
  assertBleActive();
386
- if (noble)
379
+ if (noble) {
380
+ if (noble.state === 'poweredOff') {
381
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BlePoweredOff);
382
+ }
383
+ if (noble.state === 'unsupported') {
384
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleUnsupported);
385
+ }
386
+ if (noble.state === 'unauthorized') {
387
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleLocationError);
388
+ }
387
389
  return;
390
+ }
388
391
  try {
389
392
  noble = require('@stoprocent/noble');
390
393
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Noble library loaded');
@@ -425,7 +428,7 @@ function initializeNoble() {
425
428
  }
426
429
  else if (state === 'unauthorized') {
427
430
  cleanup();
428
- reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BlePermissionError));
431
+ reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.BleLocationError));
429
432
  }
430
433
  };
431
434
  const cancel = () => {
@@ -434,6 +437,7 @@ function initializeNoble() {
434
437
  };
435
438
  pendingCancellations.add(cancel);
436
439
  noble.on('stateChange', onStateChange);
440
+ onStateChange(noble.state);
437
441
  });
438
442
  assertBleActive();
439
443
  if (!persistentDiscoverListener) {
@@ -446,7 +450,7 @@ function initializeNoble() {
446
450
  }
447
451
  catch (error) {
448
452
  logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Failed to initialize Noble:', error);
449
- bluetoothState.unsupported = true;
453
+ bluetoothState.unsupported = (noble === null || noble === void 0 ? void 0 : noble.state) === 'unsupported';
450
454
  bluetoothState.initialized = true;
451
455
  throw error;
452
456
  }
@@ -875,9 +879,7 @@ function performTargetedScan(targetDeviceId, timeoutMs = NOBLE_BLE_TARGETED_SCAN
875
879
  }
876
880
  function enumerateDevices(isWindowDestroyed) {
877
881
  return index.__awaiter(this, void 0, void 0, function* () {
878
- if (!noble) {
879
- yield initializeNoble();
880
- }
882
+ yield initializeNoble();
881
883
  if (!noble) {
882
884
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Noble not available');
883
885
  }
@@ -1176,8 +1178,7 @@ function setupConnectionAndDiscoverServices(peripheral, deviceId, webContents) {
1176
1178
  yield forceReconnectPeripheral(peripheral, deviceId);
1177
1179
  }
1178
1180
  catch (resetError) {
1179
- if (hdShared.isBleStaleBondHardwareError(resetError) ||
1180
- (resetError === null || resetError === void 0 ? void 0 : resetError.errorCode) === hdShared.HardwareErrorCode.BleDeviceDisconnected) {
1181
+ if ((resetError === null || resetError === void 0 ? void 0 : resetError.errorCode) === hdShared.HardwareErrorCode.BleDeviceDisconnected) {
1181
1182
  throw resetError;
1182
1183
  }
1183
1184
  logger === null || logger === void 0 ? void 0 : logger.error('[NobleBLE] Connection reset before discovery failed, continuing', resetError);
@@ -1275,6 +1276,7 @@ function tryDirectConnectById(deviceId) {
1275
1276
  function connectDevice(deviceId, webContents) {
1276
1277
  var _a, _b, _c;
1277
1278
  return index.__awaiter(this, void 0, void 0, function* () {
1279
+ yield initializeNoble();
1278
1280
  logger === null || logger === void 0 ? void 0 : logger.info('[NobleBLE] Connect device request:', {
1279
1281
  deviceId,
1280
1282
  hasDiscovered: discoveredDevices.has(deviceId),
@@ -1288,9 +1290,6 @@ function connectDevice(deviceId, webContents) {
1288
1290
  peripheral = undefined;
1289
1291
  }
1290
1292
  if (!peripheral) {
1291
- if (!noble) {
1292
- yield initializeNoble();
1293
- }
1294
1293
  if (!noble) {
1295
1294
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Noble not available');
1296
1295
  }
@@ -1313,29 +1312,17 @@ function connectDevice(deviceId, webContents) {
1313
1312
  return undefined;
1314
1313
  }
1315
1314
  });
1316
- let staleBondError;
1317
1315
  const connectById = () => index.__awaiter(this, void 0, void 0, function* () {
1318
- try {
1319
- const found = yield tryDirectConnectById(deviceId);
1320
- if (found) {
1321
- discoveredDevices.set(deviceId, found);
1322
- }
1323
- return found;
1324
- }
1325
- catch (error) {
1326
- if (error.errorCode !== hdShared.HardwareErrorCode.BleBondInvalid) {
1327
- throw error;
1328
- }
1329
- staleBondError = error;
1330
- return undefined;
1316
+ const found = yield tryDirectConnectById(deviceId);
1317
+ if (found) {
1318
+ discoveredDevices.set(deviceId, found);
1331
1319
  }
1320
+ return found;
1332
1321
  });
1333
1322
  peripheral = byIdFirst ? yield connectById() : yield scanForPeripheral();
1334
1323
  if (!peripheral) {
1335
1324
  peripheral = byIdFirst ? yield scanForPeripheral() : yield connectById();
1336
1325
  }
1337
- if (!peripheral && staleBondError)
1338
- throw staleBondError;
1339
1326
  }
1340
1327
  assertBleActive();
1341
1328
  if (!peripheral) {
@@ -1 +1 @@
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;AAiC1F,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+KD,wBAAgB,+BAA+B,CAAC,OAAO,CAAC,EAAE,oBAAoB,UAI7E;AA2zDD,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,CAkOpE;AAOD,wBAAgB,sBAAsB,CACpC,YAAY,GAAE,CAAC,QAAQ,EAAE;IAAE,IAAI,IAAI,IAAI,CAAA;CAAE,KAAK,IAAkC,GAC/E,OAAO,CAAC,IAAI,CAAC,CAiEf"}
1
+ {"version":3,"file":"noble-ble-handler.d.ts","sourceRoot":"","sources":["../src/noble-ble-handler.ts"],"names":[],"mappings":"AAiCA,OAAO,KAAK,EAA+B,WAAW,EAAE,MAAM,UAAU,CAAC;AAEzE,OAAO,KAAK,EAAE,wBAAwB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAiC1F,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,+CAG3F;AAED,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,OAAO,GAAG,wBAAwB,CAgCvF;AA+KD,wBAAgB,+BAA+B,CAAC,OAAO,CAAC,EAAE,oBAAoB,UAI7E;AAszDD,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,CAkOpE;AAOD,wBAAgB,sBAAsB,CACpC,YAAY,GAAE,CAAC,QAAQ,EAAE;IAAE,IAAI,IAAI,IAAI,CAAA;CAAE,KAAK,IAAkC,GAC/E,OAAO,CAAC,IAAI,CAAC,CAiEf"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-transport-electron",
3
- "version": "1.2.8-alpha.4",
3
+ "version": "1.2.8-alpha.5",
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.8-alpha.4",
29
- "@onekeyfe/hd-shared": "1.2.8-alpha.4",
30
- "@onekeyfe/hd-transport": "1.2.8-alpha.4",
28
+ "@onekeyfe/hd-core": "1.2.8-alpha.5",
29
+ "@onekeyfe/hd-shared": "1.2.8-alpha.5",
30
+ "@onekeyfe/hd-transport": "1.2.8-alpha.5",
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": "82fcab32fa1a37d1b82f73da825e39360624e0c9"
39
+ "gitHead": "c1f24dc0a59d0c02fe05594a9c4ae7652ec9cc32"
40
40
  }
@@ -31,22 +31,17 @@ describe('Electron Noble BLE device discovery', () => {
31
31
  expect(NOBLE_BLE_SUBSCRIBE_TIMEOUT_MS).toBe(10_000);
32
32
  });
33
33
 
34
- test('maps structured macOS stale pairing failures without parsing localized text', async () => {
34
+ test('maps native macOS connection failures to the generic connection error', async () => {
35
35
  const { createNobleBleConnectionError } = await import('../noble-ble-handler');
36
36
 
37
- const staleBondError = createNobleBleConnectionError(
38
- Object.assign(new Error('Peer removed pairing information on the device side'), {
39
- nativeErrorCode: 14,
40
- nativeErrorDomain: 'CBErrorDomain',
41
- })
42
- );
43
- expect(staleBondError).toMatchObject({
44
- errorCode: HardwareErrorCode.BleBondInvalid,
45
- params: {
46
- nativeErrorMessage: 'Peer removed pairing information on the device side',
47
- },
48
- });
49
- expect(staleBondError.message).toContain('Peer removed pairing information on the device side');
37
+ expect(
38
+ createNobleBleConnectionError(
39
+ Object.assign(new Error('Peer removed pairing information on the device side'), {
40
+ nativeErrorCode: 14,
41
+ nativeErrorDomain: 'CBErrorDomain',
42
+ })
43
+ )
44
+ ).toMatchObject({ errorCode: HardwareErrorCode.BleConnectedError });
50
45
  expect(createNobleBleConnectionError(new Error('Encryption is insufficient'))).toMatchObject({
51
46
  errorCode: HardwareErrorCode.BleConnectedError,
52
47
  });
@@ -81,18 +76,16 @@ describe('Electron Noble BLE device discovery', () => {
81
76
  expect(
82
77
  createNobleBleIpcErrorResponse({
83
78
  name: 'HardwareError',
84
- message: 'Bluetooth pairing information is no longer valid',
85
- errorCode: HardwareErrorCode.BleBondInvalid,
86
- params: { nativeErrorMessage: 'native message' },
79
+ message: 'Bluetooth connection failed',
80
+ errorCode: HardwareErrorCode.BleConnectedError,
87
81
  })
88
82
  ).toEqual({
89
83
  type: 'NobleBleIpcError',
90
84
  success: false,
91
85
  error: {
92
86
  name: 'HardwareError',
93
- message: 'Bluetooth pairing information is no longer valid',
94
- errorCode: HardwareErrorCode.BleBondInvalid,
95
- params: { nativeErrorMessage: 'native message' },
87
+ message: 'Bluetooth connection failed',
88
+ errorCode: HardwareErrorCode.BleConnectedError,
96
89
  },
97
90
  });
98
91
 
@@ -135,14 +128,14 @@ describe('Electron Noble BLE device discovery', () => {
135
128
  success: false as const,
136
129
  error: {
137
130
  name: 'HardwareError',
138
- message: 'Bluetooth pairing information is no longer valid',
139
- errorCode: HardwareErrorCode.BleBondInvalid,
131
+ message: 'Bluetooth connection failed',
132
+ errorCode: HardwareErrorCode.BleConnectedError,
140
133
  },
141
134
  })
142
135
  )
143
136
  ).rejects.toMatchObject({
144
137
  name: 'HardwareError',
145
- errorCode: HardwareErrorCode.BleBondInvalid,
138
+ errorCode: HardwareErrorCode.BleConnectedError,
146
139
  });
147
140
 
148
141
  await expect(invokeNobleBleIpc(Promise.resolve('connected'))).resolves.toBe('connected');
@@ -352,87 +345,6 @@ describe('Electron Noble BLE device discovery', () => {
352
345
  }
353
346
  );
354
347
 
355
- test.each([
356
- { rediscovered: false, expectedCode: HardwareErrorCode.BleBondInvalid },
357
- { rediscovered: true, expectedCode: HardwareErrorCode.BleConnectedError },
358
- ])(
359
- 'reports a macOS stale bond only when fallback scan misses (rediscovered=$rediscovered)',
360
- async ({ rediscovered, expectedCode }) => {
361
- jest.useFakeTimers({ doNotFake: ['performance'] });
362
- const handlers = new Map<string, IpcHandler>();
363
- const stalePeripheral = createPeripheral('device', 'Pro2 A1B2');
364
- const freshPeripheral = Object.assign(
365
- new EventEmitter(),
366
- createPeripheral('device', 'Pro2 A1B2'),
367
- {
368
- connect: jest.fn((callback: (error?: Error) => void) => {
369
- callback(new Error('fresh connection failed'));
370
- }),
371
- }
372
- );
373
- const staleBondError = Object.assign(new Error('Peer removed pairing information'), {
374
- nativeErrorCode: 14,
375
- nativeErrorDomain: 'CBErrorDomain',
376
- });
377
- let scanStarted: (() => void) | undefined;
378
- const scanning = new Promise<void>(resolve => {
379
- scanStarted = resolve;
380
- });
381
- const noble = Object.assign(new EventEmitter(), {
382
- state: 'poweredOn',
383
- startScanning: jest.fn((_services, _duplicates, callback) => {
384
- callback?.();
385
- if (rediscovered) noble.emit('discover', freshPeripheral);
386
- scanStarted?.();
387
- }),
388
- stopScanning: jest.fn(callback => callback?.()),
389
- connectAsync: jest.fn(() => Promise.reject(staleBondError)),
390
- });
391
- jest.doMock('@stoprocent/noble', () => noble);
392
- jest.doMock('electron', () => ({
393
- ipcMain: {
394
- handle: (channel: string, handler: IpcHandler) => handlers.set(channel, handler),
395
- removeHandler: (channel: string) => handlers.delete(channel),
396
- },
397
- }));
398
- jest.doMock('electron-log', () => ({
399
- info: jest.fn(),
400
- debug: jest.fn(),
401
- error: jest.fn(),
402
- }));
403
- const { setupNobleBleHandlers } = await import('../noble-ble-handler');
404
- setupNobleBleHandlers({ on: jest.fn(), send: jest.fn() } as unknown as WebContents);
405
- const availability = handlers.get(EOneKeyBleMessageKeys.BLE_AVAILABILITY_CHECK);
406
- const connect = handlers.get(EOneKeyBleMessageKeys.NOBLE_BLE_CONNECT);
407
- if (!availability || !connect) throw new Error('Noble handlers were not registered');
408
- await availability();
409
- noble.emit('discover', stalePeripheral);
410
-
411
- const connecting = Promise.resolve(connect(undefined, stalePeripheral.id));
412
- await scanning;
413
- jest.advanceTimersByTime(1500);
414
- await expect(connecting).resolves.toMatchObject({
415
- success: false,
416
- error: { errorCode: expectedCode },
417
- });
418
- if (!rediscovered) {
419
- const secondScan = new Promise<void>(resolve => {
420
- scanStarted = resolve;
421
- });
422
- const retry = Promise.resolve(connect(undefined, stalePeripheral.id));
423
- await secondScan;
424
- jest.advanceTimersByTime(1500);
425
- await expect(retry).resolves.toMatchObject({
426
- success: false,
427
- error: { errorCode: HardwareErrorCode.BleBondInvalid },
428
- });
429
- }
430
- expect(noble.connectAsync).toHaveBeenCalledTimes(rediscovered ? 1 : 2);
431
- expect(noble.startScanning).toHaveBeenCalledTimes(rediscovered ? 1 : 2);
432
- expect(freshPeripheral.connect).toHaveBeenCalledTimes(rediscovered ? 1 : 0);
433
- }
434
- );
435
-
436
348
  test('waits for a targeted scan to stop before connecting', async () => {
437
349
  jest.useFakeTimers({ doNotFake: ['performance'] });
438
350
 
@@ -1006,6 +918,59 @@ describe('Noble BLE process shutdown', () => {
1006
918
  expect(handlers.size).toBe(0);
1007
919
  });
1008
920
 
921
+ test.each([
922
+ ['poweredOff', HardwareErrorCode.BlePoweredOff],
923
+ ['unauthorized', HardwareErrorCode.BleLocationError],
924
+ ['unsupported', HardwareErrorCode.BleUnsupported],
925
+ ])(
926
+ 'preserves adapter state %s across repeated scans and connections',
927
+ async (state, errorCode) => {
928
+ jest.useFakeTimers({ doNotFake: ['performance'] });
929
+ const { sdk, native, handlers } = await setup(state);
930
+ for (let attempt = 0; attempt < 2; attempt += 1) {
931
+ for (const channel of [
932
+ EOneKeyBleMessageKeys.NOBLE_BLE_ENUMERATE,
933
+ EOneKeyBleMessageKeys.NOBLE_BLE_CONNECT,
934
+ ]) {
935
+ await expect(handlers.get(channel)?.({}, 'device')).resolves.toMatchObject({
936
+ success: false,
937
+ error: { errorCode },
938
+ });
939
+ }
940
+ }
941
+ expect(native.startScanning).not.toHaveBeenCalled();
942
+ expect(native.listenerCount('stateChange')).toBe(1);
943
+ expect(jest.getTimerCount()).toBe(0);
944
+ await sdk.disposeNobleBleSupport();
945
+ }
946
+ );
947
+
948
+ test.each([
949
+ ['poweredOff', HardwareErrorCode.BlePoweredOff],
950
+ ['unauthorized', HardwareErrorCode.BleLocationError],
951
+ ['unsupported', HardwareErrorCode.BleUnsupported],
952
+ ])('checks adapter state %s again after initialization', async (state, errorCode) => {
953
+ const { sdk, native, handlers } = await setup();
954
+ const checkAvailability = handlers.get(EOneKeyBleMessageKeys.BLE_AVAILABILITY_CHECK);
955
+ await expect(checkAvailability?.({})).resolves.toMatchObject({ available: true });
956
+ native.state = state;
957
+ native.emit('stateChange', state);
958
+ for (const channel of [
959
+ EOneKeyBleMessageKeys.NOBLE_BLE_ENUMERATE,
960
+ EOneKeyBleMessageKeys.NOBLE_BLE_CONNECT,
961
+ ]) {
962
+ await expect(handlers.get(channel)?.({}, 'device')).resolves.toMatchObject({
963
+ success: false,
964
+ error: { errorCode },
965
+ });
966
+ }
967
+ expect(native.startScanning).not.toHaveBeenCalled();
968
+ native.state = 'poweredOn';
969
+ native.emit('stateChange', native.state);
970
+ await expect(checkAvailability?.({})).resolves.toMatchObject({ available: true });
971
+ await sdk.disposeNobleBleSupport();
972
+ });
973
+
1009
974
  test('cancels an active scan, ignores its late callback and releases native once', async () => {
1010
975
  jest.useFakeTimers({ doNotFake: ['performance'] });
1011
976
  const { sdk, native, handlers } = await setup();
@@ -16,7 +16,6 @@ import {
16
16
  ONEKEY_WRITE_CHARACTERISTIC_UUID,
17
17
  createKnownBleUuidAliases,
18
18
  hasOnekeyCommunicationService,
19
- isBleStaleBondHardwareError,
20
19
  isOnekeyBluetoothDevice,
21
20
  isPro2FamilyBleName,
22
21
  matchesKnownBleUuid,
@@ -74,19 +73,6 @@ type NobleBleNativeError = Error & {
74
73
 
75
74
  export function createNobleBleConnectionError(error: NobleBleNativeError, messagePrefix = '') {
76
75
  const errorMessage = error.message;
77
- const isInvalidMacOsBond =
78
- error.nativeErrorCode === 14 && error.nativeErrorDomain === 'CBErrorDomain';
79
- if (isInvalidMacOsBond) {
80
- const nativeErrorMessage = `${messagePrefix}${errorMessage}`;
81
- return ERRORS.TypedError(
82
- HardwareErrorCode.BleBondInvalid,
83
- `${HardwareErrorCodeMessage[HardwareErrorCode.BleBondInvalid]} (${nativeErrorMessage})`,
84
- {
85
- nativeErrorMessage,
86
- }
87
- );
88
- }
89
-
90
76
  return ERRORS.TypedError(HardwareErrorCode.BleConnectedError, `${messagePrefix}${errorMessage}`);
91
77
  }
92
78
 
@@ -443,7 +429,18 @@ function updateBluetoothState(state: string): void {
443
429
  // Initialize Noble
444
430
  async function initializeNoble(): Promise<void> {
445
431
  assertBleActive();
446
- if (noble) return;
432
+ if (noble) {
433
+ if (noble.state === 'poweredOff') {
434
+ throw ERRORS.TypedError(HardwareErrorCode.BlePoweredOff);
435
+ }
436
+ if (noble.state === 'unsupported') {
437
+ throw ERRORS.TypedError(HardwareErrorCode.BleUnsupported);
438
+ }
439
+ if (noble.state === 'unauthorized') {
440
+ throw ERRORS.TypedError(HardwareErrorCode.BleLocationError);
441
+ }
442
+ return;
443
+ }
447
444
 
448
445
  try {
449
446
  // eslint-disable-next-line @typescript-eslint/no-var-requires, global-require
@@ -496,7 +493,7 @@ async function initializeNoble(): Promise<void> {
496
493
  reject(ERRORS.TypedError(HardwareErrorCode.BlePoweredOff));
497
494
  } else if (state === 'unauthorized') {
498
495
  cleanup();
499
- reject(ERRORS.TypedError(HardwareErrorCode.BlePermissionError));
496
+ reject(ERRORS.TypedError(HardwareErrorCode.BleLocationError));
500
497
  }
501
498
  };
502
499
 
@@ -508,6 +505,7 @@ async function initializeNoble(): Promise<void> {
508
505
  };
509
506
  pendingCancellations.add(cancel);
510
507
  noble.on('stateChange', onStateChange);
508
+ onStateChange(noble.state);
511
509
  });
512
510
 
513
511
  assertBleActive();
@@ -522,7 +520,7 @@ async function initializeNoble(): Promise<void> {
522
520
  logger?.info('[NobleBLE] Noble initialized successfully');
523
521
  } catch (error) {
524
522
  logger?.error('[NobleBLE] Failed to initialize Noble:', error);
525
- bluetoothState.unsupported = true;
523
+ bluetoothState.unsupported = noble?.state === 'unsupported';
526
524
  bluetoothState.initialized = true;
527
525
  throw error;
528
526
  }
@@ -1151,9 +1149,7 @@ async function performTargetedScan(
1151
1149
 
1152
1150
  // Enumerate devices
1153
1151
  async function enumerateDevices(isWindowDestroyed: () => boolean): Promise<DeviceInfo[]> {
1154
- if (!noble) {
1155
- await initializeNoble();
1156
- }
1152
+ await initializeNoble();
1157
1153
 
1158
1154
  if (!noble) {
1159
1155
  throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'Noble not available');
@@ -1634,7 +1630,6 @@ async function setupConnectionAndDiscoverServices(
1634
1630
  await forceReconnectPeripheral(peripheral, deviceId);
1635
1631
  } catch (resetError) {
1636
1632
  if (
1637
- isBleStaleBondHardwareError(resetError) ||
1638
1633
  (resetError as { errorCode?: unknown })?.errorCode === HardwareErrorCode.BleDeviceDisconnected
1639
1634
  ) {
1640
1635
  throw resetError;
@@ -1759,6 +1754,7 @@ async function tryDirectConnectById(deviceId: string): Promise<Peripheral | unde
1759
1754
 
1760
1755
  // Connect to device - supports both discovered and direct connection modes
1761
1756
  async function connectDevice(deviceId: string, webContents: WebContents): Promise<void> {
1757
+ await initializeNoble();
1762
1758
  logger?.info('[NobleBLE] Connect device request:', {
1763
1759
  deviceId,
1764
1760
  hasDiscovered: discoveredDevices.has(deviceId),
@@ -1776,11 +1772,6 @@ async function connectDevice(deviceId: string, webContents: WebContents): Promis
1776
1772
  }
1777
1773
 
1778
1774
  if (!peripheral) {
1779
- // Initialize Noble if not already done
1780
- if (!noble) {
1781
- await initializeNoble();
1782
- }
1783
-
1784
1775
  if (!noble) {
1785
1776
  throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'Noble not available');
1786
1777
  }
@@ -1812,21 +1803,12 @@ async function connectDevice(deviceId: string, webContents: WebContents): Promis
1812
1803
  }
1813
1804
  };
1814
1805
 
1815
- let staleBondError: Error | undefined;
1816
1806
  const connectById = async () => {
1817
- try {
1818
- const found = await tryDirectConnectById(deviceId);
1819
- if (found) {
1820
- discoveredDevices.set(deviceId, found);
1821
- }
1822
- return found;
1823
- } catch (error) {
1824
- if ((error as { errorCode?: number }).errorCode !== HardwareErrorCode.BleBondInvalid) {
1825
- throw error;
1826
- }
1827
- staleBondError = error as Error;
1828
- return undefined;
1807
+ const found = await tryDirectConnectById(deviceId);
1808
+ if (found) {
1809
+ discoveredDevices.set(deviceId, found);
1829
1810
  }
1811
+ return found;
1830
1812
  };
1831
1813
 
1832
1814
  peripheral = byIdFirst ? await connectById() : await scanForPeripheral();
@@ -1835,7 +1817,6 @@ async function connectDevice(deviceId: string, webContents: WebContents): Promis
1835
1817
  // silent), or not reachable by id. Try the other one before giving up.
1836
1818
  peripheral = byIdFirst ? await scanForPeripheral() : await connectById();
1837
1819
  }
1838
- if (!peripheral && staleBondError) throw staleBondError;
1839
1820
  }
1840
1821
 
1841
1822
  assertBleActive();