import { HoistService, InitContext, PlainObject } from '@xh/hoist/core'; /** * Establishes and maintains a websocket connection to the Hoist server, if enabled via `AppSpec`. * * Once a connection is established, this service exposes a `channelKey` property that is unique to * this user and client app instance. This key can be used in application-specific requests to the * server to identify this unique client app instance / connection. The server can then push * messages as requested - e.g. when a particular query or dataset of interest is updated. * * Callers can register a callback via `subscribe()` to receive incoming messages on a requested * topic. The {@link WebSocketSubscription} returned from `subscribe()` can be used to later * `unsubscribe()` if updates are no longer desired. `HoistModel` and `HoistComponent` callers are * encouraged to save a reference to their subscription via a {@link managed} property to have * callbacks unsubscribed automatically when the component/model is unmounted/destroyed. * * This service also provides a `sendMessage()` method to push messages back to the server over the * same socket, although this is a relatively uncommon usage and is specifically *not* recommended * over plain-old Ajax requests. * * Note this service requires the server-side application to be configured to listen to inbound * websocket connections. See `WebSocketService.groovy` in hoist-core for additional documentation. * * See {@link WebSocketIndicator}, a simple component for displaying connection status. */ export declare class WebSocketService extends HoistService { static instance: WebSocketService; /** Check connection and send a new heartbeat (which should be promptly ack'd) every 10s. */ readonly HEARTBEAT_TOPIC = "xhHeartbeat"; readonly HEARTBEAT_INTERVAL: number; readonly REG_SUCCESS_TOPIC = "xhRegistrationSuccess"; readonly FORCE_APP_SUSPEND_TOPIC = "xhForceAppSuspend"; readonly REQ_CLIENT_HEALTH_RPT_TOPIC = "xhRequestClientHealthReport"; readonly METADATA_FOR_HANDSHAKE: string[]; /** True if WebSockets not explicitly disabled via {@link AppSpec.disableWebSockets}. */ enabled: boolean; /** Unique channel assigned by server upon successful connection. */ channelKey: string; /** Last time a message was received, including heartbeat messages. */ lastMessageTime: Date; /** Observable flag indicating service is connected and available for use. */ get connected(): boolean; /** Set to true to log all sent/received messages - very chatty. */ logMessages: boolean; telemetry: WebSocketTelemetry; private _timer; private _socket; private _subsByTopic; private _lastHeartbeatSent; private _lastHeartbeatReceived; constructor(); initAsync(ctx: InitContext): Promise; /** * Subscribe a callback to receive inbound messages for a given topic on a going-forward basis. * * @param topic - application-specific topic of interest. * @param fn - handler to call for each incoming message on the requested topic. * @returns subscription reference that can be used to unsubscribe to future messages for the * same topic/handler. Callers should take care to save this reference and use it to * dispose of their subs on destroy. */ subscribe(topic: string, fn: (msg: WebSocketMessage) => any): WebSocketSubscription; /** * Cancel a subscription for a given topic/handler. * @param subscription - WebSocketSubscription returned when the subscription was established. */ unsubscribe(subscription: WebSocketSubscription): void; /** * Send a message back to the server via the connected websocket. */ sendMessage(message: WebSocketMessage): void; shutdown(): void; getFormattedTelemetry(): PlainObject; private connect; private disconnect; private reconnect; private heartbeatOrReconnect; private get heartbeatWasUnacknowledged(); private onServerInstanceChange; onOpen(ev: any): void; onClose(ev: any): void; onError(ev: any): void; onMessage(rawMsg: MessageEvent): void; private notifySubscribers; private getSubsForTopic; private updateConnectedStatus; private installChannelKey; private updateLastMessageTime; private buildWebSocketUrl; private maybeLogMessage; private noteTelemetryEvent; private ensureEnabled; } /** * Wrapper class to encapsulate and manage a subscription to messages for a given topic + handler. * Returned from `WebSocketService.subscribe()` and used to `unsubscribe()`. */ export declare class WebSocketSubscription { topic: string; fn: (msg: WebSocketMessage) => any; constructor(topic: any, fn: any); destroy(): void; } export interface WebSocketMessage { topic: string; data?: any; } /** Telemetry collected by this service + included in {@link ClientHealthService} reporting. */ export interface WebSocketTelemetry { channelKey: string; subscriptionCount: number; events: { connOpened?: WebSocketEventTelemetry; connClosed?: WebSocketEventTelemetry; connError?: WebSocketEventTelemetry; msgReceived?: WebSocketEventTelemetry; msgSent?: WebSocketEventTelemetry; heartbeatReceived?: WebSocketEventTelemetry; heartbeatSent?: WebSocketEventTelemetry; heartbeatReconnectAttempt?: WebSocketEventTelemetry; instanceChangeReconnectAttempt?: WebSocketEventTelemetry; }; } export interface WebSocketEventTelemetry { count: number; lastTime: number; }