kard-network-ble-mesh 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json ADDED
@@ -0,0 +1,90 @@
1
+ {
2
+ "name": "kard-network-ble-mesh",
3
+ "version": "1.0.0",
4
+ "description": "Kard Network BLE Mesh networking library for React Native with seamless permissions",
5
+ "main": "lib/commonjs/index",
6
+ "module": "lib/module/index",
7
+ "types": "lib/typescript/index.d.ts",
8
+ "react-native": "src/index",
9
+ "source": "src/index",
10
+ "files": [
11
+ "src",
12
+ "lib",
13
+ "android",
14
+ "ios",
15
+ "cpp",
16
+ "*.podspec",
17
+ "!ios/build",
18
+ "!android/build",
19
+ "!android/gradle",
20
+ "!android/gradlew",
21
+ "!android/gradlew.bat",
22
+ "!android/local.properties",
23
+ "!**/__tests__",
24
+ "!**/__fixtures__",
25
+ "!**/__mocks__",
26
+ "!**/.*"
27
+ ],
28
+ "scripts": {
29
+ "typescript": "tsc --noEmit",
30
+ "lint": "eslint \"**/*.{js,ts,tsx}\"",
31
+ "prepare": "bob build",
32
+ "clean": "del-cli android/build lib",
33
+ "build": "npm run clean && bob build",
34
+ "publish:npm": "bash scripts/build-and-publish.sh",
35
+ "version:patch": "npm version patch",
36
+ "version:minor": "npm version minor",
37
+ "version:major": "npm version major",
38
+ "release": "npm run build && npm run publish:npm"
39
+ },
40
+ "keywords": [
41
+ "react-native",
42
+ "ios",
43
+ "android",
44
+ "bluetooth",
45
+ "ble",
46
+ "mesh",
47
+ "p2p",
48
+ "kard"
49
+ ],
50
+ "repository": {
51
+ "type": "git",
52
+ "url": "git+https://github.com/anthropics/kard-network-ble-mesh.git"
53
+ },
54
+ "author": "Kard Network",
55
+ "license": "MIT",
56
+ "bugs": {
57
+ "url": "https://github.com/anthropics/kard-network-ble-mesh/issues"
58
+ },
59
+ "homepage": "https://github.com/anthropics/kard-network-ble-mesh#readme",
60
+ "publishConfig": {
61
+ "registry": "https://registry.npmjs.org/"
62
+ },
63
+ "devDependencies": {
64
+ "@types/react": "^18.2.0",
65
+ "@types/react-native": "^0.72.0",
66
+ "del-cli": "^5.0.0",
67
+ "react": "18.2.0",
68
+ "react-native": "0.73.0",
69
+ "react-native-builder-bob": "^0.23.0",
70
+ "typescript": "^5.0.0"
71
+ },
72
+ "peerDependencies": {
73
+ "react": "*",
74
+ "react-native": "*"
75
+ },
76
+ "react-native-builder-bob": {
77
+ "source": "src",
78
+ "output": "lib",
79
+ "targets": [
80
+ "commonjs",
81
+ "module",
82
+ [
83
+ "typescript",
84
+ {
85
+ "project": "tsconfig.build.json"
86
+ }
87
+ ]
88
+ ]
89
+ }
90
+ }
package/src/index.ts ADDED
@@ -0,0 +1,334 @@
1
+ import { NativeModules, NativeEventEmitter, Platform, PermissionsAndroid, Permission } from 'react-native';
2
+
3
+ const LINKING_ERROR =
4
+ `The package 'kard-network-ble-mesh' doesn't seem to be linked. Make sure: \n\n` +
5
+ Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) +
6
+ '- You rebuilt the app after installing the package\n' +
7
+ '- You are not using Expo Go (this package requires a development build)\n';
8
+
9
+ const BleMeshModule = NativeModules.BleMesh
10
+ ? NativeModules.BleMesh
11
+ : new Proxy(
12
+ {},
13
+ {
14
+ get() {
15
+ throw new Error(LINKING_ERROR);
16
+ },
17
+ }
18
+ );
19
+
20
+ const eventEmitter = new NativeEventEmitter(BleMeshModule);
21
+
22
+ // Types
23
+ export interface Peer {
24
+ peerId: string;
25
+ nickname: string;
26
+ isConnected: boolean;
27
+ rssi?: number;
28
+ lastSeen: number;
29
+ isVerified: boolean;
30
+ }
31
+
32
+ export interface Message {
33
+ id: string;
34
+ content: string;
35
+ senderPeerId: string;
36
+ senderNickname: string;
37
+ timestamp: number;
38
+ isPrivate: boolean;
39
+ channel?: string;
40
+ }
41
+
42
+ export interface FileTransfer {
43
+ id: string;
44
+ fileName: string;
45
+ fileSize: number;
46
+ mimeType: string;
47
+ data: string; // base64 encoded
48
+ senderPeerId: string;
49
+ timestamp: number;
50
+ }
51
+
52
+ export interface PermissionStatus {
53
+ bluetooth: boolean;
54
+ bluetoothAdvertise?: boolean; // Android 12+
55
+ bluetoothConnect?: boolean; // Android 12+
56
+ bluetoothScan?: boolean; // Android 12+
57
+ location: boolean;
58
+ }
59
+
60
+ export type ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'scanning';
61
+
62
+ export interface MeshServiceConfig {
63
+ nickname?: string;
64
+ autoRequestPermissions?: boolean;
65
+ }
66
+
67
+ // Event types
68
+ export type PeerListUpdatedEvent = { peers: Peer[] };
69
+ export type MessageReceivedEvent = { message: Message };
70
+ export type FileReceivedEvent = { file: FileTransfer };
71
+ export type ConnectionStateChangedEvent = { state: ConnectionState; peerCount: number };
72
+ export type PermissionsChangedEvent = { permissions: PermissionStatus };
73
+ export type ErrorEvent = { code: string; message: string };
74
+
75
+ // Event listener types
76
+ type EventCallback<T> = (data: T) => void;
77
+
78
+ class BleMeshService {
79
+ private isInitialized = false;
80
+
81
+ // Request all required permissions for BLE mesh networking
82
+ async requestPermissions(): Promise<PermissionStatus> {
83
+ if (Platform.OS === 'android') {
84
+ return this.requestAndroidPermissions();
85
+ } else if (Platform.OS === 'ios') {
86
+ return this.requestIOSPermissions();
87
+ }
88
+ throw new Error('Unsupported platform');
89
+ }
90
+
91
+ private async requestAndroidPermissions(): Promise<PermissionStatus> {
92
+ const apiLevel = Platform.Version as number;
93
+
94
+ let permissions: Permission[] = [];
95
+
96
+ if (apiLevel >= 31) {
97
+ // Android 12+ (API 31+)
98
+ permissions = [
99
+ PermissionsAndroid.PERMISSIONS.BLUETOOTH_ADVERTISE as Permission,
100
+ PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT as Permission,
101
+ PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN as Permission,
102
+ PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION as Permission,
103
+ ];
104
+ } else {
105
+ // Android 11 and below
106
+ permissions = [
107
+ PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION as Permission,
108
+ PermissionsAndroid.PERMISSIONS.ACCESS_COARSE_LOCATION as Permission,
109
+ ];
110
+ }
111
+
112
+ const results = await PermissionsAndroid.requestMultiple(permissions);
113
+
114
+ const status: PermissionStatus = {
115
+ bluetooth: true,
116
+ location: results[PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION] === 'granted',
117
+ };
118
+
119
+ if (apiLevel >= 31) {
120
+ status.bluetoothAdvertise = results[PermissionsAndroid.PERMISSIONS.BLUETOOTH_ADVERTISE] === 'granted';
121
+ status.bluetoothConnect = results[PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT] === 'granted';
122
+ status.bluetoothScan = results[PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN] === 'granted';
123
+ status.bluetooth = status.bluetoothAdvertise && status.bluetoothConnect && status.bluetoothScan;
124
+ }
125
+
126
+ return status;
127
+ }
128
+
129
+ private async requestIOSPermissions(): Promise<PermissionStatus> {
130
+ // On iOS, Bluetooth permissions are requested automatically when starting services
131
+ // The native module handles the permission prompts
132
+ const result = await BleMeshModule.requestPermissions();
133
+ return {
134
+ bluetooth: result.bluetooth,
135
+ location: result.location,
136
+ };
137
+ }
138
+
139
+ // Check current permission status without requesting
140
+ async checkPermissions(): Promise<PermissionStatus> {
141
+ if (Platform.OS === 'android') {
142
+ return this.checkAndroidPermissions();
143
+ }
144
+ return BleMeshModule.checkPermissions();
145
+ }
146
+
147
+ private async checkAndroidPermissions(): Promise<PermissionStatus> {
148
+ const apiLevel = Platform.Version as number;
149
+
150
+ const fineLocation = await PermissionsAndroid.check(
151
+ PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION
152
+ );
153
+
154
+ const status: PermissionStatus = {
155
+ bluetooth: true,
156
+ location: fineLocation,
157
+ };
158
+
159
+ if (apiLevel >= 31) {
160
+ const advertise = await PermissionsAndroid.check(
161
+ PermissionsAndroid.PERMISSIONS.BLUETOOTH_ADVERTISE
162
+ );
163
+ const connect = await PermissionsAndroid.check(
164
+ PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT
165
+ );
166
+ const scan = await PermissionsAndroid.check(
167
+ PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN
168
+ );
169
+
170
+ status.bluetoothAdvertise = advertise;
171
+ status.bluetoothConnect = connect;
172
+ status.bluetoothScan = scan;
173
+ status.bluetooth = advertise && connect && scan;
174
+ }
175
+
176
+ return status;
177
+ }
178
+
179
+ // Initialize and start the mesh service
180
+ async start(config: MeshServiceConfig = {}): Promise<void> {
181
+ const { nickname = 'anon', autoRequestPermissions = true } = config;
182
+
183
+ if (autoRequestPermissions) {
184
+ const permissions = await this.requestPermissions();
185
+ const hasAllPermissions = permissions.bluetooth && permissions.location;
186
+
187
+ if (!hasAllPermissions) {
188
+ throw new Error('Required permissions not granted. Bluetooth and Location permissions are required.');
189
+ }
190
+ }
191
+
192
+ await BleMeshModule.start(nickname);
193
+ this.isInitialized = true;
194
+ }
195
+
196
+ // Stop the mesh service
197
+ async stop(): Promise<void> {
198
+ if (!this.isInitialized) return;
199
+ await BleMeshModule.stop();
200
+ this.isInitialized = false;
201
+ }
202
+
203
+ // Set the user's nickname
204
+ async setNickname(nickname: string): Promise<void> {
205
+ this.ensureInitialized();
206
+ await BleMeshModule.setNickname(nickname);
207
+ }
208
+
209
+ // Get the current peer ID
210
+ async getMyPeerId(): Promise<string> {
211
+ this.ensureInitialized();
212
+ return BleMeshModule.getMyPeerId();
213
+ }
214
+
215
+ // Get the current nickname
216
+ async getMyNickname(): Promise<string> {
217
+ this.ensureInitialized();
218
+ return BleMeshModule.getMyNickname();
219
+ }
220
+
221
+ // Get list of currently connected peers
222
+ async getPeers(): Promise<Peer[]> {
223
+ this.ensureInitialized();
224
+ return BleMeshModule.getPeers();
225
+ }
226
+
227
+ // Send a public broadcast message to all peers
228
+ async sendMessage(content: string, channel?: string): Promise<string> {
229
+ this.ensureInitialized();
230
+ return BleMeshModule.sendMessage(content, channel || null);
231
+ }
232
+
233
+ // Send a private encrypted message to a specific peer
234
+ async sendPrivateMessage(content: string, recipientPeerId: string): Promise<string> {
235
+ this.ensureInitialized();
236
+ return BleMeshModule.sendPrivateMessage(content, recipientPeerId);
237
+ }
238
+
239
+ // Send a file (broadcast or private)
240
+ async sendFile(
241
+ filePath: string,
242
+ options?: { recipientPeerId?: string; channel?: string }
243
+ ): Promise<string> {
244
+ this.ensureInitialized();
245
+ return BleMeshModule.sendFile(filePath, options?.recipientPeerId || null, options?.channel || null);
246
+ }
247
+
248
+ // Send a read receipt for a message
249
+ async sendReadReceipt(messageId: string, recipientPeerId: string): Promise<void> {
250
+ this.ensureInitialized();
251
+ await BleMeshModule.sendReadReceipt(messageId, recipientPeerId);
252
+ }
253
+
254
+ // Check if we have an encrypted session with a peer
255
+ async hasEncryptedSession(peerId: string): Promise<boolean> {
256
+ this.ensureInitialized();
257
+ return BleMeshModule.hasEncryptedSession(peerId);
258
+ }
259
+
260
+ // Initiate a Noise handshake with a peer
261
+ async initiateHandshake(peerId: string): Promise<void> {
262
+ this.ensureInitialized();
263
+ await BleMeshModule.initiateHandshake(peerId);
264
+ }
265
+
266
+ // Get the identity fingerprint for verification
267
+ async getIdentityFingerprint(): Promise<string> {
268
+ this.ensureInitialized();
269
+ return BleMeshModule.getIdentityFingerprint();
270
+ }
271
+
272
+ // Get peer's fingerprint for verification
273
+ async getPeerFingerprint(peerId: string): Promise<string | null> {
274
+ this.ensureInitialized();
275
+ return BleMeshModule.getPeerFingerprint(peerId);
276
+ }
277
+
278
+ // Force a broadcast announce to refresh presence
279
+ async broadcastAnnounce(): Promise<void> {
280
+ this.ensureInitialized();
281
+ await BleMeshModule.broadcastAnnounce();
282
+ }
283
+
284
+ // Event listeners
285
+ onPeerListUpdated(callback: EventCallback<PeerListUpdatedEvent>): () => void {
286
+ const subscription = eventEmitter.addListener('onPeerListUpdated', callback);
287
+ return () => subscription.remove();
288
+ }
289
+
290
+ onMessageReceived(callback: EventCallback<MessageReceivedEvent>): () => void {
291
+ const subscription = eventEmitter.addListener('onMessageReceived', callback);
292
+ return () => subscription.remove();
293
+ }
294
+
295
+ onFileReceived(callback: EventCallback<FileReceivedEvent>): () => void {
296
+ const subscription = eventEmitter.addListener('onFileReceived', callback);
297
+ return () => subscription.remove();
298
+ }
299
+
300
+ onConnectionStateChanged(callback: EventCallback<ConnectionStateChangedEvent>): () => void {
301
+ const subscription = eventEmitter.addListener('onConnectionStateChanged', callback);
302
+ return () => subscription.remove();
303
+ }
304
+
305
+ onReadReceipt(callback: EventCallback<{ messageId: string; fromPeerId: string }>): () => void {
306
+ const subscription = eventEmitter.addListener('onReadReceipt', callback);
307
+ return () => subscription.remove();
308
+ }
309
+
310
+ onDeliveryAck(callback: EventCallback<{ messageId: string; fromPeerId: string }>): () => void {
311
+ const subscription = eventEmitter.addListener('onDeliveryAck', callback);
312
+ return () => subscription.remove();
313
+ }
314
+
315
+ onError(callback: EventCallback<ErrorEvent>): () => void {
316
+ const subscription = eventEmitter.addListener('onError', callback);
317
+ return () => subscription.remove();
318
+ }
319
+
320
+ private ensureInitialized(): void {
321
+ if (!this.isInitialized) {
322
+ throw new Error('BleMesh service not initialized. Call start() first.');
323
+ }
324
+ }
325
+ }
326
+
327
+ // Export singleton instance
328
+ export const BleMesh = new BleMeshService();
329
+
330
+ // Also export the class for those who want multiple instances
331
+ export { BleMeshService };
332
+
333
+ // Default export
334
+ export default BleMesh;