@persistica/flux-agent 0.0.38 → 0.0.40
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 +89 -30
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -703,6 +703,18 @@ var ChannelStateManager = class {
|
|
|
703
703
|
}
|
|
704
704
|
return Promise.reject(new Error("Not connected"));
|
|
705
705
|
}
|
|
706
|
+
/**
|
|
707
|
+
* Re-subscribes every joined channel on a fresh socket. The mesh ties
|
|
708
|
+
* channel membership to the socket, so after a re-sign-on nothing is joined
|
|
709
|
+
* even though the channels — and their use counts — live on here unchanged.
|
|
710
|
+
* The mesh answers each with a subscribe ack nobody awaits, which
|
|
711
|
+
* `FluxWebSocketConnection.handleMessage` swallows.
|
|
712
|
+
*/
|
|
713
|
+
resubscribeJoinedChannels(webSocketClient) {
|
|
714
|
+
for (const channelName of this.joinedChannels.keys()) {
|
|
715
|
+
webSocketClient.send(`${SUBSCRIBE_NETWORK_CHANNEL_NAME}:${channelName}`);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
706
718
|
/**
|
|
707
719
|
* Leave a channel.
|
|
708
720
|
*/
|
|
@@ -754,6 +766,7 @@ var ChannelStateManager = class {
|
|
|
754
766
|
|
|
755
767
|
// libs/flux/shared/connection/src/lib/flux-ws-connection.ts
|
|
756
768
|
var RECONNECT_DELAY_MS = 2e3;
|
|
769
|
+
var MAX_SIGN_ON_RETRY_DELAY_MS = 3e4;
|
|
757
770
|
var createWSConnection = (id, ticket, stateManager, onReconnectCallback, options) => {
|
|
758
771
|
return new FluxWebSocketConnection(
|
|
759
772
|
id,
|
|
@@ -779,20 +792,7 @@ var FluxWebSocketConnection = class {
|
|
|
779
792
|
secretKey: this.options.secretKey,
|
|
780
793
|
retries: this.options.retries ?? 1e4
|
|
781
794
|
};
|
|
782
|
-
|
|
783
|
-
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
784
|
-
url.searchParams.set("token", this.token);
|
|
785
|
-
this.socket = new FluxWebSocketClientConnection(
|
|
786
|
-
{
|
|
787
|
-
url: url.toString(),
|
|
788
|
-
// The URL carries a mesh-issued ticket that expires (15 minutes)
|
|
789
|
-
// long before this connection does. Re-dialling it is pointless —
|
|
790
|
-
// the mesh answers 'jwt expired' every time — so coming back is
|
|
791
|
-
// `onReconnectCallback`'s job: it signs on again from scratch.
|
|
792
|
-
autoReconnect: false,
|
|
793
|
-
retries: this.options.retries
|
|
794
|
-
}
|
|
795
|
-
);
|
|
795
|
+
this.socket = this.createSocket(this.token);
|
|
796
796
|
this.interceptPackageTypeMessages("isReady", this.readyInterceptor);
|
|
797
797
|
}
|
|
798
798
|
fluxInstanceId;
|
|
@@ -803,6 +803,9 @@ var FluxWebSocketConnection = class {
|
|
|
803
803
|
// ****************************************************************************
|
|
804
804
|
// *** State
|
|
805
805
|
// ****************************************************************************
|
|
806
|
+
// Mutable: a re-sign-on builds a fresh socket — the ticket is baked into its
|
|
807
|
+
// URL — but keeps this connection object, so every handle and listener
|
|
808
|
+
// registered against it survives (#536).
|
|
806
809
|
socket;
|
|
807
810
|
callbacks = /* @__PURE__ */ new Set();
|
|
808
811
|
channelCallbacks = /* @__PURE__ */ new Map();
|
|
@@ -812,6 +815,9 @@ var FluxWebSocketConnection = class {
|
|
|
812
815
|
connectPromise;
|
|
813
816
|
connectPromiseResolver;
|
|
814
817
|
channelStateManager = new ChannelStateManager();
|
|
818
|
+
// Whether the authority-side channel-change subscription was requested. The
|
|
819
|
+
// mesh ties it to the socket, so a re-signed-on connection has to ask again.
|
|
820
|
+
subscribedToChannelChanges = false;
|
|
815
821
|
// ****************************************************************************
|
|
816
822
|
// *** Reused socket callbacks
|
|
817
823
|
// ****************************************************************************
|
|
@@ -823,6 +829,27 @@ var FluxWebSocketConnection = class {
|
|
|
823
829
|
socketConnectFailedHandler = () => this.handleSocketConnectFailed();
|
|
824
830
|
socketConnectingHandler = (retryAttempt) => this.handleSocketConnecting(retryAttempt);
|
|
825
831
|
socketErrorHandler = (error) => this.handleSocketError(error);
|
|
832
|
+
/**
|
|
833
|
+
* Builds the low-level socket. The URL embeds the single-use, expiring
|
|
834
|
+
* ticket, so every sign-on — the first one and every one after a drop —
|
|
835
|
+
* needs its own socket. `reconnect()` swaps the fresh one in.
|
|
836
|
+
*/
|
|
837
|
+
createSocket(ticket) {
|
|
838
|
+
const url = new URL(this.options.domain);
|
|
839
|
+
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
840
|
+
url.searchParams.set("token", ticket);
|
|
841
|
+
return new FluxWebSocketClientConnection(
|
|
842
|
+
{
|
|
843
|
+
url: url.toString(),
|
|
844
|
+
// The URL carries a mesh-issued ticket that expires (15 minutes)
|
|
845
|
+
// long before this connection does. Re-dialling it is pointless —
|
|
846
|
+
// the mesh answers 'jwt expired' every time — so coming back is
|
|
847
|
+
// `onReconnectCallback`'s job: it signs on again from scratch.
|
|
848
|
+
autoReconnect: false,
|
|
849
|
+
retries: this.options.retries
|
|
850
|
+
}
|
|
851
|
+
);
|
|
852
|
+
}
|
|
826
853
|
/**
|
|
827
854
|
* Intercepts messages of a given package type.
|
|
828
855
|
*/
|
|
@@ -866,6 +893,23 @@ var FluxWebSocketConnection = class {
|
|
|
866
893
|
this.socket.connect().catch(this.socketErrorHandler);
|
|
867
894
|
return this.connectPromise;
|
|
868
895
|
}
|
|
896
|
+
/**
|
|
897
|
+
* Signs on again with a fresh ticket, keeping this connection object — and
|
|
898
|
+
* with it every channel handle and listener registered before the drop.
|
|
899
|
+
*
|
|
900
|
+
* A sign-on used to build a whole new connection, whose empty channel state
|
|
901
|
+
* left the old handles pointing at a dead socket: `publish()` was a silent
|
|
902
|
+
* no-op and `onPublish` listeners never fired again (#536).
|
|
903
|
+
*
|
|
904
|
+
* Only swaps the socket in; the caller's next `connect()` dials it.
|
|
905
|
+
*/
|
|
906
|
+
reconnect(ticket) {
|
|
907
|
+
this.socket.close();
|
|
908
|
+
this.webSocketClient = void 0;
|
|
909
|
+
this.connectPromise = void 0;
|
|
910
|
+
this.connectPromiseResolver = void 0;
|
|
911
|
+
this.socket = this.createSocket(ticket);
|
|
912
|
+
}
|
|
869
913
|
/**
|
|
870
914
|
* Disconnects from the Flux platform.
|
|
871
915
|
*/
|
|
@@ -928,6 +972,8 @@ var FluxWebSocketConnection = class {
|
|
|
928
972
|
if (this.webSocketClient) {
|
|
929
973
|
const messageString = typeof message === "string" ? `s:${message}` : `o:${JSON.stringify(message)}`;
|
|
930
974
|
this.webSocketClient.send(`${NETWORK_CHANNEL_PUBLISH}:${channelName}:${messageString}`);
|
|
975
|
+
} else {
|
|
976
|
+
console.warn(`Cannot publish to channel "${channelName}": the connection is down. Message dropped.`);
|
|
931
977
|
}
|
|
932
978
|
}
|
|
933
979
|
/**
|
|
@@ -965,6 +1011,7 @@ var FluxWebSocketConnection = class {
|
|
|
965
1011
|
* Subscribes to the channel changes.
|
|
966
1012
|
*/
|
|
967
1013
|
subscribeToChannelChanges() {
|
|
1014
|
+
this.subscribedToChannelChanges = true;
|
|
968
1015
|
if (this.webSocketClient) {
|
|
969
1016
|
this.webSocketClient.send(AUTHORITY_CHANNEL_SUBSCRIBE);
|
|
970
1017
|
} else {
|
|
@@ -990,6 +1037,10 @@ var FluxWebSocketConnection = class {
|
|
|
990
1037
|
resolveConnectedSocket() {
|
|
991
1038
|
this.stateManager.emitNetworkState("connected");
|
|
992
1039
|
this.webSocketClient = this.socket;
|
|
1040
|
+
this.channelStateManager.resubscribeJoinedChannels(this.socket);
|
|
1041
|
+
if (this.subscribedToChannelChanges) {
|
|
1042
|
+
this.socket.send(AUTHORITY_CHANNEL_SUBSCRIBE);
|
|
1043
|
+
}
|
|
993
1044
|
const connectPromiseResolver = this.connectPromiseResolver;
|
|
994
1045
|
this.connectPromiseResolver = void 0;
|
|
995
1046
|
this.connectPromise = void 0;
|
|
@@ -1036,6 +1087,7 @@ var FluxWebSocketConnection = class {
|
|
|
1036
1087
|
`Failed to sign on again: ${error instanceof Error ? error.message : String(error)}`,
|
|
1037
1088
|
this.fluxInstanceId
|
|
1038
1089
|
);
|
|
1090
|
+
this.scheduleSignOn(Math.min(delayMs * 2, MAX_SIGN_ON_RETRY_DELAY_MS));
|
|
1039
1091
|
});
|
|
1040
1092
|
},
|
|
1041
1093
|
delayMs
|
|
@@ -1097,6 +1149,9 @@ var FluxWebSocketConnection = class {
|
|
|
1097
1149
|
}
|
|
1098
1150
|
return;
|
|
1099
1151
|
}
|
|
1152
|
+
case SUBSCRIBED_NETWORK_CHANNEL_NAME: {
|
|
1153
|
+
return;
|
|
1154
|
+
}
|
|
1100
1155
|
case RPC_REQUEST: {
|
|
1101
1156
|
const payload = JSON.parse(message_.substring(message_.indexOf(":") + 1));
|
|
1102
1157
|
void this.socket.handleMessage(payload, (data) => {
|
|
@@ -1532,22 +1587,26 @@ var FluxAgent = class {
|
|
|
1532
1587
|
}
|
|
1533
1588
|
}
|
|
1534
1589
|
);
|
|
1535
|
-
this.fluxWebSocketConnection
|
|
1536
|
-
this.
|
|
1537
|
-
|
|
1538
|
-
this.
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1590
|
+
if (this.fluxWebSocketConnection) {
|
|
1591
|
+
this.fluxWebSocketConnection.reconnect(ticket);
|
|
1592
|
+
} else {
|
|
1593
|
+
this.fluxWebSocketConnection = createWSConnection(
|
|
1594
|
+
this.id,
|
|
1595
|
+
ticket,
|
|
1596
|
+
this.stateManager,
|
|
1597
|
+
async () => {
|
|
1598
|
+
await this.connect(
|
|
1599
|
+
identification,
|
|
1600
|
+
clientUId
|
|
1601
|
+
);
|
|
1602
|
+
},
|
|
1603
|
+
{
|
|
1604
|
+
...this.options,
|
|
1605
|
+
domain
|
|
1606
|
+
}
|
|
1607
|
+
);
|
|
1608
|
+
this.fluxClientData.updateWsConnection(this.fluxWebSocketConnection);
|
|
1609
|
+
}
|
|
1551
1610
|
return await this.fluxWebSocketConnection.connectToNetwork();
|
|
1552
1611
|
} catch (error) {
|
|
1553
1612
|
this.stateManager.emitNetworkState("auth-error");
|