@depup/supabase__realtime-js 2.99.3-depup.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.
Files changed (104) hide show
  1. package/README.md +32 -0
  2. package/changes.json +14 -0
  3. package/dist/main/RealtimeChannel.d.ts +370 -0
  4. package/dist/main/RealtimeChannel.d.ts.map +1 -0
  5. package/dist/main/RealtimeChannel.js +644 -0
  6. package/dist/main/RealtimeChannel.js.map +1 -0
  7. package/dist/main/RealtimeClient.d.ts +238 -0
  8. package/dist/main/RealtimeClient.d.ts.map +1 -0
  9. package/dist/main/RealtimeClient.js +870 -0
  10. package/dist/main/RealtimeClient.js.map +1 -0
  11. package/dist/main/RealtimePresence.d.ts +76 -0
  12. package/dist/main/RealtimePresence.d.ts.map +1 -0
  13. package/dist/main/RealtimePresence.js +237 -0
  14. package/dist/main/RealtimePresence.js.map +1 -0
  15. package/dist/main/index.d.ts +6 -0
  16. package/dist/main/index.d.ts.map +1 -0
  17. package/dist/main/index.js +18 -0
  18. package/dist/main/index.js.map +1 -0
  19. package/dist/main/lib/constants.d.ts +39 -0
  20. package/dist/main/lib/constants.d.ts.map +1 -0
  21. package/dist/main/lib/constants.js +48 -0
  22. package/dist/main/lib/constants.js.map +1 -0
  23. package/dist/main/lib/push.d.ts +48 -0
  24. package/dist/main/lib/push.d.ts.map +1 -0
  25. package/dist/main/lib/push.js +102 -0
  26. package/dist/main/lib/push.js.map +1 -0
  27. package/dist/main/lib/serializer.d.ts +33 -0
  28. package/dist/main/lib/serializer.d.ts.map +1 -0
  29. package/dist/main/lib/serializer.js +155 -0
  30. package/dist/main/lib/serializer.js.map +1 -0
  31. package/dist/main/lib/timer.d.ts +22 -0
  32. package/dist/main/lib/timer.d.ts.map +1 -0
  33. package/dist/main/lib/timer.js +39 -0
  34. package/dist/main/lib/timer.js.map +1 -0
  35. package/dist/main/lib/transformers.d.ts +109 -0
  36. package/dist/main/lib/transformers.d.ts.map +1 -0
  37. package/dist/main/lib/transformers.js +241 -0
  38. package/dist/main/lib/transformers.js.map +1 -0
  39. package/dist/main/lib/version.d.ts +2 -0
  40. package/dist/main/lib/version.d.ts.map +1 -0
  41. package/dist/main/lib/version.js +11 -0
  42. package/dist/main/lib/version.js.map +1 -0
  43. package/dist/main/lib/websocket-factory.d.ts +81 -0
  44. package/dist/main/lib/websocket-factory.d.ts.map +1 -0
  45. package/dist/main/lib/websocket-factory.js +131 -0
  46. package/dist/main/lib/websocket-factory.js.map +1 -0
  47. package/dist/module/RealtimeChannel.d.ts +370 -0
  48. package/dist/module/RealtimeChannel.d.ts.map +1 -0
  49. package/dist/module/RealtimeChannel.js +639 -0
  50. package/dist/module/RealtimeChannel.js.map +1 -0
  51. package/dist/module/RealtimeClient.d.ts +238 -0
  52. package/dist/module/RealtimeClient.d.ts.map +1 -0
  53. package/dist/module/RealtimeClient.js +866 -0
  54. package/dist/module/RealtimeClient.js.map +1 -0
  55. package/dist/module/RealtimePresence.d.ts +76 -0
  56. package/dist/module/RealtimePresence.d.ts.map +1 -0
  57. package/dist/module/RealtimePresence.js +233 -0
  58. package/dist/module/RealtimePresence.js.map +1 -0
  59. package/dist/module/index.d.ts +6 -0
  60. package/dist/module/index.d.ts.map +1 -0
  61. package/dist/module/index.js +6 -0
  62. package/dist/module/index.js.map +1 -0
  63. package/dist/module/lib/constants.d.ts +39 -0
  64. package/dist/module/lib/constants.d.ts.map +1 -0
  65. package/dist/module/lib/constants.js +45 -0
  66. package/dist/module/lib/constants.js.map +1 -0
  67. package/dist/module/lib/push.d.ts +48 -0
  68. package/dist/module/lib/push.d.ts.map +1 -0
  69. package/dist/module/lib/push.js +99 -0
  70. package/dist/module/lib/push.js.map +1 -0
  71. package/dist/module/lib/serializer.d.ts +33 -0
  72. package/dist/module/lib/serializer.d.ts.map +1 -0
  73. package/dist/module/lib/serializer.js +152 -0
  74. package/dist/module/lib/serializer.js.map +1 -0
  75. package/dist/module/lib/timer.d.ts +22 -0
  76. package/dist/module/lib/timer.d.ts.map +1 -0
  77. package/dist/module/lib/timer.js +36 -0
  78. package/dist/module/lib/timer.js.map +1 -0
  79. package/dist/module/lib/transformers.d.ts +109 -0
  80. package/dist/module/lib/transformers.d.ts.map +1 -0
  81. package/dist/module/lib/transformers.js +229 -0
  82. package/dist/module/lib/transformers.js.map +1 -0
  83. package/dist/module/lib/version.d.ts +2 -0
  84. package/dist/module/lib/version.d.ts.map +1 -0
  85. package/dist/module/lib/version.js +8 -0
  86. package/dist/module/lib/version.js.map +1 -0
  87. package/dist/module/lib/websocket-factory.d.ts +81 -0
  88. package/dist/module/lib/websocket-factory.d.ts.map +1 -0
  89. package/dist/module/lib/websocket-factory.js +127 -0
  90. package/dist/module/lib/websocket-factory.js.map +1 -0
  91. package/dist/tsconfig.module.tsbuildinfo +1 -0
  92. package/dist/tsconfig.tsbuildinfo +1 -0
  93. package/package.json +85 -0
  94. package/src/RealtimeChannel.ts +1039 -0
  95. package/src/RealtimeClient.ts +1023 -0
  96. package/src/RealtimePresence.ts +346 -0
  97. package/src/index.ts +53 -0
  98. package/src/lib/constants.ts +49 -0
  99. package/src/lib/push.ts +121 -0
  100. package/src/lib/serializer.ts +203 -0
  101. package/src/lib/timer.ts +43 -0
  102. package/src/lib/transformers.ts +270 -0
  103. package/src/lib/version.ts +7 -0
  104. package/src/lib/websocket-factory.ts +192 -0
@@ -0,0 +1,644 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.REALTIME_CHANNEL_STATES = exports.REALTIME_SUBSCRIBE_STATES = exports.REALTIME_LISTEN_TYPES = exports.REALTIME_POSTGRES_CHANGES_LISTEN_EVENT = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const constants_1 = require("./lib/constants");
6
+ const push_1 = tslib_1.__importDefault(require("./lib/push"));
7
+ const timer_1 = tslib_1.__importDefault(require("./lib/timer"));
8
+ const RealtimePresence_1 = tslib_1.__importDefault(require("./RealtimePresence"));
9
+ const Transformers = tslib_1.__importStar(require("./lib/transformers"));
10
+ const transformers_1 = require("./lib/transformers");
11
+ var REALTIME_POSTGRES_CHANGES_LISTEN_EVENT;
12
+ (function (REALTIME_POSTGRES_CHANGES_LISTEN_EVENT) {
13
+ REALTIME_POSTGRES_CHANGES_LISTEN_EVENT["ALL"] = "*";
14
+ REALTIME_POSTGRES_CHANGES_LISTEN_EVENT["INSERT"] = "INSERT";
15
+ REALTIME_POSTGRES_CHANGES_LISTEN_EVENT["UPDATE"] = "UPDATE";
16
+ REALTIME_POSTGRES_CHANGES_LISTEN_EVENT["DELETE"] = "DELETE";
17
+ })(REALTIME_POSTGRES_CHANGES_LISTEN_EVENT || (exports.REALTIME_POSTGRES_CHANGES_LISTEN_EVENT = REALTIME_POSTGRES_CHANGES_LISTEN_EVENT = {}));
18
+ var REALTIME_LISTEN_TYPES;
19
+ (function (REALTIME_LISTEN_TYPES) {
20
+ REALTIME_LISTEN_TYPES["BROADCAST"] = "broadcast";
21
+ REALTIME_LISTEN_TYPES["PRESENCE"] = "presence";
22
+ REALTIME_LISTEN_TYPES["POSTGRES_CHANGES"] = "postgres_changes";
23
+ REALTIME_LISTEN_TYPES["SYSTEM"] = "system";
24
+ })(REALTIME_LISTEN_TYPES || (exports.REALTIME_LISTEN_TYPES = REALTIME_LISTEN_TYPES = {}));
25
+ var REALTIME_SUBSCRIBE_STATES;
26
+ (function (REALTIME_SUBSCRIBE_STATES) {
27
+ REALTIME_SUBSCRIBE_STATES["SUBSCRIBED"] = "SUBSCRIBED";
28
+ REALTIME_SUBSCRIBE_STATES["TIMED_OUT"] = "TIMED_OUT";
29
+ REALTIME_SUBSCRIBE_STATES["CLOSED"] = "CLOSED";
30
+ REALTIME_SUBSCRIBE_STATES["CHANNEL_ERROR"] = "CHANNEL_ERROR";
31
+ })(REALTIME_SUBSCRIBE_STATES || (exports.REALTIME_SUBSCRIBE_STATES = REALTIME_SUBSCRIBE_STATES = {}));
32
+ exports.REALTIME_CHANNEL_STATES = constants_1.CHANNEL_STATES;
33
+ /** A channel is the basic building block of Realtime
34
+ * and narrows the scope of data flow to subscribed clients.
35
+ * You can think of a channel as a chatroom where participants are able to see who's online
36
+ * and send and receive messages.
37
+ */
38
+ class RealtimeChannel {
39
+ /**
40
+ * Creates a channel that can broadcast messages, sync presence, and listen to Postgres changes.
41
+ *
42
+ * The topic determines which realtime stream you are subscribing to. Config options let you
43
+ * enable acknowledgement for broadcasts, presence tracking, or private channels.
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * import RealtimeClient from '@supabase/realtime-js'
48
+ *
49
+ * const client = new RealtimeClient('https://xyzcompany.supabase.co/realtime/v1', {
50
+ * params: { apikey: 'public-anon-key' },
51
+ * })
52
+ * const channel = new RealtimeChannel('realtime:public:messages', { config: {} }, client)
53
+ * ```
54
+ */
55
+ constructor(
56
+ /** Topic name can be any string. */
57
+ topic, params = { config: {} }, socket) {
58
+ var _a, _b;
59
+ this.topic = topic;
60
+ this.params = params;
61
+ this.socket = socket;
62
+ this.bindings = {};
63
+ this.state = constants_1.CHANNEL_STATES.closed;
64
+ this.joinedOnce = false;
65
+ this.pushBuffer = [];
66
+ this.subTopic = topic.replace(/^realtime:/i, '');
67
+ this.params.config = Object.assign({
68
+ broadcast: { ack: false, self: false },
69
+ presence: { key: '', enabled: false },
70
+ private: false,
71
+ }, params.config);
72
+ this.timeout = this.socket.timeout;
73
+ this.joinPush = new push_1.default(this, constants_1.CHANNEL_EVENTS.join, this.params, this.timeout);
74
+ this.rejoinTimer = new timer_1.default(() => this._rejoinUntilConnected(), this.socket.reconnectAfterMs);
75
+ this.joinPush.receive('ok', () => {
76
+ this.state = constants_1.CHANNEL_STATES.joined;
77
+ this.rejoinTimer.reset();
78
+ this.pushBuffer.forEach((pushEvent) => pushEvent.send());
79
+ this.pushBuffer = [];
80
+ });
81
+ this._onClose(() => {
82
+ this.rejoinTimer.reset();
83
+ this.socket.log('channel', `close ${this.topic} ${this._joinRef()}`);
84
+ this.state = constants_1.CHANNEL_STATES.closed;
85
+ this.socket._remove(this);
86
+ });
87
+ this._onError((reason) => {
88
+ if (this._isLeaving() || this._isClosed()) {
89
+ return;
90
+ }
91
+ this.socket.log('channel', `error ${this.topic}`, reason);
92
+ this.state = constants_1.CHANNEL_STATES.errored;
93
+ this.rejoinTimer.scheduleTimeout();
94
+ });
95
+ this.joinPush.receive('timeout', () => {
96
+ if (!this._isJoining()) {
97
+ return;
98
+ }
99
+ this.socket.log('channel', `timeout ${this.topic}`, this.joinPush.timeout);
100
+ this.state = constants_1.CHANNEL_STATES.errored;
101
+ this.rejoinTimer.scheduleTimeout();
102
+ });
103
+ this.joinPush.receive('error', (reason) => {
104
+ if (this._isLeaving() || this._isClosed()) {
105
+ return;
106
+ }
107
+ this.socket.log('channel', `error ${this.topic}`, reason);
108
+ this.state = constants_1.CHANNEL_STATES.errored;
109
+ this.rejoinTimer.scheduleTimeout();
110
+ });
111
+ this._on(constants_1.CHANNEL_EVENTS.reply, {}, (payload, ref) => {
112
+ this._trigger(this._replyEventName(ref), payload);
113
+ });
114
+ this.presence = new RealtimePresence_1.default(this);
115
+ this.broadcastEndpointURL = (0, transformers_1.httpEndpointURL)(this.socket.endPoint);
116
+ this.private = this.params.config.private || false;
117
+ if (!this.private && ((_b = (_a = this.params.config) === null || _a === void 0 ? void 0 : _a.broadcast) === null || _b === void 0 ? void 0 : _b.replay)) {
118
+ throw `tried to use replay on public channel '${this.topic}'. It must be a private channel.`;
119
+ }
120
+ }
121
+ /** Subscribe registers your client with the server */
122
+ subscribe(callback, timeout = this.timeout) {
123
+ var _a, _b, _c;
124
+ if (!this.socket.isConnected()) {
125
+ this.socket.connect();
126
+ }
127
+ if (this.state == constants_1.CHANNEL_STATES.closed) {
128
+ const { config: { broadcast, presence, private: isPrivate }, } = this.params;
129
+ const postgres_changes = (_b = (_a = this.bindings.postgres_changes) === null || _a === void 0 ? void 0 : _a.map((r) => r.filter)) !== null && _b !== void 0 ? _b : [];
130
+ const presence_enabled = (!!this.bindings[REALTIME_LISTEN_TYPES.PRESENCE] &&
131
+ this.bindings[REALTIME_LISTEN_TYPES.PRESENCE].length > 0) ||
132
+ ((_c = this.params.config.presence) === null || _c === void 0 ? void 0 : _c.enabled) === true;
133
+ const accessTokenPayload = {};
134
+ const config = {
135
+ broadcast,
136
+ presence: Object.assign(Object.assign({}, presence), { enabled: presence_enabled }),
137
+ postgres_changes,
138
+ private: isPrivate,
139
+ };
140
+ if (this.socket.accessTokenValue) {
141
+ accessTokenPayload.access_token = this.socket.accessTokenValue;
142
+ }
143
+ this._onError((e) => callback === null || callback === void 0 ? void 0 : callback(REALTIME_SUBSCRIBE_STATES.CHANNEL_ERROR, e));
144
+ this._onClose(() => callback === null || callback === void 0 ? void 0 : callback(REALTIME_SUBSCRIBE_STATES.CLOSED));
145
+ this.updateJoinPayload(Object.assign({ config }, accessTokenPayload));
146
+ this.joinedOnce = true;
147
+ this._rejoin(timeout);
148
+ this.joinPush
149
+ .receive('ok', async ({ postgres_changes }) => {
150
+ var _a;
151
+ // Only refresh auth if using callback-based tokens
152
+ if (!this.socket._isManualToken()) {
153
+ this.socket.setAuth();
154
+ }
155
+ if (postgres_changes === undefined) {
156
+ callback === null || callback === void 0 ? void 0 : callback(REALTIME_SUBSCRIBE_STATES.SUBSCRIBED);
157
+ return;
158
+ }
159
+ else {
160
+ const clientPostgresBindings = this.bindings.postgres_changes;
161
+ const bindingsLen = (_a = clientPostgresBindings === null || clientPostgresBindings === void 0 ? void 0 : clientPostgresBindings.length) !== null && _a !== void 0 ? _a : 0;
162
+ const newPostgresBindings = [];
163
+ for (let i = 0; i < bindingsLen; i++) {
164
+ const clientPostgresBinding = clientPostgresBindings[i];
165
+ const { filter: { event, schema, table, filter }, } = clientPostgresBinding;
166
+ const serverPostgresFilter = postgres_changes && postgres_changes[i];
167
+ if (serverPostgresFilter &&
168
+ serverPostgresFilter.event === event &&
169
+ RealtimeChannel.isFilterValueEqual(serverPostgresFilter.schema, schema) &&
170
+ RealtimeChannel.isFilterValueEqual(serverPostgresFilter.table, table) &&
171
+ RealtimeChannel.isFilterValueEqual(serverPostgresFilter.filter, filter)) {
172
+ newPostgresBindings.push(Object.assign(Object.assign({}, clientPostgresBinding), { id: serverPostgresFilter.id }));
173
+ }
174
+ else {
175
+ this.unsubscribe();
176
+ this.state = constants_1.CHANNEL_STATES.errored;
177
+ callback === null || callback === void 0 ? void 0 : callback(REALTIME_SUBSCRIBE_STATES.CHANNEL_ERROR, new Error('mismatch between server and client bindings for postgres changes'));
178
+ return;
179
+ }
180
+ }
181
+ this.bindings.postgres_changes = newPostgresBindings;
182
+ callback && callback(REALTIME_SUBSCRIBE_STATES.SUBSCRIBED);
183
+ return;
184
+ }
185
+ })
186
+ .receive('error', (error) => {
187
+ this.state = constants_1.CHANNEL_STATES.errored;
188
+ callback === null || callback === void 0 ? void 0 : callback(REALTIME_SUBSCRIBE_STATES.CHANNEL_ERROR, new Error(JSON.stringify(Object.values(error).join(', ') || 'error')));
189
+ return;
190
+ })
191
+ .receive('timeout', () => {
192
+ callback === null || callback === void 0 ? void 0 : callback(REALTIME_SUBSCRIBE_STATES.TIMED_OUT);
193
+ return;
194
+ });
195
+ }
196
+ return this;
197
+ }
198
+ /**
199
+ * Returns the current presence state for this channel.
200
+ *
201
+ * The shape is a map keyed by presence key (for example a user id) where each entry contains the
202
+ * tracked metadata for that user.
203
+ */
204
+ presenceState() {
205
+ return this.presence.state;
206
+ }
207
+ /**
208
+ * Sends the supplied payload to the presence tracker so other subscribers can see that this
209
+ * client is online. Use `untrack` to stop broadcasting presence for the same key.
210
+ */
211
+ async track(payload, opts = {}) {
212
+ return await this.send({
213
+ type: 'presence',
214
+ event: 'track',
215
+ payload,
216
+ }, opts.timeout || this.timeout);
217
+ }
218
+ /**
219
+ * Removes the current presence state for this client.
220
+ */
221
+ async untrack(opts = {}) {
222
+ return await this.send({
223
+ type: 'presence',
224
+ event: 'untrack',
225
+ }, opts);
226
+ }
227
+ on(type, filter, callback) {
228
+ if (this.state === constants_1.CHANNEL_STATES.joined && type === REALTIME_LISTEN_TYPES.PRESENCE) {
229
+ this.socket.log('channel', `resubscribe to ${this.topic} due to change in presence callbacks on joined channel`);
230
+ this.unsubscribe().then(async () => await this.subscribe());
231
+ }
232
+ return this._on(type, filter, callback);
233
+ }
234
+ /**
235
+ * Sends a broadcast message explicitly via REST API.
236
+ *
237
+ * This method always uses the REST API endpoint regardless of WebSocket connection state.
238
+ * Useful when you want to guarantee REST delivery or when gradually migrating from implicit REST fallback.
239
+ *
240
+ * @param event The name of the broadcast event
241
+ * @param payload Payload to be sent (required)
242
+ * @param opts Options including timeout
243
+ * @returns Promise resolving to object with success status, and error details if failed
244
+ */
245
+ async httpSend(event, payload, opts = {}) {
246
+ var _a;
247
+ if (payload === undefined || payload === null) {
248
+ return Promise.reject('Payload is required for httpSend()');
249
+ }
250
+ const headers = {
251
+ apikey: this.socket.apiKey ? this.socket.apiKey : '',
252
+ 'Content-Type': 'application/json',
253
+ };
254
+ if (this.socket.accessTokenValue) {
255
+ headers['Authorization'] = `Bearer ${this.socket.accessTokenValue}`;
256
+ }
257
+ const options = {
258
+ method: 'POST',
259
+ headers,
260
+ body: JSON.stringify({
261
+ messages: [
262
+ {
263
+ topic: this.subTopic,
264
+ event,
265
+ payload: payload,
266
+ private: this.private,
267
+ },
268
+ ],
269
+ }),
270
+ };
271
+ const response = await this._fetchWithTimeout(this.broadcastEndpointURL, options, (_a = opts.timeout) !== null && _a !== void 0 ? _a : this.timeout);
272
+ if (response.status === 202) {
273
+ return { success: true };
274
+ }
275
+ let errorMessage = response.statusText;
276
+ try {
277
+ const errorBody = await response.json();
278
+ errorMessage = errorBody.error || errorBody.message || errorMessage;
279
+ }
280
+ catch (_b) { }
281
+ return Promise.reject(new Error(errorMessage));
282
+ }
283
+ /**
284
+ * Sends a message into the channel.
285
+ *
286
+ * @param args Arguments to send to channel
287
+ * @param args.type The type of event to send
288
+ * @param args.event The name of the event being sent
289
+ * @param args.payload Payload to be sent
290
+ * @param opts Options to be used during the send process
291
+ */
292
+ async send(args, opts = {}) {
293
+ var _a, _b;
294
+ if (!this._canPush() && args.type === 'broadcast') {
295
+ console.warn('Realtime send() is automatically falling back to REST API. ' +
296
+ 'This behavior will be deprecated in the future. ' +
297
+ 'Please use httpSend() explicitly for REST delivery.');
298
+ const { event, payload: endpoint_payload } = args;
299
+ const headers = {
300
+ apikey: this.socket.apiKey ? this.socket.apiKey : '',
301
+ 'Content-Type': 'application/json',
302
+ };
303
+ if (this.socket.accessTokenValue) {
304
+ headers['Authorization'] = `Bearer ${this.socket.accessTokenValue}`;
305
+ }
306
+ const options = {
307
+ method: 'POST',
308
+ headers,
309
+ body: JSON.stringify({
310
+ messages: [
311
+ {
312
+ topic: this.subTopic,
313
+ event,
314
+ payload: endpoint_payload,
315
+ private: this.private,
316
+ },
317
+ ],
318
+ }),
319
+ };
320
+ try {
321
+ const response = await this._fetchWithTimeout(this.broadcastEndpointURL, options, (_a = opts.timeout) !== null && _a !== void 0 ? _a : this.timeout);
322
+ await ((_b = response.body) === null || _b === void 0 ? void 0 : _b.cancel());
323
+ return response.ok ? 'ok' : 'error';
324
+ }
325
+ catch (error) {
326
+ if (error.name === 'AbortError') {
327
+ return 'timed out';
328
+ }
329
+ else {
330
+ return 'error';
331
+ }
332
+ }
333
+ }
334
+ else {
335
+ return new Promise((resolve) => {
336
+ var _a, _b, _c;
337
+ const push = this._push(args.type, args, opts.timeout || this.timeout);
338
+ if (args.type === 'broadcast' && !((_c = (_b = (_a = this.params) === null || _a === void 0 ? void 0 : _a.config) === null || _b === void 0 ? void 0 : _b.broadcast) === null || _c === void 0 ? void 0 : _c.ack)) {
339
+ resolve('ok');
340
+ }
341
+ push.receive('ok', () => resolve('ok'));
342
+ push.receive('error', () => resolve('error'));
343
+ push.receive('timeout', () => resolve('timed out'));
344
+ });
345
+ }
346
+ }
347
+ /**
348
+ * Updates the payload that will be sent the next time the channel joins (reconnects).
349
+ * Useful for rotating access tokens or updating config without re-creating the channel.
350
+ */
351
+ updateJoinPayload(payload) {
352
+ this.joinPush.updatePayload(payload);
353
+ }
354
+ /**
355
+ * Leaves the channel.
356
+ *
357
+ * Unsubscribes from server events, and instructs channel to terminate on server.
358
+ * Triggers onClose() hooks.
359
+ *
360
+ * To receive leave acknowledgements, use the a `receive` hook to bind to the server ack, ie:
361
+ * channel.unsubscribe().receive("ok", () => alert("left!") )
362
+ */
363
+ unsubscribe(timeout = this.timeout) {
364
+ this.state = constants_1.CHANNEL_STATES.leaving;
365
+ const onClose = () => {
366
+ this.socket.log('channel', `leave ${this.topic}`);
367
+ this._trigger(constants_1.CHANNEL_EVENTS.close, 'leave', this._joinRef());
368
+ };
369
+ this.joinPush.destroy();
370
+ let leavePush = null;
371
+ return new Promise((resolve) => {
372
+ leavePush = new push_1.default(this, constants_1.CHANNEL_EVENTS.leave, {}, timeout);
373
+ leavePush
374
+ .receive('ok', () => {
375
+ onClose();
376
+ resolve('ok');
377
+ })
378
+ .receive('timeout', () => {
379
+ onClose();
380
+ resolve('timed out');
381
+ })
382
+ .receive('error', () => {
383
+ resolve('error');
384
+ });
385
+ leavePush.send();
386
+ if (!this._canPush()) {
387
+ leavePush.trigger('ok', {});
388
+ }
389
+ }).finally(() => {
390
+ leavePush === null || leavePush === void 0 ? void 0 : leavePush.destroy();
391
+ });
392
+ }
393
+ /**
394
+ * Teardown the channel.
395
+ *
396
+ * Destroys and stops related timers.
397
+ */
398
+ teardown() {
399
+ this.pushBuffer.forEach((push) => push.destroy());
400
+ this.pushBuffer = [];
401
+ this.rejoinTimer.reset();
402
+ this.joinPush.destroy();
403
+ this.state = constants_1.CHANNEL_STATES.closed;
404
+ this.bindings = {};
405
+ }
406
+ /** @internal */
407
+ async _fetchWithTimeout(url, options, timeout) {
408
+ const controller = new AbortController();
409
+ const id = setTimeout(() => controller.abort(), timeout);
410
+ const response = await this.socket.fetch(url, Object.assign(Object.assign({}, options), { signal: controller.signal }));
411
+ clearTimeout(id);
412
+ return response;
413
+ }
414
+ /** @internal */
415
+ _push(event, payload, timeout = this.timeout) {
416
+ if (!this.joinedOnce) {
417
+ throw `tried to push '${event}' to '${this.topic}' before joining. Use channel.subscribe() before pushing events`;
418
+ }
419
+ let pushEvent = new push_1.default(this, event, payload, timeout);
420
+ if (this._canPush()) {
421
+ pushEvent.send();
422
+ }
423
+ else {
424
+ this._addToPushBuffer(pushEvent);
425
+ }
426
+ return pushEvent;
427
+ }
428
+ /** @internal */
429
+ _addToPushBuffer(pushEvent) {
430
+ pushEvent.startTimeout();
431
+ this.pushBuffer.push(pushEvent);
432
+ // Enforce buffer size limit
433
+ if (this.pushBuffer.length > constants_1.MAX_PUSH_BUFFER_SIZE) {
434
+ const removedPush = this.pushBuffer.shift();
435
+ if (removedPush) {
436
+ removedPush.destroy();
437
+ this.socket.log('channel', `discarded push due to buffer overflow: ${removedPush.event}`, removedPush.payload);
438
+ }
439
+ }
440
+ }
441
+ /**
442
+ * Overridable message hook
443
+ *
444
+ * Receives all events for specialized message handling before dispatching to the channel callbacks.
445
+ * Must return the payload, modified or unmodified.
446
+ *
447
+ * @internal
448
+ */
449
+ _onMessage(_event, payload, _ref) {
450
+ return payload;
451
+ }
452
+ /** @internal */
453
+ _isMember(topic) {
454
+ return this.topic === topic;
455
+ }
456
+ /** @internal */
457
+ _joinRef() {
458
+ return this.joinPush.ref;
459
+ }
460
+ /** @internal */
461
+ _trigger(type, payload, ref) {
462
+ var _a, _b;
463
+ const typeLower = type.toLocaleLowerCase();
464
+ const { close, error, leave, join } = constants_1.CHANNEL_EVENTS;
465
+ const events = [close, error, leave, join];
466
+ if (ref && events.indexOf(typeLower) >= 0 && ref !== this._joinRef()) {
467
+ return;
468
+ }
469
+ let handledPayload = this._onMessage(typeLower, payload, ref);
470
+ if (payload && !handledPayload) {
471
+ throw 'channel onMessage callbacks must return the payload, modified or unmodified';
472
+ }
473
+ if (['insert', 'update', 'delete'].includes(typeLower)) {
474
+ (_a = this.bindings.postgres_changes) === null || _a === void 0 ? void 0 : _a.filter((bind) => {
475
+ var _a, _b, _c;
476
+ return ((_a = bind.filter) === null || _a === void 0 ? void 0 : _a.event) === '*' || ((_c = (_b = bind.filter) === null || _b === void 0 ? void 0 : _b.event) === null || _c === void 0 ? void 0 : _c.toLocaleLowerCase()) === typeLower;
477
+ }).map((bind) => bind.callback(handledPayload, ref));
478
+ }
479
+ else {
480
+ (_b = this.bindings[typeLower]) === null || _b === void 0 ? void 0 : _b.filter((bind) => {
481
+ var _a, _b, _c, _d, _e, _f;
482
+ if (['broadcast', 'presence', 'postgres_changes'].includes(typeLower)) {
483
+ if ('id' in bind) {
484
+ const bindId = bind.id;
485
+ const bindEvent = (_a = bind.filter) === null || _a === void 0 ? void 0 : _a.event;
486
+ return (bindId &&
487
+ ((_b = payload.ids) === null || _b === void 0 ? void 0 : _b.includes(bindId)) &&
488
+ (bindEvent === '*' ||
489
+ (bindEvent === null || bindEvent === void 0 ? void 0 : bindEvent.toLocaleLowerCase()) === ((_c = payload.data) === null || _c === void 0 ? void 0 : _c.type.toLocaleLowerCase())));
490
+ }
491
+ else {
492
+ const bindEvent = (_e = (_d = bind === null || bind === void 0 ? void 0 : bind.filter) === null || _d === void 0 ? void 0 : _d.event) === null || _e === void 0 ? void 0 : _e.toLocaleLowerCase();
493
+ return bindEvent === '*' || bindEvent === ((_f = payload === null || payload === void 0 ? void 0 : payload.event) === null || _f === void 0 ? void 0 : _f.toLocaleLowerCase());
494
+ }
495
+ }
496
+ else {
497
+ return bind.type.toLocaleLowerCase() === typeLower;
498
+ }
499
+ }).map((bind) => {
500
+ if (typeof handledPayload === 'object' && 'ids' in handledPayload) {
501
+ const postgresChanges = handledPayload.data;
502
+ const { schema, table, commit_timestamp, type, errors } = postgresChanges;
503
+ const enrichedPayload = {
504
+ schema: schema,
505
+ table: table,
506
+ commit_timestamp: commit_timestamp,
507
+ eventType: type,
508
+ new: {},
509
+ old: {},
510
+ errors: errors,
511
+ };
512
+ handledPayload = Object.assign(Object.assign({}, enrichedPayload), this._getPayloadRecords(postgresChanges));
513
+ }
514
+ bind.callback(handledPayload, ref);
515
+ });
516
+ }
517
+ }
518
+ /** @internal */
519
+ _isClosed() {
520
+ return this.state === constants_1.CHANNEL_STATES.closed;
521
+ }
522
+ /** @internal */
523
+ _isJoined() {
524
+ return this.state === constants_1.CHANNEL_STATES.joined;
525
+ }
526
+ /** @internal */
527
+ _isJoining() {
528
+ return this.state === constants_1.CHANNEL_STATES.joining;
529
+ }
530
+ /** @internal */
531
+ _isLeaving() {
532
+ return this.state === constants_1.CHANNEL_STATES.leaving;
533
+ }
534
+ /** @internal */
535
+ _replyEventName(ref) {
536
+ return `chan_reply_${ref}`;
537
+ }
538
+ /** @internal */
539
+ _on(type, filter, callback) {
540
+ const typeLower = type.toLocaleLowerCase();
541
+ const binding = {
542
+ type: typeLower,
543
+ filter: filter,
544
+ callback: callback,
545
+ };
546
+ if (this.bindings[typeLower]) {
547
+ this.bindings[typeLower].push(binding);
548
+ }
549
+ else {
550
+ this.bindings[typeLower] = [binding];
551
+ }
552
+ return this;
553
+ }
554
+ /** @internal */
555
+ _off(type, filter) {
556
+ const typeLower = type.toLocaleLowerCase();
557
+ if (this.bindings[typeLower]) {
558
+ this.bindings[typeLower] = this.bindings[typeLower].filter((bind) => {
559
+ var _a;
560
+ return !(((_a = bind.type) === null || _a === void 0 ? void 0 : _a.toLocaleLowerCase()) === typeLower &&
561
+ RealtimeChannel.isEqual(bind.filter, filter));
562
+ });
563
+ }
564
+ return this;
565
+ }
566
+ /** @internal */
567
+ static isEqual(obj1, obj2) {
568
+ if (Object.keys(obj1).length !== Object.keys(obj2).length) {
569
+ return false;
570
+ }
571
+ for (const k in obj1) {
572
+ if (obj1[k] !== obj2[k]) {
573
+ return false;
574
+ }
575
+ }
576
+ return true;
577
+ }
578
+ /**
579
+ * Compares two optional filter values for equality.
580
+ * Treats undefined, null, and empty string as equivalent empty values.
581
+ * @internal
582
+ */
583
+ static isFilterValueEqual(serverValue, clientValue) {
584
+ const normalizedServer = serverValue !== null && serverValue !== void 0 ? serverValue : undefined;
585
+ const normalizedClient = clientValue !== null && clientValue !== void 0 ? clientValue : undefined;
586
+ return normalizedServer === normalizedClient;
587
+ }
588
+ /** @internal */
589
+ _rejoinUntilConnected() {
590
+ this.rejoinTimer.scheduleTimeout();
591
+ if (this.socket.isConnected()) {
592
+ this._rejoin();
593
+ }
594
+ }
595
+ /**
596
+ * Registers a callback that will be executed when the channel closes.
597
+ *
598
+ * @internal
599
+ */
600
+ _onClose(callback) {
601
+ this._on(constants_1.CHANNEL_EVENTS.close, {}, callback);
602
+ }
603
+ /**
604
+ * Registers a callback that will be executed when the channel encounteres an error.
605
+ *
606
+ * @internal
607
+ */
608
+ _onError(callback) {
609
+ this._on(constants_1.CHANNEL_EVENTS.error, {}, (reason) => callback(reason));
610
+ }
611
+ /**
612
+ * Returns `true` if the socket is connected and the channel has been joined.
613
+ *
614
+ * @internal
615
+ */
616
+ _canPush() {
617
+ return this.socket.isConnected() && this._isJoined();
618
+ }
619
+ /** @internal */
620
+ _rejoin(timeout = this.timeout) {
621
+ if (this._isLeaving()) {
622
+ return;
623
+ }
624
+ this.socket._leaveOpenTopic(this.topic);
625
+ this.state = constants_1.CHANNEL_STATES.joining;
626
+ this.joinPush.resend(timeout);
627
+ }
628
+ /** @internal */
629
+ _getPayloadRecords(payload) {
630
+ const records = {
631
+ new: {},
632
+ old: {},
633
+ };
634
+ if (payload.type === 'INSERT' || payload.type === 'UPDATE') {
635
+ records.new = Transformers.convertChangeData(payload.columns, payload.record);
636
+ }
637
+ if (payload.type === 'UPDATE' || payload.type === 'DELETE') {
638
+ records.old = Transformers.convertChangeData(payload.columns, payload.old_record);
639
+ }
640
+ return records;
641
+ }
642
+ }
643
+ exports.default = RealtimeChannel;
644
+ //# sourceMappingURL=RealtimeChannel.js.map