import z, { ZodType } from "zod"; import type { RelayAdapter, RelayRESTInput } from "./adapter.ts"; import { Procedure } from "./procedure.ts"; import type { Relays } from "./relay.ts"; import { Route } from "./route.ts"; /** * Make a new relay client instance. * * @param config - Client configuration. * @param procedures - Map of procedures to make available to the client. */ export function makeRelayClient(config: RelayClientConfig, relays: TRelays): RelayClient { return mapRelays(relays, config.adapter); } /* |-------------------------------------------------------------------------------- | Helpers |-------------------------------------------------------------------------------- */ function mapRelays(relays: TRelays, adapter: RelayAdapter): RelayClient { const client: any = {}; for (const key in relays) { const relay = relays[key]; if (relay instanceof Procedure) { client[key] = async (params: unknown) => { const response = await adapter.send({ method: relay.method, params }); if ("error" in response) { throw new Error(response.error.message); } if ("result" in response && relay.state.result !== undefined) { return relay.state.result.parseAsync(response.result); } return response.result; }; } else if (relay instanceof Route) { client[key] = async (...args: any[]) => { const input: RelayRESTInput = { method: relay.state.method, url: `${adapter.url}${relay.state.path}`, query: "" }; let index = 0; // argument incrementor if (relay.state.params !== undefined) { const params = args[index++] as { [key: string]: string }; for (const key in params) { input.url = input.url.replace(`:${key}`, params[key]); } } if (relay.state.query !== undefined) { const query = args[index++] as { [key: string]: string }; const pieces: string[] = []; for (const key in query) { pieces.push(`${key}=${query[key]}`); } if (pieces.length > 0) { input.query = `?${pieces.join("&")}`; } } if (relay.state.body !== undefined) { input.body = JSON.stringify(args[index++]); } // ### Fetch const data = await adapter.fetch(input); if (relay.state.output !== undefined) { return relay.state.output.parse(data); } return data; }; } else { client[key] = mapRelays(relay, adapter); } } return client; } /* |-------------------------------------------------------------------------------- | Types |-------------------------------------------------------------------------------- */ export type RelayClient = { [TKey in keyof TRelays]: TRelays[TKey] extends Procedure ? TState["params"] extends ZodType ? (params: z.infer) => Promise : void> : () => Promise : void> : TRelays[TKey] extends Route ? (...args: TRelays[TKey]["args"]) => Promise> : TRelays[TKey] extends Relays ? RelayClient : never; }; type RelayRouteResponse = TRoute["state"]["output"] extends ZodType ? z.infer : void; export type RelayClientConfig = { adapter: RelayAdapter; };