@supabase/realtime-js 2.99.3 → 2.100.0-canary.1
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 +37 -56
- package/dist/main/RealtimeClient.d.ts.map +1 -1
- package/dist/main/RealtimeClient.js +233 -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 +37 -56
- package/dist/module/RealtimeClient.d.ts.map +1 -1
- package/dist/module/RealtimeClient.js +234 -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 +290 -581
- 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
package/src/RealtimeChannel.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { CHANNEL_EVENTS, CHANNEL_STATES
|
|
2
|
-
import
|
|
1
|
+
import { CHANNEL_EVENTS, CHANNEL_STATES } from './lib/constants'
|
|
2
|
+
import type { ChannelState } from './lib/constants'
|
|
3
3
|
import type RealtimeClient from './RealtimeClient'
|
|
4
|
-
import Timer from './lib/timer'
|
|
5
4
|
import RealtimePresence, { REALTIME_PRESENCE_LISTEN_EVENTS } from './RealtimePresence'
|
|
6
5
|
import type {
|
|
7
6
|
RealtimePresenceJoinPayload,
|
|
@@ -10,6 +9,8 @@ import type {
|
|
|
10
9
|
} from './RealtimePresence'
|
|
11
10
|
import * as Transformers from './lib/transformers'
|
|
12
11
|
import { httpEndpointURL } from './lib/transformers'
|
|
12
|
+
import ChannelAdapter from './phoenix/channelAdapter'
|
|
13
|
+
import { ChannelBindingCallback, ChannelOnErrorCallback } from './phoenix/types'
|
|
13
14
|
|
|
14
15
|
type ReplayOption = {
|
|
15
16
|
since: number
|
|
@@ -147,7 +148,7 @@ export enum REALTIME_SUBSCRIBE_STATES {
|
|
|
147
148
|
|
|
148
149
|
export const REALTIME_CHANNEL_STATES = CHANNEL_STATES
|
|
149
150
|
|
|
150
|
-
|
|
151
|
+
type PostgresChangesFilters = {
|
|
151
152
|
postgres_changes: {
|
|
152
153
|
id: string
|
|
153
154
|
event: string
|
|
@@ -156,30 +157,52 @@ interface PostgresChangesFilters {
|
|
|
156
157
|
filter?: string
|
|
157
158
|
}[]
|
|
158
159
|
}
|
|
160
|
+
|
|
161
|
+
type Binding = {
|
|
162
|
+
type: string
|
|
163
|
+
filter: { [key: string]: any }
|
|
164
|
+
callback: ChannelBindingCallback
|
|
165
|
+
ref: number
|
|
166
|
+
id?: string
|
|
167
|
+
}
|
|
168
|
+
|
|
159
169
|
/** A channel is the basic building block of Realtime
|
|
160
170
|
* and narrows the scope of data flow to subscribed clients.
|
|
161
171
|
* You can think of a channel as a chatroom where participants are able to see who's online
|
|
162
172
|
* and send and receive messages.
|
|
163
173
|
*/
|
|
164
174
|
export default class RealtimeChannel {
|
|
165
|
-
bindings: {
|
|
166
|
-
[key: string]: {
|
|
167
|
-
type: string
|
|
168
|
-
filter: { [key: string]: any }
|
|
169
|
-
callback: Function
|
|
170
|
-
id?: string
|
|
171
|
-
}[]
|
|
172
|
-
} = {}
|
|
173
|
-
timeout: number
|
|
174
|
-
state: CHANNEL_STATES = CHANNEL_STATES.closed
|
|
175
|
-
joinedOnce = false
|
|
176
|
-
joinPush: Push
|
|
177
|
-
rejoinTimer: Timer
|
|
178
|
-
pushBuffer: Push[] = []
|
|
179
|
-
presence: RealtimePresence
|
|
180
|
-
broadcastEndpointURL: string
|
|
175
|
+
bindings: Record<string, Binding[]> = {}
|
|
181
176
|
subTopic: string
|
|
177
|
+
broadcastEndpointURL: string
|
|
182
178
|
private: boolean
|
|
179
|
+
presence: RealtimePresence
|
|
180
|
+
/** @internal */
|
|
181
|
+
channelAdapter: ChannelAdapter
|
|
182
|
+
|
|
183
|
+
get state() {
|
|
184
|
+
return this.channelAdapter.state
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
set state(state: ChannelState) {
|
|
188
|
+
this.channelAdapter.state = state
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
get joinedOnce() {
|
|
192
|
+
return this.channelAdapter.joinedOnce
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
get timeout() {
|
|
196
|
+
return this.socket.timeout
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
get joinPush() {
|
|
200
|
+
return this.channelAdapter.joinPush
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
get rejoinTimer() {
|
|
204
|
+
return this.channelAdapter.rejoinTimer
|
|
205
|
+
}
|
|
183
206
|
|
|
184
207
|
/**
|
|
185
208
|
* Creates a channel that can broadcast messages, sync presence, and listen to Postgres changes.
|
|
@@ -212,53 +235,17 @@ export default class RealtimeChannel {
|
|
|
212
235
|
},
|
|
213
236
|
...params.config,
|
|
214
237
|
}
|
|
215
|
-
|
|
216
|
-
this.
|
|
217
|
-
this.
|
|
218
|
-
|
|
219
|
-
this.state = CHANNEL_STATES.joined
|
|
220
|
-
this.rejoinTimer.reset()
|
|
221
|
-
this.pushBuffer.forEach((pushEvent: Push) => pushEvent.send())
|
|
222
|
-
this.pushBuffer = []
|
|
223
|
-
})
|
|
238
|
+
|
|
239
|
+
this.channelAdapter = new ChannelAdapter(this.socket.socketAdapter, topic, this.params)
|
|
240
|
+
this.presence = new RealtimePresence(this)
|
|
241
|
+
|
|
224
242
|
this._onClose(() => {
|
|
225
|
-
this.rejoinTimer.reset()
|
|
226
|
-
this.socket.log('channel', `close ${this.topic} ${this._joinRef()}`)
|
|
227
|
-
this.state = CHANNEL_STATES.closed
|
|
228
243
|
this.socket._remove(this)
|
|
229
244
|
})
|
|
230
|
-
this._onError((reason: string) => {
|
|
231
|
-
if (this._isLeaving() || this._isClosed()) {
|
|
232
|
-
return
|
|
233
|
-
}
|
|
234
|
-
this.socket.log('channel', `error ${this.topic}`, reason)
|
|
235
|
-
this.state = CHANNEL_STATES.errored
|
|
236
|
-
this.rejoinTimer.scheduleTimeout()
|
|
237
|
-
})
|
|
238
|
-
this.joinPush.receive('timeout', () => {
|
|
239
|
-
if (!this._isJoining()) {
|
|
240
|
-
return
|
|
241
|
-
}
|
|
242
|
-
this.socket.log('channel', `timeout ${this.topic}`, this.joinPush.timeout)
|
|
243
|
-
this.state = CHANNEL_STATES.errored
|
|
244
|
-
this.rejoinTimer.scheduleTimeout()
|
|
245
|
-
})
|
|
246
245
|
|
|
247
|
-
this.
|
|
248
|
-
if (this._isLeaving() || this._isClosed()) {
|
|
249
|
-
return
|
|
250
|
-
}
|
|
251
|
-
this.socket.log('channel', `error ${this.topic}`, reason)
|
|
252
|
-
this.state = CHANNEL_STATES.errored
|
|
253
|
-
this.rejoinTimer.scheduleTimeout()
|
|
254
|
-
})
|
|
255
|
-
this._on(CHANNEL_EVENTS.reply, {}, (payload: any, ref: string) => {
|
|
256
|
-
this._trigger(this._replyEventName(ref), payload)
|
|
257
|
-
})
|
|
258
|
-
|
|
259
|
-
this.presence = new RealtimePresence(this)
|
|
246
|
+
this._updateFilterTransform()
|
|
260
247
|
|
|
261
|
-
this.broadcastEndpointURL = httpEndpointURL(this.socket.
|
|
248
|
+
this.broadcastEndpointURL = httpEndpointURL(this.socket.socketAdapter.endPointURL())
|
|
262
249
|
this.private = this.params.config.private || false
|
|
263
250
|
|
|
264
251
|
if (!this.private && this.params.config?.broadcast?.replay) {
|
|
@@ -274,7 +261,7 @@ export default class RealtimeChannel {
|
|
|
274
261
|
if (!this.socket.isConnected()) {
|
|
275
262
|
this.socket.connect()
|
|
276
263
|
}
|
|
277
|
-
if (this.
|
|
264
|
+
if (this.channelAdapter.isClosed()) {
|
|
278
265
|
const {
|
|
279
266
|
config: { broadcast, presence, private: isPrivate },
|
|
280
267
|
} = this.params
|
|
@@ -297,16 +284,18 @@ export default class RealtimeChannel {
|
|
|
297
284
|
accessTokenPayload.access_token = this.socket.accessTokenValue
|
|
298
285
|
}
|
|
299
286
|
|
|
300
|
-
this._onError((
|
|
287
|
+
this._onError((reason: unknown) => {
|
|
288
|
+
callback?.(REALTIME_SUBSCRIBE_STATES.CHANNEL_ERROR, reason as Error)
|
|
289
|
+
})
|
|
301
290
|
|
|
302
291
|
this._onClose(() => callback?.(REALTIME_SUBSCRIBE_STATES.CLOSED))
|
|
303
292
|
|
|
304
293
|
this.updateJoinPayload({ ...{ config }, ...accessTokenPayload })
|
|
305
294
|
|
|
306
|
-
this.
|
|
307
|
-
this._rejoin(timeout)
|
|
295
|
+
this._updateFilterMessage()
|
|
308
296
|
|
|
309
|
-
this.
|
|
297
|
+
this.channelAdapter
|
|
298
|
+
.subscribe(timeout)
|
|
310
299
|
.receive('ok', async ({ postgres_changes }: PostgresChangesFilters) => {
|
|
311
300
|
// Only refresh auth if using callback-based tokens
|
|
312
301
|
if (!this.socket._isManualToken()) {
|
|
@@ -315,46 +304,9 @@ export default class RealtimeChannel {
|
|
|
315
304
|
if (postgres_changes === undefined) {
|
|
316
305
|
callback?.(REALTIME_SUBSCRIBE_STATES.SUBSCRIBED)
|
|
317
306
|
return
|
|
318
|
-
} else {
|
|
319
|
-
const clientPostgresBindings = this.bindings.postgres_changes
|
|
320
|
-
const bindingsLen = clientPostgresBindings?.length ?? 0
|
|
321
|
-
const newPostgresBindings = []
|
|
322
|
-
|
|
323
|
-
for (let i = 0; i < bindingsLen; i++) {
|
|
324
|
-
const clientPostgresBinding = clientPostgresBindings[i]
|
|
325
|
-
const {
|
|
326
|
-
filter: { event, schema, table, filter },
|
|
327
|
-
} = clientPostgresBinding
|
|
328
|
-
const serverPostgresFilter = postgres_changes && postgres_changes[i]
|
|
329
|
-
|
|
330
|
-
if (
|
|
331
|
-
serverPostgresFilter &&
|
|
332
|
-
serverPostgresFilter.event === event &&
|
|
333
|
-
RealtimeChannel.isFilterValueEqual(serverPostgresFilter.schema, schema) &&
|
|
334
|
-
RealtimeChannel.isFilterValueEqual(serverPostgresFilter.table, table) &&
|
|
335
|
-
RealtimeChannel.isFilterValueEqual(serverPostgresFilter.filter, filter)
|
|
336
|
-
) {
|
|
337
|
-
newPostgresBindings.push({
|
|
338
|
-
...clientPostgresBinding,
|
|
339
|
-
id: serverPostgresFilter.id,
|
|
340
|
-
})
|
|
341
|
-
} else {
|
|
342
|
-
this.unsubscribe()
|
|
343
|
-
this.state = CHANNEL_STATES.errored
|
|
344
|
-
|
|
345
|
-
callback?.(
|
|
346
|
-
REALTIME_SUBSCRIBE_STATES.CHANNEL_ERROR,
|
|
347
|
-
new Error('mismatch between server and client bindings for postgres changes')
|
|
348
|
-
)
|
|
349
|
-
return
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
this.bindings.postgres_changes = newPostgresBindings
|
|
354
|
-
|
|
355
|
-
callback && callback(REALTIME_SUBSCRIBE_STATES.SUBSCRIBED)
|
|
356
|
-
return
|
|
357
307
|
}
|
|
308
|
+
|
|
309
|
+
this._updatePostgresBindings(postgres_changes, callback)
|
|
358
310
|
})
|
|
359
311
|
.receive('error', (error: { [key: string]: any }) => {
|
|
360
312
|
this.state = CHANNEL_STATES.errored
|
|
@@ -362,16 +314,59 @@ export default class RealtimeChannel {
|
|
|
362
314
|
REALTIME_SUBSCRIBE_STATES.CHANNEL_ERROR,
|
|
363
315
|
new Error(JSON.stringify(Object.values(error).join(', ') || 'error'))
|
|
364
316
|
)
|
|
365
|
-
return
|
|
366
317
|
})
|
|
367
318
|
.receive('timeout', () => {
|
|
368
319
|
callback?.(REALTIME_SUBSCRIBE_STATES.TIMED_OUT)
|
|
369
|
-
return
|
|
370
320
|
})
|
|
371
321
|
}
|
|
372
322
|
return this
|
|
373
323
|
}
|
|
374
324
|
|
|
325
|
+
private _updatePostgresBindings(
|
|
326
|
+
postgres_changes: PostgresChangesFilters['postgres_changes'],
|
|
327
|
+
callback?: (status: REALTIME_SUBSCRIBE_STATES, err?: Error) => void
|
|
328
|
+
) {
|
|
329
|
+
const clientPostgresBindings = this.bindings.postgres_changes
|
|
330
|
+
const bindingsLen = clientPostgresBindings?.length ?? 0
|
|
331
|
+
const newPostgresBindings = []
|
|
332
|
+
|
|
333
|
+
for (let i = 0; i < bindingsLen; i++) {
|
|
334
|
+
const clientPostgresBinding = clientPostgresBindings[i]
|
|
335
|
+
const {
|
|
336
|
+
filter: { event, schema, table, filter },
|
|
337
|
+
} = clientPostgresBinding
|
|
338
|
+
const serverPostgresFilter = postgres_changes && postgres_changes[i]
|
|
339
|
+
|
|
340
|
+
if (
|
|
341
|
+
serverPostgresFilter &&
|
|
342
|
+
serverPostgresFilter.event === event &&
|
|
343
|
+
RealtimeChannel.isFilterValueEqual(serverPostgresFilter.schema, schema) &&
|
|
344
|
+
RealtimeChannel.isFilterValueEqual(serverPostgresFilter.table, table) &&
|
|
345
|
+
RealtimeChannel.isFilterValueEqual(serverPostgresFilter.filter, filter)
|
|
346
|
+
) {
|
|
347
|
+
newPostgresBindings.push({
|
|
348
|
+
...clientPostgresBinding,
|
|
349
|
+
id: serverPostgresFilter.id,
|
|
350
|
+
})
|
|
351
|
+
} else {
|
|
352
|
+
this.unsubscribe()
|
|
353
|
+
this.state = CHANNEL_STATES.errored
|
|
354
|
+
|
|
355
|
+
callback?.(
|
|
356
|
+
REALTIME_SUBSCRIBE_STATES.CHANNEL_ERROR,
|
|
357
|
+
new Error('mismatch between server and client bindings for postgres changes')
|
|
358
|
+
)
|
|
359
|
+
return
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
this.bindings.postgres_changes = newPostgresBindings
|
|
364
|
+
|
|
365
|
+
if (this.state != CHANNEL_STATES.errored && callback) {
|
|
366
|
+
callback(REALTIME_SUBSCRIBE_STATES.SUBSCRIBED)
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
375
370
|
/**
|
|
376
371
|
* Returns the current presence state for this channel.
|
|
377
372
|
*
|
|
@@ -431,6 +426,11 @@ export default class RealtimeChannel {
|
|
|
431
426
|
filter: { event: `${REALTIME_PRESENCE_LISTEN_EVENTS.LEAVE}` },
|
|
432
427
|
callback: (payload: RealtimePresenceLeavePayload<T>) => void
|
|
433
428
|
): RealtimeChannel
|
|
429
|
+
on<T extends { [key: string]: any }>(
|
|
430
|
+
type: `${REALTIME_LISTEN_TYPES.PRESENCE}`,
|
|
431
|
+
filter: { event: '*' },
|
|
432
|
+
callback: (payload?: RealtimePresenceJoinPayload<T> | RealtimePresenceLeavePayload<T>) => void
|
|
433
|
+
): RealtimeChannel
|
|
434
434
|
on<T extends { [key: string]: any }>(
|
|
435
435
|
type: `${REALTIME_LISTEN_TYPES.POSTGRES_CHANGES}`,
|
|
436
436
|
filter: RealtimePostgresChangesFilter<`${REALTIME_POSTGRES_CHANGES_LISTEN_EVENT.ALL}`>,
|
|
@@ -534,12 +534,9 @@ export default class RealtimeChannel {
|
|
|
534
534
|
filter: { event: string; [key: string]: string },
|
|
535
535
|
callback: (payload: any) => void
|
|
536
536
|
): RealtimeChannel {
|
|
537
|
-
if (this.
|
|
538
|
-
this.socket.log(
|
|
539
|
-
|
|
540
|
-
`resubscribe to ${this.topic} due to change in presence callbacks on joined channel`
|
|
541
|
-
)
|
|
542
|
-
this.unsubscribe().then(async () => await this.subscribe())
|
|
537
|
+
if (this.channelAdapter.isJoined() && type === REALTIME_LISTEN_TYPES.PRESENCE) {
|
|
538
|
+
this.socket.log('channel', `cannot add presence callbacks for ${this.topic} after joining.`)
|
|
539
|
+
throw new Error('cannot add presence callbacks after joining a channel')
|
|
543
540
|
}
|
|
544
541
|
return this._on(type, filter, callback)
|
|
545
542
|
}
|
|
@@ -624,7 +621,7 @@ export default class RealtimeChannel {
|
|
|
624
621
|
},
|
|
625
622
|
opts: { [key: string]: any } = {}
|
|
626
623
|
): Promise<RealtimeChannelSendResponse> {
|
|
627
|
-
if (!this.
|
|
624
|
+
if (!this.channelAdapter.canPush() && args.type === 'broadcast') {
|
|
628
625
|
console.warn(
|
|
629
626
|
'Realtime send() is automatically falling back to REST API. ' +
|
|
630
627
|
'This behavior will be deprecated in the future. ' +
|
|
@@ -674,7 +671,7 @@ export default class RealtimeChannel {
|
|
|
674
671
|
}
|
|
675
672
|
} else {
|
|
676
673
|
return new Promise((resolve) => {
|
|
677
|
-
const push = this.
|
|
674
|
+
const push = this.channelAdapter.push(args.type, args, opts.timeout || this.timeout)
|
|
678
675
|
|
|
679
676
|
if (args.type === 'broadcast' && !this.params?.config?.broadcast?.ack) {
|
|
680
677
|
resolve('ok')
|
|
@@ -691,8 +688,8 @@ export default class RealtimeChannel {
|
|
|
691
688
|
* Updates the payload that will be sent the next time the channel joins (reconnects).
|
|
692
689
|
* Useful for rotating access tokens or updating config without re-creating the channel.
|
|
693
690
|
*/
|
|
694
|
-
updateJoinPayload(payload:
|
|
695
|
-
this.
|
|
691
|
+
updateJoinPayload(payload: Record<string, any>) {
|
|
692
|
+
this.channelAdapter.updateJoinPayload(payload)
|
|
696
693
|
}
|
|
697
694
|
|
|
698
695
|
/**
|
|
@@ -704,56 +701,24 @@ export default class RealtimeChannel {
|
|
|
704
701
|
* To receive leave acknowledgements, use the a `receive` hook to bind to the server ack, ie:
|
|
705
702
|
* channel.unsubscribe().receive("ok", () => alert("left!") )
|
|
706
703
|
*/
|
|
707
|
-
unsubscribe(timeout = this.timeout)
|
|
708
|
-
this.state = CHANNEL_STATES.leaving
|
|
709
|
-
const onClose = () => {
|
|
710
|
-
this.socket.log('channel', `leave ${this.topic}`)
|
|
711
|
-
this._trigger(CHANNEL_EVENTS.close, 'leave', this._joinRef())
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
this.joinPush.destroy()
|
|
715
|
-
|
|
716
|
-
let leavePush: Push | null = null
|
|
717
|
-
|
|
704
|
+
async unsubscribe(timeout = this.timeout) {
|
|
718
705
|
return new Promise<RealtimeChannelSendResponse>((resolve) => {
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
.receive('ok', () =>
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
})
|
|
725
|
-
.receive('timeout', () => {
|
|
726
|
-
onClose()
|
|
727
|
-
resolve('timed out')
|
|
728
|
-
})
|
|
729
|
-
.receive('error', () => {
|
|
730
|
-
resolve('error')
|
|
731
|
-
})
|
|
732
|
-
|
|
733
|
-
leavePush.send()
|
|
734
|
-
if (!this._canPush()) {
|
|
735
|
-
leavePush.trigger('ok', {})
|
|
736
|
-
}
|
|
737
|
-
}).finally(() => {
|
|
738
|
-
leavePush?.destroy()
|
|
706
|
+
this.channelAdapter
|
|
707
|
+
.unsubscribe(timeout)
|
|
708
|
+
.receive('ok', () => resolve('ok'))
|
|
709
|
+
.receive('timeout', () => resolve('timed out'))
|
|
710
|
+
.receive('error', () => resolve('error'))
|
|
739
711
|
})
|
|
740
712
|
}
|
|
713
|
+
|
|
741
714
|
/**
|
|
742
|
-
* Teardown the channel.
|
|
743
|
-
*
|
|
744
715
|
* Destroys and stops related timers.
|
|
745
716
|
*/
|
|
746
717
|
teardown() {
|
|
747
|
-
this.
|
|
748
|
-
this.pushBuffer = []
|
|
749
|
-
this.rejoinTimer.reset()
|
|
750
|
-
this.joinPush.destroy()
|
|
751
|
-
this.state = CHANNEL_STATES.closed
|
|
752
|
-
this.bindings = {}
|
|
718
|
+
this.channelAdapter.teardown()
|
|
753
719
|
}
|
|
754
720
|
|
|
755
721
|
/** @internal */
|
|
756
|
-
|
|
757
722
|
async _fetchWithTimeout(url: string, options: { [key: string]: any }, timeout: number) {
|
|
758
723
|
const controller = new AbortController()
|
|
759
724
|
const id = setTimeout(() => controller.abort(), timeout)
|
|
@@ -769,195 +734,112 @@ export default class RealtimeChannel {
|
|
|
769
734
|
}
|
|
770
735
|
|
|
771
736
|
/** @internal */
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
737
|
+
_on(type: string, filter: { [key: string]: any }, callback: ChannelBindingCallback) {
|
|
738
|
+
const typeLower = type.toLocaleLowerCase()
|
|
739
|
+
|
|
740
|
+
const ref = this.channelAdapter.on(type, callback)
|
|
741
|
+
|
|
742
|
+
const binding: Binding = {
|
|
743
|
+
type: typeLower,
|
|
744
|
+
filter: filter,
|
|
745
|
+
callback: callback,
|
|
746
|
+
ref: ref,
|
|
775
747
|
}
|
|
776
|
-
|
|
777
|
-
if (this.
|
|
778
|
-
|
|
748
|
+
|
|
749
|
+
if (this.bindings[typeLower]) {
|
|
750
|
+
this.bindings[typeLower].push(binding)
|
|
779
751
|
} else {
|
|
780
|
-
this.
|
|
752
|
+
this.bindings[typeLower] = [binding]
|
|
781
753
|
}
|
|
782
754
|
|
|
783
|
-
|
|
784
|
-
}
|
|
755
|
+
this._updateFilterMessage()
|
|
785
756
|
|
|
786
|
-
|
|
787
|
-
_addToPushBuffer(pushEvent: Push) {
|
|
788
|
-
pushEvent.startTimeout()
|
|
789
|
-
this.pushBuffer.push(pushEvent)
|
|
790
|
-
|
|
791
|
-
// Enforce buffer size limit
|
|
792
|
-
if (this.pushBuffer.length > MAX_PUSH_BUFFER_SIZE) {
|
|
793
|
-
const removedPush = this.pushBuffer.shift()
|
|
794
|
-
if (removedPush) {
|
|
795
|
-
removedPush.destroy()
|
|
796
|
-
this.socket.log(
|
|
797
|
-
'channel',
|
|
798
|
-
`discarded push due to buffer overflow: ${removedPush.event}`,
|
|
799
|
-
removedPush.payload
|
|
800
|
-
)
|
|
801
|
-
}
|
|
802
|
-
}
|
|
757
|
+
return this
|
|
803
758
|
}
|
|
804
759
|
|
|
805
760
|
/**
|
|
806
|
-
*
|
|
807
|
-
*
|
|
808
|
-
* Receives all events for specialized message handling before dispatching to the channel callbacks.
|
|
809
|
-
* Must return the payload, modified or unmodified.
|
|
761
|
+
* Registers a callback that will be executed when the channel closes.
|
|
810
762
|
*
|
|
811
763
|
* @internal
|
|
812
764
|
*/
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
}
|
|
816
|
-
|
|
817
|
-
/** @internal */
|
|
818
|
-
_isMember(topic: string): boolean {
|
|
819
|
-
return this.topic === topic
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
/** @internal */
|
|
823
|
-
_joinRef(): string {
|
|
824
|
-
return this.joinPush.ref
|
|
765
|
+
private _onClose(callback: ChannelBindingCallback) {
|
|
766
|
+
this.channelAdapter.onClose(callback)
|
|
825
767
|
}
|
|
826
768
|
|
|
827
|
-
/**
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
}
|
|
835
|
-
let handledPayload = this._onMessage(typeLower, payload, ref)
|
|
836
|
-
if (payload && !handledPayload) {
|
|
837
|
-
throw 'channel onMessage callbacks must return the payload, modified or unmodified'
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
if (['insert', 'update', 'delete'].includes(typeLower)) {
|
|
841
|
-
this.bindings.postgres_changes
|
|
842
|
-
?.filter((bind) => {
|
|
843
|
-
return bind.filter?.event === '*' || bind.filter?.event?.toLocaleLowerCase() === typeLower
|
|
844
|
-
})
|
|
845
|
-
.map((bind) => bind.callback(handledPayload, ref))
|
|
846
|
-
} else {
|
|
847
|
-
this.bindings[typeLower]
|
|
848
|
-
?.filter((bind) => {
|
|
849
|
-
if (['broadcast', 'presence', 'postgres_changes'].includes(typeLower)) {
|
|
850
|
-
if ('id' in bind) {
|
|
851
|
-
const bindId = bind.id
|
|
852
|
-
const bindEvent = bind.filter?.event
|
|
853
|
-
return (
|
|
854
|
-
bindId &&
|
|
855
|
-
payload.ids?.includes(bindId) &&
|
|
856
|
-
(bindEvent === '*' ||
|
|
857
|
-
bindEvent?.toLocaleLowerCase() === payload.data?.type.toLocaleLowerCase())
|
|
858
|
-
)
|
|
859
|
-
} else {
|
|
860
|
-
const bindEvent = bind?.filter?.event?.toLocaleLowerCase()
|
|
861
|
-
return bindEvent === '*' || bindEvent === payload?.event?.toLocaleLowerCase()
|
|
862
|
-
}
|
|
863
|
-
} else {
|
|
864
|
-
return bind.type.toLocaleLowerCase() === typeLower
|
|
865
|
-
}
|
|
866
|
-
})
|
|
867
|
-
.map((bind) => {
|
|
868
|
-
if (typeof handledPayload === 'object' && 'ids' in handledPayload) {
|
|
869
|
-
const postgresChanges = handledPayload.data
|
|
870
|
-
const { schema, table, commit_timestamp, type, errors } = postgresChanges
|
|
871
|
-
const enrichedPayload = {
|
|
872
|
-
schema: schema,
|
|
873
|
-
table: table,
|
|
874
|
-
commit_timestamp: commit_timestamp,
|
|
875
|
-
eventType: type,
|
|
876
|
-
new: {},
|
|
877
|
-
old: {},
|
|
878
|
-
errors: errors,
|
|
879
|
-
}
|
|
880
|
-
handledPayload = {
|
|
881
|
-
...enrichedPayload,
|
|
882
|
-
...this._getPayloadRecords(postgresChanges),
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
bind.callback(handledPayload, ref)
|
|
886
|
-
})
|
|
887
|
-
}
|
|
888
|
-
}
|
|
889
|
-
|
|
890
|
-
/** @internal */
|
|
891
|
-
_isClosed(): boolean {
|
|
892
|
-
return this.state === CHANNEL_STATES.closed
|
|
893
|
-
}
|
|
894
|
-
|
|
895
|
-
/** @internal */
|
|
896
|
-
_isJoined(): boolean {
|
|
897
|
-
return this.state === CHANNEL_STATES.joined
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
/** @internal */
|
|
901
|
-
_isJoining(): boolean {
|
|
902
|
-
return this.state === CHANNEL_STATES.joining
|
|
769
|
+
/**
|
|
770
|
+
* Registers a callback that will be executed when the channel encounteres an error.
|
|
771
|
+
*
|
|
772
|
+
* @internal
|
|
773
|
+
*/
|
|
774
|
+
private _onError(callback: ChannelOnErrorCallback) {
|
|
775
|
+
this.channelAdapter.onError(callback)
|
|
903
776
|
}
|
|
904
777
|
|
|
905
778
|
/** @internal */
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
779
|
+
private _updateFilterMessage() {
|
|
780
|
+
this.channelAdapter.updateFilterBindings((binding, payload: any, ref) => {
|
|
781
|
+
const typeLower = binding.event.toLocaleLowerCase()
|
|
909
782
|
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
}
|
|
783
|
+
if (this._notThisChannelEvent(typeLower, ref)) {
|
|
784
|
+
return false
|
|
785
|
+
}
|
|
914
786
|
|
|
915
|
-
|
|
916
|
-
_on(type: string, filter: { [key: string]: any }, callback: Function) {
|
|
917
|
-
const typeLower = type.toLocaleLowerCase()
|
|
918
|
-
const binding = {
|
|
919
|
-
type: typeLower,
|
|
920
|
-
filter: filter,
|
|
921
|
-
callback: callback,
|
|
922
|
-
}
|
|
787
|
+
const bind = this.bindings[typeLower]?.find((bind) => bind.ref === binding.ref)
|
|
923
788
|
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
this.bindings[typeLower] = [binding]
|
|
928
|
-
}
|
|
789
|
+
if (!bind) {
|
|
790
|
+
return true
|
|
791
|
+
}
|
|
929
792
|
|
|
930
|
-
|
|
793
|
+
if (['broadcast', 'presence', 'postgres_changes'].includes(typeLower)) {
|
|
794
|
+
if ('id' in bind) {
|
|
795
|
+
const bindId = bind.id
|
|
796
|
+
const bindEvent = bind.filter?.event
|
|
797
|
+
return (
|
|
798
|
+
bindId &&
|
|
799
|
+
payload.ids?.includes(bindId) &&
|
|
800
|
+
(bindEvent === '*' ||
|
|
801
|
+
bindEvent?.toLocaleLowerCase() === payload.data?.type.toLocaleLowerCase())
|
|
802
|
+
)
|
|
803
|
+
} else {
|
|
804
|
+
const bindEvent = bind?.filter?.event?.toLocaleLowerCase()
|
|
805
|
+
return bindEvent === '*' || bindEvent === payload?.event?.toLocaleLowerCase()
|
|
806
|
+
}
|
|
807
|
+
} else {
|
|
808
|
+
return bind.type.toLocaleLowerCase() === typeLower
|
|
809
|
+
}
|
|
810
|
+
})
|
|
931
811
|
}
|
|
932
812
|
|
|
933
813
|
/** @internal */
|
|
934
|
-
|
|
935
|
-
const
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
this.bindings[typeLower] = this.bindings[typeLower].filter((bind) => {
|
|
939
|
-
return !(
|
|
940
|
-
bind.type?.toLocaleLowerCase() === typeLower &&
|
|
941
|
-
RealtimeChannel.isEqual(bind.filter, filter)
|
|
942
|
-
)
|
|
943
|
-
})
|
|
944
|
-
}
|
|
945
|
-
return this
|
|
814
|
+
private _notThisChannelEvent(event: string, ref?: string | null) {
|
|
815
|
+
const { close, error, leave, join } = CHANNEL_EVENTS
|
|
816
|
+
const events: string[] = [close, error, leave, join]
|
|
817
|
+
return ref && events.includes(event) && ref !== this.joinPush.ref
|
|
946
818
|
}
|
|
947
819
|
|
|
948
820
|
/** @internal */
|
|
949
|
-
private
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
821
|
+
private _updateFilterTransform() {
|
|
822
|
+
this.channelAdapter.updatePayloadTransform((event, payload: any, ref) => {
|
|
823
|
+
if (typeof payload === 'object' && 'ids' in payload) {
|
|
824
|
+
const postgresChanges = payload.data
|
|
825
|
+
const { schema, table, commit_timestamp, type, errors } = postgresChanges
|
|
826
|
+
const enrichedPayload = {
|
|
827
|
+
schema: schema,
|
|
828
|
+
table: table,
|
|
829
|
+
commit_timestamp: commit_timestamp,
|
|
830
|
+
eventType: type,
|
|
831
|
+
new: {},
|
|
832
|
+
old: {},
|
|
833
|
+
errors: errors,
|
|
834
|
+
}
|
|
835
|
+
return {
|
|
836
|
+
...enrichedPayload,
|
|
837
|
+
...this._getPayloadRecords(postgresChanges),
|
|
838
|
+
}
|
|
957
839
|
}
|
|
958
|
-
}
|
|
959
840
|
|
|
960
|
-
|
|
841
|
+
return payload
|
|
842
|
+
})
|
|
961
843
|
}
|
|
962
844
|
|
|
963
845
|
/**
|
|
@@ -974,51 +856,6 @@ export default class RealtimeChannel {
|
|
|
974
856
|
return normalizedServer === normalizedClient
|
|
975
857
|
}
|
|
976
858
|
|
|
977
|
-
/** @internal */
|
|
978
|
-
private _rejoinUntilConnected() {
|
|
979
|
-
this.rejoinTimer.scheduleTimeout()
|
|
980
|
-
if (this.socket.isConnected()) {
|
|
981
|
-
this._rejoin()
|
|
982
|
-
}
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
/**
|
|
986
|
-
* Registers a callback that will be executed when the channel closes.
|
|
987
|
-
*
|
|
988
|
-
* @internal
|
|
989
|
-
*/
|
|
990
|
-
private _onClose(callback: Function) {
|
|
991
|
-
this._on(CHANNEL_EVENTS.close, {}, callback)
|
|
992
|
-
}
|
|
993
|
-
|
|
994
|
-
/**
|
|
995
|
-
* Registers a callback that will be executed when the channel encounteres an error.
|
|
996
|
-
*
|
|
997
|
-
* @internal
|
|
998
|
-
*/
|
|
999
|
-
private _onError(callback: Function) {
|
|
1000
|
-
this._on(CHANNEL_EVENTS.error, {}, (reason: string) => callback(reason))
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
|
-
/**
|
|
1004
|
-
* Returns `true` if the socket is connected and the channel has been joined.
|
|
1005
|
-
*
|
|
1006
|
-
* @internal
|
|
1007
|
-
*/
|
|
1008
|
-
private _canPush(): boolean {
|
|
1009
|
-
return this.socket.isConnected() && this._isJoined()
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
|
-
/** @internal */
|
|
1013
|
-
private _rejoin(timeout = this.timeout): void {
|
|
1014
|
-
if (this._isLeaving()) {
|
|
1015
|
-
return
|
|
1016
|
-
}
|
|
1017
|
-
this.socket._leaveOpenTopic(this.topic)
|
|
1018
|
-
this.state = CHANNEL_STATES.joining
|
|
1019
|
-
this.joinPush.resend(timeout)
|
|
1020
|
-
}
|
|
1021
|
-
|
|
1022
859
|
/** @internal */
|
|
1023
860
|
private _getPayloadRecords(payload: any) {
|
|
1024
861
|
const records = {
|