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