From 48a286946413e84a67b6ed07d022bb3426655086 Mon Sep 17 00:00:00 2001 From: HEY Research Lab <327801004+hey-research-lab@users.noreply.github.com> Date: Thu, 22 Oct 2026 16:45:29 +0800 Subject: [PATCH] fix: keep default config for options passed as undefined new Codex(key, { apiUrl: process.env.CODEX_API_URL }) with the variable unset replaced the default apiUrl with undefined, so every query and mutation failed with "Only absolute URLs are supported". The same held for apiRealtimeUrl and ws. --- src/sdk/index.test.ts | 32 ++++++++++++++++++++++++++++++++ src/sdk/index.ts | 7 ++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/sdk/index.test.ts b/src/sdk/index.test.ts index e8a664c..4a21f9d 100644 --- a/src/sdk/index.test.ts +++ b/src/sdk/index.test.ts @@ -100,6 +100,38 @@ describe("Codex", () => { createGraphQLClientSpy.mockRestore(); createWebsocketClientSpy.mockRestore(); }); + it("should keep the defaults for options passed as undefined", async () => { + const urls: string[] = []; + const fetchStub = jest.fn(async (input: RequestInfo | URL) => { + urls.push(String(input)); + return new Response(JSON.stringify({ data: { getNetworks: [] } }), { + headers: { "content-type": "application/json" }, + }); + }); + const createWebsocketClientSpy = jest.spyOn( + Codex.prototype as any, + "createWebsocketClient", + ); + + const sdkInstance = new Codex("test-key", { + apiUrl: undefined, + apiRealtimeUrl: undefined, + ws: undefined, + fetch: fetchStub, + }); + await sdkInstance.send(getNetworksString); + + expect(urls).toEqual(["https://graph.codex.io/graphql"]); + expect(createWebsocketClientSpy).toHaveBeenCalledWith( + expect.objectContaining({ + apiRealtimeUrl: "wss://graph.codex.io/graphql", + ws: true, + }), + ); + + createWebsocketClientSpy.mockRestore(); + await sdkInstance.dispose(); + }); }); describe("updateConfig", () => { diff --git a/src/sdk/index.ts b/src/sdk/index.ts index 4fa2ffd..5c6e7f6 100644 --- a/src/sdk/index.ts +++ b/src/sdk/index.ts @@ -85,7 +85,12 @@ export class Codex { private parseConfig(config: Partial) { invariant(this.apiKey, "apiKey must be defined"); - return Object.assign({}, defaultConfig, config); + // An option passed as undefined (e.g. `apiUrl: process.env.CODEX_API_URL` + // with the variable unset) keeps its default instead of replacing it. + const defined = Object.fromEntries( + Object.entries(config).filter(([, value]) => value !== undefined), + ); + return Object.assign({}, defaultConfig, defined) as ApiConfig; } private createGraphQLClient(config: ApiConfig) {