@cratis/arc 22.185.1 → 22.186.1

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.
Files changed (47) hide show
  1. package/commands/Command.ts +13 -20
  2. package/dist/cjs/commands/Command.d.ts +1 -0
  3. package/dist/cjs/commands/Command.d.ts.map +1 -1
  4. package/dist/cjs/commands/Command.js +15 -16
  5. package/dist/cjs/commands/Command.js.map +1 -1
  6. package/dist/cjs/queries/ObservableQueryFor.d.ts +2 -0
  7. package/dist/cjs/queries/ObservableQueryFor.d.ts.map +1 -1
  8. package/dist/cjs/queries/ObservableQueryFor.js +34 -27
  9. package/dist/cjs/queries/ObservableQueryFor.js.map +1 -1
  10. package/dist/cjs/queries/QueryInstanceCache.d.ts +1 -0
  11. package/dist/cjs/queries/QueryInstanceCache.d.ts.map +1 -1
  12. package/dist/cjs/queries/QueryInstanceCache.js +11 -16
  13. package/dist/cjs/queries/QueryInstanceCache.js.map +1 -1
  14. package/dist/cjs/queries/ServerSentEventHubConnection.d.ts +7 -0
  15. package/dist/cjs/queries/ServerSentEventHubConnection.d.ts.map +1 -1
  16. package/dist/cjs/queries/ServerSentEventHubConnection.js +97 -57
  17. package/dist/cjs/queries/ServerSentEventHubConnection.js.map +1 -1
  18. package/dist/cjs/queries/WebSocketHubConnection.d.ts +6 -0
  19. package/dist/cjs/queries/WebSocketHubConnection.d.ts.map +1 -1
  20. package/dist/cjs/queries/WebSocketHubConnection.js +62 -43
  21. package/dist/cjs/queries/WebSocketHubConnection.js.map +1 -1
  22. package/dist/esm/commands/Command.d.ts +1 -0
  23. package/dist/esm/commands/Command.d.ts.map +1 -1
  24. package/dist/esm/commands/Command.js +15 -16
  25. package/dist/esm/commands/Command.js.map +1 -1
  26. package/dist/esm/queries/ObservableQueryFor.d.ts +2 -0
  27. package/dist/esm/queries/ObservableQueryFor.d.ts.map +1 -1
  28. package/dist/esm/queries/ObservableQueryFor.js +35 -28
  29. package/dist/esm/queries/ObservableQueryFor.js.map +1 -1
  30. package/dist/esm/queries/QueryInstanceCache.d.ts +1 -0
  31. package/dist/esm/queries/QueryInstanceCache.d.ts.map +1 -1
  32. package/dist/esm/queries/QueryInstanceCache.js +11 -16
  33. package/dist/esm/queries/QueryInstanceCache.js.map +1 -1
  34. package/dist/esm/queries/ServerSentEventHubConnection.d.ts +7 -0
  35. package/dist/esm/queries/ServerSentEventHubConnection.d.ts.map +1 -1
  36. package/dist/esm/queries/ServerSentEventHubConnection.js +97 -57
  37. package/dist/esm/queries/ServerSentEventHubConnection.js.map +1 -1
  38. package/dist/esm/queries/WebSocketHubConnection.d.ts +6 -0
  39. package/dist/esm/queries/WebSocketHubConnection.d.ts.map +1 -1
  40. package/dist/esm/queries/WebSocketHubConnection.js +62 -43
  41. package/dist/esm/queries/WebSocketHubConnection.js.map +1 -1
  42. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  43. package/package.json +1 -1
  44. package/queries/ObservableQueryFor.ts +43 -32
  45. package/queries/QueryInstanceCache.ts +12 -18
  46. package/queries/ServerSentEventHubConnection.ts +120 -113
  47. package/queries/WebSocketHubConnection.ts +72 -53
@@ -13,6 +13,8 @@ import { HubMessageType } from './WebSocketHubConnection.js';
13
13
  * It must stay above 1 — a threshold at or below the server's own cadence makes the client and
14
14
  * server timers race, and the client tears down connections the server considers healthy.
15
15
  */ const IDLE_THRESHOLD_FACTOR = 2;
16
+ /** Maximum number of subscribe retries before falling back to a full SSE reconnect. */ const SUBSCRIBE_MAX_RETRIES = 3;
17
+ /** Base delay between subscribe retries; multiplied by the attempt number. */ const SUBSCRIBE_RETRY_DELAY_MS = 200;
16
18
  /**
17
19
  * A multiplexed SSE hub connection that uses EventSource for server→client streaming
18
20
  * and fetch POST requests for client→server subscribe/unsubscribe commands.
@@ -164,41 +166,54 @@ import { HubMessageType } from './WebSocketHubConnection.js';
164
166
  return;
165
167
  }
166
168
  this._policy.cancel();
167
- let url = this._sseUrl;
168
- if (this._microservice?.length > 0) {
169
- const param = `${Globals.microserviceWSQueryArgument}=${encodeURIComponent(this._microservice)}`;
170
- url += (url.includes('?') ? '&' : '?') + param;
171
- }
169
+ const url = this.buildEventSourceUrl();
172
170
  this._connectionId = undefined;
173
171
  this._supportsSubscriptionRevisions = false;
174
172
  const eventSource = Globals.eventSourceFactory ? Globals.eventSourceFactory(url) : new EventSource(url);
175
173
  this._eventSource = eventSource;
176
174
  eventSource.onopen = ()=>{
177
- if (this._disconnected || this._eventSource !== eventSource) return;
175
+ if (!this.isActiveSource(eventSource)) return;
178
176
  console.log('SSE hub connection established', url);
179
177
  this._policy.reset();
180
178
  this._keepAlive.start();
181
- // If the server does not send a Connected message within the timeout, the
182
- // connection is broken. Close and retry via the reconnect policy.
183
- this.clearConnectTimeout();
184
- this._connectTimeoutTimer = setTimeout(()=>{
185
- if (!this._disconnected && this._eventSource === eventSource && !this._connectionId) {
186
- console.warn('SSE hub: no Connected message within timeout; retrying', this._connectTimeoutMs, url);
187
- this.reconnect();
188
- }
189
- }, this._connectTimeoutMs);
179
+ this.startConnectTimeout(eventSource, url);
190
180
  };
191
181
  eventSource.onmessage = (event)=>{
192
- if (this._disconnected || this._eventSource !== eventSource) return;
182
+ if (!this.isActiveSource(eventSource)) return;
193
183
  this._keepAlive.recordActivity();
194
184
  this.handleMessage(event.data);
195
185
  };
196
186
  eventSource.onerror = ()=>{
197
- if (this._disconnected || this._eventSource !== eventSource) return;
187
+ if (!this.isActiveSource(eventSource)) return;
198
188
  console.warn('SSE hub connection error', url);
199
189
  this.reconnect();
200
190
  };
201
191
  }
192
+ buildEventSourceUrl() {
193
+ let url = this._sseUrl;
194
+ if (this._microservice?.length > 0) {
195
+ const param = `${Globals.microserviceWSQueryArgument}=${encodeURIComponent(this._microservice)}`;
196
+ url += (url.includes('?') ? '&' : '?') + param;
197
+ }
198
+ return url;
199
+ }
200
+ isActiveSource(eventSource) {
201
+ return !this._disconnected && this._eventSource === eventSource;
202
+ }
203
+ /**
204
+ * If the server does not send a Connected message within the timeout, the
205
+ * connection is broken. Close and retry via the reconnect policy.
206
+ * @param {EventSource} eventSource The source the timeout guards.
207
+ * @param {string} url The URL used for diagnostics.
208
+ */ startConnectTimeout(eventSource, url) {
209
+ this.clearConnectTimeout();
210
+ this._connectTimeoutTimer = setTimeout(()=>{
211
+ if (this.isActiveSource(eventSource) && !this._connectionId) {
212
+ console.warn('SSE hub: no Connected message within timeout; retrying', this._connectTimeoutMs, url);
213
+ this.reconnect();
214
+ }
215
+ }, this._connectTimeoutMs);
216
+ }
202
217
  reconnect() {
203
218
  this._keepAlive.stop();
204
219
  this.clearConnectTimeout();
@@ -236,22 +251,26 @@ import { HubMessageType } from './WebSocketHubConnection.js';
236
251
  case HubMessageType.Ping:
237
252
  break;
238
253
  case HubMessageType.Unauthorized:
239
- if (this.isMessageForCurrentSubscription(message)) {
240
- console.warn('SSE hub: query unauthorized', message.queryId);
241
- this.handleUnauthorized(message);
242
- }
254
+ this.handleUnauthorizedMessage(message);
243
255
  break;
244
256
  case HubMessageType.Error:
245
- if (this.isMessageForCurrentSubscription(message)) {
246
- console.error('SSE hub: query error', message.queryId, message.payload);
247
- this.handleError(message);
248
- }
257
+ this.handleErrorMessage(message);
249
258
  break;
250
259
  }
251
260
  } catch (error) {
252
261
  console.error('SSE hub: error parsing message', error);
253
262
  }
254
263
  }
264
+ handleUnauthorizedMessage(message) {
265
+ if (!this.isMessageForCurrentSubscription(message)) return;
266
+ console.warn('SSE hub: query unauthorized', message.queryId);
267
+ this.handleUnauthorized(message);
268
+ }
269
+ handleErrorMessage(message) {
270
+ if (!this.isMessageForCurrentSubscription(message)) return;
271
+ console.error('SSE hub: query error', message.queryId, message.payload);
272
+ this.handleError(message);
273
+ }
255
274
  handleConnected(message) {
256
275
  const connectionId = message.payload;
257
276
  const connectionIdChanged = this._connectionId !== connectionId;
@@ -341,14 +360,12 @@ import { HubMessageType } from './WebSocketHubConnection.js';
341
360
  body.revision = subscription.revision;
342
361
  }
343
362
  const customHeaders = Globals.httpHeadersCallback?.() ?? {};
344
- // Maximum number of subscribe retries before falling back to a full SSE reconnect.
345
- // In a round-robin load-balanced deployment the subscribe POST may land on a different
346
- // backend instance than the one holding the SSE connection. Retrying gives the load
347
- // balancer the chance to route a subsequent attempt to the correct instance without
348
- // tearing down the SSE connection unnecessarily. With N backend instances at most
349
- // N-1 retries are needed, so 3 retries covers deployments with up to 4 replicas.
350
- const maxRetries = 3;
351
- const retryDelayMs = 200;
363
+ const target = {
364
+ queryId,
365
+ subscription,
366
+ connectionId,
367
+ attempt
368
+ };
352
369
  fetch(this._subscribeUrl, {
353
370
  method: 'POST',
354
371
  headers: {
@@ -358,34 +375,57 @@ import { HubMessageType } from './WebSocketHubConnection.js';
358
375
  body: JSON.stringify(body)
359
376
  }).then((response)=>{
360
377
  if (!response.ok) {
361
- if (!this.isCurrentSubscription(queryId, subscription, connectionId)) {
362
- console.warn('SSE hub: subscribe POST returned a failure for an obsolete connection or subscription; ignoring', queryId, response.status);
363
- } else if (attempt < maxRetries) {
364
- setTimeout(()=>{
365
- if (this.isCurrentSubscription(queryId, subscription, connectionId)) {
366
- this.sendSubscribe(queryId, subscription, attempt + 1);
367
- }
368
- }, retryDelayMs * (attempt + 1));
369
- } else {
370
- console.warn('SSE hub: subscribe POST failed after retries; reconnecting', queryId, response.status, attempt + 1);
371
- this.reconnect();
372
- }
378
+ this.handleSubscribeFailure(target, console.warn, response.status);
373
379
  }
374
380
  }).catch((error)=>{
375
- if (!this.isCurrentSubscription(queryId, subscription, connectionId)) {
376
- console.warn('SSE hub: subscribe POST failed for an obsolete connection or subscription; ignoring', queryId, error);
377
- } else if (attempt < maxRetries) {
378
- setTimeout(()=>{
379
- if (this.isCurrentSubscription(queryId, subscription, connectionId)) {
380
- this.sendSubscribe(queryId, subscription, attempt + 1);
381
- }
382
- }, retryDelayMs * (attempt + 1));
383
- } else {
384
- console.error('SSE hub: subscribe POST failed after retries; reconnecting', queryId, attempt + 1, error);
385
- this.reconnect();
386
- }
381
+ this.handleSubscribeFailure(target, console.error, undefined, error);
387
382
  });
388
383
  }
384
+ /**
385
+ * Decide what to do after a failed subscribe POST: ignore it when obsolete, retry with back-off,
386
+ * or fall back to a full reconnect once the retries are exhausted.
387
+ *
388
+ * The retry limit exists because in a round-robin load-balanced deployment the subscribe POST may
389
+ * land on a different backend instance than the one holding the SSE connection. Retrying gives
390
+ * the load balancer the chance to route a subsequent attempt to the correct instance without
391
+ * tearing down the SSE connection unnecessarily. With N backend instances at most N-1 retries
392
+ * are needed, so 3 retries covers deployments with up to 4 replicas.
393
+ * @param {SubscribeTarget} target The subscribe attempt that failed.
394
+ * @param {Function} exhaustedLog The logger used when the retries are exhausted.
395
+ * @param {number | undefined} status The HTTP status, when the server responded.
396
+ * @param {unknown} error The thrown error, when the request itself failed.
397
+ */ handleSubscribeFailure(target, exhaustedLog, status, error) {
398
+ const { queryId, subscription, connectionId, attempt } = target;
399
+ const detail = error === undefined ? [
400
+ status
401
+ ] : [
402
+ error
403
+ ];
404
+ if (!this.isCurrentSubscription(queryId, subscription, connectionId)) {
405
+ const reason = error === undefined ? 'returned a failure' : 'failed';
406
+ console.warn(`SSE hub: subscribe POST ${reason} for an obsolete connection or subscription; ignoring`, queryId, ...detail);
407
+ } else if (attempt < SUBSCRIBE_MAX_RETRIES) {
408
+ this.scheduleSubscribeRetry(target);
409
+ } else {
410
+ const tail = error === undefined ? [
411
+ status,
412
+ attempt + 1
413
+ ] : [
414
+ attempt + 1,
415
+ error
416
+ ];
417
+ exhaustedLog('SSE hub: subscribe POST failed after retries; reconnecting', queryId, ...tail);
418
+ this.reconnect();
419
+ }
420
+ }
421
+ scheduleSubscribeRetry(target) {
422
+ const { queryId, subscription, connectionId, attempt } = target;
423
+ setTimeout(()=>{
424
+ if (this.isCurrentSubscription(queryId, subscription, connectionId)) {
425
+ this.sendSubscribe(queryId, subscription, attempt + 1);
426
+ }
427
+ }, SUBSCRIBE_RETRY_DELAY_MS * (attempt + 1));
428
+ }
389
429
  isMessageForCurrentSubscription(message) {
390
430
  if (!message.queryId) return false;
391
431
  const subscription = this._subscriptions.get(message.queryId);
@@ -1 +1 @@
1
- {"version":3,"file":"ServerSentEventHubConnection.js","sources":["../../../queries/ServerSentEventHubConnection.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 { IObservableQueryHubConnection } from './IObservableQueryHubConnection.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';\nimport {\n type HubMessage,\n HubMessageType,\n type SubscriptionRequest,\n} from './WebSocketHubConnection.js';\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\ninterface ActiveSubscription {\n request: SubscriptionRequest;\n callback: DataReceived<any>;\n revision: number;\n}\n\n/**\n * How many keep-alive intervals of silence are tolerated before the connection is considered dead.\n *\n * The server guarantees a message every interval, so this is pure slack for latency and jitter.\n * It must stay above 1 — a threshold at or below the server's own cadence makes the client and\n * server timers race, and the client tears down connections the server considers healthy.\n */\nconst IDLE_THRESHOLD_FACTOR = 2;\n\n/**\n * A multiplexed SSE hub connection that uses EventSource for server→client streaming\n * and fetch POST requests for client→server subscribe/unsubscribe commands.\n *\n * Protocol:\n * 1. Open EventSource to the SSE hub endpoint.\n * 2. Server sends a {@link HubMessageType.Connected} message with the connection identifier.\n * 3. Client sends POST to subscribe/unsubscribe endpoints using the connection identifier.\n * 4. Server streams {@link HubMessageType.QueryResult} messages tagged with queryId.\n * 5. When EventSource closes, server cleans up all subscriptions for this connection.\n */\nexport class ServerSentEventHubConnection implements IObservableQueryHubConnection {\n private _eventSource?: EventSource;\n private _connectionId?: string;\n private _disconnected = false;\n private _subscriptions: Map<string, ActiveSubscription> = new Map();\n private _pendingSubscriptions: Map<string, ActiveSubscription> = new Map();\n private _lastPongLatency: number = 0;\n private _latencySamples: number[] = [];\n private _connectTimeoutTimer?: ReturnType<typeof setTimeout>;\n private _nextRevision = 0;\n private _supportsSubscriptionRevisions = false;\n private readonly _keepAlive: HubConnectionKeepAlive;\n\n /**\n * Initializes a new instance of {@link ServerSentEventHubConnection}.\n * @param {string} _sseUrl The SSE hub endpoint URL (e.g. `http://localhost:5000/.cratis/queries/sse`).\n * @param {string} _subscribeUrl The subscribe POST endpoint URL.\n * @param {string} _unsubscribeUrl The unsubscribe POST endpoint URL.\n * @param {string} _microservice The microservice name to pass as a query argument.\n * @param {number} keepAliveIntervalMs The keep-alive cadence to assume until the server advertises\n * its own on the {@link HubMessageType.Connected} message (default: 30 000 ms). The connection is\n * considered stale after {@link IDLE_THRESHOLD_FACTOR} times this without any server message.\n * @param {number} _connectTimeoutMs How long to wait for the {@link HubMessageType.Connected}\n * message after the HTTP connection opens before giving up and retrying (default: 15 000 ms).\n * @param {IReconnectPolicy} _policy The reconnect policy to use (default: {@link ReconnectPolicy}).\n */\n constructor(\n private readonly _sseUrl: string,\n private readonly _subscribeUrl: string,\n private readonly _unsubscribeUrl: string,\n private readonly _microservice: string,\n keepAliveIntervalMs: number = 30000,\n private readonly _connectTimeoutMs: number = 15000,\n private readonly _policy: IReconnectPolicy = new ReconnectPolicy(),\n ) {\n // SSE is server→client only: the client cannot send pings. Instead we watch for\n // inactivity — if the server stops sending messages (including its own keep-alive\n // pings) for the entire idle threshold, the connection is stale and we reconnect.\n //\n // The server guarantees a message at least every keep-alive interval, so the threshold\n // only has to absorb network latency, timer jitter and server-side scheduling hiccups.\n // A hard TCP drop surfaces immediately through `onerror`, so this watchdog only needs to\n // catch silent black-holes — that makes a generous tolerance strictly better than a tight\n // one, which would tear down healthy connections.\n //\n // The interval below is only the starting assumption; the server advertises its real\n // cadence on the Connected message and {@link handleConnected} reconfigures from it.\n this._keepAlive = new HubConnectionKeepAlive(\n keepAliveIntervalMs,\n () => {\n if (!this._disconnected && this._subscriptions.size > 0) {\n console.warn(\n 'SSE hub: no messages received; reconnecting',\n this._keepAlive.idleThresholdMs,\n this._sseUrl,\n );\n this.reconnect();\n }\n },\n keepAliveIntervalMs * IDLE_THRESHOLD_FACTOR,\n );\n }\n\n /** @inheritdoc */\n get queryCount(): number {\n return this._subscriptions.size;\n }\n\n /** @inheritdoc */\n get isConnected(): boolean {\n return (\n this._connectionId !== undefined &&\n this._eventSource?.readyState === EventSource.OPEN\n );\n }\n\n /** @inheritdoc */\n get lastPingLatency(): number {\n return this._lastPongLatency;\n }\n\n /** @inheritdoc */\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 /** @inheritdoc */\n subscribe(\n queryId: string,\n request: SubscriptionRequest,\n callback: DataReceived<any>,\n ): void {\n const sub: ActiveSubscription = {\n request,\n callback,\n revision: this.getNextRevision(),\n };\n this._subscriptions.set(queryId, sub);\n\n this.ensureConnected();\n\n if (this._connectionId) {\n this.sendSubscribe(queryId, sub);\n } else {\n // Not yet connected, queue for when Connected message arrives.\n this._pendingSubscriptions.set(queryId, sub);\n }\n }\n\n /** @inheritdoc */\n unsubscribe(queryId: string): void {\n const subscription = this._subscriptions.get(queryId);\n this._subscriptions.delete(queryId);\n this._pendingSubscriptions.delete(queryId);\n\n if (this._connectionId && subscription) {\n this.sendUnsubscribe(queryId, subscription.revision);\n }\n\n if (this._subscriptions.size === 0) {\n this.close();\n }\n }\n\n /** @inheritdoc */\n dispose(): void {\n this._disconnected = true;\n this._subscriptions.clear();\n this._pendingSubscriptions.clear();\n this._policy.cancel();\n this._keepAlive.stop();\n this.clearConnectTimeout();\n this.detachAndCloseEventSource();\n this._connectionId = undefined;\n }\n\n private detachAndCloseEventSource(): void {\n const eventSource = this._eventSource;\n if (!eventSource) return;\n\n eventSource.onopen = null;\n eventSource.onmessage = null;\n eventSource.onerror = null;\n eventSource.close();\n\n if (this._eventSource === eventSource) {\n this._eventSource = undefined;\n }\n }\n\n private ensureConnected(): void {\n if (this._disconnected) {\n this._disconnected = false;\n }\n\n if (this._eventSource && this._eventSource.readyState !== EventSource.CLOSED) {\n return;\n }\n\n // An immediate subscription can win while a back-off callback is pending. Cancel that callback before\n // opening, and let openEventSource perform the same live-source guard for already-queued callbacks.\n this._policy.cancel();\n this.openEventSource();\n }\n\n private close(): void {\n this._disconnected = true;\n this._policy.cancel();\n this._keepAlive.stop();\n this.clearConnectTimeout();\n this.detachAndCloseEventSource();\n this._connectionId = undefined;\n }\n\n private openEventSource(): void {\n if (this._eventSource && this._eventSource.readyState !== EventSource.CLOSED) {\n return;\n }\n\n this._policy.cancel();\n let url = this._sseUrl;\n if (this._microservice?.length > 0) {\n const param = `${Globals.microserviceWSQueryArgument}=${encodeURIComponent(this._microservice)}`;\n url += (url.includes('?') ? '&' : '?') + param;\n }\n\n this._connectionId = undefined;\n this._supportsSubscriptionRevisions = false;\n const eventSource = Globals.eventSourceFactory\n ? Globals.eventSourceFactory(url)\n : new EventSource(url);\n this._eventSource = eventSource;\n\n eventSource.onopen = () => {\n if (this._disconnected || this._eventSource !== eventSource) return;\n console.log('SSE hub connection established', url);\n this._policy.reset();\n this._keepAlive.start();\n\n // If the server does not send a Connected message within the timeout, the\n // connection is broken. Close and retry via the reconnect policy.\n this.clearConnectTimeout();\n this._connectTimeoutTimer = setTimeout(() => {\n if (\n !this._disconnected &&\n this._eventSource === eventSource &&\n !this._connectionId\n ) {\n console.warn(\n 'SSE hub: no Connected message within timeout; retrying',\n this._connectTimeoutMs,\n url,\n );\n this.reconnect();\n }\n }, this._connectTimeoutMs);\n };\n\n eventSource.onmessage = (event: MessageEvent) => {\n if (this._disconnected || this._eventSource !== eventSource) return;\n this._keepAlive.recordActivity();\n this.handleMessage(event.data as string);\n };\n\n eventSource.onerror = () => {\n if (this._disconnected || this._eventSource !== eventSource) return;\n console.warn('SSE hub connection error', url);\n this.reconnect();\n };\n }\n\n private reconnect(): void {\n this._keepAlive.stop();\n this.clearConnectTimeout();\n\n // Detach before closing so callbacks already queued by the retired source are inert.\n this.detachAndCloseEventSource();\n this._connectionId = undefined;\n\n // Move all active subscriptions to pending so they re-subscribe when\n // the next Connected message arrives after the managed reconnect.\n for (const [queryId, sub] of this._subscriptions) {\n this._pendingSubscriptions.set(queryId, sub);\n }\n\n if (this._subscriptions.size === 0) return;\n\n this._policy.schedule(() => {\n if (!this._disconnected && this._subscriptions.size > 0) {\n this.ensureConnected();\n }\n }, this._sseUrl);\n }\n\n private clearConnectTimeout(): void {\n if (this._connectTimeoutTimer !== undefined) {\n clearTimeout(this._connectTimeoutTimer);\n this._connectTimeoutTimer = undefined;\n }\n }\n\n private handleMessage(rawData: string): void {\n try {\n const message = JSON.parse(rawData) as HubMessage;\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.Ping:\n // Server-sent keep-alive ping — activity already recorded in onmessage.\n break;\n case HubMessageType.Unauthorized:\n if (this.isMessageForCurrentSubscription(message)) {\n console.warn('SSE hub: query unauthorized', message.queryId);\n this.handleUnauthorized(message);\n }\n break;\n case HubMessageType.Error:\n if (this.isMessageForCurrentSubscription(message)) {\n console.error(\n 'SSE hub: query error',\n message.queryId,\n message.payload,\n );\n this.handleError(message);\n }\n break;\n }\n } catch (error) {\n console.error('SSE hub: error parsing message', error);\n }\n }\n\n private handleConnected(message: HubMessage): void {\n const connectionId = message.payload as string;\n const connectionIdChanged = this._connectionId !== connectionId;\n this._connectionId = connectionId;\n this._supportsSubscriptionRevisions =\n message.supportsSubscriptionRevisions === true;\n console.log('SSE hub: connected', this._connectionId);\n\n // Connected message arrived — cancel the connect timeout.\n this.clearConnectTimeout();\n\n this.applyServerKeepAliveInterval(message.keepAliveIntervalMs);\n\n // The active collection is authoritative. A previous connection's subscribe POST may\n // still be in flight, so relying only on pending subscriptions can orphan a query when\n // the server assigns a new connection ID before that POST completes.\n if (connectionIdChanged) {\n for (const [queryId, subscription] of this._subscriptions) {\n this.sendSubscribe(queryId, subscription);\n }\n }\n this._pendingSubscriptions.clear();\n }\n\n /**\n * Align the idle watchdog with the keep-alive cadence the server actually runs on.\n *\n * Without this the client assumes the default interval, so a server configured with a longer\n * interval — or with keep-alive switched off entirely — would look silent and be reconnected\n * on a loop even though it is perfectly healthy.\n * @param {number | undefined} serverIntervalMs The interval advertised by the server, if any.\n */\n private applyServerKeepAliveInterval(serverIntervalMs?: number): void {\n if (serverIntervalMs === undefined) return;\n\n // Keep-alive disabled server-side: silence is expected, so watching for it would guarantee\n // an endless reconnect loop. Hard drops still surface through `onerror`.\n if (serverIntervalMs <= 0) {\n this._keepAlive.stop();\n return;\n }\n\n this._keepAlive.reconfigure(\n serverIntervalMs,\n serverIntervalMs * IDLE_THRESHOLD_FACTOR,\n );\n }\n\n private handleQueryResult(message: HubMessage): void {\n const result = message.payload as QueryResult<any>;\n this.deliverResult(message, result, result?.isValid === false);\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.deliverResult(message, result, false);\n }\n\n private handleUnauthorized(message: HubMessage): void {\n this.deliverResult(message, QueryResult.unauthorized(), true);\n }\n\n private deliverResult(message: HubMessage, result: QueryResult<any>, terminal: boolean): void {\n if (!message.queryId) return;\n\n const sub = this._subscriptions.get(message.queryId);\n if (!sub || !this.isMessageForCurrentSubscription(message)) return;\n\n if (terminal) {\n this._subscriptions.delete(message.queryId);\n this._pendingSubscriptions.delete(message.queryId);\n }\n sub.callback(result);\n }\n\n private sendSubscribe(\n queryId: string,\n subscription: ActiveSubscription,\n attempt: number = 0,\n ): void {\n if (!this._connectionId || this._disconnected) return;\n\n // Capture the connection ID and subscription instance so retries can detect whether\n // either has been replaced while this request is in flight.\n const connectionId = this._connectionId;\n\n const body: {\n connectionId: string;\n queryId: string;\n request: SubscriptionRequest;\n revision?: number;\n } = {\n connectionId,\n queryId,\n request: subscription.request,\n };\n if (this._supportsSubscriptionRevisions) {\n body.revision = subscription.revision;\n }\n\n const customHeaders = Globals.httpHeadersCallback?.() ?? {};\n\n // Maximum number of subscribe retries before falling back to a full SSE reconnect.\n // In a round-robin load-balanced deployment the subscribe POST may land on a different\n // backend instance than the one holding the SSE connection. Retrying gives the load\n // balancer the chance to route a subsequent attempt to the correct instance without\n // tearing down the SSE connection unnecessarily. With N backend instances at most\n // N-1 retries are needed, so 3 retries covers deployments with up to 4 replicas.\n const maxRetries = 3;\n const retryDelayMs = 200;\n\n fetch(this._subscribeUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...customHeaders },\n body: JSON.stringify(body),\n })\n .then((response) => {\n if (!response.ok) {\n if (\n !this.isCurrentSubscription(queryId, subscription, connectionId)\n ) {\n console.warn(\n 'SSE hub: subscribe POST returned a failure for an obsolete connection or subscription; ignoring',\n queryId,\n response.status,\n );\n } else if (attempt < maxRetries) {\n setTimeout(\n () => {\n if (\n this.isCurrentSubscription(\n queryId,\n subscription,\n connectionId,\n )\n ) {\n this.sendSubscribe(\n queryId,\n subscription,\n attempt + 1,\n );\n }\n },\n retryDelayMs * (attempt + 1),\n );\n } else {\n console.warn(\n 'SSE hub: subscribe POST failed after retries; reconnecting',\n queryId,\n response.status,\n attempt + 1,\n );\n this.reconnect();\n }\n }\n })\n .catch((error) => {\n if (!this.isCurrentSubscription(queryId, subscription, connectionId)) {\n console.warn(\n 'SSE hub: subscribe POST failed for an obsolete connection or subscription; ignoring',\n queryId,\n error,\n );\n } else if (attempt < maxRetries) {\n setTimeout(\n () => {\n if (\n this.isCurrentSubscription(\n queryId,\n subscription,\n connectionId,\n )\n ) {\n this.sendSubscribe(queryId, subscription, attempt + 1);\n }\n },\n retryDelayMs * (attempt + 1),\n );\n } else {\n console.error(\n 'SSE hub: subscribe POST failed after retries; reconnecting',\n queryId,\n attempt + 1,\n error,\n );\n this.reconnect();\n }\n });\n }\n\n private isMessageForCurrentSubscription(message: HubMessage): boolean {\n if (!message.queryId) return false;\n\n const subscription = this._subscriptions.get(message.queryId);\n if (!subscription) return false;\n\n if (!this._supportsSubscriptionRevisions) {\n return message.revision === undefined;\n }\n\n return (\n this.isValidRevision(message.revision) &&\n message.revision === subscription.revision\n );\n }\n\n private isCurrentSubscription(\n queryId: string,\n subscription: ActiveSubscription,\n connectionId: string,\n ): boolean {\n return (\n !this._disconnected &&\n this._connectionId === connectionId &&\n this._subscriptions.get(queryId) === subscription\n );\n }\n\n private getNextRevision(): number {\n if (this._nextRevision >= Number.MAX_SAFE_INTEGER) {\n throw new RangeError(\n 'SSE hub subscription revision exhausted the safe integer range',\n );\n }\n\n return ++this._nextRevision;\n }\n\n private isValidRevision(revision: number | undefined): revision is number {\n return revision !== undefined && Number.isSafeInteger(revision) && revision > 0;\n }\n\n private sendUnsubscribe(queryId: string, revision: number): void {\n if (!this._connectionId) return;\n\n const body: {\n connectionId: string;\n queryId: string;\n revision?: number;\n } = {\n connectionId: this._connectionId,\n queryId,\n };\n if (this._supportsSubscriptionRevisions) {\n body.revision = revision;\n }\n\n const customHeaders = Globals.httpHeadersCallback?.() ?? {};\n\n fetch(this._unsubscribeUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...customHeaders },\n body: JSON.stringify(body),\n }).catch((error) => {\n console.error('SSE hub: unsubscribe POST failed', queryId, error);\n });\n }\n}\n"],"names":["IDLE_THRESHOLD_FACTOR","ServerSentEventHubConnection","_eventSource","_connectionId","_disconnected","_subscriptions","Map","_pendingSubscriptions","_lastPongLatency","_latencySamples","_connectTimeoutTimer","_nextRevision","_supportsSubscriptionRevisions","_keepAlive","_subscribeUrl","_unsubscribeUrl","keepAliveIntervalMs","_connectTimeoutMs","_policy","ReconnectPolicy","_sseUrl","_microservice","HubConnectionKeepAlive","size","console","warn","idleThresholdMs","reconnect","queryCount","isConnected","undefined","readyState","EventSource","OPEN","lastPingLatency","averageLatency","length","reduce","a","b","subscribe","queryId","request","callback","sub","revision","getNextRevision","set","ensureConnected","sendSubscribe","unsubscribe","subscription","get","delete","sendUnsubscribe","close","dispose","clear","cancel","stop","clearConnectTimeout","detachAndCloseEventSource","eventSource","onopen","onmessage","onerror","CLOSED","openEventSource","url","param","Globals","microserviceWSQueryArgument","encodeURIComponent","includes","eventSourceFactory","log","reset","start","setTimeout","event","recordActivity","handleMessage","data","schedule","clearTimeout","rawData","message","JSON","parse","type","HubMessageType","Connected","handleConnected","QueryResult","handleQueryResult","Ping","Unauthorized","isMessageForCurrentSubscription","handleUnauthorized","Error","error","payload","handleError","connectionId","connectionIdChanged","supportsSubscriptionRevisions","applyServerKeepAliveInterval","serverIntervalMs","reconfigure","result","deliverResult","isValid","isSuccess","isReady","isAuthorized","hasExceptions","validationResults","exceptionMessages","exceptionStackTrace","paging","totalItems","totalPages","page","Object","unauthorized","terminal","attempt","body","customHeaders","httpHeadersCallback","maxRetries","retryDelayMs","fetch","method","headers","stringify","then","response","ok","isCurrentSubscription","status","catch","isValidRevision","Number","MAX_SAFE_INTEGER","RangeError","isSafeInteger"],"mappings":"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"}
1
+ {"version":3,"file":"ServerSentEventHubConnection.js","sources":["../../../queries/ServerSentEventHubConnection.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 { IObservableQueryHubConnection } from './IObservableQueryHubConnection.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';\nimport {\n type HubMessage,\n HubMessageType,\n type SubscriptionRequest,\n} from './WebSocketHubConnection.js';\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\ninterface ActiveSubscription {\n request: SubscriptionRequest;\n callback: DataReceived<any>;\n revision: number;\n}\n\n/**\n * How many keep-alive intervals of silence are tolerated before the connection is considered dead.\n *\n * The server guarantees a message every interval, so this is pure slack for latency and jitter.\n * It must stay above 1 — a threshold at or below the server's own cadence makes the client and\n * server timers race, and the client tears down connections the server considers healthy.\n */\nconst IDLE_THRESHOLD_FACTOR = 2;\n\n/** Maximum number of subscribe retries before falling back to a full SSE reconnect. */\nconst SUBSCRIBE_MAX_RETRIES = 3;\n\n/** Base delay between subscribe retries; multiplied by the attempt number. */\nconst SUBSCRIBE_RETRY_DELAY_MS = 200;\n\ninterface SubscribeTarget {\n queryId: string;\n subscription: ActiveSubscription;\n connectionId: string;\n attempt: number;\n}\n\n/**\n * A multiplexed SSE hub connection that uses EventSource for server→client streaming\n * and fetch POST requests for client→server subscribe/unsubscribe commands.\n *\n * Protocol:\n * 1. Open EventSource to the SSE hub endpoint.\n * 2. Server sends a {@link HubMessageType.Connected} message with the connection identifier.\n * 3. Client sends POST to subscribe/unsubscribe endpoints using the connection identifier.\n * 4. Server streams {@link HubMessageType.QueryResult} messages tagged with queryId.\n * 5. When EventSource closes, server cleans up all subscriptions for this connection.\n */\nexport class ServerSentEventHubConnection implements IObservableQueryHubConnection {\n private _eventSource?: EventSource;\n private _connectionId?: string;\n private _disconnected = false;\n private _subscriptions: Map<string, ActiveSubscription> = new Map();\n private _pendingSubscriptions: Map<string, ActiveSubscription> = new Map();\n private _lastPongLatency: number = 0;\n private _latencySamples: number[] = [];\n private _connectTimeoutTimer?: ReturnType<typeof setTimeout>;\n private _nextRevision = 0;\n private _supportsSubscriptionRevisions = false;\n private readonly _keepAlive: HubConnectionKeepAlive;\n\n /**\n * Initializes a new instance of {@link ServerSentEventHubConnection}.\n * @param {string} _sseUrl The SSE hub endpoint URL (e.g. `http://localhost:5000/.cratis/queries/sse`).\n * @param {string} _subscribeUrl The subscribe POST endpoint URL.\n * @param {string} _unsubscribeUrl The unsubscribe POST endpoint URL.\n * @param {string} _microservice The microservice name to pass as a query argument.\n * @param {number} keepAliveIntervalMs The keep-alive cadence to assume until the server advertises\n * its own on the {@link HubMessageType.Connected} message (default: 30 000 ms). The connection is\n * considered stale after {@link IDLE_THRESHOLD_FACTOR} times this without any server message.\n * @param {number} _connectTimeoutMs How long to wait for the {@link HubMessageType.Connected}\n * message after the HTTP connection opens before giving up and retrying (default: 15 000 ms).\n * @param {IReconnectPolicy} _policy The reconnect policy to use (default: {@link ReconnectPolicy}).\n */\n constructor(\n private readonly _sseUrl: string,\n private readonly _subscribeUrl: string,\n private readonly _unsubscribeUrl: string,\n private readonly _microservice: string,\n keepAliveIntervalMs: number = 30000,\n private readonly _connectTimeoutMs: number = 15000,\n private readonly _policy: IReconnectPolicy = new ReconnectPolicy(),\n ) {\n // SSE is server→client only: the client cannot send pings. Instead we watch for\n // inactivity — if the server stops sending messages (including its own keep-alive\n // pings) for the entire idle threshold, the connection is stale and we reconnect.\n //\n // The server guarantees a message at least every keep-alive interval, so the threshold\n // only has to absorb network latency, timer jitter and server-side scheduling hiccups.\n // A hard TCP drop surfaces immediately through `onerror`, so this watchdog only needs to\n // catch silent black-holes — that makes a generous tolerance strictly better than a tight\n // one, which would tear down healthy connections.\n //\n // The interval below is only the starting assumption; the server advertises its real\n // cadence on the Connected message and {@link handleConnected} reconfigures from it.\n this._keepAlive = new HubConnectionKeepAlive(\n keepAliveIntervalMs,\n () => {\n if (!this._disconnected && this._subscriptions.size > 0) {\n console.warn(\n 'SSE hub: no messages received; reconnecting',\n this._keepAlive.idleThresholdMs,\n this._sseUrl,\n );\n this.reconnect();\n }\n },\n keepAliveIntervalMs * IDLE_THRESHOLD_FACTOR,\n );\n }\n\n /** @inheritdoc */\n get queryCount(): number {\n return this._subscriptions.size;\n }\n\n /** @inheritdoc */\n get isConnected(): boolean {\n return (\n this._connectionId !== undefined &&\n this._eventSource?.readyState === EventSource.OPEN\n );\n }\n\n /** @inheritdoc */\n get lastPingLatency(): number {\n return this._lastPongLatency;\n }\n\n /** @inheritdoc */\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 /** @inheritdoc */\n subscribe(\n queryId: string,\n request: SubscriptionRequest,\n callback: DataReceived<any>,\n ): void {\n const sub: ActiveSubscription = {\n request,\n callback,\n revision: this.getNextRevision(),\n };\n this._subscriptions.set(queryId, sub);\n\n this.ensureConnected();\n\n if (this._connectionId) {\n this.sendSubscribe(queryId, sub);\n } else {\n // Not yet connected, queue for when Connected message arrives.\n this._pendingSubscriptions.set(queryId, sub);\n }\n }\n\n /** @inheritdoc */\n unsubscribe(queryId: string): void {\n const subscription = this._subscriptions.get(queryId);\n this._subscriptions.delete(queryId);\n this._pendingSubscriptions.delete(queryId);\n\n if (this._connectionId && subscription) {\n this.sendUnsubscribe(queryId, subscription.revision);\n }\n\n if (this._subscriptions.size === 0) {\n this.close();\n }\n }\n\n /** @inheritdoc */\n dispose(): void {\n this._disconnected = true;\n this._subscriptions.clear();\n this._pendingSubscriptions.clear();\n this._policy.cancel();\n this._keepAlive.stop();\n this.clearConnectTimeout();\n this.detachAndCloseEventSource();\n this._connectionId = undefined;\n }\n\n private detachAndCloseEventSource(): void {\n const eventSource = this._eventSource;\n if (!eventSource) return;\n\n eventSource.onopen = null;\n eventSource.onmessage = null;\n eventSource.onerror = null;\n eventSource.close();\n\n if (this._eventSource === eventSource) {\n this._eventSource = undefined;\n }\n }\n\n private ensureConnected(): void {\n if (this._disconnected) {\n this._disconnected = false;\n }\n\n if (this._eventSource && this._eventSource.readyState !== EventSource.CLOSED) {\n return;\n }\n\n // An immediate subscription can win while a back-off callback is pending. Cancel that callback before\n // opening, and let openEventSource perform the same live-source guard for already-queued callbacks.\n this._policy.cancel();\n this.openEventSource();\n }\n\n private close(): void {\n this._disconnected = true;\n this._policy.cancel();\n this._keepAlive.stop();\n this.clearConnectTimeout();\n this.detachAndCloseEventSource();\n this._connectionId = undefined;\n }\n\n private openEventSource(): void {\n if (this._eventSource && this._eventSource.readyState !== EventSource.CLOSED) {\n return;\n }\n\n this._policy.cancel();\n const url = this.buildEventSourceUrl();\n\n this._connectionId = undefined;\n this._supportsSubscriptionRevisions = false;\n const eventSource = Globals.eventSourceFactory\n ? Globals.eventSourceFactory(url)\n : new EventSource(url);\n this._eventSource = eventSource;\n\n eventSource.onopen = () => {\n if (!this.isActiveSource(eventSource)) return;\n console.log('SSE hub connection established', url);\n this._policy.reset();\n this._keepAlive.start();\n this.startConnectTimeout(eventSource, url);\n };\n\n eventSource.onmessage = (event: MessageEvent) => {\n if (!this.isActiveSource(eventSource)) return;\n this._keepAlive.recordActivity();\n this.handleMessage(event.data as string);\n };\n\n eventSource.onerror = () => {\n if (!this.isActiveSource(eventSource)) return;\n console.warn('SSE hub connection error', url);\n this.reconnect();\n };\n }\n\n private buildEventSourceUrl(): string {\n let url = this._sseUrl;\n if (this._microservice?.length > 0) {\n const param = `${Globals.microserviceWSQueryArgument}=${encodeURIComponent(this._microservice)}`;\n url += (url.includes('?') ? '&' : '?') + param;\n }\n return url;\n }\n\n private isActiveSource(eventSource: EventSource): boolean {\n return !this._disconnected && this._eventSource === eventSource;\n }\n\n /**\n * If the server does not send a Connected message within the timeout, the\n * connection is broken. Close and retry via the reconnect policy.\n * @param {EventSource} eventSource The source the timeout guards.\n * @param {string} url The URL used for diagnostics.\n */\n private startConnectTimeout(eventSource: EventSource, url: string): void {\n this.clearConnectTimeout();\n this._connectTimeoutTimer = setTimeout(() => {\n if (this.isActiveSource(eventSource) && !this._connectionId) {\n console.warn(\n 'SSE hub: no Connected message within timeout; retrying',\n this._connectTimeoutMs,\n url,\n );\n this.reconnect();\n }\n }, this._connectTimeoutMs);\n }\n\n private reconnect(): void {\n this._keepAlive.stop();\n this.clearConnectTimeout();\n\n // Detach before closing so callbacks already queued by the retired source are inert.\n this.detachAndCloseEventSource();\n this._connectionId = undefined;\n\n // Move all active subscriptions to pending so they re-subscribe when\n // the next Connected message arrives after the managed reconnect.\n for (const [queryId, sub] of this._subscriptions) {\n this._pendingSubscriptions.set(queryId, sub);\n }\n\n if (this._subscriptions.size === 0) return;\n\n this._policy.schedule(() => {\n if (!this._disconnected && this._subscriptions.size > 0) {\n this.ensureConnected();\n }\n }, this._sseUrl);\n }\n\n private clearConnectTimeout(): void {\n if (this._connectTimeoutTimer !== undefined) {\n clearTimeout(this._connectTimeoutTimer);\n this._connectTimeoutTimer = undefined;\n }\n }\n\n private handleMessage(rawData: string): void {\n try {\n const message = JSON.parse(rawData) as HubMessage;\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.Ping:\n // Server-sent keep-alive ping — activity already recorded in onmessage.\n break;\n case HubMessageType.Unauthorized:\n this.handleUnauthorizedMessage(message);\n break;\n case HubMessageType.Error:\n this.handleErrorMessage(message);\n break;\n }\n } catch (error) {\n console.error('SSE hub: error parsing message', error);\n }\n }\n\n private handleUnauthorizedMessage(message: HubMessage): void {\n if (!this.isMessageForCurrentSubscription(message)) return;\n console.warn('SSE hub: query unauthorized', message.queryId);\n this.handleUnauthorized(message);\n }\n\n private handleErrorMessage(message: HubMessage): void {\n if (!this.isMessageForCurrentSubscription(message)) return;\n console.error('SSE hub: query error', message.queryId, message.payload);\n this.handleError(message);\n }\n\n private handleConnected(message: HubMessage): void {\n const connectionId = message.payload as string;\n const connectionIdChanged = this._connectionId !== connectionId;\n this._connectionId = connectionId;\n this._supportsSubscriptionRevisions =\n message.supportsSubscriptionRevisions === true;\n console.log('SSE hub: connected', this._connectionId);\n\n // Connected message arrived — cancel the connect timeout.\n this.clearConnectTimeout();\n\n this.applyServerKeepAliveInterval(message.keepAliveIntervalMs);\n\n // The active collection is authoritative. A previous connection's subscribe POST may\n // still be in flight, so relying only on pending subscriptions can orphan a query when\n // the server assigns a new connection ID before that POST completes.\n if (connectionIdChanged) {\n for (const [queryId, subscription] of this._subscriptions) {\n this.sendSubscribe(queryId, subscription);\n }\n }\n this._pendingSubscriptions.clear();\n }\n\n /**\n * Align the idle watchdog with the keep-alive cadence the server actually runs on.\n *\n * Without this the client assumes the default interval, so a server configured with a longer\n * interval — or with keep-alive switched off entirely — would look silent and be reconnected\n * on a loop even though it is perfectly healthy.\n * @param {number | undefined} serverIntervalMs The interval advertised by the server, if any.\n */\n private applyServerKeepAliveInterval(serverIntervalMs?: number): void {\n if (serverIntervalMs === undefined) return;\n\n // Keep-alive disabled server-side: silence is expected, so watching for it would guarantee\n // an endless reconnect loop. Hard drops still surface through `onerror`.\n if (serverIntervalMs <= 0) {\n this._keepAlive.stop();\n return;\n }\n\n this._keepAlive.reconfigure(\n serverIntervalMs,\n serverIntervalMs * IDLE_THRESHOLD_FACTOR,\n );\n }\n\n private handleQueryResult(message: HubMessage): void {\n const result = message.payload as QueryResult<any>;\n this.deliverResult(message, result, result?.isValid === false);\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.deliverResult(message, result, false);\n }\n\n private handleUnauthorized(message: HubMessage): void {\n this.deliverResult(message, QueryResult.unauthorized(), true);\n }\n\n private deliverResult(message: HubMessage, result: QueryResult<any>, terminal: boolean): void {\n if (!message.queryId) return;\n\n const sub = this._subscriptions.get(message.queryId);\n if (!sub || !this.isMessageForCurrentSubscription(message)) return;\n\n if (terminal) {\n this._subscriptions.delete(message.queryId);\n this._pendingSubscriptions.delete(message.queryId);\n }\n sub.callback(result);\n }\n\n private sendSubscribe(\n queryId: string,\n subscription: ActiveSubscription,\n attempt: number = 0,\n ): void {\n if (!this._connectionId || this._disconnected) return;\n\n // Capture the connection ID and subscription instance so retries can detect whether\n // either has been replaced while this request is in flight.\n const connectionId = this._connectionId;\n\n const body: {\n connectionId: string;\n queryId: string;\n request: SubscriptionRequest;\n revision?: number;\n } = {\n connectionId,\n queryId,\n request: subscription.request,\n };\n if (this._supportsSubscriptionRevisions) {\n body.revision = subscription.revision;\n }\n\n const customHeaders = Globals.httpHeadersCallback?.() ?? {};\n const target: SubscribeTarget = { queryId, subscription, connectionId, attempt };\n\n fetch(this._subscribeUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...customHeaders },\n body: JSON.stringify(body),\n })\n .then((response) => {\n if (!response.ok) {\n this.handleSubscribeFailure(target, console.warn, response.status);\n }\n })\n .catch((error) => {\n this.handleSubscribeFailure(target, console.error, undefined, error);\n });\n }\n\n /**\n * Decide what to do after a failed subscribe POST: ignore it when obsolete, retry with back-off,\n * or fall back to a full reconnect once the retries are exhausted.\n *\n * The retry limit exists because in a round-robin load-balanced deployment the subscribe POST may\n * land on a different backend instance than the one holding the SSE connection. Retrying gives\n * the load balancer the chance to route a subsequent attempt to the correct instance without\n * tearing down the SSE connection unnecessarily. With N backend instances at most N-1 retries\n * are needed, so 3 retries covers deployments with up to 4 replicas.\n * @param {SubscribeTarget} target The subscribe attempt that failed.\n * @param {Function} exhaustedLog The logger used when the retries are exhausted.\n * @param {number | undefined} status The HTTP status, when the server responded.\n * @param {unknown} error The thrown error, when the request itself failed.\n */\n private handleSubscribeFailure(\n target: SubscribeTarget,\n exhaustedLog: (...data: unknown[]) => void,\n status?: number,\n error?: unknown,\n ): void {\n const { queryId, subscription, connectionId, attempt } = target;\n const detail = error === undefined ? [status] : [error];\n\n if (!this.isCurrentSubscription(queryId, subscription, connectionId)) {\n const reason = error === undefined ? 'returned a failure' : 'failed';\n console.warn(\n `SSE hub: subscribe POST ${reason} for an obsolete connection or subscription; ignoring`,\n queryId,\n ...detail,\n );\n } else if (attempt < SUBSCRIBE_MAX_RETRIES) {\n this.scheduleSubscribeRetry(target);\n } else {\n const tail = error === undefined ? [status, attempt + 1] : [attempt + 1, error];\n exhaustedLog(\n 'SSE hub: subscribe POST failed after retries; reconnecting',\n queryId,\n ...tail,\n );\n this.reconnect();\n }\n }\n\n private scheduleSubscribeRetry(target: SubscribeTarget): void {\n const { queryId, subscription, connectionId, attempt } = target;\n setTimeout(() => {\n if (this.isCurrentSubscription(queryId, subscription, connectionId)) {\n this.sendSubscribe(queryId, subscription, attempt + 1);\n }\n }, SUBSCRIBE_RETRY_DELAY_MS * (attempt + 1));\n }\n\n private isMessageForCurrentSubscription(message: HubMessage): boolean {\n if (!message.queryId) return false;\n\n const subscription = this._subscriptions.get(message.queryId);\n if (!subscription) return false;\n\n if (!this._supportsSubscriptionRevisions) {\n return message.revision === undefined;\n }\n\n return (\n this.isValidRevision(message.revision) &&\n message.revision === subscription.revision\n );\n }\n\n private isCurrentSubscription(\n queryId: string,\n subscription: ActiveSubscription,\n connectionId: string,\n ): boolean {\n return (\n !this._disconnected &&\n this._connectionId === connectionId &&\n this._subscriptions.get(queryId) === subscription\n );\n }\n\n private getNextRevision(): number {\n if (this._nextRevision >= Number.MAX_SAFE_INTEGER) {\n throw new RangeError(\n 'SSE hub subscription revision exhausted the safe integer range',\n );\n }\n\n return ++this._nextRevision;\n }\n\n private isValidRevision(revision: number | undefined): revision is number {\n return revision !== undefined && Number.isSafeInteger(revision) && revision > 0;\n }\n\n private sendUnsubscribe(queryId: string, revision: number): void {\n if (!this._connectionId) return;\n\n const body: {\n connectionId: string;\n queryId: string;\n revision?: number;\n } = {\n connectionId: this._connectionId,\n queryId,\n };\n if (this._supportsSubscriptionRevisions) {\n body.revision = revision;\n }\n\n const customHeaders = Globals.httpHeadersCallback?.() ?? {};\n\n fetch(this._unsubscribeUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...customHeaders },\n body: JSON.stringify(body),\n }).catch((error) => {\n console.error('SSE hub: unsubscribe POST failed', queryId, error);\n });\n }\n}\n"],"names":["IDLE_THRESHOLD_FACTOR","SUBSCRIBE_MAX_RETRIES","SUBSCRIBE_RETRY_DELAY_MS","ServerSentEventHubConnection","_eventSource","_connectionId","_disconnected","_subscriptions","Map","_pendingSubscriptions","_lastPongLatency","_latencySamples","_connectTimeoutTimer","_nextRevision","_supportsSubscriptionRevisions","_keepAlive","_subscribeUrl","_unsubscribeUrl","keepAliveIntervalMs","_connectTimeoutMs","_policy","ReconnectPolicy","_sseUrl","_microservice","HubConnectionKeepAlive","size","console","warn","idleThresholdMs","reconnect","queryCount","isConnected","undefined","readyState","EventSource","OPEN","lastPingLatency","averageLatency","length","reduce","a","b","subscribe","queryId","request","callback","sub","revision","getNextRevision","set","ensureConnected","sendSubscribe","unsubscribe","subscription","get","delete","sendUnsubscribe","close","dispose","clear","cancel","stop","clearConnectTimeout","detachAndCloseEventSource","eventSource","onopen","onmessage","onerror","CLOSED","openEventSource","url","buildEventSourceUrl","Globals","eventSourceFactory","isActiveSource","log","reset","start","startConnectTimeout","event","recordActivity","handleMessage","data","param","microserviceWSQueryArgument","encodeURIComponent","includes","setTimeout","schedule","clearTimeout","rawData","message","JSON","parse","type","HubMessageType","Connected","handleConnected","QueryResult","handleQueryResult","Ping","Unauthorized","handleUnauthorizedMessage","Error","handleErrorMessage","error","isMessageForCurrentSubscription","handleUnauthorized","payload","handleError","connectionId","connectionIdChanged","supportsSubscriptionRevisions","applyServerKeepAliveInterval","serverIntervalMs","reconfigure","result","deliverResult","isValid","isSuccess","isReady","isAuthorized","hasExceptions","validationResults","exceptionMessages","exceptionStackTrace","paging","totalItems","totalPages","page","Object","unauthorized","terminal","attempt","body","customHeaders","httpHeadersCallback","target","fetch","method","headers","stringify","then","response","ok","handleSubscribeFailure","status","catch","exhaustedLog","detail","isCurrentSubscription","reason","scheduleSubscribeRetry","tail","isValidRevision","Number","MAX_SAFE_INTEGER","RangeError","isSafeInteger"],"mappings":"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"}
@@ -56,6 +56,12 @@ export declare class WebSocketHubConnection {
56
56
  private ensureConnected;
57
57
  private close;
58
58
  private openSocket;
59
+ private buildSocketUrl;
60
+ private isSocketLive;
61
+ private isActiveSocket;
62
+ private isOpenAndReleased;
63
+ private handleSocketOpened;
64
+ private handleSocketClosed;
59
65
  private cancelPendingReconnect;
60
66
  private detachAndCloseSocket;
61
67
  private resetSubscriptionRelease;
@@ -1 +1 @@
1
- {"version":3,"file":"WebSocketHubConnection.d.ts","sourceRoot":"","sources":["../../../queries/WebSocketHubConnection.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"WebSocketHubConnection.d.ts","sourceRoot":"","sources":["../../../queries/WebSocketHubConnection.ts"],"names":[],"mappings":"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"}
@@ -100,7 +100,7 @@ import { QueryResult } from './QueryResult.js';
100
100
  };
101
101
  this._subscriptions.set(queryId, subscription);
102
102
  this.ensureConnected();
103
- if (this._socket?.readyState === WebSocket.OPEN && this._subscriptionsReleased) {
103
+ if (this.isOpenAndReleased()) {
104
104
  this.sendSubscribeMessage(queryId, subscription);
105
105
  }
106
106
  // Until the server has said what it supports, subscriptions are held back and sent once, by releaseSubscriptions.
@@ -111,7 +111,7 @@ import { QueryResult } from './QueryResult.js';
111
111
  */ unsubscribe(queryId) {
112
112
  const subscription = this._subscriptions.get(queryId);
113
113
  this._subscriptions.delete(queryId);
114
- if (this._socket?.readyState === WebSocket.OPEN && this._subscriptionsReleased && subscription) {
114
+ if (this.isOpenAndReleased() && subscription) {
115
115
  const message = {
116
116
  type: "Unsubscribe",
117
117
  queryId
@@ -137,7 +137,7 @@ import { QueryResult } from './QueryResult.js';
137
137
  // Reset disconnected flag when a new subscription comes in
138
138
  this._disconnected = false;
139
139
  }
140
- if (this._socket && (this._socket.readyState === WebSocket.OPEN || this._socket.readyState === WebSocket.CONNECTING)) {
140
+ if (this.isSocketLive(this._socket)) {
141
141
  this.cancelPendingReconnect();
142
142
  return;
143
143
  }
@@ -161,7 +161,7 @@ import { QueryResult } from './QueryResult.js';
161
161
  }
162
162
  openSocket() {
163
163
  const currentSocket = this._socket;
164
- if (currentSocket && (currentSocket.readyState === WebSocket.OPEN || currentSocket.readyState === WebSocket.CONNECTING)) {
164
+ if (this.isSocketLive(currentSocket)) {
165
165
  return;
166
166
  }
167
167
  if (currentSocket) {
@@ -170,60 +170,79 @@ import { QueryResult } from './QueryResult.js';
170
170
  }
171
171
  this._supportsSubscriptionRevisions = false;
172
172
  this.resetSubscriptionRelease();
173
- let url = this._url;
174
- if (this._microservice?.length > 0) {
175
- const param = `${Globals.microserviceWSQueryArgument}=${encodeURIComponent(this._microservice)}`;
176
- url += (url.includes('?') ? '&' : '?') + param;
177
- }
173
+ const url = this.buildSocketUrl();
178
174
  const socket = new WebSocket(url);
179
175
  this._socket = socket;
180
176
  socket.onopen = ()=>{
181
- if (this._socket !== socket || this._disconnected) return;
182
- console.log('Hub connection established', url);
183
- this.cancelPendingReconnect();
184
- this._policy.reset();
185
- this._keepAlive.start();
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);
177
+ if (!this.isActiveSocket(socket)) return;
178
+ this.handleSocketOpened(socket, url);
196
179
  };
197
180
  socket.onclose = ()=>{
198
- if (this._socket !== socket || this._disconnected) return;
199
- console.log('Hub connection closed', url);
200
- this._socket = undefined;
201
- this._supportsSubscriptionRevisions = false;
202
- this.resetSubscriptionRelease();
203
- this._keepAlive.stop();
204
- if (this._subscriptions.size === 0) return;
205
- this._reconnectPending = true;
206
- const scheduled = this._policy.schedule(()=>{
207
- this._reconnectPending = false;
208
- if (!this._disconnected && this._subscriptions.size > 0) {
209
- this.ensureConnected();
210
- }
211
- }, this._url);
212
- if (!scheduled) {
213
- this._reconnectPending = false;
214
- }
181
+ if (!this.isActiveSocket(socket)) return;
182
+ this.handleSocketClosed(url);
215
183
  };
216
184
  socket.onerror = (error)=>{
217
- if (this._socket !== socket || this._disconnected) return;
185
+ if (!this.isActiveSocket(socket)) return;
218
186
  console.error('Hub connection error', url, error);
219
187
  this._keepAlive.stop();
220
188
  // onclose will fire after onerror, triggering reconnect
221
189
  };
222
190
  socket.onmessage = (ev)=>{
223
- if (this._socket !== socket || this._disconnected) return;
191
+ if (!this.isActiveSocket(socket)) return;
224
192
  this.handleMessage(ev.data);
225
193
  };
226
194
  }
195
+ buildSocketUrl() {
196
+ let url = this._url;
197
+ if (this._microservice?.length > 0) {
198
+ const param = `${Globals.microserviceWSQueryArgument}=${encodeURIComponent(this._microservice)}`;
199
+ url += (url.includes('?') ? '&' : '?') + param;
200
+ }
201
+ return url;
202
+ }
203
+ isSocketLive(socket) {
204
+ return socket !== undefined && (socket.readyState === WebSocket.OPEN || socket.readyState === WebSocket.CONNECTING);
205
+ }
206
+ isActiveSocket(socket) {
207
+ return this._socket === socket && !this._disconnected;
208
+ }
209
+ isOpenAndReleased() {
210
+ return this._socket?.readyState === WebSocket.OPEN && this._subscriptionsReleased;
211
+ }
212
+ handleSocketOpened(socket, url) {
213
+ console.log('Hub connection established', url);
214
+ this.cancelPendingReconnect();
215
+ this._policy.reset();
216
+ this._keepAlive.start();
217
+ // A capable server announces itself with a Connected message straight after accepting the socket. Sending
218
+ // the subscriptions before that would mean sending them twice (once without a revision, once with), and the
219
+ // server would run every query twice and cancel the first run. Wait for it, and only fall back to the
220
+ // revision-less form for a server that never says anything.
221
+ this._releaseTimer = setTimeout(()=>{
222
+ this._releaseTimer = undefined;
223
+ if (this.isActiveSocket(socket)) {
224
+ this.releaseSubscriptions();
225
+ }
226
+ }, WebSocketHubConnection.ConnectedGraceMs);
227
+ }
228
+ handleSocketClosed(url) {
229
+ console.log('Hub connection closed', url);
230
+ this._socket = undefined;
231
+ this._supportsSubscriptionRevisions = false;
232
+ this.resetSubscriptionRelease();
233
+ this._keepAlive.stop();
234
+ if (this._subscriptions.size === 0) return;
235
+ this._reconnectPending = true;
236
+ const scheduled = this._policy.schedule(()=>{
237
+ this._reconnectPending = false;
238
+ if (!this._disconnected && this._subscriptions.size > 0) {
239
+ this.ensureConnected();
240
+ }
241
+ }, this._url);
242
+ if (!scheduled) {
243
+ this._reconnectPending = false;
244
+ }
245
+ }
227
246
  cancelPendingReconnect() {
228
247
  if (!this._reconnectPending) return;
229
248
  this._policy.cancel();