@supabase/realtime-js 2.7.4 → 2.8.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.
@@ -80,7 +80,11 @@ export type RealtimePostgresChangesFilter<
80
80
  filter?: string
81
81
  }
82
82
 
83
- export type RealtimeChannelSendResponse = 'ok' | 'timed out' | 'rate limited'
83
+ export type RealtimeChannelSendResponse =
84
+ | 'ok'
85
+ | 'timed out'
86
+ | 'rate limited'
87
+ | 'error'
84
88
 
85
89
  export enum REALTIME_POSTGRES_CHANGES_LISTEN_EVENT {
86
90
  ALL = '*',
@@ -109,7 +113,7 @@ export enum REALTIME_SUBSCRIBE_STATES {
109
113
  * and narrows the scope of data flow to subscribed clients.
110
114
  * You can think of a channel as a chatroom where participants are able to see who's online
111
115
  * and send and receive messages.
112
- **/
116
+ */
113
117
  export default class RealtimeChannel {
114
118
  bindings: {
115
119
  [key: string]: {
@@ -126,6 +130,8 @@ export default class RealtimeChannel {
126
130
  rejoinTimer: Timer
127
131
  pushBuffer: Push[] = []
128
132
  presence: RealtimePresence
133
+ broadcastEndpointURL: string
134
+ subTopic: string
129
135
 
130
136
  constructor(
131
137
  /** Topic name can be any string. */
@@ -133,6 +139,8 @@ export default class RealtimeChannel {
133
139
  public params: RealtimeChannelOptions = { config: {} },
134
140
  public socket: RealtimeClient
135
141
  ) {
142
+ this.subTopic = topic.replace(/^realtime:/i, '')
143
+
136
144
  this.params.config = {
137
145
  ...{
138
146
  broadcast: { ack: false, self: false },
@@ -184,6 +192,8 @@ export default class RealtimeChannel {
184
192
  })
185
193
 
186
194
  this.presence = new RealtimePresence(this)
195
+
196
+ this.broadcastEndpointURL = this._broadcastEndpointURL()
187
197
  }
188
198
 
189
199
  /** Subscribe registers your client with the server */
@@ -191,6 +201,10 @@ export default class RealtimeChannel {
191
201
  callback?: (status: `${REALTIME_SUBSCRIBE_STATES}`, err?: Error) => void,
192
202
  timeout = this.timeout
193
203
  ): RealtimeChannel {
204
+ if (!this.socket.isConnected()) {
205
+ this.socket.connect()
206
+ }
207
+
194
208
  if (this.joinedOnce) {
195
209
  throw `tried to subscribe multiple times. 'subscribe' can only be called a single time per channel instance`
196
210
  } else {
@@ -392,32 +406,74 @@ export default class RealtimeChannel {
392
406
  ): RealtimeChannel {
393
407
  return this._on(type, filter, callback)
394
408
  }
395
-
396
- send(
397
- payload: { type: string; [key: string]: any },
409
+ /**
410
+ * Sends a message into the channel.
411
+ *
412
+ * @param args Arguments to send to channel
413
+ * @param args.type The type of event to send
414
+ * @param args.event The name of the event being sent
415
+ * @param args.payload Payload to be sent
416
+ * @param opts Options to be used during the send process
417
+ */
418
+ async send(
419
+ args: {
420
+ type: 'broadcast' | 'presence' | 'postgres_changes'
421
+ event: string
422
+ payload?: any
423
+ [key: string]: any
424
+ },
398
425
  opts: { [key: string]: any } = {}
399
426
  ): Promise<RealtimeChannelSendResponse> {
400
- return new Promise((resolve) => {
401
- const push = this._push(
402
- payload.type,
403
- payload,
404
- opts.timeout || this.timeout
405
- )
406
-
407
- if (push.rateLimited) {
408
- resolve('rate limited')
427
+ if (!this._canPush() && args.type === 'broadcast') {
428
+ const { event, payload: endpoint_payload } = args
429
+ const options = {
430
+ method: 'POST',
431
+ headers: {
432
+ apikey: this.socket.accessToken ?? '',
433
+ 'Content-Type': 'application/json',
434
+ },
435
+ body: JSON.stringify({
436
+ messages: [
437
+ { topic: this.subTopic, event, payload: endpoint_payload },
438
+ ],
439
+ }),
409
440
  }
410
441
 
411
- if (
412
- payload.type === 'broadcast' &&
413
- !this.params?.config?.broadcast?.ack
414
- ) {
415
- resolve('ok')
442
+ try {
443
+ const response = await this._fetchWithTimeout(
444
+ this.broadcastEndpointURL,
445
+ options,
446
+ opts.timeout ?? this.timeout
447
+ )
448
+
449
+ if (response.ok) {
450
+ return 'ok'
451
+ } else {
452
+ return 'error'
453
+ }
454
+ } catch (error: any) {
455
+ if (error.name === 'AbortError') {
456
+ return 'timed out'
457
+ } else {
458
+ return 'error'
459
+ }
416
460
  }
461
+ } else {
462
+ return new Promise((resolve) => {
463
+ const push = this._push(args.type, args, opts.timeout || this.timeout)
417
464
 
418
- push.receive('ok', () => resolve('ok'))
419
- push.receive('timeout', () => resolve('timed out'))
420
- })
465
+ if (push.rateLimited) {
466
+ resolve('rate limited')
467
+ }
468
+
469
+ if (args.type === 'broadcast' && !this.params?.config?.broadcast?.ack) {
470
+ resolve('ok')
471
+ }
472
+
473
+ push.receive('ok', () => resolve('ok'))
474
+ push.receive('timeout', () => resolve('timed out'))
475
+ })
476
+ }
421
477
  }
422
478
 
423
479
  updateJoinPayload(payload: { [key: string]: any }): void {
@@ -468,6 +524,32 @@ export default class RealtimeChannel {
468
524
  })
469
525
  }
470
526
 
527
+ /** @internal */
528
+ _broadcastEndpointURL(): string {
529
+ let url = this.socket.endPoint
530
+ url = url.replace(/^ws/i, 'http')
531
+ url = url.replace(/(\/socket\/websocket|\/socket|\/websocket)\/?$/i, '')
532
+ return url.replace(/\/+$/, '') + '/api/broadcast'
533
+ }
534
+
535
+ async _fetchWithTimeout(
536
+ url: string,
537
+ options: { [key: string]: any },
538
+ timeout: number
539
+ ) {
540
+ const controller = new AbortController()
541
+ const id = setTimeout(() => controller.abort(), timeout)
542
+
543
+ const response = await this.socket.fetch(url, {
544
+ ...options,
545
+ signal: controller.signal,
546
+ })
547
+
548
+ clearTimeout(id)
549
+
550
+ return response
551
+ }
552
+
471
553
  /** @internal */
472
554
  _push(
473
555
  event: string,
@@ -14,6 +14,8 @@ import Serializer from './lib/serializer'
14
14
  import RealtimeChannel from './RealtimeChannel'
15
15
  import type { RealtimeChannelOptions } from './RealtimeChannel'
16
16
 
17
+ type Fetch = typeof fetch
18
+
17
19
  export type RealtimeClientOptions = {
18
20
  transport?: WebSocket
19
21
  timeout?: number
@@ -25,6 +27,7 @@ export type RealtimeClientOptions = {
25
27
  headers?: { [key: string]: string }
26
28
  params?: { [key: string]: any }
27
29
  log_level?: 'info' | 'debug' | 'warn' | 'error'
30
+ fetch?: Fetch
28
31
  }
29
32
 
30
33
  export type RealtimeMessage = {
@@ -72,6 +75,7 @@ export default class RealtimeClient {
72
75
  }
73
76
  eventsPerSecondLimitMs: number = 100
74
77
  inThrottle: boolean = false
78
+ fetch: Fetch
75
79
 
76
80
  /**
77
81
  * Initializes the Socket.
@@ -122,6 +126,8 @@ export default class RealtimeClient {
122
126
  this.disconnect()
123
127
  this.connect()
124
128
  }, this.reconnectAfterMs)
129
+
130
+ this.fetch = this._resolveFetch(options?.fetch)
125
131
  }
126
132
 
127
133
  /**
@@ -232,10 +238,6 @@ export default class RealtimeClient {
232
238
  topic: string,
233
239
  params: RealtimeChannelOptions = { config: {} }
234
240
  ): RealtimeChannel {
235
- if (!this.isConnected()) {
236
- this.connect()
237
- }
238
-
239
241
  const chan = new RealtimeChannel(`realtime:${topic}`, params, this)
240
242
  this.channels.push(chan)
241
243
  return chan
@@ -285,6 +287,26 @@ export default class RealtimeClient {
285
287
  })
286
288
  }
287
289
 
290
+ /**
291
+ * Use either custom fetch, if provided, or default fetch to make HTTP requests
292
+ *
293
+ * @internal
294
+ */
295
+ _resolveFetch = (customFetch?: Fetch): Fetch => {
296
+ let _fetch: Fetch
297
+ if (customFetch) {
298
+ _fetch = customFetch
299
+ } else if (typeof fetch === 'undefined') {
300
+ _fetch = (...args) =>
301
+ import('@supabase/node-fetch' as any).then(({ default: fetch }) =>
302
+ fetch(...args)
303
+ )
304
+ } else {
305
+ _fetch = fetch
306
+ }
307
+ return (...args) => _fetch(...args)
308
+ }
309
+
288
310
  /**
289
311
  * Return the next message ref, accounting for overflows
290
312
  *
@@ -1 +1 @@
1
- export const version = '2.7.4'
1
+ export const version = '2.8.1'