@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 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
- * @param { RTCSessionDescriptionInit } offer
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
- * Listen to messages on this channel.
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 client.
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
- options
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
- // Defaults
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 channelName = message_.slice(firstColon + 1, secondColon);
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(secondColon + 1);
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 } identification
1046
- * @param { string } [clientUId]
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
- const ticket = await authenticateAgentOrThrow(
1056
- this.networkId,
1057
- this.options?.domain ?? "http://localhost:5100",
1058
- identification,
1059
- {
1060
- clientUId,
1061
- machineUID: await getMachineUID() ?? void 0
1062
- }
1063
- );
1064
- this.fluxWebSocketConnection = createWSConnection(
1065
- this.id,
1066
- ticket,
1067
- this.stateManager,
1068
- async () => {
1069
- await this.connect(
1070
- identification,
1071
- clientUId
1072
- );
1073
- },
1074
- this.options
1075
- );
1076
- this.fluxClientData.updateWsConnection(this.fluxWebSocketConnection);
1077
- const fluxNetworkConnection = await this.fluxWebSocketConnection.connectToNetwork();
1078
- return fluxNetworkConnection;
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.12",
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": "./index.js",
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/persistica.git"
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/persistica/issues"
26
+ "url": "https://github.com/rujorgensen/flux/issues"
21
27
  },
22
- "homepage": "https://github.com/rujorgensen/persistica#readme"
28
+ "homepage": "https://github.com/rujorgensen/flux#readme"
23
29
  }
package/src/index.d.ts CHANGED
@@ -1,2 +1,4 @@
1
1
  export type { FluxAgentNetworkConnection } from '@flux/shared/connection';
2
2
  export { FluxAgent, } from './lib/flux-agent.class';
3
+ export { FluxNetworkChannel, } from '@flux/shared/connection';
4
+ export { FluxRemoteClient, } from './lib/flux-remote-client.class';
@@ -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 } channelTopic - the topic to subscribe to
6
- * @param { unknown } identification - whatever identification the authority requires
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> } - true if the channel connection is authorized, false otherwise
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 { 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]
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
- * @param { RTCSessionDescriptionInit } offer
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 } identification
22
- * @param { string } [clientUId]
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
- * @param {IEncryptedData} encryptedData - The encrypted data object
9
- * @param {string} password - The password
10
- * @returns {Promise<Object>} - Decrypted JSON object
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 {Object} data - The data to encrypt
5
- * @param {string} password - The password
6
- * @returns {Promise<Object>} - Encrypted data (Base64 format)
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
- * Derive a cryptographic key from a password using PBKDF2
2
+ * Derives a cryptographic key from a password using PBKDF2.
3
3
  *
4
- * @param { string } password - User-provided password
5
- * @param { Uint8Array } salt - Salt for key derivation
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> } - AES-256-GCM key
7
+ * @returns { Promise<CryptoKey> } The derived cryptographic key
8
8
  */
9
9
  export declare function deriveKey(password: string, salt: any): Promise<CryptoKey>;