diff --git a/apps/hermes/client/js/package.json b/apps/hermes/client/js/package.json index 575e86f635..dc609edfec 100644 --- a/apps/hermes/client/js/package.json +++ b/apps/hermes/client/js/package.json @@ -1,6 +1,6 @@ { "name": "@pythnetwork/hermes-client", - "version": "1.0.3", + "version": "1.0.4", "description": "Pyth Hermes Client", "author": { "name": "Pyth Data Association" diff --git a/apps/hermes/client/js/src/HermesClient.ts b/apps/hermes/client/js/src/HermesClient.ts index 510c1043dd..f4a12eaed6 100644 --- a/apps/hermes/client/js/src/HermesClient.ts +++ b/apps/hermes/client/js/src/HermesClient.ts @@ -29,12 +29,17 @@ export type HermesClientConfig = { * it will timeout regardless of the retries at the configured `timeout` time. */ httpRetries?: number; + /** + * Optional headers to be included in every request. + */ + headers?: HeadersInit; }; export class HermesClient { private baseURL: string; private timeout: DurationInMs; private httpRetries: number; + private headers: HeadersInit; /** * Constructs a new Connection. @@ -46,6 +51,7 @@ export class HermesClient { this.baseURL = endpoint; this.timeout = config?.timeout ?? DEFAULT_TIMEOUT; this.httpRetries = config?.httpRetries ?? DEFAULT_HTTP_RETRIES; + this.headers = config?.headers ?? {}; } private async httpRequest( @@ -58,7 +64,11 @@ export class HermesClient { ): Promise { const controller = externalAbortController ?? new AbortController(); const { signal } = controller; - options = { ...options, signal }; // Merge any existing options with the signal + options = { + ...options, + signal, + headers: { ...this.headers, ...options?.headers }, + }; // Merge any existing options with the signal and headers // Set a timeout to abort the request if it takes too long const timeout = setTimeout(() => controller.abort(), this.timeout); @@ -129,7 +139,7 @@ export class HermesClient { parsed?: boolean; } ): Promise { - const url = new URL(`v2/updates/price/latest`, this.baseURL); + const url = new URL("v2/updates/price/latest", this.baseURL); for (const id of ids) { url.searchParams.append("ids[]", id); } @@ -208,7 +218,7 @@ export class HermesClient { this.appendUrlSearchParams(url, transformedOptions); } - return new EventSource(url.toString()); + return new EventSource(url.toString(), { headers: this.headers }); } private appendUrlSearchParams( diff --git a/apps/hermes/client/js/src/examples/HermesClient.ts b/apps/hermes/client/js/src/examples/HermesClient.ts index 3965006f30..e643936052 100644 --- a/apps/hermes/client/js/src/examples/HermesClient.ts +++ b/apps/hermes/client/js/src/examples/HermesClient.ts @@ -28,8 +28,35 @@ const argv = yargs(hideBin(process.argv)) }) .parseSync(); +/** + * Extracts the endpoint and basic authorization headers from a given URL string. + * + * @param {string} urlString - The URL string containing the endpoint and optional basic auth credentials. + * @returns {{ endpoint: string; headers: HeadersInit }} An object containing the endpoint URL and headers. + */ +function extractBasicAuthorizationHeadersFromUrl(urlString: string): { + endpoint: string; + headers: HeadersInit; +} { + const url = new URL(urlString); + const headers: HeadersInit = {}; + + if (url.username && url.password) { + headers["Authorization"] = `Basic ${btoa( + `${url.username}:${url.password}` + )}`; + url.username = ""; + url.password = ""; + } + + return { endpoint: url.toString(), headers }; +} + async function run() { - const connection = new HermesClient(argv.endpoint); + const { endpoint, headers } = extractBasicAuthorizationHeadersFromUrl( + argv.endpoint + ); + const connection = new HermesClient(endpoint, { headers }); const priceIds = argv.priceIds as string[];