@onekeyfe/hwk-desktop-noble-ble 1.2.3-alpha.4 → 1.2.5-alpha.0
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.
- package/dist/{chunk-RNZYQRXV.mjs → chunk-A5AENDC2.mjs} +1 -1
- package/dist/chunk-A5AENDC2.mjs.map +1 -0
- package/dist/constants.d.mts +2 -6
- package/dist/constants.d.ts +2 -6
- package/dist/constants.js.map +1 -1
- package/dist/constants.mjs +1 -1
- package/dist/index.d.mts +36 -116
- package/dist/index.d.ts +36 -116
- package/dist/index.js +157 -185
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +158 -186
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/dist/chunk-RNZYQRXV.mjs.map +0 -1
|
@@ -0,0 +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":[]}
|
package/dist/constants.d.mts
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* IPC
|
|
3
|
-
*
|
|
4
|
-
* handler through `desktopApi.thirdPartyBle`, it does not register its own.
|
|
5
|
-
* Namespaced under `$onekey-3p-ble-…` to avoid clashing with the
|
|
6
|
-
* existing OneKey BLE handler (`$onekey-noble-ble-…`) when both run in
|
|
7
|
-
* the same Electron app.
|
|
2
|
+
* Renderer/main IPC channels shared by every third-party BLE vendor; prefixed to
|
|
3
|
+
* avoid clashing with the OneKey handler (`$onekey-noble-ble-*`) in one app.
|
|
8
4
|
*/
|
|
9
5
|
declare const THIRD_PARTY_BLE_CHANNELS: {
|
|
10
6
|
readonly scan: "$onekey-3p-ble-scan";
|
package/dist/constants.d.ts
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* IPC
|
|
3
|
-
*
|
|
4
|
-
* handler through `desktopApi.thirdPartyBle`, it does not register its own.
|
|
5
|
-
* Namespaced under `$onekey-3p-ble-…` to avoid clashing with the
|
|
6
|
-
* existing OneKey BLE handler (`$onekey-noble-ble-…`) when both run in
|
|
7
|
-
* the same Electron app.
|
|
2
|
+
* Renderer/main IPC channels shared by every third-party BLE vendor; prefixed to
|
|
3
|
+
* avoid clashing with the OneKey handler (`$onekey-noble-ble-*`) in one app.
|
|
8
4
|
*/
|
|
9
5
|
declare const THIRD_PARTY_BLE_CHANNELS: {
|
|
10
6
|
readonly scan: "$onekey-3p-ble-scan";
|
package/dist/constants.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/constants.ts"],"sourcesContent":["/**\n * IPC
|
|
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":[]}
|
package/dist/constants.mjs
CHANGED
package/dist/index.d.mts
CHANGED
|
@@ -12,12 +12,7 @@ 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
|
-
* Shape of the API the renderer process talks to. In a real Electron app
|
|
17
|
-
* this is normally exposed via `contextBridge.exposeInMainWorld('desktopApi',
|
|
18
|
-
* { trezorBle: ... })`, but the transport accepts the bridge directly so
|
|
19
|
-
* non-Electron hosts (and unit tests) can plug in their own implementation.
|
|
20
|
-
*/
|
|
15
|
+
/** Device info the main process returns to the renderer over IPC. */
|
|
21
16
|
interface ThirdPartyBleDeviceInfo {
|
|
22
17
|
/** Stable id (noble peripheral.id) used as connectId. */
|
|
23
18
|
id: string;
|
|
@@ -53,7 +48,7 @@ interface ThirdPartyBleAvailability {
|
|
|
53
48
|
type ThirdPartyBleScanOptions = ElectronBleScanOptions;
|
|
54
49
|
interface ThirdPartyBleApi {
|
|
55
50
|
scan(options?: ThirdPartyBleScanOptions): Promise<ThirdPartyBleDeviceInfo[]>;
|
|
56
|
-
stopScan(): Promise<void>;
|
|
51
|
+
stopScan(vendor?: string): Promise<void>;
|
|
57
52
|
connect(id: string, options: ElectronBleConnectOptions): Promise<{
|
|
58
53
|
id: string;
|
|
59
54
|
name?: string;
|
|
@@ -62,19 +57,18 @@ interface ThirdPartyBleApi {
|
|
|
62
57
|
/** Subscribe to the BLE notify characteristic for `id`. */
|
|
63
58
|
subscribe(id: string): Promise<void>;
|
|
64
59
|
unsubscribe(id: string): Promise<void>;
|
|
65
|
-
/**
|
|
66
|
-
* Write an already-framed payload. The main process applies the framing rule
|
|
67
|
-
* recorded at connect time — splitting and zero-padding for `padded`, or
|
|
68
|
-
* writing the buffer as given for `raw`.
|
|
69
|
-
*/
|
|
60
|
+
/** Write a framed payload; main chunks and zero-pads `padded`, writes `raw` as-is. */
|
|
70
61
|
write(id: string, hexData: string): Promise<void>;
|
|
71
62
|
checkAvailability(): Promise<ThirdPartyBleAvailability>;
|
|
72
63
|
/** Look up a previously-scanned device by id without re-scanning. */
|
|
73
64
|
getDevice(id: string): Promise<ThirdPartyBleDeviceInfo | null>;
|
|
74
65
|
/** Read current RSSI (dBm) of a *connected* peripheral. */
|
|
75
66
|
readRssi(id: string): Promise<number>;
|
|
76
|
-
/**
|
|
77
|
-
cancelPairing(
|
|
67
|
+
/** Cancel only the named vendor/device; omitting the argument cancels everything. */
|
|
68
|
+
cancelPairing(options?: {
|
|
69
|
+
vendor: string;
|
|
70
|
+
id?: string;
|
|
71
|
+
}): Promise<void>;
|
|
78
72
|
/** Register a listener for incoming BLE notifications. Returns an unsubscribe fn. */
|
|
79
73
|
onNotification(handler: (id: string, hexData: string) => void): () => void;
|
|
80
74
|
/** Register a listener for unexpected disconnects. Returns an unsubscribe fn. */
|
|
@@ -93,11 +87,8 @@ interface NobleLike {
|
|
|
93
87
|
stopScanningAsync(): Promise<void>;
|
|
94
88
|
stop?(): void;
|
|
95
89
|
/**
|
|
96
|
-
* Connect by id
|
|
97
|
-
*
|
|
98
|
-
* one for an unknown address (`BLEManager::Connect`, lib/win/src/ble_manager.cc)
|
|
99
|
-
* and macOS resolves it via `retrievePeripheralsWithIdentifiers`
|
|
100
|
-
* (lib/mac/src/ble_manager.mm). Optional so a stub noble can omit it.
|
|
90
|
+
* Connect by id with no scan; both backends emit `discover` as a side effect
|
|
91
|
+
* (win `BLEManager::Connect`, mac `retrievePeripheralsWithIdentifiers`).
|
|
101
92
|
*/
|
|
102
93
|
connectAsync?(idOrAddress: string): Promise<NoblePeripheralLike | undefined>;
|
|
103
94
|
cancelConnect?(idOrAddress: string): void;
|
|
@@ -149,12 +140,7 @@ interface NobleBleHandlerOptions {
|
|
|
149
140
|
connectTimeoutMs?: number;
|
|
150
141
|
logger?: BleDebugLogger;
|
|
151
142
|
}
|
|
152
|
-
/**
|
|
153
|
-
* Core BLE logic, decoupled from Electron's IPC layer so it can be unit
|
|
154
|
-
* tested with a fake noble. Mirrors the OneKey `noble-ble-handler.ts`
|
|
155
|
-
* pattern (a single class that owns the peripheral cache + disconnect
|
|
156
|
-
* callbacks), but trimmed to the minimum surface we expose.
|
|
157
|
-
*/
|
|
143
|
+
/** Noble BLE logic decoupled from Electron IPC, so tests can use a fake noble. */
|
|
158
144
|
declare class NobleBleHandler {
|
|
159
145
|
private _noble;
|
|
160
146
|
private readonly _factory;
|
|
@@ -165,6 +151,8 @@ declare class NobleBleHandler {
|
|
|
165
151
|
private readonly _connected;
|
|
166
152
|
private _discoverHandler?;
|
|
167
153
|
private _scanning;
|
|
154
|
+
private readonly _scanOwners;
|
|
155
|
+
private _scanTransition;
|
|
168
156
|
private _idleStopTimer?;
|
|
169
157
|
private _onNotification?;
|
|
170
158
|
private _onDeviceDisconnected?;
|
|
@@ -178,121 +166,57 @@ declare class NobleBleHandler {
|
|
|
178
166
|
private readonly _connectAttempts;
|
|
179
167
|
private _nativeReleased;
|
|
180
168
|
private _lastNobleRecoverAt?;
|
|
181
|
-
/** The connect currently in flight, so cancelPairing can abandon it. */
|
|
182
|
-
private _activeConnect?;
|
|
183
169
|
constructor(options?: NobleBleHandlerOptions);
|
|
184
170
|
setNotificationListener(handler: (id: string, hexData: string) => void): void;
|
|
185
171
|
setDisconnectedListener(handler: (id: string) => void): void;
|
|
186
172
|
init(): Promise<void>;
|
|
187
173
|
checkAvailability(): Promise<ThirdPartyBleAvailability>;
|
|
188
174
|
/**
|
|
189
|
-
* Lazy-start
|
|
190
|
-
*
|
|
191
|
-
* Scans UNFILTERED and applies the caller's match criteria in `_snapshot()` instead. A
|
|
192
|
-
* service-UUID filter cannot be used here: noble's Windows backend applies it
|
|
193
|
-
* per RECEIVED PACKET (`BLEManager::OnScanResult`, lib/win/src/ble_manager.cc),
|
|
194
|
-
* and a Safe 7's ADV packet carries only its name — the service UUID lives in
|
|
195
|
-
* the scan response, which arrives as a separate, irregularly-timed event. So
|
|
196
|
-
* a filtered scan drops every ADV packet and the device appears to be
|
|
197
|
-
* undiscoverable for minutes at a time while it is plainly on air. (OneKey's
|
|
198
|
-
* own devices do advertise their service UUID, which is why the same filter is
|
|
199
|
-
* safe in `hd-transport-electron` and was copied here by mistake.)
|
|
175
|
+
* Lazy-start an unfiltered scan and return the current snapshot. noble-win filters per
|
|
176
|
+
* packet, and a Safe 7 ADV carries only its name (UUID is in the scan response).
|
|
200
177
|
*/
|
|
201
178
|
scan(options?: ElectronBleScanOptions): Promise<ThirdPartyBleDeviceInfo[]>;
|
|
202
179
|
/**
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
* (see `scan`): it matches the name from the ADV packet, or the service UUID
|
|
206
|
-
* once a scan response has merged into the same peripheral.
|
|
207
|
-
*
|
|
208
|
-
* Note the TTL only prunes what the CALLER sees. `_discovered` is a cache, not
|
|
209
|
-
* the source of truth for reachability — a device missing from here can still
|
|
210
|
-
* be connected to by id (`_directConnect`).
|
|
180
|
+
* In-range devices matching the caller, minus TTL-expired ones. `_discovered`
|
|
181
|
+
* is only a cache; a missing device can still be reached by `_directConnect`.
|
|
211
182
|
*/
|
|
212
183
|
private _snapshot;
|
|
213
184
|
/** A noble instance with its own bindings, so a stuck one can be replaced. */
|
|
214
185
|
private _createFreshNoble;
|
|
215
186
|
/**
|
|
216
|
-
* Rebuild noble when
|
|
217
|
-
*
|
|
218
|
-
* noble's RadioWatcher mid-churn: it latches `unsupported` and never
|
|
219
|
-
* re-evaluates, so every later scan fails until the process restarts. Fresh
|
|
220
|
-
* bindings restart that watcher, which is the in-process equivalent of the
|
|
221
|
-
* app restart that is otherwise the only cure.
|
|
187
|
+
* Rebuild noble when Windows BLE re-enumeration latched its RadioWatcher at
|
|
188
|
+
* `unsupported`; fresh bindings restart the watcher without an app restart.
|
|
222
189
|
*/
|
|
223
190
|
private _recoverNobleIfStuck;
|
|
224
191
|
private _armIdleStop;
|
|
225
192
|
private _clearIdleStop;
|
|
193
|
+
private _setScanning;
|
|
226
194
|
/** Stop scanning but keep the discovered cache (used before connect). */
|
|
227
195
|
private _pauseScan;
|
|
228
196
|
/** Stop scanning and forget discovered devices (idle timeout / teardown). */
|
|
229
197
|
private _stopContinuousScan;
|
|
230
|
-
/**
|
|
231
|
-
|
|
232
|
-
* are filtered per vendor, but the radio is not: one vendor's stopScan ends
|
|
233
|
-
* the other's discovery too. That is safe only because the host mounts one
|
|
234
|
-
* vendor's flow at a time; queues are per adapter, not shared. Anything
|
|
235
|
-
* that breaks that — a background presence probe, say — needs per-vendor
|
|
236
|
-
* refcounting here first.
|
|
237
|
-
*/
|
|
238
|
-
stopScan(): Promise<void>;
|
|
198
|
+
/** A vendor releasing discovery must not stop another vendor's scan. */
|
|
199
|
+
stopScan(vendor?: string): Promise<void>;
|
|
239
200
|
/**
|
|
240
201
|
* Look up a previously-scanned device by id (no extra BLE traffic).
|
|
241
202
|
* Returns null if the device hasn't been seen by a recent scan.
|
|
242
203
|
*/
|
|
243
204
|
getDevice(id: string): ThirdPartyBleDeviceInfo | null;
|
|
244
|
-
/**
|
|
245
|
-
* Read the current RSSI (in dBm) for a connected peripheral. Requires
|
|
246
|
-
* the device to be connected — noble can't read RSSI off a scan-only
|
|
247
|
-
* peripheral. Falls back to the cached scan-time rssi when the noble
|
|
248
|
-
* peripheral doesn't expose updateRssiAsync.
|
|
249
|
-
*/
|
|
205
|
+
/** Live RSSI (dBm) of a connected peripheral, else the cached scan-time value. */
|
|
250
206
|
readRssi(id: string): Promise<number>;
|
|
207
|
+
/** Abandon matching pairing attempts without touching another owner's links. */
|
|
208
|
+
cancelPairing(options?: {
|
|
209
|
+
vendor: string;
|
|
210
|
+
id?: string;
|
|
211
|
+
}): Promise<void>;
|
|
251
212
|
/**
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
* Caller is responsible for surfacing the cancellation to the upper UI layer.
|
|
255
|
-
*
|
|
256
|
-
* Abandoning the connect is what actually ends the flow. Pairing happens
|
|
257
|
-
* inside connectAsync and the entry only reaches _connected after service
|
|
258
|
-
* discovery, so the loop below never sees the device being paired — without
|
|
259
|
-
* the abandon the caller waits out the full connect timeout, which is sized
|
|
260
|
-
* to the OS pairing window and so feels like a hang.
|
|
261
|
-
*/
|
|
262
|
-
cancelPairing(): Promise<void>;
|
|
263
|
-
/**
|
|
264
|
-
* Scan for a specific peripheral id and resolve THE MOMENT it's discovered,
|
|
265
|
-
* stopping the scan immediately (don't wait out the full window). The fast
|
|
266
|
-
* reconnect path for a stored connectId when the device IS advertising.
|
|
267
|
-
*
|
|
268
|
-
* This used to be the only reconnect path, on two assumptions that are both
|
|
269
|
-
* false: that noble cannot connect by id without a scan (it can — see
|
|
270
|
-
* `_directConnect`), and that "the device advertises continuously" (a bonded
|
|
271
|
-
* Safe 7 does not — it holds the link and goes silent). Callers must fall
|
|
272
|
-
* back to `_directConnect` when this returns undefined.
|
|
213
|
+
* Scan until `id` is discovered, releasing only its own scan ownership. A
|
|
214
|
+
* bonded Safe 7 goes silent, so callers fall back to `_directConnect`.
|
|
273
215
|
*/
|
|
274
216
|
private _scanUntilFound;
|
|
275
217
|
/**
|
|
276
|
-
* Connect by id
|
|
277
|
-
*
|
|
278
|
-
* This is the ONLY path that reaches a device which is connected but silent.
|
|
279
|
-
* A linked peripheral stops advertising while it HOLDS A LINK (standard BLE; a Safe 7's
|
|
280
|
-
* screen says "wait connection") — field-verified: bonding/THP handshake
|
|
281
|
-
* alone does NOT silence it, holding the connection does. So while a link is
|
|
282
|
-
* up, no amount of scanning will rediscover it — `_scanUntilFound` alone
|
|
283
|
-
* dead-ends with "device not found" on a device that is sitting right there,
|
|
284
|
-
* connected and reachable.
|
|
285
|
-
*
|
|
286
|
-
* noble supports this: `noble.connectAsync(id)` needs no prior `discover`,
|
|
287
|
-
* because both native backends materialize the peripheral themselves (Windows
|
|
288
|
-
* synthesizes one for an unknown address, macOS retrieves it by identifier)
|
|
289
|
-
* and then emit a `discover`, which our own handler turns back into a
|
|
290
|
-
* `_discovered` entry. OneKey's own noble handler calls this "direct
|
|
291
|
-
* connection mode"; Trezor Suite's equivalent is asking the adapter for its
|
|
292
|
-
* peripheral list instead of keeping a cache.
|
|
293
|
-
*
|
|
294
|
-
* Returns undefined (not throw) so the caller reports the normal
|
|
295
|
-
* "device not found" rather than a confusing noble-internal error.
|
|
218
|
+
* Connect by id without advertisement: the only route to a linked, silent device.
|
|
219
|
+
* Returns undefined instead of throwing so the caller reports "device not found".
|
|
296
220
|
*/
|
|
297
221
|
private _directConnect;
|
|
298
222
|
private _safeDisconnect;
|
|
@@ -308,9 +232,8 @@ declare class NobleBleHandler {
|
|
|
308
232
|
/** Retire a renderer's handler without stopping a process-wide native manager. */
|
|
309
233
|
dispose(): Promise<void>;
|
|
310
234
|
/**
|
|
311
|
-
* Terminal native release, including instances
|
|
312
|
-
*
|
|
313
|
-
* and deduplicate instances passed to releaseNoble across those transports.
|
|
235
|
+
* Terminal native release, including recovered instances. A host sharing noble
|
|
236
|
+
* must defer stop() until every transport disposed, and dedupe the instances.
|
|
314
237
|
*/
|
|
315
238
|
disposeForAppQuit(releaseNoble?: (instance: {
|
|
316
239
|
stop?(): void;
|
|
@@ -346,11 +269,8 @@ interface ThirdPartyBleSupportHandle {
|
|
|
346
269
|
}) => void): Promise<void>;
|
|
347
270
|
}
|
|
348
271
|
/**
|
|
349
|
-
* Wire a `NobleBleHandler` to Electron
|
|
350
|
-
*
|
|
351
|
-
* `BrowserWindow` is ready.
|
|
352
|
-
*
|
|
353
|
-
* Call dispose() when retiring a renderer and disposeForAppQuit() before Node teardown.
|
|
272
|
+
* Wire a `NobleBleHandler` to Electron IPC once `BrowserWindow` is ready. Call dispose()
|
|
273
|
+
* when retiring a renderer and disposeForAppQuit() before Node teardown.
|
|
354
274
|
*/
|
|
355
275
|
declare function initThirdPartyBleSupport(webContents: WebContentsLike, options?: InitThirdPartyBleSupportOptions): ThirdPartyBleSupportHandle;
|
|
356
276
|
|
package/dist/index.d.ts
CHANGED
|
@@ -12,12 +12,7 @@ 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
|
-
* Shape of the API the renderer process talks to. In a real Electron app
|
|
17
|
-
* this is normally exposed via `contextBridge.exposeInMainWorld('desktopApi',
|
|
18
|
-
* { trezorBle: ... })`, but the transport accepts the bridge directly so
|
|
19
|
-
* non-Electron hosts (and unit tests) can plug in their own implementation.
|
|
20
|
-
*/
|
|
15
|
+
/** Device info the main process returns to the renderer over IPC. */
|
|
21
16
|
interface ThirdPartyBleDeviceInfo {
|
|
22
17
|
/** Stable id (noble peripheral.id) used as connectId. */
|
|
23
18
|
id: string;
|
|
@@ -53,7 +48,7 @@ interface ThirdPartyBleAvailability {
|
|
|
53
48
|
type ThirdPartyBleScanOptions = ElectronBleScanOptions;
|
|
54
49
|
interface ThirdPartyBleApi {
|
|
55
50
|
scan(options?: ThirdPartyBleScanOptions): Promise<ThirdPartyBleDeviceInfo[]>;
|
|
56
|
-
stopScan(): Promise<void>;
|
|
51
|
+
stopScan(vendor?: string): Promise<void>;
|
|
57
52
|
connect(id: string, options: ElectronBleConnectOptions): Promise<{
|
|
58
53
|
id: string;
|
|
59
54
|
name?: string;
|
|
@@ -62,19 +57,18 @@ interface ThirdPartyBleApi {
|
|
|
62
57
|
/** Subscribe to the BLE notify characteristic for `id`. */
|
|
63
58
|
subscribe(id: string): Promise<void>;
|
|
64
59
|
unsubscribe(id: string): Promise<void>;
|
|
65
|
-
/**
|
|
66
|
-
* Write an already-framed payload. The main process applies the framing rule
|
|
67
|
-
* recorded at connect time — splitting and zero-padding for `padded`, or
|
|
68
|
-
* writing the buffer as given for `raw`.
|
|
69
|
-
*/
|
|
60
|
+
/** Write a framed payload; main chunks and zero-pads `padded`, writes `raw` as-is. */
|
|
70
61
|
write(id: string, hexData: string): Promise<void>;
|
|
71
62
|
checkAvailability(): Promise<ThirdPartyBleAvailability>;
|
|
72
63
|
/** Look up a previously-scanned device by id without re-scanning. */
|
|
73
64
|
getDevice(id: string): Promise<ThirdPartyBleDeviceInfo | null>;
|
|
74
65
|
/** Read current RSSI (dBm) of a *connected* peripheral. */
|
|
75
66
|
readRssi(id: string): Promise<number>;
|
|
76
|
-
/**
|
|
77
|
-
cancelPairing(
|
|
67
|
+
/** Cancel only the named vendor/device; omitting the argument cancels everything. */
|
|
68
|
+
cancelPairing(options?: {
|
|
69
|
+
vendor: string;
|
|
70
|
+
id?: string;
|
|
71
|
+
}): Promise<void>;
|
|
78
72
|
/** Register a listener for incoming BLE notifications. Returns an unsubscribe fn. */
|
|
79
73
|
onNotification(handler: (id: string, hexData: string) => void): () => void;
|
|
80
74
|
/** Register a listener for unexpected disconnects. Returns an unsubscribe fn. */
|
|
@@ -93,11 +87,8 @@ interface NobleLike {
|
|
|
93
87
|
stopScanningAsync(): Promise<void>;
|
|
94
88
|
stop?(): void;
|
|
95
89
|
/**
|
|
96
|
-
* Connect by id
|
|
97
|
-
*
|
|
98
|
-
* one for an unknown address (`BLEManager::Connect`, lib/win/src/ble_manager.cc)
|
|
99
|
-
* and macOS resolves it via `retrievePeripheralsWithIdentifiers`
|
|
100
|
-
* (lib/mac/src/ble_manager.mm). Optional so a stub noble can omit it.
|
|
90
|
+
* Connect by id with no scan; both backends emit `discover` as a side effect
|
|
91
|
+
* (win `BLEManager::Connect`, mac `retrievePeripheralsWithIdentifiers`).
|
|
101
92
|
*/
|
|
102
93
|
connectAsync?(idOrAddress: string): Promise<NoblePeripheralLike | undefined>;
|
|
103
94
|
cancelConnect?(idOrAddress: string): void;
|
|
@@ -149,12 +140,7 @@ interface NobleBleHandlerOptions {
|
|
|
149
140
|
connectTimeoutMs?: number;
|
|
150
141
|
logger?: BleDebugLogger;
|
|
151
142
|
}
|
|
152
|
-
/**
|
|
153
|
-
* Core BLE logic, decoupled from Electron's IPC layer so it can be unit
|
|
154
|
-
* tested with a fake noble. Mirrors the OneKey `noble-ble-handler.ts`
|
|
155
|
-
* pattern (a single class that owns the peripheral cache + disconnect
|
|
156
|
-
* callbacks), but trimmed to the minimum surface we expose.
|
|
157
|
-
*/
|
|
143
|
+
/** Noble BLE logic decoupled from Electron IPC, so tests can use a fake noble. */
|
|
158
144
|
declare class NobleBleHandler {
|
|
159
145
|
private _noble;
|
|
160
146
|
private readonly _factory;
|
|
@@ -165,6 +151,8 @@ declare class NobleBleHandler {
|
|
|
165
151
|
private readonly _connected;
|
|
166
152
|
private _discoverHandler?;
|
|
167
153
|
private _scanning;
|
|
154
|
+
private readonly _scanOwners;
|
|
155
|
+
private _scanTransition;
|
|
168
156
|
private _idleStopTimer?;
|
|
169
157
|
private _onNotification?;
|
|
170
158
|
private _onDeviceDisconnected?;
|
|
@@ -178,121 +166,57 @@ declare class NobleBleHandler {
|
|
|
178
166
|
private readonly _connectAttempts;
|
|
179
167
|
private _nativeReleased;
|
|
180
168
|
private _lastNobleRecoverAt?;
|
|
181
|
-
/** The connect currently in flight, so cancelPairing can abandon it. */
|
|
182
|
-
private _activeConnect?;
|
|
183
169
|
constructor(options?: NobleBleHandlerOptions);
|
|
184
170
|
setNotificationListener(handler: (id: string, hexData: string) => void): void;
|
|
185
171
|
setDisconnectedListener(handler: (id: string) => void): void;
|
|
186
172
|
init(): Promise<void>;
|
|
187
173
|
checkAvailability(): Promise<ThirdPartyBleAvailability>;
|
|
188
174
|
/**
|
|
189
|
-
* Lazy-start
|
|
190
|
-
*
|
|
191
|
-
* Scans UNFILTERED and applies the caller's match criteria in `_snapshot()` instead. A
|
|
192
|
-
* service-UUID filter cannot be used here: noble's Windows backend applies it
|
|
193
|
-
* per RECEIVED PACKET (`BLEManager::OnScanResult`, lib/win/src/ble_manager.cc),
|
|
194
|
-
* and a Safe 7's ADV packet carries only its name — the service UUID lives in
|
|
195
|
-
* the scan response, which arrives as a separate, irregularly-timed event. So
|
|
196
|
-
* a filtered scan drops every ADV packet and the device appears to be
|
|
197
|
-
* undiscoverable for minutes at a time while it is plainly on air. (OneKey's
|
|
198
|
-
* own devices do advertise their service UUID, which is why the same filter is
|
|
199
|
-
* safe in `hd-transport-electron` and was copied here by mistake.)
|
|
175
|
+
* Lazy-start an unfiltered scan and return the current snapshot. noble-win filters per
|
|
176
|
+
* packet, and a Safe 7 ADV carries only its name (UUID is in the scan response).
|
|
200
177
|
*/
|
|
201
178
|
scan(options?: ElectronBleScanOptions): Promise<ThirdPartyBleDeviceInfo[]>;
|
|
202
179
|
/**
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
* (see `scan`): it matches the name from the ADV packet, or the service UUID
|
|
206
|
-
* once a scan response has merged into the same peripheral.
|
|
207
|
-
*
|
|
208
|
-
* Note the TTL only prunes what the CALLER sees. `_discovered` is a cache, not
|
|
209
|
-
* the source of truth for reachability — a device missing from here can still
|
|
210
|
-
* be connected to by id (`_directConnect`).
|
|
180
|
+
* In-range devices matching the caller, minus TTL-expired ones. `_discovered`
|
|
181
|
+
* is only a cache; a missing device can still be reached by `_directConnect`.
|
|
211
182
|
*/
|
|
212
183
|
private _snapshot;
|
|
213
184
|
/** A noble instance with its own bindings, so a stuck one can be replaced. */
|
|
214
185
|
private _createFreshNoble;
|
|
215
186
|
/**
|
|
216
|
-
* Rebuild noble when
|
|
217
|
-
*
|
|
218
|
-
* noble's RadioWatcher mid-churn: it latches `unsupported` and never
|
|
219
|
-
* re-evaluates, so every later scan fails until the process restarts. Fresh
|
|
220
|
-
* bindings restart that watcher, which is the in-process equivalent of the
|
|
221
|
-
* app restart that is otherwise the only cure.
|
|
187
|
+
* Rebuild noble when Windows BLE re-enumeration latched its RadioWatcher at
|
|
188
|
+
* `unsupported`; fresh bindings restart the watcher without an app restart.
|
|
222
189
|
*/
|
|
223
190
|
private _recoverNobleIfStuck;
|
|
224
191
|
private _armIdleStop;
|
|
225
192
|
private _clearIdleStop;
|
|
193
|
+
private _setScanning;
|
|
226
194
|
/** Stop scanning but keep the discovered cache (used before connect). */
|
|
227
195
|
private _pauseScan;
|
|
228
196
|
/** Stop scanning and forget discovered devices (idle timeout / teardown). */
|
|
229
197
|
private _stopContinuousScan;
|
|
230
|
-
/**
|
|
231
|
-
|
|
232
|
-
* are filtered per vendor, but the radio is not: one vendor's stopScan ends
|
|
233
|
-
* the other's discovery too. That is safe only because the host mounts one
|
|
234
|
-
* vendor's flow at a time; queues are per adapter, not shared. Anything
|
|
235
|
-
* that breaks that — a background presence probe, say — needs per-vendor
|
|
236
|
-
* refcounting here first.
|
|
237
|
-
*/
|
|
238
|
-
stopScan(): Promise<void>;
|
|
198
|
+
/** A vendor releasing discovery must not stop another vendor's scan. */
|
|
199
|
+
stopScan(vendor?: string): Promise<void>;
|
|
239
200
|
/**
|
|
240
201
|
* Look up a previously-scanned device by id (no extra BLE traffic).
|
|
241
202
|
* Returns null if the device hasn't been seen by a recent scan.
|
|
242
203
|
*/
|
|
243
204
|
getDevice(id: string): ThirdPartyBleDeviceInfo | null;
|
|
244
|
-
/**
|
|
245
|
-
* Read the current RSSI (in dBm) for a connected peripheral. Requires
|
|
246
|
-
* the device to be connected — noble can't read RSSI off a scan-only
|
|
247
|
-
* peripheral. Falls back to the cached scan-time rssi when the noble
|
|
248
|
-
* peripheral doesn't expose updateRssiAsync.
|
|
249
|
-
*/
|
|
205
|
+
/** Live RSSI (dBm) of a connected peripheral, else the cached scan-time value. */
|
|
250
206
|
readRssi(id: string): Promise<number>;
|
|
207
|
+
/** Abandon matching pairing attempts without touching another owner's links. */
|
|
208
|
+
cancelPairing(options?: {
|
|
209
|
+
vendor: string;
|
|
210
|
+
id?: string;
|
|
211
|
+
}): Promise<void>;
|
|
251
212
|
/**
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
* Caller is responsible for surfacing the cancellation to the upper UI layer.
|
|
255
|
-
*
|
|
256
|
-
* Abandoning the connect is what actually ends the flow. Pairing happens
|
|
257
|
-
* inside connectAsync and the entry only reaches _connected after service
|
|
258
|
-
* discovery, so the loop below never sees the device being paired — without
|
|
259
|
-
* the abandon the caller waits out the full connect timeout, which is sized
|
|
260
|
-
* to the OS pairing window and so feels like a hang.
|
|
261
|
-
*/
|
|
262
|
-
cancelPairing(): Promise<void>;
|
|
263
|
-
/**
|
|
264
|
-
* Scan for a specific peripheral id and resolve THE MOMENT it's discovered,
|
|
265
|
-
* stopping the scan immediately (don't wait out the full window). The fast
|
|
266
|
-
* reconnect path for a stored connectId when the device IS advertising.
|
|
267
|
-
*
|
|
268
|
-
* This used to be the only reconnect path, on two assumptions that are both
|
|
269
|
-
* false: that noble cannot connect by id without a scan (it can — see
|
|
270
|
-
* `_directConnect`), and that "the device advertises continuously" (a bonded
|
|
271
|
-
* Safe 7 does not — it holds the link and goes silent). Callers must fall
|
|
272
|
-
* back to `_directConnect` when this returns undefined.
|
|
213
|
+
* Scan until `id` is discovered, releasing only its own scan ownership. A
|
|
214
|
+
* bonded Safe 7 goes silent, so callers fall back to `_directConnect`.
|
|
273
215
|
*/
|
|
274
216
|
private _scanUntilFound;
|
|
275
217
|
/**
|
|
276
|
-
* Connect by id
|
|
277
|
-
*
|
|
278
|
-
* This is the ONLY path that reaches a device which is connected but silent.
|
|
279
|
-
* A linked peripheral stops advertising while it HOLDS A LINK (standard BLE; a Safe 7's
|
|
280
|
-
* screen says "wait connection") — field-verified: bonding/THP handshake
|
|
281
|
-
* alone does NOT silence it, holding the connection does. So while a link is
|
|
282
|
-
* up, no amount of scanning will rediscover it — `_scanUntilFound` alone
|
|
283
|
-
* dead-ends with "device not found" on a device that is sitting right there,
|
|
284
|
-
* connected and reachable.
|
|
285
|
-
*
|
|
286
|
-
* noble supports this: `noble.connectAsync(id)` needs no prior `discover`,
|
|
287
|
-
* because both native backends materialize the peripheral themselves (Windows
|
|
288
|
-
* synthesizes one for an unknown address, macOS retrieves it by identifier)
|
|
289
|
-
* and then emit a `discover`, which our own handler turns back into a
|
|
290
|
-
* `_discovered` entry. OneKey's own noble handler calls this "direct
|
|
291
|
-
* connection mode"; Trezor Suite's equivalent is asking the adapter for its
|
|
292
|
-
* peripheral list instead of keeping a cache.
|
|
293
|
-
*
|
|
294
|
-
* Returns undefined (not throw) so the caller reports the normal
|
|
295
|
-
* "device not found" rather than a confusing noble-internal error.
|
|
218
|
+
* Connect by id without advertisement: the only route to a linked, silent device.
|
|
219
|
+
* Returns undefined instead of throwing so the caller reports "device not found".
|
|
296
220
|
*/
|
|
297
221
|
private _directConnect;
|
|
298
222
|
private _safeDisconnect;
|
|
@@ -308,9 +232,8 @@ declare class NobleBleHandler {
|
|
|
308
232
|
/** Retire a renderer's handler without stopping a process-wide native manager. */
|
|
309
233
|
dispose(): Promise<void>;
|
|
310
234
|
/**
|
|
311
|
-
* Terminal native release, including instances
|
|
312
|
-
*
|
|
313
|
-
* and deduplicate instances passed to releaseNoble across those transports.
|
|
235
|
+
* Terminal native release, including recovered instances. A host sharing noble
|
|
236
|
+
* must defer stop() until every transport disposed, and dedupe the instances.
|
|
314
237
|
*/
|
|
315
238
|
disposeForAppQuit(releaseNoble?: (instance: {
|
|
316
239
|
stop?(): void;
|
|
@@ -346,11 +269,8 @@ interface ThirdPartyBleSupportHandle {
|
|
|
346
269
|
}) => void): Promise<void>;
|
|
347
270
|
}
|
|
348
271
|
/**
|
|
349
|
-
* Wire a `NobleBleHandler` to Electron
|
|
350
|
-
*
|
|
351
|
-
* `BrowserWindow` is ready.
|
|
352
|
-
*
|
|
353
|
-
* Call dispose() when retiring a renderer and disposeForAppQuit() before Node teardown.
|
|
272
|
+
* Wire a `NobleBleHandler` to Electron IPC once `BrowserWindow` is ready. Call dispose()
|
|
273
|
+
* when retiring a renderer and disposeForAppQuit() before Node teardown.
|
|
354
274
|
*/
|
|
355
275
|
declare function initThirdPartyBleSupport(webContents: WebContentsLike, options?: InitThirdPartyBleSupportOptions): ThirdPartyBleSupportHandle;
|
|
356
276
|
|