@meshwhisper/sdk 0.1.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.
Files changed (163) hide show
  1. package/README.md +138 -0
  2. package/dist/browser/index.d.ts +4 -0
  3. package/dist/browser/index.d.ts.map +1 -0
  4. package/dist/browser/index.js +19 -0
  5. package/dist/browser/index.js.map +1 -0
  6. package/dist/chaff/index.d.ts +91 -0
  7. package/dist/chaff/index.d.ts.map +1 -0
  8. package/dist/chaff/index.js +268 -0
  9. package/dist/chaff/index.js.map +1 -0
  10. package/dist/cluster/index.d.ts +159 -0
  11. package/dist/cluster/index.d.ts.map +1 -0
  12. package/dist/cluster/index.js +393 -0
  13. package/dist/cluster/index.js.map +1 -0
  14. package/dist/compliance/index.d.ts +129 -0
  15. package/dist/compliance/index.d.ts.map +1 -0
  16. package/dist/compliance/index.js +315 -0
  17. package/dist/compliance/index.js.map +1 -0
  18. package/dist/crypto/index.d.ts +65 -0
  19. package/dist/crypto/index.d.ts.map +1 -0
  20. package/dist/crypto/index.js +146 -0
  21. package/dist/crypto/index.js.map +1 -0
  22. package/dist/group/index.d.ts +155 -0
  23. package/dist/group/index.d.ts.map +1 -0
  24. package/dist/group/index.js +560 -0
  25. package/dist/group/index.js.map +1 -0
  26. package/dist/index.d.ts +7 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +11 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/namespace/index.d.ts +155 -0
  31. package/dist/namespace/index.d.ts.map +1 -0
  32. package/dist/namespace/index.js +278 -0
  33. package/dist/namespace/index.js.map +1 -0
  34. package/dist/node/index.d.ts +4 -0
  35. package/dist/node/index.d.ts.map +1 -0
  36. package/dist/node/index.js +19 -0
  37. package/dist/node/index.js.map +1 -0
  38. package/dist/packet/index.d.ts +63 -0
  39. package/dist/packet/index.d.ts.map +1 -0
  40. package/dist/packet/index.js +244 -0
  41. package/dist/packet/index.js.map +1 -0
  42. package/dist/permissions/index.d.ts +107 -0
  43. package/dist/permissions/index.d.ts.map +1 -0
  44. package/dist/permissions/index.js +282 -0
  45. package/dist/permissions/index.js.map +1 -0
  46. package/dist/persistence/idb-storage.d.ts +27 -0
  47. package/dist/persistence/idb-storage.d.ts.map +1 -0
  48. package/dist/persistence/idb-storage.js +75 -0
  49. package/dist/persistence/idb-storage.js.map +1 -0
  50. package/dist/persistence/index.d.ts +4 -0
  51. package/dist/persistence/index.d.ts.map +1 -0
  52. package/dist/persistence/index.js +3 -0
  53. package/dist/persistence/index.js.map +1 -0
  54. package/dist/persistence/node-storage.d.ts +33 -0
  55. package/dist/persistence/node-storage.d.ts.map +1 -0
  56. package/dist/persistence/node-storage.js +90 -0
  57. package/dist/persistence/node-storage.js.map +1 -0
  58. package/dist/persistence/serialization.d.ts +4 -0
  59. package/dist/persistence/serialization.d.ts.map +1 -0
  60. package/dist/persistence/serialization.js +49 -0
  61. package/dist/persistence/serialization.js.map +1 -0
  62. package/dist/persistence/types.d.ts +29 -0
  63. package/dist/persistence/types.d.ts.map +1 -0
  64. package/dist/persistence/types.js +5 -0
  65. package/dist/persistence/types.js.map +1 -0
  66. package/dist/ratchet/index.d.ts +80 -0
  67. package/dist/ratchet/index.d.ts.map +1 -0
  68. package/dist/ratchet/index.js +259 -0
  69. package/dist/ratchet/index.js.map +1 -0
  70. package/dist/reciprocity/index.d.ts +109 -0
  71. package/dist/reciprocity/index.d.ts.map +1 -0
  72. package/dist/reciprocity/index.js +311 -0
  73. package/dist/reciprocity/index.js.map +1 -0
  74. package/dist/relay/index.d.ts +87 -0
  75. package/dist/relay/index.d.ts.map +1 -0
  76. package/dist/relay/index.js +286 -0
  77. package/dist/relay/index.js.map +1 -0
  78. package/dist/routing/index.d.ts +136 -0
  79. package/dist/routing/index.d.ts.map +1 -0
  80. package/dist/routing/index.js +478 -0
  81. package/dist/routing/index.js.map +1 -0
  82. package/dist/sdk/index.d.ts +322 -0
  83. package/dist/sdk/index.d.ts.map +1 -0
  84. package/dist/sdk/index.js +1530 -0
  85. package/dist/sdk/index.js.map +1 -0
  86. package/dist/sybil/index.d.ts +123 -0
  87. package/dist/sybil/index.d.ts.map +1 -0
  88. package/dist/sybil/index.js +491 -0
  89. package/dist/sybil/index.js.map +1 -0
  90. package/dist/transport/browser/index.d.ts +34 -0
  91. package/dist/transport/browser/index.d.ts.map +1 -0
  92. package/dist/transport/browser/index.js +176 -0
  93. package/dist/transport/browser/index.js.map +1 -0
  94. package/dist/transport/local/index.d.ts +57 -0
  95. package/dist/transport/local/index.d.ts.map +1 -0
  96. package/dist/transport/local/index.js +442 -0
  97. package/dist/transport/local/index.js.map +1 -0
  98. package/dist/transport/negotiator/index.d.ts +79 -0
  99. package/dist/transport/negotiator/index.d.ts.map +1 -0
  100. package/dist/transport/negotiator/index.js +289 -0
  101. package/dist/transport/negotiator/index.js.map +1 -0
  102. package/dist/transport/node/index.d.ts +56 -0
  103. package/dist/transport/node/index.d.ts.map +1 -0
  104. package/dist/transport/node/index.js +209 -0
  105. package/dist/transport/node/index.js.map +1 -0
  106. package/dist/transport/noop/index.d.ts +11 -0
  107. package/dist/transport/noop/index.d.ts.map +1 -0
  108. package/dist/transport/noop/index.js +20 -0
  109. package/dist/transport/noop/index.js.map +1 -0
  110. package/dist/transport/p2p/index.d.ts +109 -0
  111. package/dist/transport/p2p/index.d.ts.map +1 -0
  112. package/dist/transport/p2p/index.js +237 -0
  113. package/dist/transport/p2p/index.js.map +1 -0
  114. package/dist/transport/websocket/index.d.ts +89 -0
  115. package/dist/transport/websocket/index.d.ts.map +1 -0
  116. package/dist/transport/websocket/index.js +498 -0
  117. package/dist/transport/websocket/index.js.map +1 -0
  118. package/dist/transport/websocket/serialize.d.ts +5 -0
  119. package/dist/transport/websocket/serialize.d.ts.map +1 -0
  120. package/dist/transport/websocket/serialize.js +55 -0
  121. package/dist/transport/websocket/serialize.js.map +1 -0
  122. package/dist/types.d.ts +215 -0
  123. package/dist/types.d.ts.map +1 -0
  124. package/dist/types.js +15 -0
  125. package/dist/types.js.map +1 -0
  126. package/dist/x3dh/index.d.ts +120 -0
  127. package/dist/x3dh/index.d.ts.map +1 -0
  128. package/dist/x3dh/index.js +290 -0
  129. package/dist/x3dh/index.js.map +1 -0
  130. package/package.json +59 -0
  131. package/src/browser/index.ts +19 -0
  132. package/src/chaff/index.ts +340 -0
  133. package/src/cluster/index.ts +482 -0
  134. package/src/compliance/index.ts +407 -0
  135. package/src/crypto/index.ts +193 -0
  136. package/src/group/index.ts +719 -0
  137. package/src/index.ts +87 -0
  138. package/src/lz4js.d.ts +58 -0
  139. package/src/namespace/index.ts +336 -0
  140. package/src/node/index.ts +19 -0
  141. package/src/packet/index.ts +326 -0
  142. package/src/permissions/index.ts +405 -0
  143. package/src/persistence/idb-storage.ts +83 -0
  144. package/src/persistence/index.ts +3 -0
  145. package/src/persistence/node-storage.ts +96 -0
  146. package/src/persistence/serialization.ts +75 -0
  147. package/src/persistence/types.ts +33 -0
  148. package/src/ratchet/index.ts +363 -0
  149. package/src/reciprocity/index.ts +371 -0
  150. package/src/relay/index.ts +382 -0
  151. package/src/routing/index.ts +577 -0
  152. package/src/sdk/index.ts +1994 -0
  153. package/src/sybil/index.ts +661 -0
  154. package/src/transport/browser/index.ts +201 -0
  155. package/src/transport/local/index.ts +540 -0
  156. package/src/transport/negotiator/index.ts +397 -0
  157. package/src/transport/node/index.ts +234 -0
  158. package/src/transport/noop/index.ts +22 -0
  159. package/src/transport/p2p/index.ts +345 -0
  160. package/src/transport/websocket/index.ts +660 -0
  161. package/src/transport/websocket/serialize.ts +68 -0
  162. package/src/types.ts +275 -0
  163. package/src/x3dh/index.ts +388 -0
@@ -0,0 +1,345 @@
1
+ // ============================================================
2
+ // MeshWhisper SDK — Platform P2P Transport
3
+ // Abstract bridge for native P2P (Apple Multipeer Connectivity,
4
+ // Google Nearby Connections) with TypeScript-side Transport
5
+ // wrapper. Native SDKs implement PlatformP2PBridge; this module
6
+ // provides the Transport adapter and a no-op fallback for
7
+ // environments where native P2P is unavailable.
8
+ // ============================================================
9
+
10
+ import type { Packet, Transport, PacketFlags } from '../../types.js';
11
+
12
+ // ---- Bridge Interfaces ----
13
+
14
+ /**
15
+ * Metadata about a discovered P2P peer.
16
+ * Provided by the native layer during discovery.
17
+ */
18
+ export interface PeerInfo {
19
+ /** Human-readable name for the peer device. */
20
+ displayName: string;
21
+ /** The service identifier under which this peer was discovered. */
22
+ serviceId: string;
23
+ /** Advertised capabilities (e.g. "relay", "store-forward"). */
24
+ capabilities: string[];
25
+ }
26
+
27
+ /**
28
+ * Abstract interface that native platform code (Swift / Kotlin)
29
+ * must implement to provide P2P connectivity to the TypeScript SDK.
30
+ *
31
+ * - iOS: backed by Apple Multipeer Connectivity Framework
32
+ * (BLE discovery + Wi-Fi Direct data transfer).
33
+ * - Android: backed by Google Nearby Connections API
34
+ * (Bluetooth + Wi-Fi).
35
+ *
36
+ * All methods are designed to be thin wrappers around the native
37
+ * APIs so the bridge implementation stays minimal.
38
+ */
39
+ export interface PlatformP2PBridge {
40
+ /** Begin advertising this device for the given service. */
41
+ startAdvertising(serviceId: string): Promise<void>;
42
+
43
+ /** Begin scanning for peers advertising the given service. */
44
+ startDiscovery(serviceId: string): Promise<void>;
45
+
46
+ /** Stop advertising. */
47
+ stopAdvertising(): Promise<void>;
48
+
49
+ /** Stop discovery scanning. */
50
+ stopDiscovery(): Promise<void>;
51
+
52
+ /** Send raw bytes to a connected peer. */
53
+ sendData(peerId: string, data: Uint8Array): Promise<void>;
54
+
55
+ /** Register a callback invoked when a new peer is discovered. */
56
+ onPeerDiscovered(callback: (peerId: string, info: PeerInfo) => void): void;
57
+
58
+ /** Register a callback invoked when a previously discovered peer is lost. */
59
+ onPeerLost(callback: (peerId: string) => void): void;
60
+
61
+ /** Register a callback invoked when data is received from a peer. */
62
+ onDataReceived(callback: (peerId: string, data: Uint8Array) => void): void;
63
+
64
+ /** Returns the IDs of all currently connected peers. */
65
+ getConnectedPeers(): string[];
66
+
67
+ /**
68
+ * Returns true if the underlying native P2P framework is
69
+ * available on this device / OS version.
70
+ */
71
+ isSupported(): boolean;
72
+ }
73
+
74
+ // ---- Packet Serialization ----
75
+
76
+ /**
77
+ * Minimum header size in bytes:
78
+ * version (1) + flags (1) + destHash (8) + senderEphemeralId (16)
79
+ * + ttl (1) + payloadLength (4) = 31
80
+ */
81
+ const HEADER_SIZE = 31;
82
+
83
+ /**
84
+ * Serializes a Packet into a compact binary format for transmission
85
+ * over the native P2P bridge.
86
+ *
87
+ * Wire format (big-endian):
88
+ * [0] u8 version
89
+ * [1] u8 flags
90
+ * [2..9] 8B destHash
91
+ * [10..25] 16B senderEphemeralId
92
+ * [26] u8 ttl
93
+ * [27..30] u32 payloadLength
94
+ * [31..] var encryptedPayload
95
+ */
96
+ function serializePacket(packet: Packet): Uint8Array {
97
+ const totalLength = HEADER_SIZE + packet.encryptedPayload.length;
98
+ const buffer = new Uint8Array(totalLength);
99
+ const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
100
+
101
+ let offset = 0;
102
+
103
+ // version (u8)
104
+ view.setUint8(offset, packet.version);
105
+ offset += 1;
106
+
107
+ // flags (u8)
108
+ view.setUint8(offset, packet.flags as number);
109
+ offset += 1;
110
+
111
+ // destHash (8 bytes)
112
+ buffer.set(packet.destHash, offset);
113
+ offset += 8;
114
+
115
+ // senderEphemeralId (16 bytes)
116
+ buffer.set(packet.senderEphemeralId, offset);
117
+ offset += 16;
118
+
119
+ // ttl (u8)
120
+ view.setUint8(offset, packet.ttl);
121
+ offset += 1;
122
+
123
+ // payloadLength (u32 big-endian)
124
+ view.setUint32(offset, packet.payloadLength, false);
125
+ offset += 4;
126
+
127
+ // encryptedPayload
128
+ buffer.set(packet.encryptedPayload, offset);
129
+
130
+ return buffer;
131
+ }
132
+
133
+ /**
134
+ * Deserializes a Uint8Array back into a Packet.
135
+ * Throws if the data is too short or the payload length mismatches.
136
+ */
137
+ function deserializePacket(data: Uint8Array): Packet {
138
+ if (data.length < HEADER_SIZE) {
139
+ throw new Error(
140
+ `P2P: packet too short (${data.length} bytes, need at least ${HEADER_SIZE})`,
141
+ );
142
+ }
143
+
144
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
145
+ let offset = 0;
146
+
147
+ const version = view.getUint8(offset);
148
+ offset += 1;
149
+
150
+ const flags = view.getUint8(offset) as PacketFlags;
151
+ offset += 1;
152
+
153
+ const destHash = data.slice(offset, offset + 8);
154
+ offset += 8;
155
+
156
+ const senderEphemeralId = data.slice(offset, offset + 16);
157
+ offset += 16;
158
+
159
+ const ttl = view.getUint8(offset);
160
+ offset += 1;
161
+
162
+ const payloadLength = view.getUint32(offset, false);
163
+ offset += 4;
164
+
165
+ const encryptedPayload = data.slice(offset, offset + payloadLength);
166
+ if (encryptedPayload.length !== payloadLength) {
167
+ throw new Error(
168
+ `P2P: payload length mismatch (header says ${payloadLength}, got ${encryptedPayload.length})`,
169
+ );
170
+ }
171
+
172
+ return {
173
+ version,
174
+ flags,
175
+ destHash,
176
+ senderEphemeralId,
177
+ ttl,
178
+ payloadLength,
179
+ encryptedPayload,
180
+ };
181
+ }
182
+
183
+ // ---- No-Op Bridge ----
184
+
185
+ /**
186
+ * Default bridge used when no native implementation is registered.
187
+ * All methods are safe no-ops. isSupported() returns false so the
188
+ * transport layer knows to skip this bearer.
189
+ */
190
+ class NoOpBridge implements PlatformP2PBridge {
191
+ async startAdvertising(_serviceId: string): Promise<void> {}
192
+ async startDiscovery(_serviceId: string): Promise<void> {}
193
+ async stopAdvertising(): Promise<void> {}
194
+ async stopDiscovery(): Promise<void> {}
195
+ async sendData(_peerId: string, _data: Uint8Array): Promise<void> {}
196
+ onPeerDiscovered(_callback: (peerId: string, info: PeerInfo) => void): void {}
197
+ onPeerLost(_callback: (peerId: string) => void): void {}
198
+ onDataReceived(_callback: (peerId: string, data: Uint8Array) => void): void {}
199
+ getConnectedPeers(): string[] {
200
+ return [];
201
+ }
202
+ isSupported(): boolean {
203
+ return false;
204
+ }
205
+ }
206
+
207
+ // ---- Bridge Registry ----
208
+
209
+ /** The currently registered bridge (defaults to NoOpBridge). */
210
+ let activeBridge: PlatformP2PBridge = new NoOpBridge();
211
+
212
+ /**
213
+ * Registers a platform-specific P2P bridge implementation.
214
+ *
215
+ * Called by native code (via JSI on React Native, or similar
216
+ * interop layers) to inject the real Multipeer / Nearby
217
+ * Connections wrapper at runtime.
218
+ *
219
+ * @param bridge - The native bridge implementation.
220
+ */
221
+ export function registerPlatformBridge(bridge: PlatformP2PBridge): void {
222
+ activeBridge = bridge;
223
+ }
224
+
225
+ // ---- Service ID Generation ----
226
+
227
+ /** Max length for Bonjour / NSD service types. */
228
+ const MAX_SERVICE_ID_LENGTH = 15;
229
+
230
+ /**
231
+ * Generates a platform-appropriate service identifier from an
232
+ * application namespace string.
233
+ *
234
+ * Both Apple Multipeer Connectivity and Android NSD impose
235
+ * restrictions on service type identifiers (length, allowed
236
+ * characters). This function produces a short, deterministic,
237
+ * lowercase-alphanumeric ID derived from the namespace.
238
+ *
239
+ * @param namespace - The application namespace (e.g. "com.example.chat").
240
+ * @returns A service ID string safe for both iOS and Android.
241
+ */
242
+ export function generateServiceId(namespace: string): string {
243
+ // Simple FNV-1a 32-bit hash for determinism without heavy deps.
244
+ let h = 0x811c9dc5;
245
+ for (let i = 0; i < namespace.length; i++) {
246
+ h ^= namespace.charCodeAt(i);
247
+ h = Math.imul(h, 0x01000193);
248
+ }
249
+ // Unsigned conversion then base-36 for compact alphanumeric output.
250
+ const hashStr = (h >>> 0).toString(36);
251
+ // Prefix with "mw-" (MeshWhisper) for readability, truncate to limit.
252
+ const serviceId = `mw-${hashStr}`;
253
+ return serviceId.slice(0, MAX_SERVICE_ID_LENGTH);
254
+ }
255
+
256
+ // ---- Transport Implementation ----
257
+
258
+ /**
259
+ * PlatformP2PTransport adapts a PlatformP2PBridge into the
260
+ * SDK's Transport interface.
261
+ *
262
+ * When a native bridge has been registered via
263
+ * `registerPlatformBridge()`, this transport serializes Packets
264
+ * to wire bytes, sends them through the bridge, and
265
+ * deserializes incoming bytes back into Packets for the mesh
266
+ * router.
267
+ *
268
+ * When no bridge is registered, the transport reports itself
269
+ * as unavailable and all operations are safe no-ops.
270
+ */
271
+ export class PlatformP2PTransport implements Transport {
272
+ readonly type = 'platform_p2p' as const;
273
+
274
+ private readonly serviceId: string;
275
+ private receiveCallback: ((packet: Packet, source: string) => void) | null = null;
276
+ private running = false;
277
+
278
+ /**
279
+ * @param namespace - Application namespace used to derive
280
+ * the P2P service identifier.
281
+ */
282
+ constructor(namespace: string) {
283
+ this.serviceId = generateServiceId(namespace);
284
+ }
285
+
286
+ /**
287
+ * Returns the bridge currently in use. Reads from the
288
+ * module-level registry so that bridges registered after
289
+ * construction are picked up automatically.
290
+ */
291
+ private get bridge(): PlatformP2PBridge {
292
+ return activeBridge;
293
+ }
294
+
295
+ // ---- Transport interface ----
296
+
297
+ async isAvailable(): Promise<boolean> {
298
+ return this.bridge.isSupported();
299
+ }
300
+
301
+ async start(): Promise<void> {
302
+ if (this.running) return;
303
+
304
+ if (!this.bridge.isSupported()) {
305
+ // Silently skip — the transport negotiator will use
306
+ // the next bearer in priority order.
307
+ return;
308
+ }
309
+
310
+ // Wire up data reception.
311
+ this.bridge.onDataReceived((peerId: string, data: Uint8Array) => {
312
+ if (!this.receiveCallback) return;
313
+ try {
314
+ const packet = deserializePacket(data);
315
+ this.receiveCallback(packet, peerId);
316
+ } catch {
317
+ // Malformed packet — drop silently. A production build
318
+ // may want to emit a diagnostic event here.
319
+ }
320
+ });
321
+
322
+ await this.bridge.startAdvertising(this.serviceId);
323
+ await this.bridge.startDiscovery(this.serviceId);
324
+ this.running = true;
325
+ }
326
+
327
+ async stop(): Promise<void> {
328
+ if (!this.running) return;
329
+
330
+ await this.bridge.stopAdvertising();
331
+ await this.bridge.stopDiscovery();
332
+ this.running = false;
333
+ }
334
+
335
+ async send(packet: Packet, destination: string): Promise<void> {
336
+ if (!this.running || !this.bridge.isSupported()) return;
337
+
338
+ const data = serializePacket(packet);
339
+ await this.bridge.sendData(destination, data);
340
+ }
341
+
342
+ onReceive(callback: (packet: Packet, source: string) => void): void {
343
+ this.receiveCallback = callback;
344
+ }
345
+ }