From f0b5a13f6f9b29daab31cac7517a0bfe2cc17755 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20R=C3=A9?= Date: Thu, 16 Apr 2026 15:46:00 +0400 Subject: [PATCH] Use URL objects for API endpoint construction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Store baseUrl as URL and build endpoints with new URL(path, base). Accept URL | string in fetchJSON to avoid unnecessary .href calls. Signed-off-by: Émile Ré --- packages/cookie-banner/src/client.ts | 19 +++++++++++-------- packages/cookie-banner/src/http.ts | 4 ++-- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/packages/cookie-banner/src/client.ts b/packages/cookie-banner/src/client.ts index ceb064afa..e02b7d8e4 100644 --- a/packages/cookie-banner/src/client.ts +++ b/packages/cookie-banner/src/client.ts @@ -64,7 +64,7 @@ export interface CookieBannerClientOptions { } export class CookieBannerClient { - private readonly baseUrl: string; + private readonly baseUrl: URL; private readonly bannerId: string; private readonly visitorId: string; @@ -74,16 +74,16 @@ export class CookieBannerClient { constructor(config: CookieBannerClientOptions) { let base = config.baseUrl; - while (base.endsWith("/")) { - base = base.slice(0, -1); + if (!base.endsWith("/")) { + base += "/"; } - this.baseUrl = base; + this.baseUrl = new URL(base); this.bannerId = config.bannerId; this.visitorId = getOrCreateVisitorId(config.bannerId); } async load(): Promise { - const configUrl = `${this.baseUrl}/${this.bannerId}/config`; + const configUrl = new URL(`${this.bannerId}/config`, this.baseUrl); const config = await fetchJSON(configUrl); this.bannerConfig = config; @@ -101,7 +101,10 @@ export class CookieBannerClient { return; } - const consentUrl = `${this.baseUrl}/${this.bannerId}/consents/${this.visitorId}`; + const consentUrl = new URL( + `${this.bannerId}/consents/${this.visitorId}`, + this.baseUrl, + ); const apiConsent = await fetchJSON(consentUrl).catch( (err) => { if (err instanceof NotFoundError) { @@ -185,7 +188,7 @@ export class CookieBannerClient { consentData: Record, ): Promise { const cfg = this.config; - const url = `${this.baseUrl}/${this.bannerId}/consents`; + const url = new URL(`${this.bannerId}/consents`, this.baseUrl); const body = { visitor_id: this.visitorId, version: cfg.version, @@ -201,7 +204,7 @@ export class CookieBannerClient { }); void flush(this.bannerId); } catch { - enqueue(this.bannerId, url, body); + enqueue(this.bannerId, url.href, body); } this.consent = { diff --git a/packages/cookie-banner/src/http.ts b/packages/cookie-banner/src/http.ts index c68ac03d4..8017e5041 100644 --- a/packages/cookie-banner/src/http.ts +++ b/packages/cookie-banner/src/http.ts @@ -76,7 +76,7 @@ async function parseErrorBody(response: Response): Promise { } async function fetchWithTimeout( - url: string, + url: URL | string, init: RequestInit, timeout: number, ): Promise { @@ -109,7 +109,7 @@ async function fetchWithTimeout( } export async function fetchJSON( - url: string, + url: URL | string, options: RequestOptions = {}, ): Promise { const { method = "GET", headers, body, timeout = DEFAULT_TIMEOUT_MS, signal } = options;