Skip to content

Commit

Permalink
refactor(web): migrate src/client to TypeScript
Browse files Browse the repository at this point in the history
  • Loading branch information
dgdavid committed Dec 24, 2024
1 parent 767b8d7 commit e28ecc3
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 49 deletions.
50 changes: 31 additions & 19 deletions web/src/client/index.js → web/src/client/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) [2021-2023] SUSE LLC
* Copyright (c) [2021-2024] SUSE LLC
*
* All Rights Reserved.
*
Expand All @@ -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: MessageEvent) => 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";
Expand All @@ -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),
};
};

Expand Down
58 changes: 28 additions & 30 deletions web/src/client/ws.js → web/src/client/ws.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: MessageEvent) => void;

/**
* Enum for the WebSocket states.
*
*
*/

const SocketStates = Object.freeze({
CONNECTED: 0,
CONNECTING: 1,
Expand All @@ -52,10 +46,25 @@ const ATTEMPT_INTERVAL = 1000;
* HTTPClient API.
*/
class WSClient {
url: string;

client: WebSocket;

handlers: {
open: Array<BaseHandlerFn>;
close: Array<BaseHandlerFn>;
error: Array<BaseHandlerFn>;
events: Array<EventHandlerFn>;
};

reconnectAttempts: number;

timeout: ReturnType<typeof setTimeout>;

/**
* @param {URL} url - Websocket URL.
* @param url - Websocket URL.
*/
constructor(url) {
constructor(url: URL) {
this.url = url.toString();

this.handlers = {
Expand Down Expand Up @@ -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);
Expand All @@ -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 () => {
Expand All @@ -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 () => {
Expand All @@ -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 () => {
Expand All @@ -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));
}
Expand Down

0 comments on commit e28ecc3

Please sign in to comment.