@persistica/flux-agent 0.0.1 → 0.0.2

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/index.cjs CHANGED
@@ -17,19 +17,1083 @@ var __copyProps = (to, from, except, desc) => {
17
17
  };
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
 
20
- // packages/flux/agent-2/src/index.ts
21
- var src_exports = {};
22
- __export(src_exports, {
23
- persisticaFluxAgent2: () => persisticaFluxAgent2
20
+ // packages/flux/agent/src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ FluxAgent: () => FluxAgent
24
24
  });
25
- module.exports = __toCommonJS(src_exports);
25
+ module.exports = __toCommonJS(index_exports);
26
26
 
27
- // packages/flux/agent-2/src/lib/persistica-flux-agent-2.ts
28
- function persisticaFluxAgent2() {
29
- return "persistica-flux-agent-2";
27
+ // libs/flux/shared/types/src/lib/channel.type.ts
28
+ var validateChannelNameOrThrow = (channelName) => {
29
+ if (channelName.includes(":")) {
30
+ throw new Error("Channel name cannot contain :");
31
+ }
32
+ if (channelName.includes("/")) {
33
+ throw new Error("Channel name cannot contain /");
34
+ }
35
+ if (!/^[A-Za-z0-9-]+$/.test(channelName)) {
36
+ throw new Error("Channel name can only contain letters, numbers and dashes ('-')");
37
+ }
38
+ if (channelName.length > 100) {
39
+ throw new Error("Channel name cannot be longer than 100 characters");
40
+ }
41
+ return true;
42
+ };
43
+
44
+ // libs/flux/shared/types/src/lib/auth/auth.fn.ts
45
+ var checkAuthTicketShape = (ticket) => {
46
+ return typeof ticket === "string";
47
+ };
48
+
49
+ // libs/flux/shared/types/src/lib/low-level-ws.interfaces.ts
50
+ var RPC_REQUEST = "rpc-request";
51
+ var RPC_RESPONSE = "rpc-response";
52
+ var CONNECT_TO_CLIENT = "connect-to-client";
53
+ var NETWORK_CHANNEL_PUBLISH = "nc-pub";
54
+ var ON_NETWORK_CHANNEL_PUBLISH = "nc-on-pub";
55
+ var SUBSCRIBE_NETWORK_CHANNEL_NAME = "subscribe-to-nc-name";
56
+ var SUBSCRIBED_NETWORK_CHANNEL_NAME = "subscribed-to-nc-name";
57
+ var UNSUBSCRIBE_NETWORK_CHANNEL_NAME = "unsubscribe-nc-name";
58
+ var AUTHORITY_CHANNEL_SUBSCRIBE = "acs";
59
+ var AUTHORITY_DISCONNECT_AGENT = "ada";
60
+
61
+ // libs/flux/shared/types/src/lib/client.type.ts
62
+ var validateAgentUIDOrThrow = (clientOwnUID) => {
63
+ if (typeof clientOwnUID !== "string") {
64
+ throw new Error("UID must be a string");
65
+ }
66
+ if (!/^[A-Za-z0-9-]+$/.test(clientOwnUID)) {
67
+ throw new Error("UID can only contain letters, numbers and dashes ('-')");
68
+ }
69
+ if (clientOwnUID.length > 50) {
70
+ throw new Error("UID cannot be longer than 50 characters");
71
+ }
72
+ return true;
73
+ };
74
+
75
+ // libs/flux/shared/ws/src/lib/rpc/rpc-server.class.ts
76
+ var RPCServer = class {
77
+ methods = /* @__PURE__ */ new Map();
78
+ /**
79
+ * Registers a method to be called by the client.
80
+ *
81
+ * @param { TMethodName } methodName
82
+ * @param { TCallback } fn
83
+ *
84
+ * @returns { void }
85
+ */
86
+ registerMethod(methodName, fn) {
87
+ const existingMethod = this.methods.get(methodName);
88
+ if (existingMethod && existingMethod !== fn) {
89
+ throw new Error(`Method '${methodName}' already registered`);
90
+ }
91
+ this.methods.set(methodName, fn);
92
+ }
93
+ /**
94
+ * Handles the request from the client.
95
+ *
96
+ * @param { RPCRequest } request
97
+ * @param { Function } sendToRPCClient
98
+ *
99
+ * @returns { void }
100
+ */
101
+ async handleMessage(request, sendToRPCClient) {
102
+ const response = {
103
+ id: request.id,
104
+ rpcProcessAddress: request.originProcessAddress,
105
+ result: null
106
+ };
107
+ const method = this.methods.get(request.method);
108
+ if (method) {
109
+ try {
110
+ response.result = await method(...request.params);
111
+ } catch (error) {
112
+ response.error = error.message;
113
+ }
114
+ } else {
115
+ response.error = `Method '${request.method}' not found`;
116
+ }
117
+ sendToRPCClient(response);
118
+ }
119
+ };
120
+
121
+ // libs/flux/shared/ws/src/lib/ws-client.ts
122
+ var WebSocketClient = class extends RPCServer {
123
+ options;
124
+ eventListeners = {
125
+ open: [],
126
+ message: [],
127
+ close: [],
128
+ error: [],
129
+ connecting: []
130
+ };
131
+ reconnectAttempts = 0;
132
+ ws;
133
+ isOpen = false;
134
+ // Is the connection open
135
+ constructor(options) {
136
+ super();
137
+ this.options = {
138
+ autoReconnect: true,
139
+ reconnectDelay: 1e3,
140
+ heartbeatInterval: 6e4,
141
+ connectionTimeout: 5e3,
142
+ ...options
143
+ };
144
+ }
145
+ /**
146
+ *
147
+ * @returns { Promise<void> }
148
+ */
149
+ connect() {
150
+ return new Promise((resolve, reject) => {
151
+ this.emit("connecting", this.reconnectAttempts);
152
+ this.ws = new WebSocket(
153
+ this.options.url
154
+ );
155
+ const timeout = setTimeout(() => {
156
+ if (this.ws?.readyState !== WebSocket.OPEN) {
157
+ this.ws?.close();
158
+ reject(new Error("Connection timeout"));
159
+ }
160
+ }, this.options.connectionTimeout);
161
+ this.ws.onopen = () => {
162
+ clearTimeout(timeout);
163
+ this.reconnectAttempts = 0;
164
+ this.isOpen = true;
165
+ this.emit("open");
166
+ resolve();
167
+ };
168
+ this.ws.onmessage = (event) => {
169
+ this.emit("message", event.data);
170
+ };
171
+ this.ws.onclose = () => {
172
+ clearTimeout(timeout);
173
+ if (this.isOpen) {
174
+ this.emit("close");
175
+ this.isOpen = false;
176
+ }
177
+ if (this.options.autoReconnect && (this.options.retries === void 0 || this.reconnectAttempts < this.options.retries)) {
178
+ setTimeout(() => {
179
+ this.reconnectAttempts++;
180
+ this.connect();
181
+ }, Math.min(this.options.reconnectDelay ?? Number.POSITIVE_INFINITY, this.options.reconnectDelay * (this.reconnectAttempts || 1)));
182
+ } else if (this.options.autoReconnect) {
183
+ this.emit("error", new Error("Connection failed: retries exhausted"));
184
+ }
185
+ };
186
+ this.ws.onerror = (event) => {
187
+ if (event?.message?.includes("Failed to connect")) {
188
+ } else {
189
+ console.log("\u274C Error", event.message);
190
+ }
191
+ };
192
+ });
193
+ }
194
+ emit(event, data) {
195
+ for (const listener of this.eventListeners[event]) {
196
+ listener(data);
197
+ }
198
+ }
199
+ on(event, listener) {
200
+ this.eventListeners[event].push(listener);
201
+ return this;
202
+ }
203
+ /**
204
+ * Clears all subscribers.
205
+ *
206
+ * @returns { void }
207
+ */
208
+ clearEventSubscribers() {
209
+ this.eventListeners.open = [];
210
+ this.eventListeners.message = [];
211
+ this.eventListeners.close = [];
212
+ this.eventListeners.connecting = [];
213
+ this.eventListeners.error = [];
214
+ }
215
+ send(message) {
216
+ if (this.ws?.readyState === WebSocket.OPEN) {
217
+ this.ws.send(message);
218
+ } else {
219
+ console.warn("WebSocket not open.");
220
+ }
221
+ }
222
+ close() {
223
+ this.options.autoReconnect = false;
224
+ this.ws?.close();
225
+ this.clearEventSubscribers();
226
+ }
227
+ };
228
+ var FluxWebSocketClientConnection = class extends WebSocketClient {
229
+ };
230
+
231
+ // packages/flux/agent/src/lib/flux-remote-client.class.ts
232
+ var FluxRemoteClient = class {
233
+ constructor(iceConnection) {
234
+ this.iceConnection = iceConnection;
235
+ }
236
+ callProcedure() {
237
+ }
238
+ send(message) {
239
+ this.iceConnection.sendMessage(message);
240
+ }
241
+ };
242
+
243
+ // packages/flux/agent/src/lib/connector/low-level-com/web-rtc/ice-connection.ts
244
+ var peerConnectionConfig = {
245
+ iceServers: [
246
+ { urls: "stun:stun.stunprotocol.org:3478" },
247
+ { urls: "stun:stun2.1.google.com:19302" },
248
+ { urls: "stun:stun.l.google.com:19302" }
249
+ ]
250
+ };
251
+ var ICEConnection = class {
252
+ constructor(_fluxWebSocketClientConnection, _stateChange) {
253
+ this._fluxWebSocketClientConnection = _fluxWebSocketClientConnection;
254
+ this._stateChange = _stateChange;
255
+ this.peerConnection.ondatachannel = (event) => {
256
+ console.log("*********** GOT DATAT SCHANNNEL ");
257
+ this.dataChannel = event.channel;
258
+ this.dataChannel.onopen = () => console.log("ReceiveChannel opened");
259
+ this.dataChannel.onmessage = (event2) => {
260
+ console.log("Receiving ICE event", event2);
261
+ };
262
+ this.dataChannel.onopen = () => {
263
+ console.log("Opened");
264
+ };
265
+ this.dataChannel.onclose = () => {
266
+ console.log("Opened");
267
+ };
268
+ };
269
+ this.peerConnection.onicecandidate = (event) => {
270
+ if (event.candidate) {
271
+ console.log("Send this ICE Candidate to the peer");
272
+ }
273
+ if (event.candidate === null) {
274
+ this.offerSDP = JSON.stringify(this.peerConnection.localDescription);
275
+ console.log("offerSDP:", this.offerSDP);
276
+ }
277
+ };
278
+ this._fluxWebSocketClientConnection.registerMethod("createOffer", this.createOffer.bind(this));
279
+ this._fluxWebSocketClientConnection.registerMethod("acceptOffer", this.acceptOffer.bind(this));
280
+ this._fluxWebSocketClientConnection.registerMethod("acceptAnswer", this.acceptAnswer.bind(this));
281
+ this._fluxWebSocketClientConnection.registerMethod("answerAcceptedByInitiator", this.answerAcceptedByInitiator.bind(this));
282
+ }
283
+ //extends RPCServer<'createOffer' | 'acceptOffer' | 'acceptAnswer'> {
284
+ peerConnection = new RTCPeerConnection(peerConnectionConfig);
285
+ dataChannel;
286
+ // = this.peerConnection.createDataChannel('flux-channel');
287
+ offerSDP;
288
+ /**
289
+ * This offer must be sent to the other peer.
290
+ */
291
+ async createOffer() {
292
+ console.log("creating offer and data channel");
293
+ this.dataChannel = this.peerConnection.createDataChannel("flux-channel");
294
+ this._stateChange("creating-offer");
295
+ const offer = await this.peerConnection.createOffer();
296
+ await this.peerConnection.setLocalDescription(offer);
297
+ console.log("Send this Offer to the peer:", offer);
298
+ return offer;
299
+ }
300
+ /**
301
+ *
302
+ * @param { RTCSessionDescriptionInit } offer
303
+ */
304
+ async acceptOffer(offer) {
305
+ this._stateChange("setting-remote-offer");
306
+ await this.peerConnection.setRemoteDescription(new RTCSessionDescription(offer));
307
+ const answer = await this.peerConnection.createAnswer();
308
+ this._stateChange("creating-answer");
309
+ await this.peerConnection.setLocalDescription(answer);
310
+ console.log("Send this Answer to the peer");
311
+ return answer;
312
+ }
313
+ async acceptAnswer(answer) {
314
+ this._stateChange("setting-remote-answer");
315
+ await this.peerConnection.setRemoteDescription(new RTCSessionDescription(answer));
316
+ this._stateChange("connected");
317
+ console.log("the answer was accepted");
318
+ return true;
319
+ }
320
+ /**
321
+ * If remote (not initiator) the answer was accepted.
322
+ *
323
+ * @param answer
324
+ * @returns
325
+ */
326
+ answerAcceptedByInitiator(answerAccepted) {
327
+ this._stateChange(answerAccepted ? "connected" : "failed");
328
+ }
329
+ sendMessage(message) {
330
+ console.log("Sending message over webRTC");
331
+ if (this.dataChannel && this.dataChannel.readyState === "open") {
332
+ this.dataChannel.send(message);
333
+ } else {
334
+ console.warn("Connection is not established yet.", this.dataChannel?.readyState);
335
+ }
336
+ }
337
+ };
338
+
339
+ // libs/flux/shared/connection/src/lib/agent/flux-agent-network.class.ts
340
+ var FluxAgentNetworkConnection = class {
341
+ constructor(_fluxWebSocketConnection, _fluxWebSocketClientConnection, _webRTCStateChange) {
342
+ this._fluxWebSocketConnection = _fluxWebSocketConnection;
343
+ this._fluxWebSocketClientConnection = _fluxWebSocketClientConnection;
344
+ this._webRTCStateChange = _webRTCStateChange;
345
+ if (typeof RTCPeerConnection !== "undefined") {
346
+ this.iceConnection = new ICEConnection(
347
+ this._fluxWebSocketClientConnection,
348
+ this._webRTCStateChange
349
+ );
350
+ console.log("RTCPeerConnection is available", !!this.iceConnection);
351
+ } else {
352
+ console.warn("\u2757WebRTC is not available in this environment.");
353
+ }
354
+ }
355
+ iceConnection;
356
+ connectedChannelSet = /* @__PURE__ */ new Set();
357
+ /**
358
+ * Joins a channel on the network.
359
+ *
360
+ * @param { string } channelName
361
+ */
362
+ async joinChannel(channelName) {
363
+ console.log(`Joining channel "${channelName}"`);
364
+ if (!validateChannelNameOrThrow(channelName)) {
365
+ throw new Error("This will not actually be thrown");
366
+ }
367
+ const fluxNetworkChannel = await this._fluxWebSocketConnection.joinChannel(
368
+ channelName
369
+ );
370
+ this.connectedChannelSet.add(fluxNetworkChannel.channelName);
371
+ return fluxNetworkChannel;
372
+ }
373
+ /**
374
+ * Listen to messages on this channel.
375
+ *
376
+ * @param { string } channelName
377
+ *
378
+ * @returns { Promise<void> }
379
+ */
380
+ async leaveChannel(channelName) {
381
+ console.log(`Leaving channel "${channelName}"`);
382
+ if (!validateChannelNameOrThrow(channelName)) {
383
+ throw new Error("This will not actually be thrown");
384
+ }
385
+ await this._fluxWebSocketConnection.leaveChannel(
386
+ channelName
387
+ );
388
+ this.connectedChannelSet.delete(channelName);
389
+ }
390
+ /**
391
+ * Connects to a client.
392
+ *
393
+ * @param clientId
394
+ *
395
+ * @returns
396
+ */
397
+ connectToAgent(clientId) {
398
+ this._fluxWebSocketConnection.connectToAgent(clientId);
399
+ return new FluxRemoteClient(this.iceConnection);
400
+ }
401
+ readConnectedChannels() {
402
+ return [...this.connectedChannelSet];
403
+ }
404
+ };
405
+
406
+ // libs/flux/shared/connection/src/lib/flux-network-channel.class.ts
407
+ var FluxNetworkChannel = class {
408
+ constructor(channelName, _fluxWebSocketConnection) {
409
+ this.channelName = channelName;
410
+ this._fluxWebSocketConnection = _fluxWebSocketConnection;
411
+ }
412
+ /**
413
+ * Broadcasts a message to the channel.
414
+ *
415
+ * @param { string } channelName
416
+ *
417
+ * @returns { void }
418
+ */
419
+ publish(message) {
420
+ this._fluxWebSocketConnection.publish(
421
+ this.channelName,
422
+ message
423
+ );
424
+ }
425
+ /**
426
+ * Listen to messages on this channel.
427
+ *
428
+ * @param { TMessageCallback } fn
429
+ *
430
+ * @returns { void }
431
+ */
432
+ onPublish(fn) {
433
+ this._fluxWebSocketConnection.onPublish(
434
+ this.channelName,
435
+ fn
436
+ // TODO
437
+ );
438
+ }
439
+ };
440
+
441
+ // libs/flux/shared/types/src/lib/client-id.type.ts
442
+ var NANOID_LENGTH = 21;
443
+ var NANOID_ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz-";
444
+ var isNanoId = (id) => {
445
+ if (typeof id !== "string") return false;
446
+ const regex = new RegExp(`^[${NANOID_ALPHABET}]{${NANOID_LENGTH}}$`);
447
+ return regex.test(id);
448
+ };
449
+
450
+ // libs/utils/logger/src/lib/utils-logger.ts
451
+ var hasOverlap = (a, b) => a && b ? [...a].some((item) => (Array.isArray(b) ? b : [b]).includes(item)) : false;
452
+ var PicoLogger = /* @__PURE__ */ (() => {
453
+ let allowScopes;
454
+ const configure = (configuration) => {
455
+ allowScopes = typeof configuration.allowScopes === "string" && configuration.allowScopes === "*" ? "*" : new Set(configuration.allowScopes);
456
+ };
457
+ const log = (message, scopes) => {
458
+ if (allowScopes !== "*" && !hasOverlap(allowScopes, scopes)) {
459
+ return;
460
+ }
461
+ console.log(`[${scopes}] ${message}`);
462
+ };
463
+ const warn = (message, scopes) => {
464
+ if (allowScopes !== "*" && !hasOverlap(allowScopes, scopes)) {
465
+ return;
466
+ }
467
+ console.warn(`[${scopes}] ${message}`);
468
+ };
469
+ const error = (message, scopes) => {
470
+ if (allowScopes !== "*" && !hasOverlap(allowScopes, scopes)) {
471
+ return;
472
+ }
473
+ console.error(`[${scopes}] ${message}`);
474
+ };
475
+ return {
476
+ configure,
477
+ log,
478
+ warn,
479
+ error
480
+ };
481
+ })();
482
+
483
+ // libs/flux/shared/connection/src/lib/flux-ws-connection.ts
484
+ var createWSConnection = (id, ticket, stateManager, onReconnectCallback, options) => {
485
+ return new FluxWebSocketConnection(
486
+ id,
487
+ onReconnectCallback,
488
+ stateManager,
489
+ ticket,
490
+ options
491
+ );
492
+ };
493
+ var FluxWebSocketConnection = class {
494
+ constructor(fluxInstanceId, onReconnectCallback, stateManager, token, options) {
495
+ this.fluxInstanceId = fluxInstanceId;
496
+ this.onReconnectCallback = onReconnectCallback;
497
+ this.stateManager = stateManager;
498
+ this.token = token;
499
+ this.options = options;
500
+ this.options = {
501
+ // Defaults
502
+ retries: 1e4,
503
+ port: 5100,
504
+ // Override with provided options
505
+ ...this.options
506
+ };
507
+ this.socket = new FluxWebSocketClientConnection(
508
+ {
509
+ url: `ws://localhost:${this.options.port}?token=${this.token}`,
510
+ autoReconnect: true,
511
+ reconnectDelay: 2e3,
512
+ retries: this.options.retries
513
+ }
514
+ );
515
+ }
516
+ socket;
517
+ callbacks = /* @__PURE__ */ new Set();
518
+ channelCallbacks = /* @__PURE__ */ new Map();
519
+ // Handles messages before the rest of the logic. Will not continue if there are interceptors
520
+ packageTypeInterceptorCallbacks = /* @__PURE__ */ new Map();
521
+ webSocketClient;
522
+ first = true;
523
+ /**
524
+ * Intercepts messages of a given package type.
525
+ *
526
+ * @param { string } packageType
527
+ * @param { TMessageCallback } fn
528
+ *
529
+ * @returns { void }
530
+ */
531
+ interceptPackageTypeMessages(packageType, fn) {
532
+ const existingInterceptors = this.packageTypeInterceptorCallbacks.get(packageType);
533
+ if (existingInterceptors) {
534
+ existingInterceptors.add(fn);
535
+ } else {
536
+ this.packageTypeInterceptorCallbacks.set(packageType, /* @__PURE__ */ new Set([fn]));
537
+ }
538
+ }
539
+ /**
540
+ * Removes a package type interceptor.
541
+ *
542
+ * @param { string } packageType
543
+ * @param { TMessageCallback } fn
544
+ *
545
+ * @returns { void }
546
+ */
547
+ removePackageTypeInterceptor(packageType, fn) {
548
+ const existingInterceptors = this.packageTypeInterceptorCallbacks.get(packageType);
549
+ if (existingInterceptors) {
550
+ existingInterceptors.delete(fn);
551
+ } else {
552
+ console.warn(`No interceptors found for package type "${packageType}"`);
553
+ }
554
+ if (existingInterceptors && existingInterceptors.size === 0) {
555
+ this.packageTypeInterceptorCallbacks.delete(packageType);
556
+ }
557
+ }
558
+ /**
559
+ * Connect to the Flux platform.
560
+ *
561
+ * @returns { Promise<FluxWebSocketClientConnection> }
562
+ */
563
+ async connect() {
564
+ if (this.webSocketClient) {
565
+ return Promise.resolve(this.webSocketClient);
566
+ }
567
+ return new Promise((resolve) => {
568
+ this.interceptPackageTypeMessages(
569
+ "isReady",
570
+ () => {
571
+ this.stateManager.emitNetworkState("connected");
572
+ this.webSocketClient = this.socket;
573
+ resolve(this.socket);
574
+ if (this.first) {
575
+ this.first = false;
576
+ return;
577
+ }
578
+ this.onReconnectCallback();
579
+ }
580
+ );
581
+ this.socket.clearEventSubscribers();
582
+ this.socket.on("message", this.handleMessage.bind(this)).on("close", () => {
583
+ this.webSocketClient = void 0;
584
+ this.stateManager.emitNetworkState("disconnected");
585
+ PicoLogger.log("\u{1F50C}\u{1F534} Disconnected", this.fluxInstanceId);
586
+ }).on("connecting", (retryAttempt) => {
587
+ this.stateManager.emitNetworkState("disconnected");
588
+ if (retryAttempt > 0) {
589
+ PicoLogger.log(`\u{1F504} Connecting attempt: #${retryAttempt} of ${this.options?.retries ?? "none"}`, this.fluxInstanceId);
590
+ } else {
591
+ PicoLogger.log(`\u{1F504} Connecting: ${this.fluxInstanceId}`);
592
+ }
593
+ }).on("error", (error) => {
594
+ PicoLogger.log(`\u274C Error: "${error.message}".`, this.fluxInstanceId);
595
+ });
596
+ this.stateManager.emitNetworkState("connecting");
597
+ this.socket.connect();
598
+ });
599
+ }
600
+ disconnect() {
601
+ this.socket.close();
602
+ this.webSocketClient = void 0;
603
+ this.stateManager.emitNetworkState("disconnected");
604
+ }
605
+ /**
606
+ * Registers an authority to the network.
607
+ *
608
+ * @param { TAuthorizeCallback } cb
609
+ * @param { TChannnelAuthCallback<M> } authorizeNetworkChannel
610
+ *
611
+ * @returns { Promise<void> }
612
+ */
613
+ async registerAuthority(cb, authorizeNetworkChannel) {
614
+ const webSocketClient = await this.connect();
615
+ webSocketClient.registerMethod("authorize", (messageWithType) => {
616
+ const dataType = messageWithType.split(":")[0];
617
+ const message = messageWithType.substring(messageWithType.indexOf(":") + 1);
618
+ return cb(dataType === "json" ? JSON.parse(message) : message);
619
+ });
620
+ webSocketClient.registerMethod("authorizeNetworkChannel", authorizeNetworkChannel);
621
+ return Promise.resolve(void 0);
622
+ }
623
+ /**
624
+ * Connects to a network.
625
+ *
626
+ * @returns { Promise<FluxAgentNetworkConnection> }
627
+ */
628
+ async connectToNetwork() {
629
+ const webSocketClient = await this.connect();
630
+ return Promise.resolve(new FluxAgentNetworkConnection(
631
+ this,
632
+ webSocketClient,
633
+ this.stateManager.emitWebRTCState.bind(this.stateManager)
634
+ ));
635
+ }
636
+ /**
637
+ * Join a channel.
638
+ *
639
+ * @param { TChannelName } channelName
640
+ *
641
+ * @returns { Promise<FluxNetworkChannel> }
642
+ */
643
+ async joinChannel(channelName) {
644
+ if (this.webSocketClient) {
645
+ this.webSocketClient.send(`${SUBSCRIBE_NETWORK_CHANNEL_NAME}:${channelName}`);
646
+ return new Promise((resolve, reject) => {
647
+ const cb = (message) => {
648
+ this.removePackageTypeInterceptor(SUBSCRIBED_NETWORK_CHANNEL_NAME, cb);
649
+ const receivedChannelName = message.substring(message.indexOf(":") + 1);
650
+ if (channelName !== receivedChannelName) {
651
+ reject(new Error(`Channel name mismatch: "${channelName}" !== "${receivedChannelName}"`));
652
+ return;
653
+ }
654
+ resolve(new FluxNetworkChannel(channelName, this));
655
+ };
656
+ this.interceptPackageTypeMessages(
657
+ SUBSCRIBED_NETWORK_CHANNEL_NAME,
658
+ cb
659
+ );
660
+ });
661
+ }
662
+ return Promise.reject(new Error("Not connected"));
663
+ }
664
+ /**
665
+ *
666
+ * @param { TChannelName } channelName
667
+ *
668
+ * @returns { Promise<void> }
669
+ */
670
+ async leaveChannel(channelName) {
671
+ if (this.webSocketClient) {
672
+ this.webSocketClient.send(`${UNSUBSCRIBE_NETWORK_CHANNEL_NAME}:${channelName}`);
673
+ return Promise.resolve(void 0);
674
+ }
675
+ return Promise.reject(new Error("Not connected"));
676
+ }
677
+ /**
678
+ *
679
+ * @param { TChannelName } channelName
680
+ * @param { string } message
681
+ *
682
+ * @returns { void }
683
+ */
684
+ publish(channelName, message) {
685
+ if (this.webSocketClient) {
686
+ const messageString = typeof message === "string" ? message : `o:${JSON.stringify(message)}`;
687
+ this.webSocketClient.send(`${NETWORK_CHANNEL_PUBLISH}:${channelName}:${messageString}`);
688
+ }
689
+ }
690
+ /**
691
+ * Adds a callback to the list of callbacks for a given channel topic.
692
+ *
693
+ * @param { TChannelName } channelName
694
+ * @param { string } message
695
+ *
696
+ * @returns { void }
697
+ */
698
+ onPublish(channelName, fn) {
699
+ const existingSubscriber = this.channelCallbacks.get(channelName);
700
+ if (existingSubscriber) {
701
+ existingSubscriber.add(fn);
702
+ } else {
703
+ const newSubscriber = /* @__PURE__ */ new Set();
704
+ newSubscriber.add(fn);
705
+ this.channelCallbacks.set(channelName, newSubscriber);
706
+ }
707
+ }
708
+ /**
709
+ *
710
+ * @param { string } channelName
711
+ *
712
+ * @returns { Promise<FluxNetworkChannel> }
713
+ */
714
+ async connectToAgent(destinationClientId) {
715
+ if (this.webSocketClient) {
716
+ this.webSocketClient.send(`${CONNECT_TO_CLIENT}:${destinationClientId}`);
717
+ }
718
+ return Promise.resolve();
719
+ }
720
+ /**
721
+ *
722
+ * @param { TMessageCallback } cb
723
+ *
724
+ * @returns { void }
725
+ */
726
+ onMessage(cb) {
727
+ this.callbacks.add(cb);
728
+ }
729
+ // ****************************************************************************
730
+ // *** For Authorities
731
+ // ****************************************************************************
732
+ /**
733
+ * Subscribes to the channel changes.
734
+ *
735
+ * @returns { void }
736
+ */
737
+ subscribeToChannelChanges() {
738
+ if (this.webSocketClient) {
739
+ this.webSocketClient.send(AUTHORITY_CHANNEL_SUBSCRIBE);
740
+ } else {
741
+ console.warn("WebSocket client is not connected");
742
+ }
743
+ }
744
+ /**
745
+ * Disconnects an agent from the network.
746
+ *
747
+ * @param { string } id
748
+ *
749
+ * @returns { void }
750
+ */
751
+ disconnectAgent(id) {
752
+ if (!isNanoId(id)) {
753
+ throw new Error(`Invalid agent id: '${id}'`);
754
+ }
755
+ if (this.webSocketClient) {
756
+ this.webSocketClient.send(`${AUTHORITY_DISCONNECT_AGENT}:${id}`);
757
+ } else {
758
+ console.warn("WebSocket client is not connected");
759
+ }
760
+ }
761
+ // ****************************************************************************
762
+ // *** Internal Helpers
763
+ // ****************************************************************************
764
+ handleMessage(message_) {
765
+ for (const cb of this.callbacks) {
766
+ cb(message_);
767
+ }
768
+ const packageType = message_.split(":")[0];
769
+ const msgInterceptors = this.packageTypeInterceptorCallbacks.get(packageType);
770
+ if (msgInterceptors && msgInterceptors.size > 0) {
771
+ const channelName = message_.substring(message_.indexOf(":") + 1);
772
+ for (const msgInterceptor of msgInterceptors) {
773
+ msgInterceptor(channelName);
774
+ }
775
+ return;
776
+ }
777
+ switch (packageType) {
778
+ case ON_NETWORK_CHANNEL_PUBLISH: {
779
+ const firstColon = message_.indexOf(":");
780
+ const secondColon = message_.indexOf(":", firstColon + 1);
781
+ const channelName = message_.slice(firstColon + 1, secondColon);
782
+ if (validateChannelNameOrThrow(channelName)) {
783
+ const topicCallbacks = this.channelCallbacks.get(channelName);
784
+ if (topicCallbacks) {
785
+ let data = message_.slice(secondColon + 1);
786
+ if (data.startsWith("o:")) {
787
+ try {
788
+ data = JSON.parse(data.substring(2));
789
+ } catch {
790
+ data = data.substring(2);
791
+ }
792
+ }
793
+ for (const cb of topicCallbacks) {
794
+ cb(data);
795
+ }
796
+ }
797
+ }
798
+ break;
799
+ }
800
+ case RPC_REQUEST: {
801
+ const payload = JSON.parse(message_.substring(message_.indexOf(":") + 1));
802
+ this.socket.handleMessage(payload, (data) => {
803
+ this.socket.send(`${RPC_RESPONSE}:${JSON.stringify(data)}`);
804
+ });
805
+ break;
806
+ }
807
+ case RPC_RESPONSE: {
808
+ const payload = message_.substring(message_.indexOf(":") + 1);
809
+ PicoLogger.log("\u{1F50C} Unhandled type rpc response", "ws-client");
810
+ PicoLogger.log(`payload: ${payload}`, "ws-client");
811
+ break;
812
+ }
813
+ default:
814
+ PicoLogger.log(`\u{1F50C} Unhandled type: "${message_}"`, "ws-client");
815
+ break;
816
+ }
817
+ }
818
+ };
819
+
820
+ // packages/flux/agent/src/lib/flux-agent.class.ts
821
+ var import_nanoid = require("nanoid");
822
+
823
+ // packages/flux/agent/src/lib/utils/obscuring/shared.utils.ts
824
+ var ALGORITHM = { name: "AES-GCM", length: 256 };
825
+ var PBKDF2_ITERATIONS = 1e5;
826
+ async function deriveKey(password, salt) {
827
+ const passwordBuffer = new TextEncoder().encode(password);
828
+ const keyMaterial = await crypto.subtle.importKey(
829
+ "raw",
830
+ passwordBuffer,
831
+ { name: "PBKDF2" },
832
+ false,
833
+ ["deriveKey"]
834
+ );
835
+ return crypto.subtle.deriveKey(
836
+ {
837
+ name: "PBKDF2",
838
+ salt,
839
+ iterations: PBKDF2_ITERATIONS,
840
+ hash: "SHA-256"
841
+ },
842
+ keyMaterial,
843
+ ALGORITHM,
844
+ true,
845
+ ["encrypt", "decrypt"]
846
+ );
30
847
  }
31
- console.log("Hello from persistica-flux-agent!");
848
+
849
+ // packages/flux/agent/src/lib/utils/obscuring/encyprt.utils.ts
850
+ var IV_LENGTH = 12;
851
+ var SALT_LENGTH = 16;
852
+ async function encrypt(data, password) {
853
+ const iv = crypto.getRandomValues(new Uint8Array(IV_LENGTH));
854
+ const salt = crypto.getRandomValues(new Uint8Array(SALT_LENGTH));
855
+ const key = await deriveKey(password, salt);
856
+ const encoded = new TextEncoder().encode(data);
857
+ const encryptedBuffer = await crypto.subtle.encrypt(
858
+ { name: "AES-GCM", iv },
859
+ key,
860
+ encoded
861
+ );
862
+ return {
863
+ encrypted: btoa(String.fromCharCode(...new Uint8Array(encryptedBuffer))),
864
+ // Base64
865
+ iv: btoa(String.fromCharCode(...iv)),
866
+ // Base64 IV
867
+ salt: btoa(String.fromCharCode(...salt))
868
+ // Base64 Salt
869
+ };
870
+ }
871
+
872
+ // libs/flux/shared/utils/src/lib/state-manager.util.ts
873
+ var StateManager = class {
874
+ networkStateListeners = /* @__PURE__ */ new Set();
875
+ webRTCStateListeners = /* @__PURE__ */ new Set();
876
+ attachNetworkStateListener(fn) {
877
+ this.networkStateListeners.add(fn);
878
+ }
879
+ detachNetworkStateListener(fn) {
880
+ this.networkStateListeners.delete(fn);
881
+ }
882
+ attachWebRTCStateListener(fn) {
883
+ this.webRTCStateListeners.add(fn);
884
+ }
885
+ detachWebRTCStateListener(fn) {
886
+ this.webRTCStateListeners.delete(fn);
887
+ }
888
+ emitNetworkState(networkState) {
889
+ for (const fn of this.networkStateListeners) {
890
+ fn(networkState);
891
+ }
892
+ }
893
+ emitWebRTCState(rtcState) {
894
+ for (const fn of this.webRTCStateListeners) {
895
+ fn(rtcState);
896
+ }
897
+ }
898
+ /*
899
+ public clear(
900
+
901
+ ): void {
902
+ this.networkStateListeners.clear();
903
+ this.webRTCStateListeners.clear();
904
+ }
905
+
906
+ public getNetworkStateListeners(
907
+
908
+ ): Set<(networkState: TNetworkConnectionState) => void> {
909
+ return this.networkStateListeners;
910
+ }
911
+
912
+ public getWebRTCStateListeners(
913
+
914
+ ): Set<(rtcState: TRTCState) => void> {
915
+ return this.webRTCStateListeners;
916
+ }
917
+
918
+ public getNetworkStateListenersCount(
919
+
920
+ ): number {
921
+ return this.networkStateListeners.size;
922
+ }
923
+
924
+ public getWebRTCStateListenersCount(
925
+
926
+ ): number {
927
+ return this.webRTCStateListeners.size;
928
+ }
929
+ */
930
+ };
931
+
932
+ // libs/flux/shared/utils/src/lib/machine-id.util.ts
933
+ var validateMachineUID = (machineUID) => {
934
+ if (typeof machineUID !== "string") {
935
+ console.warn("Machine UID must be a string");
936
+ return false;
937
+ }
938
+ if (machineUID.length < 5) {
939
+ console.warn("Machine UID too short");
940
+ return false;
941
+ }
942
+ if (machineUID.length > 50) {
943
+ console.warn("Machine UID too long");
944
+ return false;
945
+ }
946
+ return true;
947
+ };
948
+ var getMachineUID = async () => {
949
+ if (typeof globalThis !== "object") {
950
+ throw new Error("Flux can only be used in an environment with globalThis");
951
+ }
952
+ if (globalThis.__flux_client_id !== null) {
953
+ console.log("[flux-client] Reusing existing id");
954
+ return globalThis.__flux_client_id;
955
+ }
956
+ globalThis.__flux_client_id = null;
957
+ return globalThis.__flux_client_id;
958
+ };
959
+
960
+ // packages/flux/agent/src/lib/connector/auth/register-client.auth.ts
961
+ var authenticateAgentOrThrow = async (networkId, domain, unknownIdentificationPayload, clientInfo, password) => {
962
+ if (unknownIdentificationPayload === void 0 || unknownIdentificationPayload === null) {
963
+ throw new Error("Custom payload is required");
964
+ }
965
+ let customPayload;
966
+ if (typeof unknownIdentificationPayload === "string") {
967
+ customPayload = unknownIdentificationPayload;
968
+ }
969
+ if (typeof unknownIdentificationPayload === "object") {
970
+ try {
971
+ customPayload = JSON.stringify(unknownIdentificationPayload);
972
+ } catch {
973
+ throw new Error("Custom payload must be serializable");
974
+ }
975
+ }
976
+ if (customPayload === void 0) {
977
+ throw new Error("Custom payload must be a string or serializable object");
978
+ }
979
+ if (password !== void 0) {
980
+ customPayload = JSON.stringify(await encrypt(customPayload, password));
981
+ }
982
+ const url = new URL(`${domain}/auth/network-client`);
983
+ url.searchParams.set("networkId", networkId);
984
+ if (clientInfo.clientUId) {
985
+ url.searchParams.set("requestedAgentUid", clientInfo.clientUId);
986
+ }
987
+ if (clientInfo.machineUID && validateMachineUID(clientInfo.machineUID)) {
988
+ url.searchParams.set("machineUID", clientInfo.machineUID);
989
+ }
990
+ const response = await fetch(url, {
991
+ method: "POST",
992
+ headers: {
993
+ "Content-Type": "text/plain",
994
+ "x-flux-content-type": typeof unknownIdentificationPayload === "string" ? "text/plain" : "application/json",
995
+ "Accept": "text/plain"
996
+ },
997
+ body: customPayload
998
+ });
999
+ if (!response.ok) {
1000
+ throw new Error(`Auth failed: ${response.status}`);
1001
+ }
1002
+ const result = await response.text();
1003
+ if (!checkAuthTicketShape(result)) {
1004
+ throw new Error("Auth failed: unexpected response");
1005
+ }
1006
+ return result;
1007
+ };
1008
+
1009
+ // packages/flux/agent/src/lib/connector/flux-client-data.class.ts
1010
+ var FluxClientData = class {
1011
+ callbacks = /* @__PURE__ */ new Set();
1012
+ fluxWebSocketConnection;
1013
+ updateWsConnection(fluxWebSocketConnection) {
1014
+ this.fluxWebSocketConnection = fluxWebSocketConnection;
1015
+ for (const cb of this.callbacks) {
1016
+ this.fluxWebSocketConnection.onMessage(cb);
1017
+ }
1018
+ }
1019
+ onMessage(cb) {
1020
+ this.callbacks.add(cb);
1021
+ }
1022
+ };
1023
+
1024
+ // packages/flux/agent/src/lib/flux-agent.class.ts
1025
+ globalThis.agentLoadCount ??= 0;
1026
+ globalThis.agentLoadCount++;
1027
+ console.log(`[flux-agent] Reloaded ${globalThis.agentLoadCount} time(s)`);
1028
+ var FluxAgent = class {
1029
+ constructor(networkId, options) {
1030
+ this.networkId = networkId;
1031
+ this.options = options;
1032
+ }
1033
+ id = (0, import_nanoid.nanoid)();
1034
+ fluxWebSocketConnection;
1035
+ fluxClientData = new FluxClientData();
1036
+ stateManager = new StateManager();
1037
+ /**
1038
+ * Connect to the network using provided identification.
1039
+ *
1040
+ * @param { unknown } identification
1041
+ * @param { string } [clientUId]
1042
+ *
1043
+ * @returns { Promise<FluxAgentNetworkConnection> }
1044
+ */
1045
+ async connect(identification, clientUId) {
1046
+ this.stateManager.emitNetworkState("authorizing");
1047
+ if (clientUId && !validateAgentUIDOrThrow(clientUId)) {
1048
+ throw new Error("Will never be thrown");
1049
+ }
1050
+ const ticket = await authenticateAgentOrThrow(
1051
+ this.networkId,
1052
+ this.options?.domain ?? "http://localhost:5100",
1053
+ identification,
1054
+ {
1055
+ clientUId,
1056
+ machineUID: await getMachineUID() ?? void 0
1057
+ }
1058
+ );
1059
+ this.fluxWebSocketConnection = createWSConnection(
1060
+ this.id,
1061
+ ticket,
1062
+ this.stateManager,
1063
+ async () => {
1064
+ await this.connect(
1065
+ identification,
1066
+ clientUId
1067
+ );
1068
+ },
1069
+ this.options
1070
+ );
1071
+ this.fluxClientData.updateWsConnection(this.fluxWebSocketConnection);
1072
+ const fluxNetworkConnection = await this.fluxWebSocketConnection.connectToNetwork();
1073
+ return fluxNetworkConnection;
1074
+ }
1075
+ disconnect() {
1076
+ this.fluxWebSocketConnection?.disconnect();
1077
+ }
1078
+ /**
1079
+ *
1080
+ * @param fn
1081
+ *
1082
+ * @returns { void }
1083
+ */
1084
+ onWebRTConnectionState = this.stateManager.attachWebRTCStateListener.bind(this.stateManager);
1085
+ /**
1086
+ *
1087
+ * @param fn
1088
+ *
1089
+ * @returns { void }
1090
+ */
1091
+ onNetworkState = this.stateManager.attachNetworkStateListener.bind(this.stateManager);
1092
+ onMessage(cb) {
1093
+ this.fluxClientData.onMessage(cb);
1094
+ }
1095
+ };
32
1096
  // Annotate the CommonJS export names for ESM import in node:
33
1097
  0 && (module.exports = {
34
- persisticaFluxAgent2
1098
+ FluxAgent
35
1099
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@persistica/flux-agent",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "Persistica Flux Agent",
5
5
  "type": "module",
6
6
  "keywords": [],
@@ -9,7 +9,6 @@
9
9
  },
10
10
  "main": "./index.cjs",
11
11
  "types": "./index.d.ts",
12
- "dependencies": {},
13
12
  "repository": {
14
13
  "type": "git",
15
14
  "url": "git+https://github.com/rujorgensen/persistica.git"
@@ -20,5 +19,9 @@
20
19
  "url": "https://github.com/rujorgensen/persistica/issues"
21
20
  },
22
21
  "homepage": "https://github.com/rujorgensen/persistica#readme",
22
+ "dependencies": {
23
+ "nanoid": "5.1.5"
24
+ },
25
+ "packageManager": "bun@1.2.20",
23
26
  "module": "./index.js"
24
27
  }
package/src/index.d.ts CHANGED
@@ -1 +1 @@
1
- export * from './lib/persistica-flux-agent-2';
1
+ export { FluxAgent, } from './lib/flux-agent.class';
@@ -0,0 +1,10 @@
1
+ import type { TChannelName } from '@flux/shared/types';
2
+ /**
3
+ * Callback function to authorize a channel connection.
4
+ *
5
+ * @param { TChannelName } channelTopic - the topic to subscribe to
6
+ * @param { unknown } identification - whatever identification the authority requires
7
+ *
8
+ * @returns { Promise<boolean> } - true if the channel connection is authorized, false otherwise
9
+ */
10
+ export type TChannnelAuthCallback<T> = (channelTopic: TChannelName, identification: T) => Promise<boolean>;
@@ -0,0 +1,18 @@
1
+ import { type TAuthenticationTicket, type TAgentOwnUId, type TNetworkId_S } from '@flux/shared/types';
2
+ import { type TFluxClientUID } from '@flux/shared/utils';
3
+ /**
4
+ * Authenticates with the server and returns a ticket for connecting the websocket.
5
+ *
6
+ * This client -> flux server -> authority client -> flux server -> this client.
7
+ *
8
+ * @param { string } domain The domain of the authority server.
9
+ * @param { unknown } unknownIdentificationPayload The payload to send to the authority server.
10
+ * @param { string } [password]
11
+ * @param { TAgentOwnUId } [agentOwnUId]
12
+ *
13
+ * @returns { Promise<TAuthenticationTicket> }
14
+ */
15
+ export declare const authenticateAgentOrThrow: (networkId: TNetworkId_S, domain: string, unknownIdentificationPayload: unknown, clientInfo: {
16
+ clientUId?: TAgentOwnUId;
17
+ machineUID?: TFluxClientUID;
18
+ }, password?: string) => Promise<TAuthenticationTicket>;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Collects data from any source (currently WS or WebRTC)
3
+ */
4
+ import type { TMessageCallback } from '@flux/shared/ws';
5
+ import type { FluxWebSocketConnection } from '@flux/shared/connection';
6
+ export declare class FluxClientData {
7
+ private readonly callbacks;
8
+ private fluxWebSocketConnection;
9
+ updateWsConnection(fluxWebSocketConnection: FluxWebSocketConnection): void;
10
+ onMessage(cb: TMessageCallback): void;
11
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * One device calls createOffer() and shares the generated offer with the second device.
3
+ * The second device calls acceptOffer(offer), generates an answer, and shares it back with the first device.
4
+ * Both devices exchange ICE candidates (logged in the console) to establish the P2P connection.
5
+ * Once connected, messages can be sent between devices.
6
+ */
7
+ import type { TRTCState } from '@flux/shared/utils';
8
+ import type { FluxWebSocketClientConnection } from '@flux/shared/ws';
9
+ export declare class ICEConnection {
10
+ private readonly _fluxWebSocketClientConnection;
11
+ private readonly _stateChange;
12
+ private readonly peerConnection;
13
+ private dataChannel;
14
+ private offerSDP;
15
+ constructor(_fluxWebSocketClientConnection: FluxWebSocketClientConnection, _stateChange: (state: TRTCState) => void);
16
+ /**
17
+ * This offer must be sent to the other peer.
18
+ */
19
+ private createOffer;
20
+ /**
21
+ *
22
+ * @param { RTCSessionDescriptionInit } offer
23
+ */
24
+ private acceptOffer;
25
+ private acceptAnswer;
26
+ /**
27
+ * If remote (not initiator) the answer was accepted.
28
+ *
29
+ * @param answer
30
+ * @returns
31
+ */
32
+ private answerAcceptedByInitiator;
33
+ sendMessage(message: string): void;
34
+ }
@@ -0,0 +1,4 @@
1
+ import { RPCServer } from '@flux/shared/ws';
2
+ export declare class AuthorityClient extends RPCServer<'authorize'> {
3
+ authorize(fn: (jwt: unknown) => boolean): void;
4
+ }
@@ -0,0 +1,6 @@
1
+ import { RPCServer } from '@flux/shared/ws';
2
+ export declare class ConnectToRemoteClient extends RPCServer<'createOffer' | 'acceptOffer'> {
3
+ private peerConnection;
4
+ private dataChannel;
5
+ constructor();
6
+ }
@@ -0,0 +1,43 @@
1
+ declare global {
2
+ var agentLoadCount: number | null | undefined;
3
+ }
4
+ import type { TMessageCallback } from '@flux/shared/ws';
5
+ import type { FluxAgentNetworkConnection } from '@flux/shared/connection';
6
+ export declare class FluxAgent {
7
+ private readonly networkId;
8
+ private readonly options?;
9
+ readonly id: string;
10
+ private fluxWebSocketConnection;
11
+ private readonly fluxClientData;
12
+ private readonly stateManager;
13
+ constructor(networkId: string, options?: {
14
+ domain?: string;
15
+ secretKey?: string;
16
+ retries?: number;
17
+ } | undefined);
18
+ /**
19
+ * Connect to the network using provided identification.
20
+ *
21
+ * @param { unknown } identification
22
+ * @param { string } [clientUId]
23
+ *
24
+ * @returns { Promise<FluxAgentNetworkConnection> }
25
+ */
26
+ connect(identification: unknown, clientUId?: string): Promise<FluxAgentNetworkConnection>;
27
+ disconnect(): void;
28
+ /**
29
+ *
30
+ * @param fn
31
+ *
32
+ * @returns { void }
33
+ */
34
+ onWebRTConnectionState: (fn: (rtcState: import("@flux/shared/utils").TRTCState) => void) => void;
35
+ /**
36
+ *
37
+ * @param fn
38
+ *
39
+ * @returns { void }
40
+ */
41
+ onNetworkState: (fn: (networkState: import("@flux/shared/utils").TNetworkConnectionState) => void) => void;
42
+ onMessage(cb: TMessageCallback): void;
43
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Interface to the flux platform:
3
+ *
4
+ * Requires socket connectivity to the Flux platform, and a verified network.
5
+ */
6
+ import type { IPackageStatus } from '@flux/shared/types';
7
+ import type { FluxWebSocketConnection } from '../../../../../libs/flux/shared/connection/src/lib/flux-ws-connection';
8
+ export declare class FluxInterface {
9
+ private readonly socketConnection;
10
+ constructor(socketConnection: FluxWebSocketConnection);
11
+ messageReport(packageStatus: Omit<IPackageStatus, 'txNode'>): void;
12
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * A connection to another known client
3
+ */
4
+ import { ICEConnection } from './connector/low-level-com/web-rtc/ice-connection';
5
+ export declare class FluxRemoteClient {
6
+ private readonly iceConnection;
7
+ constructor(iceConnection: ICEConnection);
8
+ callProcedure(): void;
9
+ send(message: string): void;
10
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Coordinate the route transport of messages.
3
+ *
4
+ * Requires connectivity to the Flux platform, and a network.
5
+ */
6
+ export declare class RouteCoordinator {
7
+ constructor(fluxInterface: any);
8
+ }
@@ -0,0 +1,13 @@
1
+ interface IEncryptedData {
2
+ encrypted: string;
3
+ iv: string;
4
+ salt: string;
5
+ }
6
+ /**
7
+ * Decrypts an AES-256-GCM encrypted JSON object using a password
8
+ * @param {IEncryptedData} encryptedData - The encrypted data object
9
+ * @param {string} password - The password
10
+ * @returns {Promise<Object>} - Decrypted JSON object
11
+ */
12
+ export declare function decrypt(encryptedData: IEncryptedData, password: string): Promise<any>;
13
+ export {};
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Encrypts a JSON object using AES-256-GCM with password-derived key
3
+ *
4
+ * @param {Object} data - The data to encrypt
5
+ * @param {string} password - The password
6
+ * @returns {Promise<Object>} - Encrypted data (Base64 format)
7
+ */
8
+ export declare function encrypt(data: string, password: string): Promise<{
9
+ encrypted: string;
10
+ iv: string;
11
+ salt: string;
12
+ }>;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Derive a cryptographic key from a password using PBKDF2
3
+ *
4
+ * @param { string } password - User-provided password
5
+ * @param { Uint8Array } salt - Salt for key derivation
6
+ *
7
+ * @returns { Promise<CryptoKey> } - AES-256-GCM key
8
+ */
9
+ export declare function deriveKey(password: string, salt: any): Promise<CryptoKey>;
@@ -1 +0,0 @@
1
- export declare function persisticaFluxAgent2(): string;