Warp/node_modules/algoliasearch/dist/algoliasearch-lite.esm.browser.js

941 lines
33 KiB
JavaScript
Raw Normal View History

2024-01-05 12:14:38 +00:00
function createBrowserLocalStorageCache(options) {
const namespaceKey = `algoliasearch-client-js-${options.key}`;
// eslint-disable-next-line functional/no-let
let storage;
const getStorage = () => {
if (storage === undefined) {
storage = options.localStorage || window.localStorage;
}
return storage;
};
const getNamespace = () => {
return JSON.parse(getStorage().getItem(namespaceKey) || '{}');
};
const setNamespace = (namespace) => {
getStorage().setItem(namespaceKey, JSON.stringify(namespace));
};
const removeOutdatedCacheItems = () => {
const timeToLive = options.timeToLive ? options.timeToLive * 1000 : null;
const namespace = getNamespace();
const filteredNamespaceWithoutOldFormattedCacheItems = Object.fromEntries(Object.entries(namespace).filter(([, cacheItem]) => {
return cacheItem.timestamp !== undefined;
}));
setNamespace(filteredNamespaceWithoutOldFormattedCacheItems);
if (!timeToLive)
return;
const filteredNamespaceWithoutExpiredItems = Object.fromEntries(Object.entries(filteredNamespaceWithoutOldFormattedCacheItems).filter(([, cacheItem]) => {
const currentTimestamp = 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();
const keyAsString = JSON.stringify(key);
return getNamespace()[keyAsString];
})
.then(value => {
return Promise.all([value ? value.value : defaultValue(), value !== undefined]);
})
.then(([value, exists]) => {
return Promise.all([value, exists || events.miss(value)]);
})
.then(([value]) => value);
},
set(key, value) {
return Promise.resolve().then(() => {
const namespace = getNamespace();
// eslint-disable-next-line functional/immutable-data
namespace[JSON.stringify(key)] = {
timestamp: new Date().getTime(),
value,
};
getStorage().setItem(namespaceKey, JSON.stringify(namespace));
return value;
});
},
delete(key) {
return Promise.resolve().then(() => {
const namespace = getNamespace();
// eslint-disable-next-line functional/immutable-data
delete namespace[JSON.stringify(key)];
getStorage().setItem(namespaceKey, JSON.stringify(namespace));
});
},
clear() {
return Promise.resolve().then(() => {
getStorage().removeItem(namespaceKey);
});
},
};
}
// @todo Add logger on options to debug when caches go wrong.
function createFallbackableCache(options) {
const caches = [...options.caches];
const current = caches.shift(); // eslint-disable-line functional/immutable-data
if (current === undefined) {
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();
});
},
};
}
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();
},
};
}
function createInMemoryCache(options = { serializable: true }) {
// eslint-disable-next-line functional/no-let
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();
const miss = (events && events.miss) || (() => Promise.resolve());
return promise.then((value) => miss(value)).then(() => promise);
},
set(key, value) {
// eslint-disable-next-line functional/immutable-data
cache[JSON.stringify(key)] = options.serializable ? JSON.stringify(value) : value;
return Promise.resolve(value);
},
delete(key) {
// eslint-disable-next-line functional/immutable-data
delete cache[JSON.stringify(key)];
return Promise.resolve();
},
clear() {
cache = {};
return Promise.resolve();
},
};
}
function createAuth(authMode, appId, apiKey) {
const credentials = {
'x-algolia-api-key': apiKey,
'x-algolia-application-id': appId,
};
return {
headers() {
return authMode === AuthMode.WithinHeaders ? credentials : {};
},
queryParameters() {
return authMode === AuthMode.WithinQueryParameters ? credentials : {};
},
};
}
// eslint-disable-next-line functional/prefer-readonly-type
function shuffle(array) {
let c = array.length - 1; // eslint-disable-line functional/no-let
// eslint-disable-next-line functional/no-loop-statement
for (c; c > 0; c--) {
const b = Math.floor(Math.random() * (c + 1));
const a = array[c];
array[c] = array[b]; // eslint-disable-line functional/immutable-data, no-param-reassign
array[b] = a; // eslint-disable-line functional/immutable-data, no-param-reassign
}
return array;
}
function addMethods(base, methods) {
if (!methods) {
return base;
}
Object.keys(methods).forEach(key => {
// eslint-disable-next-line functional/immutable-data, no-param-reassign
base[key] = methods[key](base);
});
return base;
}
function encode(format, ...args) {
// eslint-disable-next-line functional/no-let
let i = 0;
return format.replace(/%s/g, () => encodeURIComponent(args[i++]));
}
const version = '4.22.0';
const AuthMode = {
/**
* If auth credentials should be in query parameters.
*/
WithinQueryParameters: 0,
/**
* If auth credentials should be in headers.
*/
WithinHeaders: 1,
};
function createMappedRequestOptions(requestOptions, timeout) {
const options = requestOptions || {};
const data = options.data || {};
Object.keys(options).forEach(key => {
if (['timeout', 'headers', 'queryParameters', 'data', 'cacheable'].indexOf(key) === -1) {
data[key] = options[key]; // eslint-disable-line functional/immutable-data
}
});
return {
data: Object.entries(data).length > 0 ? data : undefined,
timeout: options.timeout || timeout,
headers: options.headers || {},
queryParameters: options.queryParameters || {},
cacheable: options.cacheable,
};
}
const CallEnum = {
/**
* If the host is read only.
*/
Read: 1,
/**
* If the host is write only.
*/
Write: 2,
/**
* If the host is both read and write.
*/
Any: 3,
};
const HostStatusEnum = {
Up: 1,
Down: 2,
Timeouted: 3,
};
// By default, API Clients at Algolia have expiration delay
// of 5 mins. In the JavaScript client, we have 2 mins.
const EXPIRATION_DELAY = 2 * 60 * 1000;
function createStatefulHost(host, status = HostStatusEnum.Up) {
return {
...host,
status,
lastUpdate: Date.now(),
};
}
function isStatefulHostUp(host) {
return host.status === HostStatusEnum.Up || Date.now() - host.lastUpdate > EXPIRATION_DELAY;
}
function isStatefulHostTimeouted(host) {
return (host.status === HostStatusEnum.Timeouted && Date.now() - host.lastUpdate <= EXPIRATION_DELAY);
}
function createStatelessHost(options) {
if (typeof options === 'string') {
return {
protocol: 'https',
url: options,
accept: CallEnum.Any,
};
}
return {
protocol: options.protocol || 'https',
url: options.url,
accept: options.accept || CallEnum.Any,
};
}
const MethodEnum = {
Delete: 'DELETE',
Get: 'GET',
Post: 'POST',
Put: 'PUT',
};
function createRetryableOptions(hostsCache, statelessHosts) {
return Promise.all(statelessHosts.map(statelessHost => {
return hostsCache.get(statelessHost, () => {
return Promise.resolve(createStatefulHost(statelessHost));
});
})).then(statefulHosts => {
const hostsUp = statefulHosts.filter(host => isStatefulHostUp(host));
const hostsTimeouted = statefulHosts.filter(host => isStatefulHostTimeouted(host));
/**
* Note, we put the hosts that previously timeouted on the end of the list.
*/
const hostsAvailable = [...hostsUp, ...hostsTimeouted];
const statelessHostsAvailable = hostsAvailable.length > 0
? hostsAvailable.map(host => createStatelessHost(host))
: statelessHosts;
return {
getTimeout(timeoutsCount, baseTimeout) {
/**
* Imagine that you have 4 hosts, if timeouts will increase
* on the following way: 1 (timeouted) > 4 (timeouted) > 5 (200)
*
* Note that, the very next request, we start from the previous timeout
*
* 5 (timeouted) > 6 (timeouted) > 7 ...
*
* This strategy may need to be reviewed, but is the strategy on the our
* current v3 version.
*/
const timeoutMultiplier = hostsTimeouted.length === 0 && timeoutsCount === 0
? 1
: hostsTimeouted.length + 3 + timeoutsCount;
return timeoutMultiplier * baseTimeout;
},
statelessHosts: statelessHostsAvailable,
};
});
}
const isNetworkError = ({ isTimedOut, status }) => {
return !isTimedOut && ~~status === 0;
};
const isRetryable = (response) => {
const status = response.status;
const isTimedOut = response.isTimedOut;
return (isTimedOut || isNetworkError(response) || (~~(status / 100) !== 2 && ~~(status / 100) !== 4));
};
const isSuccess = ({ status }) => {
return ~~(status / 100) === 2;
};
const retryDecision = (response, outcomes) => {
if (isRetryable(response)) {
return outcomes.onRetry(response);
}
if (isSuccess(response)) {
return outcomes.onSuccess(response);
}
return outcomes.onFail(response);
};
function retryableRequest(transporter, statelessHosts, request, requestOptions) {
const stackTrace = []; // eslint-disable-line functional/prefer-readonly-type
/**
* First we prepare the payload that do not depend from hosts.
*/
const data = serializeData(request, requestOptions);
const headers = serializeHeaders(transporter, requestOptions);
const method = request.method;
// On `GET`, the data is proxied to query parameters.
const dataQueryParameters = request.method !== MethodEnum.Get
? {}
: {
...request.data,
...requestOptions.data,
};
const queryParameters = {
'x-algolia-agent': transporter.userAgent.value,
...transporter.queryParameters,
...dataQueryParameters,
...requestOptions.queryParameters,
};
let timeoutsCount = 0; // eslint-disable-line functional/no-let
const retry = (hosts, // eslint-disable-line functional/prefer-readonly-type
getTimeout) => {
/**
* We iterate on each host, until there is no host left.
*/
const host = hosts.pop(); // eslint-disable-line functional/immutable-data
if (host === undefined) {
throw createRetryError(stackTraceWithoutCredentials(stackTrace));
}
const payload = {
data,
headers,
method,
url: serializeUrl(host, request.path, queryParameters),
connectTimeout: getTimeout(timeoutsCount, transporter.timeouts.connect),
responseTimeout: getTimeout(timeoutsCount, requestOptions.timeout),
};
/**
* The stackFrame is pushed to the stackTrace so we
* can have information about onRetry and onFailure
* decisions.
*/
const pushToStackTrace = (response) => {
const stackFrame = {
request: payload,
response,
host,
triesLeft: hosts.length,
};
// eslint-disable-next-line functional/immutable-data
stackTrace.push(stackFrame);
return stackFrame;
};
const decisions = {
onSuccess: response => deserializeSuccess(response),
onRetry(response) {
const stackFrame = pushToStackTrace(response);
/**
* If response is a timeout, we increaset the number of
* timeouts so we can increase the timeout later.
*/
if (response.isTimedOut) {
timeoutsCount++;
}
return Promise.all([
/**
* Failures are individually send the logger, allowing
* the end user to debug / store stack frames even
* when a retry error does not happen.
*/
transporter.logger.info('Retryable failure', stackFrameWithoutCredentials(stackFrame)),
/**
* We also store the state of the host in failure cases. If the host, is
* down it will remain down for the next 2 minutes. In a timeout situation,
* this host will be added end of the list of hosts on the next request.
*/
transporter.hostsCache.set(host, createStatefulHost(host, response.isTimedOut ? HostStatusEnum.Timeouted : HostStatusEnum.Down)),
]).then(() => retry(hosts, getTimeout));
},
onFail(response) {
pushToStackTrace(response);
throw deserializeFailure(response, stackTraceWithoutCredentials(stackTrace));
},
};
return transporter.requester.send(payload).then(response => {
return retryDecision(response, decisions);
});
};
/**
* Finally, for each retryable host perform request until we got a non
* retryable response. Some notes here:
*
* 1. The reverse here is applied so we can apply a `pop` later on => more performant.
* 2. We also get from the retryable options a timeout multiplier that is tailored
* for the current context.
*/
return createRetryableOptions(transporter.hostsCache, statelessHosts).then(options => {
return retry([...options.statelessHosts].reverse(), options.getTimeout);
});
}
function createTransporter(options) {
const { hostsCache, logger, requester, requestsCache, responsesCache, timeouts, userAgent, hosts, queryParameters, headers, } = options;
const transporter = {
hostsCache,
logger,
requester,
requestsCache,
responsesCache,
timeouts,
userAgent,
headers,
queryParameters,
hosts: hosts.map(host => createStatelessHost(host)),
read(request, requestOptions) {
/**
* First, we compute the user request options. Now, keep in mind,
* that using request options the user is able to modified the intire
* payload of the request. Such as headers, query parameters, and others.
*/
const mappedRequestOptions = createMappedRequestOptions(requestOptions, transporter.timeouts.read);
const createRetryableRequest = () => {
/**
* Then, we prepare a function factory that contains the construction of
* the retryable request. At this point, we may *not* perform the actual
* request. But we want to have the function factory ready.
*/
return retryableRequest(transporter, transporter.hosts.filter(host => (host.accept & CallEnum.Read) !== 0), request, mappedRequestOptions);
};
/**
* Once we have the function factory ready, we need to determine of the
* request is "cacheable" - should be cached. Note that, once again,
* the user can force this option.
*/
const cacheable = mappedRequestOptions.cacheable !== undefined
? mappedRequestOptions.cacheable
: request.cacheable;
/**
* If is not "cacheable", we immediatly trigger the retryable request, no
* need to check cache implementations.
*/
if (cacheable !== true) {
return createRetryableRequest();
}
/**
* If the request is "cacheable", we need to first compute the key to ask
* the cache implementations if this request is on progress or if the
* response already exists on the cache.
*/
const key = {
request,
mappedRequestOptions,
transporter: {
queryParameters: transporter.queryParameters,
headers: transporter.headers,
},
};
/**
* With the computed key, we first ask the responses cache
* implemention if this request was been resolved before.
*/
return transporter.responsesCache.get(key, () => {
/**
* If the request has never resolved before, we actually ask if there
* is a current request with the same key on progress.
*/
return transporter.requestsCache.get(key, () => {
return (transporter.requestsCache
/**
* Finally, if there is no request in progress with the same key,
* this `createRetryableRequest()` will actually trigger the
* retryable request.
*/
.set(key, createRetryableRequest())
.then(response => Promise.all([transporter.requestsCache.delete(key), response]), err => Promise.all([transporter.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 => transporter.responsesCache.set(key, response),
});
},
write(request, requestOptions) {
/**
* On write requests, no cache mechanisms are applied, and we
* proxy the request immediately to the requester.
*/
return retryableRequest(transporter, transporter.hosts.filter(host => (host.accept & CallEnum.Write) !== 0), request, createMappedRequestOptions(requestOptions, transporter.timeouts.write));
},
};
return transporter;
}
function createUserAgent(version) {
const userAgent = {
value: `Algolia for JavaScript (${version})`,
add(options) {
const addedUserAgent = `; ${options.segment}${options.version !== undefined ? ` (${options.version})` : ''}`;
if (userAgent.value.indexOf(addedUserAgent) === -1) {
// eslint-disable-next-line functional/immutable-data
userAgent.value = `${userAgent.value}${addedUserAgent}`;
}
return userAgent;
},
};
return userAgent;
}
function deserializeSuccess(response) {
// eslint-disable-next-line functional/no-try-statement
try {
return JSON.parse(response.content);
}
catch (e) {
throw createDeserializationError(e.message, response);
}
}
function deserializeFailure({ content, status }, stackFrame) {
// eslint-disable-next-line functional/no-let
let message = content;
// eslint-disable-next-line functional/no-try-statement
try {
message = JSON.parse(content).message;
}
catch (e) {
// ..
}
return createApiError(message, status, stackFrame);
}
function serializeUrl(host, path, queryParameters) {
const queryParametersAsString = serializeQueryParameters(queryParameters);
// eslint-disable-next-line functional/no-let
let url = `${host.protocol}://${host.url}/${path.charAt(0) === '/' ? path.substr(1) : path}`;
if (queryParametersAsString.length) {
url += `?${queryParametersAsString}`;
}
return url;
}
function serializeQueryParameters(parameters) {
const isObjectOrArray = (value) => Object.prototype.toString.call(value) === '[object Object]' ||
Object.prototype.toString.call(value) === '[object Array]';
return Object.keys(parameters)
.map(key => encode('%s=%s', key, isObjectOrArray(parameters[key]) ? JSON.stringify(parameters[key]) : parameters[key]))
.join('&');
}
function serializeData(request, requestOptions) {
if (request.method === MethodEnum.Get ||
(request.data === undefined && requestOptions.data === undefined)) {
return undefined;
}
const data = Array.isArray(request.data)
? request.data
: { ...request.data, ...requestOptions.data };
return JSON.stringify(data);
}
function serializeHeaders(transporter, requestOptions) {
const headers = {
...transporter.headers,
...requestOptions.headers,
};
const serializedHeaders = {};
Object.keys(headers).forEach(header => {
const value = headers[header];
// @ts-ignore
// eslint-disable-next-line functional/immutable-data
serializedHeaders[header.toLowerCase()] = value;
});
return serializedHeaders;
}
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,
},
},
};
}
function createApiError(message, status, transporterStackTrace) {
return {
name: 'ApiError',
message,
status,
transporterStackTrace,
};
}
function createDeserializationError(message, response) {
return {
name: 'DeserializationError',
message,
response,
};
}
function createRetryError(transporterStackTrace) {
return {
name: 'RetryError',
message: 'Unreachable hosts - your application id may be incorrect. If the error persists, contact support@algolia.com.',
transporterStackTrace,
};
}
const createSearchClient = options => {
const appId = options.appId;
const auth = createAuth(options.authMode !== undefined ? options.authMode : AuthMode.WithinHeaders, appId, options.apiKey);
const transporter = createTransporter({
hosts: [
{ url: `${appId}-dsn.algolia.net`, accept: CallEnum.Read },
{ url: `${appId}.algolia.net`, accept: CallEnum.Write },
].concat(shuffle([
{ url: `${appId}-1.algolianet.com` },
{ url: `${appId}-2.algolianet.com` },
{ url: `${appId}-3.algolianet.com` },
])),
...options,
headers: {
...auth.headers(),
...{ 'content-type': 'application/x-www-form-urlencoded' },
...options.headers,
},
queryParameters: {
...auth.queryParameters(),
...options.queryParameters,
},
});
const base = {
transporter,
appId,
addAlgoliaAgent(segment, version) {
transporter.userAgent.add({ segment, version });
},
clearCache() {
return Promise.all([
transporter.requestsCache.clear(),
transporter.responsesCache.clear(),
]).then(() => undefined);
},
};
return addMethods(base, options.methods);
};
const customRequest = (base) => {
return (request, requestOptions) => {
if (request.method === MethodEnum.Get) {
return base.transporter.read(request, requestOptions);
}
return base.transporter.write(request, requestOptions);
};
};
const initIndex = (base) => {
return (indexName, options = {}) => {
const searchIndex = {
transporter: base.transporter,
appId: base.appId,
indexName,
};
return addMethods(searchIndex, options.methods);
};
};
const multipleQueries = (base) => {
return (queries, requestOptions) => {
const requests = queries.map(query => {
return {
...query,
params: serializeQueryParameters(query.params || {}),
};
});
return base.transporter.read({
method: MethodEnum.Post,
path: '1/indexes/*/queries',
data: {
requests,
},
cacheable: true,
}, requestOptions);
};
};
const multipleSearchForFacetValues = (base) => {
return (queries, requestOptions) => {
return Promise.all(queries.map(query => {
const { facetName, facetQuery, ...params } = query.params;
return initIndex(base)(query.indexName, {
methods: { searchForFacetValues },
}).searchForFacetValues(facetName, facetQuery, {
...requestOptions,
...params,
});
}));
};
};
const findAnswers = (base) => {
return (query, queryLanguages, requestOptions) => {
return base.transporter.read({
method: MethodEnum.Post,
path: encode('1/answers/%s/prediction', base.indexName),
data: {
query,
queryLanguages,
},
cacheable: true,
}, requestOptions);
};
};
const search = (base) => {
return (query, requestOptions) => {
return base.transporter.read({
method: MethodEnum.Post,
path: encode('1/indexes/%s/query', base.indexName),
data: {
query,
},
cacheable: true,
}, requestOptions);
};
};
const searchForFacetValues = (base) => {
return (facetName, facetQuery, requestOptions) => {
return base.transporter.read({
method: MethodEnum.Post,
path: encode('1/indexes/%s/facets/%s/query', base.indexName, facetName),
data: {
facetQuery,
},
cacheable: true,
}, requestOptions);
};
};
const LogLevelEnum = {
Debug: 1,
Info: 2,
Error: 3,
};
/* eslint no-console: 0 */
function createConsoleLogger(logLevel) {
return {
debug(message, args) {
if (LogLevelEnum.Debug >= logLevel) {
console.debug(message, args);
}
return Promise.resolve();
},
info(message, args) {
if (LogLevelEnum.Info >= logLevel) {
console.info(message, args);
}
return Promise.resolve();
},
error(message, args) {
console.error(message, args);
return Promise.resolve();
},
};
}
function createBrowserXhrRequester() {
return {
send(request) {
return new Promise((resolve) => {
const baseRequester = new XMLHttpRequest();
baseRequester.open(request.method, request.url, true);
Object.keys(request.headers).forEach(key => baseRequester.setRequestHeader(key, request.headers[key]));
const createTimeout = (timeout, content) => {
return setTimeout(() => {
baseRequester.abort();
resolve({
status: 0,
content,
isTimedOut: true,
});
}, timeout * 1000);
};
const connectTimeout = createTimeout(request.connectTimeout, 'Connection timeout');
// eslint-disable-next-line functional/no-let
let responseTimeout;
// eslint-disable-next-line functional/immutable-data
baseRequester.onreadystatechange = () => {
if (baseRequester.readyState > baseRequester.OPENED && responseTimeout === undefined) {
clearTimeout(connectTimeout);
responseTimeout = createTimeout(request.responseTimeout, 'Socket timeout');
}
};
// eslint-disable-next-line functional/immutable-data
baseRequester.onerror = () => {
// istanbul ignore next
if (baseRequester.status === 0) {
clearTimeout(connectTimeout);
clearTimeout(responseTimeout);
resolve({
content: baseRequester.responseText || 'Network request failed',
status: baseRequester.status,
isTimedOut: false,
});
}
};
// eslint-disable-next-line functional/immutable-data
baseRequester.onload = () => {
clearTimeout(connectTimeout);
clearTimeout(responseTimeout);
resolve({
content: baseRequester.responseText,
status: baseRequester.status,
isTimedOut: false,
});
};
baseRequester.send(request.data);
});
},
};
}
function algoliasearch(appId, apiKey, options) {
const commonOptions = {
appId,
apiKey,
timeouts: {
connect: 1,
read: 2,
write: 30,
},
requester: createBrowserXhrRequester(),
logger: createConsoleLogger(LogLevelEnum.Error),
responsesCache: createInMemoryCache(),
requestsCache: createInMemoryCache({ serializable: false }),
hostsCache: createFallbackableCache({
caches: [
createBrowserLocalStorageCache({ key: `${version}-${appId}` }),
createInMemoryCache(),
],
}),
userAgent: createUserAgent(version).add({
segment: 'Browser',
version: 'lite',
}),
authMode: AuthMode.WithinQueryParameters,
};
return createSearchClient({
...commonOptions,
...options,
methods: {
search: multipleQueries,
searchForFacetValues: multipleSearchForFacetValues,
multipleQueries,
multipleSearchForFacetValues,
customRequest,
initIndex: base => (indexName) => {
return initIndex(base)(indexName, {
methods: { search, searchForFacetValues, findAnswers },
});
},
},
});
}
// eslint-disable-next-line functional/immutable-data
algoliasearch.version = version;
export default algoliasearch;