import type { Json, stringified } from "atom.io/foundations/json" import type { Socket } from "atom.io/realtime" export type Events = Json.Object export type EventPayload< receiveRelay extends Events, K extends string & keyof receiveRelay = string & keyof receiveRelay, > = [string, ...receiveRelay[K]] export function isEventPayload( value: unknown, ): value is [string, ...Json.Array] { return Array.isArray(value) && typeof value[0] === `string` } export interface EventBuffer< E extends Events, K extends string & keyof E = string & keyof E, > extends Buffer { toString(): stringified> } export abstract class CustomSocket< I extends Events, O extends Events, > implements Socket { protected listeners: Map void>> protected globalListeners: Set<(event: string, ...args: Json.Array) => void> protected globalListenersOutgoing: Set< (event: string, ...args: Json.Array) => void > protected handleEvent( ...args: EventPayload ): void { const [event, ...rest] = args for (const listener of this.globalListeners) { listener(event, ...rest) } const listeners = this.listeners.get(event) if (listeners) { for (const listener of listeners) { listener(...rest) } } } public id = `no_id_retrieved` public emit: ( event: Event, ...args: I[Event] ) => CustomSocket public constructor( emit: ( event: Event, ...args: I[Event] ) => CustomSocket, ) { this.emit = (...args) => { for (const listener of this.globalListenersOutgoing) { listener(...args) } return emit(...args) } this.listeners = new Map() this.globalListeners = new Set() this.globalListenersOutgoing = new Set() } public on( event: Event, listener: (...args: O[Event]) => void, ): this { const listeners = this.listeners.get(event) if (listeners) { listeners.add(listener) } else { this.listeners.set(event, new Set([listener])) } return this } public onAny(listener: (event: string, ...args: Json.Array) => void): this { this.globalListeners.add(listener) return this } public onAnyOutgoing( listener: (event: string, ...args: Json.Array) => void, ): this { this.globalListenersOutgoing.add(listener) return this } public off( event: Event, listener?: (...args: O[Event]) => void, ): this { const listeners = this.listeners.get(event) if (listeners) { if (listener) { listeners.delete(listener) } else { this.listeners.delete(event) } } return this } public offAny(listener?: (event: string, ...args: Json.Array) => void): this { if (listener) { this.globalListeners.delete(listener) } else { this.globalListeners.clear() } return this } }