import { Effect } from "effect"; import type * as CloudflareRpc from "../Rpc"; type AnyArgs = Array; export type AsyncMethodKey = { [Key in keyof Api]-?: Key extends string ? Api[Key] extends (...args: AnyArgs) => Promise ? Key : never : never; }[keyof Api]; export type AsyncMethodArgs = Api[Method] extends ( ...args: infer Args ) => Promise ? Args : never; export type AsyncMethodSuccess = Api[Method] extends ( ...args: AnyArgs ) => Promise ? A : never; export type AsyncMethodCloudflareReturn = CloudflareRpc.Result< AsyncMethodSuccess >; const isPropertyTarget = (value: unknown): value is object => (typeof value === "object" || typeof value === "function") && value !== null; export const lookupRpcMethod = , Error>( target: unknown, method: Method, makeError: (cause: unknown) => Error, ): Effect.Effect< (...args: AsyncMethodArgs) => AsyncMethodCloudflareReturn, Error > => Effect.try({ try: () => { if (!isPropertyTarget(target)) { throw new TypeError(`RPC target is not object-like`); } const value = Reflect.get(target, method); if (typeof value !== "function") { throw new TypeError(`RPC method "${String(method)}" is not callable`); } return ((...args: AsyncMethodArgs) => Reflect.apply(value, target, args)) as ( ...args: AsyncMethodArgs ) => AsyncMethodCloudflareReturn; }, catch: makeError, }); export const invokeRpcMethod = , Error>( target: unknown, method: Method, args: AsyncMethodArgs, makeError: (cause: unknown) => Error, ): Effect.Effect, Error> => Effect.gen(function* () { const fn = yield* lookupRpcMethod(target, method, makeError); return yield* Effect.try({ try: () => fn(...args), catch: makeError, }); });