@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.
- package/README.md +32 -0
- package/changes.json +14 -0
- package/dist/main/RealtimeChannel.d.ts +370 -0
- package/dist/main/RealtimeChannel.d.ts.map +1 -0
- package/dist/main/RealtimeChannel.js +644 -0
- package/dist/main/RealtimeChannel.js.map +1 -0
- package/dist/main/RealtimeClient.d.ts +238 -0
- package/dist/main/RealtimeClient.d.ts.map +1 -0
- package/dist/main/RealtimeClient.js +870 -0
- package/dist/main/RealtimeClient.js.map +1 -0
- package/dist/main/RealtimePresence.d.ts +76 -0
- package/dist/main/RealtimePresence.d.ts.map +1 -0
- package/dist/main/RealtimePresence.js +237 -0
- package/dist/main/RealtimePresence.js.map +1 -0
- package/dist/main/index.d.ts +6 -0
- package/dist/main/index.d.ts.map +1 -0
- package/dist/main/index.js +18 -0
- package/dist/main/index.js.map +1 -0
- package/dist/main/lib/constants.d.ts +39 -0
- package/dist/main/lib/constants.d.ts.map +1 -0
- package/dist/main/lib/constants.js +48 -0
- package/dist/main/lib/constants.js.map +1 -0
- 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/serializer.d.ts +33 -0
- package/dist/main/lib/serializer.d.ts.map +1 -0
- package/dist/main/lib/serializer.js +155 -0
- package/dist/main/lib/serializer.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/transformers.d.ts +109 -0
- package/dist/main/lib/transformers.d.ts.map +1 -0
- package/dist/main/lib/transformers.js +241 -0
- package/dist/main/lib/transformers.js.map +1 -0
- package/dist/main/lib/version.d.ts +2 -0
- package/dist/main/lib/version.d.ts.map +1 -0
- package/dist/main/lib/version.js +11 -0
- package/dist/main/lib/version.js.map +1 -0
- package/dist/main/lib/websocket-factory.d.ts +81 -0
- package/dist/main/lib/websocket-factory.d.ts.map +1 -0
- package/dist/main/lib/websocket-factory.js +131 -0
- package/dist/main/lib/websocket-factory.js.map +1 -0
- package/dist/module/RealtimeChannel.d.ts +370 -0
- package/dist/module/RealtimeChannel.d.ts.map +1 -0
- package/dist/module/RealtimeChannel.js +639 -0
- package/dist/module/RealtimeChannel.js.map +1 -0
- package/dist/module/RealtimeClient.d.ts +238 -0
- package/dist/module/RealtimeClient.d.ts.map +1 -0
- package/dist/module/RealtimeClient.js +866 -0
- package/dist/module/RealtimeClient.js.map +1 -0
- package/dist/module/RealtimePresence.d.ts +76 -0
- package/dist/module/RealtimePresence.d.ts.map +1 -0
- package/dist/module/RealtimePresence.js +233 -0
- package/dist/module/RealtimePresence.js.map +1 -0
- package/dist/module/index.d.ts +6 -0
- package/dist/module/index.d.ts.map +1 -0
- package/dist/module/index.js +6 -0
- package/dist/module/index.js.map +1 -0
- package/dist/module/lib/constants.d.ts +39 -0
- package/dist/module/lib/constants.d.ts.map +1 -0
- package/dist/module/lib/constants.js +45 -0
- package/dist/module/lib/constants.js.map +1 -0
- 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/serializer.d.ts +33 -0
- package/dist/module/lib/serializer.d.ts.map +1 -0
- package/dist/module/lib/serializer.js +152 -0
- package/dist/module/lib/serializer.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/transformers.d.ts +109 -0
- package/dist/module/lib/transformers.d.ts.map +1 -0
- package/dist/module/lib/transformers.js +229 -0
- package/dist/module/lib/transformers.js.map +1 -0
- package/dist/module/lib/version.d.ts +2 -0
- package/dist/module/lib/version.d.ts.map +1 -0
- package/dist/module/lib/version.js +8 -0
- package/dist/module/lib/version.js.map +1 -0
- package/dist/module/lib/websocket-factory.d.ts +81 -0
- package/dist/module/lib/websocket-factory.d.ts.map +1 -0
- package/dist/module/lib/websocket-factory.js +127 -0
- package/dist/module/lib/websocket-factory.js.map +1 -0
- package/dist/tsconfig.module.tsbuildinfo +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +85 -0
- package/src/RealtimeChannel.ts +1039 -0
- package/src/RealtimeClient.ts +1023 -0
- package/src/RealtimePresence.ts +346 -0
- package/src/index.ts +53 -0
- package/src/lib/constants.ts +49 -0
- package/src/lib/push.ts +121 -0
- package/src/lib/serializer.ts +203 -0
- package/src/lib/timer.ts +43 -0
- package/src/lib/transformers.ts +270 -0
- package/src/lib/version.ts +7 -0
- 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
|