@onekeyfe/hwk-desktop-noble-ble 1.2.6-alpha.0 → 1.2.8-alpha.101

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.
@@ -27,6 +27,7 @@ var THIRD_PARTY_BLE_SCAN_DURATION_MS = 5e3;
27
27
  var THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS = 1e4;
28
28
  var THIRD_PARTY_BLE_DEVICE_TTL_MS = 5e3;
29
29
  var THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS = 1e4;
30
+ var THIRD_PARTY_BLE_STATE_SETTLE_TIMEOUT_MS = 3e3;
30
31
 
31
32
  export {
32
33
  __require,
@@ -34,6 +35,7 @@ export {
34
35
  THIRD_PARTY_BLE_SCAN_DURATION_MS,
35
36
  THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS,
36
37
  THIRD_PARTY_BLE_DEVICE_TTL_MS,
37
- THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS
38
+ THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS,
39
+ THIRD_PARTY_BLE_STATE_SETTLE_TIMEOUT_MS
38
40
  };
39
- //# sourceMappingURL=chunk-A5AENDC2.mjs.map
41
+ //# sourceMappingURL=chunk-ILABAQJY.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/constants.ts"],"sourcesContent":["/**\n * Renderer/main IPC channels shared by every third-party BLE vendor; prefixed to\n * avoid clashing with the OneKey handler (`$onekey-noble-ble-*`) in one app.\n */\nexport const THIRD_PARTY_BLE_CHANNELS = {\n scan: '$onekey-3p-ble-scan',\n stopScan: '$onekey-3p-ble-stop-scan',\n connect: '$onekey-3p-ble-connect',\n disconnect: '$onekey-3p-ble-disconnect',\n write: '$onekey-3p-ble-write',\n subscribe: '$onekey-3p-ble-subscribe',\n unsubscribe: '$onekey-3p-ble-unsubscribe',\n availability: '$onekey-3p-ble-availability',\n getDevice: '$onekey-3p-ble-get-device',\n readRssi: '$onekey-3p-ble-read-rssi',\n cancelPairing: '$onekey-3p-ble-cancel-pairing',\n /** push event: main → renderer with assembled BLE notification payload */\n notification: '$onekey-3p-ble-notification',\n /** push event: main → renderer when device disconnects unexpectedly */\n disconnected: '$onekey-3p-ble-disconnected',\n} as const;\n\nexport type ThirdPartyBleChannel =\n (typeof THIRD_PARTY_BLE_CHANNELS)[keyof typeof THIRD_PARTY_BLE_CHANNELS];\n\n/** Reconnect scan window for `connect(id)`. */\nexport const THIRD_PARTY_BLE_SCAN_DURATION_MS = 5_000;\n\n/** Continuous scan auto-stops this long after the last poll (reset, not accumulated). */\nexport const THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS = 10_000;\n\n/** Drop a device from the snapshot if it hasn't re-advertised within this window. */\nexport const THIRD_PARTY_BLE_DEVICE_TTL_MS = 5_000;\n\n/** Time we'll wait for noble to reach `poweredOn` before failing init. */\nexport const THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS = 10_000;\n"],"mappings":";;;;;;;;AAIO,IAAM,2BAA2B;AAAA,EACtC,MAAM;AAAA,EACN,UAAU;AAAA,EACV,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,WAAW;AAAA,EACX,aAAa;AAAA,EACb,cAAc;AAAA,EACd,WAAW;AAAA,EACX,UAAU;AAAA,EACV,eAAe;AAAA;AAAA,EAEf,cAAc;AAAA;AAAA,EAEd,cAAc;AAChB;AAMO,IAAM,mCAAmC;AAGzC,IAAM,oCAAoC;AAG1C,IAAM,gCAAgC;AAGtC,IAAM,sCAAsC;","names":[]}
1
+ {"version":3,"sources":["../src/constants.ts"],"sourcesContent":["/**\n * Renderer/main IPC channels shared by every third-party BLE vendor; prefixed to\n * avoid clashing with the OneKey handler (`$onekey-noble-ble-*`) in one app.\n */\nexport const THIRD_PARTY_BLE_CHANNELS = {\n scan: '$onekey-3p-ble-scan',\n stopScan: '$onekey-3p-ble-stop-scan',\n connect: '$onekey-3p-ble-connect',\n disconnect: '$onekey-3p-ble-disconnect',\n write: '$onekey-3p-ble-write',\n subscribe: '$onekey-3p-ble-subscribe',\n unsubscribe: '$onekey-3p-ble-unsubscribe',\n availability: '$onekey-3p-ble-availability',\n getDevice: '$onekey-3p-ble-get-device',\n readRssi: '$onekey-3p-ble-read-rssi',\n cancelPairing: '$onekey-3p-ble-cancel-pairing',\n /** push event: main → renderer with assembled BLE notification payload */\n notification: '$onekey-3p-ble-notification',\n /** push event: main → renderer when device disconnects unexpectedly */\n disconnected: '$onekey-3p-ble-disconnected',\n} as const;\n\nexport type ThirdPartyBleChannel =\n (typeof THIRD_PARTY_BLE_CHANNELS)[keyof typeof THIRD_PARTY_BLE_CHANNELS];\n\n/** Reconnect scan window for `connect(id)`. */\nexport const THIRD_PARTY_BLE_SCAN_DURATION_MS = 5_000;\n\n/** Continuous scan auto-stops this long after the last poll (reset, not accumulated). */\nexport const THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS = 10_000;\n\n/** Drop a device from the snapshot if it hasn't re-advertised within this window. */\nexport const THIRD_PARTY_BLE_DEVICE_TTL_MS = 5_000;\n\n/** Time we'll wait for noble to reach `poweredOn` before failing init. */\nexport const THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS = 10_000;\n\n/** Time an availability check waits for a fresh noble to report its first state. */\nexport const THIRD_PARTY_BLE_STATE_SETTLE_TIMEOUT_MS = 3_000;\n"],"mappings":";;;;;;;;AAIO,IAAM,2BAA2B;AAAA,EACtC,MAAM;AAAA,EACN,UAAU;AAAA,EACV,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,WAAW;AAAA,EACX,aAAa;AAAA,EACb,cAAc;AAAA,EACd,WAAW;AAAA,EACX,UAAU;AAAA,EACV,eAAe;AAAA;AAAA,EAEf,cAAc;AAAA;AAAA,EAEd,cAAc;AAChB;AAMO,IAAM,mCAAmC;AAGzC,IAAM,oCAAoC;AAG1C,IAAM,gCAAgC;AAGtC,IAAM,sCAAsC;AAG5C,IAAM,0CAA0C;","names":[]}
@@ -28,5 +28,7 @@ declare const THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS = 10000;
28
28
  declare const THIRD_PARTY_BLE_DEVICE_TTL_MS = 5000;
29
29
  /** Time we'll wait for noble to reach `poweredOn` before failing init. */
30
30
  declare const THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS = 10000;
31
+ /** Time an availability check waits for a fresh noble to report its first state. */
32
+ declare const THIRD_PARTY_BLE_STATE_SETTLE_TIMEOUT_MS = 3000;
31
33
 
32
- export { THIRD_PARTY_BLE_CHANNELS, THIRD_PARTY_BLE_DEVICE_TTL_MS, THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS, THIRD_PARTY_BLE_SCAN_DURATION_MS, THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS, type ThirdPartyBleChannel };
34
+ export { THIRD_PARTY_BLE_CHANNELS, THIRD_PARTY_BLE_DEVICE_TTL_MS, THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS, THIRD_PARTY_BLE_SCAN_DURATION_MS, THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS, THIRD_PARTY_BLE_STATE_SETTLE_TIMEOUT_MS, type ThirdPartyBleChannel };
@@ -28,5 +28,7 @@ declare const THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS = 10000;
28
28
  declare const THIRD_PARTY_BLE_DEVICE_TTL_MS = 5000;
29
29
  /** Time we'll wait for noble to reach `poweredOn` before failing init. */
30
30
  declare const THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS = 10000;
31
+ /** Time an availability check waits for a fresh noble to report its first state. */
32
+ declare const THIRD_PARTY_BLE_STATE_SETTLE_TIMEOUT_MS = 3000;
31
33
 
32
- export { THIRD_PARTY_BLE_CHANNELS, THIRD_PARTY_BLE_DEVICE_TTL_MS, THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS, THIRD_PARTY_BLE_SCAN_DURATION_MS, THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS, type ThirdPartyBleChannel };
34
+ export { THIRD_PARTY_BLE_CHANNELS, THIRD_PARTY_BLE_DEVICE_TTL_MS, THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS, THIRD_PARTY_BLE_SCAN_DURATION_MS, THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS, THIRD_PARTY_BLE_STATE_SETTLE_TIMEOUT_MS, type ThirdPartyBleChannel };
package/dist/constants.js CHANGED
@@ -24,7 +24,8 @@ __export(constants_exports, {
24
24
  THIRD_PARTY_BLE_DEVICE_TTL_MS: () => THIRD_PARTY_BLE_DEVICE_TTL_MS,
25
25
  THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS: () => THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS,
26
26
  THIRD_PARTY_BLE_SCAN_DURATION_MS: () => THIRD_PARTY_BLE_SCAN_DURATION_MS,
27
- THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS: () => THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS
27
+ THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS: () => THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS,
28
+ THIRD_PARTY_BLE_STATE_SETTLE_TIMEOUT_MS: () => THIRD_PARTY_BLE_STATE_SETTLE_TIMEOUT_MS
28
29
  });
29
30
  module.exports = __toCommonJS(constants_exports);
30
31
  var THIRD_PARTY_BLE_CHANNELS = {
@@ -48,12 +49,14 @@ var THIRD_PARTY_BLE_SCAN_DURATION_MS = 5e3;
48
49
  var THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS = 1e4;
49
50
  var THIRD_PARTY_BLE_DEVICE_TTL_MS = 5e3;
50
51
  var THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS = 1e4;
52
+ var THIRD_PARTY_BLE_STATE_SETTLE_TIMEOUT_MS = 3e3;
51
53
  // Annotate the CommonJS export names for ESM import in node:
52
54
  0 && (module.exports = {
53
55
  THIRD_PARTY_BLE_CHANNELS,
54
56
  THIRD_PARTY_BLE_DEVICE_TTL_MS,
55
57
  THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS,
56
58
  THIRD_PARTY_BLE_SCAN_DURATION_MS,
57
- THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS
59
+ THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS,
60
+ THIRD_PARTY_BLE_STATE_SETTLE_TIMEOUT_MS
58
61
  });
59
62
  //# sourceMappingURL=constants.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/constants.ts"],"sourcesContent":["/**\n * Renderer/main IPC channels shared by every third-party BLE vendor; prefixed to\n * avoid clashing with the OneKey handler (`$onekey-noble-ble-*`) in one app.\n */\nexport const THIRD_PARTY_BLE_CHANNELS = {\n scan: '$onekey-3p-ble-scan',\n stopScan: '$onekey-3p-ble-stop-scan',\n connect: '$onekey-3p-ble-connect',\n disconnect: '$onekey-3p-ble-disconnect',\n write: '$onekey-3p-ble-write',\n subscribe: '$onekey-3p-ble-subscribe',\n unsubscribe: '$onekey-3p-ble-unsubscribe',\n availability: '$onekey-3p-ble-availability',\n getDevice: '$onekey-3p-ble-get-device',\n readRssi: '$onekey-3p-ble-read-rssi',\n cancelPairing: '$onekey-3p-ble-cancel-pairing',\n /** push event: main → renderer with assembled BLE notification payload */\n notification: '$onekey-3p-ble-notification',\n /** push event: main → renderer when device disconnects unexpectedly */\n disconnected: '$onekey-3p-ble-disconnected',\n} as const;\n\nexport type ThirdPartyBleChannel =\n (typeof THIRD_PARTY_BLE_CHANNELS)[keyof typeof THIRD_PARTY_BLE_CHANNELS];\n\n/** Reconnect scan window for `connect(id)`. */\nexport const THIRD_PARTY_BLE_SCAN_DURATION_MS = 5_000;\n\n/** Continuous scan auto-stops this long after the last poll (reset, not accumulated). */\nexport const THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS = 10_000;\n\n/** Drop a device from the snapshot if it hasn't re-advertised within this window. */\nexport const THIRD_PARTY_BLE_DEVICE_TTL_MS = 5_000;\n\n/** Time we'll wait for noble to reach `poweredOn` before failing init. */\nexport const THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS = 10_000;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAIO,IAAM,2BAA2B;AAAA,EACtC,MAAM;AAAA,EACN,UAAU;AAAA,EACV,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,WAAW;AAAA,EACX,aAAa;AAAA,EACb,cAAc;AAAA,EACd,WAAW;AAAA,EACX,UAAU;AAAA,EACV,eAAe;AAAA;AAAA,EAEf,cAAc;AAAA;AAAA,EAEd,cAAc;AAChB;AAMO,IAAM,mCAAmC;AAGzC,IAAM,oCAAoC;AAG1C,IAAM,gCAAgC;AAGtC,IAAM,sCAAsC;","names":[]}
1
+ {"version":3,"sources":["../src/constants.ts"],"sourcesContent":["/**\n * Renderer/main IPC channels shared by every third-party BLE vendor; prefixed to\n * avoid clashing with the OneKey handler (`$onekey-noble-ble-*`) in one app.\n */\nexport const THIRD_PARTY_BLE_CHANNELS = {\n scan: '$onekey-3p-ble-scan',\n stopScan: '$onekey-3p-ble-stop-scan',\n connect: '$onekey-3p-ble-connect',\n disconnect: '$onekey-3p-ble-disconnect',\n write: '$onekey-3p-ble-write',\n subscribe: '$onekey-3p-ble-subscribe',\n unsubscribe: '$onekey-3p-ble-unsubscribe',\n availability: '$onekey-3p-ble-availability',\n getDevice: '$onekey-3p-ble-get-device',\n readRssi: '$onekey-3p-ble-read-rssi',\n cancelPairing: '$onekey-3p-ble-cancel-pairing',\n /** push event: main → renderer with assembled BLE notification payload */\n notification: '$onekey-3p-ble-notification',\n /** push event: main → renderer when device disconnects unexpectedly */\n disconnected: '$onekey-3p-ble-disconnected',\n} as const;\n\nexport type ThirdPartyBleChannel =\n (typeof THIRD_PARTY_BLE_CHANNELS)[keyof typeof THIRD_PARTY_BLE_CHANNELS];\n\n/** Reconnect scan window for `connect(id)`. */\nexport const THIRD_PARTY_BLE_SCAN_DURATION_MS = 5_000;\n\n/** Continuous scan auto-stops this long after the last poll (reset, not accumulated). */\nexport const THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS = 10_000;\n\n/** Drop a device from the snapshot if it hasn't re-advertised within this window. */\nexport const THIRD_PARTY_BLE_DEVICE_TTL_MS = 5_000;\n\n/** Time we'll wait for noble to reach `poweredOn` before failing init. */\nexport const THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS = 10_000;\n\n/** Time an availability check waits for a fresh noble to report its first state. */\nexport const THIRD_PARTY_BLE_STATE_SETTLE_TIMEOUT_MS = 3_000;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAIO,IAAM,2BAA2B;AAAA,EACtC,MAAM;AAAA,EACN,UAAU;AAAA,EACV,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,WAAW;AAAA,EACX,aAAa;AAAA,EACb,cAAc;AAAA,EACd,WAAW;AAAA,EACX,UAAU;AAAA,EACV,eAAe;AAAA;AAAA,EAEf,cAAc;AAAA;AAAA,EAEd,cAAc;AAChB;AAMO,IAAM,mCAAmC;AAGzC,IAAM,oCAAoC;AAG1C,IAAM,gCAAgC;AAGtC,IAAM,sCAAsC;AAG5C,IAAM,0CAA0C;","names":[]}
@@ -3,13 +3,15 @@ import {
3
3
  THIRD_PARTY_BLE_DEVICE_TTL_MS,
4
4
  THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS,
5
5
  THIRD_PARTY_BLE_SCAN_DURATION_MS,
6
- THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS
7
- } from "./chunk-A5AENDC2.mjs";
6
+ THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS,
7
+ THIRD_PARTY_BLE_STATE_SETTLE_TIMEOUT_MS
8
+ } from "./chunk-ILABAQJY.mjs";
8
9
  export {
9
10
  THIRD_PARTY_BLE_CHANNELS,
10
11
  THIRD_PARTY_BLE_DEVICE_TTL_MS,
11
12
  THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS,
12
13
  THIRD_PARTY_BLE_SCAN_DURATION_MS,
13
- THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS
14
+ THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS,
15
+ THIRD_PARTY_BLE_STATE_SETTLE_TIMEOUT_MS
14
16
  };
15
17
  //# sourceMappingURL=constants.mjs.map
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { ElectronBleScanOptions, ElectronBleConnectOptions } from '@onekeyfe/hwk-adapter-core';
1
+ import { ElectronBleConnectOptions, ElectronBleScanOptions } from '@onekeyfe/hwk-adapter-core';
2
2
  export { THIRD_PARTY_BLE_CHANNELS, THIRD_PARTY_BLE_DEVICE_TTL_MS, THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS, THIRD_PARTY_BLE_SCAN_DURATION_MS, THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS, ThirdPartyBleChannel } from './constants.mjs';
3
3
 
4
4
  type BleDebugLogLevel = 'debug' | 'info' | 'warn' | 'error';
@@ -12,6 +12,26 @@ type BleDebugLogger = (entry: BleDebugLogEntry) => void;
12
12
  /** Replace sensitive values at any depth; everything else is forwarded verbatim. */
13
13
  declare function redactBleDebugLogData(data?: Record<string, unknown>): Record<string, unknown> | undefined;
14
14
 
15
+ /**
16
+ * Main-process allowlist entry for one vendor key. The renderer only names the vendor; which
17
+ * peripherals it may see and which GATT profile and framing it may use are decided here, so an
18
+ * IPC caller cannot turn the service into a generic BLE proxy. UUID arguments may arrive in any
19
+ * case, with or without dashes.
20
+ */
21
+ interface ThirdPartyBleVendorProfile {
22
+ /** Whether an advertised service UUID belongs to this vendor. */
23
+ isServiceUuid(uuid: string): boolean;
24
+ /** Whether a local name belongs to this vendor; the handler passes a length-capped name. */
25
+ isName?(name: string): boolean;
26
+ /** Whether this GATT profile and write framing is one the vendor's firmware uses. */
27
+ acceptsConnect(options: ElectronBleConnectOptions): boolean;
28
+ }
29
+ /**
30
+ * Vendors the desktop BLE service serves by default, keyed by the vendor string the connectors
31
+ * send. A host passing `vendors` replaces this map, so spread it to add a vendor.
32
+ */
33
+ declare const DEFAULT_THIRD_PARTY_BLE_VENDORS: Readonly<Record<string, ThirdPartyBleVendorProfile>>;
34
+
15
35
  /** Device info the main process returns to the renderer over IPC. */
16
36
  interface ThirdPartyBleDeviceInfo {
17
37
  /** Stable id (noble peripheral.id) used as connectId. */
@@ -77,7 +97,7 @@ interface ThirdPartyBleApi {
77
97
 
78
98
  /**
79
99
  * Subset of @stoprocent/noble we touch. We type as `any` to keep the package
80
- * installable without the native module — it's loaded lazily inside main().
100
+ * installable without the native module — the default factory loads it on first use.
81
101
  */
82
102
  interface NobleLike {
83
103
  state: string;
@@ -139,6 +159,11 @@ interface NobleBleHandlerOptions {
139
159
  /** Override the overall connect timeout (tests only; defaults to 31s). */
140
160
  connectTimeoutMs?: number;
141
161
  logger?: BleDebugLogger;
162
+ /**
163
+ * Vendors served over IPC, keyed by the `vendor` the renderer sends. Replaces the default map
164
+ * (spread `DEFAULT_THIRD_PARTY_BLE_VENDORS` to add one); unknown vendors are rejected.
165
+ */
166
+ vendors?: Record<string, ThirdPartyBleVendorProfile>;
142
167
  }
143
168
  /** Noble BLE logic decoupled from Electron IPC, so tests can use a fake noble. */
144
169
  declare class NobleBleHandler {
@@ -146,6 +171,7 @@ declare class NobleBleHandler {
146
171
  private readonly _factory;
147
172
  private readonly _connectTimeoutMs;
148
173
  private readonly _logger?;
174
+ private readonly _vendors;
149
175
  private readonly _discovered;
150
176
  private readonly _lastSeen;
151
177
  private readonly _connected;
@@ -167,6 +193,7 @@ declare class NobleBleHandler {
167
193
  private _nativeReleased;
168
194
  private _lastNobleRecoverAt?;
169
195
  constructor(options?: NobleBleHandlerOptions);
196
+ private _resolveVendor;
170
197
  setNotificationListener(handler: (id: string, hexData: string) => void): void;
171
198
  setDisconnectedListener(handler: (id: string) => void): void;
172
199
  init(): Promise<void>;
@@ -242,6 +269,8 @@ declare class NobleBleHandler {
242
269
  private _cleanupDevice;
243
270
  private _requireEntry;
244
271
  private _requireNoble;
272
+ /** A fresh noble reads `unknown` until its bindings report; wait for that first report. */
273
+ private _waitForKnownState;
245
274
  private _waitForPoweredOn;
246
275
  private _log;
247
276
  }
@@ -274,4 +303,4 @@ interface ThirdPartyBleSupportHandle {
274
303
  */
275
304
  declare function initThirdPartyBleSupport(webContents: WebContentsLike, options?: InitThirdPartyBleSupportOptions): ThirdPartyBleSupportHandle;
276
305
 
277
- export { type BleDebugLogEntry, type BleDebugLogLevel, type BleDebugLogger, type InitThirdPartyBleSupportOptions, type IpcMainLike, NobleBleHandler, type NobleBleHandlerOptions, type ThirdPartyBleApi, type ThirdPartyBleAvailability, type ThirdPartyBleDeviceInfo, type ThirdPartyBleSupportHandle, initThirdPartyBleSupport, redactBleDebugLogData };
306
+ export { type BleDebugLogEntry, type BleDebugLogLevel, type BleDebugLogger, DEFAULT_THIRD_PARTY_BLE_VENDORS, type InitThirdPartyBleSupportOptions, type IpcMainLike, NobleBleHandler, type NobleBleHandlerOptions, type ThirdPartyBleApi, type ThirdPartyBleAvailability, type ThirdPartyBleDeviceInfo, type ThirdPartyBleSupportHandle, type ThirdPartyBleVendorProfile, initThirdPartyBleSupport, redactBleDebugLogData };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ElectronBleScanOptions, ElectronBleConnectOptions } from '@onekeyfe/hwk-adapter-core';
1
+ import { ElectronBleConnectOptions, ElectronBleScanOptions } from '@onekeyfe/hwk-adapter-core';
2
2
  export { THIRD_PARTY_BLE_CHANNELS, THIRD_PARTY_BLE_DEVICE_TTL_MS, THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS, THIRD_PARTY_BLE_SCAN_DURATION_MS, THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS, ThirdPartyBleChannel } from './constants.js';
3
3
 
4
4
  type BleDebugLogLevel = 'debug' | 'info' | 'warn' | 'error';
@@ -12,6 +12,26 @@ type BleDebugLogger = (entry: BleDebugLogEntry) => void;
12
12
  /** Replace sensitive values at any depth; everything else is forwarded verbatim. */
13
13
  declare function redactBleDebugLogData(data?: Record<string, unknown>): Record<string, unknown> | undefined;
14
14
 
15
+ /**
16
+ * Main-process allowlist entry for one vendor key. The renderer only names the vendor; which
17
+ * peripherals it may see and which GATT profile and framing it may use are decided here, so an
18
+ * IPC caller cannot turn the service into a generic BLE proxy. UUID arguments may arrive in any
19
+ * case, with or without dashes.
20
+ */
21
+ interface ThirdPartyBleVendorProfile {
22
+ /** Whether an advertised service UUID belongs to this vendor. */
23
+ isServiceUuid(uuid: string): boolean;
24
+ /** Whether a local name belongs to this vendor; the handler passes a length-capped name. */
25
+ isName?(name: string): boolean;
26
+ /** Whether this GATT profile and write framing is one the vendor's firmware uses. */
27
+ acceptsConnect(options: ElectronBleConnectOptions): boolean;
28
+ }
29
+ /**
30
+ * Vendors the desktop BLE service serves by default, keyed by the vendor string the connectors
31
+ * send. A host passing `vendors` replaces this map, so spread it to add a vendor.
32
+ */
33
+ declare const DEFAULT_THIRD_PARTY_BLE_VENDORS: Readonly<Record<string, ThirdPartyBleVendorProfile>>;
34
+
15
35
  /** Device info the main process returns to the renderer over IPC. */
16
36
  interface ThirdPartyBleDeviceInfo {
17
37
  /** Stable id (noble peripheral.id) used as connectId. */
@@ -77,7 +97,7 @@ interface ThirdPartyBleApi {
77
97
 
78
98
  /**
79
99
  * Subset of @stoprocent/noble we touch. We type as `any` to keep the package
80
- * installable without the native module — it's loaded lazily inside main().
100
+ * installable without the native module — the default factory loads it on first use.
81
101
  */
82
102
  interface NobleLike {
83
103
  state: string;
@@ -139,6 +159,11 @@ interface NobleBleHandlerOptions {
139
159
  /** Override the overall connect timeout (tests only; defaults to 31s). */
140
160
  connectTimeoutMs?: number;
141
161
  logger?: BleDebugLogger;
162
+ /**
163
+ * Vendors served over IPC, keyed by the `vendor` the renderer sends. Replaces the default map
164
+ * (spread `DEFAULT_THIRD_PARTY_BLE_VENDORS` to add one); unknown vendors are rejected.
165
+ */
166
+ vendors?: Record<string, ThirdPartyBleVendorProfile>;
142
167
  }
143
168
  /** Noble BLE logic decoupled from Electron IPC, so tests can use a fake noble. */
144
169
  declare class NobleBleHandler {
@@ -146,6 +171,7 @@ declare class NobleBleHandler {
146
171
  private readonly _factory;
147
172
  private readonly _connectTimeoutMs;
148
173
  private readonly _logger?;
174
+ private readonly _vendors;
149
175
  private readonly _discovered;
150
176
  private readonly _lastSeen;
151
177
  private readonly _connected;
@@ -167,6 +193,7 @@ declare class NobleBleHandler {
167
193
  private _nativeReleased;
168
194
  private _lastNobleRecoverAt?;
169
195
  constructor(options?: NobleBleHandlerOptions);
196
+ private _resolveVendor;
170
197
  setNotificationListener(handler: (id: string, hexData: string) => void): void;
171
198
  setDisconnectedListener(handler: (id: string) => void): void;
172
199
  init(): Promise<void>;
@@ -242,6 +269,8 @@ declare class NobleBleHandler {
242
269
  private _cleanupDevice;
243
270
  private _requireEntry;
244
271
  private _requireNoble;
272
+ /** A fresh noble reads `unknown` until its bindings report; wait for that first report. */
273
+ private _waitForKnownState;
245
274
  private _waitForPoweredOn;
246
275
  private _log;
247
276
  }
@@ -274,4 +303,4 @@ interface ThirdPartyBleSupportHandle {
274
303
  */
275
304
  declare function initThirdPartyBleSupport(webContents: WebContentsLike, options?: InitThirdPartyBleSupportOptions): ThirdPartyBleSupportHandle;
276
305
 
277
- export { type BleDebugLogEntry, type BleDebugLogLevel, type BleDebugLogger, type InitThirdPartyBleSupportOptions, type IpcMainLike, NobleBleHandler, type NobleBleHandlerOptions, type ThirdPartyBleApi, type ThirdPartyBleAvailability, type ThirdPartyBleDeviceInfo, type ThirdPartyBleSupportHandle, initThirdPartyBleSupport, redactBleDebugLogData };
306
+ export { type BleDebugLogEntry, type BleDebugLogLevel, type BleDebugLogger, DEFAULT_THIRD_PARTY_BLE_VENDORS, type InitThirdPartyBleSupportOptions, type IpcMainLike, NobleBleHandler, type NobleBleHandlerOptions, type ThirdPartyBleApi, type ThirdPartyBleAvailability, type ThirdPartyBleDeviceInfo, type ThirdPartyBleSupportHandle, type ThirdPartyBleVendorProfile, initThirdPartyBleSupport, redactBleDebugLogData };
package/dist/index.js CHANGED
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ DEFAULT_THIRD_PARTY_BLE_VENDORS: () => DEFAULT_THIRD_PARTY_BLE_VENDORS,
23
24
  NobleBleHandler: () => NobleBleHandler,
24
25
  THIRD_PARTY_BLE_CHANNELS: () => THIRD_PARTY_BLE_CHANNELS,
25
26
  THIRD_PARTY_BLE_DEVICE_TTL_MS: () => THIRD_PARTY_BLE_DEVICE_TTL_MS,
@@ -113,25 +114,76 @@ var THIRD_PARTY_BLE_SCAN_DURATION_MS = 5e3;
113
114
  var THIRD_PARTY_BLE_SCAN_IDLE_STOP_MS = 1e4;
114
115
  var THIRD_PARTY_BLE_DEVICE_TTL_MS = 5e3;
115
116
  var THIRD_PARTY_BLE_POWER_ON_TIMEOUT_MS = 1e4;
117
+ var THIRD_PARTY_BLE_STATE_SETTLE_TIMEOUT_MS = 3e3;
118
+
119
+ // src/vendorProfiles.ts
120
+ var normalizeBleUuid = (uuid) => uuid.replace(/-/g, "").toLowerCase();
121
+ var isUuidString = (value) => typeof value === "string";
122
+ var isIntegerInRange = (value, min, max) => typeof value === "number" && Number.isInteger(value) && value >= min && value <= max;
123
+ var TREZOR_SERVICE_UUID = normalizeBleUuid("8c000001-a59b-4d58-a9ad-073df69fa1b1");
124
+ var TREZOR_WRITE_UUID = normalizeBleUuid("8c000002-a59b-4d58-a9ad-073df69fa1b1");
125
+ var TREZOR_NOTIFY_UUID = normalizeBleUuid("8c000003-a59b-4d58-a9ad-073df69fa1b1");
126
+ var TREZOR_PACKET_SIZE = 244;
127
+ var TREZOR_MAX_CHUNK_DELAY_MS = 50;
128
+ var TREZOR_NAME_BRAND = /\bTrezor\b/i;
129
+ var TREZOR_NAME_MODEL = /\bSafe\s*7\b|\bT3W1\b/i;
130
+ var trezorProfile = {
131
+ isServiceUuid: (uuid) => isUuidString(uuid) && normalizeBleUuid(uuid) === TREZOR_SERVICE_UUID,
132
+ isName: (name) => TREZOR_NAME_BRAND.test(name) && TREZOR_NAME_MODEL.test(name),
133
+ acceptsConnect: (options) => {
134
+ const { write } = options;
135
+ return isUuidString(options.serviceUuid) && isUuidString(options.writeUuid) && isUuidString(options.notifyUuid) && normalizeBleUuid(options.serviceUuid) === TREZOR_SERVICE_UUID && normalizeBleUuid(options.writeUuid) === TREZOR_WRITE_UUID && normalizeBleUuid(options.notifyUuid) === TREZOR_NOTIFY_UUID && typeof write === "object" && write !== null && write.mode === "padded" && write.chunkSize === TREZOR_PACKET_SIZE && (write.chunkDelayMs === void 0 || isIntegerInRange(write.chunkDelayMs, 0, TREZOR_MAX_CHUNK_DELAY_MS)) && write.maxLength === void 0;
136
+ }
137
+ };
138
+ var LEDGER_UUID = /^13d634002c97([0-9a-f]{4})000([0-3])4c6564676572$/;
139
+ var LEDGER_ROLE_SERVICE = "0";
140
+ var LEDGER_ROLE_NOTIFY = "1";
141
+ var LEDGER_ROLE_WRITE = "2";
142
+ var LEDGER_ROLE_WRITE_CMD = "3";
143
+ var LEDGER_MAX_FRAME_SIZE = 255;
144
+ var parseLedgerUuid = (uuid) => {
145
+ if (!isUuidString(uuid)) return void 0;
146
+ const parsed = LEDGER_UUID.exec(normalizeBleUuid(uuid));
147
+ return parsed ? { model: parsed[1], role: parsed[2] } : void 0;
148
+ };
149
+ var ledgerProfile = {
150
+ isServiceUuid: (uuid) => parseLedgerUuid(uuid)?.role === LEDGER_ROLE_SERVICE,
151
+ acceptsConnect: (options) => {
152
+ const service = parseLedgerUuid(options.serviceUuid);
153
+ const notify = parseLedgerUuid(options.notifyUuid);
154
+ const write = parseLedgerUuid(options.writeUuid);
155
+ const framing = options.write;
156
+ return service?.role === LEDGER_ROLE_SERVICE && notify?.role === LEDGER_ROLE_NOTIFY && (write?.role === LEDGER_ROLE_WRITE || write?.role === LEDGER_ROLE_WRITE_CMD) && notify.model === service.model && write.model === service.model && typeof framing === "object" && framing !== null && framing.mode === "raw" && isIntegerInRange(framing.maxLength, 1, LEDGER_MAX_FRAME_SIZE) && framing.chunkSize === void 0 && framing.chunkDelayMs === void 0;
157
+ }
158
+ };
159
+ var DEFAULT_THIRD_PARTY_BLE_VENDORS = Object.freeze({
160
+ ledger: ledgerProfile,
161
+ trezor: trezorProfile
162
+ });
116
163
 
117
164
  // src/NobleBleHandler.ts
118
165
  var DEFAULT_NOBLE_FACTORY = () => {
119
166
  const noble = require("@stoprocent/noble");
120
167
  return noble;
121
168
  };
122
- var normalizeUuid = (uuid) => uuid.replace(/-/g, "").toLowerCase();
123
- var matchesPeripheral = (p, match) => {
124
- if (!match) return false;
169
+ var BLE_NAME_MATCH_MAX_LENGTH = 64;
170
+ var matchesPeripheral = (p, profile, narrowTo) => {
125
171
  const adv = p.advertisement ?? {};
126
172
  const advertised = adv.serviceUuids ?? [];
127
- if (match.serviceUuids?.length && advertised.some(
128
- (uuid) => match.serviceUuids?.some((wanted) => normalizeUuid(wanted) === normalizeUuid(uuid))
173
+ if (advertised.some(
174
+ (uuid) => typeof uuid === "string" && profile.isServiceUuid(uuid) && (!narrowTo || narrowTo.has(normalizeBleUuid(uuid)))
129
175
  )) {
130
176
  return true;
131
177
  }
132
178
  const name = adv.localName;
133
- if (!match.namePatterns?.length || !name) return false;
134
- return match.namePatterns.every((pattern) => new RegExp(pattern, "i").test(name));
179
+ if (!profile.isName || typeof name !== "string" || !name) return false;
180
+ return profile.isName(name.slice(0, BLE_NAME_MATCH_MAX_LENGTH));
181
+ };
182
+ var requestedServiceUuids = (options) => {
183
+ const requested = options?.match ? options.match.serviceUuids : options?.serviceUuids;
184
+ if (!Array.isArray(requested)) return void 0;
185
+ const uuids = requested.filter((uuid) => typeof uuid === "string");
186
+ return uuids.length ? new Set(uuids.map(normalizeBleUuid)) : void 0;
135
187
  };
136
188
  var peripheralToInfo = (p) => {
137
189
  const adv = p.advertisement ?? {};
@@ -193,6 +245,12 @@ var NobleBleHandler = class {
193
245
  this._factory = options.nobleFactory ?? DEFAULT_NOBLE_FACTORY;
194
246
  this._connectTimeoutMs = options.connectTimeoutMs ?? BLE_CONNECT_TIMEOUT_MS;
195
247
  this._logger = options.logger;
248
+ this._vendors = new Map(Object.entries(options.vendors ?? DEFAULT_THIRD_PARTY_BLE_VENDORS));
249
+ }
250
+ _resolveVendor(vendor) {
251
+ const profile = typeof vendor === "string" ? this._vendors.get(vendor) : void 0;
252
+ if (typeof vendor !== "string" || !profile) throw new Error("Unsupported BLE vendor");
253
+ return { vendor, profile };
196
254
  }
197
255
  setNotificationListener(handler) {
198
256
  this._onNotification = handler;
@@ -233,6 +291,8 @@ var NobleBleHandler = class {
233
291
  return { available: false, state: "unsupported", initialized: false };
234
292
  }
235
293
  }
294
+ await this._waitForKnownState(THIRD_PARTY_BLE_STATE_SETTLE_TIMEOUT_MS);
295
+ this._assertActive();
236
296
  const state = this._noble?.state ?? "unknown";
237
297
  return {
238
298
  available: state === "poweredOn",
@@ -245,14 +305,15 @@ var NobleBleHandler = class {
245
305
  * packet, and a Safe 7 ADV carries only its name (UUID is in the scan response).
246
306
  */
247
307
  async scan(options) {
248
- this._scanOwners.add(options?.vendor);
308
+ const { vendor, profile } = this._resolveVendor(options?.vendor);
309
+ this._scanOwners.add(vendor);
249
310
  try {
250
311
  await this.init();
251
312
  } catch (error) {
252
- this._scanOwners.delete(options?.vendor);
313
+ this._scanOwners.delete(vendor);
253
314
  throw error;
254
315
  }
255
- if (!this._scanOwners.has(options?.vendor)) return [];
316
+ if (!this._scanOwners.has(vendor)) return [];
256
317
  try {
257
318
  await this._setScanning(true);
258
319
  } catch (error) {
@@ -261,7 +322,7 @@ var NobleBleHandler = class {
261
322
  }
262
323
  this._assertActive();
263
324
  this._armIdleStop();
264
- const devices = this._snapshot(options);
325
+ const devices = this._snapshot(vendor, profile, requestedServiceUuids(options));
265
326
  if (devices.length === 0) {
266
327
  this._log("warn", "scan.empty", {
267
328
  raw: this._discovered.size,
@@ -275,9 +336,8 @@ var NobleBleHandler = class {
275
336
  * In-range devices matching the caller, minus TTL-expired ones. `_discovered`
276
337
  * is only a cache; a missing device can still be reached by `_directConnect`.
277
338
  */
278
- _snapshot(options) {
339
+ _snapshot(vendor, profile, narrowTo) {
279
340
  const now = Date.now();
280
- const match = options?.match ?? { serviceUuids: options?.serviceUuids };
281
341
  const result = [];
282
342
  for (const [id, peripheral] of this._discovered) {
283
343
  if (now - (this._lastSeen.get(id) ?? 0) > THIRD_PARTY_BLE_DEVICE_TTL_MS) {
@@ -285,11 +345,11 @@ var NobleBleHandler = class {
285
345
  this._lastSeen.delete(id);
286
346
  continue;
287
347
  }
288
- if (!matchesPeripheral(peripheral, match)) continue;
348
+ if (!matchesPeripheral(peripheral, profile, narrowTo)) continue;
289
349
  result.push(peripheralToInfo(peripheral));
290
350
  }
291
351
  for (const [id, entry] of this._connected) {
292
- if (entry.vendor !== options?.vendor) continue;
352
+ if (entry.vendor !== vendor) continue;
293
353
  if (result.some((info) => info.id === id)) continue;
294
354
  result.push(peripheralToInfo(entry.peripheral));
295
355
  }
@@ -406,7 +466,9 @@ var NobleBleHandler = class {
406
466
  */
407
467
  getDevice(id) {
408
468
  const p = this._discovered.get(id);
409
- if (!p) return null;
469
+ if (!p || ![...this._vendors.values()].some((profile) => matchesPeripheral(p, profile))) {
470
+ return null;
471
+ }
410
472
  return peripheralToInfo(p);
411
473
  }
412
474
  /** Live RSSI (dBm) of a connected peripheral, else the cached scan-time value. */
@@ -523,10 +585,13 @@ var NobleBleHandler = class {
523
585
  // `timed out` (unreachable) and `connection failed` (stale bond) differently.
524
586
  async connect(id, options) {
525
587
  if (!options || !options.vendor || ![options.serviceUuid, options.writeUuid, options.notifyUuid].every(
526
- (uuid) => typeof uuid === "string" && /^[0-9a-f]{32}$/i.test(normalizeUuid(uuid))
588
+ (uuid) => typeof uuid === "string" && /^[0-9a-f]{32}$/i.test(normalizeBleUuid(uuid))
527
589
  )) {
528
590
  throw new Error("Invalid BLE GATT profile");
529
591
  }
592
+ if (!this._resolveVendor(options.vendor).profile.acceptsConnect(options)) {
593
+ throw new Error(`BLE GATT profile not allowed for ${options.vendor}`);
594
+ }
530
595
  this._assertActive();
531
596
  const claim = { abandoned: false };
532
597
  let abandon;
@@ -563,9 +628,9 @@ var NobleBleHandler = class {
563
628
  })();
564
629
  attempt.settled = Promise.allSettled([nativeOperation, caller]).finally(() => {
565
630
  this._connectAttempts.delete(attempt);
631
+ if (this._scanOwners.size > 0) this._armIdleStop();
566
632
  const connectScanWaiting = [...this._scanOwners].some((owner) => typeof owner === "symbol");
567
633
  if (connectScanWaiting) {
568
- this._armIdleStop();
569
634
  void this._setScanning(true).catch(() => void 0);
570
635
  }
571
636
  });
@@ -634,10 +699,10 @@ var NobleBleHandler = class {
634
699
  [options.serviceUuid],
635
700
  [options.writeUuid, options.notifyUuid]
636
701
  );
637
- const writeUuid = normalizeUuid(options.writeUuid);
638
- const notifyUuid = normalizeUuid(options.notifyUuid);
639
- const writeChar = characteristics.find((c) => normalizeUuid(c.uuid) === writeUuid);
640
- const notifyChar = characteristics.find((c) => normalizeUuid(c.uuid) === notifyUuid);
702
+ const writeUuid = normalizeBleUuid(options.writeUuid);
703
+ const notifyUuid = normalizeBleUuid(options.notifyUuid);
704
+ const writeChar = characteristics.find((c) => normalizeBleUuid(c.uuid) === writeUuid);
705
+ const notifyChar = characteristics.find((c) => normalizeBleUuid(c.uuid) === notifyUuid);
641
706
  if (!writeChar || !notifyChar) {
642
707
  throw new Error(`BLE characteristics not found on device ${id}`);
643
708
  }
@@ -839,6 +904,22 @@ var NobleBleHandler = class {
839
904
  if (!this._noble) throw new Error("Desktop BLE: noble was not initialized");
840
905
  return this._noble;
841
906
  }
907
+ /** A fresh noble reads `unknown` until its bindings report; wait for that first report. */
908
+ async _waitForKnownState(timeoutMs) {
909
+ const noble = this._requireNoble();
910
+ if (noble.state !== "unknown") return;
911
+ await new Promise((resolve) => {
912
+ const settle = () => {
913
+ clearTimeout(timer);
914
+ noble.removeListener("stateChange", settle);
915
+ this._pendingCancellations.delete(settle);
916
+ resolve();
917
+ };
918
+ const timer = setTimeout(settle, timeoutMs);
919
+ this._pendingCancellations.add(settle);
920
+ noble.on("stateChange", settle);
921
+ });
922
+ }
842
923
  async _waitForPoweredOn(timeoutMs) {
843
924
  const noble = this._requireNoble();
844
925
  if (noble.state === "poweredOn") return;
@@ -948,6 +1029,7 @@ function initThirdPartyBleSupport(webContents, options = {}) {
948
1029
  }
949
1030
  // Annotate the CommonJS export names for ESM import in node:
950
1031
  0 && (module.exports = {
1032
+ DEFAULT_THIRD_PARTY_BLE_VENDORS,
951
1033
  NobleBleHandler,
952
1034
  THIRD_PARTY_BLE_CHANNELS,
953
1035
  THIRD_PARTY_BLE_DEVICE_TTL_MS,