@algolia/client-common 5.0.0-alpha.11 → 5.0.0-alpha.110

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