@pythnetwork/pyth-lazer-sdk 0.1.1 → 0.2.0

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.
@@ -0,0 +1,32 @@
1
+ import { type Logger } from "ts-log";
2
+ import { type ParsedPayload, type Request, type Response } from "./protocol.js";
3
+ import { WebSocketPool } from "./socket/web-socket-pool.js";
4
+ export type BinaryResponse = {
5
+ subscriptionId: number;
6
+ evm?: Buffer | undefined;
7
+ solana?: Buffer | undefined;
8
+ parsed?: ParsedPayload | undefined;
9
+ };
10
+ export type JsonOrBinaryResponse = {
11
+ type: "json";
12
+ value: Response;
13
+ } | {
14
+ type: "binary";
15
+ value: BinaryResponse;
16
+ };
17
+ export declare class PythLazerClient {
18
+ wsp: WebSocketPool;
19
+ /**
20
+ * Creates a new PythLazerClient instance.
21
+ * @param urls - List of WebSocket URLs of the Pyth Lazer service
22
+ * @param token - The access token for authentication
23
+ * @param numConnections - The number of parallel WebSocket connections to establish (default: 3). A higher number gives a more reliable stream.
24
+ * @param logger - Optional logger to get socket level logs. Compatible with most loggers such as the built-in console and `bunyan`.
25
+ */
26
+ constructor(urls: string[], token: string, numConnections?: number, logger?: Logger);
27
+ addMessageListener(handler: (event: JsonOrBinaryResponse) => void): void;
28
+ subscribe(request: Request): Promise<void>;
29
+ unsubscribe(subscriptionId: number): Promise<void>;
30
+ send(request: Request): Promise<void>;
31
+ shutdown(): void;
32
+ }
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PythLazerClient = void 0;
4
+ const ts_log_1 = require("ts-log");
5
+ const protocol_js_1 = require("./protocol.js");
6
+ const web_socket_pool_js_1 = require("./socket/web-socket-pool.js");
7
+ const UINT16_NUM_BYTES = 2;
8
+ const UINT32_NUM_BYTES = 4;
9
+ const UINT64_NUM_BYTES = 8;
10
+ class PythLazerClient {
11
+ wsp;
12
+ /**
13
+ * Creates a new PythLazerClient instance.
14
+ * @param urls - List of WebSocket URLs of the Pyth Lazer service
15
+ * @param token - The access token for authentication
16
+ * @param numConnections - The number of parallel WebSocket connections to establish (default: 3). A higher number gives a more reliable stream.
17
+ * @param logger - Optional logger to get socket level logs. Compatible with most loggers such as the built-in console and `bunyan`.
18
+ */
19
+ constructor(urls, token, numConnections = 3, logger = ts_log_1.dummyLogger) {
20
+ this.wsp = new web_socket_pool_js_1.WebSocketPool(urls, token, numConnections, logger);
21
+ }
22
+ addMessageListener(handler) {
23
+ this.wsp.addMessageListener((data) => {
24
+ if (typeof data == "string") {
25
+ handler({
26
+ type: "json",
27
+ value: JSON.parse(data),
28
+ });
29
+ }
30
+ else if (Buffer.isBuffer(data)) {
31
+ let pos = 0;
32
+ const magic = data.subarray(pos, pos + UINT32_NUM_BYTES).readUint32BE();
33
+ pos += UINT32_NUM_BYTES;
34
+ if (magic != protocol_js_1.BINARY_UPDATE_FORMAT_MAGIC) {
35
+ throw new Error("binary update format magic mismatch");
36
+ }
37
+ // TODO: some uint64 values may not be representable as Number.
38
+ const subscriptionId = Number(data.subarray(pos, pos + UINT64_NUM_BYTES).readBigInt64BE());
39
+ pos += UINT64_NUM_BYTES;
40
+ const value = { subscriptionId };
41
+ while (pos < data.length) {
42
+ const len = data.subarray(pos, pos + UINT16_NUM_BYTES).readUint16BE();
43
+ pos += UINT16_NUM_BYTES;
44
+ const magic = data
45
+ .subarray(pos, pos + UINT32_NUM_BYTES)
46
+ .readUint32BE();
47
+ if (magic == protocol_js_1.EVM_FORMAT_MAGIC) {
48
+ value.evm = data.subarray(pos, pos + len);
49
+ }
50
+ else if (magic == protocol_js_1.SOLANA_FORMAT_MAGIC_BE) {
51
+ value.solana = data.subarray(pos, pos + len);
52
+ }
53
+ else if (magic == protocol_js_1.PARSED_FORMAT_MAGIC) {
54
+ value.parsed = JSON.parse(data.subarray(pos + UINT32_NUM_BYTES, pos + len).toString());
55
+ }
56
+ else {
57
+ throw new Error("unknown magic: " + magic.toString());
58
+ }
59
+ pos += len;
60
+ }
61
+ handler({ type: "binary", value });
62
+ }
63
+ else {
64
+ throw new TypeError("unexpected event data type");
65
+ }
66
+ });
67
+ }
68
+ async subscribe(request) {
69
+ if (request.type !== "subscribe") {
70
+ throw new Error("Request must be a subscribe request");
71
+ }
72
+ await this.wsp.addSubscription(request);
73
+ }
74
+ async unsubscribe(subscriptionId) {
75
+ await this.wsp.removeSubscription(subscriptionId);
76
+ }
77
+ async send(request) {
78
+ await this.wsp.sendRequest(request);
79
+ }
80
+ shutdown() {
81
+ this.wsp.shutdown();
82
+ }
83
+ }
84
+ exports.PythLazerClient = PythLazerClient;
@@ -0,0 +1,2 @@
1
+ import { TransactionInstruction } from "@solana/web3.js";
2
+ export declare const createEd25519Instruction: (message: Buffer, instructionIndex: number, startingOffset: number) => TransactionInstruction;
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.createEd25519Instruction = void 0;
27
+ const BufferLayout = __importStar(require("@solana/buffer-layout"));
28
+ const web3_js_1 = require("@solana/web3.js");
29
+ const ED25519_INSTRUCTION_LEN = 16;
30
+ const SIGNATURE_LEN = 64;
31
+ const PUBKEY_LEN = 32;
32
+ const MAGIC_LEN = 4;
33
+ const MESSAGE_SIZE_LEN = 2;
34
+ const ED25519_INSTRUCTION_LAYOUT = BufferLayout.struct([
35
+ BufferLayout.u8("numSignatures"),
36
+ BufferLayout.u8("padding"),
37
+ BufferLayout.u16("signatureOffset"),
38
+ BufferLayout.u16("signatureInstructionIndex"),
39
+ BufferLayout.u16("publicKeyOffset"),
40
+ BufferLayout.u16("publicKeyInstructionIndex"),
41
+ BufferLayout.u16("messageDataOffset"),
42
+ BufferLayout.u16("messageDataSize"),
43
+ BufferLayout.u16("messageInstructionIndex"),
44
+ ]);
45
+ const createEd25519Instruction = (message, instructionIndex, startingOffset) => {
46
+ const signatureOffset = startingOffset + MAGIC_LEN;
47
+ const publicKeyOffset = signatureOffset + SIGNATURE_LEN;
48
+ const messageDataSizeOffset = publicKeyOffset + PUBKEY_LEN;
49
+ const messageDataOffset = messageDataSizeOffset + MESSAGE_SIZE_LEN;
50
+ const messageDataSize = message.readUInt16LE(messageDataSizeOffset - startingOffset);
51
+ const instructionData = Buffer.alloc(ED25519_INSTRUCTION_LEN);
52
+ ED25519_INSTRUCTION_LAYOUT.encode({
53
+ numSignatures: 1,
54
+ padding: 0,
55
+ signatureOffset,
56
+ signatureInstructionIndex: instructionIndex,
57
+ publicKeyOffset,
58
+ publicKeyInstructionIndex: instructionIndex,
59
+ messageDataOffset,
60
+ messageDataSize: messageDataSize,
61
+ messageInstructionIndex: instructionIndex,
62
+ }, instructionData);
63
+ return new web3_js_1.TransactionInstruction({
64
+ keys: [],
65
+ programId: web3_js_1.Ed25519Program.programId,
66
+ data: instructionData,
67
+ });
68
+ };
69
+ exports.createEd25519Instruction = createEd25519Instruction;
@@ -1,21 +1,3 @@
1
- import WebSocket from "isomorphic-ws";
2
- import { type ParsedPayload, type Request, type Response } from "./protocol.js";
3
- export type BinaryResponse = {
4
- subscriptionId: number;
5
- evm?: Buffer | undefined;
6
- solana?: Buffer | undefined;
7
- parsed?: ParsedPayload | undefined;
8
- };
9
- export type JsonOrBinaryResponse = {
10
- type: "json";
11
- value: Response;
12
- } | {
13
- type: "binary";
14
- value: BinaryResponse;
15
- };
16
- export declare class PythLazerClient {
17
- ws: WebSocket;
18
- constructor(url: string, token: string);
19
- addMessageListener(handler: (event: JsonOrBinaryResponse) => void): void;
20
- send(request: Request): void;
21
- }
1
+ export * from "./client.js";
2
+ export * from "./protocol.js";
3
+ export * from "./ed25519.js";
package/dist/cjs/index.js CHANGED
@@ -1,73 +1,19 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
4
15
  };
5
16
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.PythLazerClient = void 0;
7
- const isomorphic_ws_1 = __importDefault(require("isomorphic-ws"));
8
- const protocol_js_1 = require("./protocol.js");
9
- const UINT16_NUM_BYTES = 2;
10
- const UINT32_NUM_BYTES = 4;
11
- const UINT64_NUM_BYTES = 8;
12
- class PythLazerClient {
13
- ws;
14
- constructor(url, token) {
15
- const finalUrl = new URL(url);
16
- finalUrl.searchParams.append("ACCESS_TOKEN", token);
17
- this.ws = new isomorphic_ws_1.default(finalUrl);
18
- }
19
- addMessageListener(handler) {
20
- this.ws.addEventListener("message", (event) => {
21
- if (typeof event.data == "string") {
22
- handler({
23
- type: "json",
24
- value: JSON.parse(event.data),
25
- });
26
- }
27
- else if (Buffer.isBuffer(event.data)) {
28
- let pos = 0;
29
- const magic = event.data
30
- .subarray(pos, pos + UINT32_NUM_BYTES)
31
- .readUint32BE();
32
- pos += UINT32_NUM_BYTES;
33
- if (magic != protocol_js_1.BINARY_UPDATE_FORMAT_MAGIC) {
34
- throw new Error("binary update format magic mismatch");
35
- }
36
- // TODO: some uint64 values may not be representable as Number.
37
- const subscriptionId = Number(event.data.subarray(pos, pos + UINT64_NUM_BYTES).readBigInt64BE());
38
- pos += UINT64_NUM_BYTES;
39
- const value = { subscriptionId };
40
- while (pos < event.data.length) {
41
- const len = event.data
42
- .subarray(pos, pos + UINT16_NUM_BYTES)
43
- .readUint16BE();
44
- pos += UINT16_NUM_BYTES;
45
- const magic = event.data
46
- .subarray(pos, pos + UINT32_NUM_BYTES)
47
- .readUint32BE();
48
- if (magic == protocol_js_1.EVM_FORMAT_MAGIC) {
49
- value.evm = event.data.subarray(pos, pos + len);
50
- }
51
- else if (magic == protocol_js_1.SOLANA_FORMAT_MAGIC_BE) {
52
- value.solana = event.data.subarray(pos, pos + len);
53
- }
54
- else if (magic == protocol_js_1.PARSED_FORMAT_MAGIC) {
55
- value.parsed = JSON.parse(event.data.subarray(pos + UINT32_NUM_BYTES, pos + len).toString());
56
- }
57
- else {
58
- throw new Error("unknown magic: " + magic.toString());
59
- }
60
- pos += len;
61
- }
62
- handler({ type: "binary", value });
63
- }
64
- else {
65
- throw new TypeError("unexpected event data type");
66
- }
67
- });
68
- }
69
- send(request) {
70
- this.ws.send(JSON.stringify(request));
71
- }
72
- }
73
- exports.PythLazerClient = PythLazerClient;
17
+ __exportStar(require("./client.js"), exports);
18
+ __exportStar(require("./protocol.js"), exports);
19
+ __exportStar(require("./ed25519.js"), exports);
@@ -0,0 +1,38 @@
1
+ import type { ClientRequestArgs } from "node:http";
2
+ import WebSocket, { type ClientOptions, type ErrorEvent } from "isomorphic-ws";
3
+ import type { Logger } from "ts-log";
4
+ /**
5
+ * This class wraps websocket to provide a resilient web socket client.
6
+ *
7
+ * It will reconnect if connection fails with exponential backoff. Also, it will reconnect
8
+ * if it receives no ping request or regular message from server within a while as indication
9
+ * of timeout (assuming the server sends either regularly).
10
+ *
11
+ * This class also logs events if logger is given and by replacing onError method you can handle
12
+ * connection errors yourself (e.g: do not retry and close the connection).
13
+ */
14
+ export declare class ResilientWebSocket {
15
+ endpoint: string;
16
+ wsClient: undefined | WebSocket;
17
+ wsUserClosed: boolean;
18
+ private wsOptions;
19
+ private wsFailedAttempts;
20
+ private heartbeatTimeout;
21
+ private logger;
22
+ onError: (error: ErrorEvent) => void;
23
+ onMessage: (data: WebSocket.Data) => void;
24
+ onReconnect: () => void;
25
+ constructor(endpoint: string, wsOptions?: ClientOptions | ClientRequestArgs, logger?: Logger);
26
+ send(data: string | Buffer): Promise<void>;
27
+ startWebSocket(): void;
28
+ /**
29
+ * Reset the heartbeat timeout. This is called when we receive any message (ping or regular)
30
+ * from the server. If we don't receive any message within HEARTBEAT_TIMEOUT_DURATION,
31
+ * we assume the connection is dead and reconnect.
32
+ */
33
+ private resetHeartbeat;
34
+ private waitForMaybeReadyWebSocket;
35
+ private handleClose;
36
+ private restartUnexpectedClosedWebsocket;
37
+ closeWebSocket(): void;
38
+ }
@@ -0,0 +1,161 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ResilientWebSocket = void 0;
7
+ const isomorphic_ws_1 = __importDefault(require("isomorphic-ws"));
8
+ // Reconnect with expo backoff if we don't get a message or ping for 10 seconds
9
+ const HEARTBEAT_TIMEOUT_DURATION = 10_000;
10
+ /**
11
+ * This class wraps websocket to provide a resilient web socket client.
12
+ *
13
+ * It will reconnect if connection fails with exponential backoff. Also, it will reconnect
14
+ * if it receives no ping request or regular message from server within a while as indication
15
+ * of timeout (assuming the server sends either regularly).
16
+ *
17
+ * This class also logs events if logger is given and by replacing onError method you can handle
18
+ * connection errors yourself (e.g: do not retry and close the connection).
19
+ */
20
+ class ResilientWebSocket {
21
+ endpoint;
22
+ wsClient;
23
+ wsUserClosed;
24
+ wsOptions;
25
+ wsFailedAttempts;
26
+ heartbeatTimeout;
27
+ logger;
28
+ onError;
29
+ onMessage;
30
+ onReconnect;
31
+ constructor(endpoint, wsOptions, logger) {
32
+ this.endpoint = endpoint;
33
+ this.wsOptions = wsOptions;
34
+ this.logger = logger;
35
+ this.wsFailedAttempts = 0;
36
+ this.onError = (error) => {
37
+ this.logger?.error(error.error);
38
+ };
39
+ this.wsUserClosed = true;
40
+ this.onMessage = (data) => {
41
+ void data;
42
+ };
43
+ this.onReconnect = () => {
44
+ // Empty function, can be set by the user.
45
+ };
46
+ }
47
+ async send(data) {
48
+ this.logger?.info(`Sending message`);
49
+ await this.waitForMaybeReadyWebSocket();
50
+ if (this.wsClient === undefined) {
51
+ this.logger?.error("Couldn't connect to the websocket server. Error callback is called.");
52
+ }
53
+ else {
54
+ this.wsClient.send(data);
55
+ }
56
+ }
57
+ startWebSocket() {
58
+ if (this.wsClient !== undefined) {
59
+ return;
60
+ }
61
+ this.logger?.info(`Creating Web Socket client`);
62
+ this.wsClient = new isomorphic_ws_1.default(this.endpoint, this.wsOptions);
63
+ this.wsUserClosed = false;
64
+ this.wsClient.addEventListener("open", () => {
65
+ this.wsFailedAttempts = 0;
66
+ this.resetHeartbeat();
67
+ });
68
+ this.wsClient.addEventListener("error", (event) => {
69
+ this.onError(event);
70
+ });
71
+ this.wsClient.addEventListener("message", (event) => {
72
+ this.resetHeartbeat();
73
+ this.onMessage(event.data);
74
+ });
75
+ this.wsClient.addEventListener("close", () => {
76
+ void this.handleClose();
77
+ });
78
+ // Handle ping events if supported (Node.js only)
79
+ if ("on" in this.wsClient) {
80
+ // Ping handler is undefined in browser side
81
+ this.wsClient.on("ping", () => {
82
+ this.logger?.info("Ping received");
83
+ this.resetHeartbeat();
84
+ });
85
+ }
86
+ }
87
+ /**
88
+ * Reset the heartbeat timeout. This is called when we receive any message (ping or regular)
89
+ * from the server. If we don't receive any message within HEARTBEAT_TIMEOUT_DURATION,
90
+ * we assume the connection is dead and reconnect.
91
+ */
92
+ resetHeartbeat() {
93
+ if (this.heartbeatTimeout !== undefined) {
94
+ clearTimeout(this.heartbeatTimeout);
95
+ }
96
+ this.heartbeatTimeout = setTimeout(() => {
97
+ this.logger?.warn("Connection timed out. Reconnecting...");
98
+ this.wsClient?.terminate();
99
+ void this.restartUnexpectedClosedWebsocket();
100
+ }, HEARTBEAT_TIMEOUT_DURATION);
101
+ }
102
+ async waitForMaybeReadyWebSocket() {
103
+ let waitedTime = 0;
104
+ while (this.wsClient !== undefined &&
105
+ this.wsClient.readyState !== this.wsClient.OPEN) {
106
+ if (waitedTime > 5000) {
107
+ this.wsClient.close();
108
+ return;
109
+ }
110
+ else {
111
+ waitedTime += 10;
112
+ await sleep(10);
113
+ }
114
+ }
115
+ }
116
+ async handleClose() {
117
+ if (this.heartbeatTimeout !== undefined) {
118
+ clearTimeout(this.heartbeatTimeout);
119
+ }
120
+ if (this.wsUserClosed) {
121
+ this.logger?.info("The connection has been closed successfully.");
122
+ }
123
+ else {
124
+ this.wsFailedAttempts += 1;
125
+ this.wsClient = undefined;
126
+ const waitTime = expoBackoff(this.wsFailedAttempts);
127
+ this.logger?.error("Connection closed unexpectedly or because of timeout. Reconnecting after " +
128
+ String(waitTime) +
129
+ "ms.");
130
+ await sleep(waitTime);
131
+ await this.restartUnexpectedClosedWebsocket();
132
+ }
133
+ }
134
+ async restartUnexpectedClosedWebsocket() {
135
+ if (this.wsUserClosed) {
136
+ return;
137
+ }
138
+ this.startWebSocket();
139
+ await this.waitForMaybeReadyWebSocket();
140
+ if (this.wsClient === undefined) {
141
+ this.logger?.error("Couldn't reconnect to websocket. Error callback is called.");
142
+ return;
143
+ }
144
+ this.onReconnect();
145
+ }
146
+ closeWebSocket() {
147
+ if (this.wsClient !== undefined) {
148
+ const client = this.wsClient;
149
+ this.wsClient = undefined;
150
+ client.close();
151
+ }
152
+ this.wsUserClosed = true;
153
+ }
154
+ }
155
+ exports.ResilientWebSocket = ResilientWebSocket;
156
+ async function sleep(ms) {
157
+ return new Promise((resolve) => setTimeout(resolve, ms));
158
+ }
159
+ function expoBackoff(attempts) {
160
+ return 2 ** attempts * 100;
161
+ }
@@ -0,0 +1,55 @@
1
+ import WebSocket from "isomorphic-ws";
2
+ import { type Logger } from "ts-log";
3
+ import { ResilientWebSocket } from "./resilient-web-socket.js";
4
+ import type { Request } from "../protocol.js";
5
+ export declare class WebSocketPool {
6
+ private readonly logger;
7
+ rwsPool: ResilientWebSocket[];
8
+ private cache;
9
+ private subscriptions;
10
+ private messageListeners;
11
+ /**
12
+ * Creates a new WebSocketPool instance that uses multiple redundant WebSocket connections for reliability.
13
+ * Usage semantics are similar to using a regular WebSocket client.
14
+ * @param urls - List of WebSocket URLs to connect to
15
+ * @param token - Authentication token to use for the connections
16
+ * @param numConnections - Number of parallel WebSocket connections to maintain (default: 3)
17
+ * @param logger - Optional logger to get socket level logs. Compatible with most loggers such as the built-in console and `bunyan`.
18
+ */
19
+ constructor(urls: string[], token: string, numConnections?: number, logger?: Logger);
20
+ /**
21
+ * Checks for error responses in JSON messages and throws appropriate errors
22
+ */
23
+ private handleErrorMessages;
24
+ /**
25
+ * Handles incoming websocket messages by deduplicating identical messages received across
26
+ * multiple connections before forwarding to registered handlers
27
+ */
28
+ dedupeHandler: (data: WebSocket.Data) => void;
29
+ /**
30
+ * Sends a message to all websockets in the pool
31
+ * @param request - The request to send
32
+ */
33
+ sendRequest(request: Request): Promise<void>;
34
+ /**
35
+ * Adds a subscription by sending a subscribe request to all websockets in the pool
36
+ * and storing it for replay on reconnection
37
+ * @param request - The subscription request to send
38
+ */
39
+ addSubscription(request: Request): Promise<void>;
40
+ /**
41
+ * Removes a subscription by sending an unsubscribe request to all websockets in the pool
42
+ * and removing it from stored subscriptions
43
+ * @param subscriptionId - The ID of the subscription to remove
44
+ */
45
+ removeSubscription(subscriptionId: number): Promise<void>;
46
+ /**
47
+ * Adds a message handler function to receive websocket messages
48
+ * @param handler - Function that will be called with each received message
49
+ */
50
+ addMessageListener(handler: (data: WebSocket.Data) => void): void;
51
+ /**
52
+ * Elegantly closes all websocket connections in the pool
53
+ */
54
+ shutdown(): void;
55
+ }