import * as Effect from "effect/Effect"; import * as Schema from "effect/Schema"; import * as SchemaIssue from "effect/SchemaIssue"; import * as CodexError from "../errors.ts"; const formatSchemaIssue = SchemaIssue.makeFormatterDefault(); export const JsonRpcId = Schema.Union([Schema.Number, Schema.String]); export const JsonRpcError = Schema.Struct({ code: Schema.Number, message: Schema.String, data: Schema.optional(Schema.Unknown), }); export const JsonRpcResponseEnvelope = Schema.Struct({ id: JsonRpcId, result: Schema.optional(Schema.Unknown), error: Schema.optional(JsonRpcError), }); export const decodeOptionalPayload = ( method: string, schema: Schema.Codec | undefined, raw: unknown, ): Effect.Effect => { if (!schema) { if (raw === undefined) { return Effect.sync(() => undefined as A); } return Effect.fail( CodexError.CodexAppServerRequestError.invalidParams(`${method} does not accept params`, raw), ); } return Schema.decodeUnknownEffect(schema)(raw).pipe( Effect.mapError((error) => CodexError.CodexAppServerRequestError.invalidParams( `Invalid ${method} payload: ${formatSchemaIssue(error.issue)}`, { issue: error.issue }, ), ), ); }; export const encodeOptionalPayload = ( method: string, schema: Schema.Codec | undefined, payload: A, ): Effect.Effect => { if (!schema) { if (payload === undefined) { return Effect.sync(() => undefined); } return Effect.fail( CodexError.CodexAppServerRequestError.invalidParams( `${method} does not accept params`, payload, ), ); } return Schema.encodeEffect(schema)(payload).pipe( Effect.mapError((error) => CodexError.CodexAppServerRequestError.invalidParams( `Invalid ${method} payload: ${formatSchemaIssue(error.issue)}`, { issue: error.issue }, ), ), ); }; export const decodeNotificationPayload = ( method: string, schema: Schema.Codec | undefined, raw: unknown, ): Effect.Effect => decodeOptionalPayload(method, schema, raw).pipe( Effect.mapError( (error) => new CodexError.CodexAppServerProtocolParseError({ detail: error.message, cause: error, }), ), ); export const runHandler = Effect.fnUntraced(function* ( handler: ((payload: A) => Effect.Effect) | undefined, payload: A, method: string, ) { if (!handler) { return yield* CodexError.CodexAppServerRequestError.methodNotFound(method); } return yield* handler(payload).pipe( Effect.mapError((error) => CodexError.normalizeToRequestError(error)), ); });