@cratis/arc 22.138.1 → 22.138.3
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/dist/cjs/queries/WebSocketHubConnection.d.ts +5 -0
- package/dist/cjs/queries/WebSocketHubConnection.d.ts.map +1 -1
- package/dist/cjs/queries/WebSocketHubConnection.js +39 -11
- package/dist/cjs/queries/WebSocketHubConnection.js.map +1 -1
- package/dist/esm/queries/WebSocketHubConnection.d.ts +5 -0
- package/dist/esm/queries/WebSocketHubConnection.d.ts.map +1 -1
- package/dist/esm/queries/WebSocketHubConnection.js +39 -11
- package/dist/esm/queries/WebSocketHubConnection.js.map +1 -1
- package/dist/esm/queries/for_ObservableQueryFor/when_subscribing/with_server_validation_failing.js +1 -0
- package/dist/esm/queries/for_ObservableQueryFor/when_subscribing/with_server_validation_failing.js.map +1 -1
- package/dist/esm/queries/for_ObservableQueryFor/when_subscribing/with_web_socket_hub_payload_without_readiness.js +1 -0
- package/dist/esm/queries/for_ObservableQueryFor/when_subscribing/with_web_socket_hub_payload_without_readiness.js.map +1 -1
- package/dist/esm/queries/for_WebSocketHubConnection/given/a_web_socket_hub_connection.d.ts +2 -1
- package/dist/esm/queries/for_WebSocketHubConnection/given/a_web_socket_hub_connection.d.ts.map +1 -1
- package/dist/esm/queries/for_WebSocketHubConnection/given/a_web_socket_hub_connection.js +8 -1
- package/dist/esm/queries/for_WebSocketHubConnection/given/a_web_socket_hub_connection.js.map +1 -1
- package/dist/esm/queries/for_WebSocketHubConnection/when_receiving_unauthorized/notifies_subscriber_and_removes_subscription.js +2 -6
- package/dist/esm/queries/for_WebSocketHubConnection/when_receiving_unauthorized/notifies_subscriber_and_removes_subscription.js.map +1 -1
- package/dist/esm/queries/for_WebSocketHubConnection/when_reconnecting/resubscribes_all_queries.js +4 -17
- package/dist/esm/queries/for_WebSocketHubConnection/when_reconnecting/resubscribes_all_queries.js.map +1 -1
- package/dist/esm/queries/for_WebSocketHubConnection/when_replacing_a_subscription/ignores_stale_result_unauthorized_and_error.js +3 -7
- package/dist/esm/queries/for_WebSocketHubConnection/when_replacing_a_subscription/ignores_stale_result_unauthorized_and_error.js.map +1 -1
- package/dist/esm/queries/for_WebSocketHubConnection/when_subscribing/before_the_server_announces_itself.d.ts +2 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_subscribing/before_the_server_announces_itself.d.ts.map +1 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_subscribing/before_the_server_announces_itself.js +31 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_subscribing/before_the_server_announces_itself.js.map +1 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_subscription_revisions_are_supported/upgrades_active_subscriptions_to_strict_mode.js +7 -11
- package/dist/esm/queries/for_WebSocketHubConnection/when_subscription_revisions_are_supported/upgrades_active_subscriptions_to_strict_mode.js.map +1 -1
- package/dist/esm/queries/for_WebSocketHubConnection/when_unsubscribing/the_only_query.js +1 -5
- package/dist/esm/queries/for_WebSocketHubConnection/when_unsubscribing/the_only_query.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/queries/WebSocketHubConnection.ts +44 -15
- package/queries/for_ObservableQueryFor/when_subscribing/with_server_validation_failing.ts +2 -0
- package/queries/for_ObservableQueryFor/when_subscribing/with_web_socket_hub_payload_without_readiness.ts +2 -1
- package/queries/for_WebSocketHubConnection/given/a_web_socket_hub_connection.ts +14 -1
- package/queries/for_WebSocketHubConnection/when_receiving_unauthorized/notifies_subscriber_and_removes_subscription.ts +2 -6
- package/queries/for_WebSocketHubConnection/when_reconnecting/resubscribes_all_queries.ts +5 -19
- package/queries/for_WebSocketHubConnection/when_replacing_a_subscription/ignores_stale_result_unauthorized_and_error.ts +3 -7
- package/queries/for_WebSocketHubConnection/when_subscribing/before_the_server_announces_itself.ts +43 -0
- package/queries/for_WebSocketHubConnection/when_subscription_revisions_are_supported/upgrades_active_subscriptions_to_strict_mode.ts +7 -14
- package/queries/for_WebSocketHubConnection/when_unsubscribing/the_only_query.ts +1 -5
|
@@ -32,6 +32,7 @@ export declare class WebSocketHubConnection {
|
|
|
32
32
|
private readonly _url;
|
|
33
33
|
private readonly _microservice;
|
|
34
34
|
private readonly _policy;
|
|
35
|
+
static readonly ConnectedGraceMs = 1000;
|
|
35
36
|
private _socket?;
|
|
36
37
|
private _disconnected;
|
|
37
38
|
private _subscriptions;
|
|
@@ -42,6 +43,8 @@ export declare class WebSocketHubConnection {
|
|
|
42
43
|
private _nextRevision;
|
|
43
44
|
private _supportsSubscriptionRevisions;
|
|
44
45
|
private _reconnectPending;
|
|
46
|
+
private _subscriptionsReleased;
|
|
47
|
+
private _releaseTimer?;
|
|
45
48
|
constructor(_url: string, _microservice: string, pingIntervalMs?: number, _policy?: IReconnectPolicy);
|
|
46
49
|
get queryCount(): number;
|
|
47
50
|
get isConnected(): boolean;
|
|
@@ -55,6 +58,8 @@ export declare class WebSocketHubConnection {
|
|
|
55
58
|
private openSocket;
|
|
56
59
|
private cancelPendingReconnect;
|
|
57
60
|
private detachAndCloseSocket;
|
|
61
|
+
private resetSubscriptionRelease;
|
|
62
|
+
private releaseSubscriptions;
|
|
58
63
|
private sendAllSubscriptions;
|
|
59
64
|
private sendSubscribeMessage;
|
|
60
65
|
private sendMessage;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WebSocketHubConnection.d.ts","sourceRoot":"","sources":["../../../queries/WebSocketHubConnection.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAEnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAU9D,oBAAY,cAAc;IACtB,SAAS,cAAc;IACvB,WAAW,gBAAgB;IAC3B,WAAW,gBAAgB;IAC3B,YAAY,iBAAiB;IAC7B,KAAK,UAAU;IACf,IAAI,SAAS;IACb,IAAI,SAAS;IACb,SAAS,cAAc;CAC1B;AAKD,MAAM,WAAW,UAAU;IACvB,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IAGjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAMlB,6BAA6B,CAAC,EAAE,OAAO,CAAC;IACxC,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IAMnB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAChC;AAKD,MAAM,WAAW,mBAAmB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;CACzB;AAkBD,qBAAa,sBAAsB;
|
|
1
|
+
{"version":3,"file":"WebSocketHubConnection.d.ts","sourceRoot":"","sources":["../../../queries/WebSocketHubConnection.ts"],"names":[],"mappings":"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"}
|
|
@@ -34,6 +34,7 @@ var QueryResult = require('./QueryResult.js');
|
|
|
34
34
|
_url;
|
|
35
35
|
_microservice;
|
|
36
36
|
_policy;
|
|
37
|
+
/** How long to wait for the server's Connected message before subscribing the way older servers expect. */ static ConnectedGraceMs = 1000;
|
|
37
38
|
_socket;
|
|
38
39
|
_disconnected = false;
|
|
39
40
|
_subscriptions = new Map();
|
|
@@ -44,6 +45,8 @@ var QueryResult = require('./QueryResult.js');
|
|
|
44
45
|
_nextRevision = 0;
|
|
45
46
|
_supportsSubscriptionRevisions = false;
|
|
46
47
|
_reconnectPending = false;
|
|
48
|
+
_subscriptionsReleased = false;
|
|
49
|
+
_releaseTimer;
|
|
47
50
|
/**
|
|
48
51
|
* Initializes a new instance of {@link WebSocketHubConnection}.
|
|
49
52
|
* @param {string} _url The WebSocket URL of the hub endpoint (e.g. {@code ws://localhost:5000/.cratis/queries/ws}).
|
|
@@ -99,10 +102,10 @@ var QueryResult = require('./QueryResult.js');
|
|
|
99
102
|
};
|
|
100
103
|
this._subscriptions.set(queryId, subscription);
|
|
101
104
|
this.ensureConnected();
|
|
102
|
-
if (this._socket?.readyState === WebSocket.OPEN) {
|
|
105
|
+
if (this._socket?.readyState === WebSocket.OPEN && this._subscriptionsReleased) {
|
|
103
106
|
this.sendSubscribeMessage(queryId, subscription);
|
|
104
107
|
}
|
|
105
|
-
//
|
|
108
|
+
// Until the server has said what it supports, subscriptions are held back and sent once, by releaseSubscriptions.
|
|
106
109
|
}
|
|
107
110
|
/**
|
|
108
111
|
* Unsubscribe from a query on this hub connection.
|
|
@@ -110,7 +113,7 @@ var QueryResult = require('./QueryResult.js');
|
|
|
110
113
|
*/ unsubscribe(queryId) {
|
|
111
114
|
const subscription = this._subscriptions.get(queryId);
|
|
112
115
|
this._subscriptions.delete(queryId);
|
|
113
|
-
if (this._socket?.readyState === WebSocket.OPEN && subscription) {
|
|
116
|
+
if (this._socket?.readyState === WebSocket.OPEN && this._subscriptionsReleased && subscription) {
|
|
114
117
|
const message = {
|
|
115
118
|
type: "Unsubscribe",
|
|
116
119
|
queryId
|
|
@@ -150,6 +153,7 @@ var QueryResult = require('./QueryResult.js');
|
|
|
150
153
|
this._keepAlive.stop();
|
|
151
154
|
this._policy.cancel();
|
|
152
155
|
this._reconnectPending = false;
|
|
156
|
+
this.resetSubscriptionRelease();
|
|
153
157
|
const socket = this._socket;
|
|
154
158
|
this._socket = undefined;
|
|
155
159
|
this._supportsSubscriptionRevisions = false;
|
|
@@ -167,6 +171,7 @@ var QueryResult = require('./QueryResult.js');
|
|
|
167
171
|
this.detachAndCloseSocket(currentSocket);
|
|
168
172
|
}
|
|
169
173
|
this._supportsSubscriptionRevisions = false;
|
|
174
|
+
this.resetSubscriptionRelease();
|
|
170
175
|
let url = this._url;
|
|
171
176
|
if (this._microservice?.length > 0) {
|
|
172
177
|
const param = `${Globals.Globals.microserviceWSQueryArgument}=${encodeURIComponent(this._microservice)}`;
|
|
@@ -180,13 +185,23 @@ var QueryResult = require('./QueryResult.js');
|
|
|
180
185
|
this.cancelPendingReconnect();
|
|
181
186
|
this._policy.reset();
|
|
182
187
|
this._keepAlive.start();
|
|
183
|
-
|
|
188
|
+
// A capable server announces itself with a Connected message straight after accepting the socket. Sending
|
|
189
|
+
// the subscriptions before that would mean sending them twice (once without a revision, once with), and the
|
|
190
|
+
// server would run every query twice and cancel the first run. Wait for it, and only fall back to the
|
|
191
|
+
// revision-less form for a server that never says anything.
|
|
192
|
+
this._releaseTimer = setTimeout(()=>{
|
|
193
|
+
this._releaseTimer = undefined;
|
|
194
|
+
if (this._socket === socket && !this._disconnected) {
|
|
195
|
+
this.releaseSubscriptions();
|
|
196
|
+
}
|
|
197
|
+
}, WebSocketHubConnection.ConnectedGraceMs);
|
|
184
198
|
};
|
|
185
199
|
socket.onclose = ()=>{
|
|
186
200
|
if (this._socket !== socket || this._disconnected) return;
|
|
187
201
|
console.log('Hub connection closed', url);
|
|
188
202
|
this._socket = undefined;
|
|
189
203
|
this._supportsSubscriptionRevisions = false;
|
|
204
|
+
this.resetSubscriptionRelease();
|
|
190
205
|
this._keepAlive.stop();
|
|
191
206
|
if (this._subscriptions.size === 0) return;
|
|
192
207
|
this._reconnectPending = true;
|
|
@@ -223,6 +238,22 @@ var QueryResult = require('./QueryResult.js');
|
|
|
223
238
|
socket.onmessage = null;
|
|
224
239
|
socket.close();
|
|
225
240
|
}
|
|
241
|
+
resetSubscriptionRelease() {
|
|
242
|
+
this._subscriptionsReleased = false;
|
|
243
|
+
if (this._releaseTimer !== undefined) {
|
|
244
|
+
clearTimeout(this._releaseTimer);
|
|
245
|
+
this._releaseTimer = undefined;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
releaseSubscriptions() {
|
|
249
|
+
if (this._subscriptionsReleased) return;
|
|
250
|
+
this._subscriptionsReleased = true;
|
|
251
|
+
if (this._releaseTimer !== undefined) {
|
|
252
|
+
clearTimeout(this._releaseTimer);
|
|
253
|
+
this._releaseTimer = undefined;
|
|
254
|
+
}
|
|
255
|
+
this.sendAllSubscriptions();
|
|
256
|
+
}
|
|
226
257
|
sendAllSubscriptions() {
|
|
227
258
|
for (const [queryId, sub] of this._subscriptions){
|
|
228
259
|
this.sendSubscribeMessage(queryId, sub);
|
|
@@ -275,13 +306,10 @@ var QueryResult = require('./QueryResult.js');
|
|
|
275
306
|
}
|
|
276
307
|
}
|
|
277
308
|
handleConnected(message) {
|
|
278
|
-
if (
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
this.
|
|
282
|
-
// The legacy subscriptions sent on socket open keep this client compatible with older servers.
|
|
283
|
-
// Ordered WebSocket delivery means these revision-aware replacements are processed afterwards.
|
|
284
|
-
this.sendAllSubscriptions();
|
|
309
|
+
if (this._subscriptionsReleased) return;
|
|
310
|
+
// Capability is known before anything is sent, so every subscription goes out exactly once, in its final form.
|
|
311
|
+
this._supportsSubscriptionRevisions = message.supportsSubscriptionRevisions === true;
|
|
312
|
+
this.releaseSubscriptions();
|
|
285
313
|
}
|
|
286
314
|
handleQueryResult(message) {
|
|
287
315
|
if (!message.queryId) return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WebSocketHubConnection.js","sources":["../../../queries/WebSocketHubConnection.ts"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { Globals } from '../Globals.js';\nimport type { DataReceived } from './ObservableQueryConnection.js';\nimport { HubConnectionKeepAlive } from './HubConnectionKeepAlive.js';\nimport type { IReconnectPolicy } from './IReconnectPolicy.js';\nimport { ReconnectPolicy } from './ReconnectPolicy.js';\nimport { QueryResult } from './QueryResult.js';\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\n/**\n * Message types matching the backend {@link ObservableQueryHubMessageType} enum.\n * Serialized as strings by the JsonStringEnumConverter on the server.\n */\nexport enum HubMessageType {\n Subscribe = 'Subscribe',\n Unsubscribe = 'Unsubscribe',\n QueryResult = 'QueryResult',\n Unauthorized = 'Unauthorized',\n Error = 'Error',\n Ping = 'Ping',\n Pong = 'Pong',\n Connected = 'Connected',\n}\n\n/**\n * Wire format for messages exchanged over the observable query WebSocket and SSE hub transports.\n */\nexport interface HubMessage {\n type: HubMessageType;\n queryId?: string;\n\n /** Optional positive safe-integer revision used to reject stale replacement messages. */\n revision?: number;\n\n /**\n * Whether the server supports revision-aware subscription operations and result frames.\n * Only present on {@link HubMessageType.Connected} messages from capable servers.\n */\n supportsSubscriptionRevisions?: boolean;\n payload?: any;\n timestamp?: number;\n\n /**\n * The server's keep-alive interval in milliseconds, or 0 when keep-alive is disabled.\n * Only present on {@link HubMessageType.Connected} messages.\n */\n keepAliveIntervalMs?: number;\n}\n\n/**\n * Matches the backend {@link ObservableQuerySubscriptionRequest} record.\n */\nexport interface SubscriptionRequest {\n queryName: string;\n arguments?: Record<string, string | null>;\n page?: number;\n pageSize?: number;\n sortBy?: string;\n sortDirection?: string;\n transferMode?: string;\n}\n\ninterface ActiveSubscription {\n request: SubscriptionRequest;\n callback: DataReceived<any>;\n revision: number;\n}\n\n/**\n * Represents a single multiplexed WebSocket connection to the observable query hub\n * at {@code /.cratis/queries/ws}.\n *\n * Multiple query subscriptions are carried over the same physical WebSocket. Each subscription\n * is identified by a client-generated {@code queryId}; the server tags every result message with\n * the same id so responses can be routed to the correct callback. The client initially subscribes\n * without revisions for compatibility with older servers, then replaces active subscriptions with\n * revision-aware operations when the server advertises support in a Connected message.\n */\nexport class WebSocketHubConnection {\n private _socket?: WebSocket;\n private _disconnected = false;\n private _subscriptions: Map<string, ActiveSubscription> = new Map();\n private readonly _keepAlive: HubConnectionKeepAlive;\n private _lastPingSentTime?: number;\n private _lastPongLatency: number = 0;\n private _latencySamples: number[] = [];\n private _nextRevision = 0;\n private _supportsSubscriptionRevisions = false;\n private _reconnectPending = false;\n\n /**\n * Initializes a new instance of {@link WebSocketHubConnection}.\n * @param {string} _url The WebSocket URL of the hub endpoint (e.g. {@code ws://localhost:5000/.cratis/queries/ws}).\n * @param {string} _microservice The microservice name to pass as a query argument.\n * @param {number} pingIntervalMs How often to send keep-alive pings when the connection is idle (default: 10 000 ms).\n * @param {IReconnectPolicy} _policy The reconnect policy to use (default: {@link ReconnectPolicy}).\n */\n constructor(\n private readonly _url: string,\n private readonly _microservice: string,\n pingIntervalMs: number = 10000,\n private readonly _policy: IReconnectPolicy = new ReconnectPolicy(),\n ) {\n this._keepAlive = new HubConnectionKeepAlive(pingIntervalMs, () => {\n if (this._socket?.readyState === WebSocket.OPEN) {\n this._lastPingSentTime = Date.now();\n this.sendMessage({\n type: HubMessageType.Ping,\n timestamp: this._lastPingSentTime,\n });\n }\n });\n }\n\n /**\n * Gets the number of active query subscriptions on this connection.\n */\n get queryCount(): number {\n return this._subscriptions.size;\n }\n\n /**\n * Gets whether the WebSocket connection is currently open.\n */\n get isConnected(): boolean {\n return this._socket?.readyState === WebSocket.OPEN;\n }\n\n /**\n * Gets the latency of the last ping/pong sequence in milliseconds.\n */\n get lastPingLatency(): number {\n return this._lastPongLatency;\n }\n\n /**\n * Gets the rolling average latency in milliseconds.\n */\n get averageLatency(): number {\n if (this._latencySamples.length === 0) return 0;\n return (\n this._latencySamples.reduce((a, b) => a + b, 0) / this._latencySamples.length\n );\n }\n\n /**\n * Subscribe to a query on this hub connection.\n * If the WebSocket is not yet open, the subscribe message will be sent once the connection is established.\n * @param {string} queryId Client-generated unique identifier for this subscription.\n * @param {SubscriptionRequest} request The subscription request payload.\n * @param {DataReceived<any>} callback Callback invoked whenever the server pushes a result for this query.\n */\n subscribe(\n queryId: string,\n request: SubscriptionRequest,\n callback: DataReceived<any>,\n ): void {\n const subscription = { request, callback, revision: this.getNextRevision() };\n this._subscriptions.set(queryId, subscription);\n this.ensureConnected();\n\n if (this._socket?.readyState === WebSocket.OPEN) {\n this.sendSubscribeMessage(queryId, subscription);\n }\n // If not yet open, sendAllSubscriptions will fire in onopen.\n }\n\n /**\n * Unsubscribe from a query on this hub connection.\n * @param {string} queryId The identifier of the subscription to cancel.\n */\n unsubscribe(queryId: string): void {\n const subscription = this._subscriptions.get(queryId);\n this._subscriptions.delete(queryId);\n\n if (this._socket?.readyState === WebSocket.OPEN && subscription) {\n const message: HubMessage = {\n type: HubMessageType.Unsubscribe,\n queryId,\n };\n if (this._supportsSubscriptionRevisions) {\n message.revision = subscription.revision;\n }\n this.sendMessage(message);\n }\n\n // If no subscriptions remain, close the connection to free resources.\n if (this._subscriptions.size === 0) {\n this.close();\n }\n }\n\n /**\n * Permanently close this hub connection and clean up all subscriptions.\n */\n dispose(): void {\n this._subscriptions.clear();\n this.close();\n }\n\n private ensureConnected(): void {\n if (this._disconnected) {\n // Reset disconnected flag when a new subscription comes in\n this._disconnected = false;\n }\n\n if (\n this._socket &&\n (this._socket.readyState === WebSocket.OPEN ||\n this._socket.readyState === WebSocket.CONNECTING)\n ) {\n this.cancelPendingReconnect();\n return;\n }\n\n // A subscription can arrive while an earlier close is waiting in reconnect backoff. The immediate open owns\n // the connection now, so the delayed callback must not be allowed to create a competing socket later.\n this.cancelPendingReconnect();\n this.openSocket();\n }\n\n private close(): void {\n this._disconnected = true;\n this._keepAlive.stop();\n this._policy.cancel();\n this._reconnectPending = false;\n const socket = this._socket;\n this._socket = undefined;\n this._supportsSubscriptionRevisions = false;\n if (socket) {\n this.detachAndCloseSocket(socket);\n }\n }\n\n private openSocket(): void {\n const currentSocket = this._socket;\n if (\n currentSocket &&\n (currentSocket.readyState === WebSocket.OPEN ||\n currentSocket.readyState === WebSocket.CONNECTING)\n ) {\n return;\n }\n\n if (currentSocket) {\n this._socket = undefined;\n this.detachAndCloseSocket(currentSocket);\n }\n\n this._supportsSubscriptionRevisions = false;\n\n let url = this._url;\n if (this._microservice?.length > 0) {\n const param = `${Globals.microserviceWSQueryArgument}=${encodeURIComponent(this._microservice)}`;\n url += (url.includes('?') ? '&' : '?') + param;\n }\n\n const socket = new WebSocket(url);\n this._socket = socket;\n\n socket.onopen = () => {\n if (this._socket !== socket || this._disconnected) return;\n console.log('Hub connection established', url);\n this.cancelPendingReconnect();\n this._policy.reset();\n this._keepAlive.start();\n this.sendAllSubscriptions();\n };\n\n socket.onclose = () => {\n if (this._socket !== socket || this._disconnected) return;\n console.log('Hub connection closed', url);\n this._socket = undefined;\n this._supportsSubscriptionRevisions = false;\n this._keepAlive.stop();\n if (this._subscriptions.size === 0) return;\n this._reconnectPending = true;\n const scheduled = this._policy.schedule(() => {\n this._reconnectPending = false;\n if (!this._disconnected && this._subscriptions.size > 0) {\n this.ensureConnected();\n }\n }, this._url);\n if (!scheduled) {\n this._reconnectPending = false;\n }\n };\n\n socket.onerror = (error) => {\n if (this._socket !== socket || this._disconnected) return;\n console.error('Hub connection error', url, error);\n this._keepAlive.stop();\n // onclose will fire after onerror, triggering reconnect\n };\n\n socket.onmessage = (ev) => {\n if (this._socket !== socket || this._disconnected) return;\n this.handleMessage(ev.data as string);\n };\n }\n\n private cancelPendingReconnect(): void {\n if (!this._reconnectPending) return;\n\n this._policy.cancel();\n this._reconnectPending = false;\n }\n\n private detachAndCloseSocket(socket: WebSocket): void {\n socket.onopen = null;\n socket.onclose = null;\n socket.onerror = null;\n socket.onmessage = null;\n socket.close();\n }\n\n private sendAllSubscriptions(): void {\n for (const [queryId, sub] of this._subscriptions) {\n this.sendSubscribeMessage(queryId, sub);\n }\n }\n\n private sendSubscribeMessage(\n queryId: string,\n subscription: ActiveSubscription,\n ): void {\n const message: HubMessage = {\n type: HubMessageType.Subscribe,\n queryId,\n payload: subscription.request,\n };\n if (this._supportsSubscriptionRevisions) {\n message.revision = subscription.revision;\n }\n this.sendMessage(message);\n }\n\n private sendMessage(message: HubMessage): void {\n if (this._socket?.readyState === WebSocket.OPEN) {\n this._socket.send(JSON.stringify(message));\n }\n }\n\n private handleMessage(rawData: string): void {\n try {\n const message = JSON.parse(rawData) as HubMessage;\n\n // Every received message is activity — skip keep-alive ping if data is flowing.\n this._keepAlive.recordActivity();\n\n switch (message.type) {\n case HubMessageType.Connected:\n this.handleConnected(message);\n break;\n case HubMessageType.QueryResult:\n this.handleQueryResult(message);\n break;\n case HubMessageType.Pong:\n this.handlePong(message);\n break;\n case HubMessageType.Unauthorized:\n console.warn('Hub: query unauthorized', message.queryId);\n this.handleUnauthorized(message);\n break;\n case HubMessageType.Error:\n if (this.isMessageForCurrentSubscription(message)) {\n console.error(\n 'Hub: query error',\n message.queryId,\n message.payload,\n );\n this.handleError(message);\n }\n break;\n }\n } catch (error) {\n console.error('Hub: error parsing message', error);\n }\n }\n\n private handleConnected(message: HubMessage): void {\n if (\n message.supportsSubscriptionRevisions !== true ||\n this._supportsSubscriptionRevisions\n ) {\n return;\n }\n\n this._supportsSubscriptionRevisions = true;\n\n // The legacy subscriptions sent on socket open keep this client compatible with older servers.\n // Ordered WebSocket delivery means these revision-aware replacements are processed afterwards.\n this.sendAllSubscriptions();\n }\n\n private handleQueryResult(message: HubMessage): void {\n if (!message.queryId) return;\n\n const sub = this._subscriptions.get(message.queryId);\n if (!sub || !this.isMessageForCurrentSubscription(message)) return;\n\n const result = message.payload as QueryResult<any>;\n if (result?.isValid === false) {\n this._subscriptions.delete(message.queryId);\n }\n sub.callback(result);\n }\n\n private handleError(message: HubMessage): void {\n const result = new QueryResult(\n {\n data: undefined,\n isSuccess: false,\n isReady: true,\n isAuthorized: true,\n isValid: true,\n hasExceptions: true,\n validationResults: [],\n exceptionMessages: [message.payload as string],\n exceptionStackTrace: '',\n paging: { totalItems: 0, totalPages: 0, page: 0, size: 0 },\n },\n Object,\n false,\n );\n this.handleQueryResult({ ...message, payload: result });\n }\n\n private handleUnauthorized(message: HubMessage): void {\n if (!message.queryId) return;\n\n const sub = this._subscriptions.get(message.queryId);\n if (!sub || !this.isMessageForCurrentSubscription(message)) return;\n\n this._subscriptions.delete(message.queryId);\n sub.callback(QueryResult.unauthorized());\n }\n\n private isMessageForCurrentSubscription(message: HubMessage): boolean {\n if (!message.queryId) return false;\n\n if (!this._supportsSubscriptionRevisions) {\n return (\n message.revision === undefined &&\n this._subscriptions.has(message.queryId)\n );\n }\n\n return (\n this.isValidRevision(message.revision) &&\n this._subscriptions.get(message.queryId)?.revision === message.revision\n );\n }\n\n private isValidRevision(revision: number | undefined): revision is number {\n return revision !== undefined && Number.isSafeInteger(revision) && revision > 0;\n }\n\n private getNextRevision(): number {\n if (this._nextRevision >= Number.MAX_SAFE_INTEGER) {\n throw new RangeError(\n 'WebSocket hub subscription revision exhausted the safe integer range',\n );\n }\n\n return ++this._nextRevision;\n }\n\n private handlePong(message: HubMessage): void {\n if (message.timestamp && this._lastPingSentTime) {\n const latency = Date.now() - message.timestamp;\n this._lastPongLatency = latency;\n this._latencySamples.push(latency);\n\n if (this._latencySamples.length > 100) {\n this._latencySamples.shift();\n }\n }\n }\n}\n"],"names":["HubMessageType","WebSocketHubConnection","_socket","_disconnected","_subscriptions","Map","_keepAlive","_lastPingSentTime","_lastPongLatency","_latencySamples","_nextRevision","_supportsSubscriptionRevisions","_reconnectPending","_url","pingIntervalMs","_policy","ReconnectPolicy","_microservice","HubConnectionKeepAlive","readyState","WebSocket","OPEN","Date","now","sendMessage","type","timestamp","queryCount","size","isConnected","lastPingLatency","averageLatency","length","reduce","a","b","subscribe","queryId","request","callback","subscription","revision","getNextRevision","set","ensureConnected","sendSubscribeMessage","unsubscribe","get","delete","message","close","dispose","clear","CONNECTING","cancelPendingReconnect","openSocket","stop","cancel","socket","undefined","detachAndCloseSocket","currentSocket","url","param","Globals","microserviceWSQueryArgument","encodeURIComponent","includes","onopen","console","log","reset","start","sendAllSubscriptions","onclose","scheduled","schedule","onerror","error","onmessage","ev","handleMessage","data","sub","payload","send","JSON","stringify","rawData","parse","recordActivity","handleConnected","handleQueryResult","handlePong","warn","handleUnauthorized","isMessageForCurrentSubscription","handleError","supportsSubscriptionRevisions","result","isValid","QueryResult","isSuccess","isReady","isAuthorized","hasExceptions","validationResults","exceptionMessages","exceptionStackTrace","paging","totalItems","totalPages","page","Object","unauthorized","has","isValidRevision","Number","isSafeInteger","MAX_SAFE_INTEGER","RangeError","latency","push","shift"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"WebSocketHubConnection.js","sources":["../../../queries/WebSocketHubConnection.ts"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { Globals } from '../Globals.js';\nimport type { DataReceived } from './ObservableQueryConnection.js';\nimport { HubConnectionKeepAlive } from './HubConnectionKeepAlive.js';\nimport type { IReconnectPolicy } from './IReconnectPolicy.js';\nimport { ReconnectPolicy } from './ReconnectPolicy.js';\nimport { QueryResult } from './QueryResult.js';\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\n/**\n * Message types matching the backend {@link ObservableQueryHubMessageType} enum.\n * Serialized as strings by the JsonStringEnumConverter on the server.\n */\nexport enum HubMessageType {\n Subscribe = 'Subscribe',\n Unsubscribe = 'Unsubscribe',\n QueryResult = 'QueryResult',\n Unauthorized = 'Unauthorized',\n Error = 'Error',\n Ping = 'Ping',\n Pong = 'Pong',\n Connected = 'Connected',\n}\n\n/**\n * Wire format for messages exchanged over the observable query WebSocket and SSE hub transports.\n */\nexport interface HubMessage {\n type: HubMessageType;\n queryId?: string;\n\n /** Optional positive safe-integer revision used to reject stale replacement messages. */\n revision?: number;\n\n /**\n * Whether the server supports revision-aware subscription operations and result frames.\n * Only present on {@link HubMessageType.Connected} messages from capable servers.\n */\n supportsSubscriptionRevisions?: boolean;\n payload?: any;\n timestamp?: number;\n\n /**\n * The server's keep-alive interval in milliseconds, or 0 when keep-alive is disabled.\n * Only present on {@link HubMessageType.Connected} messages.\n */\n keepAliveIntervalMs?: number;\n}\n\n/**\n * Matches the backend {@link ObservableQuerySubscriptionRequest} record.\n */\nexport interface SubscriptionRequest {\n queryName: string;\n arguments?: Record<string, string | null>;\n page?: number;\n pageSize?: number;\n sortBy?: string;\n sortDirection?: string;\n transferMode?: string;\n}\n\ninterface ActiveSubscription {\n request: SubscriptionRequest;\n callback: DataReceived<any>;\n revision: number;\n}\n\n/**\n * Represents a single multiplexed WebSocket connection to the observable query hub\n * at {@code /.cratis/queries/ws}.\n *\n * Multiple query subscriptions are carried over the same physical WebSocket. Each subscription\n * is identified by a client-generated {@code queryId}; the server tags every result message with\n * the same id so responses can be routed to the correct callback. The client initially subscribes\n * without revisions for compatibility with older servers, then replaces active subscriptions with\n * revision-aware operations when the server advertises support in a Connected message.\n */\nexport class WebSocketHubConnection {\n /** How long to wait for the server's Connected message before subscribing the way older servers expect. */\n static readonly ConnectedGraceMs = 1000;\n\n private _socket?: WebSocket;\n private _disconnected = false;\n private _subscriptions: Map<string, ActiveSubscription> = new Map();\n private readonly _keepAlive: HubConnectionKeepAlive;\n private _lastPingSentTime?: number;\n private _lastPongLatency: number = 0;\n private _latencySamples: number[] = [];\n private _nextRevision = 0;\n private _supportsSubscriptionRevisions = false;\n private _reconnectPending = false;\n private _subscriptionsReleased = false;\n private _releaseTimer?: ReturnType<typeof setTimeout>;\n\n /**\n * Initializes a new instance of {@link WebSocketHubConnection}.\n * @param {string} _url The WebSocket URL of the hub endpoint (e.g. {@code ws://localhost:5000/.cratis/queries/ws}).\n * @param {string} _microservice The microservice name to pass as a query argument.\n * @param {number} pingIntervalMs How often to send keep-alive pings when the connection is idle (default: 10 000 ms).\n * @param {IReconnectPolicy} _policy The reconnect policy to use (default: {@link ReconnectPolicy}).\n */\n constructor(\n private readonly _url: string,\n private readonly _microservice: string,\n pingIntervalMs: number = 10000,\n private readonly _policy: IReconnectPolicy = new ReconnectPolicy(),\n ) {\n this._keepAlive = new HubConnectionKeepAlive(pingIntervalMs, () => {\n if (this._socket?.readyState === WebSocket.OPEN) {\n this._lastPingSentTime = Date.now();\n this.sendMessage({\n type: HubMessageType.Ping,\n timestamp: this._lastPingSentTime,\n });\n }\n });\n }\n\n /**\n * Gets the number of active query subscriptions on this connection.\n */\n get queryCount(): number {\n return this._subscriptions.size;\n }\n\n /**\n * Gets whether the WebSocket connection is currently open.\n */\n get isConnected(): boolean {\n return this._socket?.readyState === WebSocket.OPEN;\n }\n\n /**\n * Gets the latency of the last ping/pong sequence in milliseconds.\n */\n get lastPingLatency(): number {\n return this._lastPongLatency;\n }\n\n /**\n * Gets the rolling average latency in milliseconds.\n */\n get averageLatency(): number {\n if (this._latencySamples.length === 0) return 0;\n return (\n this._latencySamples.reduce((a, b) => a + b, 0) / this._latencySamples.length\n );\n }\n\n /**\n * Subscribe to a query on this hub connection.\n * If the WebSocket is not yet open, the subscribe message will be sent once the connection is established.\n * @param {string} queryId Client-generated unique identifier for this subscription.\n * @param {SubscriptionRequest} request The subscription request payload.\n * @param {DataReceived<any>} callback Callback invoked whenever the server pushes a result for this query.\n */\n subscribe(\n queryId: string,\n request: SubscriptionRequest,\n callback: DataReceived<any>,\n ): void {\n const subscription = { request, callback, revision: this.getNextRevision() };\n this._subscriptions.set(queryId, subscription);\n this.ensureConnected();\n\n if (this._socket?.readyState === WebSocket.OPEN && this._subscriptionsReleased) {\n this.sendSubscribeMessage(queryId, subscription);\n }\n // Until the server has said what it supports, subscriptions are held back and sent once, by releaseSubscriptions.\n }\n\n /**\n * Unsubscribe from a query on this hub connection.\n * @param {string} queryId The identifier of the subscription to cancel.\n */\n unsubscribe(queryId: string): void {\n const subscription = this._subscriptions.get(queryId);\n this._subscriptions.delete(queryId);\n\n if (this._socket?.readyState === WebSocket.OPEN && this._subscriptionsReleased && subscription) {\n const message: HubMessage = {\n type: HubMessageType.Unsubscribe,\n queryId,\n };\n if (this._supportsSubscriptionRevisions) {\n message.revision = subscription.revision;\n }\n this.sendMessage(message);\n }\n\n // If no subscriptions remain, close the connection to free resources.\n if (this._subscriptions.size === 0) {\n this.close();\n }\n }\n\n /**\n * Permanently close this hub connection and clean up all subscriptions.\n */\n dispose(): void {\n this._subscriptions.clear();\n this.close();\n }\n\n private ensureConnected(): void {\n if (this._disconnected) {\n // Reset disconnected flag when a new subscription comes in\n this._disconnected = false;\n }\n\n if (\n this._socket &&\n (this._socket.readyState === WebSocket.OPEN ||\n this._socket.readyState === WebSocket.CONNECTING)\n ) {\n this.cancelPendingReconnect();\n return;\n }\n\n // A subscription can arrive while an earlier close is waiting in reconnect backoff. The immediate open owns\n // the connection now, so the delayed callback must not be allowed to create a competing socket later.\n this.cancelPendingReconnect();\n this.openSocket();\n }\n\n private close(): void {\n this._disconnected = true;\n this._keepAlive.stop();\n this._policy.cancel();\n this._reconnectPending = false;\n this.resetSubscriptionRelease();\n const socket = this._socket;\n this._socket = undefined;\n this._supportsSubscriptionRevisions = false;\n if (socket) {\n this.detachAndCloseSocket(socket);\n }\n }\n\n private openSocket(): void {\n const currentSocket = this._socket;\n if (\n currentSocket &&\n (currentSocket.readyState === WebSocket.OPEN ||\n currentSocket.readyState === WebSocket.CONNECTING)\n ) {\n return;\n }\n\n if (currentSocket) {\n this._socket = undefined;\n this.detachAndCloseSocket(currentSocket);\n }\n\n this._supportsSubscriptionRevisions = false;\n this.resetSubscriptionRelease();\n\n let url = this._url;\n if (this._microservice?.length > 0) {\n const param = `${Globals.microserviceWSQueryArgument}=${encodeURIComponent(this._microservice)}`;\n url += (url.includes('?') ? '&' : '?') + param;\n }\n\n const socket = new WebSocket(url);\n this._socket = socket;\n\n socket.onopen = () => {\n if (this._socket !== socket || this._disconnected) return;\n console.log('Hub connection established', url);\n this.cancelPendingReconnect();\n this._policy.reset();\n this._keepAlive.start();\n\n // A capable server announces itself with a Connected message straight after accepting the socket. Sending\n // the subscriptions before that would mean sending them twice (once without a revision, once with), and the\n // server would run every query twice and cancel the first run. Wait for it, and only fall back to the\n // revision-less form for a server that never says anything.\n this._releaseTimer = setTimeout(() => {\n this._releaseTimer = undefined;\n if (this._socket === socket && !this._disconnected) {\n this.releaseSubscriptions();\n }\n }, WebSocketHubConnection.ConnectedGraceMs);\n };\n\n socket.onclose = () => {\n if (this._socket !== socket || this._disconnected) return;\n console.log('Hub connection closed', url);\n this._socket = undefined;\n this._supportsSubscriptionRevisions = false;\n this.resetSubscriptionRelease();\n this._keepAlive.stop();\n if (this._subscriptions.size === 0) return;\n this._reconnectPending = true;\n const scheduled = this._policy.schedule(() => {\n this._reconnectPending = false;\n if (!this._disconnected && this._subscriptions.size > 0) {\n this.ensureConnected();\n }\n }, this._url);\n if (!scheduled) {\n this._reconnectPending = false;\n }\n };\n\n socket.onerror = (error) => {\n if (this._socket !== socket || this._disconnected) return;\n console.error('Hub connection error', url, error);\n this._keepAlive.stop();\n // onclose will fire after onerror, triggering reconnect\n };\n\n socket.onmessage = (ev) => {\n if (this._socket !== socket || this._disconnected) return;\n this.handleMessage(ev.data as string);\n };\n }\n\n private cancelPendingReconnect(): void {\n if (!this._reconnectPending) return;\n\n this._policy.cancel();\n this._reconnectPending = false;\n }\n\n private detachAndCloseSocket(socket: WebSocket): void {\n socket.onopen = null;\n socket.onclose = null;\n socket.onerror = null;\n socket.onmessage = null;\n socket.close();\n }\n\n private resetSubscriptionRelease(): void {\n this._subscriptionsReleased = false;\n if (this._releaseTimer !== undefined) {\n clearTimeout(this._releaseTimer);\n this._releaseTimer = undefined;\n }\n }\n\n private releaseSubscriptions(): void {\n if (this._subscriptionsReleased) return;\n this._subscriptionsReleased = true;\n if (this._releaseTimer !== undefined) {\n clearTimeout(this._releaseTimer);\n this._releaseTimer = undefined;\n }\n this.sendAllSubscriptions();\n }\n\n private sendAllSubscriptions(): void {\n for (const [queryId, sub] of this._subscriptions) {\n this.sendSubscribeMessage(queryId, sub);\n }\n }\n\n private sendSubscribeMessage(\n queryId: string,\n subscription: ActiveSubscription,\n ): void {\n const message: HubMessage = {\n type: HubMessageType.Subscribe,\n queryId,\n payload: subscription.request,\n };\n if (this._supportsSubscriptionRevisions) {\n message.revision = subscription.revision;\n }\n this.sendMessage(message);\n }\n\n private sendMessage(message: HubMessage): void {\n if (this._socket?.readyState === WebSocket.OPEN) {\n this._socket.send(JSON.stringify(message));\n }\n }\n\n private handleMessage(rawData: string): void {\n try {\n const message = JSON.parse(rawData) as HubMessage;\n\n // Every received message is activity — skip keep-alive ping if data is flowing.\n this._keepAlive.recordActivity();\n\n switch (message.type) {\n case HubMessageType.Connected:\n this.handleConnected(message);\n break;\n case HubMessageType.QueryResult:\n this.handleQueryResult(message);\n break;\n case HubMessageType.Pong:\n this.handlePong(message);\n break;\n case HubMessageType.Unauthorized:\n console.warn('Hub: query unauthorized', message.queryId);\n this.handleUnauthorized(message);\n break;\n case HubMessageType.Error:\n if (this.isMessageForCurrentSubscription(message)) {\n console.error(\n 'Hub: query error',\n message.queryId,\n message.payload,\n );\n this.handleError(message);\n }\n break;\n }\n } catch (error) {\n console.error('Hub: error parsing message', error);\n }\n }\n\n private handleConnected(message: HubMessage): void {\n if (this._subscriptionsReleased) return;\n\n // Capability is known before anything is sent, so every subscription goes out exactly once, in its final form.\n this._supportsSubscriptionRevisions = message.supportsSubscriptionRevisions === true;\n this.releaseSubscriptions();\n }\n\n private handleQueryResult(message: HubMessage): void {\n if (!message.queryId) return;\n\n const sub = this._subscriptions.get(message.queryId);\n if (!sub || !this.isMessageForCurrentSubscription(message)) return;\n\n const result = message.payload as QueryResult<any>;\n if (result?.isValid === false) {\n this._subscriptions.delete(message.queryId);\n }\n sub.callback(result);\n }\n\n private handleError(message: HubMessage): void {\n const result = new QueryResult(\n {\n data: undefined,\n isSuccess: false,\n isReady: true,\n isAuthorized: true,\n isValid: true,\n hasExceptions: true,\n validationResults: [],\n exceptionMessages: [message.payload as string],\n exceptionStackTrace: '',\n paging: { totalItems: 0, totalPages: 0, page: 0, size: 0 },\n },\n Object,\n false,\n );\n this.handleQueryResult({ ...message, payload: result });\n }\n\n private handleUnauthorized(message: HubMessage): void {\n if (!message.queryId) return;\n\n const sub = this._subscriptions.get(message.queryId);\n if (!sub || !this.isMessageForCurrentSubscription(message)) return;\n\n this._subscriptions.delete(message.queryId);\n sub.callback(QueryResult.unauthorized());\n }\n\n private isMessageForCurrentSubscription(message: HubMessage): boolean {\n if (!message.queryId) return false;\n\n if (!this._supportsSubscriptionRevisions) {\n return (\n message.revision === undefined &&\n this._subscriptions.has(message.queryId)\n );\n }\n\n return (\n this.isValidRevision(message.revision) &&\n this._subscriptions.get(message.queryId)?.revision === message.revision\n );\n }\n\n private isValidRevision(revision: number | undefined): revision is number {\n return revision !== undefined && Number.isSafeInteger(revision) && revision > 0;\n }\n\n private getNextRevision(): number {\n if (this._nextRevision >= Number.MAX_SAFE_INTEGER) {\n throw new RangeError(\n 'WebSocket hub subscription revision exhausted the safe integer range',\n );\n }\n\n return ++this._nextRevision;\n }\n\n private handlePong(message: HubMessage): void {\n if (message.timestamp && this._lastPingSentTime) {\n const latency = Date.now() - message.timestamp;\n this._lastPongLatency = latency;\n this._latencySamples.push(latency);\n\n if (this._latencySamples.length > 100) {\n this._latencySamples.shift();\n }\n }\n }\n}\n"],"names":["HubMessageType","WebSocketHubConnection","ConnectedGraceMs","_socket","_disconnected","_subscriptions","Map","_keepAlive","_lastPingSentTime","_lastPongLatency","_latencySamples","_nextRevision","_supportsSubscriptionRevisions","_reconnectPending","_subscriptionsReleased","_releaseTimer","_url","pingIntervalMs","_policy","ReconnectPolicy","_microservice","HubConnectionKeepAlive","readyState","WebSocket","OPEN","Date","now","sendMessage","type","timestamp","queryCount","size","isConnected","lastPingLatency","averageLatency","length","reduce","a","b","subscribe","queryId","request","callback","subscription","revision","getNextRevision","set","ensureConnected","sendSubscribeMessage","unsubscribe","get","delete","message","close","dispose","clear","CONNECTING","cancelPendingReconnect","openSocket","stop","cancel","resetSubscriptionRelease","socket","undefined","detachAndCloseSocket","currentSocket","url","param","Globals","microserviceWSQueryArgument","encodeURIComponent","includes","onopen","console","log","reset","start","setTimeout","releaseSubscriptions","onclose","scheduled","schedule","onerror","error","onmessage","ev","handleMessage","data","clearTimeout","sendAllSubscriptions","sub","payload","send","JSON","stringify","rawData","parse","recordActivity","handleConnected","handleQueryResult","handlePong","warn","handleUnauthorized","isMessageForCurrentSubscription","handleError","supportsSubscriptionRevisions","result","isValid","QueryResult","isSuccess","isReady","isAuthorized","hasExceptions","validationResults","exceptionMessages","exceptionStackTrace","paging","totalItems","totalPages","page","Object","unauthorized","has","isValidRevision","Number","isSafeInteger","MAX_SAFE_INTEGER","RangeError","latency","push","shift"],"mappings":"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"}
|
|
@@ -32,6 +32,7 @@ export declare class WebSocketHubConnection {
|
|
|
32
32
|
private readonly _url;
|
|
33
33
|
private readonly _microservice;
|
|
34
34
|
private readonly _policy;
|
|
35
|
+
static readonly ConnectedGraceMs = 1000;
|
|
35
36
|
private _socket?;
|
|
36
37
|
private _disconnected;
|
|
37
38
|
private _subscriptions;
|
|
@@ -42,6 +43,8 @@ export declare class WebSocketHubConnection {
|
|
|
42
43
|
private _nextRevision;
|
|
43
44
|
private _supportsSubscriptionRevisions;
|
|
44
45
|
private _reconnectPending;
|
|
46
|
+
private _subscriptionsReleased;
|
|
47
|
+
private _releaseTimer?;
|
|
45
48
|
constructor(_url: string, _microservice: string, pingIntervalMs?: number, _policy?: IReconnectPolicy);
|
|
46
49
|
get queryCount(): number;
|
|
47
50
|
get isConnected(): boolean;
|
|
@@ -55,6 +58,8 @@ export declare class WebSocketHubConnection {
|
|
|
55
58
|
private openSocket;
|
|
56
59
|
private cancelPendingReconnect;
|
|
57
60
|
private detachAndCloseSocket;
|
|
61
|
+
private resetSubscriptionRelease;
|
|
62
|
+
private releaseSubscriptions;
|
|
58
63
|
private sendAllSubscriptions;
|
|
59
64
|
private sendSubscribeMessage;
|
|
60
65
|
private sendMessage;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WebSocketHubConnection.d.ts","sourceRoot":"","sources":["../../../queries/WebSocketHubConnection.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAEnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAU9D,oBAAY,cAAc;IACtB,SAAS,cAAc;IACvB,WAAW,gBAAgB;IAC3B,WAAW,gBAAgB;IAC3B,YAAY,iBAAiB;IAC7B,KAAK,UAAU;IACf,IAAI,SAAS;IACb,IAAI,SAAS;IACb,SAAS,cAAc;CAC1B;AAKD,MAAM,WAAW,UAAU;IACvB,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IAGjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAMlB,6BAA6B,CAAC,EAAE,OAAO,CAAC;IACxC,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IAMnB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAChC;AAKD,MAAM,WAAW,mBAAmB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;CACzB;AAkBD,qBAAa,sBAAsB;
|
|
1
|
+
{"version":3,"file":"WebSocketHubConnection.d.ts","sourceRoot":"","sources":["../../../queries/WebSocketHubConnection.ts"],"names":[],"mappings":"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"}
|
|
@@ -32,6 +32,7 @@ import { QueryResult } from './QueryResult.js';
|
|
|
32
32
|
_url;
|
|
33
33
|
_microservice;
|
|
34
34
|
_policy;
|
|
35
|
+
/** How long to wait for the server's Connected message before subscribing the way older servers expect. */ static ConnectedGraceMs = 1000;
|
|
35
36
|
_socket;
|
|
36
37
|
_disconnected = false;
|
|
37
38
|
_subscriptions = new Map();
|
|
@@ -42,6 +43,8 @@ import { QueryResult } from './QueryResult.js';
|
|
|
42
43
|
_nextRevision = 0;
|
|
43
44
|
_supportsSubscriptionRevisions = false;
|
|
44
45
|
_reconnectPending = false;
|
|
46
|
+
_subscriptionsReleased = false;
|
|
47
|
+
_releaseTimer;
|
|
45
48
|
/**
|
|
46
49
|
* Initializes a new instance of {@link WebSocketHubConnection}.
|
|
47
50
|
* @param {string} _url The WebSocket URL of the hub endpoint (e.g. {@code ws://localhost:5000/.cratis/queries/ws}).
|
|
@@ -97,10 +100,10 @@ import { QueryResult } from './QueryResult.js';
|
|
|
97
100
|
};
|
|
98
101
|
this._subscriptions.set(queryId, subscription);
|
|
99
102
|
this.ensureConnected();
|
|
100
|
-
if (this._socket?.readyState === WebSocket.OPEN) {
|
|
103
|
+
if (this._socket?.readyState === WebSocket.OPEN && this._subscriptionsReleased) {
|
|
101
104
|
this.sendSubscribeMessage(queryId, subscription);
|
|
102
105
|
}
|
|
103
|
-
//
|
|
106
|
+
// Until the server has said what it supports, subscriptions are held back and sent once, by releaseSubscriptions.
|
|
104
107
|
}
|
|
105
108
|
/**
|
|
106
109
|
* Unsubscribe from a query on this hub connection.
|
|
@@ -108,7 +111,7 @@ import { QueryResult } from './QueryResult.js';
|
|
|
108
111
|
*/ unsubscribe(queryId) {
|
|
109
112
|
const subscription = this._subscriptions.get(queryId);
|
|
110
113
|
this._subscriptions.delete(queryId);
|
|
111
|
-
if (this._socket?.readyState === WebSocket.OPEN && subscription) {
|
|
114
|
+
if (this._socket?.readyState === WebSocket.OPEN && this._subscriptionsReleased && subscription) {
|
|
112
115
|
const message = {
|
|
113
116
|
type: "Unsubscribe",
|
|
114
117
|
queryId
|
|
@@ -148,6 +151,7 @@ import { QueryResult } from './QueryResult.js';
|
|
|
148
151
|
this._keepAlive.stop();
|
|
149
152
|
this._policy.cancel();
|
|
150
153
|
this._reconnectPending = false;
|
|
154
|
+
this.resetSubscriptionRelease();
|
|
151
155
|
const socket = this._socket;
|
|
152
156
|
this._socket = undefined;
|
|
153
157
|
this._supportsSubscriptionRevisions = false;
|
|
@@ -165,6 +169,7 @@ import { QueryResult } from './QueryResult.js';
|
|
|
165
169
|
this.detachAndCloseSocket(currentSocket);
|
|
166
170
|
}
|
|
167
171
|
this._supportsSubscriptionRevisions = false;
|
|
172
|
+
this.resetSubscriptionRelease();
|
|
168
173
|
let url = this._url;
|
|
169
174
|
if (this._microservice?.length > 0) {
|
|
170
175
|
const param = `${Globals.microserviceWSQueryArgument}=${encodeURIComponent(this._microservice)}`;
|
|
@@ -178,13 +183,23 @@ import { QueryResult } from './QueryResult.js';
|
|
|
178
183
|
this.cancelPendingReconnect();
|
|
179
184
|
this._policy.reset();
|
|
180
185
|
this._keepAlive.start();
|
|
181
|
-
|
|
186
|
+
// A capable server announces itself with a Connected message straight after accepting the socket. Sending
|
|
187
|
+
// the subscriptions before that would mean sending them twice (once without a revision, once with), and the
|
|
188
|
+
// server would run every query twice and cancel the first run. Wait for it, and only fall back to the
|
|
189
|
+
// revision-less form for a server that never says anything.
|
|
190
|
+
this._releaseTimer = setTimeout(()=>{
|
|
191
|
+
this._releaseTimer = undefined;
|
|
192
|
+
if (this._socket === socket && !this._disconnected) {
|
|
193
|
+
this.releaseSubscriptions();
|
|
194
|
+
}
|
|
195
|
+
}, WebSocketHubConnection.ConnectedGraceMs);
|
|
182
196
|
};
|
|
183
197
|
socket.onclose = ()=>{
|
|
184
198
|
if (this._socket !== socket || this._disconnected) return;
|
|
185
199
|
console.log('Hub connection closed', url);
|
|
186
200
|
this._socket = undefined;
|
|
187
201
|
this._supportsSubscriptionRevisions = false;
|
|
202
|
+
this.resetSubscriptionRelease();
|
|
188
203
|
this._keepAlive.stop();
|
|
189
204
|
if (this._subscriptions.size === 0) return;
|
|
190
205
|
this._reconnectPending = true;
|
|
@@ -221,6 +236,22 @@ import { QueryResult } from './QueryResult.js';
|
|
|
221
236
|
socket.onmessage = null;
|
|
222
237
|
socket.close();
|
|
223
238
|
}
|
|
239
|
+
resetSubscriptionRelease() {
|
|
240
|
+
this._subscriptionsReleased = false;
|
|
241
|
+
if (this._releaseTimer !== undefined) {
|
|
242
|
+
clearTimeout(this._releaseTimer);
|
|
243
|
+
this._releaseTimer = undefined;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
releaseSubscriptions() {
|
|
247
|
+
if (this._subscriptionsReleased) return;
|
|
248
|
+
this._subscriptionsReleased = true;
|
|
249
|
+
if (this._releaseTimer !== undefined) {
|
|
250
|
+
clearTimeout(this._releaseTimer);
|
|
251
|
+
this._releaseTimer = undefined;
|
|
252
|
+
}
|
|
253
|
+
this.sendAllSubscriptions();
|
|
254
|
+
}
|
|
224
255
|
sendAllSubscriptions() {
|
|
225
256
|
for (const [queryId, sub] of this._subscriptions){
|
|
226
257
|
this.sendSubscribeMessage(queryId, sub);
|
|
@@ -273,13 +304,10 @@ import { QueryResult } from './QueryResult.js';
|
|
|
273
304
|
}
|
|
274
305
|
}
|
|
275
306
|
handleConnected(message) {
|
|
276
|
-
if (
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
this.
|
|
280
|
-
// The legacy subscriptions sent on socket open keep this client compatible with older servers.
|
|
281
|
-
// Ordered WebSocket delivery means these revision-aware replacements are processed afterwards.
|
|
282
|
-
this.sendAllSubscriptions();
|
|
307
|
+
if (this._subscriptionsReleased) return;
|
|
308
|
+
// Capability is known before anything is sent, so every subscription goes out exactly once, in its final form.
|
|
309
|
+
this._supportsSubscriptionRevisions = message.supportsSubscriptionRevisions === true;
|
|
310
|
+
this.releaseSubscriptions();
|
|
283
311
|
}
|
|
284
312
|
handleQueryResult(message) {
|
|
285
313
|
if (!message.queryId) return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WebSocketHubConnection.js","sources":["../../../queries/WebSocketHubConnection.ts"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { Globals } from '../Globals.js';\nimport type { DataReceived } from './ObservableQueryConnection.js';\nimport { HubConnectionKeepAlive } from './HubConnectionKeepAlive.js';\nimport type { IReconnectPolicy } from './IReconnectPolicy.js';\nimport { ReconnectPolicy } from './ReconnectPolicy.js';\nimport { QueryResult } from './QueryResult.js';\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\n/**\n * Message types matching the backend {@link ObservableQueryHubMessageType} enum.\n * Serialized as strings by the JsonStringEnumConverter on the server.\n */\nexport enum HubMessageType {\n Subscribe = 'Subscribe',\n Unsubscribe = 'Unsubscribe',\n QueryResult = 'QueryResult',\n Unauthorized = 'Unauthorized',\n Error = 'Error',\n Ping = 'Ping',\n Pong = 'Pong',\n Connected = 'Connected',\n}\n\n/**\n * Wire format for messages exchanged over the observable query WebSocket and SSE hub transports.\n */\nexport interface HubMessage {\n type: HubMessageType;\n queryId?: string;\n\n /** Optional positive safe-integer revision used to reject stale replacement messages. */\n revision?: number;\n\n /**\n * Whether the server supports revision-aware subscription operations and result frames.\n * Only present on {@link HubMessageType.Connected} messages from capable servers.\n */\n supportsSubscriptionRevisions?: boolean;\n payload?: any;\n timestamp?: number;\n\n /**\n * The server's keep-alive interval in milliseconds, or 0 when keep-alive is disabled.\n * Only present on {@link HubMessageType.Connected} messages.\n */\n keepAliveIntervalMs?: number;\n}\n\n/**\n * Matches the backend {@link ObservableQuerySubscriptionRequest} record.\n */\nexport interface SubscriptionRequest {\n queryName: string;\n arguments?: Record<string, string | null>;\n page?: number;\n pageSize?: number;\n sortBy?: string;\n sortDirection?: string;\n transferMode?: string;\n}\n\ninterface ActiveSubscription {\n request: SubscriptionRequest;\n callback: DataReceived<any>;\n revision: number;\n}\n\n/**\n * Represents a single multiplexed WebSocket connection to the observable query hub\n * at {@code /.cratis/queries/ws}.\n *\n * Multiple query subscriptions are carried over the same physical WebSocket. Each subscription\n * is identified by a client-generated {@code queryId}; the server tags every result message with\n * the same id so responses can be routed to the correct callback. The client initially subscribes\n * without revisions for compatibility with older servers, then replaces active subscriptions with\n * revision-aware operations when the server advertises support in a Connected message.\n */\nexport class WebSocketHubConnection {\n private _socket?: WebSocket;\n private _disconnected = false;\n private _subscriptions: Map<string, ActiveSubscription> = new Map();\n private readonly _keepAlive: HubConnectionKeepAlive;\n private _lastPingSentTime?: number;\n private _lastPongLatency: number = 0;\n private _latencySamples: number[] = [];\n private _nextRevision = 0;\n private _supportsSubscriptionRevisions = false;\n private _reconnectPending = false;\n\n /**\n * Initializes a new instance of {@link WebSocketHubConnection}.\n * @param {string} _url The WebSocket URL of the hub endpoint (e.g. {@code ws://localhost:5000/.cratis/queries/ws}).\n * @param {string} _microservice The microservice name to pass as a query argument.\n * @param {number} pingIntervalMs How often to send keep-alive pings when the connection is idle (default: 10 000 ms).\n * @param {IReconnectPolicy} _policy The reconnect policy to use (default: {@link ReconnectPolicy}).\n */\n constructor(\n private readonly _url: string,\n private readonly _microservice: string,\n pingIntervalMs: number = 10000,\n private readonly _policy: IReconnectPolicy = new ReconnectPolicy(),\n ) {\n this._keepAlive = new HubConnectionKeepAlive(pingIntervalMs, () => {\n if (this._socket?.readyState === WebSocket.OPEN) {\n this._lastPingSentTime = Date.now();\n this.sendMessage({\n type: HubMessageType.Ping,\n timestamp: this._lastPingSentTime,\n });\n }\n });\n }\n\n /**\n * Gets the number of active query subscriptions on this connection.\n */\n get queryCount(): number {\n return this._subscriptions.size;\n }\n\n /**\n * Gets whether the WebSocket connection is currently open.\n */\n get isConnected(): boolean {\n return this._socket?.readyState === WebSocket.OPEN;\n }\n\n /**\n * Gets the latency of the last ping/pong sequence in milliseconds.\n */\n get lastPingLatency(): number {\n return this._lastPongLatency;\n }\n\n /**\n * Gets the rolling average latency in milliseconds.\n */\n get averageLatency(): number {\n if (this._latencySamples.length === 0) return 0;\n return (\n this._latencySamples.reduce((a, b) => a + b, 0) / this._latencySamples.length\n );\n }\n\n /**\n * Subscribe to a query on this hub connection.\n * If the WebSocket is not yet open, the subscribe message will be sent once the connection is established.\n * @param {string} queryId Client-generated unique identifier for this subscription.\n * @param {SubscriptionRequest} request The subscription request payload.\n * @param {DataReceived<any>} callback Callback invoked whenever the server pushes a result for this query.\n */\n subscribe(\n queryId: string,\n request: SubscriptionRequest,\n callback: DataReceived<any>,\n ): void {\n const subscription = { request, callback, revision: this.getNextRevision() };\n this._subscriptions.set(queryId, subscription);\n this.ensureConnected();\n\n if (this._socket?.readyState === WebSocket.OPEN) {\n this.sendSubscribeMessage(queryId, subscription);\n }\n // If not yet open, sendAllSubscriptions will fire in onopen.\n }\n\n /**\n * Unsubscribe from a query on this hub connection.\n * @param {string} queryId The identifier of the subscription to cancel.\n */\n unsubscribe(queryId: string): void {\n const subscription = this._subscriptions.get(queryId);\n this._subscriptions.delete(queryId);\n\n if (this._socket?.readyState === WebSocket.OPEN && subscription) {\n const message: HubMessage = {\n type: HubMessageType.Unsubscribe,\n queryId,\n };\n if (this._supportsSubscriptionRevisions) {\n message.revision = subscription.revision;\n }\n this.sendMessage(message);\n }\n\n // If no subscriptions remain, close the connection to free resources.\n if (this._subscriptions.size === 0) {\n this.close();\n }\n }\n\n /**\n * Permanently close this hub connection and clean up all subscriptions.\n */\n dispose(): void {\n this._subscriptions.clear();\n this.close();\n }\n\n private ensureConnected(): void {\n if (this._disconnected) {\n // Reset disconnected flag when a new subscription comes in\n this._disconnected = false;\n }\n\n if (\n this._socket &&\n (this._socket.readyState === WebSocket.OPEN ||\n this._socket.readyState === WebSocket.CONNECTING)\n ) {\n this.cancelPendingReconnect();\n return;\n }\n\n // A subscription can arrive while an earlier close is waiting in reconnect backoff. The immediate open owns\n // the connection now, so the delayed callback must not be allowed to create a competing socket later.\n this.cancelPendingReconnect();\n this.openSocket();\n }\n\n private close(): void {\n this._disconnected = true;\n this._keepAlive.stop();\n this._policy.cancel();\n this._reconnectPending = false;\n const socket = this._socket;\n this._socket = undefined;\n this._supportsSubscriptionRevisions = false;\n if (socket) {\n this.detachAndCloseSocket(socket);\n }\n }\n\n private openSocket(): void {\n const currentSocket = this._socket;\n if (\n currentSocket &&\n (currentSocket.readyState === WebSocket.OPEN ||\n currentSocket.readyState === WebSocket.CONNECTING)\n ) {\n return;\n }\n\n if (currentSocket) {\n this._socket = undefined;\n this.detachAndCloseSocket(currentSocket);\n }\n\n this._supportsSubscriptionRevisions = false;\n\n let url = this._url;\n if (this._microservice?.length > 0) {\n const param = `${Globals.microserviceWSQueryArgument}=${encodeURIComponent(this._microservice)}`;\n url += (url.includes('?') ? '&' : '?') + param;\n }\n\n const socket = new WebSocket(url);\n this._socket = socket;\n\n socket.onopen = () => {\n if (this._socket !== socket || this._disconnected) return;\n console.log('Hub connection established', url);\n this.cancelPendingReconnect();\n this._policy.reset();\n this._keepAlive.start();\n this.sendAllSubscriptions();\n };\n\n socket.onclose = () => {\n if (this._socket !== socket || this._disconnected) return;\n console.log('Hub connection closed', url);\n this._socket = undefined;\n this._supportsSubscriptionRevisions = false;\n this._keepAlive.stop();\n if (this._subscriptions.size === 0) return;\n this._reconnectPending = true;\n const scheduled = this._policy.schedule(() => {\n this._reconnectPending = false;\n if (!this._disconnected && this._subscriptions.size > 0) {\n this.ensureConnected();\n }\n }, this._url);\n if (!scheduled) {\n this._reconnectPending = false;\n }\n };\n\n socket.onerror = (error) => {\n if (this._socket !== socket || this._disconnected) return;\n console.error('Hub connection error', url, error);\n this._keepAlive.stop();\n // onclose will fire after onerror, triggering reconnect\n };\n\n socket.onmessage = (ev) => {\n if (this._socket !== socket || this._disconnected) return;\n this.handleMessage(ev.data as string);\n };\n }\n\n private cancelPendingReconnect(): void {\n if (!this._reconnectPending) return;\n\n this._policy.cancel();\n this._reconnectPending = false;\n }\n\n private detachAndCloseSocket(socket: WebSocket): void {\n socket.onopen = null;\n socket.onclose = null;\n socket.onerror = null;\n socket.onmessage = null;\n socket.close();\n }\n\n private sendAllSubscriptions(): void {\n for (const [queryId, sub] of this._subscriptions) {\n this.sendSubscribeMessage(queryId, sub);\n }\n }\n\n private sendSubscribeMessage(\n queryId: string,\n subscription: ActiveSubscription,\n ): void {\n const message: HubMessage = {\n type: HubMessageType.Subscribe,\n queryId,\n payload: subscription.request,\n };\n if (this._supportsSubscriptionRevisions) {\n message.revision = subscription.revision;\n }\n this.sendMessage(message);\n }\n\n private sendMessage(message: HubMessage): void {\n if (this._socket?.readyState === WebSocket.OPEN) {\n this._socket.send(JSON.stringify(message));\n }\n }\n\n private handleMessage(rawData: string): void {\n try {\n const message = JSON.parse(rawData) as HubMessage;\n\n // Every received message is activity — skip keep-alive ping if data is flowing.\n this._keepAlive.recordActivity();\n\n switch (message.type) {\n case HubMessageType.Connected:\n this.handleConnected(message);\n break;\n case HubMessageType.QueryResult:\n this.handleQueryResult(message);\n break;\n case HubMessageType.Pong:\n this.handlePong(message);\n break;\n case HubMessageType.Unauthorized:\n console.warn('Hub: query unauthorized', message.queryId);\n this.handleUnauthorized(message);\n break;\n case HubMessageType.Error:\n if (this.isMessageForCurrentSubscription(message)) {\n console.error(\n 'Hub: query error',\n message.queryId,\n message.payload,\n );\n this.handleError(message);\n }\n break;\n }\n } catch (error) {\n console.error('Hub: error parsing message', error);\n }\n }\n\n private handleConnected(message: HubMessage): void {\n if (\n message.supportsSubscriptionRevisions !== true ||\n this._supportsSubscriptionRevisions\n ) {\n return;\n }\n\n this._supportsSubscriptionRevisions = true;\n\n // The legacy subscriptions sent on socket open keep this client compatible with older servers.\n // Ordered WebSocket delivery means these revision-aware replacements are processed afterwards.\n this.sendAllSubscriptions();\n }\n\n private handleQueryResult(message: HubMessage): void {\n if (!message.queryId) return;\n\n const sub = this._subscriptions.get(message.queryId);\n if (!sub || !this.isMessageForCurrentSubscription(message)) return;\n\n const result = message.payload as QueryResult<any>;\n if (result?.isValid === false) {\n this._subscriptions.delete(message.queryId);\n }\n sub.callback(result);\n }\n\n private handleError(message: HubMessage): void {\n const result = new QueryResult(\n {\n data: undefined,\n isSuccess: false,\n isReady: true,\n isAuthorized: true,\n isValid: true,\n hasExceptions: true,\n validationResults: [],\n exceptionMessages: [message.payload as string],\n exceptionStackTrace: '',\n paging: { totalItems: 0, totalPages: 0, page: 0, size: 0 },\n },\n Object,\n false,\n );\n this.handleQueryResult({ ...message, payload: result });\n }\n\n private handleUnauthorized(message: HubMessage): void {\n if (!message.queryId) return;\n\n const sub = this._subscriptions.get(message.queryId);\n if (!sub || !this.isMessageForCurrentSubscription(message)) return;\n\n this._subscriptions.delete(message.queryId);\n sub.callback(QueryResult.unauthorized());\n }\n\n private isMessageForCurrentSubscription(message: HubMessage): boolean {\n if (!message.queryId) return false;\n\n if (!this._supportsSubscriptionRevisions) {\n return (\n message.revision === undefined &&\n this._subscriptions.has(message.queryId)\n );\n }\n\n return (\n this.isValidRevision(message.revision) &&\n this._subscriptions.get(message.queryId)?.revision === message.revision\n );\n }\n\n private isValidRevision(revision: number | undefined): revision is number {\n return revision !== undefined && Number.isSafeInteger(revision) && revision > 0;\n }\n\n private getNextRevision(): number {\n if (this._nextRevision >= Number.MAX_SAFE_INTEGER) {\n throw new RangeError(\n 'WebSocket hub subscription revision exhausted the safe integer range',\n );\n }\n\n return ++this._nextRevision;\n }\n\n private handlePong(message: HubMessage): void {\n if (message.timestamp && this._lastPingSentTime) {\n const latency = Date.now() - message.timestamp;\n this._lastPongLatency = latency;\n this._latencySamples.push(latency);\n\n if (this._latencySamples.length > 100) {\n this._latencySamples.shift();\n }\n }\n }\n}\n"],"names":["HubMessageType","WebSocketHubConnection","_socket","_disconnected","_subscriptions","Map","_keepAlive","_lastPingSentTime","_lastPongLatency","_latencySamples","_nextRevision","_supportsSubscriptionRevisions","_reconnectPending","_url","pingIntervalMs","_policy","ReconnectPolicy","_microservice","HubConnectionKeepAlive","readyState","WebSocket","OPEN","Date","now","sendMessage","type","timestamp","queryCount","size","isConnected","lastPingLatency","averageLatency","length","reduce","a","b","subscribe","queryId","request","callback","subscription","revision","getNextRevision","set","ensureConnected","sendSubscribeMessage","unsubscribe","get","delete","message","close","dispose","clear","CONNECTING","cancelPendingReconnect","openSocket","stop","cancel","socket","undefined","detachAndCloseSocket","currentSocket","url","param","Globals","microserviceWSQueryArgument","encodeURIComponent","includes","onopen","console","log","reset","start","sendAllSubscriptions","onclose","scheduled","schedule","onerror","error","onmessage","ev","handleMessage","data","sub","payload","send","JSON","stringify","rawData","parse","recordActivity","handleConnected","handleQueryResult","handlePong","warn","handleUnauthorized","isMessageForCurrentSubscription","handleError","supportsSubscriptionRevisions","result","isValid","QueryResult","isSuccess","isReady","isAuthorized","hasExceptions","validationResults","exceptionMessages","exceptionStackTrace","paging","totalItems","totalPages","page","Object","unauthorized","has","isValidRevision","Number","isSafeInteger","MAX_SAFE_INTEGER","RangeError","latency","push","shift"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"WebSocketHubConnection.js","sources":["../../../queries/WebSocketHubConnection.ts"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { Globals } from '../Globals.js';\nimport type { DataReceived } from './ObservableQueryConnection.js';\nimport { HubConnectionKeepAlive } from './HubConnectionKeepAlive.js';\nimport type { IReconnectPolicy } from './IReconnectPolicy.js';\nimport { ReconnectPolicy } from './ReconnectPolicy.js';\nimport { QueryResult } from './QueryResult.js';\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\n/**\n * Message types matching the backend {@link ObservableQueryHubMessageType} enum.\n * Serialized as strings by the JsonStringEnumConverter on the server.\n */\nexport enum HubMessageType {\n Subscribe = 'Subscribe',\n Unsubscribe = 'Unsubscribe',\n QueryResult = 'QueryResult',\n Unauthorized = 'Unauthorized',\n Error = 'Error',\n Ping = 'Ping',\n Pong = 'Pong',\n Connected = 'Connected',\n}\n\n/**\n * Wire format for messages exchanged over the observable query WebSocket and SSE hub transports.\n */\nexport interface HubMessage {\n type: HubMessageType;\n queryId?: string;\n\n /** Optional positive safe-integer revision used to reject stale replacement messages. */\n revision?: number;\n\n /**\n * Whether the server supports revision-aware subscription operations and result frames.\n * Only present on {@link HubMessageType.Connected} messages from capable servers.\n */\n supportsSubscriptionRevisions?: boolean;\n payload?: any;\n timestamp?: number;\n\n /**\n * The server's keep-alive interval in milliseconds, or 0 when keep-alive is disabled.\n * Only present on {@link HubMessageType.Connected} messages.\n */\n keepAliveIntervalMs?: number;\n}\n\n/**\n * Matches the backend {@link ObservableQuerySubscriptionRequest} record.\n */\nexport interface SubscriptionRequest {\n queryName: string;\n arguments?: Record<string, string | null>;\n page?: number;\n pageSize?: number;\n sortBy?: string;\n sortDirection?: string;\n transferMode?: string;\n}\n\ninterface ActiveSubscription {\n request: SubscriptionRequest;\n callback: DataReceived<any>;\n revision: number;\n}\n\n/**\n * Represents a single multiplexed WebSocket connection to the observable query hub\n * at {@code /.cratis/queries/ws}.\n *\n * Multiple query subscriptions are carried over the same physical WebSocket. Each subscription\n * is identified by a client-generated {@code queryId}; the server tags every result message with\n * the same id so responses can be routed to the correct callback. The client initially subscribes\n * without revisions for compatibility with older servers, then replaces active subscriptions with\n * revision-aware operations when the server advertises support in a Connected message.\n */\nexport class WebSocketHubConnection {\n /** How long to wait for the server's Connected message before subscribing the way older servers expect. */\n static readonly ConnectedGraceMs = 1000;\n\n private _socket?: WebSocket;\n private _disconnected = false;\n private _subscriptions: Map<string, ActiveSubscription> = new Map();\n private readonly _keepAlive: HubConnectionKeepAlive;\n private _lastPingSentTime?: number;\n private _lastPongLatency: number = 0;\n private _latencySamples: number[] = [];\n private _nextRevision = 0;\n private _supportsSubscriptionRevisions = false;\n private _reconnectPending = false;\n private _subscriptionsReleased = false;\n private _releaseTimer?: ReturnType<typeof setTimeout>;\n\n /**\n * Initializes a new instance of {@link WebSocketHubConnection}.\n * @param {string} _url The WebSocket URL of the hub endpoint (e.g. {@code ws://localhost:5000/.cratis/queries/ws}).\n * @param {string} _microservice The microservice name to pass as a query argument.\n * @param {number} pingIntervalMs How often to send keep-alive pings when the connection is idle (default: 10 000 ms).\n * @param {IReconnectPolicy} _policy The reconnect policy to use (default: {@link ReconnectPolicy}).\n */\n constructor(\n private readonly _url: string,\n private readonly _microservice: string,\n pingIntervalMs: number = 10000,\n private readonly _policy: IReconnectPolicy = new ReconnectPolicy(),\n ) {\n this._keepAlive = new HubConnectionKeepAlive(pingIntervalMs, () => {\n if (this._socket?.readyState === WebSocket.OPEN) {\n this._lastPingSentTime = Date.now();\n this.sendMessage({\n type: HubMessageType.Ping,\n timestamp: this._lastPingSentTime,\n });\n }\n });\n }\n\n /**\n * Gets the number of active query subscriptions on this connection.\n */\n get queryCount(): number {\n return this._subscriptions.size;\n }\n\n /**\n * Gets whether the WebSocket connection is currently open.\n */\n get isConnected(): boolean {\n return this._socket?.readyState === WebSocket.OPEN;\n }\n\n /**\n * Gets the latency of the last ping/pong sequence in milliseconds.\n */\n get lastPingLatency(): number {\n return this._lastPongLatency;\n }\n\n /**\n * Gets the rolling average latency in milliseconds.\n */\n get averageLatency(): number {\n if (this._latencySamples.length === 0) return 0;\n return (\n this._latencySamples.reduce((a, b) => a + b, 0) / this._latencySamples.length\n );\n }\n\n /**\n * Subscribe to a query on this hub connection.\n * If the WebSocket is not yet open, the subscribe message will be sent once the connection is established.\n * @param {string} queryId Client-generated unique identifier for this subscription.\n * @param {SubscriptionRequest} request The subscription request payload.\n * @param {DataReceived<any>} callback Callback invoked whenever the server pushes a result for this query.\n */\n subscribe(\n queryId: string,\n request: SubscriptionRequest,\n callback: DataReceived<any>,\n ): void {\n const subscription = { request, callback, revision: this.getNextRevision() };\n this._subscriptions.set(queryId, subscription);\n this.ensureConnected();\n\n if (this._socket?.readyState === WebSocket.OPEN && this._subscriptionsReleased) {\n this.sendSubscribeMessage(queryId, subscription);\n }\n // Until the server has said what it supports, subscriptions are held back and sent once, by releaseSubscriptions.\n }\n\n /**\n * Unsubscribe from a query on this hub connection.\n * @param {string} queryId The identifier of the subscription to cancel.\n */\n unsubscribe(queryId: string): void {\n const subscription = this._subscriptions.get(queryId);\n this._subscriptions.delete(queryId);\n\n if (this._socket?.readyState === WebSocket.OPEN && this._subscriptionsReleased && subscription) {\n const message: HubMessage = {\n type: HubMessageType.Unsubscribe,\n queryId,\n };\n if (this._supportsSubscriptionRevisions) {\n message.revision = subscription.revision;\n }\n this.sendMessage(message);\n }\n\n // If no subscriptions remain, close the connection to free resources.\n if (this._subscriptions.size === 0) {\n this.close();\n }\n }\n\n /**\n * Permanently close this hub connection and clean up all subscriptions.\n */\n dispose(): void {\n this._subscriptions.clear();\n this.close();\n }\n\n private ensureConnected(): void {\n if (this._disconnected) {\n // Reset disconnected flag when a new subscription comes in\n this._disconnected = false;\n }\n\n if (\n this._socket &&\n (this._socket.readyState === WebSocket.OPEN ||\n this._socket.readyState === WebSocket.CONNECTING)\n ) {\n this.cancelPendingReconnect();\n return;\n }\n\n // A subscription can arrive while an earlier close is waiting in reconnect backoff. The immediate open owns\n // the connection now, so the delayed callback must not be allowed to create a competing socket later.\n this.cancelPendingReconnect();\n this.openSocket();\n }\n\n private close(): void {\n this._disconnected = true;\n this._keepAlive.stop();\n this._policy.cancel();\n this._reconnectPending = false;\n this.resetSubscriptionRelease();\n const socket = this._socket;\n this._socket = undefined;\n this._supportsSubscriptionRevisions = false;\n if (socket) {\n this.detachAndCloseSocket(socket);\n }\n }\n\n private openSocket(): void {\n const currentSocket = this._socket;\n if (\n currentSocket &&\n (currentSocket.readyState === WebSocket.OPEN ||\n currentSocket.readyState === WebSocket.CONNECTING)\n ) {\n return;\n }\n\n if (currentSocket) {\n this._socket = undefined;\n this.detachAndCloseSocket(currentSocket);\n }\n\n this._supportsSubscriptionRevisions = false;\n this.resetSubscriptionRelease();\n\n let url = this._url;\n if (this._microservice?.length > 0) {\n const param = `${Globals.microserviceWSQueryArgument}=${encodeURIComponent(this._microservice)}`;\n url += (url.includes('?') ? '&' : '?') + param;\n }\n\n const socket = new WebSocket(url);\n this._socket = socket;\n\n socket.onopen = () => {\n if (this._socket !== socket || this._disconnected) return;\n console.log('Hub connection established', url);\n this.cancelPendingReconnect();\n this._policy.reset();\n this._keepAlive.start();\n\n // A capable server announces itself with a Connected message straight after accepting the socket. Sending\n // the subscriptions before that would mean sending them twice (once without a revision, once with), and the\n // server would run every query twice and cancel the first run. Wait for it, and only fall back to the\n // revision-less form for a server that never says anything.\n this._releaseTimer = setTimeout(() => {\n this._releaseTimer = undefined;\n if (this._socket === socket && !this._disconnected) {\n this.releaseSubscriptions();\n }\n }, WebSocketHubConnection.ConnectedGraceMs);\n };\n\n socket.onclose = () => {\n if (this._socket !== socket || this._disconnected) return;\n console.log('Hub connection closed', url);\n this._socket = undefined;\n this._supportsSubscriptionRevisions = false;\n this.resetSubscriptionRelease();\n this._keepAlive.stop();\n if (this._subscriptions.size === 0) return;\n this._reconnectPending = true;\n const scheduled = this._policy.schedule(() => {\n this._reconnectPending = false;\n if (!this._disconnected && this._subscriptions.size > 0) {\n this.ensureConnected();\n }\n }, this._url);\n if (!scheduled) {\n this._reconnectPending = false;\n }\n };\n\n socket.onerror = (error) => {\n if (this._socket !== socket || this._disconnected) return;\n console.error('Hub connection error', url, error);\n this._keepAlive.stop();\n // onclose will fire after onerror, triggering reconnect\n };\n\n socket.onmessage = (ev) => {\n if (this._socket !== socket || this._disconnected) return;\n this.handleMessage(ev.data as string);\n };\n }\n\n private cancelPendingReconnect(): void {\n if (!this._reconnectPending) return;\n\n this._policy.cancel();\n this._reconnectPending = false;\n }\n\n private detachAndCloseSocket(socket: WebSocket): void {\n socket.onopen = null;\n socket.onclose = null;\n socket.onerror = null;\n socket.onmessage = null;\n socket.close();\n }\n\n private resetSubscriptionRelease(): void {\n this._subscriptionsReleased = false;\n if (this._releaseTimer !== undefined) {\n clearTimeout(this._releaseTimer);\n this._releaseTimer = undefined;\n }\n }\n\n private releaseSubscriptions(): void {\n if (this._subscriptionsReleased) return;\n this._subscriptionsReleased = true;\n if (this._releaseTimer !== undefined) {\n clearTimeout(this._releaseTimer);\n this._releaseTimer = undefined;\n }\n this.sendAllSubscriptions();\n }\n\n private sendAllSubscriptions(): void {\n for (const [queryId, sub] of this._subscriptions) {\n this.sendSubscribeMessage(queryId, sub);\n }\n }\n\n private sendSubscribeMessage(\n queryId: string,\n subscription: ActiveSubscription,\n ): void {\n const message: HubMessage = {\n type: HubMessageType.Subscribe,\n queryId,\n payload: subscription.request,\n };\n if (this._supportsSubscriptionRevisions) {\n message.revision = subscription.revision;\n }\n this.sendMessage(message);\n }\n\n private sendMessage(message: HubMessage): void {\n if (this._socket?.readyState === WebSocket.OPEN) {\n this._socket.send(JSON.stringify(message));\n }\n }\n\n private handleMessage(rawData: string): void {\n try {\n const message = JSON.parse(rawData) as HubMessage;\n\n // Every received message is activity — skip keep-alive ping if data is flowing.\n this._keepAlive.recordActivity();\n\n switch (message.type) {\n case HubMessageType.Connected:\n this.handleConnected(message);\n break;\n case HubMessageType.QueryResult:\n this.handleQueryResult(message);\n break;\n case HubMessageType.Pong:\n this.handlePong(message);\n break;\n case HubMessageType.Unauthorized:\n console.warn('Hub: query unauthorized', message.queryId);\n this.handleUnauthorized(message);\n break;\n case HubMessageType.Error:\n if (this.isMessageForCurrentSubscription(message)) {\n console.error(\n 'Hub: query error',\n message.queryId,\n message.payload,\n );\n this.handleError(message);\n }\n break;\n }\n } catch (error) {\n console.error('Hub: error parsing message', error);\n }\n }\n\n private handleConnected(message: HubMessage): void {\n if (this._subscriptionsReleased) return;\n\n // Capability is known before anything is sent, so every subscription goes out exactly once, in its final form.\n this._supportsSubscriptionRevisions = message.supportsSubscriptionRevisions === true;\n this.releaseSubscriptions();\n }\n\n private handleQueryResult(message: HubMessage): void {\n if (!message.queryId) return;\n\n const sub = this._subscriptions.get(message.queryId);\n if (!sub || !this.isMessageForCurrentSubscription(message)) return;\n\n const result = message.payload as QueryResult<any>;\n if (result?.isValid === false) {\n this._subscriptions.delete(message.queryId);\n }\n sub.callback(result);\n }\n\n private handleError(message: HubMessage): void {\n const result = new QueryResult(\n {\n data: undefined,\n isSuccess: false,\n isReady: true,\n isAuthorized: true,\n isValid: true,\n hasExceptions: true,\n validationResults: [],\n exceptionMessages: [message.payload as string],\n exceptionStackTrace: '',\n paging: { totalItems: 0, totalPages: 0, page: 0, size: 0 },\n },\n Object,\n false,\n );\n this.handleQueryResult({ ...message, payload: result });\n }\n\n private handleUnauthorized(message: HubMessage): void {\n if (!message.queryId) return;\n\n const sub = this._subscriptions.get(message.queryId);\n if (!sub || !this.isMessageForCurrentSubscription(message)) return;\n\n this._subscriptions.delete(message.queryId);\n sub.callback(QueryResult.unauthorized());\n }\n\n private isMessageForCurrentSubscription(message: HubMessage): boolean {\n if (!message.queryId) return false;\n\n if (!this._supportsSubscriptionRevisions) {\n return (\n message.revision === undefined &&\n this._subscriptions.has(message.queryId)\n );\n }\n\n return (\n this.isValidRevision(message.revision) &&\n this._subscriptions.get(message.queryId)?.revision === message.revision\n );\n }\n\n private isValidRevision(revision: number | undefined): revision is number {\n return revision !== undefined && Number.isSafeInteger(revision) && revision > 0;\n }\n\n private getNextRevision(): number {\n if (this._nextRevision >= Number.MAX_SAFE_INTEGER) {\n throw new RangeError(\n 'WebSocket hub subscription revision exhausted the safe integer range',\n );\n }\n\n return ++this._nextRevision;\n }\n\n private handlePong(message: HubMessage): void {\n if (message.timestamp && this._lastPingSentTime) {\n const latency = Date.now() - message.timestamp;\n this._lastPongLatency = latency;\n this._latencySamples.push(latency);\n\n if (this._latencySamples.length > 100) {\n this._latencySamples.shift();\n }\n }\n }\n}\n"],"names":["HubMessageType","WebSocketHubConnection","ConnectedGraceMs","_socket","_disconnected","_subscriptions","Map","_keepAlive","_lastPingSentTime","_lastPongLatency","_latencySamples","_nextRevision","_supportsSubscriptionRevisions","_reconnectPending","_subscriptionsReleased","_releaseTimer","_url","pingIntervalMs","_policy","ReconnectPolicy","_microservice","HubConnectionKeepAlive","readyState","WebSocket","OPEN","Date","now","sendMessage","type","timestamp","queryCount","size","isConnected","lastPingLatency","averageLatency","length","reduce","a","b","subscribe","queryId","request","callback","subscription","revision","getNextRevision","set","ensureConnected","sendSubscribeMessage","unsubscribe","get","delete","message","close","dispose","clear","CONNECTING","cancelPendingReconnect","openSocket","stop","cancel","resetSubscriptionRelease","socket","undefined","detachAndCloseSocket","currentSocket","url","param","Globals","microserviceWSQueryArgument","encodeURIComponent","includes","onopen","console","log","reset","start","setTimeout","releaseSubscriptions","onclose","scheduled","schedule","onerror","error","onmessage","ev","handleMessage","data","clearTimeout","sendAllSubscriptions","sub","payload","send","JSON","stringify","rawData","parse","recordActivity","handleConnected","handleQueryResult","handlePong","warn","handleUnauthorized","isMessageForCurrentSubscription","handleError","supportsSubscriptionRevisions","result","isValid","QueryResult","isSuccess","isReady","isAuthorized","hasExceptions","validationResults","exceptionMessages","exceptionStackTrace","paging","totalItems","totalPages","page","Object","unauthorized","has","isValidRevision","Number","isSafeInteger","MAX_SAFE_INTEGER","RangeError","latency","push","shift"],"mappings":"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"}
|