@persistica/flux-agent 0.0.12 → 0.0.14
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.js +193 -143
- package/package.json +11 -5
- package/src/index.d.ts +2 -0
- package/src/lib/channel/channel.type.d.ts +3 -3
- package/src/lib/connector/auth/register-client.auth.d.ts +6 -5
- package/src/lib/connector/low-level-com/web-rtc/ice-connection.d.ts +19 -3
- package/src/lib/flux-agent.class.d.ts +13 -11
- package/src/lib/flux-remote-client.class.d.ts +8 -0
- package/src/lib/utils/obscuring/decrypt.utils.d.ts +6 -4
- package/src/lib/utils/obscuring/encyprt.utils.d.ts +5 -4
- package/src/lib/utils/obscuring/shared.utils.d.ts +4 -4
package/index.js
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
// libs/flux/shared/types/src/lib/errors/network-authority-not-found.error.ts
|
|
2
|
+
var NetworkAuthorityNotFoundError = class _NetworkAuthorityNotFoundError extends Error {
|
|
3
|
+
static message = "Network authority not found for networkId";
|
|
4
|
+
constructor(networkId) {
|
|
5
|
+
super(`${_NetworkAuthorityNotFoundError.message}: '${networkId}'`);
|
|
6
|
+
this.name = "NetworkAuthorityNotFoundError";
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
|
|
1
10
|
// libs/flux/shared/types/src/lib/channel.type.ts
|
|
2
11
|
var validateChannelNameOrThrow = (channelName) => {
|
|
3
12
|
if (channelName.includes(":")) {
|
|
@@ -73,16 +82,20 @@ var validateNetworkIdOrThrow = (networkId) => {
|
|
|
73
82
|
return true;
|
|
74
83
|
};
|
|
75
84
|
|
|
85
|
+
// libs/flux/shared/types/src/lib/client-id.type.ts
|
|
86
|
+
var NANOID_LENGTH = 21;
|
|
87
|
+
var NANOID_ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz-";
|
|
88
|
+
var isNanoId = (id) => {
|
|
89
|
+
if (typeof id !== "string") return false;
|
|
90
|
+
const regex = new RegExp(`^[${NANOID_ALPHABET}]{${NANOID_LENGTH}}$`);
|
|
91
|
+
return regex.test(id);
|
|
92
|
+
};
|
|
93
|
+
|
|
76
94
|
// libs/flux/shared/ws/src/lib/rpc/rpc-server.class.ts
|
|
77
95
|
var RPCServer = class {
|
|
78
96
|
methods = /* @__PURE__ */ new Map();
|
|
79
97
|
/**
|
|
80
98
|
* Registers a method to be called by the client.
|
|
81
|
-
*
|
|
82
|
-
* @param { TMethodName } methodName
|
|
83
|
-
* @param { TCallback } fn
|
|
84
|
-
*
|
|
85
|
-
* @returns { void }
|
|
86
99
|
*/
|
|
87
100
|
registerMethod(methodName, fn) {
|
|
88
101
|
const existingMethod = this.methods.get(methodName);
|
|
@@ -93,11 +106,6 @@ var RPCServer = class {
|
|
|
93
106
|
}
|
|
94
107
|
/**
|
|
95
108
|
* Handles the request from the client.
|
|
96
|
-
*
|
|
97
|
-
* @param { RPCRequest } request
|
|
98
|
-
* @param { Function } sendToRPCClient
|
|
99
|
-
*
|
|
100
|
-
* @returns { void }
|
|
101
109
|
*/
|
|
102
110
|
async handleMessage(request, sendToRPCClient) {
|
|
103
111
|
const response = {
|
|
@@ -144,8 +152,7 @@ var WebSocketClient = class extends RPCServer {
|
|
|
144
152
|
};
|
|
145
153
|
}
|
|
146
154
|
/**
|
|
147
|
-
*
|
|
148
|
-
* @returns { Promise<void> }
|
|
155
|
+
* Opens the WebSocket connection.
|
|
149
156
|
*/
|
|
150
157
|
connect() {
|
|
151
158
|
return new Promise((resolve, reject) => {
|
|
@@ -192,19 +199,23 @@ var WebSocketClient = class extends RPCServer {
|
|
|
192
199
|
};
|
|
193
200
|
});
|
|
194
201
|
}
|
|
202
|
+
/**
|
|
203
|
+
* Emits a WebSocket event to all registered listeners.
|
|
204
|
+
*/
|
|
195
205
|
emit(event, data) {
|
|
196
206
|
for (const listener of this.eventListeners[event]) {
|
|
197
207
|
listener(data);
|
|
198
208
|
}
|
|
199
209
|
}
|
|
210
|
+
/**
|
|
211
|
+
* Registers an event listener.
|
|
212
|
+
*/
|
|
200
213
|
on(event, listener) {
|
|
201
214
|
this.eventListeners[event].push(listener);
|
|
202
215
|
return this;
|
|
203
216
|
}
|
|
204
217
|
/**
|
|
205
218
|
* Clears all subscribers.
|
|
206
|
-
*
|
|
207
|
-
* @returns { void }
|
|
208
219
|
*/
|
|
209
220
|
clearEventSubscribers() {
|
|
210
221
|
this.eventListeners.open = [];
|
|
@@ -213,6 +224,9 @@ var WebSocketClient = class extends RPCServer {
|
|
|
213
224
|
this.eventListeners.connecting = [];
|
|
214
225
|
this.eventListeners.error = [];
|
|
215
226
|
}
|
|
227
|
+
/**
|
|
228
|
+
* Sends a message over the WebSocket connection.
|
|
229
|
+
*/
|
|
216
230
|
send(message) {
|
|
217
231
|
if (this.ws?.readyState === WebSocket.OPEN) {
|
|
218
232
|
this.ws.send(message);
|
|
@@ -220,6 +234,9 @@ var WebSocketClient = class extends RPCServer {
|
|
|
220
234
|
console.warn("WebSocket not open.");
|
|
221
235
|
}
|
|
222
236
|
}
|
|
237
|
+
/**
|
|
238
|
+
* Closes the WebSocket connection and prevents auto-reconnect.
|
|
239
|
+
*/
|
|
223
240
|
close() {
|
|
224
241
|
this.options.autoReconnect = false;
|
|
225
242
|
this.ws?.close();
|
|
@@ -234,8 +251,16 @@ var FluxRemoteClient = class {
|
|
|
234
251
|
constructor(iceConnection) {
|
|
235
252
|
this.iceConnection = iceConnection;
|
|
236
253
|
}
|
|
254
|
+
/**
|
|
255
|
+
* Calls a remote procedure on the connected client.
|
|
256
|
+
*/
|
|
237
257
|
callProcedure() {
|
|
238
258
|
}
|
|
259
|
+
/**
|
|
260
|
+
* Sends a message to the connected client.
|
|
261
|
+
*
|
|
262
|
+
* @param { string } message - The message to send to the connected client
|
|
263
|
+
*/
|
|
239
264
|
send(message) {
|
|
240
265
|
this.iceConnection.sendMessage(message);
|
|
241
266
|
}
|
|
@@ -288,6 +313,8 @@ var ICEConnection = class {
|
|
|
288
313
|
offerSDP;
|
|
289
314
|
/**
|
|
290
315
|
* This offer must be sent to the other peer.
|
|
316
|
+
*
|
|
317
|
+
* @returns { Promise<RTCSessionDescriptionInit> } The WebRTC offer
|
|
291
318
|
*/
|
|
292
319
|
async createOffer() {
|
|
293
320
|
console.log("creating offer and data channel");
|
|
@@ -299,8 +326,11 @@ var ICEConnection = class {
|
|
|
299
326
|
return offer;
|
|
300
327
|
}
|
|
301
328
|
/**
|
|
329
|
+
* Accepts a WebRTC offer from the peer and generates an answer.
|
|
330
|
+
*
|
|
331
|
+
* @param { RTCSessionDescriptionInit } offer - The offer from the remote peer
|
|
302
332
|
*
|
|
303
|
-
* @
|
|
333
|
+
* @returns { Promise<RTCSessionDescriptionInit> } The answer to send back
|
|
304
334
|
*/
|
|
305
335
|
async acceptOffer(offer) {
|
|
306
336
|
this._stateChange("setting-remote-offer");
|
|
@@ -311,6 +341,13 @@ var ICEConnection = class {
|
|
|
311
341
|
console.log("Send this Answer to the peer");
|
|
312
342
|
return answer;
|
|
313
343
|
}
|
|
344
|
+
/**
|
|
345
|
+
* Accepts a WebRTC answer from the peer.
|
|
346
|
+
*
|
|
347
|
+
* @param { any } answer - The answer from the initiating peer
|
|
348
|
+
*
|
|
349
|
+
* @returns { Promise<boolean> } True if the answer was accepted
|
|
350
|
+
*/
|
|
314
351
|
async acceptAnswer(answer) {
|
|
315
352
|
this._stateChange("setting-remote-answer");
|
|
316
353
|
await this.peerConnection.setRemoteDescription(new RTCSessionDescription(answer));
|
|
@@ -321,12 +358,16 @@ var ICEConnection = class {
|
|
|
321
358
|
/**
|
|
322
359
|
* If remote (not initiator) the answer was accepted.
|
|
323
360
|
*
|
|
324
|
-
* @param answer
|
|
325
|
-
* @returns
|
|
361
|
+
* @param { boolean } answerAccepted - Whether the answer was accepted
|
|
326
362
|
*/
|
|
327
363
|
answerAcceptedByInitiator(answerAccepted) {
|
|
328
364
|
this._stateChange(answerAccepted ? "connected" : "failed");
|
|
329
365
|
}
|
|
366
|
+
/**
|
|
367
|
+
* Sends a message to the connected peer via the WebRTC data channel.
|
|
368
|
+
*
|
|
369
|
+
* @param { string } message - The message to send
|
|
370
|
+
*/
|
|
330
371
|
sendMessage(message) {
|
|
331
372
|
console.log("Sending message over webRTC");
|
|
332
373
|
if (this.dataChannel && this.dataChannel.readyState === "open") {
|
|
@@ -357,8 +398,6 @@ var FluxAgentNetworkConnection = class {
|
|
|
357
398
|
connectedChannelSet = /* @__PURE__ */ new Set();
|
|
358
399
|
/**
|
|
359
400
|
* Joins a channel on the network.
|
|
360
|
-
*
|
|
361
|
-
* @param { string } channelName
|
|
362
401
|
*/
|
|
363
402
|
async joinChannel(channelName) {
|
|
364
403
|
console.log(`Joining channel "${channelName}"`);
|
|
@@ -372,11 +411,7 @@ var FluxAgentNetworkConnection = class {
|
|
|
372
411
|
return fluxNetworkChannel;
|
|
373
412
|
}
|
|
374
413
|
/**
|
|
375
|
-
*
|
|
376
|
-
*
|
|
377
|
-
* @param { string } channelName
|
|
378
|
-
*
|
|
379
|
-
* @returns { Promise<void> }
|
|
414
|
+
* Leaves a channel on the network.
|
|
380
415
|
*/
|
|
381
416
|
async leaveChannel(channelName) {
|
|
382
417
|
console.log(`Leaving channel "${channelName}"`);
|
|
@@ -389,16 +424,15 @@ var FluxAgentNetworkConnection = class {
|
|
|
389
424
|
this.connectedChannelSet.delete(channelName);
|
|
390
425
|
}
|
|
391
426
|
/**
|
|
392
|
-
* Connects to a
|
|
393
|
-
*
|
|
394
|
-
* @param clientId
|
|
395
|
-
*
|
|
396
|
-
* @returns
|
|
427
|
+
* Connects to a remote agent.
|
|
397
428
|
*/
|
|
398
429
|
connectToAgent(clientId) {
|
|
399
430
|
this._fluxWebSocketConnection.connectToAgent(clientId);
|
|
400
431
|
return new FluxRemoteClient(this.iceConnection);
|
|
401
432
|
}
|
|
433
|
+
/**
|
|
434
|
+
* Returns a list of channels the agent is currently subscribed to.
|
|
435
|
+
*/
|
|
402
436
|
readConnectedChannels() {
|
|
403
437
|
return [...this.connectedChannelSet];
|
|
404
438
|
}
|
|
@@ -412,10 +446,6 @@ var FluxNetworkChannel = class {
|
|
|
412
446
|
}
|
|
413
447
|
/**
|
|
414
448
|
* Broadcasts a message to the channel.
|
|
415
|
-
*
|
|
416
|
-
* @param { string } channelName
|
|
417
|
-
*
|
|
418
|
-
* @returns { void }
|
|
419
449
|
*/
|
|
420
450
|
publish(message) {
|
|
421
451
|
this._fluxWebSocketConnection.publish(
|
|
@@ -425,10 +455,6 @@ var FluxNetworkChannel = class {
|
|
|
425
455
|
}
|
|
426
456
|
/**
|
|
427
457
|
* Listen to messages on this channel.
|
|
428
|
-
*
|
|
429
|
-
* @param { TMessageCallback } fn
|
|
430
|
-
*
|
|
431
|
-
* @returns { void }
|
|
432
458
|
*/
|
|
433
459
|
onPublish(fn) {
|
|
434
460
|
this._fluxWebSocketConnection.onPublish(
|
|
@@ -439,15 +465,6 @@ var FluxNetworkChannel = class {
|
|
|
439
465
|
}
|
|
440
466
|
};
|
|
441
467
|
|
|
442
|
-
// libs/flux/shared/types/src/lib/client-id.type.ts
|
|
443
|
-
var NANOID_LENGTH = 21;
|
|
444
|
-
var NANOID_ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz-";
|
|
445
|
-
var isNanoId = (id) => {
|
|
446
|
-
if (typeof id !== "string") return false;
|
|
447
|
-
const regex = new RegExp(`^[${NANOID_ALPHABET}]{${NANOID_LENGTH}}$`);
|
|
448
|
-
return regex.test(id);
|
|
449
|
-
};
|
|
450
|
-
|
|
451
468
|
// libs/utils/logger/src/lib/utils-logger.ts
|
|
452
469
|
var hasOverlap = (a, b) => a && b ? [...a].some((item) => (Array.isArray(b) ? b : [b]).includes(item)) : false;
|
|
453
470
|
var PicoLogger = /* @__PURE__ */ (() => {
|
|
@@ -488,7 +505,11 @@ var createWSConnection = (id, ticket, stateManager, onReconnectCallback, options
|
|
|
488
505
|
onReconnectCallback,
|
|
489
506
|
stateManager,
|
|
490
507
|
ticket,
|
|
491
|
-
|
|
508
|
+
{
|
|
509
|
+
secretKey: options?.secretKey,
|
|
510
|
+
retries: options?.retries,
|
|
511
|
+
port: options?.domain ? new URL(options.domain).port ? Number.parseInt(new URL(options.domain).port) : void 0 : void 0
|
|
512
|
+
}
|
|
492
513
|
);
|
|
493
514
|
};
|
|
494
515
|
var FluxWebSocketConnection = class {
|
|
@@ -499,11 +520,9 @@ var FluxWebSocketConnection = class {
|
|
|
499
520
|
this.token = token;
|
|
500
521
|
this.options = options;
|
|
501
522
|
this.options = {
|
|
502
|
-
|
|
503
|
-
retries: 1e4,
|
|
504
|
-
port: 5100
|
|
505
|
-
// Override with provided options
|
|
506
|
-
...this.options
|
|
523
|
+
secretKey: this.options?.secretKey,
|
|
524
|
+
retries: this.options?.retries ?? 1e4,
|
|
525
|
+
port: this.options?.port ?? 5100
|
|
507
526
|
};
|
|
508
527
|
this.socket = new FluxWebSocketClientConnection(
|
|
509
528
|
{
|
|
@@ -523,11 +542,6 @@ var FluxWebSocketConnection = class {
|
|
|
523
542
|
first = true;
|
|
524
543
|
/**
|
|
525
544
|
* Intercepts messages of a given package type.
|
|
526
|
-
*
|
|
527
|
-
* @param { string } packageType
|
|
528
|
-
* @param { TMessageCallback } fn
|
|
529
|
-
*
|
|
530
|
-
* @returns { void }
|
|
531
545
|
*/
|
|
532
546
|
interceptPackageTypeMessages(packageType, fn) {
|
|
533
547
|
const existingInterceptors = this.packageTypeInterceptorCallbacks.get(packageType);
|
|
@@ -539,11 +553,6 @@ var FluxWebSocketConnection = class {
|
|
|
539
553
|
}
|
|
540
554
|
/**
|
|
541
555
|
* Removes a package type interceptor.
|
|
542
|
-
*
|
|
543
|
-
* @param { string } packageType
|
|
544
|
-
* @param { TMessageCallback } fn
|
|
545
|
-
*
|
|
546
|
-
* @returns { void }
|
|
547
556
|
*/
|
|
548
557
|
removePackageTypeInterceptor(packageType, fn) {
|
|
549
558
|
const existingInterceptors = this.packageTypeInterceptorCallbacks.get(packageType);
|
|
@@ -558,8 +567,6 @@ var FluxWebSocketConnection = class {
|
|
|
558
567
|
}
|
|
559
568
|
/**
|
|
560
569
|
* Connect to the Flux platform.
|
|
561
|
-
*
|
|
562
|
-
* @returns { Promise<FluxWebSocketClientConnection> }
|
|
563
570
|
*/
|
|
564
571
|
async connect() {
|
|
565
572
|
if (this.webSocketClient) {
|
|
@@ -598,6 +605,9 @@ var FluxWebSocketConnection = class {
|
|
|
598
605
|
this.socket.connect();
|
|
599
606
|
});
|
|
600
607
|
}
|
|
608
|
+
/**
|
|
609
|
+
* Disconnects from the Flux platform.
|
|
610
|
+
*/
|
|
601
611
|
disconnect() {
|
|
602
612
|
this.socket.close();
|
|
603
613
|
this.webSocketClient = void 0;
|
|
@@ -605,11 +615,6 @@ var FluxWebSocketConnection = class {
|
|
|
605
615
|
}
|
|
606
616
|
/**
|
|
607
617
|
* Registers an authority to the network.
|
|
608
|
-
*
|
|
609
|
-
* @param { TAuthorizeCallback } cb
|
|
610
|
-
* @param { TChannnelAuthCallback<M> } authorizeNetworkChannel
|
|
611
|
-
*
|
|
612
|
-
* @returns { Promise<void> }
|
|
613
618
|
*/
|
|
614
619
|
async registerAuthority(cb, authorizeNetworkChannel) {
|
|
615
620
|
const webSocketClient = await this.connect();
|
|
@@ -623,8 +628,6 @@ var FluxWebSocketConnection = class {
|
|
|
623
628
|
}
|
|
624
629
|
/**
|
|
625
630
|
* Connects to a network.
|
|
626
|
-
*
|
|
627
|
-
* @returns { Promise<FluxAgentNetworkConnection> }
|
|
628
631
|
*/
|
|
629
632
|
async connectToNetwork() {
|
|
630
633
|
const webSocketClient = await this.connect();
|
|
@@ -636,10 +639,6 @@ var FluxWebSocketConnection = class {
|
|
|
636
639
|
}
|
|
637
640
|
/**
|
|
638
641
|
* Join a channel.
|
|
639
|
-
*
|
|
640
|
-
* @param { TChannelName } channelName
|
|
641
|
-
*
|
|
642
|
-
* @returns { Promise<FluxNetworkChannel> }
|
|
643
642
|
*/
|
|
644
643
|
async joinChannel(channelName) {
|
|
645
644
|
if (this.webSocketClient) {
|
|
@@ -663,10 +662,7 @@ var FluxWebSocketConnection = class {
|
|
|
663
662
|
return Promise.reject(new Error("Not connected"));
|
|
664
663
|
}
|
|
665
664
|
/**
|
|
666
|
-
*
|
|
667
|
-
* @param { TChannelName } channelName
|
|
668
|
-
*
|
|
669
|
-
* @returns { Promise<void> }
|
|
665
|
+
* Leaves a channel.
|
|
670
666
|
*/
|
|
671
667
|
async leaveChannel(channelName) {
|
|
672
668
|
if (this.webSocketClient) {
|
|
@@ -676,11 +672,7 @@ var FluxWebSocketConnection = class {
|
|
|
676
672
|
return Promise.reject(new Error("Not connected"));
|
|
677
673
|
}
|
|
678
674
|
/**
|
|
679
|
-
*
|
|
680
|
-
* @param { TChannelName } channelName
|
|
681
|
-
* @param { string } message
|
|
682
|
-
*
|
|
683
|
-
* @returns { void }
|
|
675
|
+
* Publishes a message to a channel.
|
|
684
676
|
*/
|
|
685
677
|
publish(channelName, message) {
|
|
686
678
|
if (this.webSocketClient) {
|
|
@@ -690,11 +682,6 @@ var FluxWebSocketConnection = class {
|
|
|
690
682
|
}
|
|
691
683
|
/**
|
|
692
684
|
* Adds a callback to the list of callbacks for a given channel topic.
|
|
693
|
-
*
|
|
694
|
-
* @param { TChannelName } channelName
|
|
695
|
-
* @param { string } message
|
|
696
|
-
*
|
|
697
|
-
* @returns { void }
|
|
698
685
|
*/
|
|
699
686
|
onPublish(channelName, fn) {
|
|
700
687
|
const existingSubscriber = this.channelCallbacks.get(channelName);
|
|
@@ -707,10 +694,7 @@ var FluxWebSocketConnection = class {
|
|
|
707
694
|
}
|
|
708
695
|
}
|
|
709
696
|
/**
|
|
710
|
-
*
|
|
711
|
-
* @param { string } channelName
|
|
712
|
-
*
|
|
713
|
-
* @returns { Promise<FluxNetworkChannel> }
|
|
697
|
+
* Sends a connection request to another agent.
|
|
714
698
|
*/
|
|
715
699
|
async connectToAgent(destinationClientId) {
|
|
716
700
|
if (this.webSocketClient) {
|
|
@@ -719,10 +703,7 @@ var FluxWebSocketConnection = class {
|
|
|
719
703
|
return Promise.resolve();
|
|
720
704
|
}
|
|
721
705
|
/**
|
|
722
|
-
*
|
|
723
|
-
* @param { TMessageCallback } cb
|
|
724
|
-
*
|
|
725
|
-
* @returns { void }
|
|
706
|
+
* Registers a callback to be called when a message is received.
|
|
726
707
|
*/
|
|
727
708
|
onMessage(cb) {
|
|
728
709
|
this.callbacks.add(cb);
|
|
@@ -732,8 +713,6 @@ var FluxWebSocketConnection = class {
|
|
|
732
713
|
// ****************************************************************************
|
|
733
714
|
/**
|
|
734
715
|
* Subscribes to the channel changes.
|
|
735
|
-
*
|
|
736
|
-
* @returns { void }
|
|
737
716
|
*/
|
|
738
717
|
subscribeToChannelChanges() {
|
|
739
718
|
if (this.webSocketClient) {
|
|
@@ -744,10 +723,6 @@ var FluxWebSocketConnection = class {
|
|
|
744
723
|
}
|
|
745
724
|
/**
|
|
746
725
|
* Disconnects an agent from the network.
|
|
747
|
-
*
|
|
748
|
-
* @param { string } id
|
|
749
|
-
*
|
|
750
|
-
* @returns { void }
|
|
751
726
|
*/
|
|
752
727
|
disconnectAgent(id) {
|
|
753
728
|
if (!isNanoId(id)) {
|
|
@@ -762,6 +737,9 @@ var FluxWebSocketConnection = class {
|
|
|
762
737
|
// ****************************************************************************
|
|
763
738
|
// *** Internal Helpers
|
|
764
739
|
// ****************************************************************************
|
|
740
|
+
/**
|
|
741
|
+
* Handles incoming WebSocket messages.
|
|
742
|
+
*/
|
|
765
743
|
handleMessage(message_) {
|
|
766
744
|
for (const cb of this.callbacks) {
|
|
767
745
|
cb(message_);
|
|
@@ -779,11 +757,12 @@ var FluxWebSocketConnection = class {
|
|
|
779
757
|
case ON_NETWORK_CHANNEL_PUBLISH: {
|
|
780
758
|
const firstColon = message_.indexOf(":");
|
|
781
759
|
const secondColon = message_.indexOf(":", firstColon + 1);
|
|
782
|
-
const
|
|
760
|
+
const thirdColon = message_.indexOf(":", secondColon + 1);
|
|
761
|
+
const channelName = message_.slice(secondColon + 1, thirdColon);
|
|
783
762
|
if (validateChannelNameOrThrow(channelName)) {
|
|
784
763
|
const topicCallbacks = this.channelCallbacks.get(channelName);
|
|
785
764
|
if (topicCallbacks) {
|
|
786
|
-
let data = message_.slice(
|
|
765
|
+
let data = message_.slice(thirdColon + 1);
|
|
787
766
|
if (data.startsWith("o:")) {
|
|
788
767
|
try {
|
|
789
768
|
data = JSON.parse(data.substring(2));
|
|
@@ -813,7 +792,6 @@ var FluxWebSocketConnection = class {
|
|
|
813
792
|
}
|
|
814
793
|
default:
|
|
815
794
|
if (message_.startsWith(`${ERROR}:`)) {
|
|
816
|
-
throw new Error(message_.substring(message_.indexOf(":") + 1) ?? "Unknown error");
|
|
817
795
|
}
|
|
818
796
|
PicoLogger.log(`\u{1F50C} Unhandled type: "${message_}"`, "ws-client");
|
|
819
797
|
break;
|
|
@@ -877,23 +855,41 @@ async function encrypt(data, password) {
|
|
|
877
855
|
var StateManager = class {
|
|
878
856
|
networkStateListeners = /* @__PURE__ */ new Set();
|
|
879
857
|
webRTCStateListeners = /* @__PURE__ */ new Set();
|
|
858
|
+
/**
|
|
859
|
+
* Attaches a listener for network connection state changes.
|
|
860
|
+
*/
|
|
880
861
|
attachNetworkStateListener(fn) {
|
|
881
862
|
this.networkStateListeners.add(fn);
|
|
882
863
|
}
|
|
864
|
+
/**
|
|
865
|
+
* Detaches a previously registered network connection state listener.
|
|
866
|
+
*/
|
|
883
867
|
detachNetworkStateListener(fn) {
|
|
884
868
|
this.networkStateListeners.delete(fn);
|
|
885
869
|
}
|
|
870
|
+
/**
|
|
871
|
+
* Attaches a listener for WebRTC connection state changes.
|
|
872
|
+
*/
|
|
886
873
|
attachWebRTCStateListener(fn) {
|
|
887
874
|
this.webRTCStateListeners.add(fn);
|
|
888
875
|
}
|
|
876
|
+
/**
|
|
877
|
+
* Detaches a previously registered WebRTC connection state listener.
|
|
878
|
+
*/
|
|
889
879
|
detachWebRTCStateListener(fn) {
|
|
890
880
|
this.webRTCStateListeners.delete(fn);
|
|
891
881
|
}
|
|
882
|
+
/**
|
|
883
|
+
* Emits a network connection state change to all registered listeners.
|
|
884
|
+
*/
|
|
892
885
|
emitNetworkState(networkState) {
|
|
893
886
|
for (const fn of this.networkStateListeners) {
|
|
894
887
|
fn(networkState);
|
|
895
888
|
}
|
|
896
889
|
}
|
|
890
|
+
/**
|
|
891
|
+
* Emits a WebRTC connection state change to all registered listeners.
|
|
892
|
+
*/
|
|
897
893
|
emitWebRTCState(rtcState) {
|
|
898
894
|
for (const fn of this.webRTCStateListeners) {
|
|
899
895
|
fn(rtcState);
|
|
@@ -933,6 +929,34 @@ var StateManager = class {
|
|
|
933
929
|
*/
|
|
934
930
|
};
|
|
935
931
|
|
|
932
|
+
// libs/flux/shared/utils/src/lib/promises.utils.ts
|
|
933
|
+
var retryOrThrow = async (fn, shouldRetry, options) => {
|
|
934
|
+
let attempt = 0;
|
|
935
|
+
let overrideDelay;
|
|
936
|
+
while (true) {
|
|
937
|
+
try {
|
|
938
|
+
if (attempt > 0) {
|
|
939
|
+
if (options.onRetry) {
|
|
940
|
+
overrideDelay = options.onRetry(attempt, options.retries) ?? void 0;
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
return await fn();
|
|
944
|
+
} catch (err) {
|
|
945
|
+
attempt++;
|
|
946
|
+
if (attempt > options.retries || !shouldRetry(err)) {
|
|
947
|
+
throw err;
|
|
948
|
+
}
|
|
949
|
+
if (options.delayMs > 0 || overrideDelay && overrideDelay > 0) {
|
|
950
|
+
let delay = overrideDelay ?? options.delayMs;
|
|
951
|
+
await new Promise((res) => setTimeout(
|
|
952
|
+
res,
|
|
953
|
+
delay
|
|
954
|
+
));
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
};
|
|
959
|
+
|
|
936
960
|
// libs/flux/shared/utils/src/lib/machine-id.util.ts
|
|
937
961
|
var validateMachineUID = (machineUID) => {
|
|
938
962
|
if (typeof machineUID !== "string") {
|
|
@@ -1001,6 +1025,12 @@ var authenticateAgentOrThrow = async (networkId, domain, unknownIdentificationPa
|
|
|
1001
1025
|
body: customPayload
|
|
1002
1026
|
});
|
|
1003
1027
|
if (!response.ok) {
|
|
1028
|
+
if (response.status === 401) {
|
|
1029
|
+
const textResponse = await response.text();
|
|
1030
|
+
if (textResponse.startsWith(NetworkAuthorityNotFoundError.message)) {
|
|
1031
|
+
throw new NetworkAuthorityNotFoundError(networkId);
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1004
1034
|
throw new Error(`Auth failed: ${response.status}`);
|
|
1005
1035
|
}
|
|
1006
1036
|
const result = await response.text();
|
|
@@ -1042,62 +1072,82 @@ var FluxAgent = class {
|
|
|
1042
1072
|
/**
|
|
1043
1073
|
* Connect to the network using provided identification.
|
|
1044
1074
|
*
|
|
1045
|
-
* @param { unknown }
|
|
1046
|
-
* @param { string }
|
|
1047
|
-
*
|
|
1048
|
-
* @returns { Promise<FluxAgentNetworkConnection> }
|
|
1075
|
+
* @param { unknown } identification - The identification payload to authenticate with
|
|
1076
|
+
* @param { string } [clientUId] - Optional client UID to use
|
|
1077
|
+
*
|
|
1078
|
+
* @returns { Promise<FluxAgentNetworkConnection> } The network connection
|
|
1049
1079
|
*/
|
|
1050
1080
|
async connect(identification, clientUId) {
|
|
1051
1081
|
this.stateManager.emitNetworkState("authorizing");
|
|
1052
1082
|
if (clientUId && !validateAgentUIDOrThrow(clientUId)) {
|
|
1053
1083
|
throw new Error("Will never be thrown");
|
|
1054
1084
|
}
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1085
|
+
try {
|
|
1086
|
+
const ticket = await retryOrThrow(
|
|
1087
|
+
async () => {
|
|
1088
|
+
return authenticateAgentOrThrow(
|
|
1089
|
+
this.networkId,
|
|
1090
|
+
this.options?.domain ?? "http://localhost:5100",
|
|
1091
|
+
identification,
|
|
1092
|
+
{
|
|
1093
|
+
clientUId,
|
|
1094
|
+
machineUID: await getMachineUID() ?? void 0
|
|
1095
|
+
}
|
|
1096
|
+
);
|
|
1097
|
+
},
|
|
1098
|
+
// Retry if no authority was found yet
|
|
1099
|
+
(error) => error instanceof NetworkAuthorityNotFoundError,
|
|
1100
|
+
{
|
|
1101
|
+
retries: 100,
|
|
1102
|
+
delayMs: 500,
|
|
1103
|
+
// Backoff until 3 seconds
|
|
1104
|
+
onRetry: (attempt) => Math.min(3e3, 500 + attempt * 200)
|
|
1105
|
+
}
|
|
1106
|
+
);
|
|
1107
|
+
this.fluxWebSocketConnection = createWSConnection(
|
|
1108
|
+
this.id,
|
|
1109
|
+
ticket,
|
|
1110
|
+
this.stateManager,
|
|
1111
|
+
async () => {
|
|
1112
|
+
await this.connect(
|
|
1113
|
+
identification,
|
|
1114
|
+
clientUId
|
|
1115
|
+
);
|
|
1116
|
+
},
|
|
1117
|
+
this.options
|
|
1118
|
+
);
|
|
1119
|
+
this.fluxClientData.updateWsConnection(this.fluxWebSocketConnection);
|
|
1120
|
+
const fluxNetworkConnection = await this.fluxWebSocketConnection.connectToNetwork();
|
|
1121
|
+
return fluxNetworkConnection;
|
|
1122
|
+
} catch (error) {
|
|
1123
|
+
return Promise.reject(error);
|
|
1124
|
+
}
|
|
1079
1125
|
}
|
|
1126
|
+
/**
|
|
1127
|
+
* Disconnects from the network.
|
|
1128
|
+
*/
|
|
1080
1129
|
disconnect() {
|
|
1081
1130
|
this.fluxWebSocketConnection?.disconnect();
|
|
1082
1131
|
}
|
|
1083
1132
|
/**
|
|
1084
|
-
*
|
|
1085
|
-
* @param fn
|
|
1086
|
-
*
|
|
1087
|
-
* @returns { void }
|
|
1133
|
+
* Attaches a listener for WebRTC connection state changes.
|
|
1088
1134
|
*/
|
|
1089
1135
|
onWebRTConnectionState = this.stateManager.attachWebRTCStateListener.bind(this.stateManager);
|
|
1090
1136
|
/**
|
|
1091
|
-
*
|
|
1092
|
-
* @param fn
|
|
1093
|
-
*
|
|
1094
|
-
* @returns { void }
|
|
1137
|
+
* Attaches a listener for network connection state changes.
|
|
1095
1138
|
*/
|
|
1096
1139
|
onNetworkState = this.stateManager.attachNetworkStateListener.bind(this.stateManager);
|
|
1140
|
+
/**
|
|
1141
|
+
* Registers a callback to be called when a message is received.
|
|
1142
|
+
*
|
|
1143
|
+
* @param { TMessageCallback } cb - Callback to invoke when a message is received
|
|
1144
|
+
*/
|
|
1097
1145
|
onMessage(cb) {
|
|
1098
1146
|
this.fluxClientData.onMessage(cb);
|
|
1099
1147
|
}
|
|
1100
1148
|
};
|
|
1101
1149
|
export {
|
|
1102
|
-
FluxAgent
|
|
1150
|
+
FluxAgent,
|
|
1151
|
+
FluxNetworkChannel,
|
|
1152
|
+
FluxRemoteClient
|
|
1103
1153
|
};
|
package/package.json
CHANGED
|
@@ -1,23 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@persistica/flux-agent",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.14",
|
|
4
4
|
"description": "Persistica Flux Agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [],
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
10
|
-
"exports":
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./src/index.d.ts",
|
|
13
|
+
"import": "./index.js",
|
|
14
|
+
"default": "./index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
11
17
|
"main": "./index.js",
|
|
12
18
|
"types": "./src/index.d.ts",
|
|
13
19
|
"repository": {
|
|
14
20
|
"type": "git",
|
|
15
|
-
"url": "git+https://github.com/rujorgensen/
|
|
21
|
+
"url": "git+https://github.com/rujorgensen/flux.git"
|
|
16
22
|
},
|
|
17
23
|
"author": "pixl.prophet",
|
|
18
24
|
"license": "ISC",
|
|
19
25
|
"bugs": {
|
|
20
|
-
"url": "https://github.com/rujorgensen/
|
|
26
|
+
"url": "https://github.com/rujorgensen/flux/issues"
|
|
21
27
|
},
|
|
22
|
-
"homepage": "https://github.com/rujorgensen/
|
|
28
|
+
"homepage": "https://github.com/rujorgensen/flux#readme"
|
|
23
29
|
}
|
package/src/index.d.ts
CHANGED
|
@@ -2,9 +2,9 @@ import type { TChannelName } from '@flux/shared/types';
|
|
|
2
2
|
/**
|
|
3
3
|
* Callback function to authorize a channel connection.
|
|
4
4
|
*
|
|
5
|
-
* @param { TChannelName }
|
|
6
|
-
* @param {
|
|
5
|
+
* @param { TChannelName } channelTopic - The channel topic to subscribe to
|
|
6
|
+
* @param { T } identification - Whatever identification the authority requires
|
|
7
7
|
*
|
|
8
|
-
* @returns { Promise<boolean> }
|
|
8
|
+
* @returns { Promise<boolean> } True if the channel connection is authorized, false otherwise
|
|
9
9
|
*/
|
|
10
10
|
export type TChannnelAuthCallback<T> = (channelTopic: TChannelName, identification: T) => Promise<boolean>;
|
|
@@ -5,12 +5,13 @@ import { type TFluxClientUID } from '@flux/shared/utils';
|
|
|
5
5
|
*
|
|
6
6
|
* This client -> flux server -> authority client -> flux server -> this client.
|
|
7
7
|
*
|
|
8
|
-
* @param {
|
|
9
|
-
* @param {
|
|
10
|
-
* @param {
|
|
11
|
-
* @param {
|
|
8
|
+
* @param { TNetworkId_S } networkId - The network ID to connect to
|
|
9
|
+
* @param { string } domain - The domain of the authority server
|
|
10
|
+
* @param { unknown } unknownIdentificationPayload - The payload to send to the authority server
|
|
11
|
+
* @param { object } clientInfo - Optional client identification info
|
|
12
|
+
* @param { string } [password] - Optional password for encrypting the payload
|
|
12
13
|
*
|
|
13
|
-
* @returns { Promise<TAuthenticationTicket> }
|
|
14
|
+
* @returns { Promise<TAuthenticationTicket> } The authentication ticket for connecting the websocket
|
|
14
15
|
*/
|
|
15
16
|
export declare const authenticateAgentOrThrow: (networkId: TNetworkId_S, domain: string, unknownIdentificationPayload: unknown, clientInfo: {
|
|
16
17
|
clientUId?: TAgentOwnUId;
|
|
@@ -15,20 +15,36 @@ export declare class ICEConnection {
|
|
|
15
15
|
constructor(_fluxWebSocketClientConnection: FluxWebSocketClientConnection, _stateChange: (state: TRTCState) => void);
|
|
16
16
|
/**
|
|
17
17
|
* This offer must be sent to the other peer.
|
|
18
|
+
*
|
|
19
|
+
* @returns { Promise<RTCSessionDescriptionInit> } The WebRTC offer
|
|
18
20
|
*/
|
|
19
21
|
private createOffer;
|
|
20
22
|
/**
|
|
23
|
+
* Accepts a WebRTC offer from the peer and generates an answer.
|
|
24
|
+
*
|
|
25
|
+
* @param { RTCSessionDescriptionInit } offer - The offer from the remote peer
|
|
21
26
|
*
|
|
22
|
-
* @
|
|
27
|
+
* @returns { Promise<RTCSessionDescriptionInit> } The answer to send back
|
|
23
28
|
*/
|
|
24
29
|
private acceptOffer;
|
|
30
|
+
/**
|
|
31
|
+
* Accepts a WebRTC answer from the peer.
|
|
32
|
+
*
|
|
33
|
+
* @param { any } answer - The answer from the initiating peer
|
|
34
|
+
*
|
|
35
|
+
* @returns { Promise<boolean> } True if the answer was accepted
|
|
36
|
+
*/
|
|
25
37
|
private acceptAnswer;
|
|
26
38
|
/**
|
|
27
39
|
* If remote (not initiator) the answer was accepted.
|
|
28
40
|
*
|
|
29
|
-
* @param answer
|
|
30
|
-
* @returns
|
|
41
|
+
* @param { boolean } answerAccepted - Whether the answer was accepted
|
|
31
42
|
*/
|
|
32
43
|
private answerAcceptedByInitiator;
|
|
44
|
+
/**
|
|
45
|
+
* Sends a message to the connected peer via the WebRTC data channel.
|
|
46
|
+
*
|
|
47
|
+
* @param { string } message - The message to send
|
|
48
|
+
*/
|
|
33
49
|
sendMessage(message: string): void;
|
|
34
50
|
}
|
|
@@ -18,26 +18,28 @@ export declare class FluxAgent {
|
|
|
18
18
|
/**
|
|
19
19
|
* Connect to the network using provided identification.
|
|
20
20
|
*
|
|
21
|
-
* @param { unknown }
|
|
22
|
-
* @param { string }
|
|
21
|
+
* @param { unknown } identification - The identification payload to authenticate with
|
|
22
|
+
* @param { string } [clientUId] - Optional client UID to use
|
|
23
23
|
*
|
|
24
|
-
* @returns { Promise<FluxAgentNetworkConnection> }
|
|
24
|
+
* @returns { Promise<FluxAgentNetworkConnection> } The network connection
|
|
25
25
|
*/
|
|
26
26
|
connect(identification: unknown, clientUId?: string): Promise<FluxAgentNetworkConnection>;
|
|
27
|
+
/**
|
|
28
|
+
* Disconnects from the network.
|
|
29
|
+
*/
|
|
27
30
|
disconnect(): void;
|
|
28
31
|
/**
|
|
29
|
-
*
|
|
30
|
-
* @param fn
|
|
31
|
-
*
|
|
32
|
-
* @returns { void }
|
|
32
|
+
* Attaches a listener for WebRTC connection state changes.
|
|
33
33
|
*/
|
|
34
34
|
onWebRTConnectionState: (fn: (rtcState: import("@flux/shared/utils").TRTCState) => void) => void;
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
37
|
-
* @param fn
|
|
38
|
-
*
|
|
39
|
-
* @returns { void }
|
|
36
|
+
* Attaches a listener for network connection state changes.
|
|
40
37
|
*/
|
|
41
38
|
onNetworkState: (fn: (networkState: import("@flux/shared/utils").TNetworkConnectionState) => void) => void;
|
|
39
|
+
/**
|
|
40
|
+
* Registers a callback to be called when a message is received.
|
|
41
|
+
*
|
|
42
|
+
* @param { TMessageCallback } cb - Callback to invoke when a message is received
|
|
43
|
+
*/
|
|
42
44
|
onMessage(cb: TMessageCallback): void;
|
|
43
45
|
}
|
|
@@ -5,6 +5,14 @@ import { ICEConnection } from './connector/low-level-com/web-rtc/ice-connection'
|
|
|
5
5
|
export declare class FluxRemoteClient {
|
|
6
6
|
private readonly iceConnection;
|
|
7
7
|
constructor(iceConnection: ICEConnection);
|
|
8
|
+
/**
|
|
9
|
+
* Calls a remote procedure on the connected client.
|
|
10
|
+
*/
|
|
8
11
|
callProcedure(): void;
|
|
12
|
+
/**
|
|
13
|
+
* Sends a message to the connected client.
|
|
14
|
+
*
|
|
15
|
+
* @param { string } message - The message to send to the connected client
|
|
16
|
+
*/
|
|
9
17
|
send(message: string): void;
|
|
10
18
|
}
|
|
@@ -4,10 +4,12 @@ interface IEncryptedData {
|
|
|
4
4
|
salt: string;
|
|
5
5
|
}
|
|
6
6
|
/**
|
|
7
|
-
* Decrypts an AES-256-GCM encrypted JSON object using a password
|
|
8
|
-
*
|
|
9
|
-
* @param {
|
|
10
|
-
* @
|
|
7
|
+
* Decrypts an AES-256-GCM encrypted JSON object using a password.
|
|
8
|
+
*
|
|
9
|
+
* @param { IEncryptedData } encryptedData - The encrypted data object containing iv, salt, and ciphertext
|
|
10
|
+
* @param { string } password - The password to derive the decryption key from
|
|
11
|
+
*
|
|
12
|
+
* @returns { Promise<Object> } The decrypted JSON object
|
|
11
13
|
*/
|
|
12
14
|
export declare function decrypt(encryptedData: IEncryptedData, password: string): Promise<any>;
|
|
13
15
|
export {};
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Encrypts a JSON object using AES-256-GCM with password-derived key
|
|
2
|
+
* Encrypts a JSON object using AES-256-GCM with a password-derived key.
|
|
3
3
|
*
|
|
4
|
-
* @param {
|
|
5
|
-
* @param {string} password - The password
|
|
6
|
-
*
|
|
4
|
+
* @param { string } data - The data string to encrypt
|
|
5
|
+
* @param { string } password - The password to derive the encryption key from
|
|
6
|
+
*
|
|
7
|
+
* @returns { Promise<Object> } The encrypted data in Base64 format with iv and salt
|
|
7
8
|
*/
|
|
8
9
|
export declare function encrypt(data: string, password: string): Promise<{
|
|
9
10
|
encrypted: string;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Derives a cryptographic key from a password using PBKDF2.
|
|
3
3
|
*
|
|
4
|
-
* @param { string } password -
|
|
5
|
-
* @param {
|
|
4
|
+
* @param { string } password - The password to derive the key from
|
|
5
|
+
* @param { any } salt - The salt for key derivation
|
|
6
6
|
*
|
|
7
|
-
* @returns { Promise<CryptoKey> }
|
|
7
|
+
* @returns { Promise<CryptoKey> } The derived cryptographic key
|
|
8
8
|
*/
|
|
9
9
|
export declare function deriveKey(password: string, salt: any): Promise<CryptoKey>;
|