@supabase/realtime-js 1.4.3 → 1.4.6
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 +69 -0
- package/dist/main/RealtimeChannel.d.ts.map +1 -0
- package/dist/main/RealtimeChannel.js +200 -0
- package/dist/main/RealtimeChannel.js.map +1 -0
- package/dist/main/RealtimeClient.d.ts +5 -2
- package/dist/main/RealtimeClient.d.ts.map +1 -1
- package/dist/main/RealtimeClient.js +39 -3
- package/dist/main/RealtimeClient.js.map +1 -1
- package/dist/main/RealtimePresence.d.ts +3 -3
- package/dist/main/RealtimePresence.d.ts.map +1 -1
- package/dist/main/RealtimePresence.js +2 -2
- package/dist/main/RealtimePresence.js.map +1 -1
- package/dist/main/lib/version.d.ts +1 -1
- package/dist/main/lib/version.js +1 -1
- package/dist/module/RealtimeChannel.d.ts +69 -0
- package/dist/module/RealtimeChannel.d.ts.map +1 -0
- package/dist/module/RealtimeChannel.js +194 -0
- package/dist/module/RealtimeChannel.js.map +1 -0
- package/dist/module/RealtimeClient.d.ts +5 -2
- package/dist/module/RealtimeClient.d.ts.map +1 -1
- package/dist/module/RealtimeClient.js +39 -3
- package/dist/module/RealtimeClient.js.map +1 -1
- package/dist/module/RealtimePresence.d.ts +3 -3
- package/dist/module/RealtimePresence.d.ts.map +1 -1
- package/dist/module/RealtimePresence.js +2 -2
- package/dist/module/RealtimePresence.js.map +1 -1
- package/dist/module/lib/version.d.ts +1 -1
- package/dist/module/lib/version.js +1 -1
- package/package.json +3 -2
- package/src/RealtimeChannel.ts +232 -0
- package/src/RealtimeClient.ts +36 -3
- package/src/RealtimePresence.ts +4 -4
- package/src/lib/version.ts +1 -1
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { CHANNEL_EVENTS, CHANNEL_STATES } from './lib/constants'
|
|
2
|
+
import Push from './lib/push'
|
|
3
|
+
import RealtimeClient from './RealtimeClient'
|
|
4
|
+
import Timer from './lib/timer'
|
|
5
|
+
import RealtimePresence from './RealtimePresence'
|
|
6
|
+
|
|
7
|
+
export default class RealtimeChannel {
|
|
8
|
+
bindings: any[] = []
|
|
9
|
+
timeout: number
|
|
10
|
+
state = CHANNEL_STATES.closed
|
|
11
|
+
joinedOnce = false
|
|
12
|
+
joinPush: Push
|
|
13
|
+
rejoinTimer: Timer
|
|
14
|
+
pushBuffer: Push[] = []
|
|
15
|
+
presence: RealtimePresence
|
|
16
|
+
|
|
17
|
+
constructor(
|
|
18
|
+
public topic: string,
|
|
19
|
+
public params: { [key: string]: unknown } = {},
|
|
20
|
+
public socket: RealtimeClient
|
|
21
|
+
) {
|
|
22
|
+
this.timeout = this.socket.timeout
|
|
23
|
+
this.joinPush = new Push(
|
|
24
|
+
this,
|
|
25
|
+
CHANNEL_EVENTS.join,
|
|
26
|
+
this.params,
|
|
27
|
+
this.timeout
|
|
28
|
+
)
|
|
29
|
+
this.rejoinTimer = new Timer(
|
|
30
|
+
() => this.rejoinUntilConnected(),
|
|
31
|
+
this.socket.reconnectAfterMs
|
|
32
|
+
)
|
|
33
|
+
this.joinPush.receive('ok', () => {
|
|
34
|
+
this.state = CHANNEL_STATES.joined
|
|
35
|
+
this.rejoinTimer.reset()
|
|
36
|
+
this.pushBuffer.forEach((pushEvent: Push) => pushEvent.send())
|
|
37
|
+
this.pushBuffer = []
|
|
38
|
+
})
|
|
39
|
+
this.onClose(() => {
|
|
40
|
+
this.rejoinTimer.reset()
|
|
41
|
+
this.socket.log('channel', `close ${this.topic} ${this.joinRef()}`)
|
|
42
|
+
this.state = CHANNEL_STATES.closed
|
|
43
|
+
this.socket.remove(this)
|
|
44
|
+
})
|
|
45
|
+
this.onError((reason: string) => {
|
|
46
|
+
if (this.isLeaving() || this.isClosed()) {
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
this.socket.log('channel', `error ${this.topic}`, reason)
|
|
50
|
+
this.state = CHANNEL_STATES.errored
|
|
51
|
+
this.rejoinTimer.scheduleTimeout()
|
|
52
|
+
})
|
|
53
|
+
this.joinPush.receive('timeout', () => {
|
|
54
|
+
if (!this.isJoining()) {
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
this.socket.log('channel', `timeout ${this.topic}`, this.joinPush.timeout)
|
|
58
|
+
this.state = CHANNEL_STATES.errored
|
|
59
|
+
this.rejoinTimer.scheduleTimeout()
|
|
60
|
+
})
|
|
61
|
+
this.on(CHANNEL_EVENTS.reply, {}, (payload: any, ref: string) => {
|
|
62
|
+
this.trigger(this.replyEventName(ref), payload)
|
|
63
|
+
})
|
|
64
|
+
this.presence = new RealtimePresence(this)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
list() {
|
|
68
|
+
return this.presence.list()
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
rejoinUntilConnected() {
|
|
72
|
+
this.rejoinTimer.scheduleTimeout()
|
|
73
|
+
if (this.socket.isConnected()) {
|
|
74
|
+
this.rejoin()
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
subscribe(timeout = this.timeout) {
|
|
79
|
+
if (this.joinedOnce) {
|
|
80
|
+
throw `tried to subscribe multiple times. 'subscribe' can only be called a single time per channel instance`
|
|
81
|
+
} else {
|
|
82
|
+
this.joinedOnce = true
|
|
83
|
+
this.rejoin(timeout)
|
|
84
|
+
return this.joinPush
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
onClose(callback: Function) {
|
|
89
|
+
this.on(CHANNEL_EVENTS.close, {}, callback)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
onError(callback: Function) {
|
|
93
|
+
this.on(CHANNEL_EVENTS.error, {}, (reason: string) => callback(reason))
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
on(type: string, eventFilter?: { [key: string]: any }, callback?: Function) {
|
|
97
|
+
this.bindings.push({ type, eventFilter: eventFilter ?? {}, callback })
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
off(event: string) {
|
|
101
|
+
this.bindings = this.bindings.filter((bind) => bind.event !== event)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
canPush() {
|
|
105
|
+
return this.socket.isConnected() && this.isJoined()
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
push(event: CHANNEL_EVENTS, payload: any, timeout = this.timeout) {
|
|
109
|
+
if (!this.joinedOnce) {
|
|
110
|
+
throw `tried to push '${event}' to '${this.topic}' before joining. Use channel.subscribe() before pushing events`
|
|
111
|
+
}
|
|
112
|
+
let pushEvent = new Push(this, event, payload, timeout)
|
|
113
|
+
if (this.canPush()) {
|
|
114
|
+
pushEvent.send()
|
|
115
|
+
} else {
|
|
116
|
+
pushEvent.startTimeout()
|
|
117
|
+
this.pushBuffer.push(pushEvent)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return pushEvent
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
updateJoinPayload(payload: { [key: string]: unknown }): void {
|
|
124
|
+
this.joinPush.updatePayload(payload)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Leaves the channel
|
|
129
|
+
*
|
|
130
|
+
* Unsubscribes from server events, and instructs channel to terminate on server.
|
|
131
|
+
* Triggers onClose() hooks.
|
|
132
|
+
*
|
|
133
|
+
* To receive leave acknowledgements, use the a `receive` hook to bind to the server ack, ie:
|
|
134
|
+
* channel.unsubscribe().receive("ok", () => alert("left!") )
|
|
135
|
+
*/
|
|
136
|
+
unsubscribe(timeout = this.timeout) {
|
|
137
|
+
this.state = CHANNEL_STATES.leaving
|
|
138
|
+
let onClose = () => {
|
|
139
|
+
this.socket.log('channel', `leave ${this.topic}`)
|
|
140
|
+
this.trigger(CHANNEL_EVENTS.close, 'leave', this.joinRef())
|
|
141
|
+
}
|
|
142
|
+
// Destroy joinPush to avoid connection timeouts during unscription phase
|
|
143
|
+
this.joinPush.destroy()
|
|
144
|
+
|
|
145
|
+
let leavePush = new Push(this, CHANNEL_EVENTS.leave, {}, timeout)
|
|
146
|
+
leavePush.receive('ok', () => onClose()).receive('timeout', () => onClose())
|
|
147
|
+
leavePush.send()
|
|
148
|
+
if (!this.canPush()) {
|
|
149
|
+
leavePush.trigger('ok', {})
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return leavePush
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Overridable message hook
|
|
157
|
+
*
|
|
158
|
+
* Receives all events for specialized message handling before dispatching to the channel callbacks.
|
|
159
|
+
* Must return the payload, modified or unmodified.
|
|
160
|
+
*/
|
|
161
|
+
onMessage(event: string, payload: any, ref?: string) {
|
|
162
|
+
return payload
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
isMember(topic: string) {
|
|
166
|
+
return this.topic === topic
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
joinRef() {
|
|
170
|
+
return this.joinPush.ref
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
rejoin(timeout = this.timeout) {
|
|
174
|
+
if (this.isLeaving()) {
|
|
175
|
+
return
|
|
176
|
+
}
|
|
177
|
+
this.socket.leaveOpenTopic(this.topic)
|
|
178
|
+
this.state = CHANNEL_STATES.joining
|
|
179
|
+
this.joinPush.resend(timeout)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
trigger(type: string, payload?: any, ref?: string) {
|
|
183
|
+
const { close, error, leave, join } = CHANNEL_EVENTS
|
|
184
|
+
const events: string[] = [close, error, leave, join]
|
|
185
|
+
if (ref && events.indexOf(type) >= 0 && ref !== this.joinRef()) {
|
|
186
|
+
return
|
|
187
|
+
}
|
|
188
|
+
const handledPayload = this.onMessage(type, payload, ref)
|
|
189
|
+
if (payload && !handledPayload) {
|
|
190
|
+
throw 'channel onMessage callbacks must return the payload, modified or unmodified'
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
this.bindings
|
|
194
|
+
.filter((bind) => {
|
|
195
|
+
return (
|
|
196
|
+
bind?.type === type &&
|
|
197
|
+
(bind?.eventFilter?.event === '*' ||
|
|
198
|
+
bind?.eventFilter?.event === payload?.event)
|
|
199
|
+
)
|
|
200
|
+
})
|
|
201
|
+
.map((bind) => bind.callback(handledPayload, ref))
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
send(payload: { type: string; [key: string]: any }) {
|
|
205
|
+
const push = this.push(payload.type as any, payload)
|
|
206
|
+
|
|
207
|
+
return new Promise((resolve) => {
|
|
208
|
+
push.receive('ok', () => resolve('ok'))
|
|
209
|
+
push.receive('timeout', () => resolve('timeout'))
|
|
210
|
+
})
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
replyEventName(ref: string) {
|
|
214
|
+
return `chan_reply_${ref}`
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
isClosed() {
|
|
218
|
+
return this.state === CHANNEL_STATES.closed
|
|
219
|
+
}
|
|
220
|
+
isErrored() {
|
|
221
|
+
return this.state === CHANNEL_STATES.errored
|
|
222
|
+
}
|
|
223
|
+
isJoined() {
|
|
224
|
+
return this.state === CHANNEL_STATES.joined
|
|
225
|
+
}
|
|
226
|
+
isJoining() {
|
|
227
|
+
return this.state === CHANNEL_STATES.joining
|
|
228
|
+
}
|
|
229
|
+
isLeaving() {
|
|
230
|
+
return this.state === CHANNEL_STATES.leaving
|
|
231
|
+
}
|
|
232
|
+
}
|
package/src/RealtimeClient.ts
CHANGED
|
@@ -9,8 +9,9 @@ import {
|
|
|
9
9
|
DEFAULT_HEADERS,
|
|
10
10
|
} from './lib/constants'
|
|
11
11
|
import Timer from './lib/timer'
|
|
12
|
-
import RealtimeSubscription from './RealtimeSubscription'
|
|
13
12
|
import Serializer from './lib/serializer'
|
|
13
|
+
import RealtimeSubscription from './RealtimeSubscription'
|
|
14
|
+
import RealtimeChannel from './RealtimeChannel'
|
|
14
15
|
|
|
15
16
|
export type Options = {
|
|
16
17
|
transport?: WebSocket
|
|
@@ -248,8 +249,40 @@ export default class RealtimeClient {
|
|
|
248
249
|
)
|
|
249
250
|
}
|
|
250
251
|
|
|
251
|
-
channel(
|
|
252
|
-
|
|
252
|
+
channel(
|
|
253
|
+
topic: string,
|
|
254
|
+
chanParams: { [key: string]: any } = { isNewVersion: false }
|
|
255
|
+
) {
|
|
256
|
+
const { isNewVersion, ...params } = chanParams
|
|
257
|
+
|
|
258
|
+
const chan = isNewVersion
|
|
259
|
+
? new RealtimeChannel(topic, { ...params }, this)
|
|
260
|
+
: new RealtimeSubscription(topic, { ...params }, this)
|
|
261
|
+
|
|
262
|
+
if (chan instanceof RealtimeChannel) {
|
|
263
|
+
chan.presence.onJoin((key, currentPresences, newPresences) => {
|
|
264
|
+
chan.trigger('presence', {
|
|
265
|
+
event: 'JOIN',
|
|
266
|
+
key,
|
|
267
|
+
currentPresences,
|
|
268
|
+
newPresences,
|
|
269
|
+
})
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
chan.presence.onLeave((key, currentPresences, leftPresences) => {
|
|
273
|
+
chan.trigger('presence', {
|
|
274
|
+
event: 'LEAVE',
|
|
275
|
+
key,
|
|
276
|
+
currentPresences,
|
|
277
|
+
leftPresences,
|
|
278
|
+
})
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
chan.presence.onSync(() => {
|
|
282
|
+
chan.trigger('presence', { event: 'SYNC' })
|
|
283
|
+
})
|
|
284
|
+
}
|
|
285
|
+
|
|
253
286
|
this.channels.push(chan)
|
|
254
287
|
return chan
|
|
255
288
|
}
|
package/src/RealtimePresence.ts
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
PresenceOnJoinCallback,
|
|
10
10
|
PresenceOnLeaveCallback,
|
|
11
11
|
} from 'phoenix'
|
|
12
|
-
import
|
|
12
|
+
import RealtimeChannel from './RealtimeChannel'
|
|
13
13
|
|
|
14
14
|
type Presence = {
|
|
15
15
|
presence_id: string
|
|
@@ -61,13 +61,13 @@ export default class RealtimePresence {
|
|
|
61
61
|
* @param opts - The options,
|
|
62
62
|
* for example `{events: {state: 'state', diff: 'diff'}}`
|
|
63
63
|
*/
|
|
64
|
-
constructor(public channel:
|
|
64
|
+
constructor(public channel: RealtimeChannel, opts?: PresenceOpts) {
|
|
65
65
|
const events = opts?.events || {
|
|
66
66
|
state: 'presence_state',
|
|
67
67
|
diff: 'presence_diff',
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
this.channel.on(events.state, (newState: RawPresenceState) => {
|
|
70
|
+
this.channel.on(events.state, {}, (newState: RawPresenceState) => {
|
|
71
71
|
const { onJoin, onLeave, onSync } = this.caller
|
|
72
72
|
|
|
73
73
|
this.joinRef = this.channel.joinRef()
|
|
@@ -93,7 +93,7 @@ export default class RealtimePresence {
|
|
|
93
93
|
onSync()
|
|
94
94
|
})
|
|
95
95
|
|
|
96
|
-
this.channel.on(events.diff, (diff: RawPresenceDiff) => {
|
|
96
|
+
this.channel.on(events.diff, {}, (diff: RawPresenceDiff) => {
|
|
97
97
|
const { onJoin, onLeave, onSync } = this.caller
|
|
98
98
|
|
|
99
99
|
if (this.inPendingSyncState()) {
|
package/src/lib/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '1.4.
|
|
1
|
+
export const version = '1.4.6'
|