import type { AdapterOptions } from "./adapter.ts"; import type { WSError } from "./error.ts"; import type { Peer } from "./peer.ts"; import type { Message } from "./message.ts"; export class AdapterHookable { options: AdapterOptions; constructor(options?: AdapterOptions) { this.options = options || {}; } callHook( name: N, arg1: Parameters[0], arg2?: Parameters[1], ): MaybePromise> { // Call global hook first const globalHook = this.options.hooks?.[name]; const globalPromise = globalHook?.(arg1 as any, arg2 as any); // Resolve hooks for request const resolveHooksPromise = this.options.resolve?.(arg1); if (!resolveHooksPromise) { return globalPromise as any; // Fast path: no hooks to resolve } const resolvePromise = resolveHooksPromise instanceof Promise ? resolveHooksPromise.then((hooks) => hooks?.[name]) : resolveHooksPromise?.[name]; // In parallel, call global hook and resolve hook implementation return Promise.all([globalPromise, resolvePromise]).then( ([globalRes, hook]) => { const hookResPromise = hook?.(arg1 as any, arg2 as any); return hookResPromise instanceof Promise ? hookResPromise.then((hookRes) => hookRes || globalRes) : hookResPromise || globalRes; }, ) as Promise; } } // --- types --- export function defineHooks = Partial>( hooks: T, ): T { return hooks; } export type ResolveHooks = ( info: RequestInit | Peer, ) => Partial | Promise>; export type MaybePromise = T | Promise; type HookFn = ( info: Peer, ...args: ArgsT ) => MaybePromise; export interface Hooks { /** Upgrading */ upgrade: ( request: | Request | { url: string; headers: Headers; }, ) => MaybePromise; /** A message is received */ message: (peer: Peer, message: Message) => MaybePromise; /** A socket is opened */ open: (peer: Peer) => MaybePromise; /** A socket is closed */ close: ( peer: Peer, details: { code?: number; reason?: string }, ) => MaybePromise; /** An error occurs */ error: (peer: Peer, error: WSError) => MaybePromise; }