// src/cache/createBrowserLocalStorageCache.ts function createBrowserLocalStorageCache(options) { let storage; const namespaceKey = `algolia-client-js-${options.key}`; function getStorage() { if (storage === void 0) { storage = options.localStorage || window.localStorage; } return storage; } function getNamespace() { return JSON.parse(getStorage().getItem(namespaceKey) || "{}"); } function setNamespace(namespace) { getStorage().setItem(namespaceKey, JSON.stringify(namespace)); } function removeOutdatedCacheItems() { const timeToLive = options.timeToLive ? options.timeToLive * 1e3 : null; const namespace = getNamespace(); const filteredNamespaceWithoutOldFormattedCacheItems = Object.fromEntries( Object.entries(namespace).filter(([, cacheItem]) => { return cacheItem.timestamp !== void 0; }) ); setNamespace(filteredNamespaceWithoutOldFormattedCacheItems); if (!timeToLive) { return; } const filteredNamespaceWithoutExpiredItems = Object.fromEntries( Object.entries(filteredNamespaceWithoutOldFormattedCacheItems).filter(([, cacheItem]) => { const currentTimestamp = (/* @__PURE__ */ new Date()).getTime(); const isExpired = cacheItem.timestamp + timeToLive < currentTimestamp; return !isExpired; }) ); setNamespace(filteredNamespaceWithoutExpiredItems); } return { get(key, defaultValue, events = { miss: () => Promise.resolve() }) { return Promise.resolve().then(() => { removeOutdatedCacheItems(); return getNamespace()[JSON.stringify(key)]; }).then((value) => { return Promise.all([value ? value.value : defaultValue(), value !== void 0]); }).then(([value, exists]) => { return Promise.all([value, exists || events.miss(value)]); }).then(([value]) => value); }, set(key, value) { return Promise.resolve().then(() => { const namespace = getNamespace(); namespace[JSON.stringify(key)] = { timestamp: (/* @__PURE__ */ new Date()).getTime(), value }; getStorage().setItem(namespaceKey, JSON.stringify(namespace)); return value; }); }, delete(key) { return Promise.resolve().then(() => { const namespace = getNamespace(); delete namespace[JSON.stringify(key)]; getStorage().setItem(namespaceKey, JSON.stringify(namespace)); }); }, clear() { return Promise.resolve().then(() => { getStorage().removeItem(namespaceKey); }); } }; } // src/cache/createNullCache.ts function createNullCache() { return { get(_key, defaultValue, events = { miss: () => Promise.resolve() }) { const value = defaultValue(); return value.then((result) => Promise.all([result, events.miss(result)])).then(([result]) => result); }, set(_key, value) { return Promise.resolve(value); }, delete(_key) { return Promise.resolve(); }, clear() { return Promise.resolve(); } }; } // src/cache/createFallbackableCache.ts function createFallbackableCache(options) { const caches = [...options.caches]; const current = caches.shift(); if (current === void 0) { return createNullCache(); } return { get(key, defaultValue, events = { miss: () => Promise.resolve() }) { return current.get(key, defaultValue, events).catch(() => { return createFallbackableCache({ caches }).get(key, defaultValue, events); }); }, set(key, value) { return current.set(key, value).catch(() => { return createFallbackableCache({ caches }).set(key, value); }); }, delete(key) { return current.delete(key).catch(() => { return createFallbackableCache({ caches }).delete(key); }); }, clear() { return current.clear().catch(() => { return createFallbackableCache({ caches }).clear(); }); } }; } // src/cache/createMemoryCache.ts function createMemoryCache(options = { serializable: true }) { let cache = {}; return { get(key, defaultValue, events = { miss: () => Promise.resolve() }) { const keyAsString = JSON.stringify(key); if (keyAsString in cache) { return Promise.resolve(options.serializable ? JSON.parse(cache[keyAsString]) : cache[keyAsString]); } const promise = defaultValue(); return promise.then((value) => events.miss(value)).then(() => promise); }, set(key, value) { cache[JSON.stringify(key)] = options.serializable ? JSON.stringify(value) : value; return Promise.resolve(value); }, delete(key) { delete cache[JSON.stringify(key)]; return Promise.resolve(); }, clear() { cache = {}; return Promise.resolve(); } }; } // src/constants.ts var DEFAULT_CONNECT_TIMEOUT_BROWSER = 1e3; var DEFAULT_READ_TIMEOUT_BROWSER = 2e3; var DEFAULT_WRITE_TIMEOUT_BROWSER = 3e4; var DEFAULT_CONNECT_TIMEOUT_NODE = 2e3; var DEFAULT_READ_TIMEOUT_NODE = 5e3; var DEFAULT_WRITE_TIMEOUT_NODE = 3e4; // src/createAlgoliaAgent.ts function createAlgoliaAgent(version) { const algoliaAgent = { value: `Algolia for JavaScript (${version})`, add(options) { const addedAlgoliaAgent = `; ${options.segment}${options.version !== void 0 ? ` (${options.version})` : ""}`; if (algoliaAgent.value.indexOf(addedAlgoliaAgent) === -1) { algoliaAgent.value = `${algoliaAgent.value}${addedAlgoliaAgent}`; } return algoliaAgent; } }; return algoliaAgent; } // src/createAuth.ts function createAuth(appId, apiKey, authMode = "WithinHeaders") { const credentials = { "x-algolia-api-key": apiKey, "x-algolia-application-id": appId }; return { headers() { return authMode === "WithinHeaders" ? credentials : {}; }, queryParameters() { return authMode === "WithinQueryParameters" ? credentials : {}; } }; } // src/createIterablePromise.ts function createIterablePromise({ func, validate, aggregator, error, timeout = () => 0 }) { const retry = (previousResponse) => { return new Promise((resolve, reject) => { func(previousResponse).then(async (response) => { if (aggregator) { await aggregator(response); } if (await validate(response)) { return resolve(response); } if (error && await error.validate(response)) { return reject(new Error(await error.message(response))); } return setTimeout( () => { retry(response).then(resolve).catch(reject); }, await timeout() ); }).catch((err) => { reject(err); }); }); }; return retry(); } // src/getAlgoliaAgent.ts function getAlgoliaAgent({ algoliaAgents, client, version }) { const defaultAlgoliaAgent = createAlgoliaAgent(version).add({ segment: client, version }); algoliaAgents.forEach((algoliaAgent) => defaultAlgoliaAgent.add(algoliaAgent)); return defaultAlgoliaAgent; } // src/logger/createNullLogger.ts function createNullLogger() { return { debug(_message, _args) { return Promise.resolve(); }, info(_message, _args) { return Promise.resolve(); }, error(_message, _args) { return Promise.resolve(); } }; } // src/transporter/createStatefulHost.ts var EXPIRATION_DELAY = 2 * 60 * 1e3; function createStatefulHost(host, status = "up") { const lastUpdate = Date.now(); function isUp() { return status === "up" || Date.now() - lastUpdate > EXPIRATION_DELAY; } function isTimedOut() { return status === "timed out" && Date.now() - lastUpdate <= EXPIRATION_DELAY; } return { ...host, status, lastUpdate, isUp, isTimedOut }; } // src/transporter/errors.ts var AlgoliaError = class extends Error { name = "AlgoliaError"; constructor(message, name) { super(message); if (name) { this.name = name; } } }; var IndexNotFoundError = class extends AlgoliaError { constructor(indexName) { super(`${indexName} does not exist`, "IndexNotFoundError"); } }; var IndicesInSameAppError = class extends AlgoliaError { constructor() { super("Indices are in the same application. Use operationIndex instead.", "IndicesInSameAppError"); } }; var IndexAlreadyExistsError = class extends AlgoliaError { constructor(indexName) { super(`${indexName} index already exists.`, "IndexAlreadyExistsError"); } }; var ErrorWithStackTrace = class extends AlgoliaError { stackTrace; constructor(message, stackTrace, name) { super(message, name); this.stackTrace = stackTrace; } }; var RetryError = class extends ErrorWithStackTrace { constructor(stackTrace) { super( "Unreachable hosts - your application id may be incorrect. If the error persists, please reach out to the Algolia Support team: https://alg.li/support.", stackTrace, "RetryError" ); } }; var ApiError = class extends ErrorWithStackTrace { status; constructor(message, status, stackTrace, name = "ApiError") { super(message, stackTrace, name); this.status = status; } }; var DeserializationError = class extends AlgoliaError { response; constructor(message, response) { super(message, "DeserializationError"); this.response = response; } }; var DetailedApiError = class extends ApiError { error; constructor(message, status, error, stackTrace) { super(message, status, stackTrace, "DetailedApiError"); this.error = error; } }; // src/transporter/helpers.ts function shuffle(array) { const shuffledArray = array; for (let c = array.length - 1; c > 0; c--) { const b = Math.floor(Math.random() * (c + 1)); const a = array[c]; shuffledArray[c] = array[b]; shuffledArray[b] = a; } return shuffledArray; } function serializeUrl(host, path, queryParameters) { const queryParametersAsString = serializeQueryParameters(queryParameters); let url = `${host.protocol}://${host.url}${host.port ? `:${host.port}` : ""}/${path.charAt(0) === "/" ? path.substring(1) : path}`; if (queryParametersAsString.length) { url += `?${queryParametersAsString}`; } return url; } function serializeQueryParameters(parameters) { return Object.keys(parameters).filter((key) => parameters[key] !== void 0).sort().map( (key) => `${key}=${encodeURIComponent( Object.prototype.toString.call(parameters[key]) === "[object Array]" ? parameters[key].join(",") : parameters[key] ).replace(/\+/g, "%20")}` ).join("&"); } function serializeData(request, requestOptions) { if (request.method === "GET" || request.data === void 0 && requestOptions.data === void 0) { return void 0; } const data = Array.isArray(request.data) ? request.data : { ...request.data, ...requestOptions.data }; return JSON.stringify(data); } function serializeHeaders(baseHeaders, requestHeaders, requestOptionsHeaders) { const headers = { Accept: "application/json", ...baseHeaders, ...requestHeaders, ...requestOptionsHeaders }; const serializedHeaders = {}; Object.keys(headers).forEach((header) => { const value = headers[header]; serializedHeaders[header.toLowerCase()] = value; }); return serializedHeaders; } function deserializeSuccess(response) { try { return JSON.parse(response.content); } catch (e) { throw new DeserializationError(e.message, response); } } function deserializeFailure({ content, status }, stackFrame) { try { const parsed = JSON.parse(content); if ("error" in parsed) { return new DetailedApiError(parsed.message, status, parsed.error, stackFrame); } return new ApiError(parsed.message, status, stackFrame); } catch { } return new ApiError(content, status, stackFrame); } // src/transporter/responses.ts function isNetworkError({ isTimedOut, status }) { return !isTimedOut && ~~status === 0; } function isRetryable({ isTimedOut, status }) { return isTimedOut || isNetworkError({ isTimedOut, status }) || ~~(status / 100) !== 2 && ~~(status / 100) !== 4; } function isSuccess({ status }) { return ~~(status / 100) === 2; } // src/transporter/stackTrace.ts function stackTraceWithoutCredentials(stackTrace) { return stackTrace.map((stackFrame) => stackFrameWithoutCredentials(stackFrame)); } function stackFrameWithoutCredentials(stackFrame) { const modifiedHeaders = stackFrame.request.headers["x-algolia-api-key"] ? { "x-algolia-api-key": "*****" } : {}; return { ...stackFrame, request: { ...stackFrame.request, headers: { ...stackFrame.request.headers, ...modifiedHeaders } } }; } // src/transporter/createTransporter.ts function createTransporter({ hosts, hostsCache, baseHeaders, logger, baseQueryParameters, algoliaAgent, timeouts, requester, requestsCache, responsesCache }) { async function createRetryableOptions(compatibleHosts) { const statefulHosts = await Promise.all( compatibleHosts.map((compatibleHost) => { return hostsCache.get(compatibleHost, () => { return Promise.resolve(createStatefulHost(compatibleHost)); }); }) ); const hostsUp = statefulHosts.filter((host) => host.isUp()); const hostsTimedOut = statefulHosts.filter((host) => host.isTimedOut()); const hostsAvailable = [...hostsUp, ...hostsTimedOut]; const compatibleHostsAvailable = hostsAvailable.length > 0 ? hostsAvailable : compatibleHosts; return { hosts: compatibleHostsAvailable, getTimeout(timeoutsCount, baseTimeout) { const timeoutMultiplier = hostsTimedOut.length === 0 && timeoutsCount === 0 ? 1 : hostsTimedOut.length + 3 + timeoutsCount; return timeoutMultiplier * baseTimeout; } }; } async function retryableRequest(request, requestOptions, isRead = true) { const stackTrace = []; const data = serializeData(request, requestOptions); const headers = serializeHeaders(baseHeaders, request.headers, requestOptions.headers); const dataQueryParameters = request.method === "GET" ? { ...request.data, ...requestOptions.data } : {}; const queryParameters = { ...baseQueryParameters, ...request.queryParameters, ...dataQueryParameters }; if (algoliaAgent.value) { queryParameters["x-algolia-agent"] = algoliaAgent.value; } if (requestOptions && requestOptions.queryParameters) { for (const key of Object.keys(requestOptions.queryParameters)) { if (!requestOptions.queryParameters[key] || Object.prototype.toString.call(requestOptions.queryParameters[key]) === "[object Object]") { queryParameters[key] = requestOptions.queryParameters[key]; } else { queryParameters[key] = requestOptions.queryParameters[key].toString(); } } } let timeoutsCount = 0; const retry = async (retryableHosts, getTimeout) => { const host = retryableHosts.pop(); if (host === void 0) { throw new RetryError(stackTraceWithoutCredentials(stackTrace)); } const timeout = { ...timeouts, ...requestOptions.timeouts }; const payload = { data, headers, method: request.method, url: serializeUrl(host, request.path, queryParameters), connectTimeout: getTimeout(timeoutsCount, timeout.connect), responseTimeout: getTimeout(timeoutsCount, isRead ? timeout.read : timeout.write) }; const pushToStackTrace = (response2) => { const stackFrame = { request: payload, response: response2, host, triesLeft: retryableHosts.length }; stackTrace.push(stackFrame); return stackFrame; }; const response = await requester.send(payload); if (isRetryable(response)) { const stackFrame = pushToStackTrace(response); if (response.isTimedOut) { timeoutsCount++; } logger.info("Retryable failure", stackFrameWithoutCredentials(stackFrame)); await hostsCache.set(host, createStatefulHost(host, response.isTimedOut ? "timed out" : "down")); return retry(retryableHosts, getTimeout); } if (isSuccess(response)) { return deserializeSuccess(response); } pushToStackTrace(response); throw deserializeFailure(response, stackTrace); }; const compatibleHosts = hosts.filter( (host) => host.accept === "readWrite" || (isRead ? host.accept === "read" : host.accept === "write") ); const options = await createRetryableOptions(compatibleHosts); return retry([...options.hosts].reverse(), options.getTimeout); } function createRequest(request, requestOptions = {}) { const isRead = request.useReadTransporter || request.method === "GET"; if (!isRead) { return retryableRequest(request, requestOptions, isRead); } const createRetryableRequest = () => { return retryableRequest(request, requestOptions); }; const cacheable = requestOptions.cacheable || request.cacheable; if (cacheable !== true) { return createRetryableRequest(); } const key = { request, requestOptions, transporter: { queryParameters: baseQueryParameters, headers: baseHeaders } }; return responsesCache.get( key, () => { return requestsCache.get( key, () => ( /** * Finally, if there is no request in progress with the same key, * this `createRetryableRequest()` will actually trigger the * retryable request. */ requestsCache.set(key, createRetryableRequest()).then( (response) => Promise.all([requestsCache.delete(key), response]), (err) => Promise.all([requestsCache.delete(key), Promise.reject(err)]) ).then(([_, response]) => response) ) ); }, { /** * Of course, once we get this response back from the server, we * tell response cache to actually store the received response * to be used later. */ miss: (response) => responsesCache.set(key, response) } ); } return { hostsCache, requester, timeouts, logger, algoliaAgent, baseHeaders, baseQueryParameters, hosts, request: createRequest, requestsCache, responsesCache }; } // src/types/logger.ts var LogLevelEnum = { Debug: 1, Info: 2, Error: 3 }; export { AlgoliaError, ApiError, DEFAULT_CONNECT_TIMEOUT_BROWSER, DEFAULT_CONNECT_TIMEOUT_NODE, DEFAULT_READ_TIMEOUT_BROWSER, DEFAULT_READ_TIMEOUT_NODE, DEFAULT_WRITE_TIMEOUT_BROWSER, DEFAULT_WRITE_TIMEOUT_NODE, DeserializationError, DetailedApiError, ErrorWithStackTrace, IndexAlreadyExistsError, IndexNotFoundError, IndicesInSameAppError, LogLevelEnum, RetryError, createAlgoliaAgent, createAuth, createBrowserLocalStorageCache, createFallbackableCache, createIterablePromise, createMemoryCache, createNullCache, createNullLogger, createStatefulHost, createTransporter, deserializeFailure, deserializeSuccess, getAlgoliaAgent, isNetworkError, isRetryable, isSuccess, serializeData, serializeHeaders, serializeQueryParameters, serializeUrl, shuffle, stackFrameWithoutCredentials, stackTraceWithoutCredentials }; //# sourceMappingURL=common.js.map