@supabase/realtime-js 2.99.3-canary.0 → 2.99.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main/RealtimeChannel.d.ts +28 -35
- package/dist/main/RealtimeChannel.d.ts.map +1 -1
- package/dist/main/RealtimeChannel.js +301 -140
- package/dist/main/RealtimeChannel.js.map +1 -1
- package/dist/main/RealtimeClient.d.ts +57 -38
- package/dist/main/RealtimeClient.d.ts.map +1 -1
- package/dist/main/RealtimeClient.js +520 -232
- package/dist/main/RealtimeClient.js.map +1 -1
- package/dist/main/RealtimePresence.d.ts +24 -8
- package/dist/main/RealtimePresence.d.ts.map +1 -1
- package/dist/main/RealtimePresence.js +202 -6
- package/dist/main/RealtimePresence.js.map +1 -1
- package/dist/main/lib/constants.d.ts +35 -39
- package/dist/main/lib/constants.d.ts.map +1 -1
- package/dist/main/lib/constants.js +35 -30
- package/dist/main/lib/constants.js.map +1 -1
- package/dist/main/lib/push.d.ts +48 -0
- package/dist/main/lib/push.d.ts.map +1 -0
- package/dist/main/lib/push.js +102 -0
- package/dist/main/lib/push.js.map +1 -0
- package/dist/main/lib/timer.d.ts +22 -0
- package/dist/main/lib/timer.d.ts.map +1 -0
- package/dist/main/lib/timer.js +39 -0
- package/dist/main/lib/timer.js.map +1 -0
- package/dist/main/lib/version.d.ts +1 -1
- package/dist/main/lib/version.d.ts.map +1 -1
- package/dist/main/lib/version.js +1 -1
- package/dist/main/lib/version.js.map +1 -1
- package/dist/main/lib/websocket-factory.d.ts +9 -0
- package/dist/main/lib/websocket-factory.d.ts.map +1 -1
- package/dist/main/lib/websocket-factory.js +12 -0
- package/dist/main/lib/websocket-factory.js.map +1 -1
- package/dist/module/RealtimeChannel.d.ts +28 -35
- package/dist/module/RealtimeChannel.d.ts.map +1 -1
- package/dist/module/RealtimeChannel.js +302 -141
- package/dist/module/RealtimeChannel.js.map +1 -1
- package/dist/module/RealtimeClient.d.ts +57 -38
- package/dist/module/RealtimeClient.d.ts.map +1 -1
- package/dist/module/RealtimeClient.js +521 -233
- package/dist/module/RealtimeClient.js.map +1 -1
- package/dist/module/RealtimePresence.d.ts +24 -8
- package/dist/module/RealtimePresence.d.ts.map +1 -1
- package/dist/module/RealtimePresence.js +202 -5
- package/dist/module/RealtimePresence.js.map +1 -1
- package/dist/module/lib/constants.d.ts +35 -39
- package/dist/module/lib/constants.d.ts.map +1 -1
- package/dist/module/lib/constants.js +35 -30
- package/dist/module/lib/constants.js.map +1 -1
- package/dist/module/lib/push.d.ts +48 -0
- package/dist/module/lib/push.d.ts.map +1 -0
- package/dist/module/lib/push.js +99 -0
- package/dist/module/lib/push.js.map +1 -0
- package/dist/module/lib/timer.d.ts +22 -0
- package/dist/module/lib/timer.d.ts.map +1 -0
- package/dist/module/lib/timer.js +36 -0
- package/dist/module/lib/timer.js.map +1 -0
- package/dist/module/lib/version.d.ts +1 -1
- package/dist/module/lib/version.d.ts.map +1 -1
- package/dist/module/lib/version.js +1 -1
- package/dist/module/lib/version.js.map +1 -1
- package/dist/module/lib/websocket-factory.d.ts +9 -0
- package/dist/module/lib/websocket-factory.d.ts.map +1 -1
- package/dist/module/lib/websocket-factory.js +12 -0
- package/dist/module/lib/websocket-factory.js.map +1 -1
- package/dist/tsconfig.module.tsbuildinfo +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/src/RealtimeChannel.ts +364 -201
- package/src/RealtimeClient.ts +583 -296
- package/src/RealtimePresence.ts +287 -10
- package/src/lib/constants.ts +37 -50
- package/src/lib/push.ts +121 -0
- package/src/lib/timer.ts +43 -0
- package/src/lib/version.ts +1 -1
- package/src/lib/websocket-factory.ts +13 -0
- package/dist/main/phoenix/channelAdapter.d.ts +0 -32
- package/dist/main/phoenix/channelAdapter.d.ts.map +0 -1
- package/dist/main/phoenix/channelAdapter.js +0 -103
- package/dist/main/phoenix/channelAdapter.js.map +0 -1
- package/dist/main/phoenix/presenceAdapter.d.ts +0 -53
- package/dist/main/phoenix/presenceAdapter.d.ts.map +0 -1
- package/dist/main/phoenix/presenceAdapter.js +0 -93
- package/dist/main/phoenix/presenceAdapter.js.map +0 -1
- package/dist/main/phoenix/socketAdapter.d.ts +0 -38
- package/dist/main/phoenix/socketAdapter.d.ts.map +0 -1
- package/dist/main/phoenix/socketAdapter.js +0 -114
- package/dist/main/phoenix/socketAdapter.js.map +0 -1
- package/dist/main/phoenix/types.d.ts +0 -5
- package/dist/main/phoenix/types.d.ts.map +0 -1
- package/dist/main/phoenix/types.js +0 -3
- package/dist/main/phoenix/types.js.map +0 -1
- package/dist/module/phoenix/channelAdapter.d.ts +0 -32
- package/dist/module/phoenix/channelAdapter.d.ts.map +0 -1
- package/dist/module/phoenix/channelAdapter.js +0 -100
- package/dist/module/phoenix/channelAdapter.js.map +0 -1
- package/dist/module/phoenix/presenceAdapter.d.ts +0 -53
- package/dist/module/phoenix/presenceAdapter.d.ts.map +0 -1
- package/dist/module/phoenix/presenceAdapter.js +0 -90
- package/dist/module/phoenix/presenceAdapter.js.map +0 -1
- package/dist/module/phoenix/socketAdapter.d.ts +0 -38
- package/dist/module/phoenix/socketAdapter.d.ts.map +0 -1
- package/dist/module/phoenix/socketAdapter.js +0 -111
- package/dist/module/phoenix/socketAdapter.js.map +0 -1
- package/dist/module/phoenix/types.d.ts +0 -5
- package/dist/module/phoenix/types.d.ts.map +0 -1
- package/dist/module/phoenix/types.js +0 -2
- package/dist/module/phoenix/types.js.map +0 -1
- package/src/phoenix/channelAdapter.ts +0 -147
- package/src/phoenix/presenceAdapter.ts +0 -116
- package/src/phoenix/socketAdapter.ts +0 -168
- package/src/phoenix/types.ts +0 -32
|
@@ -4,9 +4,10 @@ const tslib_1 = require("tslib");
|
|
|
4
4
|
const websocket_factory_1 = tslib_1.__importDefault(require("./lib/websocket-factory"));
|
|
5
5
|
const constants_1 = require("./lib/constants");
|
|
6
6
|
const serializer_1 = tslib_1.__importDefault(require("./lib/serializer"));
|
|
7
|
+
const timer_1 = tslib_1.__importDefault(require("./lib/timer"));
|
|
7
8
|
const transformers_1 = require("./lib/transformers");
|
|
8
9
|
const RealtimeChannel_1 = tslib_1.__importDefault(require("./RealtimeChannel"));
|
|
9
|
-
const
|
|
10
|
+
const noop = () => { };
|
|
10
11
|
// Connection-related constants
|
|
11
12
|
const CONNECTION_TIMEOUTS = {
|
|
12
13
|
HEARTBEAT_INTERVAL: 25000,
|
|
@@ -22,54 +23,6 @@ const WORKER_SCRIPT = `
|
|
|
22
23
|
}
|
|
23
24
|
});`;
|
|
24
25
|
class RealtimeClient {
|
|
25
|
-
get endPoint() {
|
|
26
|
-
return this.socketAdapter.endPoint;
|
|
27
|
-
}
|
|
28
|
-
get timeout() {
|
|
29
|
-
return this.socketAdapter.timeout;
|
|
30
|
-
}
|
|
31
|
-
get transport() {
|
|
32
|
-
return this.socketAdapter.transport;
|
|
33
|
-
}
|
|
34
|
-
get heartbeatCallback() {
|
|
35
|
-
return this.socketAdapter.heartbeatCallback;
|
|
36
|
-
}
|
|
37
|
-
get heartbeatIntervalMs() {
|
|
38
|
-
return this.socketAdapter.heartbeatIntervalMs;
|
|
39
|
-
}
|
|
40
|
-
get heartbeatTimer() {
|
|
41
|
-
if (this.worker) {
|
|
42
|
-
return this._workerHeartbeatTimer;
|
|
43
|
-
}
|
|
44
|
-
return this.socketAdapter.heartbeatTimer;
|
|
45
|
-
}
|
|
46
|
-
get pendingHeartbeatRef() {
|
|
47
|
-
if (this.worker) {
|
|
48
|
-
return this._pendingWorkerHeartbeatRef;
|
|
49
|
-
}
|
|
50
|
-
return this.socketAdapter.pendingHeartbeatRef;
|
|
51
|
-
}
|
|
52
|
-
get reconnectTimer() {
|
|
53
|
-
return this.socketAdapter.reconnectTimer;
|
|
54
|
-
}
|
|
55
|
-
get vsn() {
|
|
56
|
-
return this.socketAdapter.vsn;
|
|
57
|
-
}
|
|
58
|
-
get encode() {
|
|
59
|
-
return this.socketAdapter.encode;
|
|
60
|
-
}
|
|
61
|
-
get decode() {
|
|
62
|
-
return this.socketAdapter.decode;
|
|
63
|
-
}
|
|
64
|
-
get reconnectAfterMs() {
|
|
65
|
-
return this.socketAdapter.reconnectAfterMs;
|
|
66
|
-
}
|
|
67
|
-
get sendBuffer() {
|
|
68
|
-
return this.socketAdapter.sendBuffer;
|
|
69
|
-
}
|
|
70
|
-
get stateChangeCallbacks() {
|
|
71
|
-
return this.socketAdapter.stateChangeCallbacks;
|
|
72
|
-
}
|
|
73
26
|
/**
|
|
74
27
|
* Initializes the Socket.
|
|
75
28
|
*
|
|
@@ -101,20 +54,39 @@ class RealtimeClient {
|
|
|
101
54
|
*/
|
|
102
55
|
constructor(endPoint, options) {
|
|
103
56
|
var _a;
|
|
104
|
-
this.channels = new Array();
|
|
105
57
|
this.accessTokenValue = null;
|
|
106
|
-
this.accessToken = null;
|
|
107
58
|
this.apiKey = null;
|
|
59
|
+
this._manuallySetToken = false;
|
|
60
|
+
this.channels = new Array();
|
|
61
|
+
this.endPoint = '';
|
|
108
62
|
this.httpEndpoint = '';
|
|
109
63
|
/** @deprecated headers cannot be set on websocket connections */
|
|
110
64
|
this.headers = {};
|
|
111
65
|
this.params = {};
|
|
66
|
+
this.timeout = constants_1.DEFAULT_TIMEOUT;
|
|
67
|
+
this.transport = null;
|
|
68
|
+
this.heartbeatIntervalMs = CONNECTION_TIMEOUTS.HEARTBEAT_INTERVAL;
|
|
69
|
+
this.heartbeatTimer = undefined;
|
|
70
|
+
this.pendingHeartbeatRef = null;
|
|
71
|
+
this.heartbeatCallback = noop;
|
|
112
72
|
this.ref = 0;
|
|
73
|
+
this.reconnectTimer = null;
|
|
74
|
+
this.vsn = constants_1.DEFAULT_VSN;
|
|
75
|
+
this.logger = noop;
|
|
76
|
+
this.conn = null;
|
|
77
|
+
this.sendBuffer = [];
|
|
113
78
|
this.serializer = new serializer_1.default();
|
|
114
|
-
this.
|
|
79
|
+
this.stateChangeCallbacks = {
|
|
80
|
+
open: [],
|
|
81
|
+
close: [],
|
|
82
|
+
error: [],
|
|
83
|
+
message: [],
|
|
84
|
+
};
|
|
85
|
+
this.accessToken = null;
|
|
86
|
+
this._connectionState = 'disconnected';
|
|
87
|
+
this._wasManualDisconnect = false;
|
|
115
88
|
this._authPromise = null;
|
|
116
|
-
this.
|
|
117
|
-
this._pendingWorkerHeartbeatRef = null;
|
|
89
|
+
this._heartbeatSentAt = null;
|
|
118
90
|
/**
|
|
119
91
|
* Use either custom fetch, if provided, or default fetch to make HTTP requests
|
|
120
92
|
*
|
|
@@ -131,9 +103,11 @@ class RealtimeClient {
|
|
|
131
103
|
throw new Error('API key is required to connect to Realtime');
|
|
132
104
|
}
|
|
133
105
|
this.apiKey = options.params.apikey;
|
|
134
|
-
|
|
135
|
-
this.
|
|
106
|
+
// Initialize endpoint URLs
|
|
107
|
+
this.endPoint = `${endPoint}/${constants_1.TRANSPORTS.websocket}`;
|
|
136
108
|
this.httpEndpoint = (0, transformers_1.httpEndpointURL)(endPoint);
|
|
109
|
+
this._initializeOptions(options);
|
|
110
|
+
this._setupReconnectionTimer();
|
|
137
111
|
this.fetch = this._resolveFetch(options === null || options === void 0 ? void 0 : options.fetch);
|
|
138
112
|
}
|
|
139
113
|
/**
|
|
@@ -141,44 +115,55 @@ class RealtimeClient {
|
|
|
141
115
|
*/
|
|
142
116
|
connect() {
|
|
143
117
|
// Skip if already connecting, disconnecting, or connected
|
|
144
|
-
if (this.isConnecting() ||
|
|
118
|
+
if (this.isConnecting() ||
|
|
119
|
+
this.isDisconnecting() ||
|
|
120
|
+
(this.conn !== null && this.isConnected())) {
|
|
145
121
|
return;
|
|
146
122
|
}
|
|
123
|
+
this._setConnectionState('connecting');
|
|
147
124
|
// Trigger auth if needed and not already in progress
|
|
148
125
|
// This ensures auth is called for standalone RealtimeClient usage
|
|
149
126
|
// while avoiding race conditions with SupabaseClient's immediate setAuth call
|
|
150
127
|
if (this.accessToken && !this._authPromise) {
|
|
151
128
|
this._setAuthSafely('connect');
|
|
152
129
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
//
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
130
|
+
// Establish WebSocket connection
|
|
131
|
+
if (this.transport) {
|
|
132
|
+
// Use custom transport if provided
|
|
133
|
+
this.conn = new this.transport(this.endpointURL());
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
// Try to use native WebSocket
|
|
137
|
+
try {
|
|
138
|
+
this.conn = websocket_factory_1.default.createWebSocket(this.endpointURL());
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
this._setConnectionState('disconnected');
|
|
142
|
+
const errorMessage = error.message;
|
|
143
|
+
// Provide helpful error message based on environment
|
|
144
|
+
if (errorMessage.includes('Node.js')) {
|
|
145
|
+
throw new Error(`${errorMessage}\n\n` +
|
|
146
|
+
'To use Realtime in Node.js, you need to provide a WebSocket implementation:\n\n' +
|
|
147
|
+
'Option 1: Use Node.js 22+ which has native WebSocket support\n' +
|
|
148
|
+
'Option 2: Install and provide the "ws" package:\n\n' +
|
|
149
|
+
' npm install ws\n\n' +
|
|
150
|
+
' import ws from "ws"\n' +
|
|
151
|
+
' const client = new RealtimeClient(url, {\n' +
|
|
152
|
+
' ...options,\n' +
|
|
153
|
+
' transport: ws\n' +
|
|
154
|
+
' })');
|
|
155
|
+
}
|
|
156
|
+
throw new Error(`WebSocket not available: ${errorMessage}`);
|
|
171
157
|
}
|
|
172
|
-
throw new Error(`WebSocket not available: ${errorMessage}`);
|
|
173
158
|
}
|
|
174
|
-
this.
|
|
159
|
+
this._setupConnectionHandlers();
|
|
175
160
|
}
|
|
176
161
|
/**
|
|
177
162
|
* Returns the URL of the websocket.
|
|
178
163
|
* @returns string The URL of the websocket.
|
|
179
164
|
*/
|
|
180
165
|
endpointURL() {
|
|
181
|
-
return this.
|
|
166
|
+
return this._appendParams(this.endPoint, Object.assign({}, this.params, { vsn: this.vsn }));
|
|
182
167
|
}
|
|
183
168
|
/**
|
|
184
169
|
* Disconnects the socket.
|
|
@@ -186,14 +171,34 @@ class RealtimeClient {
|
|
|
186
171
|
* @param code A numeric status code to send on disconnect.
|
|
187
172
|
* @param reason A custom reason for the disconnect.
|
|
188
173
|
*/
|
|
189
|
-
|
|
174
|
+
disconnect(code, reason) {
|
|
190
175
|
if (this.isDisconnecting()) {
|
|
191
|
-
return
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
this._setConnectionState('disconnecting', true);
|
|
179
|
+
if (this.conn) {
|
|
180
|
+
// Setup fallback timer to prevent hanging in disconnecting state
|
|
181
|
+
const fallbackTimer = setTimeout(() => {
|
|
182
|
+
this._setConnectionState('disconnected');
|
|
183
|
+
}, 100);
|
|
184
|
+
this.conn.onclose = () => {
|
|
185
|
+
clearTimeout(fallbackTimer);
|
|
186
|
+
this._setConnectionState('disconnected');
|
|
187
|
+
};
|
|
188
|
+
// Close the WebSocket connection if close method exists
|
|
189
|
+
if (typeof this.conn.close === 'function') {
|
|
190
|
+
if (code) {
|
|
191
|
+
this.conn.close(code, reason !== null && reason !== void 0 ? reason : '');
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
this.conn.close();
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
this._teardownConnection();
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
this._setConnectionState('disconnected');
|
|
192
201
|
}
|
|
193
|
-
return await this.socketAdapter.disconnect(() => {
|
|
194
|
-
clearInterval(this._workerHeartbeatTimer);
|
|
195
|
-
this._terminateWorker();
|
|
196
|
-
}, code, reason);
|
|
197
202
|
}
|
|
198
203
|
/**
|
|
199
204
|
* Returns all created channels
|
|
@@ -202,63 +207,65 @@ class RealtimeClient {
|
|
|
202
207
|
return this.channels;
|
|
203
208
|
}
|
|
204
209
|
/**
|
|
205
|
-
* Unsubscribes
|
|
210
|
+
* Unsubscribes and removes a single channel
|
|
206
211
|
* @param channel A RealtimeChannel instance
|
|
207
212
|
*/
|
|
208
213
|
async removeChannel(channel) {
|
|
209
214
|
const status = await channel.unsubscribe();
|
|
210
|
-
if (status === 'ok') {
|
|
211
|
-
channel.teardown();
|
|
212
|
-
}
|
|
213
215
|
if (this.channels.length === 0) {
|
|
214
216
|
this.disconnect();
|
|
215
217
|
}
|
|
216
218
|
return status;
|
|
217
219
|
}
|
|
218
220
|
/**
|
|
219
|
-
* Unsubscribes
|
|
221
|
+
* Unsubscribes and removes all channels
|
|
220
222
|
*/
|
|
221
223
|
async removeAllChannels() {
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
channel.teardown();
|
|
225
|
-
return result;
|
|
226
|
-
});
|
|
227
|
-
const result = await Promise.all(promises);
|
|
224
|
+
const values_1 = await Promise.all(this.channels.map((channel) => channel.unsubscribe()));
|
|
225
|
+
this.channels = [];
|
|
228
226
|
this.disconnect();
|
|
229
|
-
return
|
|
227
|
+
return values_1;
|
|
230
228
|
}
|
|
231
229
|
/**
|
|
232
230
|
* Logs the message.
|
|
233
231
|
*
|
|
234
|
-
* For customized logging, `this.logger` can be overridden
|
|
232
|
+
* For customized logging, `this.logger` can be overridden.
|
|
235
233
|
*/
|
|
236
234
|
log(kind, msg, data) {
|
|
237
|
-
this.
|
|
235
|
+
this.logger(kind, msg, data);
|
|
238
236
|
}
|
|
239
237
|
/**
|
|
240
238
|
* Returns the current state of the socket.
|
|
241
239
|
*/
|
|
242
240
|
connectionState() {
|
|
243
|
-
|
|
241
|
+
switch (this.conn && this.conn.readyState) {
|
|
242
|
+
case constants_1.SOCKET_STATES.connecting:
|
|
243
|
+
return constants_1.CONNECTION_STATE.Connecting;
|
|
244
|
+
case constants_1.SOCKET_STATES.open:
|
|
245
|
+
return constants_1.CONNECTION_STATE.Open;
|
|
246
|
+
case constants_1.SOCKET_STATES.closing:
|
|
247
|
+
return constants_1.CONNECTION_STATE.Closing;
|
|
248
|
+
default:
|
|
249
|
+
return constants_1.CONNECTION_STATE.Closed;
|
|
250
|
+
}
|
|
244
251
|
}
|
|
245
252
|
/**
|
|
246
253
|
* Returns `true` is the connection is open.
|
|
247
254
|
*/
|
|
248
255
|
isConnected() {
|
|
249
|
-
return this.
|
|
256
|
+
return this.connectionState() === constants_1.CONNECTION_STATE.Open;
|
|
250
257
|
}
|
|
251
258
|
/**
|
|
252
259
|
* Returns `true` if the connection is currently connecting.
|
|
253
260
|
*/
|
|
254
261
|
isConnecting() {
|
|
255
|
-
return this.
|
|
262
|
+
return this._connectionState === 'connecting';
|
|
256
263
|
}
|
|
257
264
|
/**
|
|
258
265
|
* Returns `true` if the connection is currently disconnecting.
|
|
259
266
|
*/
|
|
260
267
|
isDisconnecting() {
|
|
261
|
-
return this.
|
|
268
|
+
return this._connectionState === 'disconnecting';
|
|
262
269
|
}
|
|
263
270
|
/**
|
|
264
271
|
* Creates (or reuses) a {@link RealtimeChannel} for the provided topic.
|
|
@@ -285,7 +292,20 @@ class RealtimeClient {
|
|
|
285
292
|
* If the socket is not connected, the message gets enqueued within a local buffer, and sent out when a connection is next established.
|
|
286
293
|
*/
|
|
287
294
|
push(data) {
|
|
288
|
-
|
|
295
|
+
const { topic, event, payload, ref } = data;
|
|
296
|
+
const callback = () => {
|
|
297
|
+
this.encode(data, (result) => {
|
|
298
|
+
var _a;
|
|
299
|
+
(_a = this.conn) === null || _a === void 0 ? void 0 : _a.send(result);
|
|
300
|
+
});
|
|
301
|
+
};
|
|
302
|
+
this.log('push', `${topic} ${event} (${ref})`, payload);
|
|
303
|
+
if (this.isConnected()) {
|
|
304
|
+
callback();
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
this.sendBuffer.push(callback);
|
|
308
|
+
}
|
|
289
309
|
}
|
|
290
310
|
/**
|
|
291
311
|
* Sets the JWT access token used for channel subscription authorization and Realtime RLS.
|
|
@@ -328,14 +348,70 @@ class RealtimeClient {
|
|
|
328
348
|
* Sends a heartbeat message if the socket is connected.
|
|
329
349
|
*/
|
|
330
350
|
async sendHeartbeat() {
|
|
331
|
-
|
|
351
|
+
var _a;
|
|
352
|
+
if (!this.isConnected()) {
|
|
353
|
+
try {
|
|
354
|
+
this.heartbeatCallback('disconnected');
|
|
355
|
+
}
|
|
356
|
+
catch (e) {
|
|
357
|
+
this.log('error', 'error in heartbeat callback', e);
|
|
358
|
+
}
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
// Handle heartbeat timeout and force reconnection if needed
|
|
362
|
+
if (this.pendingHeartbeatRef) {
|
|
363
|
+
this.pendingHeartbeatRef = null;
|
|
364
|
+
this._heartbeatSentAt = null;
|
|
365
|
+
this.log('transport', 'heartbeat timeout. Attempting to re-establish connection');
|
|
366
|
+
try {
|
|
367
|
+
this.heartbeatCallback('timeout');
|
|
368
|
+
}
|
|
369
|
+
catch (e) {
|
|
370
|
+
this.log('error', 'error in heartbeat callback', e);
|
|
371
|
+
}
|
|
372
|
+
// Force reconnection after heartbeat timeout
|
|
373
|
+
this._wasManualDisconnect = false;
|
|
374
|
+
(_a = this.conn) === null || _a === void 0 ? void 0 : _a.close(constants_1.WS_CLOSE_NORMAL, 'heartbeat timeout');
|
|
375
|
+
setTimeout(() => {
|
|
376
|
+
var _a;
|
|
377
|
+
if (!this.isConnected()) {
|
|
378
|
+
(_a = this.reconnectTimer) === null || _a === void 0 ? void 0 : _a.scheduleTimeout();
|
|
379
|
+
}
|
|
380
|
+
}, CONNECTION_TIMEOUTS.HEARTBEAT_TIMEOUT_FALLBACK);
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
// Send heartbeat message to server
|
|
384
|
+
this._heartbeatSentAt = Date.now();
|
|
385
|
+
this.pendingHeartbeatRef = this._makeRef();
|
|
386
|
+
this.push({
|
|
387
|
+
topic: 'phoenix',
|
|
388
|
+
event: 'heartbeat',
|
|
389
|
+
payload: {},
|
|
390
|
+
ref: this.pendingHeartbeatRef,
|
|
391
|
+
});
|
|
392
|
+
try {
|
|
393
|
+
this.heartbeatCallback('sent');
|
|
394
|
+
}
|
|
395
|
+
catch (e) {
|
|
396
|
+
this.log('error', 'error in heartbeat callback', e);
|
|
397
|
+
}
|
|
398
|
+
this._setAuthSafely('heartbeat');
|
|
332
399
|
}
|
|
333
400
|
/**
|
|
334
401
|
* Sets a callback that receives lifecycle events for internal heartbeat messages.
|
|
335
402
|
* Useful for instrumenting connection health (e.g. sent/ok/timeout/disconnected).
|
|
336
403
|
*/
|
|
337
404
|
onHeartbeat(callback) {
|
|
338
|
-
this.
|
|
405
|
+
this.heartbeatCallback = callback;
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Flushes send buffer
|
|
409
|
+
*/
|
|
410
|
+
flushSendBuffer() {
|
|
411
|
+
if (this.isConnected() && this.sendBuffer.length > 0) {
|
|
412
|
+
this.sendBuffer.forEach((callback) => callback());
|
|
413
|
+
this.sendBuffer = [];
|
|
414
|
+
}
|
|
339
415
|
}
|
|
340
416
|
/**
|
|
341
417
|
* Return the next message ref, accounting for overflows
|
|
@@ -343,10 +419,29 @@ class RealtimeClient {
|
|
|
343
419
|
* @internal
|
|
344
420
|
*/
|
|
345
421
|
_makeRef() {
|
|
346
|
-
|
|
422
|
+
let newRef = this.ref + 1;
|
|
423
|
+
if (newRef === this.ref) {
|
|
424
|
+
this.ref = 0;
|
|
425
|
+
}
|
|
426
|
+
else {
|
|
427
|
+
this.ref = newRef;
|
|
428
|
+
}
|
|
429
|
+
return this.ref.toString();
|
|
347
430
|
}
|
|
348
431
|
/**
|
|
349
|
-
*
|
|
432
|
+
* Unsubscribe from channels with the specified topic.
|
|
433
|
+
*
|
|
434
|
+
* @internal
|
|
435
|
+
*/
|
|
436
|
+
_leaveOpenTopic(topic) {
|
|
437
|
+
let dupChannel = this.channels.find((c) => c.topic === topic && (c._isJoined() || c._isJoining()));
|
|
438
|
+
if (dupChannel) {
|
|
439
|
+
this.log('transport', `leaving duplicate topic "${topic}"`);
|
|
440
|
+
dupChannel.unsubscribe();
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Removes a subscription from the socket.
|
|
350
445
|
*
|
|
351
446
|
* @param channel An open subscription.
|
|
352
447
|
*
|
|
@@ -355,6 +450,262 @@ class RealtimeClient {
|
|
|
355
450
|
_remove(channel) {
|
|
356
451
|
this.channels = this.channels.filter((c) => c.topic !== channel.topic);
|
|
357
452
|
}
|
|
453
|
+
/** @internal */
|
|
454
|
+
_onConnMessage(rawMessage) {
|
|
455
|
+
this.decode(rawMessage.data, (msg) => {
|
|
456
|
+
// Handle heartbeat responses
|
|
457
|
+
if (msg.topic === 'phoenix' &&
|
|
458
|
+
msg.event === 'phx_reply' &&
|
|
459
|
+
msg.ref &&
|
|
460
|
+
msg.ref === this.pendingHeartbeatRef) {
|
|
461
|
+
const latency = this._heartbeatSentAt ? Date.now() - this._heartbeatSentAt : undefined;
|
|
462
|
+
try {
|
|
463
|
+
this.heartbeatCallback(msg.payload.status === 'ok' ? 'ok' : 'error', latency);
|
|
464
|
+
}
|
|
465
|
+
catch (e) {
|
|
466
|
+
this.log('error', 'error in heartbeat callback', e);
|
|
467
|
+
}
|
|
468
|
+
this._heartbeatSentAt = null;
|
|
469
|
+
this.pendingHeartbeatRef = null;
|
|
470
|
+
}
|
|
471
|
+
// Log incoming message
|
|
472
|
+
const { topic, event, payload, ref } = msg;
|
|
473
|
+
const refString = ref ? `(${ref})` : '';
|
|
474
|
+
const status = payload.status || '';
|
|
475
|
+
this.log('receive', `${status} ${topic} ${event} ${refString}`.trim(), payload);
|
|
476
|
+
// Route message to appropriate channels
|
|
477
|
+
this.channels
|
|
478
|
+
.filter((channel) => channel._isMember(topic))
|
|
479
|
+
.forEach((channel) => channel._trigger(event, payload, ref));
|
|
480
|
+
this._triggerStateCallbacks('message', msg);
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Clear specific timer
|
|
485
|
+
* @internal
|
|
486
|
+
*/
|
|
487
|
+
_clearTimer(timer) {
|
|
488
|
+
var _a;
|
|
489
|
+
if (timer === 'heartbeat' && this.heartbeatTimer) {
|
|
490
|
+
clearInterval(this.heartbeatTimer);
|
|
491
|
+
this.heartbeatTimer = undefined;
|
|
492
|
+
}
|
|
493
|
+
else if (timer === 'reconnect') {
|
|
494
|
+
(_a = this.reconnectTimer) === null || _a === void 0 ? void 0 : _a.reset();
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* Clear all timers
|
|
499
|
+
* @internal
|
|
500
|
+
*/
|
|
501
|
+
_clearAllTimers() {
|
|
502
|
+
this._clearTimer('heartbeat');
|
|
503
|
+
this._clearTimer('reconnect');
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* Setup connection handlers for WebSocket events
|
|
507
|
+
* @internal
|
|
508
|
+
*/
|
|
509
|
+
_setupConnectionHandlers() {
|
|
510
|
+
if (!this.conn)
|
|
511
|
+
return;
|
|
512
|
+
// Set binary type if supported (browsers and most WebSocket implementations)
|
|
513
|
+
if ('binaryType' in this.conn) {
|
|
514
|
+
;
|
|
515
|
+
this.conn.binaryType = 'arraybuffer';
|
|
516
|
+
}
|
|
517
|
+
this.conn.onopen = () => this._onConnOpen();
|
|
518
|
+
this.conn.onerror = (error) => this._onConnError(error);
|
|
519
|
+
this.conn.onmessage = (event) => this._onConnMessage(event);
|
|
520
|
+
this.conn.onclose = (event) => this._onConnClose(event);
|
|
521
|
+
if (this.conn.readyState === constants_1.SOCKET_STATES.open) {
|
|
522
|
+
this._onConnOpen();
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Teardown connection and cleanup resources
|
|
527
|
+
* @internal
|
|
528
|
+
*/
|
|
529
|
+
_teardownConnection() {
|
|
530
|
+
if (this.conn) {
|
|
531
|
+
if (this.conn.readyState === constants_1.SOCKET_STATES.open ||
|
|
532
|
+
this.conn.readyState === constants_1.SOCKET_STATES.connecting) {
|
|
533
|
+
try {
|
|
534
|
+
this.conn.close();
|
|
535
|
+
}
|
|
536
|
+
catch (e) {
|
|
537
|
+
this.log('error', 'Error closing connection', e);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
this.conn.onopen = null;
|
|
541
|
+
this.conn.onerror = null;
|
|
542
|
+
this.conn.onmessage = null;
|
|
543
|
+
this.conn.onclose = null;
|
|
544
|
+
this.conn = null;
|
|
545
|
+
}
|
|
546
|
+
this._clearAllTimers();
|
|
547
|
+
this._terminateWorker();
|
|
548
|
+
this.channels.forEach((channel) => channel.teardown());
|
|
549
|
+
}
|
|
550
|
+
/** @internal */
|
|
551
|
+
_onConnOpen() {
|
|
552
|
+
this._setConnectionState('connected');
|
|
553
|
+
this.log('transport', `connected to ${this.endpointURL()}`);
|
|
554
|
+
// Wait for any pending auth operations before flushing send buffer
|
|
555
|
+
// This ensures channel join messages include the correct access token
|
|
556
|
+
const authPromise = this._authPromise ||
|
|
557
|
+
(this.accessToken && !this.accessTokenValue ? this.setAuth() : Promise.resolve());
|
|
558
|
+
authPromise
|
|
559
|
+
.then(() => {
|
|
560
|
+
// When subscribe() is called before the accessToken callback has
|
|
561
|
+
// resolved (common on React Native / Expo where token storage is
|
|
562
|
+
// async), the phx_join payload captured at subscribe()-time will
|
|
563
|
+
// have no access_token. By this point auth has settled and
|
|
564
|
+
// this.accessTokenValue holds the real JWT.
|
|
565
|
+
//
|
|
566
|
+
// The stale join messages sitting in sendBuffer captured the old
|
|
567
|
+
// (token-less) payload in a closure, so we cannot simply flush
|
|
568
|
+
// them. Instead we:
|
|
569
|
+
// 1. Patch each channel's joinPush payload with the real token
|
|
570
|
+
// 2. Drop the stale buffered messages
|
|
571
|
+
// 3. Re-send the join for any channel still in "joining" state
|
|
572
|
+
//
|
|
573
|
+
// On browsers this is a harmless no-op: accessTokenValue was
|
|
574
|
+
// already set synchronously before subscribe() ran, so the join
|
|
575
|
+
// payload already had the correct token.
|
|
576
|
+
if (this.accessTokenValue) {
|
|
577
|
+
this.channels.forEach((channel) => {
|
|
578
|
+
channel.updateJoinPayload({ access_token: this.accessTokenValue });
|
|
579
|
+
});
|
|
580
|
+
this.sendBuffer = [];
|
|
581
|
+
this.channels.forEach((channel) => {
|
|
582
|
+
if (channel._isJoining()) {
|
|
583
|
+
channel.joinPush.sent = false;
|
|
584
|
+
channel.joinPush.send();
|
|
585
|
+
}
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
this.flushSendBuffer();
|
|
589
|
+
})
|
|
590
|
+
.catch((e) => {
|
|
591
|
+
this.log('error', 'error waiting for auth on connect', e);
|
|
592
|
+
// Proceed anyway to avoid hanging connections
|
|
593
|
+
this.flushSendBuffer();
|
|
594
|
+
});
|
|
595
|
+
this._clearTimer('reconnect');
|
|
596
|
+
if (!this.worker) {
|
|
597
|
+
this._startHeartbeat();
|
|
598
|
+
}
|
|
599
|
+
else {
|
|
600
|
+
if (!this.workerRef) {
|
|
601
|
+
this._startWorkerHeartbeat();
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
this._triggerStateCallbacks('open');
|
|
605
|
+
}
|
|
606
|
+
/** @internal */
|
|
607
|
+
_startHeartbeat() {
|
|
608
|
+
this.heartbeatTimer && clearInterval(this.heartbeatTimer);
|
|
609
|
+
this.heartbeatTimer = setInterval(() => this.sendHeartbeat(), this.heartbeatIntervalMs);
|
|
610
|
+
}
|
|
611
|
+
/** @internal */
|
|
612
|
+
_startWorkerHeartbeat() {
|
|
613
|
+
if (this.workerUrl) {
|
|
614
|
+
this.log('worker', `starting worker for from ${this.workerUrl}`);
|
|
615
|
+
}
|
|
616
|
+
else {
|
|
617
|
+
this.log('worker', `starting default worker`);
|
|
618
|
+
}
|
|
619
|
+
const objectUrl = this._workerObjectUrl(this.workerUrl);
|
|
620
|
+
this.workerRef = new Worker(objectUrl);
|
|
621
|
+
this.workerRef.onerror = (error) => {
|
|
622
|
+
this.log('worker', 'worker error', error.message);
|
|
623
|
+
this._terminateWorker();
|
|
624
|
+
};
|
|
625
|
+
this.workerRef.onmessage = (event) => {
|
|
626
|
+
if (event.data.event === 'keepAlive') {
|
|
627
|
+
this.sendHeartbeat();
|
|
628
|
+
}
|
|
629
|
+
};
|
|
630
|
+
this.workerRef.postMessage({
|
|
631
|
+
event: 'start',
|
|
632
|
+
interval: this.heartbeatIntervalMs,
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* Terminate the Web Worker and clear the reference
|
|
637
|
+
* @internal
|
|
638
|
+
*/
|
|
639
|
+
_terminateWorker() {
|
|
640
|
+
if (this.workerRef) {
|
|
641
|
+
this.log('worker', 'terminating worker');
|
|
642
|
+
this.workerRef.terminate();
|
|
643
|
+
this.workerRef = undefined;
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
/** @internal */
|
|
647
|
+
_onConnClose(event) {
|
|
648
|
+
var _a;
|
|
649
|
+
this._setConnectionState('disconnected');
|
|
650
|
+
this.log('transport', 'close', event);
|
|
651
|
+
this._triggerChanError();
|
|
652
|
+
this._clearTimer('heartbeat');
|
|
653
|
+
// Only schedule reconnection if it wasn't a manual disconnect
|
|
654
|
+
if (!this._wasManualDisconnect) {
|
|
655
|
+
(_a = this.reconnectTimer) === null || _a === void 0 ? void 0 : _a.scheduleTimeout();
|
|
656
|
+
}
|
|
657
|
+
this._triggerStateCallbacks('close', event);
|
|
658
|
+
}
|
|
659
|
+
/** @internal */
|
|
660
|
+
_onConnError(error) {
|
|
661
|
+
this._setConnectionState('disconnected');
|
|
662
|
+
this.log('transport', `${error}`);
|
|
663
|
+
this._triggerChanError();
|
|
664
|
+
this._triggerStateCallbacks('error', error);
|
|
665
|
+
try {
|
|
666
|
+
this.heartbeatCallback('error');
|
|
667
|
+
}
|
|
668
|
+
catch (e) {
|
|
669
|
+
this.log('error', 'error in heartbeat callback', e);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
/** @internal */
|
|
673
|
+
_triggerChanError() {
|
|
674
|
+
this.channels.forEach((channel) => channel._trigger(constants_1.CHANNEL_EVENTS.error));
|
|
675
|
+
}
|
|
676
|
+
/** @internal */
|
|
677
|
+
_appendParams(url, params) {
|
|
678
|
+
if (Object.keys(params).length === 0) {
|
|
679
|
+
return url;
|
|
680
|
+
}
|
|
681
|
+
const prefix = url.match(/\?/) ? '&' : '?';
|
|
682
|
+
const query = new URLSearchParams(params);
|
|
683
|
+
return `${url}${prefix}${query}`;
|
|
684
|
+
}
|
|
685
|
+
_workerObjectUrl(url) {
|
|
686
|
+
let result_url;
|
|
687
|
+
if (url) {
|
|
688
|
+
result_url = url;
|
|
689
|
+
}
|
|
690
|
+
else {
|
|
691
|
+
const blob = new Blob([WORKER_SCRIPT], { type: 'application/javascript' });
|
|
692
|
+
result_url = URL.createObjectURL(blob);
|
|
693
|
+
}
|
|
694
|
+
return result_url;
|
|
695
|
+
}
|
|
696
|
+
/**
|
|
697
|
+
* Set connection state with proper state management
|
|
698
|
+
* @internal
|
|
699
|
+
*/
|
|
700
|
+
_setConnectionState(state, manual = false) {
|
|
701
|
+
this._connectionState = state;
|
|
702
|
+
if (state === 'connecting') {
|
|
703
|
+
this._wasManualDisconnect = false;
|
|
704
|
+
}
|
|
705
|
+
else if (state === 'disconnecting') {
|
|
706
|
+
this._wasManualDisconnect = manual;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
358
709
|
/**
|
|
359
710
|
* Perform the actual auth operation
|
|
360
711
|
* @internal
|
|
@@ -397,8 +748,8 @@ class RealtimeClient {
|
|
|
397
748
|
version: constants_1.DEFAULT_VERSION,
|
|
398
749
|
};
|
|
399
750
|
tokenToSend && channel.updateJoinPayload(payload);
|
|
400
|
-
if (channel.joinedOnce && channel.
|
|
401
|
-
channel.
|
|
751
|
+
if (channel.joinedOnce && channel._isJoined()) {
|
|
752
|
+
channel._push(constants_1.CHANNEL_EVENTS.access_token, {
|
|
402
753
|
access_token: tokenToSend,
|
|
403
754
|
});
|
|
404
755
|
}
|
|
@@ -426,139 +777,85 @@ class RealtimeClient {
|
|
|
426
777
|
});
|
|
427
778
|
}
|
|
428
779
|
}
|
|
429
|
-
/**
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
780
|
+
/**
|
|
781
|
+
* Trigger state change callbacks with proper error handling
|
|
782
|
+
* @internal
|
|
783
|
+
*/
|
|
784
|
+
_triggerStateCallbacks(event, data) {
|
|
785
|
+
try {
|
|
786
|
+
this.stateChangeCallbacks[event].forEach((callback) => {
|
|
787
|
+
try {
|
|
788
|
+
callback(data);
|
|
789
|
+
}
|
|
790
|
+
catch (e) {
|
|
791
|
+
this.log('error', `error in ${event} callback`, e);
|
|
792
|
+
}
|
|
436
793
|
});
|
|
437
|
-
if (this.worker && !this.workerRef) {
|
|
438
|
-
this._startWorkerHeartbeat();
|
|
439
|
-
}
|
|
440
|
-
});
|
|
441
|
-
this.socketAdapter.onClose(() => {
|
|
442
|
-
if (this.worker && this.workerRef) {
|
|
443
|
-
this._terminateWorker();
|
|
444
|
-
}
|
|
445
|
-
});
|
|
446
|
-
this.socketAdapter.onMessage((message) => {
|
|
447
|
-
if (message.ref && message.ref === this._pendingWorkerHeartbeatRef) {
|
|
448
|
-
this._pendingWorkerHeartbeatRef = null;
|
|
449
|
-
}
|
|
450
|
-
});
|
|
451
|
-
}
|
|
452
|
-
/** @internal */
|
|
453
|
-
_handleNodeJsRaceCondition() {
|
|
454
|
-
if (this.socketAdapter.isConnected()) {
|
|
455
|
-
// hack: ensure onConnOpen is called
|
|
456
|
-
this.socketAdapter.getSocket().onConnOpen();
|
|
457
794
|
}
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
_wrapHeartbeatCallback(heartbeatCallback) {
|
|
461
|
-
return (status, latency) => {
|
|
462
|
-
if (status == 'sent')
|
|
463
|
-
this._setAuthSafely();
|
|
464
|
-
if (heartbeatCallback)
|
|
465
|
-
heartbeatCallback(status, latency);
|
|
466
|
-
};
|
|
467
|
-
}
|
|
468
|
-
/** @internal */
|
|
469
|
-
_startWorkerHeartbeat() {
|
|
470
|
-
if (this.workerUrl) {
|
|
471
|
-
this.log('worker', `starting worker for from ${this.workerUrl}`);
|
|
472
|
-
}
|
|
473
|
-
else {
|
|
474
|
-
this.log('worker', `starting default worker`);
|
|
795
|
+
catch (e) {
|
|
796
|
+
this.log('error', `error triggering ${event} callbacks`, e);
|
|
475
797
|
}
|
|
476
|
-
const objectUrl = this._workerObjectUrl(this.workerUrl);
|
|
477
|
-
this.workerRef = new Worker(objectUrl);
|
|
478
|
-
this.workerRef.onerror = (error) => {
|
|
479
|
-
this.log('worker', 'worker error', error.message);
|
|
480
|
-
this._terminateWorker();
|
|
481
|
-
this.disconnect();
|
|
482
|
-
};
|
|
483
|
-
this.workerRef.onmessage = (event) => {
|
|
484
|
-
if (event.data.event === 'keepAlive') {
|
|
485
|
-
this.sendHeartbeat();
|
|
486
|
-
}
|
|
487
|
-
};
|
|
488
|
-
this.workerRef.postMessage({
|
|
489
|
-
event: 'start',
|
|
490
|
-
interval: this.heartbeatIntervalMs,
|
|
491
|
-
});
|
|
492
798
|
}
|
|
493
799
|
/**
|
|
494
|
-
*
|
|
800
|
+
* Setup reconnection timer with proper configuration
|
|
495
801
|
* @internal
|
|
496
802
|
*/
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
let result_url;
|
|
507
|
-
if (url) {
|
|
508
|
-
result_url = url;
|
|
509
|
-
}
|
|
510
|
-
else {
|
|
511
|
-
const blob = new Blob([WORKER_SCRIPT], { type: 'application/javascript' });
|
|
512
|
-
result_url = URL.createObjectURL(blob);
|
|
513
|
-
}
|
|
514
|
-
return result_url;
|
|
803
|
+
_setupReconnectionTimer() {
|
|
804
|
+
this.reconnectTimer = new timer_1.default(async () => {
|
|
805
|
+
setTimeout(async () => {
|
|
806
|
+
await this._waitForAuthIfNeeded();
|
|
807
|
+
if (!this.isConnected()) {
|
|
808
|
+
this.connect();
|
|
809
|
+
}
|
|
810
|
+
}, CONNECTION_TIMEOUTS.RECONNECT_DELAY);
|
|
811
|
+
}, this.reconnectAfterMs);
|
|
515
812
|
}
|
|
516
813
|
/**
|
|
517
|
-
* Initialize
|
|
814
|
+
* Initialize client options with defaults
|
|
518
815
|
* @internal
|
|
519
816
|
*/
|
|
520
817
|
_initializeOptions(options) {
|
|
521
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
522
|
-
|
|
523
|
-
this.
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
818
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
819
|
+
// Set defaults
|
|
820
|
+
this.transport = (_a = options === null || options === void 0 ? void 0 : options.transport) !== null && _a !== void 0 ? _a : null;
|
|
821
|
+
this.timeout = (_b = options === null || options === void 0 ? void 0 : options.timeout) !== null && _b !== void 0 ? _b : constants_1.DEFAULT_TIMEOUT;
|
|
822
|
+
this.heartbeatIntervalMs =
|
|
823
|
+
(_c = options === null || options === void 0 ? void 0 : options.heartbeatIntervalMs) !== null && _c !== void 0 ? _c : CONNECTION_TIMEOUTS.HEARTBEAT_INTERVAL;
|
|
824
|
+
this.worker = (_d = options === null || options === void 0 ? void 0 : options.worker) !== null && _d !== void 0 ? _d : false;
|
|
825
|
+
this.accessToken = (_e = options === null || options === void 0 ? void 0 : options.accessToken) !== null && _e !== void 0 ? _e : null;
|
|
826
|
+
this.heartbeatCallback = (_f = options === null || options === void 0 ? void 0 : options.heartbeatCallback) !== null && _f !== void 0 ? _f : noop;
|
|
827
|
+
this.vsn = (_g = options === null || options === void 0 ? void 0 : options.vsn) !== null && _g !== void 0 ? _g : constants_1.DEFAULT_VSN;
|
|
828
|
+
// Handle special cases
|
|
829
|
+
if (options === null || options === void 0 ? void 0 : options.params)
|
|
830
|
+
this.params = options.params;
|
|
831
|
+
if (options === null || options === void 0 ? void 0 : options.logger)
|
|
832
|
+
this.logger = options.logger;
|
|
833
|
+
if ((options === null || options === void 0 ? void 0 : options.logLevel) || (options === null || options === void 0 ? void 0 : options.log_level)) {
|
|
834
|
+
this.logLevel = options.logLevel || options.log_level;
|
|
835
|
+
this.params = Object.assign(Object.assign({}, this.params), { log_level: this.logLevel });
|
|
836
|
+
}
|
|
837
|
+
// Set up functions with defaults
|
|
838
|
+
this.reconnectAfterMs =
|
|
839
|
+
(_h = options === null || options === void 0 ? void 0 : options.reconnectAfterMs) !== null && _h !== void 0 ? _h : ((tries) => {
|
|
536
840
|
return RECONNECT_INTERVALS[tries - 1] || DEFAULT_RECONNECT_FALLBACK;
|
|
537
841
|
});
|
|
538
|
-
|
|
539
|
-
let defaultDecode;
|
|
540
|
-
switch (result.vsn) {
|
|
842
|
+
switch (this.vsn) {
|
|
541
843
|
case constants_1.VSN_1_0_0:
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
844
|
+
this.encode =
|
|
845
|
+
(_j = options === null || options === void 0 ? void 0 : options.encode) !== null && _j !== void 0 ? _j : ((payload, callback) => {
|
|
846
|
+
return callback(JSON.stringify(payload));
|
|
847
|
+
});
|
|
848
|
+
this.decode =
|
|
849
|
+
(_k = options === null || options === void 0 ? void 0 : options.decode) !== null && _k !== void 0 ? _k : ((payload, callback) => {
|
|
850
|
+
return callback(JSON.parse(payload));
|
|
851
|
+
});
|
|
548
852
|
break;
|
|
549
853
|
case constants_1.VSN_2_0_0:
|
|
550
|
-
|
|
551
|
-
|
|
854
|
+
this.encode = (_l = options === null || options === void 0 ? void 0 : options.encode) !== null && _l !== void 0 ? _l : this.serializer.encode.bind(this.serializer);
|
|
855
|
+
this.decode = (_m = options === null || options === void 0 ? void 0 : options.decode) !== null && _m !== void 0 ? _m : this.serializer.decode.bind(this.serializer);
|
|
552
856
|
break;
|
|
553
857
|
default:
|
|
554
|
-
throw new Error(`Unsupported serializer version: ${
|
|
555
|
-
}
|
|
556
|
-
result.encode = (_h = options === null || options === void 0 ? void 0 : options.encode) !== null && _h !== void 0 ? _h : defaultEncode;
|
|
557
|
-
result.decode = (_j = options === null || options === void 0 ? void 0 : options.decode) !== null && _j !== void 0 ? _j : defaultDecode;
|
|
558
|
-
result.beforeReconnect = this._reconnectAuth.bind(this);
|
|
559
|
-
if ((options === null || options === void 0 ? void 0 : options.logLevel) || (options === null || options === void 0 ? void 0 : options.log_level)) {
|
|
560
|
-
this.logLevel = options.logLevel || options.log_level;
|
|
561
|
-
result.params = Object.assign(Object.assign({}, result.params), { log_level: this.logLevel });
|
|
858
|
+
throw new Error(`Unsupported serializer version: ${this.vsn}`);
|
|
562
859
|
}
|
|
563
860
|
// Handle worker setup
|
|
564
861
|
if (this.worker) {
|
|
@@ -566,15 +863,6 @@ class RealtimeClient {
|
|
|
566
863
|
throw new Error('Web Worker is not supported');
|
|
567
864
|
}
|
|
568
865
|
this.workerUrl = options === null || options === void 0 ? void 0 : options.workerUrl;
|
|
569
|
-
result.autoSendHeartbeat = !this.worker;
|
|
570
|
-
}
|
|
571
|
-
return result;
|
|
572
|
-
}
|
|
573
|
-
/** @internal */
|
|
574
|
-
async _reconnectAuth() {
|
|
575
|
-
await this._waitForAuthIfNeeded();
|
|
576
|
-
if (!this.isConnected()) {
|
|
577
|
-
this.connect();
|
|
578
866
|
}
|
|
579
867
|
}
|
|
580
868
|
}
|