Files
welshman/packages/net/src/adapter.ts
T
2025-04-02 08:54:50 -07:00

125 lines
2.6 KiB
TypeScript

import EventEmitter from "events"
import {call, mergeRight, on} from "@welshman/lib"
import {isRelayUrl} from "@welshman/util"
import {LocalRelay, LOCAL_RELAY_URL} from "@welshman/relay"
import {RelayMessage, ClientMessage} from "./message.js"
import {Socket, SocketEvent} from "./socket.js"
import {Unsubscriber} from "./util.js"
import {netContext, NetContext} from "./context.js"
export enum AdapterEvent {
Receive = "adapter:event:receive",
}
export type AdapterEvents = {
[AdapterEvent.Receive]: (message: RelayMessage, url: string) => void
}
export abstract class AbstractAdapter extends EventEmitter {
_unsubscribers: Unsubscriber[] = []
abstract urls: string[]
abstract sockets: Socket[]
abstract send(message: ClientMessage): void
cleanup() {
this.removeAllListeners()
this._unsubscribers.splice(0).forEach(call)
}
}
export class SocketAdapter extends AbstractAdapter {
constructor(readonly socket: Socket) {
super()
this._unsubscribers.push(
on(socket, SocketEvent.Receive, (message: RelayMessage, url: string) => {
this.emit(AdapterEvent.Receive, message, url)
}),
)
}
get sockets() {
return [this.socket]
}
get urls() {
return [this.socket.url]
}
send(message: ClientMessage) {
this.socket.send(message)
}
}
export class LocalAdapter extends AbstractAdapter {
constructor(readonly relay: LocalRelay) {
super()
this._unsubscribers.push(
on(relay, "*", (...message: RelayMessage) => {
this.emit(AdapterEvent.Receive, message, LOCAL_RELAY_URL)
}),
)
}
get sockets() {
return []
}
get urls() {
return [LOCAL_RELAY_URL]
}
send(message: ClientMessage) {
const [type, ...rest] = message
this.relay.send(type, ...rest)
}
}
export class MockAdapter extends AbstractAdapter {
constructor(
readonly url: string,
readonly send: (message: ClientMessage) => void,
) {
super()
}
get sockets() {
return []
}
get urls() {
return [this.url]
}
receive = (message: RelayMessage) => {
this.emit(AdapterEvent.Receive, message, this.url)
}
}
export type AdapterContext = Partial<NetContext>
export const getAdapter = (url: string, adapterContext: AdapterContext = {}) => {
const context = mergeRight(netContext, adapterContext as any)
if (context.getAdapter) {
const adapter = context.getAdapter(url, context)
if (adapter) {
return adapter
}
}
if (url === LOCAL_RELAY_URL) {
return new LocalAdapter(new LocalRelay(context.repository))
}
if (isRelayUrl(url)) {
return new SocketAdapter(context.pool.get(url))
}
throw new Error(`Invalid relay url ${url}`)
}