import * as Effect from "effect/Effect"; import * as Schema from "effect/Schema"; import { RpcClientError } from "effect/unstable/rpc"; import * as AcpSchema from "../_generated/schema.gen.ts"; import * as AcpError from "../errors.ts"; const isError = Schema.is(AcpSchema.Error); const isAcpError = Schema.is(AcpError.AcpError); export const callRpc = ( method: string, effect: Effect.Effect, ): Effect.Effect => effect.pipe( Effect.catchIf(isError, (error) => Effect.fail(AcpError.AcpRequestError.fromProtocolError(error, { method })), ), Effect.catchTags({ RpcClientError: (cause) => Effect.fail( cause.reason._tag === "RpcClientDefect" && isAcpError(cause.reason.cause) ? cause.reason.cause : new AcpError.AcpTransportError({ operation: "call-rpc", method, cause, }), ), }), ); export const runHandler = Effect.fnUntraced(function* ( handler: ((payload: A) => Effect.Effect) | undefined, payload: A, method: string, ) { if (!handler) { return yield* Effect.fail(AcpError.AcpRequestError.methodNotFound(method).toProtocolError()); } return yield* handler(payload).pipe( Effect.mapError((error) => AcpError.AcpRequestError.fromCoreHandlerError(error, method).toProtocolError(), ), ); }); export function decodeExtRequestRegistration( method: string, payload: Schema.Codec, handler: (payload: A) => Effect.Effect, ) { return (params: unknown): Effect.Effect => Schema.decodeUnknownEffect(payload)(params).pipe( Effect.mapError((error) => AcpError.AcpRequestError.invalidExtensionPayload(method, error)), Effect.flatMap((decoded) => handler(decoded)), ); } export function decodeExtNotificationRegistration( method: string, payload: Schema.Codec, handler: (payload: A) => Effect.Effect, ) { return (params: unknown): Effect.Effect => Schema.decodeUnknownEffect(payload)(params).pipe( Effect.mapError((error) => AcpError.AcpProtocolParseError.fromSchemaError( "decode-notification-payload", method, error, ), ), Effect.flatMap((decoded) => handler(decoded)), ); } const encoder = new TextEncoder(); const JsonRpcId = Schema.Union([Schema.Number, Schema.String]); const JsonRpcHeaders = Schema.Array(Schema.Unknown); export const jsonRpcRequest = (method: string, params: Schema.Codec) => Schema.Struct({ jsonrpc: Schema.Literal("2.0"), id: JsonRpcId, method: Schema.Literal(method), params, headers: JsonRpcHeaders.pipe(Schema.withDecodingDefaultKey(Effect.succeed([]))), }); export const jsonRpcNotification = (method: string, params: Schema.Codec) => Schema.Struct({ jsonrpc: Schema.Literal("2.0"), method: Schema.Literal(method), params, }); export const jsonRpcResponse = (result: Schema.Codec) => Schema.Struct({ jsonrpc: Schema.Literal("2.0"), id: JsonRpcId, result, }); export const encodeJsonl = (schema: Schema.Codec, value: A) => Effect.map(Schema.encodeEffect(Schema.fromJsonString(schema))(value), (encoded) => encoder.encode(`${encoded}\n`), );