diff --git a/.eslintrc.js b/.eslintrc.js index 37a9e55..3f1fbde 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -48,12 +48,16 @@ module.exports = { { name: 'atob', message: - "'atob' unavailable in React Native 0.72. Use 'decodeBase64' helper in src/obfuscation.ts instead", + "'atob' unavailable in React Native's Hermes JS engine. Use 'decodeBase64' helper in src/obfuscation.ts instead", }, { name: 'btoa', message: - "'btoa' unavailable in React Native 0.72. Use 'encodeBase64' helper in src/obfuscation.ts instead", + "'btoa' unavailable in React Native's Hermes JS engine. Use 'encodeBase64' helper in src/obfuscation.ts instead", + }, + { + name: 'URL', + message: "URL is improperly implemented in React Native's Hermes JS engine.", }, { name: 'Buffer', diff --git a/src/api-endpoints.ts b/src/api-endpoints.ts index 6d45c0c..a7297f6 100644 --- a/src/api-endpoints.ts +++ b/src/api-endpoints.ts @@ -17,23 +17,26 @@ export default class ApiEndpoints { this.params.baseUrl = params.baseUrl ?? DEFAULT_BASE_URL; } - endpoint(resource: string): URL { - const url = new URL(this.params.baseUrl + resource); - Object.entries(this.params.queryParams ?? {}).forEach(([key, value]) => - url.searchParams.append(key, value), - ); - return url; + endpoint(resource: string): string { + const endpointUrl = `${this.params.baseUrl}${resource}`; + const queryParams = this.params.queryParams; + if (!queryParams) { + return endpointUrl; + } + const urlSearchParams = new URLSearchParams(); + Object.entries(queryParams).forEach(([key, value]) => urlSearchParams.append(key, value)); + return `${endpointUrl}?${urlSearchParams}`; } - ufcEndpoint(): URL { + ufcEndpoint(): string { return this.endpoint(UFC_ENDPOINT); } - banditParametersEndpoint(): URL { + banditParametersEndpoint(): string { return this.endpoint(BANDIT_ENDPOINT); } - precomputedFlagsEndpoint(): URL { + precomputedFlagsEndpoint(): string { return this.endpoint(PRECOMPUTED_FLAGS_ENDPOINT); } } diff --git a/src/http-client.ts b/src/http-client.ts index 789b55a..063ecb3 100644 --- a/src/http-client.ts +++ b/src/http-client.ts @@ -46,26 +46,14 @@ export interface IBanditParametersResponse { bandits: Record; } -/** - * Fixes issues with url.toString() in older React Native versions - * that leaves a trailing slash in pathname - * @param url URL to stringify - * @returns stringified url - */ -const urlWithNoTrailingSlash = (url: URL) => { - // Note: URL.pathname does not exist in some React Native JS runtime - // so we have to do string manipulation on the stringified URL - return url.toString().replace(/\/\?/, '?').replace(/\/$/, ''); -}; - export interface IHttpClient { getUniversalFlagConfiguration(): Promise; getBanditParameters(): Promise; getPrecomputedFlags( payload: PrecomputedFlagsPayload, ): Promise; - rawGet(url: URL): Promise; - rawPost(url: URL, payload: P): Promise; + rawGet(url: string): Promise; + rawPost(url: string, payload: P): Promise; } export default class FetchHttpClient implements IHttpClient { @@ -94,14 +82,14 @@ export default class FetchHttpClient implements IHttpClient { ); } - async rawGet(url: URL): Promise { + async rawGet(url: string): Promise { try { // Canonical implementation of abortable fetch for interrupting when request takes longer than desired. // https://developer.chrome.com/blog/abortable-fetch/#reacting_to_an_aborted_fetch const controller = new AbortController(); const signal = controller.signal; const timeoutId = setTimeout(() => controller.abort(), this.timeout); - const response = await fetch(urlWithNoTrailingSlash(url), { signal }); + const response = await fetch(url, { signal }); // Clear timeout when response is received within the budget. clearTimeout(timeoutId); @@ -120,13 +108,13 @@ export default class FetchHttpClient implements IHttpClient { } } - async rawPost(url: URL, payload: P): Promise { + async rawPost(url: string, payload: P): Promise { try { const controller = new AbortController(); const signal = controller.signal; const timeoutId = setTimeout(() => controller.abort(), this.timeout); - const response = await fetch(urlWithNoTrailingSlash(url), { + const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json',