unified-ble-manager 4.0.23 → 4.0.25

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.
Files changed (373) hide show
  1. package/CHANGELOG.md +113 -3
  2. package/MIGRATION_4.0.md +1 -1
  3. package/README.md +2 -2
  4. package/RELEASE.md +20 -3
  5. package/SBOM.cdx.json +4 -4
  6. package/THIRD_PARTY_LICENSES.json +1 -1
  7. package/android/build.gradle +4 -0
  8. package/android/src/main/java/com/sfourdrinier/unifiedblemanager/protocol/UnifiedBleProtocolAndroidDispatcher.kt +32 -15
  9. package/android/src/main/java/com/sfourdrinier/unifiedblemanager/radio/OwnedAndroidGattRadio.kt +480 -144
  10. package/android/src/test/java/com/sfourdrinier/unifiedblemanager/protocol/UnifiedBleProtocolAndroidDispatcherLifecycleTest.kt +300 -19
  11. package/android/src/test/java/com/sfourdrinier/unifiedblemanager/protocol/UnifiedBleProtocolAndroidDispatcherRadioIngressTest.kt +216 -0
  12. package/docs/BONDING.md +32 -32
  13. package/docs/ELECTRON.md +1 -1
  14. package/docs/EXPO_PLUGIN.md +1 -1
  15. package/docs/GAPS.4.0.md +1 -1
  16. package/docs/GETTING_STARTED.md +4 -3
  17. package/docs/NODE.md +1 -1
  18. package/docs/README.md +3 -0
  19. package/docs/TUTORIALS.md +2 -0
  20. package/docs/UNIFIED_SEMANTICS.md +4 -2
  21. package/docs/WEB.md +1 -1
  22. package/docs/generated/PLATFORM_SUPPORT.md +1 -1
  23. package/ios/Owned/OwnedCoreBluetoothProtocolRadio.swift +15 -11
  24. package/ios/Owned/OwnedCoreBluetoothProtocolRadioCancellation.swift +62 -0
  25. package/ios/Owned/OwnedCoreBluetoothProtocolRadioSupport.swift +71 -0
  26. package/lib/commonjs/backend-contract/errors.js +16 -0
  27. package/lib/commonjs/backend-contract/errors.js.map +1 -1
  28. package/lib/commonjs/backends/bluez/bluez-backend-runtime.js +2 -0
  29. package/lib/commonjs/backends/bluez/bluez-backend-runtime.js.map +1 -1
  30. package/lib/commonjs/backends/bluez/bluez-connection-runtime.js +304 -23
  31. package/lib/commonjs/backends/bluez/bluez-connection-runtime.js.map +1 -1
  32. package/lib/commonjs/backends/bluez/bluez-property-waiters.js +4 -5
  33. package/lib/commonjs/backends/bluez/bluez-property-waiters.js.map +1 -1
  34. package/lib/commonjs/backends/bluez/bluez-runtime-models.js +29 -0
  35. package/lib/commonjs/backends/bluez/bluez-runtime-models.js.map +1 -1
  36. package/lib/commonjs/backends/bluez/bluez-runtime-types.js.map +1 -1
  37. package/lib/commonjs/backends/bluez/bluez-scan-runtime.js +132 -10
  38. package/lib/commonjs/backends/bluez/bluez-scan-runtime.js.map +1 -1
  39. package/lib/commonjs/backends/bluez/bluez-security.js +10 -7
  40. package/lib/commonjs/backends/bluez/bluez-security.js.map +1 -1
  41. package/lib/commonjs/backends/bluez/bluez-subscription-runtime.js +31 -22
  42. package/lib/commonjs/backends/bluez/bluez-subscription-runtime.js.map +1 -1
  43. package/lib/commonjs/backends/corebluetooth/corebluetooth-gatt-operations.js +12 -5
  44. package/lib/commonjs/backends/corebluetooth/corebluetooth-gatt-operations.js.map +1 -1
  45. package/lib/commonjs/backends/corebluetooth/corebluetooth-read-notify-provenance.js +89 -0
  46. package/lib/commonjs/backends/corebluetooth/corebluetooth-read-notify-provenance.js.map +1 -0
  47. package/lib/commonjs/backends/reactnative/react-native-android-security.js +32 -21
  48. package/lib/commonjs/backends/reactnative/react-native-android-security.js.map +1 -1
  49. package/lib/commonjs/backends/winrt/winrt-backend.js +17 -3
  50. package/lib/commonjs/backends/winrt/winrt-backend.js.map +1 -1
  51. package/lib/commonjs/backends/winrt/winrt-gatt-operations.js +10 -4
  52. package/lib/commonjs/backends/winrt/winrt-gatt-operations.js.map +1 -1
  53. package/lib/commonjs/backends/winrt/winrt-handles.js +2 -1
  54. package/lib/commonjs/backends/winrt/winrt-handles.js.map +1 -1
  55. package/lib/commonjs/backends/winrt/winrt-security.js +43 -24
  56. package/lib/commonjs/backends/winrt/winrt-security.js.map +1 -1
  57. package/lib/commonjs/backends/winrt/winrt-subscription-runtime.js +130 -28
  58. package/lib/commonjs/backends/winrt/winrt-subscription-runtime.js.map +1 -1
  59. package/lib/commonjs/core/bounded-stream.js +20 -10
  60. package/lib/commonjs/core/bounded-stream.js.map +1 -1
  61. package/lib/commonjs/core/core-discovery.js +4 -0
  62. package/lib/commonjs/core/core-discovery.js.map +1 -1
  63. package/lib/commonjs/core/core-lifecycle-observer.js +46 -0
  64. package/lib/commonjs/core/core-lifecycle-observer.js.map +1 -1
  65. package/lib/commonjs/core/owned-bounded-stream.js +4 -4
  66. package/lib/commonjs/core/owned-bounded-stream.js.map +1 -1
  67. package/lib/commonjs/core/unified-ble-core.js +260 -94
  68. package/lib/commonjs/core/unified-ble-core.js.map +1 -1
  69. package/lib/commonjs/electron/connection-event-stream-registry.js +4 -1
  70. package/lib/commonjs/electron/connection-event-stream-registry.js.map +1 -1
  71. package/lib/commonjs/electron/renderer-stream-registry.js +4 -1
  72. package/lib/commonjs/electron/renderer-stream-registry.js.map +1 -1
  73. package/lib/commonjs/implementation-version.js +1 -1
  74. package/lib/commonjs/ipc/manager.js +39 -1
  75. package/lib/commonjs/ipc/manager.js.map +1 -1
  76. package/lib/commonjs/ipc/public-manager.js +23 -10
  77. package/lib/commonjs/ipc/public-manager.js.map +1 -1
  78. package/lib/commonjs/native-protocol/rn-android-boundary.js +10 -0
  79. package/lib/commonjs/native-protocol/rn-android-boundary.js.map +1 -1
  80. package/lib/commonjs/public/ble-manager.js +152 -46
  81. package/lib/commonjs/public/ble-manager.js.map +1 -1
  82. package/lib/commonjs/public/cleanup.js +1 -0
  83. package/lib/commonjs/public/cleanup.js.map +1 -1
  84. package/lib/commonjs/public/scan-state.js +46 -0
  85. package/lib/commonjs/public/scan-state.js.map +1 -1
  86. package/lib/commonjs/public/streams.js +12 -7
  87. package/lib/commonjs/public/streams.js.map +1 -1
  88. package/lib/commonjs/tck/first-party/winrt-tck-registration.js +5 -0
  89. package/lib/commonjs/tck/first-party/winrt-tck-registration.js.map +1 -1
  90. package/lib/commonjs/testing/deterministic/deterministic-stream.js +16 -5
  91. package/lib/commonjs/testing/deterministic/deterministic-stream.js.map +1 -1
  92. package/lib/commonjs/web/navigator-web-bluetooth-boundary.js +20 -0
  93. package/lib/commonjs/web/navigator-web-bluetooth-boundary.js.map +1 -1
  94. package/lib/commonjs/web/web-bluetooth-backend.js +135 -6
  95. package/lib/commonjs/web/web-bluetooth-backend.js.map +1 -1
  96. package/lib/commonjs/web.js.map +1 -1
  97. package/lib/module/backend-contract/errors.js +15 -0
  98. package/lib/module/backend-contract/errors.js.map +1 -1
  99. package/lib/module/backends/bluez/bluez-backend-runtime.js +3 -1
  100. package/lib/module/backends/bluez/bluez-backend-runtime.js.map +1 -1
  101. package/lib/module/backends/bluez/bluez-connection-runtime.js +305 -25
  102. package/lib/module/backends/bluez/bluez-connection-runtime.js.map +1 -1
  103. package/lib/module/backends/bluez/bluez-property-waiters.js +4 -6
  104. package/lib/module/backends/bluez/bluez-property-waiters.js.map +1 -1
  105. package/lib/module/backends/bluez/bluez-runtime-models.js +27 -0
  106. package/lib/module/backends/bluez/bluez-runtime-models.js.map +1 -1
  107. package/lib/module/backends/bluez/bluez-runtime-types.js.map +1 -1
  108. package/lib/module/backends/bluez/bluez-scan-runtime.js +133 -11
  109. package/lib/module/backends/bluez/bluez-scan-runtime.js.map +1 -1
  110. package/lib/module/backends/bluez/bluez-security.js +10 -7
  111. package/lib/module/backends/bluez/bluez-security.js.map +1 -1
  112. package/lib/module/backends/bluez/bluez-subscription-runtime.js +31 -22
  113. package/lib/module/backends/bluez/bluez-subscription-runtime.js.map +1 -1
  114. package/lib/module/backends/corebluetooth/corebluetooth-gatt-operations.js +12 -5
  115. package/lib/module/backends/corebluetooth/corebluetooth-gatt-operations.js.map +1 -1
  116. package/lib/module/backends/corebluetooth/corebluetooth-read-notify-provenance.js +79 -0
  117. package/lib/module/backends/corebluetooth/corebluetooth-read-notify-provenance.js.map +1 -0
  118. package/lib/module/backends/reactnative/react-native-android-security.js +32 -21
  119. package/lib/module/backends/reactnative/react-native-android-security.js.map +1 -1
  120. package/lib/module/backends/winrt/winrt-backend.js +17 -3
  121. package/lib/module/backends/winrt/winrt-backend.js.map +1 -1
  122. package/lib/module/backends/winrt/winrt-gatt-operations.js +12 -6
  123. package/lib/module/backends/winrt/winrt-gatt-operations.js.map +1 -1
  124. package/lib/module/backends/winrt/winrt-handles.js +2 -1
  125. package/lib/module/backends/winrt/winrt-handles.js.map +1 -1
  126. package/lib/module/backends/winrt/winrt-security.js +43 -24
  127. package/lib/module/backends/winrt/winrt-security.js.map +1 -1
  128. package/lib/module/backends/winrt/winrt-subscription-runtime.js +128 -28
  129. package/lib/module/backends/winrt/winrt-subscription-runtime.js.map +1 -1
  130. package/lib/module/core/bounded-stream.js +20 -10
  131. package/lib/module/core/bounded-stream.js.map +1 -1
  132. package/lib/module/core/core-discovery.js +4 -0
  133. package/lib/module/core/core-discovery.js.map +1 -1
  134. package/lib/module/core/core-lifecycle-observer.js +45 -0
  135. package/lib/module/core/core-lifecycle-observer.js.map +1 -1
  136. package/lib/module/core/owned-bounded-stream.js +4 -4
  137. package/lib/module/core/owned-bounded-stream.js.map +1 -1
  138. package/lib/module/core/unified-ble-core.js +261 -95
  139. package/lib/module/core/unified-ble-core.js.map +1 -1
  140. package/lib/module/electron/connection-event-stream-registry.js +5 -2
  141. package/lib/module/electron/connection-event-stream-registry.js.map +1 -1
  142. package/lib/module/electron/renderer-stream-registry.js +5 -2
  143. package/lib/module/electron/renderer-stream-registry.js.map +1 -1
  144. package/lib/module/implementation-version.js +1 -1
  145. package/lib/module/ipc/manager.js +40 -2
  146. package/lib/module/ipc/manager.js.map +1 -1
  147. package/lib/module/ipc/public-manager.js +24 -11
  148. package/lib/module/ipc/public-manager.js.map +1 -1
  149. package/lib/module/native-protocol/rn-android-boundary.js +10 -0
  150. package/lib/module/native-protocol/rn-android-boundary.js.map +1 -1
  151. package/lib/module/public/ble-manager.js +153 -47
  152. package/lib/module/public/ble-manager.js.map +1 -1
  153. package/lib/module/public/cleanup.js +1 -1
  154. package/lib/module/public/cleanup.js.map +1 -1
  155. package/lib/module/public/scan-state.js +44 -0
  156. package/lib/module/public/scan-state.js.map +1 -1
  157. package/lib/module/public/streams.js +13 -8
  158. package/lib/module/public/streams.js.map +1 -1
  159. package/lib/module/tck/first-party/winrt-tck-registration.js +5 -0
  160. package/lib/module/tck/first-party/winrt-tck-registration.js.map +1 -1
  161. package/lib/module/testing/deterministic/deterministic-stream.js +16 -5
  162. package/lib/module/testing/deterministic/deterministic-stream.js.map +1 -1
  163. package/lib/module/web/navigator-web-bluetooth-boundary.js +20 -0
  164. package/lib/module/web/navigator-web-bluetooth-boundary.js.map +1 -1
  165. package/lib/module/web/web-bluetooth-backend.js +135 -6
  166. package/lib/module/web/web-bluetooth-backend.js.map +1 -1
  167. package/lib/module/web.js.map +1 -1
  168. package/lib/typescript/commonjs/src/backend-contract/errors.d.ts +2 -0
  169. package/lib/typescript/commonjs/src/backend-contract/errors.d.ts.map +1 -1
  170. package/lib/typescript/commonjs/src/backend-contract/streams.d.ts +5 -1
  171. package/lib/typescript/commonjs/src/backend-contract/streams.d.ts.map +1 -1
  172. package/lib/typescript/commonjs/src/backends/bluez/bluez-backend-provider.d.ts +1 -1
  173. package/lib/typescript/commonjs/src/backends/bluez/bluez-backend-runtime.d.ts +2 -1
  174. package/lib/typescript/commonjs/src/backends/bluez/bluez-backend-runtime.d.ts.map +1 -1
  175. package/lib/typescript/commonjs/src/backends/bluez/bluez-connection-runtime.d.ts +1 -0
  176. package/lib/typescript/commonjs/src/backends/bluez/bluez-connection-runtime.d.ts.map +1 -1
  177. package/lib/typescript/commonjs/src/backends/bluez/bluez-property-waiters.d.ts +1 -0
  178. package/lib/typescript/commonjs/src/backends/bluez/bluez-property-waiters.d.ts.map +1 -1
  179. package/lib/typescript/commonjs/src/backends/bluez/bluez-runtime-models.d.ts +3 -0
  180. package/lib/typescript/commonjs/src/backends/bluez/bluez-runtime-models.d.ts.map +1 -1
  181. package/lib/typescript/commonjs/src/backends/bluez/bluez-runtime-types.d.ts +22 -1
  182. package/lib/typescript/commonjs/src/backends/bluez/bluez-runtime-types.d.ts.map +1 -1
  183. package/lib/typescript/commonjs/src/backends/bluez/bluez-scan-runtime.d.ts.map +1 -1
  184. package/lib/typescript/commonjs/src/backends/bluez/bluez-security.d.ts +1 -1
  185. package/lib/typescript/commonjs/src/backends/bluez/bluez-security.d.ts.map +1 -1
  186. package/lib/typescript/commonjs/src/backends/bluez/bluez-subscription-runtime.d.ts.map +1 -1
  187. package/lib/typescript/commonjs/src/backends/corebluetooth/corebluetooth-gatt-operations.d.ts.map +1 -1
  188. package/lib/typescript/commonjs/src/backends/corebluetooth/corebluetooth-identity.d.ts +1 -1
  189. package/lib/typescript/commonjs/src/backends/corebluetooth/corebluetooth-read-notify-provenance.d.ts +37 -0
  190. package/lib/typescript/commonjs/src/backends/corebluetooth/corebluetooth-read-notify-provenance.d.ts.map +1 -0
  191. package/lib/typescript/commonjs/src/backends/reactnative/react-native-android-provider.d.ts +1 -1
  192. package/lib/typescript/commonjs/src/backends/reactnative/react-native-android-security.d.ts +1 -1
  193. package/lib/typescript/commonjs/src/backends/reactnative/react-native-android-security.d.ts.map +1 -1
  194. package/lib/typescript/commonjs/src/backends/reactnative/react-native-apple-provider.d.ts +1 -1
  195. package/lib/typescript/commonjs/src/backends/winrt/winrt-backend.d.ts +7 -1
  196. package/lib/typescript/commonjs/src/backends/winrt/winrt-backend.d.ts.map +1 -1
  197. package/lib/typescript/commonjs/src/backends/winrt/winrt-boundary.d.ts +1 -0
  198. package/lib/typescript/commonjs/src/backends/winrt/winrt-boundary.d.ts.map +1 -1
  199. package/lib/typescript/commonjs/src/backends/winrt/winrt-gatt-operations.d.ts +1 -0
  200. package/lib/typescript/commonjs/src/backends/winrt/winrt-gatt-operations.d.ts.map +1 -1
  201. package/lib/typescript/commonjs/src/backends/winrt/winrt-handles.d.ts.map +1 -1
  202. package/lib/typescript/commonjs/src/backends/winrt/winrt-provider.d.ts +1 -1
  203. package/lib/typescript/commonjs/src/backends/winrt/winrt-security.d.ts +1 -1
  204. package/lib/typescript/commonjs/src/backends/winrt/winrt-security.d.ts.map +1 -1
  205. package/lib/typescript/commonjs/src/backends/winrt/winrt-subscription-runtime.d.ts +4 -1
  206. package/lib/typescript/commonjs/src/backends/winrt/winrt-subscription-runtime.d.ts.map +1 -1
  207. package/lib/typescript/commonjs/src/core/bounded-stream.d.ts +5 -3
  208. package/lib/typescript/commonjs/src/core/bounded-stream.d.ts.map +1 -1
  209. package/lib/typescript/commonjs/src/core/core-discovery.d.ts.map +1 -1
  210. package/lib/typescript/commonjs/src/core/core-lifecycle-observer.d.ts +10 -0
  211. package/lib/typescript/commonjs/src/core/core-lifecycle-observer.d.ts.map +1 -1
  212. package/lib/typescript/commonjs/src/core/owned-bounded-stream.d.ts +3 -3
  213. package/lib/typescript/commonjs/src/core/owned-bounded-stream.d.ts.map +1 -1
  214. package/lib/typescript/commonjs/src/core/unified-ble-core.d.ts +6 -0
  215. package/lib/typescript/commonjs/src/core/unified-ble-core.d.ts.map +1 -1
  216. package/lib/typescript/commonjs/src/electron/connection-event-stream-registry.d.ts.map +1 -1
  217. package/lib/typescript/commonjs/src/electron/renderer-stream-registry.d.ts.map +1 -1
  218. package/lib/typescript/commonjs/src/implementation-version.d.ts +1 -1
  219. package/lib/typescript/commonjs/src/ipc/manager.d.ts.map +1 -1
  220. package/lib/typescript/commonjs/src/ipc/public-manager.d.ts.map +1 -1
  221. package/lib/typescript/commonjs/src/manager/consumer-handles.d.ts +1 -0
  222. package/lib/typescript/commonjs/src/manager/consumer-handles.d.ts.map +1 -1
  223. package/lib/typescript/commonjs/src/native-protocol/rn-android-boundary.d.ts.map +1 -1
  224. package/lib/typescript/commonjs/src/public/ble-manager.d.ts +13 -2
  225. package/lib/typescript/commonjs/src/public/ble-manager.d.ts.map +1 -1
  226. package/lib/typescript/commonjs/src/public/cleanup.d.ts +3 -1
  227. package/lib/typescript/commonjs/src/public/cleanup.d.ts.map +1 -1
  228. package/lib/typescript/commonjs/src/public/scan-state.d.ts +15 -0
  229. package/lib/typescript/commonjs/src/public/scan-state.d.ts.map +1 -1
  230. package/lib/typescript/commonjs/src/public/streams.d.ts +1 -0
  231. package/lib/typescript/commonjs/src/public/streams.d.ts.map +1 -1
  232. package/lib/typescript/commonjs/src/tck/first-party/winrt-tck-registration.d.ts +1 -0
  233. package/lib/typescript/commonjs/src/tck/first-party/winrt-tck-registration.d.ts.map +1 -1
  234. package/lib/typescript/commonjs/src/testing/deterministic/deterministic-stream.d.ts +4 -2
  235. package/lib/typescript/commonjs/src/testing/deterministic/deterministic-stream.d.ts.map +1 -1
  236. package/lib/typescript/commonjs/src/web/navigator-web-bluetooth-boundary.d.ts +9 -1
  237. package/lib/typescript/commonjs/src/web/navigator-web-bluetooth-boundary.d.ts.map +1 -1
  238. package/lib/typescript/commonjs/src/web/web-bluetooth-backend.d.ts +16 -0
  239. package/lib/typescript/commonjs/src/web/web-bluetooth-backend.d.ts.map +1 -1
  240. package/lib/typescript/commonjs/src/web/web-bluetooth-boundary.d.ts +11 -0
  241. package/lib/typescript/commonjs/src/web/web-bluetooth-boundary.d.ts.map +1 -1
  242. package/lib/typescript/commonjs/src/web.d.ts +1 -1
  243. package/lib/typescript/commonjs/src/web.d.ts.map +1 -1
  244. package/lib/typescript/module/src/backend-contract/errors.d.ts +2 -0
  245. package/lib/typescript/module/src/backend-contract/errors.d.ts.map +1 -1
  246. package/lib/typescript/module/src/backend-contract/streams.d.ts +5 -1
  247. package/lib/typescript/module/src/backend-contract/streams.d.ts.map +1 -1
  248. package/lib/typescript/module/src/backends/bluez/bluez-backend-provider.d.ts +1 -1
  249. package/lib/typescript/module/src/backends/bluez/bluez-backend-runtime.d.ts +2 -1
  250. package/lib/typescript/module/src/backends/bluez/bluez-backend-runtime.d.ts.map +1 -1
  251. package/lib/typescript/module/src/backends/bluez/bluez-connection-runtime.d.ts +1 -0
  252. package/lib/typescript/module/src/backends/bluez/bluez-connection-runtime.d.ts.map +1 -1
  253. package/lib/typescript/module/src/backends/bluez/bluez-property-waiters.d.ts +1 -0
  254. package/lib/typescript/module/src/backends/bluez/bluez-property-waiters.d.ts.map +1 -1
  255. package/lib/typescript/module/src/backends/bluez/bluez-runtime-models.d.ts +3 -0
  256. package/lib/typescript/module/src/backends/bluez/bluez-runtime-models.d.ts.map +1 -1
  257. package/lib/typescript/module/src/backends/bluez/bluez-runtime-types.d.ts +22 -1
  258. package/lib/typescript/module/src/backends/bluez/bluez-runtime-types.d.ts.map +1 -1
  259. package/lib/typescript/module/src/backends/bluez/bluez-scan-runtime.d.ts.map +1 -1
  260. package/lib/typescript/module/src/backends/bluez/bluez-security.d.ts +1 -1
  261. package/lib/typescript/module/src/backends/bluez/bluez-security.d.ts.map +1 -1
  262. package/lib/typescript/module/src/backends/bluez/bluez-subscription-runtime.d.ts.map +1 -1
  263. package/lib/typescript/module/src/backends/corebluetooth/corebluetooth-gatt-operations.d.ts.map +1 -1
  264. package/lib/typescript/module/src/backends/corebluetooth/corebluetooth-identity.d.ts +1 -1
  265. package/lib/typescript/module/src/backends/corebluetooth/corebluetooth-read-notify-provenance.d.ts +37 -0
  266. package/lib/typescript/module/src/backends/corebluetooth/corebluetooth-read-notify-provenance.d.ts.map +1 -0
  267. package/lib/typescript/module/src/backends/reactnative/react-native-android-provider.d.ts +1 -1
  268. package/lib/typescript/module/src/backends/reactnative/react-native-android-security.d.ts +1 -1
  269. package/lib/typescript/module/src/backends/reactnative/react-native-android-security.d.ts.map +1 -1
  270. package/lib/typescript/module/src/backends/reactnative/react-native-apple-provider.d.ts +1 -1
  271. package/lib/typescript/module/src/backends/winrt/winrt-backend.d.ts +7 -1
  272. package/lib/typescript/module/src/backends/winrt/winrt-backend.d.ts.map +1 -1
  273. package/lib/typescript/module/src/backends/winrt/winrt-boundary.d.ts +1 -0
  274. package/lib/typescript/module/src/backends/winrt/winrt-boundary.d.ts.map +1 -1
  275. package/lib/typescript/module/src/backends/winrt/winrt-gatt-operations.d.ts +1 -0
  276. package/lib/typescript/module/src/backends/winrt/winrt-gatt-operations.d.ts.map +1 -1
  277. package/lib/typescript/module/src/backends/winrt/winrt-handles.d.ts.map +1 -1
  278. package/lib/typescript/module/src/backends/winrt/winrt-provider.d.ts +1 -1
  279. package/lib/typescript/module/src/backends/winrt/winrt-security.d.ts +1 -1
  280. package/lib/typescript/module/src/backends/winrt/winrt-security.d.ts.map +1 -1
  281. package/lib/typescript/module/src/backends/winrt/winrt-subscription-runtime.d.ts +4 -1
  282. package/lib/typescript/module/src/backends/winrt/winrt-subscription-runtime.d.ts.map +1 -1
  283. package/lib/typescript/module/src/core/bounded-stream.d.ts +5 -3
  284. package/lib/typescript/module/src/core/bounded-stream.d.ts.map +1 -1
  285. package/lib/typescript/module/src/core/core-discovery.d.ts.map +1 -1
  286. package/lib/typescript/module/src/core/core-lifecycle-observer.d.ts +10 -0
  287. package/lib/typescript/module/src/core/core-lifecycle-observer.d.ts.map +1 -1
  288. package/lib/typescript/module/src/core/owned-bounded-stream.d.ts +3 -3
  289. package/lib/typescript/module/src/core/owned-bounded-stream.d.ts.map +1 -1
  290. package/lib/typescript/module/src/core/unified-ble-core.d.ts +6 -0
  291. package/lib/typescript/module/src/core/unified-ble-core.d.ts.map +1 -1
  292. package/lib/typescript/module/src/electron/connection-event-stream-registry.d.ts.map +1 -1
  293. package/lib/typescript/module/src/electron/renderer-stream-registry.d.ts.map +1 -1
  294. package/lib/typescript/module/src/implementation-version.d.ts +1 -1
  295. package/lib/typescript/module/src/ipc/manager.d.ts.map +1 -1
  296. package/lib/typescript/module/src/ipc/public-manager.d.ts.map +1 -1
  297. package/lib/typescript/module/src/manager/consumer-handles.d.ts +1 -0
  298. package/lib/typescript/module/src/manager/consumer-handles.d.ts.map +1 -1
  299. package/lib/typescript/module/src/native-protocol/rn-android-boundary.d.ts.map +1 -1
  300. package/lib/typescript/module/src/public/ble-manager.d.ts +13 -2
  301. package/lib/typescript/module/src/public/ble-manager.d.ts.map +1 -1
  302. package/lib/typescript/module/src/public/cleanup.d.ts +3 -1
  303. package/lib/typescript/module/src/public/cleanup.d.ts.map +1 -1
  304. package/lib/typescript/module/src/public/scan-state.d.ts +15 -0
  305. package/lib/typescript/module/src/public/scan-state.d.ts.map +1 -1
  306. package/lib/typescript/module/src/public/streams.d.ts +1 -0
  307. package/lib/typescript/module/src/public/streams.d.ts.map +1 -1
  308. package/lib/typescript/module/src/tck/first-party/winrt-tck-registration.d.ts +1 -0
  309. package/lib/typescript/module/src/tck/first-party/winrt-tck-registration.d.ts.map +1 -1
  310. package/lib/typescript/module/src/testing/deterministic/deterministic-stream.d.ts +4 -2
  311. package/lib/typescript/module/src/testing/deterministic/deterministic-stream.d.ts.map +1 -1
  312. package/lib/typescript/module/src/web/navigator-web-bluetooth-boundary.d.ts +9 -1
  313. package/lib/typescript/module/src/web/navigator-web-bluetooth-boundary.d.ts.map +1 -1
  314. package/lib/typescript/module/src/web/web-bluetooth-backend.d.ts +16 -0
  315. package/lib/typescript/module/src/web/web-bluetooth-backend.d.ts.map +1 -1
  316. package/lib/typescript/module/src/web/web-bluetooth-boundary.d.ts +11 -0
  317. package/lib/typescript/module/src/web/web-bluetooth-boundary.d.ts.map +1 -1
  318. package/lib/typescript/module/src/web.d.ts +1 -1
  319. package/lib/typescript/module/src/web.d.ts.map +1 -1
  320. package/native/PREBUILDS.json +9 -9
  321. package/native/electron/corebluetooth/binding.gyp +2 -1
  322. package/native/electron/corebluetooth/prebuilds/darwin-arm64/unified_ble_corebluetooth.node +0 -0
  323. package/native/electron/corebluetooth/prebuilds/darwin-x64/unified_ble_corebluetooth.node +0 -0
  324. package/native/electron/corebluetooth/src/addon.mm +170 -24
  325. package/native/electron/winrt/prebuilds/win32-arm64/unified_ble_winrt.node +0 -0
  326. package/native/electron/winrt/prebuilds/win32-x64/unified_ble_winrt.node +0 -0
  327. package/native/electron/winrt/src/addon.cpp +19 -0
  328. package/native/electron/winrt/src/winrt-boundary.inc +28 -3
  329. package/native/protocol/CMakeLists.txt +14 -0
  330. package/native/protocol/include/OwnedCoreBluetoothReadNotifyProvenance.hpp +61 -0
  331. package/native/tauri/src/btleplug_dispatcher.rs +1836 -243
  332. package/package.json +1 -1
  333. package/src/backend-contract/errors.ts +20 -0
  334. package/src/backend-contract/streams.ts +5 -1
  335. package/src/backends/bluez/bluez-backend-runtime.ts +8 -1
  336. package/src/backends/bluez/bluez-connection-runtime.ts +406 -27
  337. package/src/backends/bluez/bluez-property-waiters.ts +8 -6
  338. package/src/backends/bluez/bluez-runtime-models.ts +24 -0
  339. package/src/backends/bluez/bluez-runtime-types.ts +23 -1
  340. package/src/backends/bluez/bluez-scan-runtime.ts +170 -14
  341. package/src/backends/bluez/bluez-security.ts +10 -7
  342. package/src/backends/bluez/bluez-subscription-runtime.ts +41 -22
  343. package/src/backends/corebluetooth/corebluetooth-gatt-operations.ts +12 -5
  344. package/src/backends/corebluetooth/corebluetooth-read-notify-provenance.ts +133 -0
  345. package/src/backends/reactnative/react-native-android-security.ts +35 -17
  346. package/src/backends/winrt/winrt-backend.ts +27 -3
  347. package/src/backends/winrt/winrt-boundary.ts +1 -0
  348. package/src/backends/winrt/winrt-gatt-operations.ts +19 -5
  349. package/src/backends/winrt/winrt-handles.ts +2 -0
  350. package/src/backends/winrt/winrt-security.ts +55 -26
  351. package/src/backends/winrt/winrt-subscription-runtime.ts +201 -38
  352. package/src/core/bounded-stream.ts +27 -11
  353. package/src/core/core-discovery.ts +4 -0
  354. package/src/core/core-lifecycle-observer.ts +54 -1
  355. package/src/core/owned-bounded-stream.ts +5 -5
  356. package/src/core/unified-ble-core.ts +329 -118
  357. package/src/electron/connection-event-stream-registry.ts +3 -1
  358. package/src/electron/renderer-stream-registry.ts +3 -1
  359. package/src/implementation-version.ts +1 -1
  360. package/src/ipc/manager.ts +49 -2
  361. package/src/ipc/public-manager.ts +19 -7
  362. package/src/manager/consumer-handles.ts +1 -0
  363. package/src/native-protocol/rn-android-boundary.ts +10 -0
  364. package/src/public/ble-manager.ts +172 -58
  365. package/src/public/cleanup.ts +1 -1
  366. package/src/public/scan-state.ts +60 -0
  367. package/src/public/streams.ts +15 -8
  368. package/src/tck/first-party/winrt-tck-registration.ts +6 -0
  369. package/src/testing/deterministic/deterministic-stream.ts +19 -6
  370. package/src/web/navigator-web-bluetooth-boundary.ts +50 -0
  371. package/src/web/web-bluetooth-backend.ts +167 -6
  372. package/src/web/web-bluetooth-boundary.ts +11 -0
  373. package/src/web.ts +1 -0
package/CHANGELOG.md CHANGED
@@ -6,6 +6,116 @@ All notable changes to `unified-ble-manager` are documented here.
6
6
 
7
7
  No changes yet.
8
8
 
9
+ ## [4.0.25] - 2026-09-05
10
+
11
+ ### Fixes
12
+
13
+ - Tauri: separate live correlation capacity from 30s replay tombstones. Routed
14
+ cleanup still admits after a full replay window. Live exhaustion is retryable
15
+ backpressure, not `protocol.violation`. Replay of an old correlation still
16
+ rejects.
17
+ - Tauri: a native connect that completes after its caller/lease is gone gets an
18
+ orphan cleanup owner before compensating disconnect. Reservation and owner
19
+ release together, and only for the matching generation.
20
+ - Tauri: scan-stream failure keeps the scan owner in a stopping state until
21
+ native stop settles. A failed stop stays retryable and blocks a second scan.
22
+ Event and poll emit failures carry structured native diagnostics on the
23
+ shared `source-failed` terminal without inventing drop counters (#173).
24
+ - Tauri: post-await lease/generation checks on unsubscribe and scan stop.
25
+ Old-generation resources quarantine instead of attaching to a replacement
26
+ caller. Compensating unsubscribe after subscribe success uses the same orphan
27
+ owner instead of discarding the outcome.
28
+ - Tauri: overlapping stop requests for the same scan join one native
29
+ `stop_scan()`. Completions carry an attempt token, so a late stop of scan A
30
+ cannot take scan B’s stopping owner.
31
+ - Core: retain unadopted scan/connect leases when stale-admission `stop`/`release`
32
+ fails, and retry that exact lease on later manager cleanup. Aborted, destroyed,
33
+ or deadline-expired callers settle promptly; native work stays owned until
34
+ adopted or compensated.
35
+ - Core: coalesce concurrent `rediscoverGatt` waiters onto one replacement after
36
+ a shared in-flight discovery settles. A starter abort or timeout does not fail
37
+ a sibling waiter with its own admission.
38
+ - Public scan observation overflow is subscriber-local. Consuming only `events`
39
+ at full speed with `duplicates: 'all'` and `overflowPolicy: 'error'` no longer
40
+ terminates the scan because an unused observation queue filled.
41
+ - Drop-policy public scan overflow (`balanced` / `latest` / drop-*) reports an
42
+ overflow notice and keeps scanning. The radio stays up. `overflowPolicy:
43
+ 'error'` (`lossless-bounded`) still fail-closes the consumed view as
44
+ `failed`/`overflow`; physical `stop()` remains cleanup.
45
+ - In-process and IPC scan sessions leave `active` when the host source ends
46
+ without `stop()`. An already-terminal source never publishes `active`; the
47
+ first state is the projected terminal (`failed` for `source-failed` /
48
+ `connection-lost` / `overflow`, `stopped` for ordinary close). That event is
49
+ ended delivery; `stop()` remains the physical cleanup path. The data pump
50
+ still drains advertisements already accepted by `finishWithReason` before
51
+ the observation terminal. Structured `source-failed` errors survive scan-state
52
+ binding and reach `scan.observations`.
53
+ - BlueZ `StopDiscovery` / `StopNotify` release this client’s session even if
54
+ another D-Bus client keeps `Discovering` / `Notifying` true.
55
+ - BlueZ `StartNotify` confirmation failure after native accept keeps a retryable
56
+ `StopNotify` owner instead of orphaning the notify session. A late enablement
57
+ success does not promote `removing` / `enabling-failed` back to `ready`.
58
+ - BlueZ `SetDiscoveryFilter`, `StartDiscovery`, and `ConnectDevice` honor caller
59
+ deadline/abort without dropping a later native allocation.
60
+ - BlueZ keeps a retryable compensating `Disconnect` when `ConnectDevice`
61
+ succeeds after the caller is gone. A failed first Disconnect is retried on
62
+ destroy instead of being logged and forgotten.
63
+ - BlueZ address-connect fallback checks scan-plan compatibility, waits out a
64
+ stopping scan, and widens an excluding live scan for concurrent address
65
+ waiters, restoring the owner filter only after the last borrower leaves
66
+ instead of waiting under a filter that cannot observe the target.
67
+ - Apple CoreBluetooth rejects an independent GATT read while that characteristic
68
+ is notifying, because `didUpdateValueFor` cannot distinguish a read response
69
+ from a notification. Electron/Node CoreBluetooth uses the same policy. A read
70
+ admitted while idle still completes from its ATT response if CCCD enable
71
+ races it; overlapping reads and subscribe-while-read are rejected; fused
72
+ values that cannot be attributed are dropped. Remaining native reject codes
73
+ (iOS 1031/1011/1032, Electron 413/414/415) map to `gatt.read-failed` or
74
+ `gatt.subscribe-failed`, not `platform.failure`.
75
+ - Android `setCharacteristicNotification(...) == false` is a local registration
76
+ failure, not proven link loss. Generation-matched disconnect and GATT status 19
77
+ remain the physical-loss authority. The 4.0.22 250 ms CCCD/disconnect two-signal
78
+ arbitration is unchanged. Notifications that arrive after the CCCD write and
79
+ before `onDescriptorWrite` SUCCESS are staged and flushed to admitted
80
+ subscribers. A later same-generation CCCD SUCCESS claims the arbiter instead
81
+ of leaving the peer enabled after a failed public subscribe.
82
+ - WinRT stages `ValueChanged` notifications that arrive before CCCD enablement
83
+ completes, then flushes them once and in order only to admitted subscribers.
84
+ Staging continues until every pending waiter is admitted, and a per-subscriber
85
+ overflow does not discard siblings’ pending buffer.
86
+ - WinRT subscription invalidation waits are bounded. If native `startNotify`
87
+ never settles, disconnect still completes within the cleanup budget. A late
88
+ enable issues a compensating `stopNotify` when the native CCCD key is vacant,
89
+ and skips that disable only when a replacement generation already occupies it.
90
+ Native notification map entries are keyed by connection generation.
91
+ - `cancelPairing()` honors abort and deadline on Android, WinRT, and BlueZ.
92
+ Admission runs before native cancellation. A timed-out wait is not reported as
93
+ `'cancelled'`. An abandoned waiter leaves the in-flight pairing owned.
94
+ `pair()` abort/deadline keeps the pairing owned until the native result;
95
+ a later native `paired`/`rejected` is authoritative for both `pair()` and
96
+ `cancelPairing()`.
97
+ - Web adapter `watchState()` follows browser `availabilitychanged` instead of
98
+ staying stale until another API samples availability. When that event is
99
+ missing, watches share one bounded `getAvailability()` poll. A completed
100
+ availability sample is applied unless a newer sample has already been applied,
101
+ so an outstanding later probe cannot make `attach()` throw `adapter.unavailable`
102
+ from a stale unavailable cache.
103
+
104
+ ## [4.0.24] - 2026-09-05
105
+
106
+ ### Fixes
107
+
108
+ - Arbitrate an Android characteristic-write callback failure against an
109
+ already-in-flight disconnect callback. A provisional write status such as
110
+ 133 is retained for a bounded 250 ms evidence window, allowing the
111
+ authoritative connection-state callback to report typed `connection.lost`
112
+ without relabeling an isolated status 133 as link loss.
113
+ - Preserve the Android GATT operation and status for synchronous and
114
+ asynchronous characteristic-write failures that are not proven link loss.
115
+ - Make exact and UUID-keyed pending-write cleanup identity-safe so stale
116
+ teardown and late callbacks cannot settle a replacement write or remove its
117
+ payload.
118
+
9
119
  ## [4.0.23] - 2026-09-05
10
120
 
11
121
  ### Fixes
@@ -239,7 +349,7 @@ hardware reproduced a later callback ordering that required the 4.0.22 fix.
239
349
 
240
350
  ### Changed behaviour
241
351
 
242
- - `cancelPairing()` reports what the cancellation **achieved**, not what it requested, and can no longer contradict the pairing it cancelled. Every backend answered `'cancelled'` unconditionally the moment the cancel was dispatched, so a cancellation that lost the race told the caller no bond exists while one did — and a caller who believes that never looks again. It now reads the in-flight pairing's own result rather than forming a second opinion, which is what makes the two calls incapable of disagreeing: there is one source of truth and the cancellation reads it. `SecurityCancelPairingResult` gains the words to say so — `'paired'` when the bond completed before the cancellation arrived, and `'rejected'` (carrying the peer's reason) when the peer refused, because claiming credit for stopping something that stopped itself is the same substitution with the arrow reversed. A pairing that *fails* is not given an invented outcome: `cancelPairing()` rejects with the error the pairing rejected with. `'paired'` deliberately matches `SecurityPairResult`'s `'paired'` — a bond exists as a result of this operation — and is not `'already-paired'`, which in that type means the peer was bonded *before* the call; one word, one meaning, in both types. Applies to BlueZ, Android, WinRT **and** the deterministic `/testing` backend, since a mock that answers differently from every real radio lets a consumer's suite pass against a contract no device honours. WinRT additionally short-circuited *upstream* of the shared mapper — when its dispatcher reported the operation already terminal, which is exactly the lost race, it answered `'not-pairing'`, contradicting its own `pair()` and making that word mean both "there was nothing to stop" and "it was already over" (#159).
352
+ - `cancelPairing()` reports what the cancellation **achieved**, not what it requested, and can no longer contradict the pairing it cancelled. Every backend answered `'cancelled'` unconditionally the moment the cancel was dispatched, so a cancellation that lost the race told the caller no bond exists while one did — and a caller who believes that never looks again. It now reads the in-flight pairing's own result rather than forming a second opinion, which is what makes the two calls incapable of disagreeing: there is one source of truth and the cancellation reads it. `SecurityCancelPairingResult` gains the words to say so — `'paired'` when the bond completed before the cancellation arrived, and `'rejected'` (carrying the peer's reason) when the peer refused, because claiming credit for stopping something that stopped itself is the same substitution with the arrow reversed. A pairing that _fails_ is not given an invented outcome: `cancelPairing()` rejects with the error the pairing rejected with. `'paired'` deliberately matches `SecurityPairResult`'s `'paired'` — a bond exists as a result of this operation — and is not `'already-paired'`, which in that type means the peer was bonded _before_ the call; one word, one meaning, in both types. Applies to BlueZ, Android, WinRT **and** the deterministic `/testing` backend, since a mock that answers differently from every real radio lets a consumer's suite pass against a contract no device honours. WinRT additionally short-circuited _upstream_ of the shared mapper — when its dispatcher reported the operation already terminal, which is exactly the lost race, it answered `'not-pairing'`, contradicting its own `pair()` and making that word mean both "there was nothing to stop" and "it was already over" (#159).
243
353
 
244
354
  ### Additions
245
355
 
@@ -247,13 +357,13 @@ hardware reproduced a later callback ordering that required the 4.0.22 fix.
247
357
 
248
358
  This package never acquires that privilege: it opens no management socket, shells out to nothing, and does not assume it is root. A library that silently escalates hands an application capabilities its author did not choose and cannot audit. The **host** supplies the operation via `BluezBackendProviderOptions.pairingGeneration`, which makes the escalation visible in the application that opted into it. Omit it and `'require'`/`'disallow'` keep failing closed exactly as in 4.0.6 — the default posture is unchanged.
249
359
 
250
- Three properties of the kernel setting a host must accept, documented in `docs/BONDING.md` rather than discovered: it is **adapter-wide**, not per-pairing, so every pairing on that controller uses the selected generation while it is held; it **outlives the process**, because the kernel keeps it until something sets it back; and a **failed restore never changes the pairing's outcome** — a bond that was created is reported as created, with the restore failure reported separately, because leaving a controller in LE Legacy and telling a caller they are not bonded are both serious and are different facts. Concurrent *directed* pairings on one adapter are serialised so they cannot corrupt each other's restore value; an undirected (`'prefer'`) pairing does not take that lock and uses whichever generation is held, which `docs/BONDING.md` states beside the option.
360
+ Three properties of the kernel setting a host must accept, documented in `docs/BONDING.md` rather than discovered: it is **adapter-wide**, not per-pairing, so every pairing on that controller uses the selected generation while it is held; it **outlives the process**, because the kernel keeps it until something sets it back; and a **failed restore never changes the pairing's outcome** — a bond that was created is reported as created, with the restore failure reported separately, because leaving a controller in LE Legacy and telling a caller they are not bonded are both serious and are different facts. Concurrent _directed_ pairings on one adapter are serialised so they cannot corrupt each other's restore value; an undirected (`'prefer'`) pairing does not take that lock and uses whichever generation is held, which `docs/BONDING.md` states beside the option.
251
361
 
252
362
  `security:pairing-generation` joins the capability catalog and is reported by the instantiated backend at runtime: `unsupported` with the platform reason when no operation was supplied, `limited` when one was — so two BlueZ backends on one machine legitimately answer differently. Evidence is deterministic tests only; this is **not** physical-radio proof, and the label says so (#144).
253
363
 
254
364
  ### Fixes
255
365
 
256
- - A Tauri connect that succeeds physically but is then denied admission no longer strands the peer. Both compensation branches — the caller vanished, and the caller's lease went stale — removed the peer reservation *before* attempting the disconnect and then discarded its result with `.ok()`. If that disconnect failed or hung while the peripheral stayed connected, the result was a connected peer with no owner and no handle: nothing could reach it, nothing could retry it, and the caller was told only that admission was denied. Compensation now follows the rule the explicit disconnect already follows — a bounded wait, a fresh state reading, and a D-Bus error naming a vanished device object read as evidence of release rather than as a failed question — and surrenders the reservation only when the platform proves the link is down. Genuine indeterminacy keeps it so a retry can reclaim the peer, and the admission error carries what compensation could not undo, which is the difference between "try again" and "a peer is stranded" (#146).
366
+ - A Tauri connect that succeeds physically but is then denied admission no longer strands the peer. Both compensation branches — the caller vanished, and the caller's lease went stale — removed the peer reservation _before_ attempting the disconnect and then discarded its result with `.ok()`. If that disconnect failed or hung while the peripheral stayed connected, the result was a connected peer with no owner and no handle: nothing could reach it, nothing could retry it, and the caller was told only that admission was denied. Compensation now follows the rule the explicit disconnect already follows — a bounded wait, a fresh state reading, and a D-Bus error naming a vanished device object read as evidence of release rather than as a failed question — and surrenders the reservation only when the platform proves the link is down. Genuine indeterminacy keeps it so a retry can reclaim the peer, and the admission error carries what compensation could not undo, which is the difference between "try again" and "a peer is stranded" (#146).
257
367
 
258
368
  ### Known limitations
259
369
 
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.23`. 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.
5
+ This source targets `4.0.25`. 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
@@ -18,7 +18,7 @@ root import does not pick a radio. Package SemVer and backend support labels are
18
18
  independent: each radio backend keeps its evidence-derived label. See
19
19
  [`docs/PLATFORMS.md`](docs/PLATFORMS.md).
20
20
 
21
- This source tree is versioned `4.0.23`. Install the exact version shown in the npm
21
+ This source tree is versioned `4.0.25`. Install the exact version shown in the npm
22
22
  registry. During release preparation, the version in `package.json` can be ahead
23
23
  of npm until the matching tag-driven workflow publishes it; the registry and
24
24
  GitHub release remain authoritative.
@@ -243,7 +243,7 @@ Web Bluetooth replaces the scan with `ble.choose(...)` from a user gesture. The
243
243
 
244
244
  | Member | Use |
245
245
  | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
246
- | `observations` | Bounded stream: `value`, `overflow`, or `terminal` |
246
+ | `observations` | Bounded stream: `value`, `overflow`, or `terminal`. Drop-policy overflow means ads were missed; the scan stays up. `error` fail-closes. |
247
247
  | `events` | Optional derived current-view events: `observed` and monotonic `lost` (`observation.reportLostAfterMs`); unsupported host façades reject this option |
248
248
  | `plan` | Host-owned native/residual planning diagnostics, or `null` when this host has no planner |
249
249
  | `stop()` | End the scan and return a cleanup receipt. `find` already does this. |
package/RELEASE.md CHANGED
@@ -82,8 +82,9 @@ Active `4.0.0-rc.*` release-train candidates publish to npm `latest` so a bare `
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
84
  once tagged. Stable `4.0.0` through `4.0.20` are immutable. The unpublished
85
- `v4.0.21` tag is also immutable after its cancelled workflow. `4.0.22` is immutable published history. `4.0.23` is the
86
- current train head.
85
+ `v4.0.21` tag is also immutable after its cancelled workflow. `4.0.22`,
86
+ `4.0.23`, and `4.0.24` are immutable tagged history. This branch prepares
87
+ `4.0.25`.
87
88
 
88
89
  ```sh
89
90
  release_candidate=4.0.0-rc.N
@@ -111,6 +112,22 @@ The first stable tag `v4.0.0` is immutable published history. Do not recreate or
111
112
  git tag -a v4.0.0 -m "v4.0.0"
112
113
  ```
113
114
 
115
+ ## Releasing 4.0.25
116
+
117
+ Release `v4.0.25` only from the exact current `main` commit after its canonical
118
+ CI succeeds. Verify `package.json` is `4.0.25`, the worktree is clean, and
119
+ release-note extraction finds `## [4.0.25]`. Push a new annotated `v4.0.25`
120
+ tag; never move the immutable `v4.0.24` tag. Follow the required local validation,
121
+ publish workflow, and registry verification below.
122
+
123
+ ## Releasing 4.0.24
124
+
125
+ Release `v4.0.24` only from the exact current `main` commit after its canonical
126
+ CI succeeds. Verify `package.json` is `4.0.24`, the worktree is clean, and
127
+ release-note extraction finds `## [4.0.24]`. Push a new annotated `v4.0.24`
128
+ tag; never move an earlier immutable tag. Follow the required local validation,
129
+ publish workflow, and registry verification below.
130
+
114
131
  ## Releasing 4.0.23
115
132
 
116
133
  Release `v4.0.23` only from the exact current `main` commit after its canonical
@@ -483,7 +500,7 @@ a green publish job and a package a consumer can actually install are not the
483
500
  same claim.
484
501
 
485
502
  ```sh
486
- version=4.0.23
503
+ version=4.0.25
487
504
 
488
505
  npm view "unified-ble-manager@$version" version
489
506
  npm view unified-ble-manager dist-tags --json
package/SBOM.cdx.json CHANGED
@@ -6,15 +6,15 @@
6
6
  "metadata": {
7
7
  "component": {
8
8
  "type": "library",
9
- "bom-ref": "pkg:npm/unified-ble-manager@4.0.23",
9
+ "bom-ref": "pkg:npm/unified-ble-manager@4.0.25",
10
10
  "name": "unified-ble-manager",
11
- "version": "4.0.23",
11
+ "version": "4.0.25",
12
12
  "licenses": [
13
13
  {
14
14
  "expression": "Apache-2.0"
15
15
  }
16
16
  ],
17
- "purl": "pkg:npm/unified-ble-manager@4.0.23"
17
+ "purl": "pkg:npm/unified-ble-manager@4.0.25"
18
18
  },
19
19
  "properties": [
20
20
  {
@@ -537,7 +537,7 @@
537
537
  "dependsOn": []
538
538
  },
539
539
  {
540
- "ref": "pkg:npm/unified-ble-manager@4.0.23",
540
+ "ref": "pkg:npm/unified-ble-manager@4.0.25",
541
541
  "dependsOn": [
542
542
  "pkg:npm/%40babel/runtime@7.29.7",
543
543
  "pkg:npm/node-addon-api@8.9.0",
@@ -4,7 +4,7 @@
4
4
  "schemaVersion": "1.0.0",
5
5
  "package": {
6
6
  "name": "unified-ble-manager",
7
- "version": "4.0.23"
7
+ "version": "4.0.25"
8
8
  },
9
9
  "source": {
10
10
  "method": "pnpm-lock production graph with installed-manifest license audit",
@@ -89,6 +89,9 @@ android {
89
89
  kotlinOptions {
90
90
  jvmTarget = "17"
91
91
  }
92
+ testOptions {
93
+ unitTests.returnDefaultValues = true
94
+ }
92
95
  externalNativeBuild {
93
96
  cmake {
94
97
  path file("src/main/jni/CMakeLists.txt")
@@ -105,6 +108,7 @@ dependencies {
105
108
  implementation "com.facebook.react:react-android"
106
109
  implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
107
110
  testImplementation "junit:junit:4.13.2"
111
+ testImplementation "org.mockito:mockito-core:5.14.2"
108
112
  }
109
113
 
110
114
  react {
@@ -30,6 +30,7 @@ class UnifiedBleProtocolAndroidDispatcher(
30
30
  private val establishedConnections = ConcurrentHashMap<String, ProtocolWireRecord>()
31
31
  private val activeDatabases = ConcurrentHashMap<String, ProtocolWireRecord>()
32
32
  private val activeSubscriptions = ConcurrentHashMap<String, SubscriptionRoute>()
33
+ private val pendingSubscriptions = ConcurrentHashMap<String, SubscriptionRoute>()
33
34
  private val radioOperationIds = ConcurrentHashMap<String, Long>()
34
35
  private val activeScanCommand = AtomicReference<ProtocolWireRecord?>(null)
35
36
  private val cancelledScanCommands = ConcurrentHashMap<String, ProtocolWireRecord>()
@@ -78,11 +79,7 @@ class UnifiedBleProtocolAndroidDispatcher(
78
79
  activeDatabases.remove(deviceKey)
79
80
  failPendingCommandsForDevice(deviceKey, "Android GATT link was lost")
80
81
  if (established != null) {
81
- activeSubscriptions.entries.forEach { entry ->
82
- if (entry.value.endpoint.deviceId.equals(deviceId, ignoreCase = true)) {
83
- activeSubscriptions.remove(entry.key, entry.value)
84
- }
85
- }
82
+ clearSubscriptionRoutesForDevice(deviceId)
86
83
  emitConnectionLost(established, status)
87
84
  }
88
85
  }
@@ -134,7 +131,7 @@ class UnifiedBleProtocolAndroidDispatcher(
134
131
  }
135
132
  }
136
133
  radio.onProtocolNotification = { deviceId, characteristic, value ->
137
- activeSubscriptions.values
134
+ (pendingSubscriptions.values.asSequence() + activeSubscriptions.values.asSequence())
138
135
  .filter { route -> route.matches(deviceId, characteristic, radio) }
139
136
  .forEach { route ->
140
137
  UnifiedBleProtocolJsiBinding.emitNotification(nativeHandle, route.subscriptionId, value)
@@ -166,6 +163,7 @@ class UnifiedBleProtocolAndroidDispatcher(
166
163
  }
167
164
  establishedConnections.clear()
168
165
  activeDatabases.clear()
166
+ pendingSubscriptions.clear()
169
167
  activeSubscriptions.clear()
170
168
  }
171
169
 
@@ -272,6 +270,7 @@ class UnifiedBleProtocolAndroidDispatcher(
272
270
  pendingConnects.clear()
273
271
  establishedConnections.clear()
274
272
  activeDatabases.clear()
273
+ pendingSubscriptions.clear()
275
274
  activeSubscriptions.clear()
276
275
  activeScanCommand.set(null)
277
276
  }
@@ -409,7 +408,9 @@ class UnifiedBleProtocolAndroidDispatcher(
409
408
  ) { result ->
410
409
  result.fold(
411
410
  onSuccess = { emitSuccess(command, "write") },
412
- onFailure = { error -> emitFailure(command, "writeFailed", error.message ?: "Android GATT write failed") }
411
+ onFailure = { error ->
412
+ emitGattOperationFailure(command, "writeFailed", error, "Android GATT write failed")
413
+ }
413
414
  )
414
415
  }
415
416
  radioOperationIds[operationKey(command)] = radioOperationId
@@ -599,6 +600,14 @@ class UnifiedBleProtocolAndroidDispatcher(
599
600
  return
600
601
  }
601
602
  val endpoint = characteristicEndpoint(command.requiredRecord(4))
603
+ val route = SubscriptionRoute(
604
+ subscriptionId,
605
+ endpoint,
606
+ command.optionalString(21)
607
+ )
608
+ if (enable) {
609
+ pendingSubscriptions[subscriptionId] = route
610
+ }
602
611
  val radioOperationId = radio.setNotifyExact(
603
612
  endpoint.deviceId,
604
613
  endpoint.serviceUuid,
@@ -611,6 +620,7 @@ class UnifiedBleProtocolAndroidDispatcher(
611
620
  result.fold(
612
621
  onSuccess = {
613
622
  if (!isPending(command)) {
623
+ pendingSubscriptions.remove(subscriptionId, route)
614
624
  if (enable) {
615
625
  radio.setNotifyExact(
616
626
  endpoint.deviceId,
@@ -632,17 +642,16 @@ class UnifiedBleProtocolAndroidDispatcher(
632
642
  return@fold
633
643
  }
634
644
  if (enable) {
635
- activeSubscriptions[subscriptionId] = SubscriptionRoute(
636
- subscriptionId,
637
- endpoint,
638
- command.optionalString(21)
639
- )
645
+ pendingSubscriptions.remove(subscriptionId, route)
646
+ activeSubscriptions[subscriptionId] = route
640
647
  } else {
648
+ pendingSubscriptions.remove(subscriptionId)
641
649
  activeSubscriptions.remove(subscriptionId)
642
650
  }
643
651
  emitSuccess(command, if (enable) "subscribed" else "unsubscribed")
644
652
  },
645
653
  onFailure = { error ->
654
+ pendingSubscriptions.remove(subscriptionId, route)
646
655
  emitGattOperationFailure(command, "subscriptionFailed", error, "Android CCCD operation failed")
647
656
  }
648
657
  )
@@ -663,6 +672,7 @@ class UnifiedBleProtocolAndroidDispatcher(
663
672
  pendingConnects.clear()
664
673
  establishedConnections.clear()
665
674
  activeDatabases.clear()
675
+ pendingSubscriptions.clear()
666
676
  activeSubscriptions.clear()
667
677
  if (result.isSuccessful) {
668
678
  activeScanCommand.set(null)
@@ -735,7 +745,9 @@ class UnifiedBleProtocolAndroidDispatcher(
735
745
  }
736
746
  if (commandKind == "subscribe") {
737
747
  val endpoint = characteristicEndpoint(command.requiredRecord(4))
738
- activeSubscriptions.remove(command.requiredString(7))
748
+ val subscriptionId = command.requiredString(7)
749
+ pendingSubscriptions.remove(subscriptionId)
750
+ activeSubscriptions.remove(subscriptionId)
739
751
  radio.setNotifyExact(
740
752
  endpoint.deviceId,
741
753
  endpoint.serviceUuid,
@@ -880,8 +892,8 @@ class UnifiedBleProtocolAndroidDispatcher(
880
892
  androidGattStatus: Int? = null
881
893
  ) {
882
894
  // Claim before constructing or emitting the terminal. Link loss can race a
883
- // native CCCD callback; a check followed by a later claim lets both paths
884
- // publish a result for the same operation.
895
+ // native GATT operation callback; a check followed by a later claim lets
896
+ // both paths publish a result for the same operation.
885
897
  if (!claimExactPendingCommand(pendingCommands, operationKey(command), command)) return
886
898
  val errorFields = mutableMapOf<Int, ProtocolWireValue>(
887
899
  1 to ProtocolWireValue.StringValue(code),
@@ -1135,6 +1147,11 @@ class UnifiedBleProtocolAndroidDispatcher(
1135
1147
  }
1136
1148
 
1137
1149
  private fun clearSubscriptionRoutesForDevice(deviceId: String) {
1150
+ pendingSubscriptions.entries.forEach { entry ->
1151
+ if (entry.value.endpoint.deviceId.equals(deviceId, ignoreCase = true)) {
1152
+ pendingSubscriptions.remove(entry.key, entry.value)
1153
+ }
1154
+ }
1138
1155
  activeSubscriptions.entries.forEach { entry ->
1139
1156
  if (entry.value.endpoint.deviceId.equals(deviceId, ignoreCase = true)) {
1140
1157
  activeSubscriptions.remove(entry.key, entry.value)