@supabase/realtime-js 2.99.2 → 2.100.0-rc.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.
- package/dist/main/RealtimeChannel.d.ts +35 -28
- package/dist/main/RealtimeChannel.d.ts.map +1 -1
- package/dist/main/RealtimeChannel.js +140 -301
- package/dist/main/RealtimeChannel.js.map +1 -1
- package/dist/main/RealtimeClient.d.ts +38 -57
- package/dist/main/RealtimeClient.d.ts.map +1 -1
- package/dist/main/RealtimeClient.js +232 -520
- package/dist/main/RealtimeClient.js.map +1 -1
- package/dist/main/RealtimePresence.d.ts +8 -24
- package/dist/main/RealtimePresence.d.ts.map +1 -1
- package/dist/main/RealtimePresence.js +6 -202
- package/dist/main/RealtimePresence.js.map +1 -1
- package/dist/main/lib/constants.d.ts +39 -35
- package/dist/main/lib/constants.d.ts.map +1 -1
- package/dist/main/lib/constants.js +30 -35
- package/dist/main/lib/constants.js.map +1 -1
- 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 +0 -9
- package/dist/main/lib/websocket-factory.d.ts.map +1 -1
- package/dist/main/lib/websocket-factory.js +0 -12
- package/dist/main/lib/websocket-factory.js.map +1 -1
- package/dist/main/phoenix/channelAdapter.d.ts +32 -0
- package/dist/main/phoenix/channelAdapter.d.ts.map +1 -0
- package/dist/main/phoenix/channelAdapter.js +103 -0
- package/dist/main/phoenix/channelAdapter.js.map +1 -0
- package/dist/main/phoenix/presenceAdapter.d.ts +53 -0
- package/dist/main/phoenix/presenceAdapter.d.ts.map +1 -0
- package/dist/main/phoenix/presenceAdapter.js +93 -0
- package/dist/main/phoenix/presenceAdapter.js.map +1 -0
- package/dist/main/phoenix/socketAdapter.d.ts +38 -0
- package/dist/main/phoenix/socketAdapter.d.ts.map +1 -0
- package/dist/main/phoenix/socketAdapter.js +114 -0
- package/dist/main/phoenix/socketAdapter.js.map +1 -0
- package/dist/main/phoenix/types.d.ts +5 -0
- package/dist/main/phoenix/types.d.ts.map +1 -0
- package/dist/main/phoenix/types.js +3 -0
- package/dist/main/phoenix/types.js.map +1 -0
- package/dist/module/RealtimeChannel.d.ts +35 -28
- package/dist/module/RealtimeChannel.d.ts.map +1 -1
- package/dist/module/RealtimeChannel.js +141 -302
- package/dist/module/RealtimeChannel.js.map +1 -1
- package/dist/module/RealtimeClient.d.ts +38 -57
- package/dist/module/RealtimeClient.d.ts.map +1 -1
- package/dist/module/RealtimeClient.js +233 -521
- package/dist/module/RealtimeClient.js.map +1 -1
- package/dist/module/RealtimePresence.d.ts +8 -24
- package/dist/module/RealtimePresence.d.ts.map +1 -1
- package/dist/module/RealtimePresence.js +5 -202
- package/dist/module/RealtimePresence.js.map +1 -1
- package/dist/module/lib/constants.d.ts +39 -35
- package/dist/module/lib/constants.d.ts.map +1 -1
- package/dist/module/lib/constants.js +30 -35
- package/dist/module/lib/constants.js.map +1 -1
- 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 +0 -9
- package/dist/module/lib/websocket-factory.d.ts.map +1 -1
- package/dist/module/lib/websocket-factory.js +0 -12
- package/dist/module/lib/websocket-factory.js.map +1 -1
- package/dist/module/phoenix/channelAdapter.d.ts +32 -0
- package/dist/module/phoenix/channelAdapter.d.ts.map +1 -0
- package/dist/module/phoenix/channelAdapter.js +100 -0
- package/dist/module/phoenix/channelAdapter.js.map +1 -0
- package/dist/module/phoenix/presenceAdapter.d.ts +53 -0
- package/dist/module/phoenix/presenceAdapter.d.ts.map +1 -0
- package/dist/module/phoenix/presenceAdapter.js +90 -0
- package/dist/module/phoenix/presenceAdapter.js.map +1 -0
- package/dist/module/phoenix/socketAdapter.d.ts +38 -0
- package/dist/module/phoenix/socketAdapter.d.ts.map +1 -0
- package/dist/module/phoenix/socketAdapter.js +111 -0
- package/dist/module/phoenix/socketAdapter.js.map +1 -0
- package/dist/module/phoenix/types.d.ts +5 -0
- package/dist/module/phoenix/types.d.ts.map +1 -0
- package/dist/module/phoenix/types.js +2 -0
- package/dist/module/phoenix/types.js.map +1 -0
- package/dist/tsconfig.module.tsbuildinfo +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/RealtimeChannel.ts +201 -364
- package/src/RealtimeClient.ts +296 -583
- package/src/RealtimePresence.ts +10 -287
- package/src/lib/constants.ts +50 -37
- package/src/lib/version.ts +1 -1
- package/src/lib/websocket-factory.ts +0 -13
- package/src/phoenix/channelAdapter.ts +147 -0
- package/src/phoenix/presenceAdapter.ts +116 -0
- package/src/phoenix/socketAdapter.ts +168 -0
- package/src/phoenix/types.ts +32 -0
- package/dist/main/lib/push.d.ts +0 -48
- package/dist/main/lib/push.d.ts.map +0 -1
- package/dist/main/lib/push.js +0 -102
- package/dist/main/lib/push.js.map +0 -1
- package/dist/main/lib/timer.d.ts +0 -22
- package/dist/main/lib/timer.d.ts.map +0 -1
- package/dist/main/lib/timer.js +0 -39
- package/dist/main/lib/timer.js.map +0 -1
- package/dist/module/lib/push.d.ts +0 -48
- package/dist/module/lib/push.d.ts.map +0 -1
- package/dist/module/lib/push.js +0 -99
- package/dist/module/lib/push.js.map +0 -1
- package/dist/module/lib/timer.d.ts +0 -22
- package/dist/module/lib/timer.d.ts.map +0 -1
- package/dist/module/lib/timer.js +0 -36
- package/dist/module/lib/timer.js.map +0 -1
- package/src/lib/push.ts +0 -121
- package/src/lib/timer.ts +0 -43
|
@@ -4,10 +4,9 @@ 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"));
|
|
8
7
|
const transformers_1 = require("./lib/transformers");
|
|
9
8
|
const RealtimeChannel_1 = tslib_1.__importDefault(require("./RealtimeChannel"));
|
|
10
|
-
const
|
|
9
|
+
const socketAdapter_1 = tslib_1.__importDefault(require("./phoenix/socketAdapter"));
|
|
11
10
|
// Connection-related constants
|
|
12
11
|
const CONNECTION_TIMEOUTS = {
|
|
13
12
|
HEARTBEAT_INTERVAL: 25000,
|
|
@@ -23,6 +22,54 @@ const WORKER_SCRIPT = `
|
|
|
23
22
|
}
|
|
24
23
|
});`;
|
|
25
24
|
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
|
+
}
|
|
26
73
|
/**
|
|
27
74
|
* Initializes the Socket.
|
|
28
75
|
*
|
|
@@ -54,39 +101,20 @@ class RealtimeClient {
|
|
|
54
101
|
*/
|
|
55
102
|
constructor(endPoint, options) {
|
|
56
103
|
var _a;
|
|
104
|
+
this.channels = new Array();
|
|
57
105
|
this.accessTokenValue = null;
|
|
106
|
+
this.accessToken = null;
|
|
58
107
|
this.apiKey = null;
|
|
59
|
-
this._manuallySetToken = false;
|
|
60
|
-
this.channels = new Array();
|
|
61
|
-
this.endPoint = '';
|
|
62
108
|
this.httpEndpoint = '';
|
|
63
109
|
/** @deprecated headers cannot be set on websocket connections */
|
|
64
110
|
this.headers = {};
|
|
65
111
|
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;
|
|
72
112
|
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 = [];
|
|
78
113
|
this.serializer = new serializer_1.default();
|
|
79
|
-
this.
|
|
80
|
-
open: [],
|
|
81
|
-
close: [],
|
|
82
|
-
error: [],
|
|
83
|
-
message: [],
|
|
84
|
-
};
|
|
85
|
-
this.accessToken = null;
|
|
86
|
-
this._connectionState = 'disconnected';
|
|
87
|
-
this._wasManualDisconnect = false;
|
|
114
|
+
this._manuallySetToken = false;
|
|
88
115
|
this._authPromise = null;
|
|
89
|
-
this.
|
|
116
|
+
this._workerHeartbeatTimer = undefined;
|
|
117
|
+
this._pendingWorkerHeartbeatRef = null;
|
|
90
118
|
/**
|
|
91
119
|
* Use either custom fetch, if provided, or default fetch to make HTTP requests
|
|
92
120
|
*
|
|
@@ -103,11 +131,9 @@ class RealtimeClient {
|
|
|
103
131
|
throw new Error('API key is required to connect to Realtime');
|
|
104
132
|
}
|
|
105
133
|
this.apiKey = options.params.apikey;
|
|
106
|
-
|
|
107
|
-
this.
|
|
134
|
+
const socketAdapterOptions = this._initializeOptions(options);
|
|
135
|
+
this.socketAdapter = new socketAdapter_1.default(endPoint, socketAdapterOptions);
|
|
108
136
|
this.httpEndpoint = (0, transformers_1.httpEndpointURL)(endPoint);
|
|
109
|
-
this._initializeOptions(options);
|
|
110
|
-
this._setupReconnectionTimer();
|
|
111
137
|
this.fetch = this._resolveFetch(options === null || options === void 0 ? void 0 : options.fetch);
|
|
112
138
|
}
|
|
113
139
|
/**
|
|
@@ -115,55 +141,44 @@ class RealtimeClient {
|
|
|
115
141
|
*/
|
|
116
142
|
connect() {
|
|
117
143
|
// Skip if already connecting, disconnecting, or connected
|
|
118
|
-
if (this.isConnecting() ||
|
|
119
|
-
this.isDisconnecting() ||
|
|
120
|
-
(this.conn !== null && this.isConnected())) {
|
|
144
|
+
if (this.isConnecting() || this.isDisconnecting() || this.isConnected()) {
|
|
121
145
|
return;
|
|
122
146
|
}
|
|
123
|
-
this._setConnectionState('connecting');
|
|
124
147
|
// Trigger auth if needed and not already in progress
|
|
125
148
|
// This ensures auth is called for standalone RealtimeClient usage
|
|
126
149
|
// while avoiding race conditions with SupabaseClient's immediate setAuth call
|
|
127
150
|
if (this.accessToken && !this._authPromise) {
|
|
128
151
|
this._setAuthSafely('connect');
|
|
129
152
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
//
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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}`);
|
|
153
|
+
this._setupConnectionHandlers();
|
|
154
|
+
try {
|
|
155
|
+
this.socketAdapter.connect();
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
const errorMessage = error.message;
|
|
159
|
+
// Provide helpful error message based on environment
|
|
160
|
+
if (errorMessage.includes('Node.js')) {
|
|
161
|
+
throw new Error(`${errorMessage}\n\n` +
|
|
162
|
+
'To use Realtime in Node.js, you need to provide a WebSocket implementation:\n\n' +
|
|
163
|
+
'Option 1: Use Node.js 22+ which has native WebSocket support\n' +
|
|
164
|
+
'Option 2: Install and provide the "ws" package:\n\n' +
|
|
165
|
+
' npm install ws\n\n' +
|
|
166
|
+
' import ws from "ws"\n' +
|
|
167
|
+
' const client = new RealtimeClient(url, {\n' +
|
|
168
|
+
' ...options,\n' +
|
|
169
|
+
' transport: ws\n' +
|
|
170
|
+
' })');
|
|
157
171
|
}
|
|
172
|
+
throw new Error(`WebSocket not available: ${errorMessage}`);
|
|
158
173
|
}
|
|
159
|
-
this.
|
|
174
|
+
this._handleNodeJsRaceCondition();
|
|
160
175
|
}
|
|
161
176
|
/**
|
|
162
177
|
* Returns the URL of the websocket.
|
|
163
178
|
* @returns string The URL of the websocket.
|
|
164
179
|
*/
|
|
165
180
|
endpointURL() {
|
|
166
|
-
return this.
|
|
181
|
+
return this.socketAdapter.endPointURL();
|
|
167
182
|
}
|
|
168
183
|
/**
|
|
169
184
|
* Disconnects the socket.
|
|
@@ -171,34 +186,14 @@ class RealtimeClient {
|
|
|
171
186
|
* @param code A numeric status code to send on disconnect.
|
|
172
187
|
* @param reason A custom reason for the disconnect.
|
|
173
188
|
*/
|
|
174
|
-
disconnect(code, reason) {
|
|
189
|
+
async disconnect(code, reason) {
|
|
175
190
|
if (this.isDisconnecting()) {
|
|
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');
|
|
191
|
+
return 'ok';
|
|
201
192
|
}
|
|
193
|
+
return await this.socketAdapter.disconnect(() => {
|
|
194
|
+
clearInterval(this._workerHeartbeatTimer);
|
|
195
|
+
this._terminateWorker();
|
|
196
|
+
}, code, reason);
|
|
202
197
|
}
|
|
203
198
|
/**
|
|
204
199
|
* Returns all created channels
|
|
@@ -207,65 +202,63 @@ class RealtimeClient {
|
|
|
207
202
|
return this.channels;
|
|
208
203
|
}
|
|
209
204
|
/**
|
|
210
|
-
* Unsubscribes and
|
|
205
|
+
* Unsubscribes, removes and tears down a single channel
|
|
211
206
|
* @param channel A RealtimeChannel instance
|
|
212
207
|
*/
|
|
213
208
|
async removeChannel(channel) {
|
|
214
209
|
const status = await channel.unsubscribe();
|
|
210
|
+
if (status === 'ok') {
|
|
211
|
+
channel.teardown();
|
|
212
|
+
}
|
|
215
213
|
if (this.channels.length === 0) {
|
|
216
214
|
this.disconnect();
|
|
217
215
|
}
|
|
218
216
|
return status;
|
|
219
217
|
}
|
|
220
218
|
/**
|
|
221
|
-
* Unsubscribes and
|
|
219
|
+
* Unsubscribes, removes and tears down all channels
|
|
222
220
|
*/
|
|
223
221
|
async removeAllChannels() {
|
|
224
|
-
const
|
|
225
|
-
|
|
222
|
+
const promises = this.channels.map(async (channel) => {
|
|
223
|
+
const result = await channel.unsubscribe();
|
|
224
|
+
channel.teardown();
|
|
225
|
+
return result;
|
|
226
|
+
});
|
|
227
|
+
const result = await Promise.all(promises);
|
|
226
228
|
this.disconnect();
|
|
227
|
-
return
|
|
229
|
+
return result;
|
|
228
230
|
}
|
|
229
231
|
/**
|
|
230
232
|
* Logs the message.
|
|
231
233
|
*
|
|
232
|
-
* For customized logging, `this.logger` can be overridden.
|
|
234
|
+
* For customized logging, `this.logger` can be overridden in Client constructor.
|
|
233
235
|
*/
|
|
234
236
|
log(kind, msg, data) {
|
|
235
|
-
this.
|
|
237
|
+
this.socketAdapter.log(kind, msg, data);
|
|
236
238
|
}
|
|
237
239
|
/**
|
|
238
240
|
* Returns the current state of the socket.
|
|
239
241
|
*/
|
|
240
242
|
connectionState() {
|
|
241
|
-
|
|
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
|
-
}
|
|
243
|
+
return this.socketAdapter.connectionState() || constants_1.CONNECTION_STATE.closed;
|
|
251
244
|
}
|
|
252
245
|
/**
|
|
253
246
|
* Returns `true` is the connection is open.
|
|
254
247
|
*/
|
|
255
248
|
isConnected() {
|
|
256
|
-
return this.
|
|
249
|
+
return this.socketAdapter.isConnected();
|
|
257
250
|
}
|
|
258
251
|
/**
|
|
259
252
|
* Returns `true` if the connection is currently connecting.
|
|
260
253
|
*/
|
|
261
254
|
isConnecting() {
|
|
262
|
-
return this.
|
|
255
|
+
return this.socketAdapter.isConnecting();
|
|
263
256
|
}
|
|
264
257
|
/**
|
|
265
258
|
* Returns `true` if the connection is currently disconnecting.
|
|
266
259
|
*/
|
|
267
260
|
isDisconnecting() {
|
|
268
|
-
return this.
|
|
261
|
+
return this.socketAdapter.isDisconnecting();
|
|
269
262
|
}
|
|
270
263
|
/**
|
|
271
264
|
* Creates (or reuses) a {@link RealtimeChannel} for the provided topic.
|
|
@@ -292,20 +285,7 @@ class RealtimeClient {
|
|
|
292
285
|
* If the socket is not connected, the message gets enqueued within a local buffer, and sent out when a connection is next established.
|
|
293
286
|
*/
|
|
294
287
|
push(data) {
|
|
295
|
-
|
|
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
|
-
}
|
|
288
|
+
this.socketAdapter.push(data);
|
|
309
289
|
}
|
|
310
290
|
/**
|
|
311
291
|
* Sets the JWT access token used for channel subscription authorization and Realtime RLS.
|
|
@@ -348,70 +328,14 @@ class RealtimeClient {
|
|
|
348
328
|
* Sends a heartbeat message if the socket is connected.
|
|
349
329
|
*/
|
|
350
330
|
async sendHeartbeat() {
|
|
351
|
-
|
|
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');
|
|
331
|
+
this.socketAdapter.sendHeartbeat();
|
|
399
332
|
}
|
|
400
333
|
/**
|
|
401
334
|
* Sets a callback that receives lifecycle events for internal heartbeat messages.
|
|
402
335
|
* Useful for instrumenting connection health (e.g. sent/ok/timeout/disconnected).
|
|
403
336
|
*/
|
|
404
337
|
onHeartbeat(callback) {
|
|
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
|
-
}
|
|
338
|
+
this.socketAdapter.heartbeatCallback = this._wrapHeartbeatCallback(callback);
|
|
415
339
|
}
|
|
416
340
|
/**
|
|
417
341
|
* Return the next message ref, accounting for overflows
|
|
@@ -419,29 +343,10 @@ class RealtimeClient {
|
|
|
419
343
|
* @internal
|
|
420
344
|
*/
|
|
421
345
|
_makeRef() {
|
|
422
|
-
|
|
423
|
-
if (newRef === this.ref) {
|
|
424
|
-
this.ref = 0;
|
|
425
|
-
}
|
|
426
|
-
else {
|
|
427
|
-
this.ref = newRef;
|
|
428
|
-
}
|
|
429
|
-
return this.ref.toString();
|
|
346
|
+
return this.socketAdapter.makeRef();
|
|
430
347
|
}
|
|
431
348
|
/**
|
|
432
|
-
*
|
|
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.
|
|
349
|
+
* Removes a channel from RealtimeClient
|
|
445
350
|
*
|
|
446
351
|
* @param channel An open subscription.
|
|
447
352
|
*
|
|
@@ -450,262 +355,6 @@ class RealtimeClient {
|
|
|
450
355
|
_remove(channel) {
|
|
451
356
|
this.channels = this.channels.filter((c) => c.topic !== channel.topic);
|
|
452
357
|
}
|
|
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
|
-
}
|
|
709
358
|
/**
|
|
710
359
|
* Perform the actual auth operation
|
|
711
360
|
* @internal
|
|
@@ -748,8 +397,8 @@ class RealtimeClient {
|
|
|
748
397
|
version: constants_1.DEFAULT_VERSION,
|
|
749
398
|
};
|
|
750
399
|
tokenToSend && channel.updateJoinPayload(payload);
|
|
751
|
-
if (channel.joinedOnce && channel.
|
|
752
|
-
channel.
|
|
400
|
+
if (channel.joinedOnce && channel.channelAdapter.isJoined()) {
|
|
401
|
+
channel.channelAdapter.push(constants_1.CHANNEL_EVENTS.access_token, {
|
|
753
402
|
access_token: tokenToSend,
|
|
754
403
|
});
|
|
755
404
|
}
|
|
@@ -777,85 +426,139 @@ class RealtimeClient {
|
|
|
777
426
|
});
|
|
778
427
|
}
|
|
779
428
|
}
|
|
780
|
-
/**
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
try {
|
|
788
|
-
callback(data);
|
|
789
|
-
}
|
|
790
|
-
catch (e) {
|
|
791
|
-
this.log('error', `error in ${event} callback`, e);
|
|
792
|
-
}
|
|
429
|
+
/** @internal */
|
|
430
|
+
_setupConnectionHandlers() {
|
|
431
|
+
this.socketAdapter.onOpen(() => {
|
|
432
|
+
const authPromise = this._authPromise ||
|
|
433
|
+
(this.accessToken && !this.accessTokenValue ? this.setAuth() : Promise.resolve());
|
|
434
|
+
authPromise.catch((e) => {
|
|
435
|
+
this.log('error', 'error waiting for auth on connect', e);
|
|
793
436
|
});
|
|
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();
|
|
794
457
|
}
|
|
795
|
-
|
|
796
|
-
|
|
458
|
+
}
|
|
459
|
+
/** @internal */
|
|
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`);
|
|
797
475
|
}
|
|
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
|
+
});
|
|
798
492
|
}
|
|
799
493
|
/**
|
|
800
|
-
*
|
|
494
|
+
* Terminate the Web Worker and clear the reference
|
|
801
495
|
* @internal
|
|
802
496
|
*/
|
|
803
|
-
|
|
804
|
-
this.
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
497
|
+
_terminateWorker() {
|
|
498
|
+
if (this.workerRef) {
|
|
499
|
+
this.log('worker', 'terminating worker');
|
|
500
|
+
this.workerRef.terminate();
|
|
501
|
+
this.workerRef = undefined;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
/** @internal */
|
|
505
|
+
_workerObjectUrl(url) {
|
|
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;
|
|
812
515
|
}
|
|
813
516
|
/**
|
|
814
|
-
* Initialize
|
|
517
|
+
* Initialize socket options with defaults
|
|
815
518
|
* @internal
|
|
816
519
|
*/
|
|
817
520
|
_initializeOptions(options) {
|
|
818
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j
|
|
819
|
-
|
|
820
|
-
this.
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
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) => {
|
|
521
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
522
|
+
this.worker = (_a = options === null || options === void 0 ? void 0 : options.worker) !== null && _a !== void 0 ? _a : false;
|
|
523
|
+
this.accessToken = (_b = options === null || options === void 0 ? void 0 : options.accessToken) !== null && _b !== void 0 ? _b : null;
|
|
524
|
+
const result = {};
|
|
525
|
+
result.timeout = (_c = options === null || options === void 0 ? void 0 : options.timeout) !== null && _c !== void 0 ? _c : constants_1.DEFAULT_TIMEOUT;
|
|
526
|
+
result.heartbeatIntervalMs =
|
|
527
|
+
(_d = options === null || options === void 0 ? void 0 : options.heartbeatIntervalMs) !== null && _d !== void 0 ? _d : CONNECTION_TIMEOUTS.HEARTBEAT_INTERVAL;
|
|
528
|
+
result.vsn = (_e = options === null || options === void 0 ? void 0 : options.vsn) !== null && _e !== void 0 ? _e : constants_1.DEFAULT_VSN;
|
|
529
|
+
// @ts-ignore - mismatch between phoenix and supabase
|
|
530
|
+
result.transport = (_f = options === null || options === void 0 ? void 0 : options.transport) !== null && _f !== void 0 ? _f : websocket_factory_1.default.getWebSocketConstructor();
|
|
531
|
+
result.params = options === null || options === void 0 ? void 0 : options.params;
|
|
532
|
+
result.logger = options === null || options === void 0 ? void 0 : options.logger;
|
|
533
|
+
result.heartbeatCallback = this._wrapHeartbeatCallback(options === null || options === void 0 ? void 0 : options.heartbeatCallback);
|
|
534
|
+
result.reconnectAfterMs =
|
|
535
|
+
(_g = options === null || options === void 0 ? void 0 : options.reconnectAfterMs) !== null && _g !== void 0 ? _g : ((tries) => {
|
|
840
536
|
return RECONNECT_INTERVALS[tries - 1] || DEFAULT_RECONNECT_FALLBACK;
|
|
841
537
|
});
|
|
842
|
-
|
|
538
|
+
let defaultEncode;
|
|
539
|
+
let defaultDecode;
|
|
540
|
+
switch (result.vsn) {
|
|
843
541
|
case constants_1.VSN_1_0_0:
|
|
844
|
-
|
|
845
|
-
(
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
return callback(JSON.parse(payload));
|
|
851
|
-
});
|
|
542
|
+
defaultEncode = (payload, callback) => {
|
|
543
|
+
return callback(JSON.stringify(payload));
|
|
544
|
+
};
|
|
545
|
+
defaultDecode = (payload, callback) => {
|
|
546
|
+
return callback(JSON.parse(payload));
|
|
547
|
+
};
|
|
852
548
|
break;
|
|
853
549
|
case constants_1.VSN_2_0_0:
|
|
854
|
-
|
|
855
|
-
|
|
550
|
+
defaultEncode = this.serializer.encode.bind(this.serializer);
|
|
551
|
+
defaultDecode = this.serializer.decode.bind(this.serializer);
|
|
856
552
|
break;
|
|
857
553
|
default:
|
|
858
|
-
throw new Error(`Unsupported serializer version: ${
|
|
554
|
+
throw new Error(`Unsupported serializer version: ${result.vsn}`);
|
|
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 });
|
|
859
562
|
}
|
|
860
563
|
// Handle worker setup
|
|
861
564
|
if (this.worker) {
|
|
@@ -863,6 +566,15 @@ class RealtimeClient {
|
|
|
863
566
|
throw new Error('Web Worker is not supported');
|
|
864
567
|
}
|
|
865
568
|
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();
|
|
866
578
|
}
|
|
867
579
|
}
|
|
868
580
|
}
|