diff --git a/web/src/client/index.js b/web/src/client/index.ts similarity index 54% rename from web/src/client/index.js rename to web/src/client/index.ts index 5e161ef5f3..7a2d9965cc 100644 --- a/web/src/client/index.js +++ b/web/src/client/index.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) [2021-2023] SUSE LLC + * Copyright (c) [2021-2024] SUSE LLC * * All Rights Reserved. * @@ -20,28 +20,40 @@ * find current contact information at www.suse.com. */ -// @ts-check - import { WSClient } from "./ws"; -/** - * @typedef {object} InstallerClient - * @property {() => boolean} isConnected - determines whether the client is connected - * @property {() => boolean} isRecoverable - determines whether the client is recoverable after disconnected - * @property {(handler: () => void) => (() => void)} onConnect - registers a handler to run - * @property {(handler: () => void) => (() => void)} onDisconnect - registers a handler to run - * when the connection is lost. It returns a function to deregister the - * handler. - * @property {(handler: (any) => void) => (() => void)} onEvent - registers a handler to run on events - */ +type VoidFn = () => void; +type BooleanFn = () => boolean; +type EventHandlerFn = (event: { [key: string]: unknown }) => void; + +export type InstallerClient = { + /** Whether the client is connected. */ + isConnected: BooleanFn; + /** Whether the client is recoverable after disconnecting. */ + isRecoverable: BooleanFn; + /** + * Registers a handler to run when connection is set. It returns a function + * for deregistering the handler. + */ + onConnect: (handler: VoidFn) => VoidFn; + /** + * Registers a handler to run when connection is lost. It returns a function + * for deregistering the handler. + */ + onDisconnect: (handler: VoidFn) => VoidFn; + /** + * Registers a handler to run on events. It returns a function for + * deregistering the handler. + */ + onEvent: (handler: EventHandlerFn) => VoidFn; +}; /** * Creates the Agama client * - * @param {URL} url - URL of the HTTP API. - * @return {InstallerClient} + * @param url - URL of the HTTP API. */ -const createClient = (url) => { +const createClient = (url: URL): InstallerClient => { url.hash = ""; url.pathname = url.pathname.concat("api/ws"); url.protocol = url.protocol === "http:" ? "ws" : "wss"; @@ -53,9 +65,9 @@ const createClient = (url) => { return { isConnected, isRecoverable, - onConnect: (handler) => ws.onOpen(handler), - onDisconnect: (handler) => ws.onClose(handler), - onEvent: (handler) => ws.onEvent(handler), + onConnect: (handler: VoidFn) => ws.onOpen(handler), + onDisconnect: (handler: VoidFn) => ws.onClose(handler), + onEvent: (handler: EventHandlerFn) => ws.onEvent(handler), }; }; diff --git a/web/src/client/ws.js b/web/src/client/ws.ts similarity index 84% rename from web/src/client/ws.js rename to web/src/client/ws.ts index b63c8e2a51..df7e511a08 100644 --- a/web/src/client/ws.js +++ b/web/src/client/ws.ts @@ -20,19 +20,13 @@ * find current contact information at www.suse.com. */ -// @ts-check - -/** - * @callback RemoveFn - * @return {void} - */ +type RemoveFn = () => void; +type BaseHandlerFn = () => void; +type EventHandlerFn = (event: { [key: string]: unknown }) => void; /** * Enum for the WebSocket states. - * - * */ - const SocketStates = Object.freeze({ CONNECTED: 0, CONNECTING: 1, @@ -52,10 +46,25 @@ const ATTEMPT_INTERVAL = 1000; * HTTPClient API. */ class WSClient { + url: string; + + client: WebSocket; + + handlers: { + open: Array; + close: Array; + error: Array; + events: Array; + }; + + reconnectAttempts: number; + + timeout: ReturnType; + /** - * @param {URL} url - Websocket URL. + * @param url - Websocket URL. */ - constructor(url) { + constructor(url: URL) { this.url = url.toString(); this.handlers = { @@ -126,13 +135,10 @@ class WSClient { /** * Registers a handler for events. * - * The handler is executed for all the events. It is up to the callback to - * filter the relevant events. - * - * @param {(object) => void} func - Handler function to register. - * @return {RemoveFn} + * The handler is executed for all events. It is up to the callback to + * filter the relevant ones for it. */ - onEvent(func) { + onEvent(func: EventHandlerFn): RemoveFn { this.handlers.events.push(func); return () => { const position = this.handlers.events.indexOf(func); @@ -144,11 +150,8 @@ class WSClient { * Registers a handler for close socket. * * The handler is executed when the socket is close. - * - * @param {(object) => void} func - Handler function to register. - * @return {RemoveFn} */ - onClose(func) { + onClose(func: BaseHandlerFn): RemoveFn { this.handlers.close.push(func); return () => { @@ -161,10 +164,8 @@ class WSClient { * Registers a handler for open socket. * * The handler is executed when the socket is open. - * @param {(object) => void} func - Handler function to register. - * @return {RemoveFn} */ - onOpen(func) { + onOpen(func: BaseHandlerFn): RemoveFn { this.handlers.open.push(func); return () => { @@ -177,11 +178,8 @@ class WSClient { * Registers a handler for socket errors. * * The handler is executed when an error is reported by the socket. - * - * @param {(object) => void} func - Handler function to register. - * @return {RemoveFn} */ - onError(func) { + onError(func: BaseHandlerFn): RemoveFn { this.handlers.error.push(func); return () => { @@ -195,9 +193,9 @@ class WSClient { * * Dispatchs an event by running all the handlers. * - * @param {object} event - Event object, which is basically a websocket message. + * @param event - Event object, which is basically a websocket message. */ - dispatchEvent(event) { + dispatchEvent(event: MessageEvent) { const eventObject = JSON.parse(event.data); this.handlers.events.forEach((f) => f(eventObject)); }