From 22384ae977a362c547d6f57d4e3d92bbe55ee5db Mon Sep 17 00:00:00 2001 From: "cursor[bot]" <206951365+cursor[bot]@users.noreply.github.com> Date: Wed, 6 May 2026 10:36:25 -0700 Subject: [PATCH] Adopt Effect JSON and DateTime idioms (#2559) Co-authored-by: Cursor Agent Co-authored-by: Julius Marminge --- .../src/protocol.test.ts | 12 +++-- .../effect-codex-app-server/src/protocol.ts | 45 +++++++++++-------- scripts/notify-discord-release.ts | 5 ++- 3 files changed, 38 insertions(+), 24 deletions(-) diff --git a/packages/effect-codex-app-server/src/protocol.test.ts b/packages/effect-codex-app-server/src/protocol.test.ts index b0f2fd503e22ed59a3b7cdf55b9f601ce0810ef6..e75a99f6f6008cf7d1638aa018716328da36c433 100644 --- a/packages/effect-codex-app-server/src/protocol.test.ts +++ b/packages/effect-codex-app-server/src/protocol.test.ts @@ -2,6 +2,7 @@ import * as Deferred from "effect/Deferred"; import * as Effect from "effect/Effect"; import * as Fiber from "effect/Fiber"; import * as Queue from "effect/Queue"; +import * as Schema from "effect/Schema"; import * as Stream from "effect/Stream"; import * as NodeServices from "@effect/platform-node/NodeServices"; @@ -13,7 +14,10 @@ import { makeInMemoryStdio } from "./_internal/stdio.ts"; const encoder = new TextEncoder(); -const encodeJsonl = (value: unknown) => encoder.encode(`${JSON.stringify(value)}\n`); +const encodeJsonl = (value: unknown) => + encoder.encode(`${Schema.encodeUnknownSync(Schema.UnknownFromJsonString)(value)}\n`); + +const decodeJson = Schema.decodeEffect(Schema.UnknownFromJsonString); it.layer(NodeServices.layer)("effect-codex-app-server protocol", (it) => { it.effect( @@ -60,7 +64,7 @@ it.layer(NodeServices.layer)("effect-codex-app-server protocol", (it) => { const pendingInitialize = yield* transport .request("initialize", initializeParams) .pipe(Effect.forkScoped); - assert.deepEqual(JSON.parse(yield* Queue.take(output)), { + assert.deepEqual(yield* decodeJson(yield* Queue.take(output)), { id: 1, method: "initialize", params: initializeParams, @@ -153,7 +157,7 @@ it.layer(NodeServices.layer)("effect-codex-app-server protocol", (it) => { }, }, }); - assert.deepEqual(JSON.parse(yield* Queue.take(output)), { + assert.deepEqual(yield* decodeJson(yield* Queue.take(output)), { id: 77, result: { answers: { @@ -168,7 +172,7 @@ it.layer(NodeServices.layer)("effect-codex-app-server protocol", (it) => { 78, CodexError.CodexAppServerRequestError.methodNotFound("x/test"), ); - assert.deepEqual(JSON.parse(yield* Queue.take(output)), { + assert.deepEqual(yield* decodeJson(yield* Queue.take(output)), { id: 78, error: { code: -32601, diff --git a/packages/effect-codex-app-server/src/protocol.ts b/packages/effect-codex-app-server/src/protocol.ts index 0b730aa98835e36c149daa61a75fa80268b63f3e..d552af8251ba19aac3fdb49442053a9c900e838d 100644 --- a/packages/effect-codex-app-server/src/protocol.ts +++ b/packages/effect-codex-app-server/src/protocol.ts @@ -83,17 +83,35 @@ function isIncomingResponse(value: unknown): value is typeof JsonRpcResponseEnve return Schema.is(JsonRpcResponseEnvelope)(value); } +const encodeJsonString = Schema.encodeUnknownEffect(Schema.UnknownFromJsonString); +const decodeJsonString = Schema.decodeUnknownEffect(Schema.UnknownFromJsonString); + const encodeWireMessage = ( message: Record, ): Effect.Effect => - Effect.try({ - try: () => `${JSON.stringify(message)}\n`, - catch: (cause) => - new CodexError.CodexAppServerProtocolParseError({ - detail: "Failed to encode Codex App Server message", - cause, - }), - }); + encodeJsonString(message).pipe( + Effect.map((encoded) => `${encoded}\n`), + Effect.mapError( + (cause) => + new CodexError.CodexAppServerProtocolParseError({ + detail: "Failed to encode Codex App Server message", + cause, + }), + ), + ); + +const decodeWireMessage = ( + line: string, +): Effect.Effect => + decodeJsonString(line).pipe( + Effect.mapError( + (cause) => + new CodexError.CodexAppServerProtocolParseError({ + detail: "Failed to decode Codex App Server wire message", + cause, + }), + ), + ); const normalizeIncomingError = (error: unknown, detail: string): CodexError.CodexAppServerError => Schema.is(CodexError.CodexAppServerError)(error) @@ -284,16 +302,7 @@ export const makeCodexAppServerPatchedProtocol = Effect.fn("makeCodexAppServerPa stage: "raw", payload: line, }).pipe( - Effect.flatMap(() => - Effect.try({ - try: () => JSON.parse(line), - catch: (cause) => - new CodexError.CodexAppServerProtocolParseError({ - detail: "Failed to decode Codex App Server wire message", - cause, - }), - }), - ), + Effect.flatMap(() => decodeWireMessage(line)), Effect.tap((decoded) => logProtocol({ direction: "incoming", diff --git a/scripts/notify-discord-release.ts b/scripts/notify-discord-release.ts index cb4ed0384db02d30b8d22303787faaea369fa066..554bd65aa31f8da5fd8b153ae6d034688b922ce7 100644 --- a/scripts/notify-discord-release.ts +++ b/scripts/notify-discord-release.ts @@ -2,7 +2,7 @@ import * as NodeRuntime from "@effect/platform-node/NodeRuntime"; import * as NodeServices from "@effect/platform-node/NodeServices"; -import { Config, Data, Effect, Layer, Logger, Schema } from "effect"; +import { Config, Data, DateTime, Effect, Layer, Logger, Schema } from "effect"; import { Argument, Command, Flag } from "effect/unstable/cli"; import { FetchHttpClient, @@ -201,6 +201,7 @@ export const notifyDiscordReleaseCommand = Command.make( ); const webhookUrl = yield* DiscordWebhookUrl; + const timestamp = DateTime.formatIso(yield* DateTime.now); const payload = buildDiscordReleaseAnnouncement({ target, roleId, @@ -208,7 +209,7 @@ export const notifyDiscordReleaseCommand = Command.make( version: releaseVersion, tag, releaseUrl, - timestamp: new Date().toISOString(), + timestamp, }); yield* Effect.logInfo("discord release announcement payload built").pipe( -- 2.54.0