unified-ble-manager 4.0.10 → 4.0.12
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/CHANGELOG.md +44 -0
- package/MIGRATION_4.0.md +1 -1
- package/README.md +51 -14
- package/RELEASE.md +31 -2
- package/ROADMAP.4.0.md +4 -0
- package/SBOM.cdx.json +5 -5
- package/THIRD_PARTY_LICENSES.json +2 -2
- package/android/src/main/java/com/sfourdrinier/unifiedblemanager/protocol/UnifiedBleProtocolAndroidDispatcher.kt +11 -4
- package/android/src/main/java/com/sfourdrinier/unifiedblemanager/protocol/UnifiedBleProtocolControlModule.java +6 -5
- package/android/src/main/java/com/sfourdrinier/unifiedblemanager/protocol/UnifiedBleProtocolJsiBinding.java +4 -4
- package/android/src/main/java/com/sfourdrinier/unifiedblemanager/protocol/generated/NativeProtocolV2Schema.kt +6 -0
- package/android/src/main/jni/UnifiedBleProtocolJsiBinding.cpp +10 -5
- package/android/src/test/java/com/sfourdrinier/unifiedblemanager/protocol/UnifiedBleProtocolAndroidDispatcherLifecycleTest.kt +15 -0
- package/docs/AGENTS.md +33 -0
- package/docs/CONNECTION_MANAGER.md +34 -0
- package/docs/ELECTRON.md +1 -1
- package/docs/EXPO_PLUGIN.md +1 -1
- package/docs/GAPS.4.0.md +1 -1
- package/docs/GETTING_STARTED.md +3 -3
- package/docs/NODE.md +1 -1
- package/docs/README.md +178 -0
- package/docs/WEB.md +215 -75
- package/docs/generated/PLATFORM_SUPPORT.md +1 -1
- package/ios/Generated/NativeProtocolV2Schema.swift +6 -0
- package/lib/commonjs/backends/corebluetooth/corebluetooth-backend.js +6 -4
- package/lib/commonjs/backends/corebluetooth/corebluetooth-backend.js.map +1 -1
- package/lib/commonjs/backends/corebluetooth/corebluetooth-gatt-operations.js +5 -2
- package/lib/commonjs/backends/corebluetooth/corebluetooth-gatt-operations.js.map +1 -1
- package/lib/commonjs/backends/corebluetooth/corebluetooth-handles.js +20 -0
- package/lib/commonjs/backends/corebluetooth/corebluetooth-handles.js.map +1 -1
- package/lib/commonjs/implementation-version.js +1 -1
- package/lib/commonjs/native-protocol/generated/native-protocol-v2-schema.js +4 -2
- package/lib/commonjs/native-protocol/generated/native-protocol-v2-schema.js.map +1 -1
- package/lib/commonjs/native-protocol/rn-android-boundary.js +32 -16
- package/lib/commonjs/native-protocol/rn-android-boundary.js.map +1 -1
- package/lib/module/backends/corebluetooth/corebluetooth-backend.js +6 -4
- package/lib/module/backends/corebluetooth/corebluetooth-backend.js.map +1 -1
- package/lib/module/backends/corebluetooth/corebluetooth-gatt-operations.js +5 -2
- package/lib/module/backends/corebluetooth/corebluetooth-gatt-operations.js.map +1 -1
- package/lib/module/backends/corebluetooth/corebluetooth-handles.js +20 -0
- package/lib/module/backends/corebluetooth/corebluetooth-handles.js.map +1 -1
- package/lib/module/implementation-version.js +1 -1
- package/lib/module/native-protocol/generated/native-protocol-v2-schema.js +3 -1
- package/lib/module/native-protocol/generated/native-protocol-v2-schema.js.map +1 -1
- package/lib/module/native-protocol/rn-android-boundary.js +32 -16
- package/lib/module/native-protocol/rn-android-boundary.js.map +1 -1
- package/lib/typescript/commonjs/src/backends/bluez/bluez-backend-provider.d.ts +1 -1
- package/lib/typescript/commonjs/src/backends/corebluetooth/corebluetooth-backend.d.ts +2 -1
- package/lib/typescript/commonjs/src/backends/corebluetooth/corebluetooth-backend.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/backends/corebluetooth/corebluetooth-boundary.d.ts +4 -0
- package/lib/typescript/commonjs/src/backends/corebluetooth/corebluetooth-boundary.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/backends/corebluetooth/corebluetooth-gatt-operations.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/backends/corebluetooth/corebluetooth-handles.d.ts +2 -1
- package/lib/typescript/commonjs/src/backends/corebluetooth/corebluetooth-handles.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/backends/corebluetooth/corebluetooth-identity.d.ts +1 -1
- package/lib/typescript/commonjs/src/backends/reactnative/react-native-android-provider.d.ts +1 -1
- package/lib/typescript/commonjs/src/backends/reactnative/react-native-apple-provider.d.ts +1 -1
- package/lib/typescript/commonjs/src/backends/winrt/winrt-provider.d.ts +1 -1
- package/lib/typescript/commonjs/src/implementation-version.d.ts +1 -1
- package/lib/typescript/commonjs/src/native-protocol/generated/native-protocol-v2-schema.d.ts +2 -0
- package/lib/typescript/commonjs/src/native-protocol/generated/native-protocol-v2-schema.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/native-protocol/rn-android-boundary.d.ts +2 -1
- package/lib/typescript/commonjs/src/native-protocol/rn-android-boundary.d.ts.map +1 -1
- package/lib/typescript/module/src/backends/bluez/bluez-backend-provider.d.ts +1 -1
- package/lib/typescript/module/src/backends/corebluetooth/corebluetooth-backend.d.ts +2 -1
- package/lib/typescript/module/src/backends/corebluetooth/corebluetooth-backend.d.ts.map +1 -1
- package/lib/typescript/module/src/backends/corebluetooth/corebluetooth-boundary.d.ts +4 -0
- package/lib/typescript/module/src/backends/corebluetooth/corebluetooth-boundary.d.ts.map +1 -1
- package/lib/typescript/module/src/backends/corebluetooth/corebluetooth-gatt-operations.d.ts.map +1 -1
- package/lib/typescript/module/src/backends/corebluetooth/corebluetooth-handles.d.ts +2 -1
- package/lib/typescript/module/src/backends/corebluetooth/corebluetooth-handles.d.ts.map +1 -1
- package/lib/typescript/module/src/backends/corebluetooth/corebluetooth-identity.d.ts +1 -1
- package/lib/typescript/module/src/backends/reactnative/react-native-android-provider.d.ts +1 -1
- package/lib/typescript/module/src/backends/reactnative/react-native-apple-provider.d.ts +1 -1
- package/lib/typescript/module/src/backends/winrt/winrt-provider.d.ts +1 -1
- package/lib/typescript/module/src/implementation-version.d.ts +1 -1
- package/lib/typescript/module/src/native-protocol/generated/native-protocol-v2-schema.d.ts +2 -0
- package/lib/typescript/module/src/native-protocol/generated/native-protocol-v2-schema.d.ts.map +1 -1
- package/lib/typescript/module/src/native-protocol/rn-android-boundary.d.ts +2 -1
- package/lib/typescript/module/src/native-protocol/rn-android-boundary.d.ts.map +1 -1
- package/native/AGENTS.md +18 -0
- package/native/PREBUILDS.json +5 -5
- package/native/electron/corebluetooth/index.js +12 -0
- package/native/electron/corebluetooth/prebuilds/darwin-arm64/unified_ble_corebluetooth.node +0 -0
- package/native/electron/corebluetooth/prebuilds/darwin-x64/unified_ble_corebluetooth.node +0 -0
- package/native/electron/winrt/prebuilds/win32-arm64/unified_ble_winrt.node +0 -0
- package/native/electron/winrt/prebuilds/win32-x64/unified_ble_winrt.node +0 -0
- package/native/protocol/generated/NativeProtocolV2Schema.hpp +10 -2
- package/native/protocol/schema/native-protocol-v2-abi.json +3 -1
- package/native/protocol/schema/native-protocol-v2.json +3 -1
- package/package.json +5 -4
- package/src/backends/corebluetooth/corebluetooth-backend.ts +9 -5
- package/src/backends/corebluetooth/corebluetooth-boundary.ts +9 -0
- package/src/backends/corebluetooth/corebluetooth-gatt-operations.ts +8 -2
- package/src/backends/corebluetooth/corebluetooth-handles.ts +36 -1
- package/src/implementation-version.ts +1 -1
- package/src/native-protocol/generated/native-protocol-v2-schema.ts +5 -0
- package/src/native-protocol/rn-android-boundary.ts +53 -20
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,50 @@ All notable changes to `unified-ble-manager` are documented here.
|
|
|
6
6
|
|
|
7
7
|
No changes yet.
|
|
8
8
|
|
|
9
|
+
## [4.0.12] - 2026-08-31
|
|
10
|
+
|
|
11
|
+
### Fixes
|
|
12
|
+
|
|
13
|
+
- Use React Native's architecture-neutral JavaScript call-invoker holder for
|
|
14
|
+
the Android JSI protocol transport. Native result and notification delivery
|
|
15
|
+
now work under both bridgeless and legacy React Native runtimes without
|
|
16
|
+
changing the public API or wire protocol.
|
|
17
|
+
- Preserve the requested notification or indication delivery mode through the
|
|
18
|
+
React Native Android backend and native dispatcher instead of allowing the
|
|
19
|
+
native layer to silently choose a different CCCD mode.
|
|
20
|
+
- Correct React Native protocol ownership and teardown races: binary write
|
|
21
|
+
payloads are released by exactly one owner, connection loss rejects pending
|
|
22
|
+
operations with the normalized lifecycle error, and an in-flight disconnect
|
|
23
|
+
remains pending until the authoritative native teardown terminal arrives.
|
|
24
|
+
- Treat expected, typed direct-GATT link loss as lifecycle state rather than an
|
|
25
|
+
unconditional console error across shared CoreBluetooth-backed hosts. The
|
|
26
|
+
lifecycle event and recovery contract are unchanged.
|
|
27
|
+
|
|
28
|
+
### Documentation
|
|
29
|
+
|
|
30
|
+
- Add `docs/README.md`, a complete documentation map labelling every document
|
|
31
|
+
Current, Historical, or Generated, and add `llms.txt`, a machine-readable
|
|
32
|
+
package overview for AI coding agents covering the contract facts and every
|
|
33
|
+
public entrypoint. Both are drift-gated: `pnpm docs:check` now fails when a
|
|
34
|
+
document is missing from the map, a listed document does not exist, or
|
|
35
|
+
`llms.txt` stops covering a public entrypoint.
|
|
36
|
+
- Label the remaining lineage documents (pre-4.x changelogs, delivered 4.0
|
|
37
|
+
roadmap, tvOS 3.x spec, v1 README/migration) with explicit historical-record
|
|
38
|
+
banners, and mark `docs/audits/`, `docs/review/`, and `docs/superpowers/` as
|
|
39
|
+
frozen record directories.
|
|
40
|
+
- Add scoped agent guidance (`docs/AGENTS.md`, `scripts/AGENTS.md`,
|
|
41
|
+
`native/AGENTS.md`) stating the conventions specific to those trees.
|
|
42
|
+
|
|
43
|
+
## [4.0.11] - 2026-08-30
|
|
44
|
+
|
|
45
|
+
### Documentation
|
|
46
|
+
|
|
47
|
+
- Document adapter interruption recovery through `manager.adapter.watchState()`
|
|
48
|
+
in the README and connection guide, and add the same flow to the Expo
|
|
49
|
+
example. UBM invalidates affected generations and leaves reconnect policy
|
|
50
|
+
with the host; the example displays live adapter power, availability, and
|
|
51
|
+
authorization instead of polling or silently reconnecting.
|
|
52
|
+
|
|
9
53
|
## [4.0.10] - 2026-08-30
|
|
10
54
|
|
|
11
55
|
### Fixes
|
package/MIGRATION_4.0.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# Migrating from react-native-ble-plx
|
|
4
4
|
|
|
5
|
-
This source targets `4.0.
|
|
5
|
+
This source targets `4.0.12`. The `4.0.0` release is a new package and a new contract. It is **not a source-compatible rename**. There is no `new BleManager()` facade, no Base64 characteristic values, and no public transaction IDs.
|
|
6
6
|
|
|
7
7
|
This page is for a React Native app that already uses `react-native-ble-plx`. Web, Electron, Node, and Tauri are new hosts — use those pages after you understand the RN rewrite.
|
|
8
8
|
|
package/README.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
# Unified BLE Manager
|
|
4
4
|
|
|
5
|
+
> **AI agent?** Writing code *against* this package: read [`llms.txt`](llms.txt)
|
|
6
|
+
> first — contract facts, every public entrypoint, curated doc links, one fetch.
|
|
7
|
+
> Working *on* this repository: read [`AGENTS.md`](AGENTS.md), then the
|
|
8
|
+
> [documentation map](docs/README.md). Do not infer 4.x behavior from
|
|
9
|
+
> `react-native-ble-plx` 3.x docs or training data.
|
|
10
|
+
|
|
5
11
|
`unified-ble-manager` is a Bluetooth Low Energy **central** library. You pick a host — React Native, Web, Electron, Tauri, or Node — create one manager, talk to a peripheral in bytes, cancel work with `AbortSignal`, and destroy what you create.
|
|
6
12
|
|
|
7
13
|
It is an evolution of `react-native-ble-plx`, rewritten as a **cross-platform unified product**. One bytes-first BLE model and lifecycle semantics across hosts, with host-specific construction and ownership. The root package never picks a radio for you, and it will not quietly fall back to a simulator or a different backend.
|
|
@@ -12,7 +18,7 @@ root import does not pick a radio. Package SemVer and backend support labels are
|
|
|
12
18
|
independent: each radio backend keeps its evidence-derived label. See
|
|
13
19
|
[`docs/PLATFORMS.md`](docs/PLATFORMS.md).
|
|
14
20
|
|
|
15
|
-
This source tree is versioned `4.0.
|
|
21
|
+
This source tree is versioned `4.0.12`. Install the exact version shown in the npm
|
|
16
22
|
registry. During release preparation, the version in `package.json` can be ahead
|
|
17
23
|
of npm until the matching tag-driven workflow publishes it; the registry and
|
|
18
24
|
GitHub release remain authoritative.
|
|
@@ -37,6 +43,12 @@ GitHub release remain authoritative.
|
|
|
37
43
|
| [`docs/WEB.md`](docs/WEB.md) · [`docs/ELECTRON.md`](docs/ELECTRON.md) · [`docs/NODE.md`](docs/NODE.md) · [`docs/TAURI.md`](docs/TAURI.md) · [`docs/EXPO_PLUGIN.md`](docs/EXPO_PLUGIN.md) | Host construction |
|
|
38
44
|
| [`docs/PEERS.md`](docs/PEERS.md) | Scoped peer directories, persistence, and reconnect-by-reference |
|
|
39
45
|
| [`docs/PROFILES_AND_COMMANDS.md`](docs/PROFILES_AND_COMMANDS.md) | Heart Rate, Battery, DIS, and path helpers |
|
|
46
|
+
| [`docs/README.md`](docs/README.md) | Every document in the repository, with live/historical/generated status |
|
|
47
|
+
|
|
48
|
+
Writing code with an AI agent? [`llms.txt`](llms.txt) is the machine-readable
|
|
49
|
+
package overview — contract facts, every public entrypoint, and curated doc
|
|
50
|
+
links in one fetch. Agents contributing to this repository start at
|
|
51
|
+
[`AGENTS.md`](AGENTS.md) and the [documentation map](docs/README.md).
|
|
40
52
|
|
|
41
53
|
## Install
|
|
42
54
|
|
|
@@ -62,7 +74,7 @@ The root import selects no radio. Import the host you actually run.
|
|
|
62
74
|
| ---------------------------------------- | ------------------------------------------------------------------------------ |
|
|
63
75
|
| `unified-ble-manager` | Host-neutral manager, handles, helpers, and shared types |
|
|
64
76
|
| `unified-ble-manager/react-native` | React Native Android / Apple manager |
|
|
65
|
-
| `unified-ble-manager/react`
|
|
77
|
+
| `unified-ble-manager/react` | React provider, hooks, and React-facing type utilities |
|
|
66
78
|
| `unified-ble-manager/expo` | Expo development-build manager, readiness, and native configuration checks |
|
|
67
79
|
| `unified-ble-manager/web` | Web Bluetooth chooser + matched manager |
|
|
68
80
|
| `unified-ble-manager/electron/main` | Trusted Electron-main radio + IPC router |
|
|
@@ -106,6 +118,31 @@ On Android, `manager.peers.bonded()` lists paired system peers and
|
|
|
106
118
|
reachable. See [`docs/PEERS.md`](docs/PEERS.md) for the persistence and error
|
|
107
119
|
semantics.
|
|
108
120
|
|
|
121
|
+
### Recovering after an adapter interruption
|
|
122
|
+
|
|
123
|
+
Adapter power and authorization changes are observable through the same public
|
|
124
|
+
contract on every backend:
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
const watch = await manager.adapter.watchState()
|
|
128
|
+
let previous = watch.initial
|
|
129
|
+
for await (const item of watch.values) {
|
|
130
|
+
if (item.kind !== 'value') continue
|
|
131
|
+
const state = item.value
|
|
132
|
+
const restored = previous.power !== 'on' && state.power === 'on'
|
|
133
|
+
previous = state
|
|
134
|
+
if (restored && state.availability === 'available' && state.authorization === 'granted') {
|
|
135
|
+
// Reconcile your saved peer/reference and reconnect using your product policy.
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
await watch.stop()
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
UBM invalidates affected connections when the adapter is unavailable and does
|
|
142
|
+
not reconnect silently. The host observes `watchState()`, resolves a fresh
|
|
143
|
+
peer/reference, and starts a new connection generation. Do not poll, create a
|
|
144
|
+
second manager, or reuse old GATT/database handles.
|
|
145
|
+
|
|
109
146
|
### Expo plugin
|
|
110
147
|
|
|
111
148
|
Use an Expo development build, never Expo Go. Plugin options live in
|
|
@@ -174,7 +211,7 @@ try {
|
|
|
174
211
|
|
|
175
212
|
Battery Level and Heart Rate Control Point are optional or conditional; see [`docs/TUTORIALS.md`](docs/TUTORIALS.md). Persistent subscriptions also live there.
|
|
176
213
|
|
|
177
|
-
Web Bluetooth replaces the scan with `ble.choose(...)` from a user gesture. Tauri and the Electron renderer use different host entrypoints — see those host pages.
|
|
214
|
+
Web Bluetooth replaces the scan with `ble.choose(...)` from a user gesture. The [complete Web Bluetooth guide](docs/WEB.md) and [TypeScript/Vite example](example-web/) cover chooser permissions, authorized peers, bounded operations, notifications, structured errors, and safe cleanup. Tauri and the Electron renderer use different host entrypoints — see those host pages.
|
|
178
215
|
|
|
179
216
|
## Why the API looks like this
|
|
180
217
|
|
|
@@ -293,17 +330,17 @@ after disconnect, service change, or rediscovery.
|
|
|
293
330
|
|
|
294
331
|
### Host factories
|
|
295
332
|
|
|
296
|
-
| Factory | Returns
|
|
297
|
-
| -------------------------------------------------------------------------------------- |
|
|
298
|
-
| `createReactNativeBleManager` | Zero-plumbing public React Native manager
|
|
299
|
-
| `createReactNativeBleManagerWithEnvironment` | Injectable RN factory for tests
|
|
300
|
-
| `createExpoBleManager` | Expo development-build manager with readiness and native configuration checks
|
|
301
|
-
| `createWebBleManager` | Zero-plumbing public Web manager; use `ble.choose()` from a user gesture
|
|
302
|
-
| `createCoreBluetoothBleManager` / `createWinRtBleManager` / `createBluezBleManager` | One-call Node managers
|
|
303
|
-
| `createElectronMainCoreBluetoothBackendProvider` / `WinRt` | Main-process provider; you still build a `BleManager`
|
|
304
|
-
| `createElectronRendererBleManager` / `createElectronRendererBleManagerWithEnvironment` | Public renderer `BleManager` over a preload transport; the renderer never loads a radio backend
|
|
305
|
-
| `createTauriBleManager` | Zero-plumbing Tauri `BleManager`; tests use `createTauriBleManagerWithEnvironment`
|
|
306
|
-
| `createBleManagerFromProvider` | Advanced provider construction
|
|
333
|
+
| Factory | Returns |
|
|
334
|
+
| -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
|
335
|
+
| `createReactNativeBleManager` | Zero-plumbing public React Native manager |
|
|
336
|
+
| `createReactNativeBleManagerWithEnvironment` | Injectable RN factory for tests |
|
|
337
|
+
| `createExpoBleManager` | Expo development-build manager with readiness and native configuration checks |
|
|
338
|
+
| `createWebBleManager` | Zero-plumbing public Web manager; use `ble.choose()` from a user gesture |
|
|
339
|
+
| `createCoreBluetoothBleManager` / `createWinRtBleManager` / `createBluezBleManager` | One-call Node managers |
|
|
340
|
+
| `createElectronMainCoreBluetoothBackendProvider` / `WinRt` | Main-process provider; you still build a `BleManager` |
|
|
341
|
+
| `createElectronRendererBleManager` / `createElectronRendererBleManagerWithEnvironment` | Public renderer `BleManager` over a preload transport; the renderer never loads a radio backend |
|
|
342
|
+
| `createTauriBleManager` | Zero-plumbing Tauri `BleManager`; tests use `createTauriBleManagerWithEnvironment` |
|
|
343
|
+
| `createBleManagerFromProvider` | Advanced provider construction |
|
|
307
344
|
| `createPublicBleManagerFacade` | Projects an already-owned `/advanced` manager into the root public `BleManager`; creates no advanced manager, backend, or radio |
|
|
308
345
|
|
|
309
346
|
## Other hosts
|
package/RELEASE.md
CHANGED
|
@@ -81,7 +81,7 @@ Active `4.0.0-rc.*` release-train candidates publish to npm `latest` so a bare `
|
|
|
81
81
|
|
|
82
82
|
On release day, set `release_candidate` to the exact candidate required by the
|
|
83
83
|
release plan. RC2, RC3, RC4, `4.0.0-rc.4.1`, and RC5 are already immutable
|
|
84
|
-
once tagged. Stable `4.0.0` through `4.0.
|
|
84
|
+
once tagged. Stable `4.0.0` through `4.0.11` are immutable. `4.0.12` is the current train head.
|
|
85
85
|
|
|
86
86
|
```sh
|
|
87
87
|
release_candidate=4.0.0-rc.N
|
|
@@ -133,6 +133,35 @@ The `v4.0.3` tag is immutable published history. Do not recreate or move it.
|
|
|
133
133
|
git tag -a v4.0.3 -m "v4.0.3"
|
|
134
134
|
```
|
|
135
135
|
|
|
136
|
+
## Releasing 4.0.12
|
|
137
|
+
|
|
138
|
+
The `v4.0.12` tag must identify the exact current `main` commit after canonical
|
|
139
|
+
CI passes. Do not tag this feature branch directly.
|
|
140
|
+
|
|
141
|
+
```sh
|
|
142
|
+
git fetch origin --tags
|
|
143
|
+
git checkout main
|
|
144
|
+
git pull --ff-only origin main
|
|
145
|
+
|
|
146
|
+
test "$(git branch --show-current)" = "main"
|
|
147
|
+
test "$(node -p "require('./package.json').version")" = "4.0.12"
|
|
148
|
+
git diff --exit-code
|
|
149
|
+
git diff --cached --exit-code
|
|
150
|
+
|
|
151
|
+
git tag -a v4.0.12 -m "v4.0.12"
|
|
152
|
+
git push origin v4.0.12
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Before tagging, confirm release-note extraction finds `## [4.0.12]`.
|
|
156
|
+
|
|
157
|
+
## Releasing 4.0.11
|
|
158
|
+
|
|
159
|
+
The `v4.0.11` tag is immutable published history. Do not recreate or move it.
|
|
160
|
+
|
|
161
|
+
```sh
|
|
162
|
+
git tag -a v4.0.11 -m "v4.0.11"
|
|
163
|
+
```
|
|
164
|
+
|
|
136
165
|
## Releasing 4.0.10
|
|
137
166
|
|
|
138
167
|
The `v4.0.10` tag must identify the exact current `main` commit after canonical
|
|
@@ -277,7 +306,7 @@ a green publish job and a package a consumer can actually install are not the
|
|
|
277
306
|
same claim.
|
|
278
307
|
|
|
279
308
|
```sh
|
|
280
|
-
version=4.0.
|
|
309
|
+
version=4.0.12
|
|
281
310
|
|
|
282
311
|
npm view "unified-ble-manager@$version" version
|
|
283
312
|
npm view unified-ble-manager dist-tags --json
|
package/ROADMAP.4.0.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
# Roadmap 4.0 — `unified-ble-manager`
|
|
4
4
|
|
|
5
|
+
> **Status: Historical record.** This roadmap's scope was delivered with the
|
|
6
|
+
> stable 4.0 releases. It is kept as a decision record and is not current
|
|
7
|
+
> guidance. See the [documentation map](docs/README.md).
|
|
8
|
+
|
|
5
9
|
**Status:** implemented 4.0 product scope; stable package/API release target
|
|
6
10
|
|
|
7
11
|
**Architecture authority:** [`docs/UNIFIED_BLE_4.0_IMPLEMENTATION_PLAN.md`](docs/UNIFIED_BLE_4.0_IMPLEMENTATION_PLAN.md)
|
package/SBOM.cdx.json
CHANGED
|
@@ -6,20 +6,20 @@
|
|
|
6
6
|
"metadata": {
|
|
7
7
|
"component": {
|
|
8
8
|
"type": "library",
|
|
9
|
-
"bom-ref": "pkg:npm/unified-ble-manager@4.0.
|
|
9
|
+
"bom-ref": "pkg:npm/unified-ble-manager@4.0.12",
|
|
10
10
|
"name": "unified-ble-manager",
|
|
11
|
-
"version": "4.0.
|
|
11
|
+
"version": "4.0.12",
|
|
12
12
|
"licenses": [
|
|
13
13
|
{
|
|
14
14
|
"expression": "Apache-2.0"
|
|
15
15
|
}
|
|
16
16
|
],
|
|
17
|
-
"purl": "pkg:npm/unified-ble-manager@4.0.
|
|
17
|
+
"purl": "pkg:npm/unified-ble-manager@4.0.12"
|
|
18
18
|
},
|
|
19
19
|
"properties": [
|
|
20
20
|
{
|
|
21
21
|
"name": "unified-ble-manager:pnpm-lock-sha256",
|
|
22
|
-
"value": "
|
|
22
|
+
"value": "58c2b09c18f10de1e250a357e5ce9445fe6bcace614e3778bc60166b9db31fa1"
|
|
23
23
|
},
|
|
24
24
|
{
|
|
25
25
|
"name": "unified-ble-manager:dependency-scope",
|
|
@@ -537,7 +537,7 @@
|
|
|
537
537
|
"dependsOn": []
|
|
538
538
|
},
|
|
539
539
|
{
|
|
540
|
-
"ref": "pkg:npm/unified-ble-manager@4.0.
|
|
540
|
+
"ref": "pkg:npm/unified-ble-manager@4.0.12",
|
|
541
541
|
"dependsOn": [
|
|
542
542
|
"pkg:npm/%40babel/runtime@7.29.7",
|
|
543
543
|
"pkg:npm/node-addon-api@8.9.0",
|
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
"schemaVersion": "1.0.0",
|
|
5
5
|
"package": {
|
|
6
6
|
"name": "unified-ble-manager",
|
|
7
|
-
"version": "4.0.
|
|
7
|
+
"version": "4.0.12"
|
|
8
8
|
},
|
|
9
9
|
"source": {
|
|
10
10
|
"method": "pnpm-lock production graph with installed-manifest license audit",
|
|
11
11
|
"lockfile": "pnpm-lock.yaml",
|
|
12
|
-
"lockfileSha256": "
|
|
12
|
+
"lockfileSha256": "58c2b09c18f10de1e250a357e5ce9445fe6bcace614e3778bc60166b9db31fa1"
|
|
13
13
|
},
|
|
14
14
|
"reviewedOverrides": [
|
|
15
15
|
{
|
|
@@ -571,7 +571,8 @@ class UnifiedBleProtocolAndroidDispatcher(
|
|
|
571
571
|
endpoint.serviceOccurrence,
|
|
572
572
|
endpoint.characteristicUuid,
|
|
573
573
|
endpoint.characteristicOccurrence,
|
|
574
|
-
enable
|
|
574
|
+
enable,
|
|
575
|
+
subscriptionType = command.optionalString(21)
|
|
575
576
|
) { result ->
|
|
576
577
|
result.fold(
|
|
577
578
|
onSuccess = {
|
|
@@ -598,7 +599,11 @@ class UnifiedBleProtocolAndroidDispatcher(
|
|
|
598
599
|
}
|
|
599
600
|
val subscriptionId = command.requiredString(7)
|
|
600
601
|
if (enable) {
|
|
601
|
-
activeSubscriptions[subscriptionId] = SubscriptionRoute(
|
|
602
|
+
activeSubscriptions[subscriptionId] = SubscriptionRoute(
|
|
603
|
+
subscriptionId,
|
|
604
|
+
endpoint,
|
|
605
|
+
command.optionalString(21)
|
|
606
|
+
)
|
|
602
607
|
} else {
|
|
603
608
|
activeSubscriptions.remove(subscriptionId)
|
|
604
609
|
}
|
|
@@ -682,7 +687,8 @@ class UnifiedBleProtocolAndroidDispatcher(
|
|
|
682
687
|
route.endpoint.serviceOccurrence,
|
|
683
688
|
route.endpoint.characteristicUuid,
|
|
684
689
|
route.endpoint.characteristicOccurrence,
|
|
685
|
-
true
|
|
690
|
+
true,
|
|
691
|
+
subscriptionType = route.mode
|
|
686
692
|
) { result ->
|
|
687
693
|
result.exceptionOrNull()?.let { error ->
|
|
688
694
|
radio.reportCleanupFailure(
|
|
@@ -1100,7 +1106,8 @@ class UnifiedBleProtocolAndroidDispatcher(
|
|
|
1100
1106
|
|
|
1101
1107
|
private data class SubscriptionRoute(
|
|
1102
1108
|
val subscriptionId: String,
|
|
1103
|
-
val endpoint: CharacteristicEndpoint
|
|
1109
|
+
val endpoint: CharacteristicEndpoint,
|
|
1110
|
+
val mode: String?
|
|
1104
1111
|
) {
|
|
1105
1112
|
fun matches(
|
|
1106
1113
|
deviceId: String,
|
|
@@ -22,10 +22,10 @@ import com.facebook.react.bridge.Arguments;
|
|
|
22
22
|
import com.facebook.react.bridge.Promise;
|
|
23
23
|
import com.facebook.react.bridge.ReactApplicationContext;
|
|
24
24
|
import com.facebook.react.bridge.ReadableMap;
|
|
25
|
-
import com.facebook.react.bridge.RuntimeExecutor;
|
|
26
25
|
import com.facebook.react.bridge.WritableMap;
|
|
27
26
|
import com.facebook.react.bridge.ActivityEventListener;
|
|
28
27
|
import com.facebook.react.module.annotations.ReactModule;
|
|
28
|
+
import com.facebook.react.turbomodule.core.CallInvokerHolderImpl;
|
|
29
29
|
import com.sfourdrinier.unifiedblemanager.NativeUnifiedBleProtocolControlSpec;
|
|
30
30
|
|
|
31
31
|
import java.io.ByteArrayOutputStream;
|
|
@@ -487,11 +487,12 @@ public final class UnifiedBleProtocolControlModule extends NativeUnifiedBleProto
|
|
|
487
487
|
public synchronized void installExecutionRuntime(Promise promise) {
|
|
488
488
|
try {
|
|
489
489
|
requireOpen();
|
|
490
|
-
final
|
|
491
|
-
|
|
492
|
-
|
|
490
|
+
final CallInvokerHolderImpl jsCallInvokerHolder =
|
|
491
|
+
(CallInvokerHolderImpl) reactContext.getJSCallInvokerHolder();
|
|
492
|
+
if (jsCallInvokerHolder == null) {
|
|
493
|
+
throw new IllegalStateException("React Native JS CallInvoker is unavailable");
|
|
493
494
|
}
|
|
494
|
-
UnifiedBleProtocolJsiBinding.install(
|
|
495
|
+
UnifiedBleProtocolJsiBinding.install(jsCallInvokerHolder, nativeHandle, reactContext);
|
|
495
496
|
promise.resolve(null);
|
|
496
497
|
} catch (RuntimeException error) {
|
|
497
498
|
Log.e(TAG, "installExecutionRuntime failed", error);
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
package com.sfourdrinier.unifiedblemanager.protocol;
|
|
4
4
|
|
|
5
|
-
import com.facebook.react.bridge.RuntimeExecutor;
|
|
6
5
|
import com.facebook.react.bridge.ReactApplicationContext;
|
|
6
|
+
import com.facebook.react.turbomodule.core.CallInvokerHolderImpl;
|
|
7
7
|
|
|
8
8
|
import java.util.concurrent.ConcurrentHashMap;
|
|
9
9
|
|
|
@@ -15,14 +15,14 @@ final class UnifiedBleProtocolJsiBinding {
|
|
|
15
15
|
private UnifiedBleProtocolJsiBinding() {}
|
|
16
16
|
|
|
17
17
|
static void install(
|
|
18
|
-
|
|
18
|
+
CallInvokerHolderImpl jsCallInvokerHolder,
|
|
19
19
|
long nativeHandle,
|
|
20
20
|
ReactApplicationContext context) {
|
|
21
21
|
final UnifiedBleProtocolAndroidDispatcher dispatcher =
|
|
22
22
|
DISPATCHERS.reserve(
|
|
23
23
|
nativeHandle, () -> new UnifiedBleProtocolAndroidDispatcher(context, nativeHandle));
|
|
24
24
|
try {
|
|
25
|
-
installNative(
|
|
25
|
+
installNative(jsCallInvokerHolder, nativeHandle);
|
|
26
26
|
} catch (RuntimeException error) {
|
|
27
27
|
try {
|
|
28
28
|
close(nativeHandle);
|
|
@@ -197,7 +197,7 @@ final class UnifiedBleProtocolJsiBinding {
|
|
|
197
197
|
}
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
-
private static native void installNative(
|
|
200
|
+
private static native void installNative(CallInvokerHolderImpl jsCallInvokerHolder, long nativeHandle);
|
|
201
201
|
private static native void uninstallNative(long nativeHandle);
|
|
202
202
|
private static native String requestCancellationNative(long nativeHandle, long dispatchEpoch, String nonce);
|
|
203
203
|
private static native boolean emitRecordNative(long nativeHandle, byte[] encodedRecord);
|
|
@@ -119,6 +119,11 @@ enum class WriteModes(val wireValue: Int) {
|
|
|
119
119
|
WITHOUT_RESPONSE(2)
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
+
enum class NotificationDeliveryModes(val wireValue: Int) {
|
|
123
|
+
NOTIFICATION(1),
|
|
124
|
+
INDICATION(2)
|
|
125
|
+
}
|
|
126
|
+
|
|
122
127
|
enum class ConnectionPriorities(val wireValue: Int) {
|
|
123
128
|
LOW_POWER(1),
|
|
124
129
|
BALANCED(2),
|
|
@@ -271,6 +276,7 @@ val NATIVE_PROTOCOL_FIELDS: List<FieldDescriptor> = listOf(
|
|
|
271
276
|
FieldDescriptor(RecordKind.COMMAND, 18, "phyRx", "enum:connectionPhys", false),
|
|
272
277
|
FieldDescriptor(RecordKind.COMMAND, 19, "pairTransport", "enum:pairTransports", false),
|
|
273
278
|
FieldDescriptor(RecordKind.COMMAND, 20, "connectionIntent", "enum:connectionIntents", false),
|
|
279
|
+
FieldDescriptor(RecordKind.COMMAND, 21, "notificationDeliveryMode", "enum:notificationDeliveryModes", false),
|
|
274
280
|
FieldDescriptor(RecordKind.TERMINAL, 1, "correlation", "record:operationCorrelation", true),
|
|
275
281
|
FieldDescriptor(RecordKind.TERMINAL, 2, "outcome", "enum:terminalOutcomes", true),
|
|
276
282
|
FieldDescriptor(RecordKind.TERMINAL, 3, "cause", "string", false),
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
#include <android/log.h>
|
|
7
7
|
#include <fbjni/fbjni.h>
|
|
8
8
|
#include <jsi/jsi.h>
|
|
9
|
-
#include <
|
|
9
|
+
#include <ReactCommon/CallInvokerHolder.h>
|
|
10
10
|
|
|
11
11
|
#include <algorithm>
|
|
12
12
|
#include <cstddef>
|
|
@@ -1302,22 +1302,27 @@ class UnifiedBleProtocolJsiBinding final : public jni::JavaClass<UnifiedBleProto
|
|
|
1302
1302
|
|
|
1303
1303
|
static void installNative(
|
|
1304
1304
|
jni::alias_ref<jclass>,
|
|
1305
|
-
jni::alias_ref<facebook::react::
|
|
1305
|
+
jni::alias_ref<facebook::react::CallInvokerHolder::javaobject> jsCallInvokerHolder,
|
|
1306
1306
|
jlong nativeHandle) {
|
|
1307
1307
|
const auto runtimeLease = unifiedBleProtocolRuntimeLease(nativeHandle);
|
|
1308
1308
|
if (runtimeLease.expired()) {
|
|
1309
1309
|
throw std::invalid_argument("Native Protocol v2 runtime is unavailable");
|
|
1310
1310
|
}
|
|
1311
|
-
auto
|
|
1311
|
+
auto callInvoker = jsCallInvokerHolder->cthis()->getCallInvoker();
|
|
1312
|
+
if (!callInvoker) {
|
|
1313
|
+
throw std::invalid_argument("React Native JS CallInvoker is unavailable");
|
|
1314
|
+
}
|
|
1312
1315
|
auto state = std::make_shared<JsiEventSinkState>(
|
|
1313
1316
|
runtimeLease,
|
|
1314
|
-
[
|
|
1317
|
+
[callInvoker](std::function<void(jsi::Runtime&)> task) {
|
|
1318
|
+
callInvoker->invokeAsync(std::move(task));
|
|
1319
|
+
},
|
|
1315
1320
|
nativeHandle);
|
|
1316
1321
|
{
|
|
1317
1322
|
std::scoped_lock lock(eventSinkStatesMutex);
|
|
1318
1323
|
eventSinkStates[nativeHandle] = state;
|
|
1319
1324
|
}
|
|
1320
|
-
|
|
1325
|
+
callInvoker->invokeAsync([runtimeLease, nativeHandle, state](jsi::Runtime& runtime) {
|
|
1321
1326
|
runtime.global().setProperty(
|
|
1322
1327
|
runtime,
|
|
1323
1328
|
kRuntimeName,
|
|
@@ -741,6 +741,21 @@ class UnifiedBleProtocolAndroidDispatcherLifecycleTest {
|
|
|
741
741
|
)
|
|
742
742
|
}
|
|
743
743
|
|
|
744
|
+
@Test
|
|
745
|
+
fun dispatcherForwardsResolvedSubscriptionModeToOwnedRadioCccdSelection() {
|
|
746
|
+
val dispatcher = readAndroidSource(
|
|
747
|
+
"android/src/main/java/com/sfourdrinier/unifiedblemanager/protocol/UnifiedBleProtocolAndroidDispatcher.kt"
|
|
748
|
+
)
|
|
749
|
+
val radio = readAndroidSource(
|
|
750
|
+
"android/src/main/java/com/sfourdrinier/unifiedblemanager/radio/OwnedAndroidGattRadio.kt"
|
|
751
|
+
)
|
|
752
|
+
|
|
753
|
+
assertTrue(dispatcher.contains("subscriptionType = command.optionalString(21)"))
|
|
754
|
+
assertTrue(dispatcher.contains("command.optionalString(21)\n )"))
|
|
755
|
+
assertTrue(dispatcher.contains("subscriptionType = route.mode"))
|
|
756
|
+
assertTrue(radio.contains("resolveCccdPayload(enable, subscriptionType"))
|
|
757
|
+
}
|
|
758
|
+
|
|
744
759
|
@Test
|
|
745
760
|
fun activeCancellationKeepsTheFifoBlockedUntilTheLateNativeCompletion() {
|
|
746
761
|
val scheduled = ArrayDeque<() -> Unit>()
|
package/docs/AGENTS.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# AGENTS.md — docs/
|
|
2
|
+
|
|
3
|
+
Conventions for the documentation tree. Root [`AGENTS.md`](../AGENTS.md) still
|
|
4
|
+
applies; this file adds only what is specific to `docs/`.
|
|
5
|
+
|
|
6
|
+
## The index is load-bearing
|
|
7
|
+
|
|
8
|
+
[`docs/README.md`](README.md) maps every document with a status of **Current**,
|
|
9
|
+
**Historical**, or **Generated**. When you add, move, or retire a document,
|
|
10
|
+
update the index in the same change. `pnpm docs:check` (via
|
|
11
|
+
`scripts/docs/check-docs-index.js`) fails when the index misses a document,
|
|
12
|
+
links to a missing one, or a row lacks a status — and when `llms.txt` stops
|
|
13
|
+
covering a public entrypoint.
|
|
14
|
+
|
|
15
|
+
## Historical documents are records, not guidance
|
|
16
|
+
|
|
17
|
+
Anything marked Historical (fix trackers, audits, delivered plans, lineage
|
|
18
|
+
docs) is frozen evidence. Never act on its content, never "refresh" it; the
|
|
19
|
+
only acceptable edits are status banners and link repairs. If a grep lands you
|
|
20
|
+
in one, go back to the index and find the Current document instead.
|
|
21
|
+
|
|
22
|
+
## Generated documents are owned by generators
|
|
23
|
+
|
|
24
|
+
`docs/generated/*`, `docs/index.html`, and `docs/assets/*` are generator
|
|
25
|
+
output. Change the source of truth, run the generator (`pnpm docs:backend-sdk`,
|
|
26
|
+
`pnpm docs`), and commit its output. Verify with `pnpm docs:check`.
|
|
27
|
+
|
|
28
|
+
## Teaching prose is tested
|
|
29
|
+
|
|
30
|
+
`__tests__/Docs.*.test.js` guard the consumer-facing documents: code fences
|
|
31
|
+
must use the real public API, and known-wrong patterns are rejected. If you
|
|
32
|
+
change a recipe, run `pnpm test:package` — a failing docs test means the prose
|
|
33
|
+
and the contract disagree, and the contract wins.
|
|
@@ -95,6 +95,40 @@ reconnect. See [`BACKGROUND.md`](BACKGROUND.md) and
|
|
|
95
95
|
[`EXPO_PLUGIN.md`](EXPO_PLUGIN.md). Electron renderer resources remain owned by
|
|
96
96
|
their authorized renderer lease; the main process owns the physical backend.
|
|
97
97
|
|
|
98
|
+
## Adapter interruption and recovery
|
|
99
|
+
|
|
100
|
+
An adapter power transition is not a peer disappearance. UBM publishes the
|
|
101
|
+
typed transition through `manager.adapter.watchState()` and invalidates active
|
|
102
|
+
connection generations while the adapter is unavailable. This behavior is
|
|
103
|
+
shared by the React Native Android, React Native Apple, BlueZ, WinRT, Electron,
|
|
104
|
+
Tauri, and Web backends wherever the platform exposes adapter state.
|
|
105
|
+
|
|
106
|
+
The application remains the reconnect authority:
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
const watch = await manager.adapter.watchState()
|
|
110
|
+
let prior = watch.initial
|
|
111
|
+
for await (const item of watch.values) {
|
|
112
|
+
if (item.kind !== 'value') continue
|
|
113
|
+
const current = item.value
|
|
114
|
+
const becameReady = prior.power !== 'on'
|
|
115
|
+
&& current.power === 'on'
|
|
116
|
+
&& current.availability === 'available'
|
|
117
|
+
&& current.authorization === 'granted'
|
|
118
|
+
prior = current
|
|
119
|
+
if (becameReady) {
|
|
120
|
+
// Resolve a fresh peer/reference and invoke the caller-owned retry policy.
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
await watch.stop()
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Do not create a second manager or reuse a connection/database from before the
|
|
127
|
+
adapter loss. A backend may report a cleanup retry while native operations are
|
|
128
|
+
settling; wait for a ready state and retry the application operation with a new
|
|
129
|
+
generation. Backends that cannot observe a given adapter transition expose the
|
|
130
|
+
truthful snapshot they have and never fabricate reconnect behavior.
|
|
131
|
+
|
|
98
132
|
## Cleanup failures
|
|
99
133
|
|
|
100
134
|
Cleanup records are data. A `release-failed` result retains retry ownership and
|
package/docs/ELECTRON.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
Main owns the radio. The renderer uses a versioned IPC client and never loads a native addon.
|
|
6
6
|
|
|
7
|
-
This source targets `4.0.
|
|
7
|
+
This source targets `4.0.12`. Tagged releases ship Node-API v8 prebuilds covering macOS and Windows `arm64`/`x64` for both Node and modern Electron.
|
|
8
8
|
|
|
9
9
|
`unified-ble-manager/electron/main` and
|
|
10
10
|
`unified-ble-manager/electron/renderer` are the only Electron entrypoints.
|
package/docs/EXPO_PLUGIN.md
CHANGED
|
@@ -7,7 +7,7 @@ start a radio, request runtime permissions during prebuild, or prove physical
|
|
|
7
7
|
radio/restoration reliability. Expo Go is not a supported BLE execution
|
|
8
8
|
environment because it cannot contain this native module.
|
|
9
9
|
|
|
10
|
-
Use the v2 plugin options in this `4.0.
|
|
10
|
+
Use the v2 plugin options in this `4.0.12` source. Those options match
|
|
11
11
|
the schema introduced at `4.0.0-rc.4`. Expo Go cannot load this native module.
|
|
12
12
|
|
|
13
13
|
## Installation and development build
|
package/docs/GAPS.4.0.md
CHANGED
|
@@ -14,7 +14,7 @@ This file tracks platform code, CI, package, lab, and live-radio evidence. It do
|
|
|
14
14
|
|
|
15
15
|
The clean-baseline contract, unified core, public manager, deterministic backend, TCK, native protocol, first-party backend implementations, host-isolated package exports, SDK/CLI, and legacy-absence gates exist in the 4.0 source. Passing deterministic, compile, ABI, or package tests are implementation proof; they do not become physical-radio support evidence unless a retained record proves the corresponding live scenario.
|
|
16
16
|
|
|
17
|
-
This source targets `unified-ble-manager@4.0.
|
|
17
|
+
This source targets `unified-ble-manager@4.0.12`; the npm registry and release
|
|
18
18
|
provenance, not a source version string, determine whether it is published.
|
|
19
19
|
Earlier stable releases remain immutable published history. Backend support labels remain
|
|
20
20
|
evidence-derived. This package does not rewrite the evidence inventory: a backend remains at the support level
|
package/docs/GETTING_STARTED.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
This page gets you to a first scan, connect, read, notify, and teardown on React Native. Other hosts are linked at the bottom. The root import does not turn Bluetooth on.
|
|
6
6
|
|
|
7
|
-
This source targets `4.0.
|
|
7
|
+
This source targets `4.0.12`; verify the published version in the npm registry.
|
|
8
8
|
|
|
9
9
|
## Pick a host
|
|
10
10
|
|
|
@@ -38,11 +38,11 @@ request runtime permissions on Android 12+, add
|
|
|
38
38
|
#### Expo / CNG v2
|
|
39
39
|
|
|
40
40
|
The Expo v2 schema and `unified-ble-manager/expo` factory are in this source.
|
|
41
|
-
After the npm registry lists `4.0.
|
|
41
|
+
After the npm registry lists `4.0.12`, install that exact version and keep it in
|
|
42
42
|
your lockfile while validating the native build:
|
|
43
43
|
|
|
44
44
|
```sh
|
|
45
|
-
pnpm add unified-ble-manager@4.0.
|
|
45
|
+
pnpm add unified-ble-manager@4.0.12
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
The package does not run in Expo Go.
|
package/docs/NODE.md
CHANGED
|
@@ -10,7 +10,7 @@ The root import does not open an adapter. Pick one backend:
|
|
|
10
10
|
| `unified-ble-manager/node/winrt` | Windows |
|
|
11
11
|
| `unified-ble-manager/node/bluez` | Linux (needs `dbus-next@^0.10.2` in the app) |
|
|
12
12
|
|
|
13
|
-
This source targets `4.0.
|
|
13
|
+
This source targets `4.0.12`. Tagged releases ship Node-API v8 prebuilds for macOS and Windows on `arm64` and `x64`. A normal install should not compile native code. BlueZ talks D-Bus and has no addon.
|
|
14
14
|
|
|
15
15
|
## One-call factories
|
|
16
16
|
|