diff --git a/src/client/stdio.test.ts b/src/client/stdio.test.ts index 646f9ea5..bc7f5a01 100644 --- a/src/client/stdio.test.ts +++ b/src/client/stdio.test.ts @@ -1,9 +1,7 @@ import { JSONRPCMessage } from "../types.js"; -import { StdioClientTransport, StdioServerParameters } from "./stdio.js"; +import { StdioClientTransport, getDefaultServerParameters } from "./stdio.js"; -const serverParameters: StdioServerParameters = { - command: "/usr/bin/tee", -}; +const serverParameters = getDefaultServerParameters(); test("should start then close cleanly", async () => { const client = new StdioClientTransport(serverParameters); diff --git a/src/client/stdio.ts b/src/client/stdio.ts index b83bf27c..92cda35e 100644 --- a/src/client/stdio.ts +++ b/src/client/stdio.ts @@ -39,6 +39,15 @@ export type StdioServerParameters = { cwd?: string; }; +// Configure default server parameters based on OS +// Uses 'more' command for Windows and 'tee' command for Unix/Linux +export const getDefaultServerParameters = (): StdioServerParameters => { + if (process.platform === "win32") { + return { command: "more" }; + } + return { command: "/usr/bin/tee" }; +}; + /** * Environment variables to inherit by default, if an environment is not explicitly given. */ diff --git a/src/inMemory.test.ts b/src/inMemory.test.ts index f7e9e979..f7d86718 100644 --- a/src/inMemory.test.ts +++ b/src/inMemory.test.ts @@ -69,10 +69,43 @@ describe("InMemoryTransport", () => { }); test("should throw error when sending after close", async () => { - await clientTransport.close(); + const [client, server] = InMemoryTransport.createLinkedPair(); + let clientError: Error | undefined; + let serverError: Error | undefined; + + client.onerror = (err) => { + clientError = err; + }; + + server.onerror = (err) => { + serverError = err; + }; + + await client.close(); + + // Attempt to send message from client await expect( - clientTransport.send({ jsonrpc: "2.0", method: "test", id: 1 }), + client.send({ + jsonrpc: "2.0", + method: "test", + id: 1, + }), ).rejects.toThrow("Not connected"); + + // Attempt to send message from server + await expect( + server.send({ + jsonrpc: "2.0", + method: "test", + id: 2, + }), + ).rejects.toThrow("Not connected"); + + // Verify that both sides received errors + expect(clientError).toBeDefined(); + expect(clientError?.message).toBe("Not connected"); + expect(serverError).toBeDefined(); + expect(serverError?.message).toBe("Not connected"); }); test("should queue messages sent before start", async () => { @@ -91,4 +124,65 @@ describe("InMemoryTransport", () => { await serverTransport.start(); expect(receivedMessage).toEqual(message); }); + + describe("error handling", () => { + test("should trigger onerror when sending without connection", async () => { + const transport = new InMemoryTransport(); + let error: Error | undefined; + + transport.onerror = (err) => { + error = err; + }; + + await expect( + transport.send({ + jsonrpc: "2.0", + method: "test", + id: 1, + }), + ).rejects.toThrow("Not connected"); + + expect(error).toBeDefined(); + expect(error?.message).toBe("Not connected"); + }); + + test("should trigger onerror when sending after close", async () => { + const [client, server] = InMemoryTransport.createLinkedPair(); + let clientError: Error | undefined; + let serverError: Error | undefined; + + client.onerror = (err) => { + clientError = err; + }; + + server.onerror = (err) => { + serverError = err; + }; + + await client.close(); + + // Attempt to send message from client + await expect( + client.send({ + jsonrpc: "2.0", + method: "test", + id: 1, + }), + ).rejects.toThrow("Not connected"); + + // Attempt to send message from server + await expect( + server.send({ + jsonrpc: "2.0", + method: "test", + id: 2, + }), + ).rejects.toThrow("Not connected"); + + // Verify that both sides received errors + expect(clientError?.message).toBe("Not connected"); + expect(serverError).toBeDefined(); + expect(serverError?.message).toBe("Not connected"); + }); + }); }); diff --git a/src/inMemory.ts b/src/inMemory.ts index 106a9e7e..bd95b4fe 100644 --- a/src/inMemory.ts +++ b/src/inMemory.ts @@ -43,7 +43,9 @@ export class InMemoryTransport implements Transport { async send(message: JSONRPCMessage): Promise { if (!this._otherTransport) { - throw new Error("Not connected"); + const error = new Error("Not connected"); + this.onerror?.(error); + throw error; } if (this._otherTransport.onmessage) {