import { makeRelayClient, RelayClient, RelayClientConfig } from "./client.ts"; import { Procedure, Procedures } from "./procedure.ts"; export class Relay> { readonly #index = new Map(); declare readonly $inferClient: RelayClient; declare readonly $inferIndex: TProcedureIndex; /** * Instantiate a new Relay instance. * * @param procedures - Procedures to register with the instance. */ constructor(readonly procedures: TProcedures) { indexProcedures(procedures, this.#index); } /** * Retrieve a registered procedure registered with the relay instance. * * @param method - Method name assigned to the procedure. */ procedure(method: TMethod): TProcedureIndex[TMethod] { return this.#index.get(method) as TProcedureIndex[TMethod]; } /** * Create a new relay client instance from the instance procedures. * * @param config - Client configuration. */ client(config: RelayClientConfig): this["$inferClient"] { return makeRelayClient(config, this.procedures) as any; } } /* |-------------------------------------------------------------------------------- | Types |-------------------------------------------------------------------------------- */ function indexProcedures, TProcedureKey = keyof TProcedureIndex>( procedures: TProcedures, index: Map, ) { for (const key in procedures) { if (procedures[key] instanceof Procedure) { const method = procedures[key].method as TProcedureKey; if (index.has(method)) { throw new Error(`Relay > Procedure with method '${method}' already exists!`); } index.set(method, procedures[key]); } else { indexProcedures(procedures[key], index); } } } /* |-------------------------------------------------------------------------------- | Types |-------------------------------------------------------------------------------- */ type ProcedureIndex = MergeUnion>; type FlattenProcedures = { [TKey in keyof TProcedures]: TProcedures[TKey] extends Procedure ? Record : TProcedures[TKey] extends Procedures ? FlattenProcedures : never; }[keyof TProcedures]; type MergeUnion = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? { [K in keyof I]: I[K] } : never;