Skip to content
This repository was archived by the owner on Oct 9, 2025. It is now read-only.

Commit ef4e8d9

Browse files
authored
add: Worker for Healthcheck (#419)
Added capacity to use Worker for Healthchecks. We also allow users to override the worker URL so they are able to modify it as they see fit.
1 parent 95ab09e commit ef4e8d9

File tree

5 files changed

+92
-9
lines changed

5 files changed

+92
-9
lines changed

package-lock.json

Lines changed: 9 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
"sinon": "^9.0.2",
5757
"typedoc": "^0.22.16",
5858
"typescript": "^4.0.3",
59-
"vitest": "^2.0.5"
59+
"vitest": "^2.0.5",
60+
"web-worker": "1.2.0"
6061
}
6162
}

src/RealtimeClient.ts

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ export type RealtimeClientOptions = {
3838
params?: { [key: string]: any }
3939
log_level?: 'info' | 'debug' | 'warn' | 'error'
4040
fetch?: Fetch
41+
worker?: boolean
42+
workerUrl?: string
4143
}
4244

4345
export type RealtimeMessage = {
@@ -104,6 +106,9 @@ export default class RealtimeClient {
104106
message: [],
105107
}
106108
fetch: Fetch
109+
worker?: boolean
110+
workerUrl?: string
111+
workerRef?: Worker
107112

108113
/**
109114
* Initializes the Socket.
@@ -119,6 +124,8 @@ export default class RealtimeClient {
119124
* @param options.encode The function to encode outgoing messages. Defaults to JSON: (payload, callback) => callback(JSON.stringify(payload))
120125
* @param options.decode The function to decode incoming messages. Defaults to Serializer's decode.
121126
* @param options.reconnectAfterMs he optional function that returns the millsec reconnect interval. Defaults to stepped backoff off.
127+
* @param options.worker Use Web Worker to set a side flow. Defaults to false.
128+
* @param options.workerUrl The URL of the worker script. Defaults to https://realtime.supabase.com/worker.js that includes a heartbeat event call to keep the connection alive.
122129
*/
123130
constructor(endPoint: string, options?: RealtimeClientOptions) {
124131
this.endPoint = `${endPoint}/${TRANSPORTS.websocket}`
@@ -160,6 +167,14 @@ export default class RealtimeClient {
160167
}, this.reconnectAfterMs)
161168

162169
this.fetch = this._resolveFetch(options?.fetch)
170+
if (options?.worker) {
171+
if (typeof window !== 'undefined' && !window.Worker) {
172+
throw new Error('Web Worker is not supported')
173+
}
174+
this.worker = options?.worker || false
175+
this.workerUrl =
176+
options?.workerUrl || 'https://realtime.supabase.com/worker.js'
177+
}
163178
}
164179

165180
/**
@@ -452,11 +467,31 @@ export default class RealtimeClient {
452467
this.log('transport', `connected to ${this._endPointURL()}`)
453468
this._flushSendBuffer()
454469
this.reconnectTimer.reset()
455-
this.heartbeatTimer && clearInterval(this.heartbeatTimer)
456-
this.heartbeatTimer = setInterval(
457-
() => this._sendHeartbeat(),
458-
this.heartbeatIntervalMs
459-
)
470+
if (!this.worker) {
471+
this.heartbeatTimer && clearInterval(this.heartbeatTimer)
472+
this.heartbeatTimer = setInterval(
473+
() => this._sendHeartbeat(),
474+
this.heartbeatIntervalMs
475+
)
476+
} else {
477+
this.log('worker', `starting worker for from ${this.workerUrl!}`)
478+
479+
this.workerRef = new Worker(this.workerUrl!)
480+
this.workerRef.onerror = (error) => {
481+
this.log('worker', 'worker error', error.message)
482+
this.workerRef!.terminate()
483+
}
484+
this.workerRef.onmessage = (event) => {
485+
if (event.data.event === 'keepAlive') {
486+
this._sendHeartbeat()
487+
}
488+
}
489+
this.workerRef.postMessage({
490+
event: 'start',
491+
interval: this.heartbeatIntervalMs,
492+
})
493+
}
494+
460495
this.stateChangeCallbacks.open.forEach((callback) => callback())!
461496
}
462497

test/channel.test.ts

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
11
import assert from 'assert'
22
import sinon from 'sinon'
3-
import { describe, beforeEach, afterEach, test } from 'vitest'
3+
import {
4+
describe,
5+
beforeEach,
6+
afterEach,
7+
test,
8+
beforeAll,
9+
afterAll,
10+
} from 'vitest'
411

512
import RealtimeClient from '../src/RealtimeClient'
613
import RealtimeChannel from '../src/RealtimeChannel'
714
import { Response } from '@supabase/node-fetch'
15+
import { WebSocketServer } from 'ws'
16+
import Worker from 'web-worker'
817

918
let channel, socket
1019
const defaultRef = '1'
@@ -1418,3 +1427,34 @@ describe('trigger', () => {
14181427
assert.equal(client.accessToken, '123')
14191428
})
14201429
})
1430+
1431+
describe('worker', () => {
1432+
let client
1433+
let mockServer
1434+
1435+
beforeAll(() => {
1436+
window.Worker = Worker
1437+
mockServer = new WebSocketServer({ port: 8080 })
1438+
})
1439+
1440+
afterAll(() => {
1441+
window.Worker = undefined
1442+
mockServer.close()
1443+
})
1444+
1445+
beforeEach(() => {
1446+
client = new RealtimeClient('ws://localhost:8080/socket', {
1447+
worker: true,
1448+
workerUrl: 'https://realtime.supabase.com/worker.js',
1449+
heartbeatIntervalMs: 10,
1450+
})
1451+
})
1452+
1453+
test('sets worker flag', () => {
1454+
assert.ok(client.worker)
1455+
})
1456+
1457+
test('sets worker URL', () => {
1458+
assert.equal(client.workerUrl, 'https://realtime.supabase.com/worker.js')
1459+
})
1460+
})

tsconfig.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,5 @@
1313
"moduleResolution": "Node",
1414
"forceConsistentCasingInFileNames": true,
1515
"stripInternal": true,
16-
"types": ["vitest/globals"]
1716
}
1817
}

0 commit comments

Comments
 (0)