@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.
- package/README.md +138 -0
- package/dist/browser/index.d.ts +4 -0
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.js +19 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/chaff/index.d.ts +91 -0
- package/dist/chaff/index.d.ts.map +1 -0
- package/dist/chaff/index.js +268 -0
- package/dist/chaff/index.js.map +1 -0
- package/dist/cluster/index.d.ts +159 -0
- package/dist/cluster/index.d.ts.map +1 -0
- package/dist/cluster/index.js +393 -0
- package/dist/cluster/index.js.map +1 -0
- package/dist/compliance/index.d.ts +129 -0
- package/dist/compliance/index.d.ts.map +1 -0
- package/dist/compliance/index.js +315 -0
- package/dist/compliance/index.js.map +1 -0
- package/dist/crypto/index.d.ts +65 -0
- package/dist/crypto/index.d.ts.map +1 -0
- package/dist/crypto/index.js +146 -0
- package/dist/crypto/index.js.map +1 -0
- package/dist/group/index.d.ts +155 -0
- package/dist/group/index.d.ts.map +1 -0
- package/dist/group/index.js +560 -0
- package/dist/group/index.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/namespace/index.d.ts +155 -0
- package/dist/namespace/index.d.ts.map +1 -0
- package/dist/namespace/index.js +278 -0
- package/dist/namespace/index.js.map +1 -0
- package/dist/node/index.d.ts +4 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.js +19 -0
- package/dist/node/index.js.map +1 -0
- package/dist/packet/index.d.ts +63 -0
- package/dist/packet/index.d.ts.map +1 -0
- package/dist/packet/index.js +244 -0
- package/dist/packet/index.js.map +1 -0
- package/dist/permissions/index.d.ts +107 -0
- package/dist/permissions/index.d.ts.map +1 -0
- package/dist/permissions/index.js +282 -0
- package/dist/permissions/index.js.map +1 -0
- package/dist/persistence/idb-storage.d.ts +27 -0
- package/dist/persistence/idb-storage.d.ts.map +1 -0
- package/dist/persistence/idb-storage.js +75 -0
- package/dist/persistence/idb-storage.js.map +1 -0
- package/dist/persistence/index.d.ts +4 -0
- package/dist/persistence/index.d.ts.map +1 -0
- package/dist/persistence/index.js +3 -0
- package/dist/persistence/index.js.map +1 -0
- package/dist/persistence/node-storage.d.ts +33 -0
- package/dist/persistence/node-storage.d.ts.map +1 -0
- package/dist/persistence/node-storage.js +90 -0
- package/dist/persistence/node-storage.js.map +1 -0
- package/dist/persistence/serialization.d.ts +4 -0
- package/dist/persistence/serialization.d.ts.map +1 -0
- package/dist/persistence/serialization.js +49 -0
- package/dist/persistence/serialization.js.map +1 -0
- package/dist/persistence/types.d.ts +29 -0
- package/dist/persistence/types.d.ts.map +1 -0
- package/dist/persistence/types.js +5 -0
- package/dist/persistence/types.js.map +1 -0
- package/dist/ratchet/index.d.ts +80 -0
- package/dist/ratchet/index.d.ts.map +1 -0
- package/dist/ratchet/index.js +259 -0
- package/dist/ratchet/index.js.map +1 -0
- package/dist/reciprocity/index.d.ts +109 -0
- package/dist/reciprocity/index.d.ts.map +1 -0
- package/dist/reciprocity/index.js +311 -0
- package/dist/reciprocity/index.js.map +1 -0
- package/dist/relay/index.d.ts +87 -0
- package/dist/relay/index.d.ts.map +1 -0
- package/dist/relay/index.js +286 -0
- package/dist/relay/index.js.map +1 -0
- package/dist/routing/index.d.ts +136 -0
- package/dist/routing/index.d.ts.map +1 -0
- package/dist/routing/index.js +478 -0
- package/dist/routing/index.js.map +1 -0
- package/dist/sdk/index.d.ts +322 -0
- package/dist/sdk/index.d.ts.map +1 -0
- package/dist/sdk/index.js +1530 -0
- package/dist/sdk/index.js.map +1 -0
- package/dist/sybil/index.d.ts +123 -0
- package/dist/sybil/index.d.ts.map +1 -0
- package/dist/sybil/index.js +491 -0
- package/dist/sybil/index.js.map +1 -0
- package/dist/transport/browser/index.d.ts +34 -0
- package/dist/transport/browser/index.d.ts.map +1 -0
- package/dist/transport/browser/index.js +176 -0
- package/dist/transport/browser/index.js.map +1 -0
- package/dist/transport/local/index.d.ts +57 -0
- package/dist/transport/local/index.d.ts.map +1 -0
- package/dist/transport/local/index.js +442 -0
- package/dist/transport/local/index.js.map +1 -0
- package/dist/transport/negotiator/index.d.ts +79 -0
- package/dist/transport/negotiator/index.d.ts.map +1 -0
- package/dist/transport/negotiator/index.js +289 -0
- package/dist/transport/negotiator/index.js.map +1 -0
- package/dist/transport/node/index.d.ts +56 -0
- package/dist/transport/node/index.d.ts.map +1 -0
- package/dist/transport/node/index.js +209 -0
- package/dist/transport/node/index.js.map +1 -0
- package/dist/transport/noop/index.d.ts +11 -0
- package/dist/transport/noop/index.d.ts.map +1 -0
- package/dist/transport/noop/index.js +20 -0
- package/dist/transport/noop/index.js.map +1 -0
- package/dist/transport/p2p/index.d.ts +109 -0
- package/dist/transport/p2p/index.d.ts.map +1 -0
- package/dist/transport/p2p/index.js +237 -0
- package/dist/transport/p2p/index.js.map +1 -0
- package/dist/transport/websocket/index.d.ts +89 -0
- package/dist/transport/websocket/index.d.ts.map +1 -0
- package/dist/transport/websocket/index.js +498 -0
- package/dist/transport/websocket/index.js.map +1 -0
- package/dist/transport/websocket/serialize.d.ts +5 -0
- package/dist/transport/websocket/serialize.d.ts.map +1 -0
- package/dist/transport/websocket/serialize.js +55 -0
- package/dist/transport/websocket/serialize.js.map +1 -0
- package/dist/types.d.ts +215 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +15 -0
- package/dist/types.js.map +1 -0
- package/dist/x3dh/index.d.ts +120 -0
- package/dist/x3dh/index.d.ts.map +1 -0
- package/dist/x3dh/index.js +290 -0
- package/dist/x3dh/index.js.map +1 -0
- package/package.json +59 -0
- package/src/browser/index.ts +19 -0
- package/src/chaff/index.ts +340 -0
- package/src/cluster/index.ts +482 -0
- package/src/compliance/index.ts +407 -0
- package/src/crypto/index.ts +193 -0
- package/src/group/index.ts +719 -0
- package/src/index.ts +87 -0
- package/src/lz4js.d.ts +58 -0
- package/src/namespace/index.ts +336 -0
- package/src/node/index.ts +19 -0
- package/src/packet/index.ts +326 -0
- package/src/permissions/index.ts +405 -0
- package/src/persistence/idb-storage.ts +83 -0
- package/src/persistence/index.ts +3 -0
- package/src/persistence/node-storage.ts +96 -0
- package/src/persistence/serialization.ts +75 -0
- package/src/persistence/types.ts +33 -0
- package/src/ratchet/index.ts +363 -0
- package/src/reciprocity/index.ts +371 -0
- package/src/relay/index.ts +382 -0
- package/src/routing/index.ts +577 -0
- package/src/sdk/index.ts +1994 -0
- package/src/sybil/index.ts +661 -0
- package/src/transport/browser/index.ts +201 -0
- package/src/transport/local/index.ts +540 -0
- package/src/transport/negotiator/index.ts +397 -0
- package/src/transport/node/index.ts +234 -0
- package/src/transport/noop/index.ts +22 -0
- package/src/transport/p2p/index.ts +345 -0
- package/src/transport/websocket/index.ts +660 -0
- package/src/transport/websocket/serialize.ts +68 -0
- package/src/types.ts +275 -0
- package/src/x3dh/index.ts +388 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// MeshWhisper SDK — Node Transport
|
|
3
|
+
// Connects the SDK to a MeshWhisper Node (relay + directory).
|
|
4
|
+
// All traffic flows through the Node WebSocket endpoint; the
|
|
5
|
+
// Node routes packets by destination hash and buffers blobs
|
|
6
|
+
// for offline recipients.
|
|
7
|
+
// ============================================================
|
|
8
|
+
import { WebSocket } from 'ws';
|
|
9
|
+
import { serializePacket, deserializePacket, HEADER_SIZE, } from '../websocket/serialize.js';
|
|
10
|
+
// ---- Constants ----
|
|
11
|
+
/** Foundation-hosted relay nodes. Used when node config is "mesh". */
|
|
12
|
+
export const FOUNDATION_RELAY_NODES = [
|
|
13
|
+
'wss://relay.meshwhisper.io', // TODO: deploy actual Foundation nodes
|
|
14
|
+
];
|
|
15
|
+
const RECONNECT_BASE_MS = 1_000;
|
|
16
|
+
const RECONNECT_MAX_MS = 30_000;
|
|
17
|
+
// ---- NodeTransport ----
|
|
18
|
+
/**
|
|
19
|
+
* Transport that connects to a MeshWhisper Node for relay and
|
|
20
|
+
* store-and-forward delivery.
|
|
21
|
+
*
|
|
22
|
+
* Sends a `hello` control message on connect to register the device's
|
|
23
|
+
* current destination hashes. The Node then routes incoming packets
|
|
24
|
+
* to the registered hashes and delivers any queued blobs.
|
|
25
|
+
*
|
|
26
|
+
* All outbound packets are forwarded to the Node regardless of the
|
|
27
|
+
* `destination` argument — the packet's destHash header field is what
|
|
28
|
+
* the Node uses for routing.
|
|
29
|
+
*/
|
|
30
|
+
export class NodeTransport {
|
|
31
|
+
nodeUrl;
|
|
32
|
+
getDestHashes;
|
|
33
|
+
pushConfig;
|
|
34
|
+
type = 'internet';
|
|
35
|
+
ws = null;
|
|
36
|
+
receiveCallbacks = [];
|
|
37
|
+
running = false;
|
|
38
|
+
reconnectAttempt = 0;
|
|
39
|
+
reconnectTimer = null;
|
|
40
|
+
/**
|
|
41
|
+
* @param nodeUrl - The WebSocket URL of the Node, or "mesh" for Foundation nodes.
|
|
42
|
+
* @param getDestHashes - Callback returning the device's current dest hashes
|
|
43
|
+
* as hex strings. Called on every (re)connect so the Node always has
|
|
44
|
+
* up-to-date hashes.
|
|
45
|
+
* @param pushConfig - Optional push token to register with the Node so it can
|
|
46
|
+
* wake the device via APNs/FCM when a message arrives while offline.
|
|
47
|
+
*/
|
|
48
|
+
constructor(nodeUrl, getDestHashes, pushConfig) {
|
|
49
|
+
this.nodeUrl = nodeUrl;
|
|
50
|
+
this.getDestHashes = getDestHashes;
|
|
51
|
+
this.pushConfig = pushConfig;
|
|
52
|
+
}
|
|
53
|
+
// ---- Transport interface ----
|
|
54
|
+
async start() {
|
|
55
|
+
this.running = true;
|
|
56
|
+
await this.connect();
|
|
57
|
+
}
|
|
58
|
+
async stop() {
|
|
59
|
+
this.running = false;
|
|
60
|
+
if (this.reconnectTimer) {
|
|
61
|
+
clearTimeout(this.reconnectTimer);
|
|
62
|
+
this.reconnectTimer = null;
|
|
63
|
+
}
|
|
64
|
+
if (this.ws) {
|
|
65
|
+
this.ws.close(1000, 'shutdown');
|
|
66
|
+
this.ws = null;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async isAvailable() {
|
|
70
|
+
return this.ws?.readyState === WebSocket.OPEN;
|
|
71
|
+
}
|
|
72
|
+
async send(packet, _destination) {
|
|
73
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
74
|
+
throw new Error('NodeTransport: not connected to Node');
|
|
75
|
+
}
|
|
76
|
+
const binary = serializePacket(packet);
|
|
77
|
+
return new Promise((resolve, reject) => {
|
|
78
|
+
this.ws.send(binary, { binary: true }, (err) => {
|
|
79
|
+
if (err)
|
|
80
|
+
reject(new Error(`NodeTransport send failed: ${err.message}`));
|
|
81
|
+
else
|
|
82
|
+
resolve();
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
onReceive(callback) {
|
|
87
|
+
this.receiveCallbacks.push(callback);
|
|
88
|
+
}
|
|
89
|
+
// ---- Connection management ----
|
|
90
|
+
resolveUrl() {
|
|
91
|
+
if (this.nodeUrl === 'mesh') {
|
|
92
|
+
return FOUNDATION_RELAY_NODES[0];
|
|
93
|
+
}
|
|
94
|
+
return this.nodeUrl;
|
|
95
|
+
}
|
|
96
|
+
connect() {
|
|
97
|
+
const url = this.resolveUrl();
|
|
98
|
+
return new Promise((resolve, reject) => {
|
|
99
|
+
let resolved = false;
|
|
100
|
+
const ws = new WebSocket(url);
|
|
101
|
+
ws.binaryType = 'arraybuffer';
|
|
102
|
+
ws.on('open', () => {
|
|
103
|
+
this.ws = ws;
|
|
104
|
+
this.reconnectAttempt = 0;
|
|
105
|
+
resolved = true;
|
|
106
|
+
// Register dest hashes (and optional push token) with the Node
|
|
107
|
+
ws.send(JSON.stringify(this.buildHello()));
|
|
108
|
+
resolve();
|
|
109
|
+
});
|
|
110
|
+
ws.on('message', (raw) => {
|
|
111
|
+
this.handleMessage(raw);
|
|
112
|
+
});
|
|
113
|
+
ws.on('close', () => {
|
|
114
|
+
this.ws = null;
|
|
115
|
+
if (this.running)
|
|
116
|
+
this.scheduleReconnect();
|
|
117
|
+
});
|
|
118
|
+
ws.on('error', (err) => {
|
|
119
|
+
if (!resolved) {
|
|
120
|
+
resolved = true;
|
|
121
|
+
reject(new Error(`NodeTransport: failed to connect to ${url}: ${err.message}`));
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
scheduleReconnect() {
|
|
127
|
+
if (!this.running)
|
|
128
|
+
return;
|
|
129
|
+
const delay = Math.min(RECONNECT_BASE_MS * Math.pow(2, this.reconnectAttempt), RECONNECT_MAX_MS);
|
|
130
|
+
this.reconnectAttempt++;
|
|
131
|
+
this.reconnectTimer = setTimeout(async () => {
|
|
132
|
+
if (!this.running)
|
|
133
|
+
return;
|
|
134
|
+
try {
|
|
135
|
+
await this.connect();
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
this.scheduleReconnect();
|
|
139
|
+
}
|
|
140
|
+
}, delay);
|
|
141
|
+
}
|
|
142
|
+
// ---- Message handling ----
|
|
143
|
+
handleMessage(raw) {
|
|
144
|
+
// Binary messages are relay packets
|
|
145
|
+
if (raw instanceof ArrayBuffer && raw.byteLength >= HEADER_SIZE) {
|
|
146
|
+
try {
|
|
147
|
+
const packet = deserializePacket(new Uint8Array(raw));
|
|
148
|
+
const source = this.resolveUrl();
|
|
149
|
+
for (const cb of this.receiveCallbacks) {
|
|
150
|
+
try {
|
|
151
|
+
cb(packet, source);
|
|
152
|
+
}
|
|
153
|
+
catch { /* swallow */ }
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
// Malformed packet — discard
|
|
158
|
+
}
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
// JSON messages from the Node (informational; no action needed in v1)
|
|
162
|
+
if (typeof raw === 'string' || raw instanceof Buffer) {
|
|
163
|
+
try {
|
|
164
|
+
JSON.parse(raw.toString());
|
|
165
|
+
// Reserved for future Node→client control messages
|
|
166
|
+
}
|
|
167
|
+
catch { /* not JSON */ }
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
// ---- Dest hash refresh ----
|
|
171
|
+
/**
|
|
172
|
+
* Re-registers the device's destination hashes with the Node.
|
|
173
|
+
* Call this when the epoch hour rolls over and hashes rotate.
|
|
174
|
+
*/
|
|
175
|
+
refreshDestHashes() {
|
|
176
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN)
|
|
177
|
+
return;
|
|
178
|
+
this.ws.send(JSON.stringify(this.buildHello()));
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Update the push token (e.g. after APNs/FCM issues a new token).
|
|
182
|
+
* Re-registers with the Node immediately if connected.
|
|
183
|
+
*/
|
|
184
|
+
setPushConfig(pushConfig) {
|
|
185
|
+
this.pushConfig = pushConfig;
|
|
186
|
+
this.refreshDestHashes();
|
|
187
|
+
}
|
|
188
|
+
// ---- Private helpers ----
|
|
189
|
+
buildHello() {
|
|
190
|
+
const msg = {
|
|
191
|
+
type: 'hello',
|
|
192
|
+
destHashes: this.getDestHashes(),
|
|
193
|
+
};
|
|
194
|
+
if (this.pushConfig) {
|
|
195
|
+
msg['pushPlatform'] = this.pushConfig.platform;
|
|
196
|
+
if (this.pushConfig.platform === 'webpush') {
|
|
197
|
+
msg['pushSubscription'] = JSON.stringify(this.pushConfig.subscription);
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
msg['pushToken'] = this.pushConfig.token;
|
|
201
|
+
if (this.pushConfig.platform === 'apns' && this.pushConfig.topic) {
|
|
202
|
+
msg['pushTopic'] = this.pushConfig.topic;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return msg;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/transport/node/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Transport, Packet, BearerType } from '../../types.js';
|
|
2
|
+
export declare class NoOpTransport implements Transport {
|
|
3
|
+
readonly type: BearerType;
|
|
4
|
+
constructor(type: BearerType);
|
|
5
|
+
start(): Promise<void>;
|
|
6
|
+
stop(): Promise<void>;
|
|
7
|
+
isAvailable(): Promise<boolean>;
|
|
8
|
+
send(_packet: Packet, _destination: string): Promise<void>;
|
|
9
|
+
onReceive(_callback: (packet: Packet, source: string) => void): void;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/transport/noop/index.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEpE,qBAAa,aAAc,YAAW,SAAS;IACjC,QAAQ,CAAC,IAAI,EAAE,UAAU;gBAAhB,IAAI,EAAE,UAAU;IAE/B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IACtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IACrB,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAE/B,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhE,SAAS,CAAC,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI;CACrE"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// MeshWhisper SDK — No-Op Transport
|
|
3
|
+
// Satisfies the Transport interface but does nothing.
|
|
4
|
+
// Used as a placeholder for transports that are unavailable
|
|
5
|
+
// in the current environment (e.g. LocalTransport in a browser).
|
|
6
|
+
// ============================================================
|
|
7
|
+
export class NoOpTransport {
|
|
8
|
+
type;
|
|
9
|
+
constructor(type) {
|
|
10
|
+
this.type = type;
|
|
11
|
+
}
|
|
12
|
+
async start() { }
|
|
13
|
+
async stop() { }
|
|
14
|
+
async isAvailable() { return false; }
|
|
15
|
+
async send(_packet, _destination) {
|
|
16
|
+
throw new Error(`NoOpTransport(${this.type}): not available in this environment`);
|
|
17
|
+
}
|
|
18
|
+
onReceive(_callback) { }
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/transport/noop/index.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,oCAAoC;AACpC,sDAAsD;AACtD,4DAA4D;AAC5D,iEAAiE;AACjE,+DAA+D;AAI/D,MAAM,OAAO,aAAa;IACH;IAArB,YAAqB,IAAgB;QAAhB,SAAI,GAAJ,IAAI,CAAY;IAAG,CAAC;IAEzC,KAAK,CAAC,KAAK,KAAmB,CAAC;IAC/B,KAAK,CAAC,IAAI,KAAmB,CAAC;IAC9B,KAAK,CAAC,WAAW,KAAuB,OAAO,KAAK,CAAC,CAAC,CAAC;IAEvD,KAAK,CAAC,IAAI,CAAC,OAAe,EAAE,YAAoB;QAC9C,MAAM,IAAI,KAAK,CAAC,iBAAiB,IAAI,CAAC,IAAI,sCAAsC,CAAC,CAAC;IACpF,CAAC;IAED,SAAS,CAAC,SAAmD,IAAS,CAAC;CACxE"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import type { Packet, Transport } from '../../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Metadata about a discovered P2P peer.
|
|
4
|
+
* Provided by the native layer during discovery.
|
|
5
|
+
*/
|
|
6
|
+
export interface PeerInfo {
|
|
7
|
+
/** Human-readable name for the peer device. */
|
|
8
|
+
displayName: string;
|
|
9
|
+
/** The service identifier under which this peer was discovered. */
|
|
10
|
+
serviceId: string;
|
|
11
|
+
/** Advertised capabilities (e.g. "relay", "store-forward"). */
|
|
12
|
+
capabilities: string[];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Abstract interface that native platform code (Swift / Kotlin)
|
|
16
|
+
* must implement to provide P2P connectivity to the TypeScript SDK.
|
|
17
|
+
*
|
|
18
|
+
* - iOS: backed by Apple Multipeer Connectivity Framework
|
|
19
|
+
* (BLE discovery + Wi-Fi Direct data transfer).
|
|
20
|
+
* - Android: backed by Google Nearby Connections API
|
|
21
|
+
* (Bluetooth + Wi-Fi).
|
|
22
|
+
*
|
|
23
|
+
* All methods are designed to be thin wrappers around the native
|
|
24
|
+
* APIs so the bridge implementation stays minimal.
|
|
25
|
+
*/
|
|
26
|
+
export interface PlatformP2PBridge {
|
|
27
|
+
/** Begin advertising this device for the given service. */
|
|
28
|
+
startAdvertising(serviceId: string): Promise<void>;
|
|
29
|
+
/** Begin scanning for peers advertising the given service. */
|
|
30
|
+
startDiscovery(serviceId: string): Promise<void>;
|
|
31
|
+
/** Stop advertising. */
|
|
32
|
+
stopAdvertising(): Promise<void>;
|
|
33
|
+
/** Stop discovery scanning. */
|
|
34
|
+
stopDiscovery(): Promise<void>;
|
|
35
|
+
/** Send raw bytes to a connected peer. */
|
|
36
|
+
sendData(peerId: string, data: Uint8Array): Promise<void>;
|
|
37
|
+
/** Register a callback invoked when a new peer is discovered. */
|
|
38
|
+
onPeerDiscovered(callback: (peerId: string, info: PeerInfo) => void): void;
|
|
39
|
+
/** Register a callback invoked when a previously discovered peer is lost. */
|
|
40
|
+
onPeerLost(callback: (peerId: string) => void): void;
|
|
41
|
+
/** Register a callback invoked when data is received from a peer. */
|
|
42
|
+
onDataReceived(callback: (peerId: string, data: Uint8Array) => void): void;
|
|
43
|
+
/** Returns the IDs of all currently connected peers. */
|
|
44
|
+
getConnectedPeers(): string[];
|
|
45
|
+
/**
|
|
46
|
+
* Returns true if the underlying native P2P framework is
|
|
47
|
+
* available on this device / OS version.
|
|
48
|
+
*/
|
|
49
|
+
isSupported(): boolean;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Registers a platform-specific P2P bridge implementation.
|
|
53
|
+
*
|
|
54
|
+
* Called by native code (via JSI on React Native, or similar
|
|
55
|
+
* interop layers) to inject the real Multipeer / Nearby
|
|
56
|
+
* Connections wrapper at runtime.
|
|
57
|
+
*
|
|
58
|
+
* @param bridge - The native bridge implementation.
|
|
59
|
+
*/
|
|
60
|
+
export declare function registerPlatformBridge(bridge: PlatformP2PBridge): void;
|
|
61
|
+
/**
|
|
62
|
+
* Generates a platform-appropriate service identifier from an
|
|
63
|
+
* application namespace string.
|
|
64
|
+
*
|
|
65
|
+
* Both Apple Multipeer Connectivity and Android NSD impose
|
|
66
|
+
* restrictions on service type identifiers (length, allowed
|
|
67
|
+
* characters). This function produces a short, deterministic,
|
|
68
|
+
* lowercase-alphanumeric ID derived from the namespace.
|
|
69
|
+
*
|
|
70
|
+
* @param namespace - The application namespace (e.g. "com.example.chat").
|
|
71
|
+
* @returns A service ID string safe for both iOS and Android.
|
|
72
|
+
*/
|
|
73
|
+
export declare function generateServiceId(namespace: string): string;
|
|
74
|
+
/**
|
|
75
|
+
* PlatformP2PTransport adapts a PlatformP2PBridge into the
|
|
76
|
+
* SDK's Transport interface.
|
|
77
|
+
*
|
|
78
|
+
* When a native bridge has been registered via
|
|
79
|
+
* `registerPlatformBridge()`, this transport serializes Packets
|
|
80
|
+
* to wire bytes, sends them through the bridge, and
|
|
81
|
+
* deserializes incoming bytes back into Packets for the mesh
|
|
82
|
+
* router.
|
|
83
|
+
*
|
|
84
|
+
* When no bridge is registered, the transport reports itself
|
|
85
|
+
* as unavailable and all operations are safe no-ops.
|
|
86
|
+
*/
|
|
87
|
+
export declare class PlatformP2PTransport implements Transport {
|
|
88
|
+
readonly type: "platform_p2p";
|
|
89
|
+
private readonly serviceId;
|
|
90
|
+
private receiveCallback;
|
|
91
|
+
private running;
|
|
92
|
+
/**
|
|
93
|
+
* @param namespace - Application namespace used to derive
|
|
94
|
+
* the P2P service identifier.
|
|
95
|
+
*/
|
|
96
|
+
constructor(namespace: string);
|
|
97
|
+
/**
|
|
98
|
+
* Returns the bridge currently in use. Reads from the
|
|
99
|
+
* module-level registry so that bridges registered after
|
|
100
|
+
* construction are picked up automatically.
|
|
101
|
+
*/
|
|
102
|
+
private get bridge();
|
|
103
|
+
isAvailable(): Promise<boolean>;
|
|
104
|
+
start(): Promise<void>;
|
|
105
|
+
stop(): Promise<void>;
|
|
106
|
+
send(packet: Packet, destination: string): Promise<void>;
|
|
107
|
+
onReceive(callback: (packet: Packet, source: string) => void): void;
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/transport/p2p/index.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,EAAe,MAAM,gBAAgB,CAAC;AAIrE;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB,+CAA+C;IAC/C,WAAW,EAAE,MAAM,CAAC;IACpB,mEAAmE;IACnE,SAAS,EAAE,MAAM,CAAC;IAClB,+DAA+D;IAC/D,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,iBAAiB;IAChC,2DAA2D;IAC3D,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnD,8DAA8D;IAC9D,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjD,wBAAwB;IACxB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjC,+BAA+B;IAC/B,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAE/B,0CAA0C;IAC1C,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE1D,iEAAiE;IACjE,gBAAgB,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,KAAK,IAAI,GAAG,IAAI,CAAC;IAE3E,6EAA6E;IAC7E,UAAU,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IAErD,qEAAqE;IACrE,cAAc,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,KAAK,IAAI,GAAG,IAAI,CAAC;IAE3E,wDAAwD;IACxD,iBAAiB,IAAI,MAAM,EAAE,CAAC;IAE9B;;;OAGG;IACH,WAAW,IAAI,OAAO,CAAC;CACxB;AA4ID;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAEtE;AAOD;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAY3D;AAID;;;;;;;;;;;;GAYG;AACH,qBAAa,oBAAqB,YAAW,SAAS;IACpD,QAAQ,CAAC,IAAI,EAAG,cAAc,CAAU;IAExC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,eAAe,CAA2D;IAClF,OAAO,CAAC,OAAO,CAAS;IAExB;;;OAGG;gBACS,SAAS,EAAE,MAAM;IAI7B;;;;OAIG;IACH,OAAO,KAAK,MAAM,GAEjB;IAIK,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAI/B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA0BtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAQrB,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAO9D,SAAS,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI;CAGpE"}
|
|
@@ -0,0 +1,237 @@
|
|
|
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
|
+
// ---- Packet Serialization ----
|
|
10
|
+
/**
|
|
11
|
+
* Minimum header size in bytes:
|
|
12
|
+
* version (1) + flags (1) + destHash (8) + senderEphemeralId (16)
|
|
13
|
+
* + ttl (1) + payloadLength (4) = 31
|
|
14
|
+
*/
|
|
15
|
+
const HEADER_SIZE = 31;
|
|
16
|
+
/**
|
|
17
|
+
* Serializes a Packet into a compact binary format for transmission
|
|
18
|
+
* over the native P2P bridge.
|
|
19
|
+
*
|
|
20
|
+
* Wire format (big-endian):
|
|
21
|
+
* [0] u8 version
|
|
22
|
+
* [1] u8 flags
|
|
23
|
+
* [2..9] 8B destHash
|
|
24
|
+
* [10..25] 16B senderEphemeralId
|
|
25
|
+
* [26] u8 ttl
|
|
26
|
+
* [27..30] u32 payloadLength
|
|
27
|
+
* [31..] var encryptedPayload
|
|
28
|
+
*/
|
|
29
|
+
function serializePacket(packet) {
|
|
30
|
+
const totalLength = HEADER_SIZE + packet.encryptedPayload.length;
|
|
31
|
+
const buffer = new Uint8Array(totalLength);
|
|
32
|
+
const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
33
|
+
let offset = 0;
|
|
34
|
+
// version (u8)
|
|
35
|
+
view.setUint8(offset, packet.version);
|
|
36
|
+
offset += 1;
|
|
37
|
+
// flags (u8)
|
|
38
|
+
view.setUint8(offset, packet.flags);
|
|
39
|
+
offset += 1;
|
|
40
|
+
// destHash (8 bytes)
|
|
41
|
+
buffer.set(packet.destHash, offset);
|
|
42
|
+
offset += 8;
|
|
43
|
+
// senderEphemeralId (16 bytes)
|
|
44
|
+
buffer.set(packet.senderEphemeralId, offset);
|
|
45
|
+
offset += 16;
|
|
46
|
+
// ttl (u8)
|
|
47
|
+
view.setUint8(offset, packet.ttl);
|
|
48
|
+
offset += 1;
|
|
49
|
+
// payloadLength (u32 big-endian)
|
|
50
|
+
view.setUint32(offset, packet.payloadLength, false);
|
|
51
|
+
offset += 4;
|
|
52
|
+
// encryptedPayload
|
|
53
|
+
buffer.set(packet.encryptedPayload, offset);
|
|
54
|
+
return buffer;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Deserializes a Uint8Array back into a Packet.
|
|
58
|
+
* Throws if the data is too short or the payload length mismatches.
|
|
59
|
+
*/
|
|
60
|
+
function deserializePacket(data) {
|
|
61
|
+
if (data.length < HEADER_SIZE) {
|
|
62
|
+
throw new Error(`P2P: packet too short (${data.length} bytes, need at least ${HEADER_SIZE})`);
|
|
63
|
+
}
|
|
64
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
65
|
+
let offset = 0;
|
|
66
|
+
const version = view.getUint8(offset);
|
|
67
|
+
offset += 1;
|
|
68
|
+
const flags = view.getUint8(offset);
|
|
69
|
+
offset += 1;
|
|
70
|
+
const destHash = data.slice(offset, offset + 8);
|
|
71
|
+
offset += 8;
|
|
72
|
+
const senderEphemeralId = data.slice(offset, offset + 16);
|
|
73
|
+
offset += 16;
|
|
74
|
+
const ttl = view.getUint8(offset);
|
|
75
|
+
offset += 1;
|
|
76
|
+
const payloadLength = view.getUint32(offset, false);
|
|
77
|
+
offset += 4;
|
|
78
|
+
const encryptedPayload = data.slice(offset, offset + payloadLength);
|
|
79
|
+
if (encryptedPayload.length !== payloadLength) {
|
|
80
|
+
throw new Error(`P2P: payload length mismatch (header says ${payloadLength}, got ${encryptedPayload.length})`);
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
version,
|
|
84
|
+
flags,
|
|
85
|
+
destHash,
|
|
86
|
+
senderEphemeralId,
|
|
87
|
+
ttl,
|
|
88
|
+
payloadLength,
|
|
89
|
+
encryptedPayload,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
// ---- No-Op Bridge ----
|
|
93
|
+
/**
|
|
94
|
+
* Default bridge used when no native implementation is registered.
|
|
95
|
+
* All methods are safe no-ops. isSupported() returns false so the
|
|
96
|
+
* transport layer knows to skip this bearer.
|
|
97
|
+
*/
|
|
98
|
+
class NoOpBridge {
|
|
99
|
+
async startAdvertising(_serviceId) { }
|
|
100
|
+
async startDiscovery(_serviceId) { }
|
|
101
|
+
async stopAdvertising() { }
|
|
102
|
+
async stopDiscovery() { }
|
|
103
|
+
async sendData(_peerId, _data) { }
|
|
104
|
+
onPeerDiscovered(_callback) { }
|
|
105
|
+
onPeerLost(_callback) { }
|
|
106
|
+
onDataReceived(_callback) { }
|
|
107
|
+
getConnectedPeers() {
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
isSupported() {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// ---- Bridge Registry ----
|
|
115
|
+
/** The currently registered bridge (defaults to NoOpBridge). */
|
|
116
|
+
let activeBridge = new NoOpBridge();
|
|
117
|
+
/**
|
|
118
|
+
* Registers a platform-specific P2P bridge implementation.
|
|
119
|
+
*
|
|
120
|
+
* Called by native code (via JSI on React Native, or similar
|
|
121
|
+
* interop layers) to inject the real Multipeer / Nearby
|
|
122
|
+
* Connections wrapper at runtime.
|
|
123
|
+
*
|
|
124
|
+
* @param bridge - The native bridge implementation.
|
|
125
|
+
*/
|
|
126
|
+
export function registerPlatformBridge(bridge) {
|
|
127
|
+
activeBridge = bridge;
|
|
128
|
+
}
|
|
129
|
+
// ---- Service ID Generation ----
|
|
130
|
+
/** Max length for Bonjour / NSD service types. */
|
|
131
|
+
const MAX_SERVICE_ID_LENGTH = 15;
|
|
132
|
+
/**
|
|
133
|
+
* Generates a platform-appropriate service identifier from an
|
|
134
|
+
* application namespace string.
|
|
135
|
+
*
|
|
136
|
+
* Both Apple Multipeer Connectivity and Android NSD impose
|
|
137
|
+
* restrictions on service type identifiers (length, allowed
|
|
138
|
+
* characters). This function produces a short, deterministic,
|
|
139
|
+
* lowercase-alphanumeric ID derived from the namespace.
|
|
140
|
+
*
|
|
141
|
+
* @param namespace - The application namespace (e.g. "com.example.chat").
|
|
142
|
+
* @returns A service ID string safe for both iOS and Android.
|
|
143
|
+
*/
|
|
144
|
+
export function generateServiceId(namespace) {
|
|
145
|
+
// Simple FNV-1a 32-bit hash for determinism without heavy deps.
|
|
146
|
+
let h = 0x811c9dc5;
|
|
147
|
+
for (let i = 0; i < namespace.length; i++) {
|
|
148
|
+
h ^= namespace.charCodeAt(i);
|
|
149
|
+
h = Math.imul(h, 0x01000193);
|
|
150
|
+
}
|
|
151
|
+
// Unsigned conversion then base-36 for compact alphanumeric output.
|
|
152
|
+
const hashStr = (h >>> 0).toString(36);
|
|
153
|
+
// Prefix with "mw-" (MeshWhisper) for readability, truncate to limit.
|
|
154
|
+
const serviceId = `mw-${hashStr}`;
|
|
155
|
+
return serviceId.slice(0, MAX_SERVICE_ID_LENGTH);
|
|
156
|
+
}
|
|
157
|
+
// ---- Transport Implementation ----
|
|
158
|
+
/**
|
|
159
|
+
* PlatformP2PTransport adapts a PlatformP2PBridge into the
|
|
160
|
+
* SDK's Transport interface.
|
|
161
|
+
*
|
|
162
|
+
* When a native bridge has been registered via
|
|
163
|
+
* `registerPlatformBridge()`, this transport serializes Packets
|
|
164
|
+
* to wire bytes, sends them through the bridge, and
|
|
165
|
+
* deserializes incoming bytes back into Packets for the mesh
|
|
166
|
+
* router.
|
|
167
|
+
*
|
|
168
|
+
* When no bridge is registered, the transport reports itself
|
|
169
|
+
* as unavailable and all operations are safe no-ops.
|
|
170
|
+
*/
|
|
171
|
+
export class PlatformP2PTransport {
|
|
172
|
+
type = 'platform_p2p';
|
|
173
|
+
serviceId;
|
|
174
|
+
receiveCallback = null;
|
|
175
|
+
running = false;
|
|
176
|
+
/**
|
|
177
|
+
* @param namespace - Application namespace used to derive
|
|
178
|
+
* the P2P service identifier.
|
|
179
|
+
*/
|
|
180
|
+
constructor(namespace) {
|
|
181
|
+
this.serviceId = generateServiceId(namespace);
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Returns the bridge currently in use. Reads from the
|
|
185
|
+
* module-level registry so that bridges registered after
|
|
186
|
+
* construction are picked up automatically.
|
|
187
|
+
*/
|
|
188
|
+
get bridge() {
|
|
189
|
+
return activeBridge;
|
|
190
|
+
}
|
|
191
|
+
// ---- Transport interface ----
|
|
192
|
+
async isAvailable() {
|
|
193
|
+
return this.bridge.isSupported();
|
|
194
|
+
}
|
|
195
|
+
async start() {
|
|
196
|
+
if (this.running)
|
|
197
|
+
return;
|
|
198
|
+
if (!this.bridge.isSupported()) {
|
|
199
|
+
// Silently skip — the transport negotiator will use
|
|
200
|
+
// the next bearer in priority order.
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
// Wire up data reception.
|
|
204
|
+
this.bridge.onDataReceived((peerId, data) => {
|
|
205
|
+
if (!this.receiveCallback)
|
|
206
|
+
return;
|
|
207
|
+
try {
|
|
208
|
+
const packet = deserializePacket(data);
|
|
209
|
+
this.receiveCallback(packet, peerId);
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
// Malformed packet — drop silently. A production build
|
|
213
|
+
// may want to emit a diagnostic event here.
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
await this.bridge.startAdvertising(this.serviceId);
|
|
217
|
+
await this.bridge.startDiscovery(this.serviceId);
|
|
218
|
+
this.running = true;
|
|
219
|
+
}
|
|
220
|
+
async stop() {
|
|
221
|
+
if (!this.running)
|
|
222
|
+
return;
|
|
223
|
+
await this.bridge.stopAdvertising();
|
|
224
|
+
await this.bridge.stopDiscovery();
|
|
225
|
+
this.running = false;
|
|
226
|
+
}
|
|
227
|
+
async send(packet, destination) {
|
|
228
|
+
if (!this.running || !this.bridge.isSupported())
|
|
229
|
+
return;
|
|
230
|
+
const data = serializePacket(packet);
|
|
231
|
+
await this.bridge.sendData(destination, data);
|
|
232
|
+
}
|
|
233
|
+
onReceive(callback) {
|
|
234
|
+
this.receiveCallback = callback;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/transport/p2p/index.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,2CAA2C;AAC3C,gEAAgE;AAChE,4DAA4D;AAC5D,iEAAiE;AACjE,0DAA0D;AAC1D,gDAAgD;AAChD,+DAA+D;AAkE/D,iCAAiC;AAEjC;;;;GAIG;AACH,MAAM,WAAW,GAAG,EAAE,CAAC;AAEvB;;;;;;;;;;;;GAYG;AACH,SAAS,eAAe,CAAC,MAAc;IACrC,MAAM,WAAW,GAAG,WAAW,GAAG,MAAM,CAAC,gBAAgB,CAAC,MAAM,CAAC;IACjE,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,WAAW,CAAC,CAAC;IAC3C,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;IAE/E,IAAI,MAAM,GAAG,CAAC,CAAC;IAEf,eAAe;IACf,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IACtC,MAAM,IAAI,CAAC,CAAC;IAEZ,aAAa;IACb,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,KAAe,CAAC,CAAC;IAC9C,MAAM,IAAI,CAAC,CAAC;IAEZ,qBAAqB;IACrB,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACpC,MAAM,IAAI,CAAC,CAAC;IAEZ,+BAA+B;IAC/B,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC;IAC7C,MAAM,IAAI,EAAE,CAAC;IAEb,WAAW;IACX,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;IAClC,MAAM,IAAI,CAAC,CAAC;IAEZ,iCAAiC;IACjC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;IACpD,MAAM,IAAI,CAAC,CAAC;IAEZ,mBAAmB;IACnB,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC;IAE5C,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,SAAS,iBAAiB,CAAC,IAAgB;IACzC,IAAI,IAAI,CAAC,MAAM,GAAG,WAAW,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CACb,0BAA0B,IAAI,CAAC,MAAM,yBAAyB,WAAW,GAAG,CAC7E,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;IACzE,IAAI,MAAM,GAAG,CAAC,CAAC;IAEf,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACtC,MAAM,IAAI,CAAC,CAAC;IAEZ,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAgB,CAAC;IACnD,MAAM,IAAI,CAAC,CAAC;IAEZ,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC;IAChD,MAAM,IAAI,CAAC,CAAC;IAEZ,MAAM,iBAAiB,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,EAAE,CAAC,CAAC;IAC1D,MAAM,IAAI,EAAE,CAAC;IAEb,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAClC,MAAM,IAAI,CAAC,CAAC;IAEZ,MAAM,aAAa,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACpD,MAAM,IAAI,CAAC,CAAC;IAEZ,MAAM,gBAAgB,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,CAAC,CAAC;IACpE,IAAI,gBAAgB,CAAC,MAAM,KAAK,aAAa,EAAE,CAAC;QAC9C,MAAM,IAAI,KAAK,CACb,6CAA6C,aAAa,SAAS,gBAAgB,CAAC,MAAM,GAAG,CAC9F,CAAC;IACJ,CAAC;IAED,OAAO;QACL,OAAO;QACP,KAAK;QACL,QAAQ;QACR,iBAAiB;QACjB,GAAG;QACH,aAAa;QACb,gBAAgB;KACjB,CAAC;AACJ,CAAC;AAED,yBAAyB;AAEzB;;;;GAIG;AACH,MAAM,UAAU;IACd,KAAK,CAAC,gBAAgB,CAAC,UAAkB,IAAkB,CAAC;IAC5D,KAAK,CAAC,cAAc,CAAC,UAAkB,IAAkB,CAAC;IAC1D,KAAK,CAAC,eAAe,KAAmB,CAAC;IACzC,KAAK,CAAC,aAAa,KAAmB,CAAC;IACvC,KAAK,CAAC,QAAQ,CAAC,OAAe,EAAE,KAAiB,IAAkB,CAAC;IACpE,gBAAgB,CAAC,SAAmD,IAAS,CAAC;IAC9E,UAAU,CAAC,SAAmC,IAAS,CAAC;IACxD,cAAc,CAAC,SAAqD,IAAS,CAAC;IAC9E,iBAAiB;QACf,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,WAAW;QACT,OAAO,KAAK,CAAC;IACf,CAAC;CACF;AAED,4BAA4B;AAE5B,gEAAgE;AAChE,IAAI,YAAY,GAAsB,IAAI,UAAU,EAAE,CAAC;AAEvD;;;;;;;;GAQG;AACH,MAAM,UAAU,sBAAsB,CAAC,MAAyB;IAC9D,YAAY,GAAG,MAAM,CAAC;AACxB,CAAC;AAED,kCAAkC;AAElC,kDAAkD;AAClD,MAAM,qBAAqB,GAAG,EAAE,CAAC;AAEjC;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,iBAAiB,CAAC,SAAiB;IACjD,gEAAgE;IAChE,IAAI,CAAC,GAAG,UAAU,CAAC;IACnB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC1C,CAAC,IAAI,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAC7B,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;IAC/B,CAAC;IACD,oEAAoE;IACpE,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACvC,sEAAsE;IACtE,MAAM,SAAS,GAAG,MAAM,OAAO,EAAE,CAAC;IAClC,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,qBAAqB,CAAC,CAAC;AACnD,CAAC;AAED,qCAAqC;AAErC;;;;;;;;;;;;GAYG;AACH,MAAM,OAAO,oBAAoB;IACtB,IAAI,GAAG,cAAuB,CAAC;IAEvB,SAAS,CAAS;IAC3B,eAAe,GAAsD,IAAI,CAAC;IAC1E,OAAO,GAAG,KAAK,CAAC;IAExB;;;OAGG;IACH,YAAY,SAAiB;QAC3B,IAAI,CAAC,SAAS,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAChD,CAAC;IAED;;;;OAIG;IACH,IAAY,MAAM;QAChB,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,gCAAgC;IAEhC,KAAK,CAAC,WAAW;QACf,OAAO,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO;QAEzB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC;YAC/B,oDAAoD;YACpD,qCAAqC;YACrC,OAAO;QACT,CAAC;QAED,0BAA0B;QAC1B,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,MAAc,EAAE,IAAgB,EAAE,EAAE;YAC9D,IAAI,CAAC,IAAI,CAAC,eAAe;gBAAE,OAAO;YAClC,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;gBACvC,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YACvC,CAAC;YAAC,MAAM,CAAC;gBACP,wDAAwD;gBACxD,4CAA4C;YAC9C,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,MAAM,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACnD,MAAM,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACjD,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;IACtB,CAAC;IAED,KAAK,CAAC,IAAI;QACR,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAE1B,MAAM,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC;QACpC,MAAM,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC;QAClC,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,MAAc,EAAE,WAAmB;QAC5C,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE;YAAE,OAAO;QAExD,MAAM,IAAI,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;QACrC,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;IAChD,CAAC;IAED,SAAS,CAAC,QAAkD;QAC1D,IAAI,CAAC,eAAe,GAAG,QAAQ,CAAC;IAClC,CAAC;CACF"}
|