export class UIClient {
private static instance: null | UIClient = null
+ private abortConnection: () => void
private client: WebSocketClient
private readonly refreshListeners: Set<() => void>
private uiServerConfiguration: UIServerConfigurationSection
this.uiServerConfiguration = uiServerConfiguration
this.refreshListeners = new Set()
this.wsEventTarget = new EventTarget()
- this.client = this.createClient()
- this.client.connect().catch(() => undefined)
+ const { abort, client } = this.createClientWithAbort()
+ this.client = client
+ this.abortConnection = abort
+ this.client.connect().catch((error: unknown) => {
+ console.error('WebSocket connect failed', error)
+ })
}
public static getInstance (uiServerConfiguration?: UIServerConfigurationSection): UIClient {
}
public setConfiguration (uiServerConfiguration: UIServerConfigurationSection): void {
+ this.abortConnection()
this.client.disconnect()
this.uiServerConfiguration = uiServerConfiguration
- this.client = this.createClient()
- this.client.connect().catch(() => undefined)
+ const { abort, client } = this.createClientWithAbort()
+ this.client = client
+ this.abortConnection = abort
+ this.client.connect().catch((error: unknown) => {
+ console.error('WebSocket connect failed', error)
+ })
}
public async setSupervisionUrl (hashId: string, supervisionUrl: string): Promise<ResponsePayload> {
this.wsEventTarget.removeEventListener(event, listener as EventListener, options)
}
- private createClient (): WebSocketClient {
+ private createClientWithAbort (): { abort: () => void; client: WebSocketClient } {
+ let aborted = false
const config = this.uiServerConfiguration
const uiUrl = `${config.secure === true ? ApplicationProtocol.WSS : ApplicationProtocol.WS}://${config.host}:${config.port.toString()}`
const uiProtocols =
const eventTarget = this.wsEventTarget
+ // Factory builds its own URL/protocols because WebSocketClient.buildProtocols()
+ // uses Node.js Buffer for base64 encoding, which isn't available in the browser.
+ // Browser uses btoa() instead. Both produce identical output.
const factory: WebSocketFactory = (_url, _protocols) => {
const adapter = createBrowserWsAdapter(
new WebSocket(uiUrl, uiProtocols) as unknown as Parameters<typeof createBrowserWsAdapter>[0]
},
set onclose (handler) {
adapter.onclose = event => {
+ if (aborted) return
handler?.(event)
useToast().info('WebSocket to UI server closed')
- eventTarget.dispatchEvent(new Event('close'))
+ eventTarget.dispatchEvent(
+ new CloseEvent('close', { code: event.code, reason: event.reason })
+ )
}
},
get onerror () {
},
set onerror (handler) {
adapter.onerror = event => {
+ if (aborted) return
handler?.(event)
useToast().error(
`Error in WebSocket to UI server '${config.host}:${config.port.toString()}'`
},
set onopen (handler) {
adapter.onopen = () => {
+ if (aborted) return
handler?.()
useToast().success(
`WebSocket to UI server '${config.host}:${config.port.toString()}' successfully opened`
}
}
- return new WebSocketClient(
+ const client = new WebSocketClient(
factory,
{
authentication: config.authentication,
}
}
)
+
+ return {
+ abort: () => {
+ aborted = true
+ },
+ client,
+ }
}
private async sendRequest (