@algolia/client-common 5.0.0-alpha.1 → 5.0.0-alpha.100
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{client-common.cjs.js → client-common.cjs} +189 -226
- package/dist/client-common.esm.node.js +188 -222
- package/dist/index.d.ts +9 -9
- package/dist/index.d.ts.map +1 -1
- 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/createFallbackableCache.d.ts.map +1 -1
- package/dist/src/cache/createMemoryCache.d.ts +2 -2
- package/dist/src/cache/createMemoryCache.d.ts.map +1 -1
- package/dist/src/cache/createNullCache.d.ts +2 -2
- package/dist/src/cache/createNullCache.d.ts.map +1 -1
- package/dist/src/cache/index.d.ts +4 -4
- package/dist/src/cache/index.d.ts.map +1 -1
- package/dist/src/constants.d.ts +6 -6
- package/dist/src/constants.d.ts.map +1 -1
- package/dist/src/createAlgoliaAgent.d.ts +2 -2
- package/dist/src/createAlgoliaAgent.d.ts.map +1 -1
- package/dist/src/createAuth.d.ts +5 -5
- package/dist/src/createAuth.d.ts.map +1 -1
- package/dist/src/createEchoRequester.d.ts +6 -6
- package/dist/src/createEchoRequester.d.ts.map +1 -1
- package/dist/src/createIterablePromise.d.ts +13 -0
- package/dist/src/createIterablePromise.d.ts.map +1 -0
- 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/createStatefulHost.d.ts.map +1 -1
- 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/index.d.ts.map +1 -1
- package/dist/src/transporter/responses.d.ts +4 -4
- package/dist/src/transporter/responses.d.ts.map +1 -1
- package/dist/src/transporter/stackTrace.d.ts +3 -3
- package/dist/src/transporter/stackTrace.d.ts.map +1 -1
- package/dist/src/types/{Cache.d.ts → cache.d.ts} +61 -47
- package/dist/src/types/cache.d.ts.map +1 -0
- package/dist/src/types/createClient.d.ts +12 -0
- package/dist/src/types/createClient.d.ts.map +1 -0
- package/dist/src/types/createIterablePromise.d.ts +36 -0
- package/dist/src/types/createIterablePromise.d.ts.map +1 -0
- package/dist/src/types/{Host.d.ts → host.d.ts} +33 -33
- package/dist/src/types/host.d.ts.map +1 -0
- package/dist/src/types/index.d.ts +6 -6
- package/dist/src/types/index.d.ts.map +1 -1
- package/dist/src/types/{Requester.d.ts → requester.d.ts} +66 -66
- package/dist/src/types/requester.d.ts.map +1 -0
- package/dist/src/types/{Transporter.d.ts → transporter.d.ts} +128 -128
- package/dist/src/types/transporter.d.ts.map +1 -0
- package/index.ts +1 -1
- package/package.json +13 -9
- package/src/__tests__/cache/browser-local-storage-cache.test.ts +61 -9
- package/src/__tests__/create-iterable-promise.test.ts +238 -0
- package/src/cache/createBrowserLocalStorageCache.ts +55 -6
- package/src/createEchoRequester.ts +2 -2
- package/src/createIterablePromise.ts +47 -0
- package/src/transporter/createTransporter.ts +4 -1
- package/src/transporter/errors.ts +39 -3
- package/src/transporter/helpers.ts +14 -6
- package/src/types/{Cache.ts → cache.ts} +17 -0
- package/src/types/{CreateClient.ts → createClient.ts} +2 -2
- package/src/types/createIterablePromise.ts +40 -0
- package/src/types/index.ts +6 -6
- package/src/types/{Requester.ts → requester.ts} +1 -1
- package/src/types/{Transporter.ts → transporter.ts} +3 -3
- package/dist/src/createRetryablePromise.d.ts +0 -14
- package/dist/src/createRetryablePromise.d.ts.map +0 -1
- package/dist/src/types/Cache.d.ts.map +0 -1
- package/dist/src/types/CreateClient.d.ts +0 -12
- package/dist/src/types/CreateClient.d.ts.map +0 -1
- package/dist/src/types/CreateRetryablePromise.d.ts +0 -19
- package/dist/src/types/CreateRetryablePromise.d.ts.map +0 -1
- package/dist/src/types/Host.d.ts.map +0 -1
- package/dist/src/types/Requester.d.ts.map +0 -1
- package/dist/src/types/Transporter.d.ts.map +0 -1
- package/src/__tests__/create-retryable-promise.test.ts +0 -86
- package/src/createRetryablePromise.ts +0 -52
- package/src/types/CreateRetryablePromise.ts +0 -21
- /package/src/types/{Host.ts → host.ts} +0 -0
|
@@ -7,11 +7,9 @@ function createAuth(appId, apiKey, authMode = 'WithinHeaders') {
|
|
|
7
7
|
headers() {
|
|
8
8
|
return authMode === 'WithinHeaders' ? credentials : {};
|
|
9
9
|
},
|
|
10
|
-
|
|
11
10
|
queryParameters() {
|
|
12
11
|
return authMode === 'WithinQueryParameters' ? credentials : {};
|
|
13
12
|
}
|
|
14
|
-
|
|
15
13
|
};
|
|
16
14
|
}
|
|
17
15
|
|
|
@@ -22,15 +20,12 @@ function getUrlParams({
|
|
|
22
20
|
}) {
|
|
23
21
|
const algoliaAgent = urlSearchParams.get('x-algolia-agent') || '';
|
|
24
22
|
const searchParams = {};
|
|
25
|
-
|
|
26
23
|
for (const [k, v] of urlSearchParams) {
|
|
27
24
|
if (k === 'x-algolia-agent') {
|
|
28
25
|
continue;
|
|
29
26
|
}
|
|
30
|
-
|
|
31
27
|
searchParams[k] = v;
|
|
32
28
|
}
|
|
33
|
-
|
|
34
29
|
return {
|
|
35
30
|
host,
|
|
36
31
|
algoliaAgent,
|
|
@@ -38,7 +33,6 @@ function getUrlParams({
|
|
|
38
33
|
path: pathname
|
|
39
34
|
};
|
|
40
35
|
}
|
|
41
|
-
|
|
42
36
|
function createEchoRequester({
|
|
43
37
|
getURL,
|
|
44
38
|
status = 200
|
|
@@ -50,11 +44,12 @@ function createEchoRequester({
|
|
|
50
44
|
algoliaAgent,
|
|
51
45
|
path
|
|
52
46
|
} = getUrlParams(getURL(request.url));
|
|
53
|
-
const content = {
|
|
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,95 +58,108 @@ function createEchoRequester({
|
|
|
63
58
|
status
|
|
64
59
|
});
|
|
65
60
|
}
|
|
66
|
-
|
|
67
61
|
return {
|
|
68
62
|
send
|
|
69
63
|
};
|
|
70
64
|
}
|
|
71
65
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
* @param
|
|
78
|
-
* @param
|
|
79
|
-
* @param
|
|
80
|
-
* @param
|
|
81
|
-
* @param createRetryablePromiseOptions.timeout - The function to decide how long to wait between retries.
|
|
66
|
+
/**
|
|
67
|
+
* Helper: Returns the promise of a given `func` to iterate on, based on a given `validate` condition.
|
|
68
|
+
*
|
|
69
|
+
* @param createIterator - The createIterator options.
|
|
70
|
+
* @param createIterator.func - The function to run, which returns a promise.
|
|
71
|
+
* @param createIterator.validate - The validator function. It receives the resolved return of `func`.
|
|
72
|
+
* @param createIterator.aggregator - The function that runs right after the `func` method has been executed, allows you to do anything with the response before `validate`.
|
|
73
|
+
* @param createIterator.error - The `validate` condition to throw an error, and its message.
|
|
74
|
+
* @param createIterator.timeout - The function to decide how long to wait between iterations.
|
|
82
75
|
*/
|
|
83
|
-
|
|
84
|
-
function createRetryablePromise({
|
|
76
|
+
function createIterablePromise({
|
|
85
77
|
func,
|
|
86
78
|
validate,
|
|
87
|
-
|
|
88
|
-
|
|
79
|
+
aggregator,
|
|
80
|
+
error,
|
|
81
|
+
timeout = () => 0
|
|
89
82
|
}) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const retry = () => {
|
|
83
|
+
const retry = previousResponse => {
|
|
93
84
|
return new Promise((resolve, reject) => {
|
|
94
|
-
func().then(response => {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
reject(new Error(`The maximum number of retries exceeded. (${retryCount + 1}/${maxRetries})`));
|
|
101
|
-
} else {
|
|
102
|
-
retryCount += 1;
|
|
103
|
-
setTimeout(() => {
|
|
104
|
-
retry().then(resolve).catch(reject);
|
|
105
|
-
}, timeout(retryCount));
|
|
85
|
+
func(previousResponse).then(response => {
|
|
86
|
+
if (aggregator) {
|
|
87
|
+
aggregator(response);
|
|
88
|
+
}
|
|
89
|
+
if (validate(response)) {
|
|
90
|
+
return resolve(response);
|
|
106
91
|
}
|
|
107
|
-
|
|
108
|
-
|
|
92
|
+
if (error && error.validate(response)) {
|
|
93
|
+
return reject(new Error(error.message(response)));
|
|
94
|
+
}
|
|
95
|
+
return setTimeout(() => {
|
|
96
|
+
retry(response).then(resolve).catch(reject);
|
|
97
|
+
}, timeout());
|
|
98
|
+
}).catch(err => {
|
|
99
|
+
reject(err);
|
|
109
100
|
});
|
|
110
101
|
});
|
|
111
102
|
};
|
|
112
|
-
|
|
113
103
|
return retry();
|
|
114
104
|
}
|
|
115
105
|
|
|
116
106
|
function createBrowserLocalStorageCache(options) {
|
|
117
|
-
let storage;
|
|
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,51 +486,49 @@ function createTransporter({
|
|
|
488
486
|
});
|
|
489
487
|
}));
|
|
490
488
|
const hostsUp = statefulHosts.filter(host => host.isUp());
|
|
491
|
-
const hostsTimedOut = statefulHosts.filter(host => host.isTimedOut());
|
|
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
524
|
const queryParameters = {
|
|
530
|
-
'x-algolia-agent': algoliaAgent.value,
|
|
531
525
|
...baseQueryParameters,
|
|
532
526
|
...request.queryParameters,
|
|
533
527
|
...dataQueryParameters
|
|
534
528
|
};
|
|
535
|
-
|
|
529
|
+
if (algoliaAgent.value) {
|
|
530
|
+
queryParameters['x-algolia-agent'] = algoliaAgent.value;
|
|
531
|
+
}
|
|
536
532
|
if (requestOptions && requestOptions.queryParameters) {
|
|
537
533
|
for (const key of Object.keys(requestOptions.queryParameters)) {
|
|
538
534
|
// We want to keep `undefined` and `null` values,
|
|
@@ -545,25 +541,19 @@ function createTransporter({
|
|
|
545
541
|
}
|
|
546
542
|
}
|
|
547
543
|
}
|
|
548
|
-
|
|
549
544
|
let timeoutsCount = 0;
|
|
550
|
-
|
|
551
545
|
const retry = async (retryableHosts, getTimeout) => {
|
|
552
|
-
/**
|
|
553
|
-
* We iterate on each host, until there is no host left.
|
|
546
|
+
/**
|
|
547
|
+
* We iterate on each host, until there is no host left.
|
|
554
548
|
*/
|
|
555
549
|
const host = retryableHosts.pop();
|
|
556
|
-
|
|
557
550
|
if (host === undefined) {
|
|
558
551
|
throw new RetryError(stackTraceWithoutCredentials(stackTrace));
|
|
559
552
|
}
|
|
560
|
-
|
|
561
553
|
let responseTimeout = requestOptions.timeout;
|
|
562
|
-
|
|
563
554
|
if (responseTimeout === undefined) {
|
|
564
555
|
responseTimeout = isRead ? timeouts.read : timeouts.write;
|
|
565
556
|
}
|
|
566
|
-
|
|
567
557
|
const payload = {
|
|
568
558
|
data,
|
|
569
559
|
headers,
|
|
@@ -572,12 +562,11 @@ function createTransporter({
|
|
|
572
562
|
connectTimeout: getTimeout(timeoutsCount, timeouts.connect),
|
|
573
563
|
responseTimeout: getTimeout(timeoutsCount, responseTimeout)
|
|
574
564
|
};
|
|
575
|
-
/**
|
|
576
|
-
* The stackFrame is pushed to the stackTrace so we
|
|
577
|
-
* can have information about onRetry and onFailure
|
|
578
|
-
* decisions.
|
|
565
|
+
/**
|
|
566
|
+
* The stackFrame is pushed to the stackTrace so we
|
|
567
|
+
* can have information about onRetry and onFailure
|
|
568
|
+
* decisions.
|
|
579
569
|
*/
|
|
580
|
-
|
|
581
570
|
const pushToStackTrace = response => {
|
|
582
571
|
const stackFrame = {
|
|
583
572
|
request: payload,
|
|
@@ -588,102 +577,85 @@ function createTransporter({
|
|
|
588
577
|
stackTrace.push(stackFrame);
|
|
589
578
|
return stackFrame;
|
|
590
579
|
};
|
|
591
|
-
|
|
592
580
|
const response = await requester.send(payload);
|
|
593
|
-
|
|
594
581
|
if (isRetryable(response)) {
|
|
595
|
-
const stackFrame = pushToStackTrace(response);
|
|
596
|
-
|
|
582
|
+
const stackFrame = pushToStackTrace(response);
|
|
583
|
+
// If response is a timeout, we increase the number of timeouts so we can increase the timeout later.
|
|
597
584
|
if (response.isTimedOut) {
|
|
598
585
|
timeoutsCount++;
|
|
599
586
|
}
|
|
600
|
-
/**
|
|
601
|
-
* Failures are individually sent to the logger, allowing
|
|
602
|
-
* the end user to debug / store stack frames even
|
|
603
|
-
* when a retry error does not happen.
|
|
587
|
+
/**
|
|
588
|
+
* Failures are individually sent to the logger, allowing
|
|
589
|
+
* the end user to debug / store stack frames even
|
|
590
|
+
* when a retry error does not happen.
|
|
604
591
|
*/
|
|
605
592
|
// eslint-disable-next-line no-console -- this will be fixed by exposing a `logger` to the transporter
|
|
606
|
-
|
|
607
|
-
|
|
608
593
|
console.log('Retryable failure', stackFrameWithoutCredentials(stackFrame));
|
|
609
|
-
/**
|
|
610
|
-
* We also store the state of the host in failure cases. If the host, is
|
|
611
|
-
* down it will remain down for the next 2 minutes. In a timeout situation,
|
|
612
|
-
* this host will be added end of the list of hosts on the next request.
|
|
594
|
+
/**
|
|
595
|
+
* We also store the state of the host in failure cases. If the host, is
|
|
596
|
+
* down it will remain down for the next 2 minutes. In a timeout situation,
|
|
597
|
+
* this host will be added end of the list of hosts on the next request.
|
|
613
598
|
*/
|
|
614
|
-
|
|
615
599
|
await hostsCache.set(host, createStatefulHost(host, response.isTimedOut ? 'timed out' : 'down'));
|
|
616
600
|
return retry(retryableHosts, getTimeout);
|
|
617
601
|
}
|
|
618
|
-
|
|
619
602
|
if (isSuccess(response)) {
|
|
620
603
|
return deserializeSuccess(response);
|
|
621
604
|
}
|
|
622
|
-
|
|
623
605
|
pushToStackTrace(response);
|
|
624
606
|
throw deserializeFailure(response, stackTrace);
|
|
625
607
|
};
|
|
626
|
-
/**
|
|
627
|
-
* Finally, for each retryable host perform request until we got a non
|
|
628
|
-
* retryable response. Some notes here:
|
|
629
|
-
*
|
|
630
|
-
* 1. The reverse here is applied so we can apply a `pop` later on => more performant.
|
|
631
|
-
* 2. We also get from the retryable options a timeout multiplier that is tailored
|
|
632
|
-
* for the current context.
|
|
608
|
+
/**
|
|
609
|
+
* Finally, for each retryable host perform request until we got a non
|
|
610
|
+
* retryable response. Some notes here:
|
|
611
|
+
*
|
|
612
|
+
* 1. The reverse here is applied so we can apply a `pop` later on => more performant.
|
|
613
|
+
* 2. We also get from the retryable options a timeout multiplier that is tailored
|
|
614
|
+
* for the current context.
|
|
633
615
|
*/
|
|
634
|
-
|
|
635
|
-
|
|
636
616
|
const compatibleHosts = hosts.filter(host => host.accept === 'readWrite' || (isRead ? host.accept === 'read' : host.accept === 'write'));
|
|
637
617
|
const options = await createRetryableOptions(compatibleHosts);
|
|
638
618
|
return retry([...options.hosts].reverse(), options.getTimeout);
|
|
639
619
|
}
|
|
640
|
-
|
|
641
620
|
function createRequest(request, requestOptions = {}) {
|
|
642
|
-
/**
|
|
643
|
-
* A read request is either a `GET` request, or a request that we make
|
|
644
|
-
* via the `read` transporter (e.g. `search`).
|
|
621
|
+
/**
|
|
622
|
+
* A read request is either a `GET` request, or a request that we make
|
|
623
|
+
* via the `read` transporter (e.g. `search`).
|
|
645
624
|
*/
|
|
646
625
|
const isRead = request.useReadTransporter || request.method === 'GET';
|
|
647
|
-
|
|
648
626
|
if (!isRead) {
|
|
649
|
-
/**
|
|
650
|
-
* On write requests, no cache mechanisms are applied, and we
|
|
651
|
-
* proxy the request immediately to the requester.
|
|
627
|
+
/**
|
|
628
|
+
* On write requests, no cache mechanisms are applied, and we
|
|
629
|
+
* proxy the request immediately to the requester.
|
|
652
630
|
*/
|
|
653
631
|
return retryableRequest(request, requestOptions, isRead);
|
|
654
632
|
}
|
|
655
|
-
|
|
656
633
|
const createRetryableRequest = () => {
|
|
657
|
-
/**
|
|
658
|
-
* Then, we prepare a function factory that contains the construction of
|
|
659
|
-
* the retryable request. At this point, we may *not* perform the actual
|
|
660
|
-
* request. But we want to have the function factory ready.
|
|
634
|
+
/**
|
|
635
|
+
* Then, we prepare a function factory that contains the construction of
|
|
636
|
+
* the retryable request. At this point, we may *not* perform the actual
|
|
637
|
+
* request. But we want to have the function factory ready.
|
|
661
638
|
*/
|
|
662
639
|
return retryableRequest(request, requestOptions);
|
|
663
640
|
};
|
|
664
|
-
/**
|
|
665
|
-
* Once we have the function factory ready, we need to determine of the
|
|
666
|
-
* request is "cacheable" - should be cached. Note that, once again,
|
|
667
|
-
* the user can force this option.
|
|
641
|
+
/**
|
|
642
|
+
* Once we have the function factory ready, we need to determine of the
|
|
643
|
+
* request is "cacheable" - should be cached. Note that, once again,
|
|
644
|
+
* the user can force this option.
|
|
668
645
|
*/
|
|
669
|
-
|
|
670
|
-
|
|
671
646
|
const cacheable = requestOptions.cacheable || request.cacheable;
|
|
672
|
-
/**
|
|
673
|
-
* If is not "cacheable", we immediately trigger the retryable request, no
|
|
674
|
-
* need to check cache implementations.
|
|
647
|
+
/**
|
|
648
|
+
* If is not "cacheable", we immediately trigger the retryable request, no
|
|
649
|
+
* need to check cache implementations.
|
|
675
650
|
*/
|
|
676
|
-
|
|
677
651
|
if (cacheable !== true) {
|
|
678
652
|
return createRetryableRequest();
|
|
679
653
|
}
|
|
680
|
-
/**
|
|
681
|
-
* If the request is "cacheable", we need to first compute the key to ask
|
|
682
|
-
* the cache implementations if this request is on progress or if the
|
|
683
|
-
* response already exists on the cache.
|
|
654
|
+
/**
|
|
655
|
+
* If the request is "cacheable", we need to first compute the key to ask
|
|
656
|
+
* the cache implementations if this request is on progress or if the
|
|
657
|
+
* response already exists on the cache.
|
|
684
658
|
*/
|
|
685
|
-
|
|
686
|
-
|
|
687
659
|
const key = {
|
|
688
660
|
request,
|
|
689
661
|
requestOptions,
|
|
@@ -692,33 +664,31 @@ function createTransporter({
|
|
|
692
664
|
headers: baseHeaders
|
|
693
665
|
}
|
|
694
666
|
};
|
|
695
|
-
/**
|
|
696
|
-
* With the computed key, we first ask the responses cache
|
|
697
|
-
* implementation if this request was been resolved before.
|
|
667
|
+
/**
|
|
668
|
+
* With the computed key, we first ask the responses cache
|
|
669
|
+
* implementation if this request was been resolved before.
|
|
698
670
|
*/
|
|
699
|
-
|
|
700
671
|
return responsesCache.get(key, () => {
|
|
701
|
-
/**
|
|
702
|
-
* If the request has never resolved before, we actually ask if there
|
|
703
|
-
* is a current request with the same key on progress.
|
|
672
|
+
/**
|
|
673
|
+
* If the request has never resolved before, we actually ask if there
|
|
674
|
+
* is a current request with the same key on progress.
|
|
704
675
|
*/
|
|
705
676
|
return requestsCache.get(key, () =>
|
|
706
|
-
/**
|
|
707
|
-
* Finally, if there is no request in progress with the same key,
|
|
708
|
-
* this `createRetryableRequest()` will actually trigger the
|
|
709
|
-
* retryable request.
|
|
677
|
+
/**
|
|
678
|
+
* Finally, if there is no request in progress with the same key,
|
|
679
|
+
* this `createRetryableRequest()` will actually trigger the
|
|
680
|
+
* retryable request.
|
|
710
681
|
*/
|
|
711
682
|
requestsCache.set(key, createRetryableRequest()).then(response => Promise.all([requestsCache.delete(key), response]), err => Promise.all([requestsCache.delete(key), Promise.reject(err)])).then(([_, response]) => response));
|
|
712
683
|
}, {
|
|
713
|
-
/**
|
|
714
|
-
* Of course, once we get this response back from the server, we
|
|
715
|
-
* tell response cache to actually store the received response
|
|
716
|
-
* to be used later.
|
|
684
|
+
/**
|
|
685
|
+
* Of course, once we get this response back from the server, we
|
|
686
|
+
* tell response cache to actually store the received response
|
|
687
|
+
* to be used later.
|
|
717
688
|
*/
|
|
718
689
|
miss: response => responsesCache.set(key, response)
|
|
719
690
|
});
|
|
720
691
|
}
|
|
721
|
-
|
|
722
692
|
return {
|
|
723
693
|
hostsCache,
|
|
724
694
|
requester,
|
|
@@ -736,17 +706,13 @@ function createTransporter({
|
|
|
736
706
|
function createAlgoliaAgent(version) {
|
|
737
707
|
const algoliaAgent = {
|
|
738
708
|
value: `Algolia for JavaScript (${version})`,
|
|
739
|
-
|
|
740
709
|
add(options) {
|
|
741
710
|
const addedAlgoliaAgent = `; ${options.segment}${options.version !== undefined ? ` (${options.version})` : ''}`;
|
|
742
|
-
|
|
743
711
|
if (algoliaAgent.value.indexOf(addedAlgoliaAgent) === -1) {
|
|
744
712
|
algoliaAgent.value = `${algoliaAgent.value}${addedAlgoliaAgent}`;
|
|
745
713
|
}
|
|
746
|
-
|
|
747
714
|
return algoliaAgent;
|
|
748
715
|
}
|
|
749
|
-
|
|
750
716
|
};
|
|
751
717
|
return algoliaAgent;
|
|
752
718
|
}
|
|
@@ -771,4 +737,4 @@ const DEFAULT_CONNECT_TIMEOUT_NODE = 2000;
|
|
|
771
737
|
const DEFAULT_READ_TIMEOUT_NODE = 5000;
|
|
772
738
|
const DEFAULT_WRITE_TIMEOUT_NODE = 30000;
|
|
773
739
|
|
|
774
|
-
export { AlgoliaError, ApiError, DEFAULT_CONNECT_TIMEOUT_BROWSER, DEFAULT_CONNECT_TIMEOUT_NODE,
|
|
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 };
|