{"version":3,"sources":["../../src/network/Connection.ts"],"sourcesContent":["import { io } from \"socket.io-client\";\nimport { Socket } from 'socket.io';\nimport log from '../utils/log.js';\nimport type Listener from './types/Listener.js';\n\ntype ConnectionOptions = {\n    timeout?: number,\n    listeners?: Listener[],\n    onConnect?: Function,\n    onDisconnect?: Function,\n    onSetListeners?: Function\n};\n\ntype ConnectionParametersServer = {\n    socket: Socket,\n    name: string,\n    options?: ConnectionOptions\n};\n\ntype ConnectionParametersClient = {\n    host: string,\n    port: number,\n    id: string,\n    options?: ConnectionOptions\n};\n\nfunction isServer(params: ConnectionParametersServer | ConnectionParametersClient): params is ConnectionParametersClient {\n  return 'host' in params && 'port' in params && 'id' in params;\n}\n\nfunction isClient(params: ConnectionParametersServer | ConnectionParametersClient): params is ConnectionParametersServer {\n  return 'socket' in params && 'name' in params;\n}\n\n\nclass Connection {\n    /*\n     * this class is manager for the socket instance\n     * it takes either a socket or a host and port to create the socket\n     * if it takes the host and port it will consider that connection is a server\n     * if it takes a socket it will consider that connection is a client\n     *\n     * it manages conenction, the listeners and the available emitters */\n    private socket: Socket | any;\n    private request_id: number = 0;\n    // this node information\n    public name?: string;\n    public id?: string;\n    public type: 'client' | 'server';\n    public host?: string;\n    public port?: number;\n    // is connected or not\n    public isConnected: boolean;\n    // ot target of the socket\n    public socketId: string;\n    public targetType: 'client' | 'server';\n    public targetName?: string;\n    public targetId?: string;\n    public targetListeners: Listener[] = [];\n    public targetHost?: string;\n    public targetPort?: number;\n    // callbacks\n    public options: {\n        timeout: number,\n        listeners: Listener[],\n        onConnect: Function,\n        onDisconnect: Function,\n        onSetListeners: Function\n    };\n\n    /*\n     * @param Node: Node\n     * @param socket: Socket\n     * @param host: string\n     * @param port: number\n     * @param id: string\n     * @param name: string\n     * */\n\n    constructor(params : ConnectionParametersServer | ConnectionParametersClient) {\n        // options\n        this.options = { \n            // callbacks\n            onConnect: params?.options?.onConnect || (() => {}),\n            onDisconnect: params?.options?.onDisconnect || (() => {}),\n            onSetListeners: params?.options?.onSetListeners || (() => {}),\n            // listeners\n            listeners: params?.options?.listeners || [],\n            // timeout\n            timeout: params?.options?.timeout || 5 * 60 * 1000, // 5 minutes\n        };\n        // if get a socket to connect to the server\n        if (isClient(params)) {\n            params = params as unknown as ConnectionParametersServer;\n            this.type = 'server';\n            this.name = params.name;\n            this.targetType = 'client';\n            // the socket\n            this.socket = params.socket;\n            // get the id of client\n            this.targetId = params.socket.handshake.auth.id;\n            //log('[Connection][server] targetId: ', this.targetId)\n            // since we are already getting the socket\n            this.isConnected = true;\n            // if get a host and port to connect to the server\n        } else if (isServer(params)) {\n            params = params as unknown as ConnectionParametersClient;\n            this.type = 'client';\n            this.targetType = 'server';\n            // use the id\n            this.id = params.id;\n            this.socket = io(`ws://${params.host}:${params.port}`, {\n                auth: { id: params.id },\n                timeout: this.options.timeout || 5 * 60 * 1000, // default 5 minutes\n            });\n            // since we are not connected yet\n            this.isConnected = false;\n        } else\n            throw new Error('Connection must have either a socket and a name or a host and port');\n        // set the socket id\n        this.socketId = this.socket.id;\n        // initialize the listeners\n        this.initilaizeListeners()\n    }\n\n    private initilaizeListeners(): void {\n        /* this function inizializes the default listeners for the socket */\n        // set the object listeners\n        this.options.listeners.forEach( l => {\n            this.socket.removeAllListeners(l.event);\n            this.socket.on(l.event, this.respond(l.event, async (parameters:any) => {\n                //log(`[${this.id}] [Connection][initilaizeListeners] got event: ${l.event} from ${this.targetName}: `, parameters)\n                return await l.callback(parameters);\n            }));\n        });\n        // if target is asking for connections\n        this.socket.on(\"_listeners\", this.respond(\"_listeners\", () => this.getListeners()));\n        // if the target is sending a their listeners\n        this.socket.on(\"_set_listeners\", this.respond(\"_set_listeners\", (listeners: string[]) => {\n            //log(`[${this.id}] [Connection][Node] got liteners from ${this.targetName}: `, listeners)\n            this.targetListeners =\n                listeners.map(event => ({ event, callback: () => {} }));\n            this.options.onSetListeners(this.targetListeners);\n            return 'ok';\n        }));\n        // if target is asking for name\n        this.socket.on(\"_name\", this.respond(\"_name\", () => this.name));\n        // if target is asking for id\n        this.socket.on(\"_id\", () => this.id);\n        // on connected\n        this.socket.on(\"connect\", async () => {\n            //log(`[connection][${this.socket.id}] is connected, querying target name and listeners:`)\n            // ask for listeners\n            this.targetName = await this.queryTargetName();\n            this.targetListeners = await this.queryTargetListeners();\n            //log(`[connection][${this.socket.id}] target name: ${this.targetName}, target listeners: ${this.targetListeners}`)\n            this.isConnected = true;\n            this.options.onConnect(this);\n        });\n        // if it disconnects\n        this.socket.on(\"reconnect\", async (attempt: number) => {\n            //log(`[connection][${this.socket.id}] is reconnected, attempt: ${attempt}`)\n            this.targetName = await this.queryTargetName();\n            this.targetListeners = await this.queryTargetListeners();\n            this.isConnected = true;\n            this.options.onConnect(this);\n        });\n        // if it disconnects\n        this.socket.on(\"diconnect\", () => {\n            //log(`[connection][${this.socket.id}] is disconnected`)\n            this.isConnected = false;\n            this.options.onDisconnect(this);\n        });\n    }\n\n    public async connected(): Promise<boolean> {\n        return new Promise((resolve, reject) => {\n            let interval: NodeJS.Timeout;\n            let timeout: NodeJS.Timeout\n            // set interval to check for connection\n            interval = setInterval(() => {\n                if(this.isConnected) {\n                    clearInterval(interval);\n                    clearTimeout(timeout);\n                    resolve(true);\n                }\n            }, 100); // 100 ms\n            // set timeout to reject if no connection\n            timeout = setTimeout(() => {\n                clearInterval(interval);\n                reject(false);\n            }, 1000 * 60 ); // 1 minute\n        })\n    }\n\n\n    public getType(){\n        return this.type;\n    }\n\n    public on(event: string, callback: Function): void {\n        this.socket.on(event, callback);\n    }\n\n    public emit(event: string, data: any): void {\n        this.socket.emit(event, data);\n    }\n\n    public getName(): string | undefined {\n        return this.name;\n    }\n\n    // this is the id of the conenction?\n    public getId(): string | undefined {\n        return this.id;\n    }\n\n    public getTargetName(): string | undefined {\n        return this.targetName;\n    }\n\n    // this is the id of the target client\n    public getTargetId(): string | undefined {\n        return this.targetId;\n    }\n\n    public async setListeners(listeners: Listener[]): Promise<void> {\n        // set the listeners on the socket\n        listeners.forEach( l => {\n            this.options.listeners.push(l);\n            // remove the listener if it exists\n            this.socket.removeAllListeners(l.event);\n            // add new listener\n            this.socket.on(l.event, this.respond(l.event, async (parameters:any) => {\n                // run the callback defined in the listner\n                return await l.callback(parameters);\n            }));\n        });\n        // update the listeners on the target\n        if(this.type === 'server'){\n            await this.query('_set_listeners', listeners.map(listener => listener.event));\n        }\n    }\n\n    public addListeners(listeners: Listener[]): void {\n        // make sure we are not adding the same listener\n        const eventMap = new Map(this.options.listeners.map(l => [l.event, l]));\n        // add the listeners\n        listeners.forEach( l => eventMap.set(l.event, l) );\n        // set the listeners in the connection\n        this.options.listeners = Array.from(eventMap.values());\n        // set the listeners on the socket\n        this.setListeners(this.options.listeners);\n    }\n\n    public getTargetListeners(): Listener[] {\n        return this.targetListeners;\n    }\n\n    public onSetListeners(callback: Function): void {\n        this.options.onSetListeners = callback;\n    }\n\n    public onConnect(callback: Function): void {\n        this.options.onConnect = callback;\n    }\n\n    public onDisconnect(callback: Function): void {\n        this.options.onDisconnect = callback;\n    }\n\n    private queryTargetListeners(): Promise<Listener[]> {\n        // query the target listeners\n        return this.query('_listeners');\n    }\n\n    private queryTargetName(): Promise<string> {\n        // query the target name\n        return this.query('_name');\n    }\n\n    public async query(event: string, data?: any, retries = 3, retryDelay = 500): Promise<any> {\n        let attempt = 0;\n        const tryQuery = (): Promise<any> => {\n            return new Promise((resolve, reject) => {\n                const request_id = ++this.request_id;\n                if (this.request_id >= Number.MAX_SAFE_INTEGER - 1) this.request_id = 0;\n                const responseEvent = `${event}_${request_id}_response`;\n                const timeoutDuration = this.options.timeout || 5000;\n                const timeout = setTimeout(() => {\n                    this.socket.removeAllListeners(responseEvent);\n                    reject(new Error(`Query '${event}' timed out after ${timeoutDuration}ms (attempt ${attempt + 1})`));\n                }, timeoutDuration);\n                this.socket.once(responseEvent, (response: any) => {\n                    clearTimeout(timeout);\n                    resolve(response);\n                });\n                this.socket.emit(event, { data, request_id });\n            });\n        };\n        while (attempt <= retries) {\n            try {\n                return await tryQuery();\n            } catch (err) {\n                if (attempt >= retries) {\n                    throw new Error(`Query '${event}' failed after ${retries + 1} attempts: ${err}`);\n                }\n                attempt++;\n                await new Promise((r) => setTimeout(r, retryDelay)).catch(e => {\n                    throw new Error(`Error during retry delay: ${e}`);\n                });\n            }\n        }\n        // This should never be reached, but TypeScript wants a return or throw here\n        throw new Error('Unexpected query failure');\n    }\n\n    public send = this.query;\n\n    private respond(event: string, callback: Function) {\n        /* this is a wrapper function to respond to a query */\n        return async (parameters: any) => {\n            let data = parameters.data;\n            let request_id = parameters.request_id;\n            let response = await callback(data);\n            this.socket.emit(event + `_${request_id}_response`, response);\n        }\n    }\n\n    public getListeners(): any[] {\n        if(this.type === 'server')\n            return this.options.listeners;\n        else if(this.type === 'client')\n            return this.socket._callbacks;\n        else\n            throw new Error('Connection type not recognized');\n    }\n\n    public close(): void {\n        this.socket.disconnect();\n    }\n\n}\n\nexport default Connection;\n"],"mappings":"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","names":[]}