diff --git a/niljs/src/rpc/rpcClient.test.ts b/niljs/src/rpc/rpcClient.test.ts index 24b6de83a..c8ba361c7 100644 --- a/niljs/src/rpc/rpcClient.test.ts +++ b/niljs/src/rpc/rpcClient.test.ts @@ -8,3 +8,21 @@ test("creates a new RPC client with the correct endpoint", () => { expect(actualClient).not.toBeUndefined(); }); + +test("creates a new RPC client with the correct endpoint and headers", () => { + const actualClient = createRPCClient(endpoint, { + headers: { + "My-header": "my-value", + }, + }); + + expect(actualClient).not.toBeUndefined(); +}); + +test("throws an error when the headers are invalid (helps in JavaScript)", () => { + const headers = { + "Invalid-header": 333, + } as unknown as Record; + + expect(() => createRPCClient(endpoint, { headers })).toThrow(); +}); diff --git a/niljs/src/rpc/rpcClient.ts b/niljs/src/rpc/rpcClient.ts index 7c78b61a2..8a51ec8df 100644 --- a/niljs/src/rpc/rpcClient.ts +++ b/niljs/src/rpc/rpcClient.ts @@ -1,5 +1,6 @@ import { Client, HTTPTransport, RequestManager } from "@open-rpc/client-js"; import fetch from "isomorphic-fetch"; +import { isValidHttpHeaders } from "../utils/rpc.js"; import { version } from "../version.js"; /** @@ -7,6 +8,7 @@ import { version } from "../version.js"; */ type RPCClientOptions = { signal?: AbortSignal; + headers?: Record; }; /** @@ -15,14 +17,17 @@ type RPCClientOptions = { * HTTP is currently the only supported transport. * @example const client = createRPCClient(RPC_ENDPOINT); */ -const createRPCClient = (endpoint: string, { signal }: RPCClientOptions = {}) => { +const createRPCClient = (endpoint: string, { signal, headers = {} }: RPCClientOptions = {}) => { const fetcher: typeof fetch = (url, options) => { return fetch(url, { ...options, signal }); }; + isValidHttpHeaders(headers); + const transport = new HTTPTransport(endpoint, { headers: { "Client-Version": version, + ...headers, }, fetcher, }); diff --git a/niljs/src/transport/HttpTransport.ts b/niljs/src/transport/HttpTransport.ts index 30c010c1d..754aebe8e 100644 --- a/niljs/src/transport/HttpTransport.ts +++ b/niljs/src/transport/HttpTransport.ts @@ -33,8 +33,8 @@ class HttpTransport implements ITransport { * @constructor * @param {IHttpTransportConfig} config The transport config. See {@link IHttpTransportConfig}. */ - constructor({ endpoint, signal, timeout = 20000 }: IHttpTransportConfig) { - this.rpcClient = createRPCClient(endpoint, { signal }); + constructor({ endpoint, signal, timeout = 20000, headers }: IHttpTransportConfig) { + this.rpcClient = createRPCClient(endpoint, { signal, headers }); this.timeout = timeout; } diff --git a/niljs/src/transport/types/IHttpTransportConfig.ts b/niljs/src/transport/types/IHttpTransportConfig.ts index 83bc04be9..821a0264b 100644 --- a/niljs/src/transport/types/IHttpTransportConfig.ts +++ b/niljs/src/transport/types/IHttpTransportConfig.ts @@ -21,6 +21,12 @@ type IHttpTransportConfig = { * @default undefined */ signal?: AbortSignal; + /** + * The headers to be sent with the request. + * @example { 'My-header': 'my-value' } + * @default {} + */ + headers?: Record; }; export type { IHttpTransportConfig }; diff --git a/niljs/src/utils/rpc.ts b/niljs/src/utils/rpc.ts new file mode 100644 index 000000000..4e2c65d9f --- /dev/null +++ b/niljs/src/utils/rpc.ts @@ -0,0 +1,15 @@ +function isValidHttpHeaders(headers: unknown) { + if (headers === null || typeof headers !== "object" || Array.isArray(headers)) { + throw new Error("Invalid headers provided to the RPC client."); + } + + const isValidObj = Object.entries(headers).every( + ([key, value]) => typeof key === "string" && typeof value === "string", + ); + + if (!isValidObj) { + throw new Error("Invalid http headers provided to the RPC client."); + } +} + +export { isValidHttpHeaders };