Socket
Socket
Sign inDemoInstall

@algolia/recommend

Package Overview
Dependencies
Maintainers
3
Versions
180
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@algolia/recommend - npm Package Compare versions

Comparing version 4.14.1 to 5.0.0-alpha.1

dist/builds/browser.d.ts

352

dist/recommend.cjs.js
'use strict';
var cacheCommon = require('@algolia/cache-common');
var cacheInMemory = require('@algolia/cache-in-memory');
Object.defineProperty(exports, '__esModule', { value: true });
var clientCommon = require('@algolia/client-common');
var loggerCommon = require('@algolia/logger-common');
var requesterNodeHttp = require('@algolia/requester-node-http');
var transporter = require('@algolia/transporter');
var requesterCommon = require('@algolia/requester-common');
const createRecommendClient = options => {
const appId = options.appId;
const auth = clientCommon.createAuth(options.authMode !== undefined ? options.authMode : clientCommon.AuthMode.WithinHeaders, appId, options.apiKey);
const transporter$1 = transporter.createTransporter({
hosts: [
{ url: `${appId}-dsn.algolia.net`, accept: transporter.CallEnum.Read },
{ url: `${appId}.algolia.net`, accept: transporter.CallEnum.Write },
].concat(clientCommon.shuffle([
{ url: `${appId}-1.algolianet.com` },
{ url: `${appId}-2.algolianet.com` },
{ url: `${appId}-3.algolianet.com` },
])),
// This file is generated, manual changes will be lost - read more on https://github.com/algolia/api-clients-automation.
const apiClientVersion = '5.0.0-alpha.1';
function getDefaultHosts(appId) {
return [
{
url: `${appId}-dsn.algolia.net`,
accept: 'read',
protocol: 'https',
},
{
url: `${appId}.algolia.net`,
accept: 'write',
protocol: 'https',
},
].concat(clientCommon.shuffle([
{
url: `${appId}-1.algolianet.com`,
accept: 'readWrite',
protocol: 'https',
},
{
url: `${appId}-2.algolianet.com`,
accept: 'readWrite',
protocol: 'https',
},
{
url: `${appId}-3.algolianet.com`,
accept: 'readWrite',
protocol: 'https',
},
]));
}
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
function createRecommendClient({ appId: appIdOption, apiKey: apiKeyOption, authMode, algoliaAgents, ...options }) {
const auth = clientCommon.createAuth(appIdOption, apiKeyOption, authMode);
const transporter = clientCommon.createTransporter({
hosts: getDefaultHosts(appIdOption),
...options,
headers: {
algoliaAgent: clientCommon.getAlgoliaAgent({
algoliaAgents,
client: 'Recommend',
version: apiClientVersion,
}),
baseHeaders: {
'content-type': 'text/plain',
...auth.headers(),
...{ 'content-type': 'application/x-www-form-urlencoded' },
...options.headers,
...options.baseHeaders,
},
queryParameters: {
baseQueryParameters: {
...auth.queryParameters(),
...options.queryParameters,
...options.baseQueryParameters,
},
});
const base = {
transporter: transporter$1,
appId,
return {
transporter,
/**
* Get the value of the `algoliaAgent`, used by our libraries internally and telemetry system.
*/
get _ua() {
return transporter.algoliaAgent.value;
},
/**
* Adds a `segment` to the `x-algolia-agent` sent with every requests.
*
* @param segment - The algolia agent (user-agent) segment to add.
* @param version - The version of the agent.
*/
addAlgoliaAgent(segment, version) {
transporter$1.userAgent.add({ segment, version });
transporter.algoliaAgent.add({ segment, version });
},
clearCache() {
return Promise.all([
transporter$1.requestsCache.clear(),
transporter$1.responsesCache.clear(),
]).then(() => undefined);
/**
* This method allow you to send requests to the Algolia REST API.
*
* @summary Send requests to the Algolia REST API.
* @param del - The del object.
* @param del.path - The path of the API endpoint to target, anything after the /1 needs to be specified.
* @param del.parameters - Query parameters to be applied to the current query.
* @param requestOptions - The requestOptions to send along with the query, they will be merged with the transporter requestOptions.
*/
del({ path, parameters }, requestOptions) {
if (!path) {
throw new Error('Parameter `path` is required when calling `del`.');
}
const requestPath = '/1{path}'.replace('{path}', path);
const headers = {};
const queryParameters = parameters ? parameters : {};
const request = {
method: 'DELETE',
path: requestPath,
queryParameters,
headers,
};
return transporter.request(request, requestOptions);
},
/**
* This method allow you to send requests to the Algolia REST API.
*
* @summary Send requests to the Algolia REST API.
* @param get - The get object.
* @param get.path - The path of the API endpoint to target, anything after the /1 needs to be specified.
* @param get.parameters - Query parameters to be applied to the current query.
* @param requestOptions - The requestOptions to send along with the query, they will be merged with the transporter requestOptions.
*/
get({ path, parameters }, requestOptions) {
if (!path) {
throw new Error('Parameter `path` is required when calling `get`.');
}
const requestPath = '/1{path}'.replace('{path}', path);
const headers = {};
const queryParameters = parameters ? parameters : {};
const request = {
method: 'GET',
path: requestPath,
queryParameters,
headers,
};
return transporter.request(request, requestOptions);
},
/**
* Returns recommendations or trending results, for a specific model and `objectID`.
*
* @summary Get results.
* @param getRecommendationsParams - The getRecommendationsParams object.
* @param requestOptions - The requestOptions to send along with the query, they will be merged with the transporter requestOptions.
*/
getRecommendations(getRecommendationsParams, requestOptions) {
if (!getRecommendationsParams) {
throw new Error('Parameter `getRecommendationsParams` is required when calling `getRecommendations`.');
}
if (!getRecommendationsParams.requests) {
throw new Error('Parameter `getRecommendationsParams.requests` is required when calling `getRecommendations`.');
}
const requestPath = '/1/indexes/*/recommendations';
const headers = {};
const queryParameters = {};
const request = {
method: 'POST',
path: requestPath,
queryParameters,
headers,
data: getRecommendationsParams,
useReadTransporter: true,
cacheable: true,
};
return transporter.request(request, requestOptions);
},
/**
* This method allow you to send requests to the Algolia REST API.
*
* @summary Send requests to the Algolia REST API.
* @param post - The post object.
* @param post.path - The path of the API endpoint to target, anything after the /1 needs to be specified.
* @param post.parameters - Query parameters to be applied to the current query.
* @param post.body - The parameters to send with the custom request.
* @param requestOptions - The requestOptions to send along with the query, they will be merged with the transporter requestOptions.
*/
post({ path, parameters, body }, requestOptions) {
if (!path) {
throw new Error('Parameter `path` is required when calling `post`.');
}
const requestPath = '/1{path}'.replace('{path}', path);
const headers = {};
const queryParameters = parameters ? parameters : {};
const request = {
method: 'POST',
path: requestPath,
queryParameters,
headers,
data: body ? body : {},
};
return transporter.request(request, requestOptions);
},
/**
* This method allow you to send requests to the Algolia REST API.
*
* @summary Send requests to the Algolia REST API.
* @param put - The put object.
* @param put.path - The path of the API endpoint to target, anything after the /1 needs to be specified.
* @param put.parameters - Query parameters to be applied to the current query.
* @param put.body - The parameters to send with the custom request.
* @param requestOptions - The requestOptions to send along with the query, they will be merged with the transporter requestOptions.
*/
put({ path, parameters, body }, requestOptions) {
if (!path) {
throw new Error('Parameter `path` is required when calling `put`.');
}
const requestPath = '/1{path}'.replace('{path}', path);
const headers = {};
const queryParameters = parameters ? parameters : {};
const request = {
method: 'PUT',
path: requestPath,
queryParameters,
headers,
data: body ? body : {},
};
return transporter.request(request, requestOptions);
},
};
return clientCommon.addMethods(base, options.methods);
};
}
const getRecommendations = base => {
return (queries, requestOptions) => {
const requests = queries.map(query => ({
...query,
// The `threshold` param is required by the endpoint to make it easier
// to provide a default value later, so we default it in the client
// so that users don't have to provide a value.
threshold: query.threshold || 0,
}));
return base.transporter.read({
method: requesterCommon.MethodEnum.Post,
path: '1/indexes/*/recommendations',
data: {
requests,
},
cacheable: true,
}, requestOptions);
};
};
const getFrequentlyBoughtTogether = base => {
return (queries, requestOptions) => {
return getRecommendations(base)(queries.map(query => ({
...query,
fallbackParameters: {},
model: 'bought-together',
})), requestOptions);
};
};
const getRelatedProducts = base => {
return (queries, requestOptions) => {
return getRecommendations(base)(queries.map(query => ({
...query,
model: 'related-products',
})), requestOptions);
};
};
const getTrendingFacets = base => {
return (queries, requestOptions) => {
const requests = queries.map(query => ({
...query,
model: 'trending-facets',
// The `threshold` param is required by the endpoint to make it easier
// to provide a default value later, so we default it in the client
// so that users don't have to provide a value.
threshold: query.threshold || 0,
}));
return base.transporter.read({
method: requesterCommon.MethodEnum.Post,
path: '1/indexes/*/recommendations',
data: {
requests,
},
cacheable: true,
}, requestOptions);
};
};
const getTrendingItems = base => {
return (queries, requestOptions) => {
const requests = queries.map(query => ({
...query,
model: 'trending-items',
// The `threshold` param is required by the endpoint to make it easier
// to provide a default value later, so we default it in the client
// so that users don't have to provide a value.
threshold: query.threshold || 0,
}));
return base.transporter.read({
method: requesterCommon.MethodEnum.Post,
path: '1/indexes/*/recommendations',
data: {
requests,
},
cacheable: true,
}, requestOptions);
};
};
function recommend(appId, apiKey, options) {
const commonOptions = {
// This file is generated, manual changes will be lost - read more on https://github.com/algolia/api-clients-automation.
function recommendClient(appId, apiKey, options) {
if (!appId || typeof appId !== 'string') {
throw new Error('`appId` is missing.');
}
if (!apiKey || typeof apiKey !== 'string') {
throw new Error('`apiKey` is missing.');
}
return createRecommendClient({
appId,
apiKey,
timeouts: {
connect: 2,
read: 5,
write: 30,
connect: clientCommon.DEFAULT_CONNECT_TIMEOUT_NODE,
read: clientCommon.DEFAULT_READ_TIMEOUT_NODE,
write: clientCommon.DEFAULT_WRITE_TIMEOUT_NODE,
},
requester: requesterNodeHttp.createNodeHttpRequester(),
logger: loggerCommon.createNullLogger(),
responsesCache: cacheCommon.createNullCache(),
requestsCache: cacheCommon.createNullCache(),
hostsCache: cacheInMemory.createInMemoryCache(),
userAgent: transporter.createUserAgent(clientCommon.version)
.add({ segment: 'Recommend', version: clientCommon.version })
.add({ segment: 'Node.js', version: process.versions.node }),
};
return createRecommendClient({
...commonOptions,
requester: requesterNodeHttp.createHttpRequester(),
algoliaAgents: [{ segment: 'Node.js', version: process.versions.node }],
responsesCache: clientCommon.createNullCache(),
requestsCache: clientCommon.createNullCache(),
hostsCache: clientCommon.createMemoryCache(),
...options,
methods: {
destroy: clientCommon.destroy,
getFrequentlyBoughtTogether,
getRecommendations,
getRelatedProducts,
getTrendingFacets,
getTrendingItems,
},
});
}
// eslint-disable-next-line functional/immutable-data
recommend.version = clientCommon.version;
module.exports = recommend;
exports.apiClientVersion = apiClientVersion;
exports.recommendClient = recommendClient;

@@ -0,506 +1,527 @@

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 : {};
}
};
}
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) || '{}');
};
return {
get(key, defaultValue, events = {
miss: () => Promise.resolve(),
}) {
return Promise.resolve()
.then(() => {
const keyAsString = JSON.stringify(key);
const value = getNamespace()[keyAsString];
return Promise.all([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)] = 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);
});
},
};
let storage; // We've changed the namespace to avoid conflicts with v4, as this version is a huge breaking change
const namespaceKey = `algolia-client-js-${options.key}`;
function getStorage() {
if (storage === undefined) {
storage = options.localStorage || window.localStorage;
}
return storage;
}
function getNamespace() {
return JSON.parse(getStorage().getItem(namespaceKey) || '{}');
}
return {
get(key, defaultValue, events = {
miss: () => Promise.resolve()
}) {
return Promise.resolve().then(() => {
const keyAsString = JSON.stringify(key);
const value = getNamespace()[keyAsString];
return Promise.all([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();
namespace[JSON.stringify(key)] = 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);
});
}
};
}
// @todo Add logger on options to debug when caches go wrong.
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 createFallbackableCache(options) {
const caches = [...options.caches];
const current = caches.shift(); // eslint-disable-line functional/immutable-data
if (current === undefined) {
return createNullCache();
const caches = [...options.caches];
const current = caches.shift();
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();
});
}
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 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();
}
};
}
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();
},
};
// 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 = '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
};
}
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 : {};
},
};
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
// 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
class AlgoliaError extends Error {
constructor(message, name) {
super(message);
_defineProperty(this, "name", 'AlgoliaError');
if (name) {
this.name = name;
}
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;
class ErrorWithStackTrace extends AlgoliaError {
constructor(message, stackTrace, name) {
super(message, name); // the array and object should be frozen to reflect the stackTrace at the time of the error
_defineProperty(this, "stackTrace", void 0);
this.stackTrace = stackTrace;
}
}
function encode(format, ...args) {
// eslint-disable-next-line functional/no-let
let i = 0;
return format.replace(/%s/g, () => encodeURIComponent(args[i++]));
class RetryError extends ErrorWithStackTrace {
constructor(stackTrace) {
super('Unreachable hosts - your application id may be incorrect. If the error persists, contact support@algolia.com.', stackTrace, 'RetryError');
}
}
class ApiError extends ErrorWithStackTrace {
constructor(message, status, stackTrace) {
super(message, stackTrace, 'ApiError');
const version = '4.14.1';
_defineProperty(this, "status", void 0);
const AuthMode = {
/**
* If auth credentials should be in query parameters.
*/
WithinQueryParameters: 0,
/**
* If auth credentials should be in headers.
*/
WithinHeaders: 1,
};
this.status = status;
}
const LogLevelEnum = {
Debug: 1,
Info: 2,
Error: 3,
};
}
class DeserializationError extends AlgoliaError {
constructor(message, response) {
super(message, 'DeserializationError');
/* 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();
},
};
_defineProperty(this, "response", void 0);
this.response = response;
}
}
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 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}/${path.charAt(0) === '/' ? path.substr(1) : path}`;
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,
};
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]';
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,
};
return Object.keys(parameters).map(key => `${key}=${isObjectOrArray(parameters[key]) ? JSON.stringify(parameters[key]) : parameters[key]}`).join('&');
}
function serializeData(request, requestOptions) {
if (request.method === 'GET' || request.data === undefined && requestOptions.data === undefined) {
return undefined;
}
const HostStatusEnum = {
Up: 1,
Down: 2,
Timeouted: 3,
};
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) {
let message = content;
// 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(),
};
try {
message = JSON.parse(content).message;
} catch (e) {// ..
}
return new ApiError(message, status, stackFrame);
}
function isStatefulHostUp(host) {
return host.status === HostStatusEnum.Up || Date.now() - host.lastUpdate > EXPIRATION_DELAY;
function isNetworkError({
isTimedOut,
status
}) {
return !isTimedOut && ~~status === 0;
}
function isStatefulHostTimeouted(host) {
return (host.status === HostStatusEnum.Timeouted && Date.now() - host.lastUpdate <= EXPIRATION_DELAY);
function isRetryable({
isTimedOut,
status
}) {
return isTimedOut || isNetworkError({
isTimedOut,
status
}) || ~~(status / 100) !== 2 && ~~(status / 100) !== 4;
}
function isSuccess({
status
}) {
return ~~(status / 100) === 2;
}
function createStatelessHost(options) {
if (typeof options === 'string') {
return {
protocol: 'https',
url: options,
accept: CallEnum.Any,
};
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
}
}
return {
protocol: options.protocol || 'https',
url: options.url,
accept: options.accept || CallEnum.Any,
};
};
}
const MethodEnum = {
Delete: 'DELETE',
Get: 'GET',
Post: 'POST',
Put: 'PUT',
};
function createTransporter({
hosts,
hostsCache,
baseHeaders,
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()); // Note, we put the hosts that previously timed out on the end of the list.
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));
const hostsAvailable = [...hostsUp, ...hostsTimedOut];
const compatibleHostsAvailable = hostsAvailable.length > 0 ? hostsAvailable : compatibleHosts;
return {
hosts: compatibleHostsAvailable,
getTimeout(timeoutsCount, baseTimeout) {
/**
* Note, we put the hosts that previously timeouted on the end of the list.
* Imagine that you have 4 hosts, if timeouts will increase
* on the following way: 1 (timed out) > 4 (timed out) > 5 (200).
*
* Note that, the very next request, we start from the previous timeout.
*
* 5 (timed out) > 6 (timed out) > 7 ...
*
* This strategy may need to be reviewed, but is the strategy on the our
* current v3 version.
*/
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 timeoutMultiplier = hostsTimedOut.length === 0 && timeoutsCount === 0 ? 1 : hostsTimedOut.length + 3 + timeoutsCount;
return timeoutMultiplier * baseTimeout;
}
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
async function retryableRequest(request, requestOptions, isRead = true) {
const stackTrace = [];
/**
* 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 headers = serializeHeaders(baseHeaders, request.headers, requestOptions.headers); // On `GET`, the data is proxied to query parameters.
const dataQueryParameters = request.method === 'GET' ? { ...request.data,
...requestOptions.data
} : {};
const queryParameters = {
'x-algolia-agent': transporter.userAgent.value,
...transporter.queryParameters,
...dataQueryParameters,
...requestOptions.queryParameters,
'x-algolia-agent': algoliaAgent.value,
...baseQueryParameters,
...request.queryParameters,
...dataQueryParameters
};
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));
if (requestOptions && requestOptions.queryParameters) {
for (const key of Object.keys(requestOptions.queryParameters)) {
// We want to keep `undefined` and `null` values,
// but also avoid stringifying `object`s, as they are
// handled in the `serializeUrl` step right after.
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();
}
const payload = {
data,
headers,
method,
url: serializeUrl(host, request.path, queryParameters),
connectTimeout: getTimeout(timeoutsCount, transporter.timeouts.connect),
responseTimeout: getTimeout(timeoutsCount, requestOptions.timeout),
}
}
let timeoutsCount = 0;
const retry = async (retryableHosts, getTimeout) => {
/**
* We iterate on each host, until there is no host left.
*/
const host = retryableHosts.pop();
if (host === undefined) {
throw new RetryError(stackTraceWithoutCredentials(stackTrace));
}
let responseTimeout = requestOptions.timeout;
if (responseTimeout === undefined) {
responseTimeout = isRead ? timeouts.read : timeouts.write;
}
const payload = {
data,
headers,
method: request.method,
url: serializeUrl(host, request.path, queryParameters),
connectTimeout: getTimeout(timeoutsCount, timeouts.connect),
responseTimeout: getTimeout(timeoutsCount, responseTimeout)
};
/**
* 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: retryableHosts.length
};
stackTrace.push(stackFrame);
return stackFrame;
};
const response = await requester.send(payload);
if (isRetryable(response)) {
const stackFrame = pushToStackTrace(response); // If response is a timeout, we increase the number of timeouts so we can increase the timeout later.
if (response.isTimedOut) {
timeoutsCount++;
}
/**
* The stackFrame is pushed to the stackTrace so we
* can have information about onRetry and onFailure
* decisions.
* Failures are individually sent to the logger, allowing
* the end user to debug / store stack frames even
* when a retry error does not happen.
*/
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);
});
// eslint-disable-next-line no-console -- this will be fixed by exposing a `logger` to the transporter
console.log('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.
*/
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);
};

@@ -515,384 +536,423 @@ /**

*/
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;
},
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 = {}) {
/**
* A read request is either a `GET` request, or a request that we make
* via the `read` transporter (e.g. `search`).
*/
const isRead = request.useReadTransporter || request.method === 'GET';
if (!isRead) {
/**
* On write requests, no cache mechanisms are applied, and we
* proxy the request immediately to the requester.
*/
return retryableRequest(request, requestOptions, isRead);
}
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(request, requestOptions);
};
return userAgent;
}
/**
* 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.
*/
function deserializeSuccess(response) {
// eslint-disable-next-line functional/no-try-statement
try {
return JSON.parse(response.content);
const cacheable = requestOptions.cacheable || request.cacheable;
/**
* If is not "cacheable", we immediately trigger the retryable request, no
* need to check cache implementations.
*/
if (cacheable !== true) {
return createRetryableRequest();
}
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);
}
/**
* 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.
*/
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 key = {
request,
requestOptions,
transporter: {
queryParameters: baseQueryParameters,
headers: baseHeaders
}
};
const serializedHeaders = {};
Object.keys(headers).forEach(header => {
const value = headers[header];
// @ts-ignore
// eslint-disable-next-line functional/immutable-data
serializedHeaders[header.toLowerCase()] = value;
/**
* With the computed key, we first ask the responses cache
* implementation if this request was been resolved before.
*/
return 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 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 serializedHeaders;
}
return {
hostsCache,
requester,
timeouts,
algoliaAgent,
baseHeaders,
baseQueryParameters,
hosts,
request: createRequest,
requestsCache,
responsesCache
};
}
function stackTraceWithoutCredentials(stackTrace) {
return stackTrace.map(stackFrame => stackFrameWithoutCredentials(stackFrame));
function createAlgoliaAgent(version) {
const algoliaAgent = {
value: `Algolia for JavaScript (${version})`,
add(options) {
const addedAlgoliaAgent = `; ${options.segment}${options.version !== undefined ? ` (${options.version})` : ''}`;
if (algoliaAgent.value.indexOf(addedAlgoliaAgent) === -1) {
algoliaAgent.value = `${algoliaAgent.value}${addedAlgoliaAgent}`;
}
return algoliaAgent;
}
};
return algoliaAgent;
}
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 getAlgoliaAgent({
algoliaAgents,
client,
version
}) {
const defaultAlgoliaAgent = createAlgoliaAgent(version).add({
segment: client,
version
});
algoliaAgents.forEach(algoliaAgent => defaultAlgoliaAgent.add(algoliaAgent));
return defaultAlgoliaAgent;
}
function createDeserializationError(message, response) {
return {
name: 'DeserializationError',
message,
response,
};
const DEFAULT_CONNECT_TIMEOUT_BROWSER = 1000;
const DEFAULT_READ_TIMEOUT_BROWSER = 2000;
const DEFAULT_WRITE_TIMEOUT_BROWSER = 30000;
function createXhrRequester() {
function 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);
};
const connectTimeout = createTimeout(request.connectTimeout, 'Connection timeout');
let responseTimeout;
baseRequester.onreadystatechange = () => {
if (baseRequester.readyState > baseRequester.OPENED &&
responseTimeout === undefined) {
clearTimeout(connectTimeout);
responseTimeout = createTimeout(request.responseTimeout, 'Socket timeout');
}
};
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,
});
}
};
baseRequester.onload = () => {
clearTimeout(connectTimeout);
clearTimeout(responseTimeout);
resolve({
content: baseRequester.responseText,
status: baseRequester.status,
isTimedOut: false,
});
};
baseRequester.send(request.data);
});
}
return { send };
}
function createRetryError(transporterStackTrace) {
return {
name: 'RetryError',
message: 'Unreachable hosts - your application id may be incorrect. If the error persists, contact support@algolia.com.',
transporterStackTrace,
};
// This file is generated, manual changes will be lost - read more on https://github.com/algolia/api-clients-automation.
const apiClientVersion = '5.0.0-alpha.1';
function getDefaultHosts(appId) {
return [
{
url: `${appId}-dsn.algolia.net`,
accept: 'read',
protocol: 'https',
},
{
url: `${appId}.algolia.net`,
accept: 'write',
protocol: 'https',
},
].concat(shuffle([
{
url: `${appId}-1.algolianet.com`,
accept: 'readWrite',
protocol: 'https',
},
{
url: `${appId}-2.algolianet.com`,
accept: 'readWrite',
protocol: 'https',
},
{
url: `${appId}-3.algolianet.com`,
accept: 'readWrite',
protocol: 'https',
},
]));
}
const createRecommendClient = options => {
const appId = options.appId;
const auth = createAuth(options.authMode !== undefined ? options.authMode : AuthMode.WithinHeaders, appId, options.apiKey);
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
function createRecommendClient({ appId: appIdOption, apiKey: apiKeyOption, authMode, algoliaAgents, ...options }) {
const auth = createAuth(appIdOption, apiKeyOption, authMode);
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` },
])),
hosts: getDefaultHosts(appIdOption),
...options,
headers: {
algoliaAgent: getAlgoliaAgent({
algoliaAgents,
client: 'Recommend',
version: apiClientVersion,
}),
baseHeaders: {
'content-type': 'text/plain',
...auth.headers(),
...{ 'content-type': 'application/x-www-form-urlencoded' },
...options.headers,
...options.baseHeaders,
},
queryParameters: {
baseQueryParameters: {
...auth.queryParameters(),
...options.queryParameters,
...options.baseQueryParameters,
},
});
const base = {
return {
transporter,
appId,
/**
* Get the value of the `algoliaAgent`, used by our libraries internally and telemetry system.
*/
get _ua() {
return transporter.algoliaAgent.value;
},
/**
* Adds a `segment` to the `x-algolia-agent` sent with every requests.
*
* @param segment - The algolia agent (user-agent) segment to add.
* @param version - The version of the agent.
*/
addAlgoliaAgent(segment, version) {
transporter.userAgent.add({ segment, version });
transporter.algoliaAgent.add({ segment, version });
},
clearCache() {
return Promise.all([
transporter.requestsCache.clear(),
transporter.responsesCache.clear(),
]).then(() => undefined);
/**
* This method allow you to send requests to the Algolia REST API.
*
* @summary Send requests to the Algolia REST API.
* @param del - The del object.
* @param del.path - The path of the API endpoint to target, anything after the /1 needs to be specified.
* @param del.parameters - Query parameters to be applied to the current query.
* @param requestOptions - The requestOptions to send along with the query, they will be merged with the transporter requestOptions.
*/
del({ path, parameters }, requestOptions) {
if (!path) {
throw new Error('Parameter `path` is required when calling `del`.');
}
const requestPath = '/1{path}'.replace('{path}', path);
const headers = {};
const queryParameters = parameters ? parameters : {};
const request = {
method: 'DELETE',
path: requestPath,
queryParameters,
headers,
};
return transporter.request(request, requestOptions);
},
/**
* This method allow you to send requests to the Algolia REST API.
*
* @summary Send requests to the Algolia REST API.
* @param get - The get object.
* @param get.path - The path of the API endpoint to target, anything after the /1 needs to be specified.
* @param get.parameters - Query parameters to be applied to the current query.
* @param requestOptions - The requestOptions to send along with the query, they will be merged with the transporter requestOptions.
*/
get({ path, parameters }, requestOptions) {
if (!path) {
throw new Error('Parameter `path` is required when calling `get`.');
}
const requestPath = '/1{path}'.replace('{path}', path);
const headers = {};
const queryParameters = parameters ? parameters : {};
const request = {
method: 'GET',
path: requestPath,
queryParameters,
headers,
};
return transporter.request(request, requestOptions);
},
/**
* Returns recommendations or trending results, for a specific model and `objectID`.
*
* @summary Get results.
* @param getRecommendationsParams - The getRecommendationsParams object.
* @param requestOptions - The requestOptions to send along with the query, they will be merged with the transporter requestOptions.
*/
getRecommendations(getRecommendationsParams, requestOptions) {
if (!getRecommendationsParams) {
throw new Error('Parameter `getRecommendationsParams` is required when calling `getRecommendations`.');
}
if (!getRecommendationsParams.requests) {
throw new Error('Parameter `getRecommendationsParams.requests` is required when calling `getRecommendations`.');
}
const requestPath = '/1/indexes/*/recommendations';
const headers = {};
const queryParameters = {};
const request = {
method: 'POST',
path: requestPath,
queryParameters,
headers,
data: getRecommendationsParams,
useReadTransporter: true,
cacheable: true,
};
return transporter.request(request, requestOptions);
},
/**
* This method allow you to send requests to the Algolia REST API.
*
* @summary Send requests to the Algolia REST API.
* @param post - The post object.
* @param post.path - The path of the API endpoint to target, anything after the /1 needs to be specified.
* @param post.parameters - Query parameters to be applied to the current query.
* @param post.body - The parameters to send with the custom request.
* @param requestOptions - The requestOptions to send along with the query, they will be merged with the transporter requestOptions.
*/
post({ path, parameters, body }, requestOptions) {
if (!path) {
throw new Error('Parameter `path` is required when calling `post`.');
}
const requestPath = '/1{path}'.replace('{path}', path);
const headers = {};
const queryParameters = parameters ? parameters : {};
const request = {
method: 'POST',
path: requestPath,
queryParameters,
headers,
data: body ? body : {},
};
return transporter.request(request, requestOptions);
},
/**
* This method allow you to send requests to the Algolia REST API.
*
* @summary Send requests to the Algolia REST API.
* @param put - The put object.
* @param put.path - The path of the API endpoint to target, anything after the /1 needs to be specified.
* @param put.parameters - Query parameters to be applied to the current query.
* @param put.body - The parameters to send with the custom request.
* @param requestOptions - The requestOptions to send along with the query, they will be merged with the transporter requestOptions.
*/
put({ path, parameters, body }, requestOptions) {
if (!path) {
throw new Error('Parameter `path` is required when calling `put`.');
}
const requestPath = '/1{path}'.replace('{path}', path);
const headers = {};
const queryParameters = parameters ? parameters : {};
const request = {
method: 'PUT',
path: requestPath,
queryParameters,
headers,
data: body ? body : {},
};
return transporter.request(request, requestOptions);
},
};
return addMethods(base, options.methods);
};
}
const getRecommendations = base => {
return (queries, requestOptions) => {
const requests = queries.map(query => ({
...query,
// The `threshold` param is required by the endpoint to make it easier
// to provide a default value later, so we default it in the client
// so that users don't have to provide a value.
threshold: query.threshold || 0,
}));
return base.transporter.read({
method: MethodEnum.Post,
path: '1/indexes/*/recommendations',
data: {
requests,
},
cacheable: true,
}, requestOptions);
};
};
const getFrequentlyBoughtTogether = base => {
return (queries, requestOptions) => {
return getRecommendations(base)(queries.map(query => ({
...query,
fallbackParameters: {},
model: 'bought-together',
})), requestOptions);
};
};
const getRelatedProducts = base => {
return (queries, requestOptions) => {
return getRecommendations(base)(queries.map(query => ({
...query,
model: 'related-products',
})), requestOptions);
};
};
const getTrendingFacets = base => {
return (queries, requestOptions) => {
const requests = queries.map(query => ({
...query,
model: 'trending-facets',
// The `threshold` param is required by the endpoint to make it easier
// to provide a default value later, so we default it in the client
// so that users don't have to provide a value.
threshold: query.threshold || 0,
}));
return base.transporter.read({
method: MethodEnum.Post,
path: '1/indexes/*/recommendations',
data: {
requests,
},
cacheable: true,
}, requestOptions);
};
};
const getTrendingItems = base => {
return (queries, requestOptions) => {
const requests = queries.map(query => ({
...query,
model: 'trending-items',
// The `threshold` param is required by the endpoint to make it easier
// to provide a default value later, so we default it in the client
// so that users don't have to provide a value.
threshold: query.threshold || 0,
}));
return base.transporter.read({
method: MethodEnum.Post,
path: '1/indexes/*/recommendations',
data: {
requests,
},
cacheable: true,
}, requestOptions);
};
};
function recommend(appId, apiKey, options) {
const commonOptions = {
// This file is generated, manual changes will be lost - read more on https://github.com/algolia/api-clients-automation.
function recommendClient(appId, apiKey, options) {
if (!appId || typeof appId !== 'string') {
throw new Error('`appId` is missing.');
}
if (!apiKey || typeof apiKey !== 'string') {
throw new Error('`apiKey` is missing.');
}
return createRecommendClient({
appId,
apiKey,
timeouts: {
connect: 1,
read: 2,
write: 30,
connect: DEFAULT_CONNECT_TIMEOUT_BROWSER,
read: DEFAULT_READ_TIMEOUT_BROWSER,
write: DEFAULT_WRITE_TIMEOUT_BROWSER,
},
requester: createBrowserXhrRequester(),
logger: createConsoleLogger(LogLevelEnum.Error),
responsesCache: createInMemoryCache(),
requestsCache: createInMemoryCache({ serializable: false }),
requester: createXhrRequester(),
algoliaAgents: [{ segment: 'Browser' }],
authMode: 'WithinQueryParameters',
responsesCache: createMemoryCache(),
requestsCache: createMemoryCache({ serializable: false }),
hostsCache: createFallbackableCache({
caches: [
createBrowserLocalStorageCache({ key: `${version}-${appId}` }),
createInMemoryCache(),
createBrowserLocalStorageCache({ key: `${apiClientVersion}-${appId}` }),
createMemoryCache(),
],
}),
userAgent: createUserAgent(version)
.add({ segment: 'Recommend', version })
.add({ segment: 'Browser' }),
authMode: AuthMode.WithinQueryParameters,
};
return createRecommendClient({
...commonOptions,
...options,
methods: {
getFrequentlyBoughtTogether,
getRecommendations,
getRelatedProducts,
getTrendingFacets,
getTrendingItems,
},
});
}
// eslint-disable-next-line functional/immutable-data
recommend.version = version;
export default recommend;
export { apiClientVersion, recommendClient };

@@ -1,2 +0,2 @@

/*! recommend.umd.js | 4.14.1 | © Algolia, inc. | https://github.com/algolia/algoliasearch-client-javascript */
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self)["@algolia/recommend"]=t()}(this,(function(){"use strict";function e(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function t(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function r(r){for(var n=1;n<arguments.length;n++){var o=null!=arguments[n]?arguments[n]:{};n%2?t(Object(o),!0).forEach((function(t){e(r,t,o[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(r,Object.getOwnPropertyDescriptors(o)):t(Object(o)).forEach((function(e){Object.defineProperty(r,e,Object.getOwnPropertyDescriptor(o,e))}))}return r}function n(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){if(!(Symbol.iterator in Object(e)||"[object Arguments]"===Object.prototype.toString.call(e)))return;var r=[],n=!0,o=!1,a=void 0;try{for(var u,i=e[Symbol.iterator]();!(n=(u=i.next()).done)&&(r.push(u.value),!t||r.length!==t);n=!0);}catch(e){o=!0,a=e}finally{try{n||null==i.return||i.return()}finally{if(o)throw a}}return r}(e,t)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance")}()}function o(e){return function(e){if(Array.isArray(e)){for(var t=0,r=new Array(e.length);t<e.length;t++)r[t]=e[t];return r}}(e)||function(e){if(Symbol.iterator in Object(e)||"[object Arguments]"===Object.prototype.toString.call(e))return Array.from(e)}(e)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance")}()}function a(e){var t,r="algoliasearch-client-js-".concat(e.key),o=function(){return void 0===t&&(t=e.localStorage||window.localStorage),t},a=function(){return JSON.parse(o().getItem(r)||"{}")};return{get:function(e,t){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{miss:function(){return Promise.resolve()}};return Promise.resolve().then((function(){var r=JSON.stringify(e),n=a()[r];return Promise.all([n||t(),void 0!==n])})).then((function(e){var t=n(e,2),o=t[0],a=t[1];return Promise.all([o,a||r.miss(o)])})).then((function(e){return n(e,1)[0]}))},set:function(e,t){return Promise.resolve().then((function(){var n=a();return n[JSON.stringify(e)]=t,o().setItem(r,JSON.stringify(n)),t}))},delete:function(e){return Promise.resolve().then((function(){var t=a();delete t[JSON.stringify(e)],o().setItem(r,JSON.stringify(t))}))},clear:function(){return Promise.resolve().then((function(){o().removeItem(r)}))}}}function u(e){var t=o(e.caches),r=t.shift();return void 0===r?{get:function(e,t){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{miss:function(){return Promise.resolve()}},o=t();return o.then((function(e){return Promise.all([e,r.miss(e)])})).then((function(e){return n(e,1)[0]}))},set:function(e,t){return Promise.resolve(t)},delete:function(e){return Promise.resolve()},clear:function(){return Promise.resolve()}}:{get:function(e,n){var o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{miss:function(){return Promise.resolve()}};return r.get(e,n,o).catch((function(){return u({caches:t}).get(e,n,o)}))},set:function(e,n){return r.set(e,n).catch((function(){return u({caches:t}).set(e,n)}))},delete:function(e){return r.delete(e).catch((function(){return u({caches:t}).delete(e)}))},clear:function(){return r.clear().catch((function(){return u({caches:t}).clear()}))}}}function i(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{serializable:!0},t={};return{get:function(r,n){var o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{miss:function(){return Promise.resolve()}},a=JSON.stringify(r);if(a in t)return Promise.resolve(e.serializable?JSON.parse(t[a]):t[a]);var u=n(),i=o&&o.miss||function(){return Promise.resolve()};return u.then((function(e){return i(e)})).then((function(){return u}))},set:function(r,n){return t[JSON.stringify(r)]=e.serializable?JSON.stringify(n):n,Promise.resolve(n)},delete:function(e){return delete t[JSON.stringify(e)],Promise.resolve()},clear:function(){return t={},Promise.resolve()}}}function s(e){for(var t=e.length-1;t>0;t--){var r=Math.floor(Math.random()*(t+1)),n=e[t];e[t]=e[r],e[r]=n}return e}var c={WithinQueryParameters:0,WithinHeaders:1},l=1,f=2,d=3;function h(e,t){var r=e||{},n=r.data||{};return Object.keys(r).forEach((function(e){-1===["timeout","headers","queryParameters","data","cacheable"].indexOf(e)&&(n[e]=r[e])})),{data:Object.entries(n).length>0?n:void 0,timeout:r.timeout||t,headers:r.headers||{},queryParameters:r.queryParameters||{},cacheable:r.cacheable}}var m={Read:1,Write:2,Any:3},p=1,g=2,v=3;function y(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:p;return r(r({},e),{},{status:t,lastUpdate:Date.now()})}function b(e){return"string"==typeof e?{protocol:"https",url:e,accept:m.Any}:{protocol:e.protocol||"https",url:e.url,accept:e.accept||m.Any}}var P="GET",O="POST";function q(e,t){return Promise.all(t.map((function(t){return e.get(t,(function(){return Promise.resolve(y(t))}))}))).then((function(e){var r=e.filter((function(e){return function(e){return e.status===p||Date.now()-e.lastUpdate>12e4}(e)})),n=e.filter((function(e){return function(e){return e.status===v&&Date.now()-e.lastUpdate<=12e4}(e)})),a=[].concat(o(r),o(n));return{getTimeout:function(e,t){return(0===n.length&&0===e?1:n.length+3+e)*t},statelessHosts:a.length>0?a.map((function(e){return b(e)})):t}}))}function S(e,t,n,a){var u=[],i=function(e,t){if(e.method===P||void 0===e.data&&void 0===t.data)return;var n=Array.isArray(e.data)?e.data:r(r({},e.data),t.data);return JSON.stringify(n)}(n,a),s=function(e,t){var n=r(r({},e.headers),t.headers),o={};return Object.keys(n).forEach((function(e){var t=n[e];o[e.toLowerCase()]=t})),o}(e,a),c=n.method,l=n.method!==P?{}:r(r({},n.data),a.data),f=r(r(r({"x-algolia-agent":e.userAgent.value},e.queryParameters),l),a.queryParameters),d=0,h=function t(r,o){var l=r.pop();if(void 0===l)throw{name:"RetryError",message:"Unreachable hosts - your application id may be incorrect. If the error persists, contact support@algolia.com.",transporterStackTrace:T(u)};var h={data:i,headers:s,method:c,url:w(l,n.path,f),connectTimeout:o(d,e.timeouts.connect),responseTimeout:o(d,a.timeout)},m=function(e){var t={request:h,response:e,host:l,triesLeft:r.length};return u.push(t),t},p={onSuccess:function(e){return function(e){try{return JSON.parse(e.content)}catch(t){throw function(e,t){return{name:"DeserializationError",message:e,response:t}}(t.message,e)}}(e)},onRetry:function(n){var a=m(n);return n.isTimedOut&&d++,Promise.all([e.logger.info("Retryable failure",A(a)),e.hostsCache.set(l,y(l,n.isTimedOut?v:g))]).then((function(){return t(r,o)}))},onFail:function(e){throw m(e),function(e,t){var r=e.content,n=e.status,o=r;try{o=JSON.parse(r).message}catch(e){}return function(e,t,r){return{name:"ApiError",message:e,status:t,transporterStackTrace:r}}(o,n,t)}(e,T(u))}};return e.requester.send(h).then((function(e){return function(e,t){return function(e){var t=e.status;return e.isTimedOut||function(e){var t=e.isTimedOut,r=e.status;return!t&&0==~~r}(e)||2!=~~(t/100)&&4!=~~(t/100)}(e)?t.onRetry(e):2==~~(e.status/100)?t.onSuccess(e):t.onFail(e)}(e,p)}))};return q(e.hostsCache,t).then((function(e){return h(o(e.statelessHosts).reverse(),e.getTimeout)}))}function j(e){var t={value:"Algolia for JavaScript (".concat(e,")"),add:function(e){var r="; ".concat(e.segment).concat(void 0!==e.version?" (".concat(e.version,")"):"");return-1===t.value.indexOf(r)&&(t.value="".concat(t.value).concat(r)),t}};return t}function w(e,t,r){var n,o=(n=r,Object.keys(n).map((function(e){return function(e){for(var t=arguments.length,r=new Array(t>1?t-1:0),n=1;n<t;n++)r[n-1]=arguments[n];var o=0;return e.replace(/%s/g,(function(){return encodeURIComponent(r[o++])}))}("%s=%s",e,(t=n[e],"[object Object]"===Object.prototype.toString.call(t)||"[object Array]"===Object.prototype.toString.call(t)?JSON.stringify(n[e]):n[e]));var t})).join("&")),a="".concat(e.protocol,"://").concat(e.url,"/").concat("/"===t.charAt(0)?t.substr(1):t);return o.length&&(a+="?".concat(o)),a}function T(e){return e.map((function(e){return A(e)}))}function A(e){var t=e.request.headers["x-algolia-api-key"]?{"x-algolia-api-key":"*****"}:{};return r(r({},e),{},{request:r(r({},e.request),{},{headers:r(r({},e.request.headers),t)})})}var C=function(e){var t=e.appId,o=function(e,t,r){var n={"x-algolia-api-key":r,"x-algolia-application-id":t};return{headers:function(){return e===c.WithinHeaders?n:{}},queryParameters:function(){return e===c.WithinQueryParameters?n:{}}}}(void 0!==e.authMode?e.authMode:c.WithinHeaders,t,e.apiKey),a=function(e){var t=e.hostsCache,r=e.logger,o=e.requester,a=e.requestsCache,u=e.responsesCache,i=e.timeouts,s=e.userAgent,c=e.hosts,l=e.queryParameters,f={hostsCache:t,logger:r,requester:o,requestsCache:a,responsesCache:u,timeouts:i,userAgent:s,headers:e.headers,queryParameters:l,hosts:c.map((function(e){return b(e)})),read:function(e,t){var r=h(t,f.timeouts.read),o=function(){return S(f,f.hosts.filter((function(e){return 0!=(e.accept&m.Read)})),e,r)};if(!0!==(void 0!==r.cacheable?r.cacheable:e.cacheable))return o();var a={request:e,mappedRequestOptions:r,transporter:{queryParameters:f.queryParameters,headers:f.headers}};return f.responsesCache.get(a,(function(){return f.requestsCache.get(a,(function(){return f.requestsCache.set(a,o()).then((function(e){return Promise.all([f.requestsCache.delete(a),e])}),(function(e){return Promise.all([f.requestsCache.delete(a),Promise.reject(e)])})).then((function(e){var t=n(e,2);t[0];return t[1]}))}))}),{miss:function(e){return f.responsesCache.set(a,e)}})},write:function(e,t){return S(f,f.hosts.filter((function(e){return 0!=(e.accept&m.Write)})),e,h(t,f.timeouts.write))}};return f}(r(r({hosts:[{url:"".concat(t,"-dsn.algolia.net"),accept:m.Read},{url:"".concat(t,".algolia.net"),accept:m.Write}].concat(s([{url:"".concat(t,"-1.algolianet.com")},{url:"".concat(t,"-2.algolianet.com")},{url:"".concat(t,"-3.algolianet.com")}]))},e),{},{headers:r(r(r({},o.headers()),{"content-type":"application/x-www-form-urlencoded"}),e.headers),queryParameters:r(r({},o.queryParameters()),e.queryParameters)}));return function(e,t){return t?(Object.keys(t).forEach((function(r){e[r]=t[r](e)})),e):e}({transporter:a,appId:t,addAlgoliaAgent:function(e,t){a.userAgent.add({segment:e,version:t})},clearCache:function(){return Promise.all([a.requestsCache.clear(),a.responsesCache.clear()]).then((function(){}))}},e.methods)},N=function(e){return function(t,n){var o=t.map((function(e){return r(r({},e),{},{threshold:e.threshold||0})}));return e.transporter.read({method:O,path:"1/indexes/*/recommendations",data:{requests:o},cacheable:!0},n)}},k=function(e){return function(t,n){return N(e)(t.map((function(e){return r(r({},e),{},{fallbackParameters:{},model:"bought-together"})})),n)}},x=function(e){return function(t,n){return N(e)(t.map((function(e){return r(r({},e),{},{model:"related-products"})})),n)}},J=function(e){return function(t,n){var o=t.map((function(e){return r(r({},e),{},{model:"trending-facets",threshold:e.threshold||0})}));return e.transporter.read({method:O,path:"1/indexes/*/recommendations",data:{requests:o},cacheable:!0},n)}},E=function(e){return function(t,n){var o=t.map((function(e){return r(r({},e),{},{model:"trending-items",threshold:e.threshold||0})}));return e.transporter.read({method:O,path:"1/indexes/*/recommendations",data:{requests:o},cacheable:!0},n)}};function R(e,t,n){var o,s={appId:e,apiKey:t,timeouts:{connect:1,read:2,write:30},requester:{send:function(e){return new Promise((function(t){var r=new XMLHttpRequest;r.open(e.method,e.url,!0),Object.keys(e.headers).forEach((function(t){return r.setRequestHeader(t,e.headers[t])}));var n,o=function(e,n){return setTimeout((function(){r.abort(),t({status:0,content:n,isTimedOut:!0})}),1e3*e)},a=o(e.connectTimeout,"Connection timeout");r.onreadystatechange=function(){r.readyState>r.OPENED&&void 0===n&&(clearTimeout(a),n=o(e.responseTimeout,"Socket timeout"))},r.onerror=function(){0===r.status&&(clearTimeout(a),clearTimeout(n),t({content:r.responseText||"Network request failed",status:r.status,isTimedOut:!1}))},r.onload=function(){clearTimeout(a),clearTimeout(n),t({content:r.responseText,status:r.status,isTimedOut:!1})},r.send(e.data)}))}},logger:(o=d,{debug:function(e,t){return l>=o&&console.debug(e,t),Promise.resolve()},info:function(e,t){return f>=o&&console.info(e,t),Promise.resolve()},error:function(e,t){return console.error(e,t),Promise.resolve()}}),responsesCache:i(),requestsCache:i({serializable:!1}),hostsCache:u({caches:[a({key:"".concat("4.14.1","-").concat(e)}),i()]}),userAgent:j("4.14.1").add({segment:"Recommend",version:"4.14.1"}).add({segment:"Browser"}),authMode:c.WithinQueryParameters};return C(r(r(r({},s),n),{},{methods:{getFrequentlyBoughtTogether:k,getRecommendations:N,getRelatedProducts:x,getTrendingFacets:J,getTrendingItems:E}}))}return R.version="4.14.1",R}));
/*! recommend.umd.js | 5.0.0-alpha.1 | © Algolia, inc. | https://github.com/algolia/algoliasearch-client-javascript */
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self)["@algolia/recommend"]={})}(this,(function(e){"use strict";function t(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function r(e){let t;const r=`algolia-client-js-${e.key}`;function a(){return void 0===t&&(t=e.localStorage||window.localStorage),t}function s(){return JSON.parse(a().getItem(r)||"{}")}return{get:(e,t,r={miss:()=>Promise.resolve()})=>Promise.resolve().then((()=>{const r=JSON.stringify(e),a=s()[r];return Promise.all([a||t(),void 0!==a])})).then((([e,t])=>Promise.all([e,t||r.miss(e)]))).then((([e])=>e)),set:(e,t)=>Promise.resolve().then((()=>{const n=s();return n[JSON.stringify(e)]=t,a().setItem(r,JSON.stringify(n)),t})),delete:e=>Promise.resolve().then((()=>{const t=s();delete t[JSON.stringify(e)],a().setItem(r,JSON.stringify(t))})),clear:()=>Promise.resolve().then((()=>{a().removeItem(r)}))}}function a(e){const t=[...e.caches],r=t.shift();return void 0===r?{get:(e,t,r={miss:()=>Promise.resolve()})=>t().then((e=>Promise.all([e,r.miss(e)]))).then((([e])=>e)),set:(e,t)=>Promise.resolve(t),delete:e=>Promise.resolve(),clear:()=>Promise.resolve()}:{get:(e,s,n={miss:()=>Promise.resolve()})=>r.get(e,s,n).catch((()=>a({caches:t}).get(e,s,n))),set:(e,s)=>r.set(e,s).catch((()=>a({caches:t}).set(e,s))),delete:e=>r.delete(e).catch((()=>a({caches:t}).delete(e))),clear:()=>r.clear().catch((()=>a({caches:t}).clear()))}}function s(e={serializable:!0}){let t={};return{get(r,a,s={miss:()=>Promise.resolve()}){const n=JSON.stringify(r);if(n in t)return Promise.resolve(e.serializable?JSON.parse(t[n]):t[n]);const o=a();return o.then((e=>s.miss(e))).then((()=>o))},set:(r,a)=>(t[JSON.stringify(r)]=e.serializable?JSON.stringify(a):a,Promise.resolve(a)),delete:e=>(delete t[JSON.stringify(e)],Promise.resolve()),clear:()=>(t={},Promise.resolve())}}const n=12e4;function o(e,t="up"){const r=Date.now();return{...e,status:t,lastUpdate:r,isUp:function(){return"up"===t||Date.now()-r>n},isTimedOut:function(){return"timed out"===t&&Date.now()-r<=n}}}function i(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}class c extends Error{constructor(e,t){super(e),i(this,"name","AlgoliaError"),t&&(this.name=t)}}class u extends c{constructor(e,t,r){super(e,r),i(this,"stackTrace",void 0),this.stackTrace=t}}class l extends u{constructor(e){super("Unreachable hosts - your application id may be incorrect. If the error persists, contact support@algolia.com.",e,"RetryError")}}class p extends u{constructor(e,t,r){super(e,r,"ApiError"),i(this,"status",void 0),this.status=t}}class d extends c{constructor(e,t){super(e,"DeserializationError"),i(this,"response",void 0),this.response=t}}function h(e,t,r){const a=function(e){const t=e=>"[object Object]"===Object.prototype.toString.call(e)||"[object Array]"===Object.prototype.toString.call(e);return Object.keys(e).map((r=>`${r}=${t(e[r])?JSON.stringify(e[r]):e[r]}`)).join("&")}(r);let s=`${e.protocol}://${e.url}/${"/"===t.charAt(0)?t.substr(1):t}`;return a.length&&(s+=`?${a}`),s}function m(e){const t=e.request.headers["x-algolia-api-key"]?{"x-algolia-api-key":"*****"}:{};return{...e,request:{...e.request,headers:{...e.request.headers,...t}}}}function f({hosts:e,hostsCache:t,baseHeaders:r,baseQueryParameters:a,algoliaAgent:s,timeouts:n,requester:i,requestsCache:c,responsesCache:u}){async function f(c,u,f=!0){const g=[],y=function(e,t){if("GET"===e.method||void 0===e.data&&void 0===t.data)return;const r=Array.isArray(e.data)?e.data:{...e.data,...t.data};return JSON.stringify(r)}(c,u),O=function(e,t,r){const a={Accept:"application/json",...e,...t,...r},s={};return Object.keys(a).forEach((e=>{const t=a[e];s[e.toLowerCase()]=t})),s}(r,c.headers,u.headers),b="GET"===c.method?{...c.data,...u.data}:{},P={"x-algolia-agent":s.value,...a,...c.queryParameters,...b};if(u&&u.queryParameters)for(const e of Object.keys(u.queryParameters))u.queryParameters[e]&&"[object Object]"!==Object.prototype.toString.call(u.queryParameters[e])?P[e]=u.queryParameters[e].toString():P[e]=u.queryParameters[e];let v=0;const w=async(e,r)=>{const a=e.pop();if(void 0===a)throw new l(function(e){return e.map((e=>m(e)))}(g));let s=u.timeout;void 0===s&&(s=f?n.read:n.write);const b={data:y,headers:O,method:c.method,url:h(a,c.path,P),connectTimeout:r(v,n.connect),responseTimeout:r(v,s)},j=t=>{const r={request:b,response:t,host:a,triesLeft:e.length};return g.push(r),r},q=await i.send(b);if(function({isTimedOut:e,status:t}){return e||function({isTimedOut:e,status:t}){return!e&&0==~~t}({isTimedOut:e,status:t})||2!=~~(t/100)&&4!=~~(t/100)}(q)){const s=j(q);return q.isTimedOut&&v++,console.log("Retryable failure",m(s)),await t.set(a,o(a,q.isTimedOut?"timed out":"down")),w(e,r)}if(function({status:e}){return 2==~~(e/100)}(q))return function(e){try{return JSON.parse(e.content)}catch(t){throw new d(t.message,e)}}(q);throw j(q),function({content:e,status:t},r){let a=e;try{a=JSON.parse(e).message}catch(e){}return new p(a,t,r)}(q,g)},j=e.filter((e=>"readWrite"===e.accept||(f?"read"===e.accept:"write"===e.accept))),q=await async function(e){const r=await Promise.all(e.map((e=>t.get(e,(()=>Promise.resolve(o(e))))))),a=r.filter((e=>e.isUp())),s=r.filter((e=>e.isTimedOut())),n=[...a,...s];return{hosts:n.length>0?n:e,getTimeout:(e,t)=>(0===s.length&&0===e?1:s.length+3+e)*t}}(j);return w([...q.hosts].reverse(),q.getTimeout)}return{hostsCache:t,requester:i,timeouts:n,algoliaAgent:s,baseHeaders:r,baseQueryParameters:a,hosts:e,request:function(e,t={}){const s=e.useReadTransporter||"GET"===e.method;if(!s)return f(e,t,s);const n=()=>f(e,t);if(!0!==(t.cacheable||e.cacheable))return n();const o={request:e,requestOptions:t,transporter:{queryParameters:a,headers:r}};return u.get(o,(()=>c.get(o,(()=>c.set(o,n()).then((e=>Promise.all([c.delete(o),e])),(e=>Promise.all([c.delete(o),Promise.reject(e)]))).then((([e,t])=>t))))),{miss:e=>u.set(o,e)})},requestsCache:c,responsesCache:u}}function g({algoliaAgents:e,client:t,version:r}){const a=function(e){const t={value:`Algolia for JavaScript (${e})`,add(e){const r=`; ${e.segment}${void 0!==e.version?` (${e.version})`:""}`;return-1===t.value.indexOf(r)&&(t.value=`${t.value}${r}`),t}};return t}(r).add({segment:t,version:r});return e.forEach((e=>a.add(e))),a}function y(e,t){if(null==e)return{};var r,a,s=function(e,t){if(null==e)return{};var r,a,s={},n=Object.keys(e);for(a=0;a<n.length;a++)r=n[a],t.indexOf(r)>=0||(s[r]=e[r]);return s}(e,t);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);for(a=0;a<n.length;a++)r=n[a],t.indexOf(r)>=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(s[r]=e[r])}return s}var O=["appId","apiKey","authMode","algoliaAgents"];function b(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,a)}return r}function P(e){for(var r=1;r<arguments.length;r++){var a=null!=arguments[r]?arguments[r]:{};r%2?b(Object(a),!0).forEach((function(r){t(e,r,a[r])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(a)):b(Object(a)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(a,t))}))}return e}var v="5.0.0-alpha.1";function w(e){return[{url:"".concat(e,"-dsn.algolia.net"),accept:"read",protocol:"https"},{url:"".concat(e,".algolia.net"),accept:"write",protocol:"https"}].concat(function(e){const t=e;for(let r=e.length-1;r>0;r--){const a=Math.floor(Math.random()*(r+1)),s=e[r];t[r]=e[a],t[a]=s}return t}([{url:"".concat(e,"-1.algolianet.com"),accept:"readWrite",protocol:"https"},{url:"".concat(e,"-2.algolianet.com"),accept:"readWrite",protocol:"https"},{url:"".concat(e,"-3.algolianet.com"),accept:"readWrite",protocol:"https"}]))}function j(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,a)}return r}e.apiClientVersion=v,e.recommendClient=function(e,n,o){if(!e||"string"!=typeof e)throw new Error("`appId` is missing.");if(!n||"string"!=typeof n)throw new Error("`apiKey` is missing.");return function(e){var t=e.appId,r=e.apiKey,a=e.authMode,s=e.algoliaAgents,n=y(e,O),o=function(e,t,r="WithinHeaders"){const a={"x-algolia-api-key":t,"x-algolia-application-id":e};return{headers:()=>"WithinHeaders"===r?a:{},queryParameters:()=>"WithinQueryParameters"===r?a:{}}}(t,r,a),i=f(P(P({hosts:w(t)},n),{},{algoliaAgent:g({algoliaAgents:s,client:"Recommend",version:v}),baseHeaders:P(P({"content-type":"text/plain"},o.headers()),n.baseHeaders),baseQueryParameters:P(P({},o.queryParameters()),n.baseQueryParameters)}));return{transporter:i,get _ua(){return i.algoliaAgent.value},addAlgoliaAgent:function(e,t){i.algoliaAgent.add({segment:e,version:t})},del:function(e,t){var r=e.path,a=e.parameters;if(!r)throw new Error("Parameter `path` is required when calling `del`.");var s={method:"DELETE",path:"/1{path}".replace("{path}",r),queryParameters:a||{},headers:{}};return i.request(s,t)},get:function(e,t){var r=e.path,a=e.parameters;if(!r)throw new Error("Parameter `path` is required when calling `get`.");var s={method:"GET",path:"/1{path}".replace("{path}",r),queryParameters:a||{},headers:{}};return i.request(s,t)},getRecommendations:function(e,t){if(!e)throw new Error("Parameter `getRecommendationsParams` is required when calling `getRecommendations`.");if(!e.requests)throw new Error("Parameter `getRecommendationsParams.requests` is required when calling `getRecommendations`.");var r={method:"POST",path:"/1/indexes/*/recommendations",queryParameters:{},headers:{},data:e,useReadTransporter:!0,cacheable:!0};return i.request(r,t)},post:function(e,t){var r=e.path,a=e.parameters,s=e.body;if(!r)throw new Error("Parameter `path` is required when calling `post`.");var n={method:"POST",path:"/1{path}".replace("{path}",r),queryParameters:a||{},headers:{},data:s||{}};return i.request(n,t)},put:function(e,t){var r=e.path,a=e.parameters,s=e.body;if(!r)throw new Error("Parameter `path` is required when calling `put`.");var n={method:"PUT",path:"/1{path}".replace("{path}",r),queryParameters:a||{},headers:{},data:s||{}};return i.request(n,t)}}}(function(e){for(var r=1;r<arguments.length;r++){var a=null!=arguments[r]?arguments[r]:{};r%2?j(Object(a),!0).forEach((function(r){t(e,r,a[r])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(a)):j(Object(a)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(a,t))}))}return e}({appId:e,apiKey:n,timeouts:{connect:1e3,read:2e3,write:3e4},requester:{send:function(e){return new Promise((t=>{const r=new XMLHttpRequest;r.open(e.method,e.url,!0),Object.keys(e.headers).forEach((t=>r.setRequestHeader(t,e.headers[t])));const a=(e,a)=>setTimeout((()=>{r.abort(),t({status:0,content:a,isTimedOut:!0})}),e),s=a(e.connectTimeout,"Connection timeout");let n;r.onreadystatechange=()=>{r.readyState>r.OPENED&&void 0===n&&(clearTimeout(s),n=a(e.responseTimeout,"Socket timeout"))},r.onerror=()=>{0===r.status&&(clearTimeout(s),clearTimeout(n),t({content:r.responseText||"Network request failed",status:r.status,isTimedOut:!1}))},r.onload=()=>{clearTimeout(s),clearTimeout(n),t({content:r.responseText,status:r.status,isTimedOut:!1})},r.send(e.data)}))}},algoliaAgents:[{segment:"Browser"}],authMode:"WithinQueryParameters",responsesCache:s(),requestsCache:s({serializable:!1}),hostsCache:a({caches:[r({key:"".concat(v,"-").concat(e)}),s()]})},o))}}));

@@ -1,3 +0,2 @@

/* eslint-disable import/no-unresolved*/
export * from './dist/recommend';
export { default } from './dist/recommend';
// eslint-disable-next-line import/no-unresolved
export * from './dist/builds/node';

@@ -1,15 +0,2 @@

/* eslint-disable functional/immutable-data, import/no-commonjs */
const recommend = require('./dist/recommend.cjs.js');
/**
* The Common JS build is the default entry point for the Node environment. Keep in
* in mind, that for the browser environment, we hint the bundler to use the UMD
* build instead as specified on the key `browser` of our `package.json` file.
*/
module.exports = recommend;
/**
* In addition, we also set explicitly the default export below making
* this Common JS module in compliance with es6 modules specification.
*/
module.exports.default = recommend;
// eslint-disable-next-line import/no-commonjs,import/extensions
module.exports = require('./dist/recommend.cjs.js');
{
"name": "@algolia/recommend",
"version": "4.14.1",
"private": false,
"description": "The perfect starting point to integrate Algolia Recommend within your JavaScript project.",
"repository": {
"type": "git",
"url": "git://github.com/algolia/algoliasearch-client-javascript.git"
},
"version": "5.0.0-alpha.1",
"description": "JavaScript client for recommend",
"repository": "algolia/algoliasearch-client-javascript",
"license": "MIT",
"sideEffects": false,
"author": "Algolia",
"main": "index.js",
"jsdelivr": "./dist/recommend.umd.js",
"unpkg": "./dist/recommend.umd.js",
"browser": {
"./index.js": "./dist/recommend.umd.js"
},
"jsdelivr": "dist/recommend.umd.js",
"unpkg": "dist/recommend.umd.js",
"module": "dist/recommend.esm.node.js",
"browser": "dist/recommend.umd.js",
"types": "index.d.ts",
"files": [
"dist",
"model",
"index.js",
"index.d.ts"
],
"scripts": {
"clean": "rm -rf ./dist"
},
"dependencies": {
"@algolia/cache-browser-local-storage": "4.14.1",
"@algolia/cache-common": "4.14.1",
"@algolia/cache-in-memory": "4.14.1",
"@algolia/client-common": "4.14.1",
"@algolia/client-search": "4.14.1",
"@algolia/logger-common": "4.14.1",
"@algolia/logger-console": "4.14.1",
"@algolia/requester-browser-xhr": "4.14.1",
"@algolia/requester-common": "4.14.1",
"@algolia/requester-node-http": "4.14.1",
"@algolia/transporter": "4.14.1"
"@algolia/client-common": "5.0.0-alpha.1",
"@algolia/requester-browser-xhr": "5.0.0-alpha.1",
"@algolia/requester-node-http": "5.0.0-alpha.1"
},
"devDependencies": {
"@types/node": "16.11.45",
"typescript": "4.7.4"
},
"engines": {
"node": ">= 14.0.0"
}
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc