@algolia/client-common 5.0.0-alpha.1 → 5.0.0-alpha.100

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/dist/{client-common.cjs.js → client-common.cjs} +189 -226
  2. package/dist/client-common.esm.node.js +188 -222
  3. package/dist/index.d.ts +9 -9
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/src/cache/createBrowserLocalStorageCache.d.ts +2 -2
  6. package/dist/src/cache/createBrowserLocalStorageCache.d.ts.map +1 -1
  7. package/dist/src/cache/createFallbackableCache.d.ts +2 -2
  8. package/dist/src/cache/createFallbackableCache.d.ts.map +1 -1
  9. package/dist/src/cache/createMemoryCache.d.ts +2 -2
  10. package/dist/src/cache/createMemoryCache.d.ts.map +1 -1
  11. package/dist/src/cache/createNullCache.d.ts +2 -2
  12. package/dist/src/cache/createNullCache.d.ts.map +1 -1
  13. package/dist/src/cache/index.d.ts +4 -4
  14. package/dist/src/cache/index.d.ts.map +1 -1
  15. package/dist/src/constants.d.ts +6 -6
  16. package/dist/src/constants.d.ts.map +1 -1
  17. package/dist/src/createAlgoliaAgent.d.ts +2 -2
  18. package/dist/src/createAlgoliaAgent.d.ts.map +1 -1
  19. package/dist/src/createAuth.d.ts +5 -5
  20. package/dist/src/createAuth.d.ts.map +1 -1
  21. package/dist/src/createEchoRequester.d.ts +6 -6
  22. package/dist/src/createEchoRequester.d.ts.map +1 -1
  23. package/dist/src/createIterablePromise.d.ts +13 -0
  24. package/dist/src/createIterablePromise.d.ts.map +1 -0
  25. package/dist/src/getAlgoliaAgent.d.ts +7 -7
  26. package/dist/src/getAlgoliaAgent.d.ts.map +1 -1
  27. package/dist/src/transporter/createStatefulHost.d.ts +2 -2
  28. package/dist/src/transporter/createStatefulHost.d.ts.map +1 -1
  29. package/dist/src/transporter/createTransporter.d.ts +2 -2
  30. package/dist/src/transporter/createTransporter.d.ts.map +1 -1
  31. package/dist/src/transporter/errors.d.ts +37 -20
  32. package/dist/src/transporter/errors.d.ts.map +1 -1
  33. package/dist/src/transporter/helpers.d.ts +8 -8
  34. package/dist/src/transporter/helpers.d.ts.map +1 -1
  35. package/dist/src/transporter/index.d.ts +6 -6
  36. package/dist/src/transporter/index.d.ts.map +1 -1
  37. package/dist/src/transporter/responses.d.ts +4 -4
  38. package/dist/src/transporter/responses.d.ts.map +1 -1
  39. package/dist/src/transporter/stackTrace.d.ts +3 -3
  40. package/dist/src/transporter/stackTrace.d.ts.map +1 -1
  41. package/dist/src/types/{Cache.d.ts → cache.d.ts} +61 -47
  42. package/dist/src/types/cache.d.ts.map +1 -0
  43. package/dist/src/types/createClient.d.ts +12 -0
  44. package/dist/src/types/createClient.d.ts.map +1 -0
  45. package/dist/src/types/createIterablePromise.d.ts +36 -0
  46. package/dist/src/types/createIterablePromise.d.ts.map +1 -0
  47. package/dist/src/types/{Host.d.ts → host.d.ts} +33 -33
  48. package/dist/src/types/host.d.ts.map +1 -0
  49. package/dist/src/types/index.d.ts +6 -6
  50. package/dist/src/types/index.d.ts.map +1 -1
  51. package/dist/src/types/{Requester.d.ts → requester.d.ts} +66 -66
  52. package/dist/src/types/requester.d.ts.map +1 -0
  53. package/dist/src/types/{Transporter.d.ts → transporter.d.ts} +128 -128
  54. package/dist/src/types/transporter.d.ts.map +1 -0
  55. package/index.ts +1 -1
  56. package/package.json +13 -9
  57. package/src/__tests__/cache/browser-local-storage-cache.test.ts +61 -9
  58. package/src/__tests__/create-iterable-promise.test.ts +238 -0
  59. package/src/cache/createBrowserLocalStorageCache.ts +55 -6
  60. package/src/createEchoRequester.ts +2 -2
  61. package/src/createIterablePromise.ts +47 -0
  62. package/src/transporter/createTransporter.ts +4 -1
  63. package/src/transporter/errors.ts +39 -3
  64. package/src/transporter/helpers.ts +14 -6
  65. package/src/types/{Cache.ts → cache.ts} +17 -0
  66. package/src/types/{CreateClient.ts → createClient.ts} +2 -2
  67. package/src/types/createIterablePromise.ts +40 -0
  68. package/src/types/index.ts +6 -6
  69. package/src/types/{Requester.ts → requester.ts} +1 -1
  70. package/src/types/{Transporter.ts → transporter.ts} +3 -3
  71. package/dist/src/createRetryablePromise.d.ts +0 -14
  72. package/dist/src/createRetryablePromise.d.ts.map +0 -1
  73. package/dist/src/types/Cache.d.ts.map +0 -1
  74. package/dist/src/types/CreateClient.d.ts +0 -12
  75. package/dist/src/types/CreateClient.d.ts.map +0 -1
  76. package/dist/src/types/CreateRetryablePromise.d.ts +0 -19
  77. package/dist/src/types/CreateRetryablePromise.d.ts.map +0 -1
  78. package/dist/src/types/Host.d.ts.map +0 -1
  79. package/dist/src/types/Requester.d.ts.map +0 -1
  80. package/dist/src/types/Transporter.d.ts.map +0 -1
  81. package/src/__tests__/create-retryable-promise.test.ts +0 -86
  82. package/src/createRetryablePromise.ts +0 -52
  83. package/src/types/CreateRetryablePromise.ts +0 -21
  84. /package/src/types/{Host.ts → host.ts} +0 -0
@@ -7,11 +7,9 @@ function createAuth(appId, apiKey, authMode = 'WithinHeaders') {
7
7
  headers() {
8
8
  return authMode === 'WithinHeaders' ? credentials : {};
9
9
  },
10
-
11
10
  queryParameters() {
12
11
  return authMode === 'WithinQueryParameters' ? credentials : {};
13
12
  }
14
-
15
13
  };
16
14
  }
17
15
 
@@ -22,15 +20,12 @@ function getUrlParams({
22
20
  }) {
23
21
  const algoliaAgent = urlSearchParams.get('x-algolia-agent') || '';
24
22
  const searchParams = {};
25
-
26
23
  for (const [k, v] of urlSearchParams) {
27
24
  if (k === 'x-algolia-agent') {
28
25
  continue;
29
26
  }
30
-
31
27
  searchParams[k] = v;
32
28
  }
33
-
34
29
  return {
35
30
  host,
36
31
  algoliaAgent,
@@ -38,7 +33,6 @@ function getUrlParams({
38
33
  path: pathname
39
34
  };
40
35
  }
41
-
42
36
  function createEchoRequester({
43
37
  getURL,
44
38
  status = 200
@@ -50,11 +44,12 @@ function createEchoRequester({
50
44
  algoliaAgent,
51
45
  path
52
46
  } = getUrlParams(getURL(request.url));
53
- const content = { ...request,
47
+ const content = {
48
+ ...request,
54
49
  data: request.data ? JSON.parse(request.data) : undefined,
55
50
  path,
56
51
  host,
57
- algoliaAgent: encodeURI(algoliaAgent),
52
+ algoliaAgent: encodeURIComponent(algoliaAgent),
58
53
  searchParams
59
54
  };
60
55
  return Promise.resolve({
@@ -63,95 +58,108 @@ function createEchoRequester({
63
58
  status
64
59
  });
65
60
  }
66
-
67
61
  return {
68
62
  send
69
63
  };
70
64
  }
71
65
 
72
- const DEFAULT_MAX_RETRIES = 50;
73
- const DEFAULT_TIMEOUT = retryCount => Math.min(retryCount * 200, 5000);
74
- /**
75
- * Return a promise that retry a task until it meets the condition.
76
- *
77
- * @param createRetryablePromiseOptions - The createRetryablePromise options.
78
- * @param createRetryablePromiseOptions.func - The function to run, which returns a promise.
79
- * @param createRetryablePromiseOptions.validate - The validator function. It receives the resolved return of `func`.
80
- * @param createRetryablePromiseOptions.maxRetries - The maximum number of retries. 50 by default.
81
- * @param createRetryablePromiseOptions.timeout - The function to decide how long to wait between retries.
66
+ /**
67
+ * Helper: Returns the promise of a given `func` to iterate on, based on a given `validate` condition.
68
+ *
69
+ * @param createIterator - The createIterator options.
70
+ * @param createIterator.func - The function to run, which returns a promise.
71
+ * @param createIterator.validate - The validator function. It receives the resolved return of `func`.
72
+ * @param createIterator.aggregator - The function that runs right after the `func` method has been executed, allows you to do anything with the response before `validate`.
73
+ * @param createIterator.error - The `validate` condition to throw an error, and its message.
74
+ * @param createIterator.timeout - The function to decide how long to wait between iterations.
82
75
  */
83
-
84
- function createRetryablePromise({
76
+ function createIterablePromise({
85
77
  func,
86
78
  validate,
87
- maxRetries = DEFAULT_MAX_RETRIES,
88
- timeout = DEFAULT_TIMEOUT
79
+ aggregator,
80
+ error,
81
+ timeout = () => 0
89
82
  }) {
90
- let retryCount = 0;
91
-
92
- const retry = () => {
83
+ const retry = previousResponse => {
93
84
  return new Promise((resolve, reject) => {
94
- func().then(response => {
95
- const isValid = validate(response);
96
-
97
- if (isValid) {
98
- resolve(response);
99
- } else if (retryCount + 1 >= maxRetries) {
100
- reject(new Error(`The maximum number of retries exceeded. (${retryCount + 1}/${maxRetries})`));
101
- } else {
102
- retryCount += 1;
103
- setTimeout(() => {
104
- retry().then(resolve).catch(reject);
105
- }, timeout(retryCount));
85
+ func(previousResponse).then(response => {
86
+ if (aggregator) {
87
+ aggregator(response);
88
+ }
89
+ if (validate(response)) {
90
+ return resolve(response);
106
91
  }
107
- }).catch(error => {
108
- reject(error);
92
+ if (error && error.validate(response)) {
93
+ return reject(new Error(error.message(response)));
94
+ }
95
+ return setTimeout(() => {
96
+ retry(response).then(resolve).catch(reject);
97
+ }, timeout());
98
+ }).catch(err => {
99
+ reject(err);
109
100
  });
110
101
  });
111
102
  };
112
-
113
103
  return retry();
114
104
  }
115
105
 
116
106
  function createBrowserLocalStorageCache(options) {
117
- let storage; // We've changed the namespace to avoid conflicts with v4, as this version is a huge breaking change
118
-
107
+ let storage;
108
+ // We've changed the namespace to avoid conflicts with v4, as this version is a huge breaking change
119
109
  const namespaceKey = `algolia-client-js-${options.key}`;
120
-
121
110
  function getStorage() {
122
111
  if (storage === undefined) {
123
112
  storage = options.localStorage || window.localStorage;
124
113
  }
125
-
126
114
  return storage;
127
115
  }
128
-
129
116
  function getNamespace() {
130
117
  return JSON.parse(getStorage().getItem(namespaceKey) || '{}');
131
118
  }
132
-
119
+ function setNamespace(namespace) {
120
+ getStorage().setItem(namespaceKey, JSON.stringify(namespace));
121
+ }
122
+ function removeOutdatedCacheItems() {
123
+ const timeToLive = options.timeToLive ? options.timeToLive * 1000 : null;
124
+ const namespace = getNamespace();
125
+ const filteredNamespaceWithoutOldFormattedCacheItems = Object.fromEntries(Object.entries(namespace).filter(([, cacheItem]) => {
126
+ return cacheItem.timestamp !== undefined;
127
+ }));
128
+ setNamespace(filteredNamespaceWithoutOldFormattedCacheItems);
129
+ if (!timeToLive) {
130
+ return;
131
+ }
132
+ const filteredNamespaceWithoutExpiredItems = Object.fromEntries(Object.entries(filteredNamespaceWithoutOldFormattedCacheItems).filter(([, cacheItem]) => {
133
+ const currentTimestamp = new Date().getTime();
134
+ const isExpired = cacheItem.timestamp + timeToLive < currentTimestamp;
135
+ return !isExpired;
136
+ }));
137
+ setNamespace(filteredNamespaceWithoutExpiredItems);
138
+ }
133
139
  return {
134
140
  get(key, defaultValue, events = {
135
141
  miss: () => Promise.resolve()
136
142
  }) {
137
143
  return Promise.resolve().then(() => {
138
- const keyAsString = JSON.stringify(key);
139
- const value = getNamespace()[keyAsString];
140
- return Promise.all([value || defaultValue(), value !== undefined]);
144
+ removeOutdatedCacheItems();
145
+ return getNamespace()[JSON.stringify(key)];
146
+ }).then(value => {
147
+ return Promise.all([value ? value.value : defaultValue(), value !== undefined]);
141
148
  }).then(([value, exists]) => {
142
149
  return Promise.all([value, exists || events.miss(value)]);
143
150
  }).then(([value]) => value);
144
151
  },
145
-
146
152
  set(key, value) {
147
153
  return Promise.resolve().then(() => {
148
154
  const namespace = getNamespace();
149
- namespace[JSON.stringify(key)] = value;
155
+ namespace[JSON.stringify(key)] = {
156
+ timestamp: new Date().getTime(),
157
+ value
158
+ };
150
159
  getStorage().setItem(namespaceKey, JSON.stringify(namespace));
151
160
  return value;
152
161
  });
153
162
  },
154
-
155
163
  delete(key) {
156
164
  return Promise.resolve().then(() => {
157
165
  const namespace = getNamespace();
@@ -159,13 +167,11 @@ function createBrowserLocalStorageCache(options) {
159
167
  getStorage().setItem(namespaceKey, JSON.stringify(namespace));
160
168
  });
161
169
  },
162
-
163
170
  clear() {
164
171
  return Promise.resolve().then(() => {
165
172
  getStorage().removeItem(namespaceKey);
166
173
  });
167
174
  }
168
-
169
175
  };
170
176
  }
171
177
 
@@ -177,30 +183,24 @@ function createNullCache() {
177
183
  const value = defaultValue();
178
184
  return value.then(result => Promise.all([result, events.miss(result)])).then(([result]) => result);
179
185
  },
180
-
181
186
  set(_key, value) {
182
187
  return Promise.resolve(value);
183
188
  },
184
-
185
189
  delete(_key) {
186
190
  return Promise.resolve();
187
191
  },
188
-
189
192
  clear() {
190
193
  return Promise.resolve();
191
194
  }
192
-
193
195
  };
194
196
  }
195
197
 
196
198
  function createFallbackableCache(options) {
197
199
  const caches = [...options.caches];
198
200
  const current = caches.shift();
199
-
200
201
  if (current === undefined) {
201
202
  return createNullCache();
202
203
  }
203
-
204
204
  return {
205
205
  get(key, defaultValue, events = {
206
206
  miss: () => Promise.resolve()
@@ -211,7 +211,6 @@ function createFallbackableCache(options) {
211
211
  }).get(key, defaultValue, events);
212
212
  });
213
213
  },
214
-
215
214
  set(key, value) {
216
215
  return current.set(key, value).catch(() => {
217
216
  return createFallbackableCache({
@@ -219,7 +218,6 @@ function createFallbackableCache(options) {
219
218
  }).set(key, value);
220
219
  });
221
220
  },
222
-
223
221
  delete(key) {
224
222
  return current.delete(key).catch(() => {
225
223
  return createFallbackableCache({
@@ -227,7 +225,6 @@ function createFallbackableCache(options) {
227
225
  }).delete(key);
228
226
  });
229
227
  },
230
-
231
228
  clear() {
232
229
  return current.clear().catch(() => {
233
230
  return createFallbackableCache({
@@ -235,7 +232,6 @@ function createFallbackableCache(options) {
235
232
  }).clear();
236
233
  });
237
234
  }
238
-
239
235
  };
240
236
  }
241
237
 
@@ -248,30 +244,24 @@ function createMemoryCache(options = {
248
244
  miss: () => Promise.resolve()
249
245
  }) {
250
246
  const keyAsString = JSON.stringify(key);
251
-
252
247
  if (keyAsString in cache) {
253
248
  return Promise.resolve(options.serializable ? JSON.parse(cache[keyAsString]) : cache[keyAsString]);
254
249
  }
255
-
256
250
  const promise = defaultValue();
257
251
  return promise.then(value => events.miss(value)).then(() => promise);
258
252
  },
259
-
260
253
  set(key, value) {
261
254
  cache[JSON.stringify(key)] = options.serializable ? JSON.stringify(value) : value;
262
255
  return Promise.resolve(value);
263
256
  },
264
-
265
257
  delete(key) {
266
258
  delete cache[JSON.stringify(key)];
267
259
  return Promise.resolve();
268
260
  },
269
-
270
261
  clear() {
271
262
  cache = {};
272
263
  return Promise.resolve();
273
264
  }
274
-
275
265
  };
276
266
  }
277
267
 
@@ -280,16 +270,14 @@ function createMemoryCache(options = {
280
270
  const EXPIRATION_DELAY = 2 * 60 * 1000;
281
271
  function createStatefulHost(host, status = 'up') {
282
272
  const lastUpdate = Date.now();
283
-
284
273
  function isUp() {
285
274
  return status === 'up' || Date.now() - lastUpdate > EXPIRATION_DELAY;
286
275
  }
287
-
288
276
  function isTimedOut() {
289
277
  return status === 'timed out' && Date.now() - lastUpdate <= EXPIRATION_DELAY;
290
278
  }
291
-
292
- return { ...host,
279
+ return {
280
+ ...host,
293
281
  status,
294
282
  lastUpdate,
295
283
  isUp,
@@ -297,7 +285,22 @@ function createStatefulHost(host, status = 'up') {
297
285
  };
298
286
  }
299
287
 
288
+ function _toPrimitive(t, r) {
289
+ if ("object" != typeof t || !t) return t;
290
+ var e = t[Symbol.toPrimitive];
291
+ if (void 0 !== e) {
292
+ var i = e.call(t, r || "default");
293
+ if ("object" != typeof i) return i;
294
+ throw new TypeError("@@toPrimitive must return a primitive value.");
295
+ }
296
+ return ("string" === r ? String : Number)(t);
297
+ }
298
+ function _toPropertyKey(t) {
299
+ var i = _toPrimitive(t, "string");
300
+ return "symbol" == typeof i ? i : String(i);
301
+ }
300
302
  function _defineProperty(obj, key, value) {
303
+ key = _toPropertyKey(key);
301
304
  if (key in obj) {
302
305
  Object.defineProperty(obj, key, {
303
306
  value: value,
@@ -308,92 +311,82 @@ function _defineProperty(obj, key, value) {
308
311
  } else {
309
312
  obj[key] = value;
310
313
  }
311
-
312
314
  return obj;
313
315
  }
314
316
 
315
317
  class AlgoliaError extends Error {
316
318
  constructor(message, name) {
317
319
  super(message);
318
-
319
320
  _defineProperty(this, "name", 'AlgoliaError');
320
-
321
321
  if (name) {
322
322
  this.name = name;
323
323
  }
324
324
  }
325
-
326
325
  }
327
326
  class ErrorWithStackTrace extends AlgoliaError {
328
327
  constructor(message, stackTrace, name) {
329
- super(message, name); // the array and object should be frozen to reflect the stackTrace at the time of the error
330
-
328
+ super(message, name);
329
+ // the array and object should be frozen to reflect the stackTrace at the time of the error
331
330
  _defineProperty(this, "stackTrace", void 0);
332
-
333
331
  this.stackTrace = stackTrace;
334
332
  }
335
-
336
333
  }
337
334
  class RetryError extends ErrorWithStackTrace {
338
335
  constructor(stackTrace) {
339
- super('Unreachable hosts - your application id may be incorrect. If the error persists, contact support@algolia.com.', stackTrace, 'RetryError');
336
+ super('Unreachable hosts - your application id may be incorrect. If the error persists, please create a ticket at https://support.algolia.com/ sharing steps we can use to reproduce the issue.', stackTrace, 'RetryError');
340
337
  }
341
-
342
338
  }
343
339
  class ApiError extends ErrorWithStackTrace {
344
- constructor(message, status, stackTrace) {
345
- super(message, stackTrace, 'ApiError');
346
-
340
+ constructor(message, status, stackTrace, name = 'ApiError') {
341
+ super(message, stackTrace, name);
347
342
  _defineProperty(this, "status", void 0);
348
-
349
343
  this.status = status;
350
344
  }
351
-
352
345
  }
353
346
  class DeserializationError extends AlgoliaError {
354
347
  constructor(message, response) {
355
348
  super(message, 'DeserializationError');
356
-
357
349
  _defineProperty(this, "response", void 0);
358
-
359
350
  this.response = response;
360
351
  }
361
-
352
+ }
353
+ // DetailedApiError is only used by the ingestion client to return more informative error, other clients will use ApiClient.
354
+ class DetailedApiError extends ApiError {
355
+ constructor(message, status, error, stackTrace) {
356
+ super(message, status, stackTrace, 'DetailedApiError');
357
+ _defineProperty(this, "error", void 0);
358
+ this.error = error;
359
+ }
362
360
  }
363
361
 
364
362
  function shuffle(array) {
365
363
  const shuffledArray = array;
366
-
367
364
  for (let c = array.length - 1; c > 0; c--) {
368
365
  const b = Math.floor(Math.random() * (c + 1));
369
366
  const a = array[c];
370
367
  shuffledArray[c] = array[b];
371
368
  shuffledArray[b] = a;
372
369
  }
373
-
374
370
  return shuffledArray;
375
371
  }
376
372
  function serializeUrl(host, path, queryParameters) {
377
373
  const queryParametersAsString = serializeQueryParameters(queryParameters);
378
374
  let url = `${host.protocol}://${host.url}/${path.charAt(0) === '/' ? path.substr(1) : path}`;
379
-
380
375
  if (queryParametersAsString.length) {
381
376
  url += `?${queryParametersAsString}`;
382
377
  }
383
-
384
378
  return url;
385
379
  }
386
380
  function serializeQueryParameters(parameters) {
387
381
  const isObjectOrArray = value => Object.prototype.toString.call(value) === '[object Object]' || Object.prototype.toString.call(value) === '[object Array]';
388
-
389
- return Object.keys(parameters).map(key => `${key}=${isObjectOrArray(parameters[key]) ? JSON.stringify(parameters[key]) : parameters[key]}`).join('&');
382
+ return Object.keys(parameters).map(key => `${key}=${encodeURIComponent(isObjectOrArray(parameters[key]) ? JSON.stringify(parameters[key]) : parameters[key])}`).join('&');
390
383
  }
391
384
  function serializeData(request, requestOptions) {
392
385
  if (request.method === 'GET' || request.data === undefined && requestOptions.data === undefined) {
393
386
  return undefined;
394
387
  }
395
-
396
- const data = Array.isArray(request.data) ? request.data : { ...request.data,
388
+ const data = Array.isArray(request.data) ? request.data : {
389
+ ...request.data,
397
390
  ...requestOptions.data
398
391
  };
399
392
  return JSON.stringify(data);
@@ -423,14 +416,16 @@ function deserializeFailure({
423
416
  content,
424
417
  status
425
418
  }, stackFrame) {
426
- let message = content;
427
-
428
419
  try {
429
- message = JSON.parse(content).message;
430
- } catch (e) {// ..
420
+ const parsed = JSON.parse(content);
421
+ if ('error' in parsed) {
422
+ return new DetailedApiError(parsed.message, status, parsed.error, stackFrame);
423
+ }
424
+ return new ApiError(parsed.message, status, stackFrame);
425
+ } catch (e) {
426
+ // ..
431
427
  }
432
-
433
- return new ApiError(message, status, stackFrame);
428
+ return new ApiError(content, status, stackFrame);
434
429
  }
435
430
 
436
431
  function isNetworkError({
@@ -461,9 +456,12 @@ function stackFrameWithoutCredentials(stackFrame) {
461
456
  const modifiedHeaders = stackFrame.request.headers['x-algolia-api-key'] ? {
462
457
  'x-algolia-api-key': '*****'
463
458
  } : {};
464
- return { ...stackFrame,
465
- request: { ...stackFrame.request,
466
- headers: { ...stackFrame.request.headers,
459
+ return {
460
+ ...stackFrame,
461
+ request: {
462
+ ...stackFrame.request,
463
+ headers: {
464
+ ...stackFrame.request.headers,
467
465
  ...modifiedHeaders
468
466
  }
469
467
  }
@@ -488,51 +486,49 @@ function createTransporter({
488
486
  });
489
487
  }));
490
488
  const hostsUp = statefulHosts.filter(host => host.isUp());
491
- const hostsTimedOut = statefulHosts.filter(host => host.isTimedOut()); // Note, we put the hosts that previously timed out on the end of the list.
492
-
489
+ const hostsTimedOut = statefulHosts.filter(host => host.isTimedOut());
490
+ // Note, we put the hosts that previously timed out on the end of the list.
493
491
  const hostsAvailable = [...hostsUp, ...hostsTimedOut];
494
492
  const compatibleHostsAvailable = hostsAvailable.length > 0 ? hostsAvailable : compatibleHosts;
495
493
  return {
496
494
  hosts: compatibleHostsAvailable,
497
-
498
495
  getTimeout(timeoutsCount, baseTimeout) {
499
- /**
500
- * Imagine that you have 4 hosts, if timeouts will increase
501
- * on the following way: 1 (timed out) > 4 (timed out) > 5 (200).
502
- *
503
- * Note that, the very next request, we start from the previous timeout.
504
- *
505
- * 5 (timed out) > 6 (timed out) > 7 ...
506
- *
507
- * This strategy may need to be reviewed, but is the strategy on the our
508
- * current v3 version.
496
+ /**
497
+ * Imagine that you have 4 hosts, if timeouts will increase
498
+ * on the following way: 1 (timed out) > 4 (timed out) > 5 (200).
499
+ *
500
+ * Note that, the very next request, we start from the previous timeout.
501
+ *
502
+ * 5 (timed out) > 6 (timed out) > 7 ...
503
+ *
504
+ * This strategy may need to be reviewed, but is the strategy on the our
505
+ * current v3 version.
509
506
  */
510
507
  const timeoutMultiplier = hostsTimedOut.length === 0 && timeoutsCount === 0 ? 1 : hostsTimedOut.length + 3 + timeoutsCount;
511
508
  return timeoutMultiplier * baseTimeout;
512
509
  }
513
-
514
510
  };
515
511
  }
516
-
517
512
  async function retryableRequest(request, requestOptions, isRead = true) {
518
513
  const stackTrace = [];
519
- /**
520
- * First we prepare the payload that do not depend from hosts.
514
+ /**
515
+ * First we prepare the payload that do not depend from hosts.
521
516
  */
522
-
523
517
  const data = serializeData(request, requestOptions);
524
- const headers = serializeHeaders(baseHeaders, request.headers, requestOptions.headers); // On `GET`, the data is proxied to query parameters.
525
-
526
- const dataQueryParameters = request.method === 'GET' ? { ...request.data,
518
+ const headers = serializeHeaders(baseHeaders, request.headers, requestOptions.headers);
519
+ // On `GET`, the data is proxied to query parameters.
520
+ const dataQueryParameters = request.method === 'GET' ? {
521
+ ...request.data,
527
522
  ...requestOptions.data
528
523
  } : {};
529
524
  const queryParameters = {
530
- 'x-algolia-agent': algoliaAgent.value,
531
525
  ...baseQueryParameters,
532
526
  ...request.queryParameters,
533
527
  ...dataQueryParameters
534
528
  };
535
-
529
+ if (algoliaAgent.value) {
530
+ queryParameters['x-algolia-agent'] = algoliaAgent.value;
531
+ }
536
532
  if (requestOptions && requestOptions.queryParameters) {
537
533
  for (const key of Object.keys(requestOptions.queryParameters)) {
538
534
  // We want to keep `undefined` and `null` values,
@@ -545,25 +541,19 @@ function createTransporter({
545
541
  }
546
542
  }
547
543
  }
548
-
549
544
  let timeoutsCount = 0;
550
-
551
545
  const retry = async (retryableHosts, getTimeout) => {
552
- /**
553
- * We iterate on each host, until there is no host left.
546
+ /**
547
+ * We iterate on each host, until there is no host left.
554
548
  */
555
549
  const host = retryableHosts.pop();
556
-
557
550
  if (host === undefined) {
558
551
  throw new RetryError(stackTraceWithoutCredentials(stackTrace));
559
552
  }
560
-
561
553
  let responseTimeout = requestOptions.timeout;
562
-
563
554
  if (responseTimeout === undefined) {
564
555
  responseTimeout = isRead ? timeouts.read : timeouts.write;
565
556
  }
566
-
567
557
  const payload = {
568
558
  data,
569
559
  headers,
@@ -572,12 +562,11 @@ function createTransporter({
572
562
  connectTimeout: getTimeout(timeoutsCount, timeouts.connect),
573
563
  responseTimeout: getTimeout(timeoutsCount, responseTimeout)
574
564
  };
575
- /**
576
- * The stackFrame is pushed to the stackTrace so we
577
- * can have information about onRetry and onFailure
578
- * decisions.
565
+ /**
566
+ * The stackFrame is pushed to the stackTrace so we
567
+ * can have information about onRetry and onFailure
568
+ * decisions.
579
569
  */
580
-
581
570
  const pushToStackTrace = response => {
582
571
  const stackFrame = {
583
572
  request: payload,
@@ -588,102 +577,85 @@ function createTransporter({
588
577
  stackTrace.push(stackFrame);
589
578
  return stackFrame;
590
579
  };
591
-
592
580
  const response = await requester.send(payload);
593
-
594
581
  if (isRetryable(response)) {
595
- const stackFrame = pushToStackTrace(response); // If response is a timeout, we increase the number of timeouts so we can increase the timeout later.
596
-
582
+ const stackFrame = pushToStackTrace(response);
583
+ // If response is a timeout, we increase the number of timeouts so we can increase the timeout later.
597
584
  if (response.isTimedOut) {
598
585
  timeoutsCount++;
599
586
  }
600
- /**
601
- * Failures are individually sent to the logger, allowing
602
- * the end user to debug / store stack frames even
603
- * when a retry error does not happen.
587
+ /**
588
+ * Failures are individually sent to the logger, allowing
589
+ * the end user to debug / store stack frames even
590
+ * when a retry error does not happen.
604
591
  */
605
592
  // eslint-disable-next-line no-console -- this will be fixed by exposing a `logger` to the transporter
606
-
607
-
608
593
  console.log('Retryable failure', stackFrameWithoutCredentials(stackFrame));
609
- /**
610
- * We also store the state of the host in failure cases. If the host, is
611
- * down it will remain down for the next 2 minutes. In a timeout situation,
612
- * this host will be added end of the list of hosts on the next request.
594
+ /**
595
+ * We also store the state of the host in failure cases. If the host, is
596
+ * down it will remain down for the next 2 minutes. In a timeout situation,
597
+ * this host will be added end of the list of hosts on the next request.
613
598
  */
614
-
615
599
  await hostsCache.set(host, createStatefulHost(host, response.isTimedOut ? 'timed out' : 'down'));
616
600
  return retry(retryableHosts, getTimeout);
617
601
  }
618
-
619
602
  if (isSuccess(response)) {
620
603
  return deserializeSuccess(response);
621
604
  }
622
-
623
605
  pushToStackTrace(response);
624
606
  throw deserializeFailure(response, stackTrace);
625
607
  };
626
- /**
627
- * Finally, for each retryable host perform request until we got a non
628
- * retryable response. Some notes here:
629
- *
630
- * 1. The reverse here is applied so we can apply a `pop` later on => more performant.
631
- * 2. We also get from the retryable options a timeout multiplier that is tailored
632
- * for the current context.
608
+ /**
609
+ * Finally, for each retryable host perform request until we got a non
610
+ * retryable response. Some notes here:
611
+ *
612
+ * 1. The reverse here is applied so we can apply a `pop` later on => more performant.
613
+ * 2. We also get from the retryable options a timeout multiplier that is tailored
614
+ * for the current context.
633
615
  */
634
-
635
-
636
616
  const compatibleHosts = hosts.filter(host => host.accept === 'readWrite' || (isRead ? host.accept === 'read' : host.accept === 'write'));
637
617
  const options = await createRetryableOptions(compatibleHosts);
638
618
  return retry([...options.hosts].reverse(), options.getTimeout);
639
619
  }
640
-
641
620
  function createRequest(request, requestOptions = {}) {
642
- /**
643
- * A read request is either a `GET` request, or a request that we make
644
- * via the `read` transporter (e.g. `search`).
621
+ /**
622
+ * A read request is either a `GET` request, or a request that we make
623
+ * via the `read` transporter (e.g. `search`).
645
624
  */
646
625
  const isRead = request.useReadTransporter || request.method === 'GET';
647
-
648
626
  if (!isRead) {
649
- /**
650
- * On write requests, no cache mechanisms are applied, and we
651
- * proxy the request immediately to the requester.
627
+ /**
628
+ * On write requests, no cache mechanisms are applied, and we
629
+ * proxy the request immediately to the requester.
652
630
  */
653
631
  return retryableRequest(request, requestOptions, isRead);
654
632
  }
655
-
656
633
  const createRetryableRequest = () => {
657
- /**
658
- * Then, we prepare a function factory that contains the construction of
659
- * the retryable request. At this point, we may *not* perform the actual
660
- * request. But we want to have the function factory ready.
634
+ /**
635
+ * Then, we prepare a function factory that contains the construction of
636
+ * the retryable request. At this point, we may *not* perform the actual
637
+ * request. But we want to have the function factory ready.
661
638
  */
662
639
  return retryableRequest(request, requestOptions);
663
640
  };
664
- /**
665
- * Once we have the function factory ready, we need to determine of the
666
- * request is "cacheable" - should be cached. Note that, once again,
667
- * the user can force this option.
641
+ /**
642
+ * Once we have the function factory ready, we need to determine of the
643
+ * request is "cacheable" - should be cached. Note that, once again,
644
+ * the user can force this option.
668
645
  */
669
-
670
-
671
646
  const cacheable = requestOptions.cacheable || request.cacheable;
672
- /**
673
- * If is not "cacheable", we immediately trigger the retryable request, no
674
- * need to check cache implementations.
647
+ /**
648
+ * If is not "cacheable", we immediately trigger the retryable request, no
649
+ * need to check cache implementations.
675
650
  */
676
-
677
651
  if (cacheable !== true) {
678
652
  return createRetryableRequest();
679
653
  }
680
- /**
681
- * If the request is "cacheable", we need to first compute the key to ask
682
- * the cache implementations if this request is on progress or if the
683
- * response already exists on the cache.
654
+ /**
655
+ * If the request is "cacheable", we need to first compute the key to ask
656
+ * the cache implementations if this request is on progress or if the
657
+ * response already exists on the cache.
684
658
  */
685
-
686
-
687
659
  const key = {
688
660
  request,
689
661
  requestOptions,
@@ -692,33 +664,31 @@ function createTransporter({
692
664
  headers: baseHeaders
693
665
  }
694
666
  };
695
- /**
696
- * With the computed key, we first ask the responses cache
697
- * implementation if this request was been resolved before.
667
+ /**
668
+ * With the computed key, we first ask the responses cache
669
+ * implementation if this request was been resolved before.
698
670
  */
699
-
700
671
  return responsesCache.get(key, () => {
701
- /**
702
- * If the request has never resolved before, we actually ask if there
703
- * is a current request with the same key on progress.
672
+ /**
673
+ * If the request has never resolved before, we actually ask if there
674
+ * is a current request with the same key on progress.
704
675
  */
705
676
  return requestsCache.get(key, () =>
706
- /**
707
- * Finally, if there is no request in progress with the same key,
708
- * this `createRetryableRequest()` will actually trigger the
709
- * retryable request.
677
+ /**
678
+ * Finally, if there is no request in progress with the same key,
679
+ * this `createRetryableRequest()` will actually trigger the
680
+ * retryable request.
710
681
  */
711
682
  requestsCache.set(key, createRetryableRequest()).then(response => Promise.all([requestsCache.delete(key), response]), err => Promise.all([requestsCache.delete(key), Promise.reject(err)])).then(([_, response]) => response));
712
683
  }, {
713
- /**
714
- * Of course, once we get this response back from the server, we
715
- * tell response cache to actually store the received response
716
- * to be used later.
684
+ /**
685
+ * Of course, once we get this response back from the server, we
686
+ * tell response cache to actually store the received response
687
+ * to be used later.
717
688
  */
718
689
  miss: response => responsesCache.set(key, response)
719
690
  });
720
691
  }
721
-
722
692
  return {
723
693
  hostsCache,
724
694
  requester,
@@ -736,17 +706,13 @@ function createTransporter({
736
706
  function createAlgoliaAgent(version) {
737
707
  const algoliaAgent = {
738
708
  value: `Algolia for JavaScript (${version})`,
739
-
740
709
  add(options) {
741
710
  const addedAlgoliaAgent = `; ${options.segment}${options.version !== undefined ? ` (${options.version})` : ''}`;
742
-
743
711
  if (algoliaAgent.value.indexOf(addedAlgoliaAgent) === -1) {
744
712
  algoliaAgent.value = `${algoliaAgent.value}${addedAlgoliaAgent}`;
745
713
  }
746
-
747
714
  return algoliaAgent;
748
715
  }
749
-
750
716
  };
751
717
  return algoliaAgent;
752
718
  }
@@ -771,4 +737,4 @@ const DEFAULT_CONNECT_TIMEOUT_NODE = 2000;
771
737
  const DEFAULT_READ_TIMEOUT_NODE = 5000;
772
738
  const DEFAULT_WRITE_TIMEOUT_NODE = 30000;
773
739
 
774
- export { AlgoliaError, ApiError, DEFAULT_CONNECT_TIMEOUT_BROWSER, DEFAULT_CONNECT_TIMEOUT_NODE, DEFAULT_MAX_RETRIES, DEFAULT_READ_TIMEOUT_BROWSER, DEFAULT_READ_TIMEOUT_NODE, DEFAULT_TIMEOUT, DEFAULT_WRITE_TIMEOUT_BROWSER, DEFAULT_WRITE_TIMEOUT_NODE, DeserializationError, ErrorWithStackTrace, RetryError, createAlgoliaAgent, createAuth, createBrowserLocalStorageCache, createEchoRequester, createFallbackableCache, createMemoryCache, createNullCache, createRetryablePromise, createStatefulHost, createTransporter, deserializeFailure, deserializeSuccess, getAlgoliaAgent, isNetworkError, isRetryable, isSuccess, serializeData, serializeHeaders, serializeQueryParameters, serializeUrl, shuffle, stackFrameWithoutCredentials, stackTraceWithoutCredentials };
740
+ export { AlgoliaError, ApiError, DEFAULT_CONNECT_TIMEOUT_BROWSER, DEFAULT_CONNECT_TIMEOUT_NODE, DEFAULT_READ_TIMEOUT_BROWSER, DEFAULT_READ_TIMEOUT_NODE, DEFAULT_WRITE_TIMEOUT_BROWSER, DEFAULT_WRITE_TIMEOUT_NODE, DeserializationError, DetailedApiError, ErrorWithStackTrace, RetryError, createAlgoliaAgent, createAuth, createBrowserLocalStorageCache, createEchoRequester, createFallbackableCache, createIterablePromise, createMemoryCache, createNullCache, createStatefulHost, createTransporter, deserializeFailure, deserializeSuccess, getAlgoliaAgent, isNetworkError, isRetryable, isSuccess, serializeData, serializeHeaders, serializeQueryParameters, serializeUrl, shuffle, stackFrameWithoutCredentials, stackTraceWithoutCredentials };