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`),
);