@magicred-1/react-native-lxmf 0.2.40 → 0.2.43

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.
@@ -33,6 +33,10 @@ class LxmfModule : Module() {
33
33
  "onMessageReceived",
34
34
  "onAnnounceReceived",
35
35
  "onStatusChanged",
36
+ "onRpcResponse",
37
+ "onMessageQueued",
38
+ "onMessageDelivered",
39
+ "onMessageFailed",
36
40
  "onLog",
37
41
  "onError",
38
42
  "onOutgoingPacket"
@@ -127,6 +131,11 @@ class LxmfModule : Module() {
127
131
  nativeFetchMessages(limit)
128
132
  }
129
133
 
134
+ // Beacon RPC
135
+ AsyncFunction("beaconRpc") { destHashHex: String, method: String, paramsJson: String? ->
136
+ nativeBeaconRpc(destHashHex, method, paramsJson).toDouble()
137
+ }
138
+
130
139
  // Configuration
131
140
  Function("setLogLevel") { level: Int ->
132
141
  nativeSetLogLevel(level) == 0
@@ -158,6 +167,14 @@ class LxmfModule : Module() {
158
167
  Function("bleUnpairedRNodeCount") {
159
168
  nusManager?.unpairedRNodeCount() ?: 0
160
169
  }
170
+
171
+ Function("getNusUnpairedRNodes") {
172
+ nusManager?.unpairedRNodesJson() ?: "[]"
173
+ }
174
+
175
+ Function("pairNusRNode") { mac: String ->
176
+ nusManager?.pairRNode(mac) ?: false
177
+ }
161
178
  }
162
179
 
163
180
  private fun drainAndEmitEvents() {
@@ -183,6 +200,10 @@ class LxmfModule : Module() {
183
200
  "packetReceived" -> "onPacketReceived"
184
201
  "txReceived" -> "onTxReceived"
185
202
  "beaconDiscovered" -> "onBeaconDiscovered"
203
+ "rpcResponse" -> "onRpcResponse"
204
+ "messageQueued" -> "onMessageQueued"
205
+ "messageDelivered" -> "onMessageDelivered"
206
+ "messageFailed" -> "onMessageFailed"
186
207
  "log" -> "onLog"
187
208
  "error" -> "onError"
188
209
  else -> null
@@ -222,6 +243,7 @@ class LxmfModule : Module() {
222
243
  private external fun nativeGetStatus(): String?
223
244
  private external fun nativeGetBeacons(): String?
224
245
  private external fun nativeFetchMessages(limit: Int): String?
246
+ private external fun nativeBeaconRpc(destHashHex: String, method: String, paramsJson: String?): Long
225
247
  private external fun nativeSetLogLevel(level: Int): Int
226
248
  private external fun nativeAbiVersion(): Int
227
249
 
@@ -2,12 +2,17 @@ package expo.modules.lxmf
2
2
 
3
3
  import android.bluetooth.*
4
4
  import android.bluetooth.le.*
5
+ import android.content.BroadcastReceiver
5
6
  import android.content.Context
7
+ import android.content.Intent
8
+ import android.content.IntentFilter
6
9
  import android.os.Build
7
10
  import android.os.Handler
8
11
  import android.os.Looper
9
12
  import android.os.ParcelUuid
10
13
  import android.util.Log
14
+ import org.json.JSONArray
15
+ import org.json.JSONObject
11
16
  import java.util.UUID
12
17
 
13
18
  private const val NUS_TAG = "LxmfNus"
@@ -43,8 +48,8 @@ class NusManager(
43
48
  private val txChars = mutableMapOf<String, BluetoothGattCharacteristic>()
44
49
  // Negotiated write MTU per connection — used for TX chunking
45
50
  private val writeMtu = mutableMapOf<String, Int>()
46
- // MACs found in scan but not OS-paired — UI should prompt user to pair in Settings
47
- private val discoveredUnpaired = mutableSetOf<String>()
51
+ // Devices found in scan but not OS-paired — keyed by MAC for O(1) lookup
52
+ private val discoveredUnpaired = mutableMapOf<String, BluetoothDevice>()
48
53
  // MACs currently attempting connection
49
54
  private val connecting = mutableSetOf<String>()
50
55
 
@@ -58,17 +63,49 @@ class NusManager(
58
63
  private const val DEFAULT_WRITE_MTU = 20 // conservative BLE default
59
64
  }
60
65
 
66
+ // ── Bond state receiver ───────────────────────────────────────────────────
67
+
68
+ private val bondReceiver = object : BroadcastReceiver() {
69
+ override fun onReceive(ctx: Context, intent: Intent) {
70
+ if (intent.action != BluetoothDevice.ACTION_BOND_STATE_CHANGED) return
71
+ val device: BluetoothDevice? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
72
+ intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE, BluetoothDevice::class.java)
73
+ } else {
74
+ @Suppress("DEPRECATION")
75
+ intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
76
+ }
77
+ val mac = device?.address ?: return
78
+ val bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE)
79
+ if (bondState == BluetoothDevice.BOND_BONDED) {
80
+ Log.i(NUS_TAG, "NUS: $mac bonded — connecting")
81
+ discoveredUnpaired.remove(mac)
82
+ if (isRunning && mac !in connections && mac !in connecting) {
83
+ connecting.add(mac)
84
+ device.connectGatt(context, false, gattCallback, BluetoothDevice.TRANSPORT_LE)
85
+ }
86
+ }
87
+ }
88
+ }
89
+
61
90
  // ── Lifecycle ────────────────────────────────────────────────────────────
62
91
 
63
92
  fun start() {
64
93
  if (isRunning) return
65
94
  isRunning = true
95
+ val filter = IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED)
96
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
97
+ context.registerReceiver(bondReceiver, filter, Context.RECEIVER_NOT_EXPORTED)
98
+ } else {
99
+ @Suppress("UnspecifiedRegisterReceiverFlag")
100
+ context.registerReceiver(bondReceiver, filter)
101
+ }
66
102
  startScanning()
67
103
  Log.i(NUS_TAG, "NusManager started")
68
104
  }
69
105
 
70
106
  fun stop() {
71
107
  isRunning = false
108
+ try { context.unregisterReceiver(bondReceiver) } catch (_: Exception) {}
72
109
  stopScanning()
73
110
  connections.values.forEach { it.disconnect(); it.close() }
74
111
  connections.clear()
@@ -79,9 +116,41 @@ class NusManager(
79
116
  Log.i(NUS_TAG, "NusManager stopped")
80
117
  }
81
118
 
82
- /** Number of RNodes visible in scan but not yet OS-paired (mirrors iOS discoveredUnpairedRNodes.count). */
119
+ /** Number of RNodes visible in scan but not yet OS-paired. */
83
120
  fun unpairedRNodeCount(): Int = discoveredUnpaired.size
84
121
 
122
+ /** JSON array of unpaired RNodes: [{"mac":"AA:BB:...","name":"RNode_1234"},...] */
123
+ fun unpairedRNodesJson(): String {
124
+ val arr = JSONArray()
125
+ discoveredUnpaired.values.forEach { device ->
126
+ arr.put(JSONObject().apply {
127
+ put("mac", device.address)
128
+ put("name", device.name ?: "")
129
+ })
130
+ }
131
+ return arr.toString()
132
+ }
133
+
134
+ /**
135
+ * Initiate OS pairing with an unpaired RNode. Shows system Bluetooth pairing dialog.
136
+ * Returns true if bond initiation succeeded (or device is already bonded and connecting).
137
+ * Requires BLUETOOTH_CONNECT permission (API 31+).
138
+ */
139
+ fun pairRNode(mac: String): Boolean {
140
+ val device = discoveredUnpaired[mac]
141
+ ?: try { adapter?.getRemoteDevice(mac) } catch (_: Exception) { null }
142
+ ?: return false
143
+ return if (device.bondState == BluetoothDevice.BOND_BONDED) {
144
+ if (mac !in connections && mac !in connecting) {
145
+ connecting.add(mac)
146
+ device.connectGatt(context, false, gattCallback, BluetoothDevice.TRANSPORT_LE)
147
+ }
148
+ true
149
+ } else {
150
+ device.createBond()
151
+ }
152
+ }
153
+
85
154
  /** Number of fully connected and ready RNodes. */
86
155
  fun connectedRNodeCount(): Int = connections.size
87
156
 
@@ -106,15 +175,27 @@ class NusManager(
106
175
  data: ByteArray,
107
176
  mtu: Int,
108
177
  ) {
178
+ // Android GATT is single-op — only one writeCharacteristic in flight at a time.
179
+ // For NUS, packets are bounded by NusInterface.mtu (244 B) so KISS frames fit in
180
+ // one ATT PDU after MTU negotiation (514 B). The loop is a safety net only.
109
181
  var offset = 0
110
182
  while (offset < data.size) {
111
183
  val end = minOf(offset + mtu, data.size)
112
184
  val chunk = data.copyOfRange(offset, end)
113
- @Suppress("DEPRECATION")
114
- char.value = chunk
115
- char.writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE
116
- @Suppress("DEPRECATION")
117
- gatt.writeCharacteristic(char)
185
+ val ok = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
186
+ gatt.writeCharacteristic(char, chunk, BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE) ==
187
+ BluetoothGatt.GATT_SUCCESS
188
+ } else {
189
+ @Suppress("DEPRECATION")
190
+ char.value = chunk
191
+ char.writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE
192
+ @Suppress("DEPRECATION")
193
+ gatt.writeCharacteristic(char)
194
+ }
195
+ if (!ok) {
196
+ Log.w(NUS_TAG, "NUS TX write rejected at offset $offset — ${data.size - offset}B dropped")
197
+ return
198
+ }
118
199
  offset = end
119
200
  }
120
201
  }
@@ -157,9 +238,9 @@ class NusManager(
157
238
  device.connectGatt(context, false, gattCallback, BluetoothDevice.TRANSPORT_LE)
158
239
  }
159
240
  else -> {
160
- // Not paired — track for UI, don't auto-connect
161
- if (discoveredUnpaired.add(mac)) {
162
- Log.i(NUS_TAG, "NUS: found unpaired RNode $mac pair in Bluetooth Settings first")
241
+ // Not paired — track for UI; call pairRNode(mac) to initiate pairing
242
+ if (discoveredUnpaired.put(mac, device) == null) {
243
+ Log.i(NUS_TAG, "NUS: found unpaired RNode $mac (${device.name ?: "?"}) call pairRNode()")
163
244
  }
164
245
  }
165
246
  }
@@ -252,6 +333,18 @@ class NusManager(
252
333
  Log.i(NUS_TAG, "NUS RNode ready: $mac (tx=${txChar != null}, rx=${rxChar != null})")
253
334
  }
254
335
 
336
+ override fun onDescriptorWrite(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int) {
337
+ val mac = gatt.device.address
338
+ if (descriptor.uuid == CCCD_UUID) {
339
+ if (status == BluetoothGatt.GATT_SUCCESS) {
340
+ Log.i(NUS_TAG, "NUS RX notifications enabled: $mac — RNode ready")
341
+ } else {
342
+ Log.e(NUS_TAG, "NUS CCCD write failed ($status) on $mac — RX notifications NOT enabled, disconnecting")
343
+ gatt.disconnect()
344
+ }
345
+ }
346
+ }
347
+
255
348
  // API < 33 compat override
256
349
  @Suppress("OVERRIDE_DEPRECATION")
257
350
  override fun onCharacteristicChanged(
@@ -12,12 +12,15 @@ export type NativeModuleType = {
12
12
  getStatus(): string | null;
13
13
  getBeacons(): string | null;
14
14
  fetchMessages(limit: number): string | null;
15
+ beaconRpc(destHashHex: string, method: string, paramsJson?: string | null): Promise<number>;
15
16
  setLogLevel(level: number): boolean;
16
17
  abiVersion(): number;
17
18
  startBLE(): boolean;
18
19
  stopBLE(): boolean;
19
20
  blePeerCount(): number;
20
21
  bleUnpairedRNodeCount(): number;
22
+ getNusUnpairedRNodes(): string;
23
+ pairNusRNode(mac: string): boolean;
21
24
  };
22
25
  declare const LxmfModuleNative: NativeModuleType | null;
23
26
  export declare const isLxmfNativeAvailable: boolean;
@@ -34,5 +34,8 @@ const missingNativeShim = {
34
34
  stopBLE: () => throwMissingNative(),
35
35
  blePeerCount: () => throwMissingNative(),
36
36
  bleUnpairedRNodeCount: () => throwMissingNative(),
37
+ beaconRpc: async () => throwMissingNative(),
38
+ getNusUnpairedRNodes: () => throwMissingNative(),
39
+ pairNusRNode: () => throwMissingNative(),
37
40
  };
38
41
  exports.LxmfModule = LxmfModuleNative ?? missingNativeShim;
@@ -33,8 +33,14 @@ export interface LxmfMessageEvent {
33
33
  data: string;
34
34
  }[];
35
35
  }
36
+ export interface RpcResponseEvent {
37
+ id: number;
38
+ method: string;
39
+ resultJson: string;
40
+ isError: boolean;
41
+ }
36
42
  export interface LxmfEvent {
37
- type: 'statusChanged' | 'packetReceived' | 'txReceived' | 'beaconDiscovered' | 'messageReceived' | 'announceReceived' | 'messageQueued' | 'messageDelivered' | 'messageFailed' | 'log' | 'error';
43
+ type: 'statusChanged' | 'packetReceived' | 'txReceived' | 'beaconDiscovered' | 'messageReceived' | 'announceReceived' | 'messageQueued' | 'messageDelivered' | 'messageFailed' | 'log' | 'error' | 'rpcResponse';
38
44
  [key: string]: any;
39
45
  }
40
46
  /** Node transport mode */
@@ -115,4 +121,10 @@ export declare function useLxmf(options?: UseLxmfOptions): {
115
121
  startBLE: () => void;
116
122
  stopBLE: () => void;
117
123
  bleUnpairedRNodeCount: () => number;
124
+ getNusUnpairedRNodes: () => {
125
+ mac: string;
126
+ name: string;
127
+ }[];
128
+ pairNusRNode: (mac: string) => boolean;
129
+ beaconRpc: (destHashHex: string, method: string, params?: unknown) => Promise<number>;
118
130
  };
package/build/useLxmf.js CHANGED
@@ -109,6 +109,19 @@ function useLxmf(options = {}) {
109
109
  pushEvent('error', event);
110
110
  setError(`${String(event.code)}: ${String(event.message)}`);
111
111
  }),
112
+ mod.addListener('onRpcResponse', (event) => {
113
+ pushEvent('rpcResponse', event);
114
+ }),
115
+ mod.addListener('onMessageQueued', (event) => {
116
+ pushEvent('messageQueued', event);
117
+ }),
118
+ mod.addListener('onMessageDelivered', (event) => {
119
+ pushEvent('messageDelivered', event);
120
+ }),
121
+ mod.addListener('onMessageFailed', (event) => {
122
+ pushEvent('messageFailed', event);
123
+ setError(`Message ${String(event.seq)} failed: ${String(event.reason ?? 'unknown')}`);
124
+ }),
112
125
  ];
113
126
  return () => {
114
127
  subscriptions.forEach((sub) => sub.remove());
@@ -253,6 +266,37 @@ function useLxmf(options = {}) {
253
266
  const bleUnpairedRNodeCount = (0, react_1.useCallback)(() => {
254
267
  return LxmfModule_1.LxmfModule.bleUnpairedRNodeCount();
255
268
  }, []);
269
+ /** List of RNodes visible in scan but not yet OS-paired. */
270
+ const getNusUnpairedRNodes = (0, react_1.useCallback)(() => {
271
+ try {
272
+ return parseJson(LxmfModule_1.LxmfModule.getNusUnpairedRNodes(), []);
273
+ }
274
+ catch {
275
+ return [];
276
+ }
277
+ }, []);
278
+ /**
279
+ * Initiate OS Bluetooth pairing with an unpaired RNode (mac = "AA:BB:CC:DD:EE:FF").
280
+ * Shows system pairing dialog. Auto-connects on bond completion via bondReceiver.
281
+ */
282
+ const pairNusRNode = (0, react_1.useCallback)((mac) => {
283
+ return LxmfModule_1.LxmfModule.pairNusRNode(mac);
284
+ }, []);
285
+ /**
286
+ * Queue a JSON-RPC 2.0 call to a beacon.
287
+ * Returns correlation id; the response arrives as an `rpcResponse` event.
288
+ * `params` is any JSON-serializable value (usually an array).
289
+ */
290
+ const beaconRpc = (0, react_1.useCallback)(async (destHashHex, method, params) => {
291
+ try {
292
+ const paramsJson = params === undefined ? null : JSON.stringify(params);
293
+ return await LxmfModule_1.LxmfModule.beaconRpc(destHashHex, method, paramsJson);
294
+ }
295
+ catch (e) {
296
+ setError(e?.message ?? 'beaconRpc failed');
297
+ return -1;
298
+ }
299
+ }, []);
256
300
  return {
257
301
  // State
258
302
  status,
@@ -274,5 +318,8 @@ function useLxmf(options = {}) {
274
318
  startBLE,
275
319
  stopBLE,
276
320
  bleUnpairedRNodeCount,
321
+ getNusUnpairedRNodes,
322
+ pairNusRNode,
323
+ beaconRpc,
277
324
  };
278
325
  }
@@ -235,6 +235,31 @@ class BLEManager: NSObject {
235
235
  return !nusPeripherals.isEmpty
236
236
  }
237
237
 
238
+ /// JSON array of discovered-but-not-yet-bonded RNodes.
239
+ /// Uses CoreBluetooth UUID as "mac" (iOS hides real MACs since iOS 13).
240
+ func unpairedRNodesJson() -> String {
241
+ var entries: [[String: String]] = []
242
+ for (uuid, peripheral) in discoveredUnpairedRNodes {
243
+ entries.append(["mac": uuid.uuidString, "name": peripheral.name ?? ""])
244
+ }
245
+ guard let data = try? JSONSerialization.data(withJSONObject: entries),
246
+ let str = String(data: data, encoding: .utf8) else { return "[]" }
247
+ return str
248
+ }
249
+
250
+ /// Initiate connection to a discovered-but-unpaired RNode by its CoreBluetooth UUID string.
251
+ /// CoreBluetooth handles pairing transparently when the peripheral requires encryption.
252
+ /// Returns false if the UUID is unknown (device not seen in scan yet).
253
+ func connectRNode(_ identifierString: String) -> Bool {
254
+ guard let uuid = UUID(uuidString: identifierString),
255
+ let peripheral = discoveredUnpairedRNodes[uuid] else { return false }
256
+ guard connectedPeripherals[uuid] == nil else { return true }
257
+ connectedPeripherals[uuid] = peripheral
258
+ peripheral.delegate = self
259
+ centralManager?.connect(peripheral, options: nil)
260
+ return true
261
+ }
262
+
238
263
  /// Derive a 6-byte pseudo-MAC from a CoreBluetooth UUID.
239
264
  /// XOR-folds the 16-byte UUID into 6 bytes for stable peer identification.
240
265
  static func uuidToAddr(_ uuid: UUID) -> Data {
@@ -134,6 +134,15 @@ func lxmf_nus_poll_tx(
134
134
  _ outCapacity: Int
135
135
  ) -> Int32
136
136
 
137
+ // --- Beacon RPC FFI ---
138
+
139
+ @_silgen_name("lxmf_beacon_rpc")
140
+ func lxmf_beacon_rpc(
141
+ _ destHashHex: UnsafePointer<CChar>?,
142
+ _ method: UnsafePointer<CChar>?,
143
+ _ paramsJson: UnsafePointer<CChar>?
144
+ ) -> Int64
145
+
137
146
 
138
147
  public class LxmfModule: Module {
139
148
  // Shared JSON buffer for FFI calls (64KB)
@@ -161,6 +170,10 @@ public class LxmfModule: Module {
161
170
  "onMessageReceived",
162
171
  "onAnnounceReceived",
163
172
  "onStatusChanged",
173
+ "onRpcResponse",
174
+ "onMessageQueued",
175
+ "onMessageDelivered",
176
+ "onMessageFailed",
164
177
  "onLog",
165
178
  "onError",
166
179
  "onOutgoingPacket"
@@ -340,6 +353,34 @@ public class LxmfModule: Module {
340
353
  Function("bleUnpairedRNodeCount") { () -> Int in
341
354
  return self.bleManager.discoveredUnpairedRNodes.count
342
355
  }
356
+
357
+ // --- Beacon RPC ---
358
+
359
+ AsyncFunction("beaconRpc") { (destHashHex: String, method: String, paramsJson: String?) -> Double in
360
+ let id = destHashHex.withCString { destPtr in
361
+ method.withCString { methodPtr in
362
+ if let p = paramsJson {
363
+ return p.withCString { paramsPtr in
364
+ lxmf_beacon_rpc(destPtr, methodPtr, paramsPtr)
365
+ }
366
+ }
367
+ return lxmf_beacon_rpc(destPtr, methodPtr, nil)
368
+ }
369
+ }
370
+ return Double(id)
371
+ }
372
+
373
+ // --- RNode pairing (NUS) ---
374
+
375
+ Function("getNusUnpairedRNodes") { () -> String in
376
+ return self.bleManager.unpairedRNodesJson()
377
+ }
378
+
379
+ // On iOS, "pairing" = connect (CoreBluetooth handles encryption/bonding transparently).
380
+ // The identifier is a CoreBluetooth UUID string, not a MAC (iOS hides MACs since iOS 13).
381
+ Function("pairNusRNode") { (identifier: String) -> Bool in
382
+ return self.bleManager.connectRNode(identifier)
383
+ }
343
384
  }
344
385
 
345
386
  // MARK: - Polling
@@ -397,6 +438,14 @@ public class LxmfModule: Module {
397
438
  sendEvent("onMessageReceived", event)
398
439
  case "announceReceived":
399
440
  sendEvent("onAnnounceReceived", event)
441
+ case "rpcResponse":
442
+ sendEvent("onRpcResponse", event)
443
+ case "messageQueued":
444
+ sendEvent("onMessageQueued", event)
445
+ case "messageDelivered":
446
+ sendEvent("onMessageDelivered", event)
447
+ case "messageFailed":
448
+ sendEvent("onMessageFailed", event)
400
449
  case "log":
401
450
  sendEvent("onLog", event)
402
451
  case "error":
@@ -8,32 +8,32 @@
8
8
  <key>BinaryPath</key>
9
9
  <string>liblxmf_rn.a</string>
10
10
  <key>LibraryIdentifier</key>
11
- <string>ios-arm64_x86_64-simulator</string>
11
+ <string>ios-arm64</string>
12
12
  <key>LibraryPath</key>
13
13
  <string>liblxmf_rn.a</string>
14
14
  <key>SupportedArchitectures</key>
15
15
  <array>
16
16
  <string>arm64</string>
17
- <string>x86_64</string>
18
17
  </array>
19
18
  <key>SupportedPlatform</key>
20
19
  <string>ios</string>
21
- <key>SupportedPlatformVariant</key>
22
- <string>simulator</string>
23
20
  </dict>
24
21
  <dict>
25
22
  <key>BinaryPath</key>
26
23
  <string>liblxmf_rn.a</string>
27
24
  <key>LibraryIdentifier</key>
28
- <string>ios-arm64</string>
25
+ <string>ios-arm64_x86_64-simulator</string>
29
26
  <key>LibraryPath</key>
30
27
  <string>liblxmf_rn.a</string>
31
28
  <key>SupportedArchitectures</key>
32
29
  <array>
33
30
  <string>arm64</string>
31
+ <string>x86_64</string>
34
32
  </array>
35
33
  <key>SupportedPlatform</key>
36
34
  <string>ios</string>
35
+ <key>SupportedPlatformVariant</key>
36
+ <string>simulator</string>
37
37
  </dict>
38
38
  </array>
39
39
  <key>CFBundlePackageType</key>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magicred-1/react-native-lxmf",
3
- "version": "0.2.40",
3
+ "version": "0.2.43",
4
4
  "description": "LXMF Reticulum mesh networking for React Native + Expo",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",