Skip to content
This repository was archived by the owner on Oct 9, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 30 additions & 19 deletions src/RealtimeClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import {
DEFAULT_TIMEOUT,
SOCKET_STATES,
TRANSPORTS,
VERSION,
VSN,
WS_CLOSE_NORMAL,
LOG_LEVEL,
} from './lib/constants'

import Serializer from './lib/serializer'
import Timer from './lib/timer'

Expand All @@ -26,23 +27,7 @@ export type Channel = {
updated_at: string
id: number
}

export type RealtimeClientOptions = {
transport?: WebSocketLikeConstructor
timeout?: number
heartbeatIntervalMs?: number
logger?: Function
encode?: Function
decode?: Function
reconnectAfterMs?: Function
headers?: { [key: string]: string }
params?: { [key: string]: any }
log_level?: 'info' | 'debug' | 'warn' | 'error'
fetch?: Fetch
worker?: boolean
workerUrl?: string
accessToken?: () => Promise<string | null>
}
export type LogLevel = LOG_LEVEL

export type RealtimeMessage = {
topic: string
Expand Down Expand Up @@ -72,6 +57,25 @@ export interface WebSocketLikeError {
type: string
}

export type RealtimeClientOptions = {
transport?: WebSocketLikeConstructor
timeout?: number
heartbeatIntervalMs?: number
logger?: Function
encode?: Function
decode?: Function
reconnectAfterMs?: Function
headers?: { [key: string]: string }
params?: { [key: string]: any }
//Deprecated: Use it in favour of correct casing `logLevel`
log_level?: LogLevel
logLevel?: LogLevel
fetch?: Fetch
worker?: boolean
workerUrl?: string
accessToken?: () => Promise<string | null>
}

const NATIVE_WEBSOCKET_AVAILABLE = typeof WebSocket !== 'undefined'
const WORKER_SCRIPT = `
addEventListener("message", (e) => {
Expand All @@ -89,12 +93,13 @@ export default class RealtimeClient {
params?: { [key: string]: string } = {}
timeout: number = DEFAULT_TIMEOUT
transport: WebSocketLikeConstructor | null
heartbeatIntervalMs: number = 30000
heartbeatIntervalMs: number = 25000
heartbeatTimer: ReturnType<typeof setInterval> | undefined = undefined
pendingHeartbeatRef: string | null = null
ref: number = 0
reconnectTimer: Timer
logger: Function = noop
logLevel?: LogLevel
encode: Function
decode: Function
reconnectAfterMs: Function
Expand Down Expand Up @@ -129,6 +134,7 @@ export default class RealtimeClient {
* @param options.headers The optional headers to pass when connecting.
* @param options.heartbeatIntervalMs The millisec interval to send a heartbeat message.
* @param options.logger The optional function for specialized logging, ie: logger: (kind, msg, data) => { console.log(`${kind}: ${msg}`, data) }
* @param options.logLevel Sets the log level for Realtime
* @param options.encode The function to encode outgoing messages. Defaults to JSON: (payload, callback) => callback(JSON.stringify(payload))
* @param options.decode The function to decode incoming messages. Defaults to Serializer's decode.
* @param options.reconnectAfterMs he optional function that returns the millsec reconnect interval. Defaults to stepped backoff off.
Expand All @@ -147,6 +153,11 @@ export default class RealtimeClient {
if (options?.headers) this.headers = { ...this.headers, ...options.headers }
if (options?.timeout) this.timeout = options.timeout
if (options?.logger) this.logger = options.logger
if (options?.logLevel || options?.log_level) {
this.logLevel = options.logLevel || options.log_level
this.params = { ...this.params, log_level: this.logLevel as string }
}

if (options?.heartbeatIntervalMs)
this.heartbeatIntervalMs = options.heartbeatIntervalMs

Expand Down
6 changes: 6 additions & 0 deletions src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,9 @@ export enum CONNECTION_STATE {
Closing = 'closing',
Closed = 'closed',
}

export enum LOG_LEVEL {
Info = 'info',
Warn = 'warn',
Error = 'error',
}
35 changes: 33 additions & 2 deletions test/socket.test.ts → test/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import crypto from 'crypto'

import RealtimeClient from '../src/RealtimeClient'
import jwt from 'jsonwebtoken'
import { CHANNEL_STATES } from '../src/lib/constants'
import { CHANNEL_STATES, LOG_LEVEL } from '../src/lib/constants'

function generateJWT(exp: string): string {
return jwt.sign({}, 'your-256-bit-secret', {
Expand Down Expand Up @@ -55,7 +55,7 @@ describe('constructor', () => {
})
assert.equal(socket.transport, null)
assert.equal(socket.timeout, 10000)
assert.equal(socket.heartbeatIntervalMs, 30000)
assert.equal(socket.heartbeatIntervalMs, 25000)
assert.equal(typeof socket.logger, 'function')
assert.equal(typeof socket.reconnectAfterMs, 'function')
})
Expand Down Expand Up @@ -749,3 +749,34 @@ describe('custom encoder and decoder', () => {
})
})
})

describe('log operations', () => {
test('calls the logger with the correct arguments', () => {
const mockLogger = vi.fn()
socket = new RealtimeClient(url, { logger: mockLogger })

socket.log('testKind', 'testMessage', { testData: 'test' })

expect(mockLogger).toHaveBeenCalledWith('testKind', 'testMessage', {
testData: 'test',
})
})
test('changing log_level sends proper params in URL', () => {
socket = new RealtimeClient(url, { log_level: LOG_LEVEL.Warn })

assert.equal(socket.logLevel, LOG_LEVEL.Warn)
assert.equal(
socket.endpointURL(),
`${url}/websocket?log_level=warn&vsn=1.0.0`
)
})
test('changing logLevel sends proper params in URL', () => {
socket = new RealtimeClient(url, { logLevel: LOG_LEVEL.Warn })

assert.equal(socket.logLevel, LOG_LEVEL.Warn)
assert.equal(
socket.endpointURL(),
`${url}/websocket?log_level=warn&vsn=1.0.0`
)
})
})