@algolia/client-common 5.0.0-alpha.10 → 5.0.0-alpha.101
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} +168 -205
- package/dist/client-common.esm.node.js +168 -204
- package/dist/index.d.ts +9 -9
- package/dist/src/cache/createBrowserLocalStorageCache.d.ts +2 -2
- package/dist/src/cache/createBrowserLocalStorageCache.d.ts.map +1 -1
- 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/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 +60 -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 +11 -8
- package/src/__tests__/cache/browser-local-storage-cache.test.ts +61 -9
- package/src/cache/createBrowserLocalStorageCache.ts +55 -6
- package/src/createEchoRequester.ts +2 -2
- package/src/transporter/errors.ts +39 -3
- package/src/transporter/helpers.ts +14 -6
- package/src/types/cache.ts +17 -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 = {
|
|
47
|
+
const content = {
|
|
48
|
+
...request,
|
|
54
49
|
data: request.data ? JSON.parse(request.data) : undefined,
|
|
55
50
|
path,
|
|
56
51
|
host,
|
|
57
|
-
algoliaAgent:
|
|
52
|
+
algoliaAgent: encodeURIComponent(algoliaAgent),
|
|
58
53
|
searchParams
|
|
59
54
|
};
|
|
60
55
|
return Promise.resolve({
|
|
@@ -63,21 +58,20 @@ function createEchoRequester({
|
|
|
63
58
|
status
|
|
64
59
|
});
|
|
65
60
|
}
|
|
66
|
-
|
|
67
61
|
return {
|
|
68
62
|
send
|
|
69
63
|
};
|
|
70
64
|
}
|
|
71
65
|
|
|
72
|
-
/**
|
|
73
|
-
* Helper: Returns the promise of a given `func` to iterate on, based on a given `validate` condition.
|
|
74
|
-
*
|
|
75
|
-
* @param createIterator - The createIterator options.
|
|
76
|
-
* @param createIterator.func - The function to run, which returns a promise.
|
|
77
|
-
* @param createIterator.validate - The validator function. It receives the resolved return of `func`.
|
|
78
|
-
* @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`.
|
|
79
|
-
* @param createIterator.error - The `validate` condition to throw an error, and its message.
|
|
80
|
-
* @param createIterator.timeout - The function to decide how long to wait between iterations.
|
|
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.
|
|
81
75
|
*/
|
|
82
76
|
function createIterablePromise({
|
|
83
77
|
func,
|
|
@@ -92,15 +86,12 @@ function createIterablePromise({
|
|
|
92
86
|
if (aggregator) {
|
|
93
87
|
aggregator(response);
|
|
94
88
|
}
|
|
95
|
-
|
|
96
89
|
if (validate(response)) {
|
|
97
90
|
return resolve(response);
|
|
98
91
|
}
|
|
99
|
-
|
|
100
92
|
if (error && error.validate(response)) {
|
|
101
93
|
return reject(new Error(error.message(response)));
|
|
102
94
|
}
|
|
103
|
-
|
|
104
95
|
return setTimeout(() => {
|
|
105
96
|
retry(response).then(resolve).catch(reject);
|
|
106
97
|
}, timeout());
|
|
@@ -109,49 +100,66 @@ function createIterablePromise({
|
|
|
109
100
|
});
|
|
110
101
|
});
|
|
111
102
|
};
|
|
112
|
-
|
|
113
103
|
return retry();
|
|
114
104
|
}
|
|
115
105
|
|
|
116
106
|
function createBrowserLocalStorageCache(options) {
|
|
117
|
-
let storage;
|
|
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
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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)] =
|
|
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
|
-
|
|
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);
|
|
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,
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
430
|
-
|
|
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 {
|
|
465
|
-
|
|
466
|
-
|
|
459
|
+
return {
|
|
460
|
+
...stackFrame,
|
|
461
|
+
request: {
|
|
462
|
+
...stackFrame.request,
|
|
463
|
+
headers: {
|
|
464
|
+
...stackFrame.request.headers,
|
|
467
465
|
...modifiedHeaders
|
|
468
466
|
}
|
|
469
467
|
}
|
|
@@ -488,53 +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());
|
|
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);
|
|
525
|
-
|
|
526
|
-
const dataQueryParameters = request.method === 'GET' ? {
|
|
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
|
-
const queryParameters = {
|
|
524
|
+
const queryParameters = {
|
|
525
|
+
...baseQueryParameters,
|
|
530
526
|
...request.queryParameters,
|
|
531
527
|
...dataQueryParameters
|
|
532
528
|
};
|
|
533
|
-
|
|
534
529
|
if (algoliaAgent.value) {
|
|
535
530
|
queryParameters['x-algolia-agent'] = algoliaAgent.value;
|
|
536
531
|
}
|
|
537
|
-
|
|
538
532
|
if (requestOptions && requestOptions.queryParameters) {
|
|
539
533
|
for (const key of Object.keys(requestOptions.queryParameters)) {
|
|
540
534
|
// We want to keep `undefined` and `null` values,
|
|
@@ -547,25 +541,19 @@ function createTransporter({
|
|
|
547
541
|
}
|
|
548
542
|
}
|
|
549
543
|
}
|
|
550
|
-
|
|
551
544
|
let timeoutsCount = 0;
|
|
552
|
-
|
|
553
545
|
const retry = async (retryableHosts, getTimeout) => {
|
|
554
|
-
/**
|
|
555
|
-
* We iterate on each host, until there is no host left.
|
|
546
|
+
/**
|
|
547
|
+
* We iterate on each host, until there is no host left.
|
|
556
548
|
*/
|
|
557
549
|
const host = retryableHosts.pop();
|
|
558
|
-
|
|
559
550
|
if (host === undefined) {
|
|
560
551
|
throw new RetryError(stackTraceWithoutCredentials(stackTrace));
|
|
561
552
|
}
|
|
562
|
-
|
|
563
553
|
let responseTimeout = requestOptions.timeout;
|
|
564
|
-
|
|
565
554
|
if (responseTimeout === undefined) {
|
|
566
555
|
responseTimeout = isRead ? timeouts.read : timeouts.write;
|
|
567
556
|
}
|
|
568
|
-
|
|
569
557
|
const payload = {
|
|
570
558
|
data,
|
|
571
559
|
headers,
|
|
@@ -574,12 +562,11 @@ function createTransporter({
|
|
|
574
562
|
connectTimeout: getTimeout(timeoutsCount, timeouts.connect),
|
|
575
563
|
responseTimeout: getTimeout(timeoutsCount, responseTimeout)
|
|
576
564
|
};
|
|
577
|
-
/**
|
|
578
|
-
* The stackFrame is pushed to the stackTrace so we
|
|
579
|
-
* can have information about onRetry and onFailure
|
|
580
|
-
* decisions.
|
|
565
|
+
/**
|
|
566
|
+
* The stackFrame is pushed to the stackTrace so we
|
|
567
|
+
* can have information about onRetry and onFailure
|
|
568
|
+
* decisions.
|
|
581
569
|
*/
|
|
582
|
-
|
|
583
570
|
const pushToStackTrace = response => {
|
|
584
571
|
const stackFrame = {
|
|
585
572
|
request: payload,
|
|
@@ -590,102 +577,85 @@ function createTransporter({
|
|
|
590
577
|
stackTrace.push(stackFrame);
|
|
591
578
|
return stackFrame;
|
|
592
579
|
};
|
|
593
|
-
|
|
594
580
|
const response = await requester.send(payload);
|
|
595
|
-
|
|
596
581
|
if (isRetryable(response)) {
|
|
597
|
-
const stackFrame = pushToStackTrace(response);
|
|
598
|
-
|
|
582
|
+
const stackFrame = pushToStackTrace(response);
|
|
583
|
+
// If response is a timeout, we increase the number of timeouts so we can increase the timeout later.
|
|
599
584
|
if (response.isTimedOut) {
|
|
600
585
|
timeoutsCount++;
|
|
601
586
|
}
|
|
602
|
-
/**
|
|
603
|
-
* Failures are individually sent to the logger, allowing
|
|
604
|
-
* the end user to debug / store stack frames even
|
|
605
|
-
* 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.
|
|
606
591
|
*/
|
|
607
592
|
// eslint-disable-next-line no-console -- this will be fixed by exposing a `logger` to the transporter
|
|
608
|
-
|
|
609
|
-
|
|
610
593
|
console.log('Retryable failure', stackFrameWithoutCredentials(stackFrame));
|
|
611
|
-
/**
|
|
612
|
-
* We also store the state of the host in failure cases. If the host, is
|
|
613
|
-
* down it will remain down for the next 2 minutes. In a timeout situation,
|
|
614
|
-
* 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.
|
|
615
598
|
*/
|
|
616
|
-
|
|
617
599
|
await hostsCache.set(host, createStatefulHost(host, response.isTimedOut ? 'timed out' : 'down'));
|
|
618
600
|
return retry(retryableHosts, getTimeout);
|
|
619
601
|
}
|
|
620
|
-
|
|
621
602
|
if (isSuccess(response)) {
|
|
622
603
|
return deserializeSuccess(response);
|
|
623
604
|
}
|
|
624
|
-
|
|
625
605
|
pushToStackTrace(response);
|
|
626
606
|
throw deserializeFailure(response, stackTrace);
|
|
627
607
|
};
|
|
628
|
-
/**
|
|
629
|
-
* Finally, for each retryable host perform request until we got a non
|
|
630
|
-
* retryable response. Some notes here:
|
|
631
|
-
*
|
|
632
|
-
* 1. The reverse here is applied so we can apply a `pop` later on => more performant.
|
|
633
|
-
* 2. We also get from the retryable options a timeout multiplier that is tailored
|
|
634
|
-
* 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.
|
|
635
615
|
*/
|
|
636
|
-
|
|
637
|
-
|
|
638
616
|
const compatibleHosts = hosts.filter(host => host.accept === 'readWrite' || (isRead ? host.accept === 'read' : host.accept === 'write'));
|
|
639
617
|
const options = await createRetryableOptions(compatibleHosts);
|
|
640
618
|
return retry([...options.hosts].reverse(), options.getTimeout);
|
|
641
619
|
}
|
|
642
|
-
|
|
643
620
|
function createRequest(request, requestOptions = {}) {
|
|
644
|
-
/**
|
|
645
|
-
* A read request is either a `GET` request, or a request that we make
|
|
646
|
-
* 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`).
|
|
647
624
|
*/
|
|
648
625
|
const isRead = request.useReadTransporter || request.method === 'GET';
|
|
649
|
-
|
|
650
626
|
if (!isRead) {
|
|
651
|
-
/**
|
|
652
|
-
* On write requests, no cache mechanisms are applied, and we
|
|
653
|
-
* 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.
|
|
654
630
|
*/
|
|
655
631
|
return retryableRequest(request, requestOptions, isRead);
|
|
656
632
|
}
|
|
657
|
-
|
|
658
633
|
const createRetryableRequest = () => {
|
|
659
|
-
/**
|
|
660
|
-
* Then, we prepare a function factory that contains the construction of
|
|
661
|
-
* the retryable request. At this point, we may *not* perform the actual
|
|
662
|
-
* 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.
|
|
663
638
|
*/
|
|
664
639
|
return retryableRequest(request, requestOptions);
|
|
665
640
|
};
|
|
666
|
-
/**
|
|
667
|
-
* Once we have the function factory ready, we need to determine of the
|
|
668
|
-
* request is "cacheable" - should be cached. Note that, once again,
|
|
669
|
-
* 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.
|
|
670
645
|
*/
|
|
671
|
-
|
|
672
|
-
|
|
673
646
|
const cacheable = requestOptions.cacheable || request.cacheable;
|
|
674
|
-
/**
|
|
675
|
-
* If is not "cacheable", we immediately trigger the retryable request, no
|
|
676
|
-
* need to check cache implementations.
|
|
647
|
+
/**
|
|
648
|
+
* If is not "cacheable", we immediately trigger the retryable request, no
|
|
649
|
+
* need to check cache implementations.
|
|
677
650
|
*/
|
|
678
|
-
|
|
679
651
|
if (cacheable !== true) {
|
|
680
652
|
return createRetryableRequest();
|
|
681
653
|
}
|
|
682
|
-
/**
|
|
683
|
-
* If the request is "cacheable", we need to first compute the key to ask
|
|
684
|
-
* the cache implementations if this request is on progress or if the
|
|
685
|
-
* 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.
|
|
686
658
|
*/
|
|
687
|
-
|
|
688
|
-
|
|
689
659
|
const key = {
|
|
690
660
|
request,
|
|
691
661
|
requestOptions,
|
|
@@ -694,33 +664,31 @@ function createTransporter({
|
|
|
694
664
|
headers: baseHeaders
|
|
695
665
|
}
|
|
696
666
|
};
|
|
697
|
-
/**
|
|
698
|
-
* With the computed key, we first ask the responses cache
|
|
699
|
-
* 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.
|
|
700
670
|
*/
|
|
701
|
-
|
|
702
671
|
return responsesCache.get(key, () => {
|
|
703
|
-
/**
|
|
704
|
-
* If the request has never resolved before, we actually ask if there
|
|
705
|
-
* 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.
|
|
706
675
|
*/
|
|
707
676
|
return requestsCache.get(key, () =>
|
|
708
|
-
/**
|
|
709
|
-
* Finally, if there is no request in progress with the same key,
|
|
710
|
-
* this `createRetryableRequest()` will actually trigger the
|
|
711
|
-
* 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.
|
|
712
681
|
*/
|
|
713
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));
|
|
714
683
|
}, {
|
|
715
|
-
/**
|
|
716
|
-
* Of course, once we get this response back from the server, we
|
|
717
|
-
* tell response cache to actually store the received response
|
|
718
|
-
* 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.
|
|
719
688
|
*/
|
|
720
689
|
miss: response => responsesCache.set(key, response)
|
|
721
690
|
});
|
|
722
691
|
}
|
|
723
|
-
|
|
724
692
|
return {
|
|
725
693
|
hostsCache,
|
|
726
694
|
requester,
|
|
@@ -738,17 +706,13 @@ function createTransporter({
|
|
|
738
706
|
function createAlgoliaAgent(version) {
|
|
739
707
|
const algoliaAgent = {
|
|
740
708
|
value: `Algolia for JavaScript (${version})`,
|
|
741
|
-
|
|
742
709
|
add(options) {
|
|
743
710
|
const addedAlgoliaAgent = `; ${options.segment}${options.version !== undefined ? ` (${options.version})` : ''}`;
|
|
744
|
-
|
|
745
711
|
if (algoliaAgent.value.indexOf(addedAlgoliaAgent) === -1) {
|
|
746
712
|
algoliaAgent.value = `${algoliaAgent.value}${addedAlgoliaAgent}`;
|
|
747
713
|
}
|
|
748
|
-
|
|
749
714
|
return algoliaAgent;
|
|
750
715
|
}
|
|
751
|
-
|
|
752
716
|
};
|
|
753
717
|
return algoliaAgent;
|
|
754
718
|
}
|
|
@@ -773,4 +737,4 @@ const DEFAULT_CONNECT_TIMEOUT_NODE = 2000;
|
|
|
773
737
|
const DEFAULT_READ_TIMEOUT_NODE = 5000;
|
|
774
738
|
const DEFAULT_WRITE_TIMEOUT_NODE = 30000;
|
|
775
739
|
|
|
776
|
-
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, 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 };
|
|
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 };
|