diff --git a/packages/opencode/src/util/rpc.ts b/packages/opencode/src/util/rpc.ts index 02586ebcfc..93f9a9e1eb 100644 --- a/packages/opencode/src/util/rpc.ts +++ b/packages/opencode/src/util/rpc.ts @@ -6,8 +6,14 @@ export function listen(rpc: Definition) { onmessage = async (evt) => { const parsed = JSON.parse(evt.data) if (parsed.type === "rpc.request") { - const result = await rpc[parsed.method](parsed.input) - postMessage(JSON.stringify({ type: "rpc.result", result, id: parsed.id })) + try { + const result = await rpc[parsed.method](parsed.input) + postMessage(JSON.stringify({ type: "rpc.result", result, id: parsed.id })) + } catch (error) { + postMessage( + JSON.stringify({ type: "rpc.error", error: error instanceof Error ? error.message : String(error), id: parsed.id }), + ) + } } } } @@ -20,15 +26,22 @@ export function client(target: { postMessage: (data: string) => void | null onmessage: ((this: Worker, ev: MessageEvent) => any) | null }) { - const pending = new Map void>() + const pending = new Map void; reject: (error: any) => void }>() const listeners = new Map void>>() let id = 0 target.onmessage = async (evt) => { const parsed = JSON.parse(evt.data) if (parsed.type === "rpc.result") { - const resolve = pending.get(parsed.id) - if (resolve) { - resolve(parsed.result) + const request = pending.get(parsed.id) + if (request) { + request.resolve(parsed.result) + pending.delete(parsed.id) + } + } + if (parsed.type === "rpc.error") { + const request = pending.get(parsed.id) + if (request) { + request.reject(new Error(parsed.error)) pending.delete(parsed.id) } } @@ -44,8 +57,8 @@ export function client(target: { return { call(method: Method, input: Parameters[0]): Promise> { const requestId = id++ - return new Promise((resolve) => { - pending.set(requestId, resolve) + return new Promise((resolve, reject) => { + pending.set(requestId, { resolve, reject }) target.postMessage(JSON.stringify({ type: "rpc.request", method, input, id: requestId })) }) }, diff --git a/packages/opencode/test/util/rpc.test.ts b/packages/opencode/test/util/rpc.test.ts new file mode 100644 index 0000000000..6b5ec8ceab --- /dev/null +++ b/packages/opencode/test/util/rpc.test.ts @@ -0,0 +1,31 @@ +import { describe, expect, test } from "bun:test" +import { Rpc } from "@/util/rpc" + +type TestRpc = { + fail(input: undefined): Promise +} + +type Target = Parameters>[0] + +describe("Rpc", () => { + test("rejects pending calls when the worker reports an error", async () => { + const target: Target = { + postMessage(data) { + const request = JSON.parse(data) + target.onmessage?.call( + {} as Worker, + { + data: JSON.stringify({ + type: "rpc.error", + id: request.id, + error: "boom", + }), + } as MessageEvent, + ) + }, + onmessage: null, + } + + await expect(Rpc.client(target).call("fail", undefined)).rejects.toThrow("boom") + }) +})