@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
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { createIterablePromise } from '../createIterablePromise';
|
|
2
|
+
|
|
3
|
+
describe('createIterablePromise', () => {
|
|
4
|
+
describe('func', () => {
|
|
5
|
+
it('provides the `previousResponse` parameter', async () => {
|
|
6
|
+
const responses: Array<string | undefined> = [];
|
|
7
|
+
const promise = createIterablePromise<string | undefined>({
|
|
8
|
+
func: (previousResponse) => {
|
|
9
|
+
return new Promise((resolve) => {
|
|
10
|
+
resolve(previousResponse === undefined ? 'yes' : 'no');
|
|
11
|
+
});
|
|
12
|
+
},
|
|
13
|
+
validate: () => responses.length === 3,
|
|
14
|
+
aggregator: (response) => responses.push(response),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
await expect(promise).resolves.toEqual('no');
|
|
18
|
+
expect(responses).toEqual(['yes', 'no', 'no']);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe('validate', () => {
|
|
23
|
+
it('iterates on a `func` until `validate` is met', async () => {
|
|
24
|
+
let calls = 0;
|
|
25
|
+
const promise = createIterablePromise({
|
|
26
|
+
func: () => {
|
|
27
|
+
return new Promise((resolve) => {
|
|
28
|
+
calls += 1;
|
|
29
|
+
resolve(`success #${calls}`);
|
|
30
|
+
});
|
|
31
|
+
},
|
|
32
|
+
validate: () => calls >= 3,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
await expect(promise).resolves.toEqual('success #3');
|
|
36
|
+
expect(calls).toBe(3);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('forward the response of the `func`', async () => {
|
|
40
|
+
let calls = 0;
|
|
41
|
+
const promise = createIterablePromise<number>({
|
|
42
|
+
func: () => {
|
|
43
|
+
return new Promise((resolve) => {
|
|
44
|
+
calls += 1;
|
|
45
|
+
resolve(calls);
|
|
46
|
+
});
|
|
47
|
+
},
|
|
48
|
+
validate: (response) => response >= 3,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
await expect(promise).resolves.toEqual(3);
|
|
52
|
+
expect(calls).toBe(3);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe('aggregator', () => {
|
|
57
|
+
it('is called before iterating', async () => {
|
|
58
|
+
let calls = 0;
|
|
59
|
+
let count = 0;
|
|
60
|
+
const promise = createIterablePromise({
|
|
61
|
+
func: () => {
|
|
62
|
+
return new Promise((resolve) => {
|
|
63
|
+
calls += 1;
|
|
64
|
+
resolve(`success #${calls}`);
|
|
65
|
+
});
|
|
66
|
+
},
|
|
67
|
+
validate: () => calls >= 3,
|
|
68
|
+
aggregator: () => (count += 3),
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
await expect(promise).resolves.toEqual('success #3');
|
|
72
|
+
expect(calls).toBe(3);
|
|
73
|
+
expect(count).toBe(3 * 3);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('forward the response of the `func`', async () => {
|
|
77
|
+
let calls = 0;
|
|
78
|
+
const responses: string[] = [];
|
|
79
|
+
const promise = createIterablePromise<string>({
|
|
80
|
+
func: () => {
|
|
81
|
+
return new Promise((resolve) => {
|
|
82
|
+
calls += 1;
|
|
83
|
+
resolve(`success #${calls}`);
|
|
84
|
+
});
|
|
85
|
+
},
|
|
86
|
+
validate: () => calls >= 3,
|
|
87
|
+
aggregator: (response) => {
|
|
88
|
+
responses.push(response);
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
await expect(promise).resolves.toEqual('success #3');
|
|
93
|
+
expect(calls).toBe(3);
|
|
94
|
+
expect(responses).toEqual(['success #1', 'success #2', 'success #3']);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
describe('timeout', () => {
|
|
99
|
+
it('defaults to no timeout (0)', async () => {
|
|
100
|
+
let calls = 0;
|
|
101
|
+
const before = Date.now();
|
|
102
|
+
const promise = createIterablePromise({
|
|
103
|
+
func: () => {
|
|
104
|
+
return new Promise((resolve) => {
|
|
105
|
+
calls += 1;
|
|
106
|
+
resolve(`success #${calls}`);
|
|
107
|
+
});
|
|
108
|
+
},
|
|
109
|
+
validate: () => calls >= 2,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
await expect(promise).resolves.toEqual('success #2');
|
|
113
|
+
|
|
114
|
+
expect(Date.now() - before).toBeGreaterThanOrEqual(0);
|
|
115
|
+
expect(Date.now() - before).toBeLessThanOrEqual(10);
|
|
116
|
+
expect(calls).toBe(2);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('waits before calling the `func` again', async () => {
|
|
120
|
+
let calls = 0;
|
|
121
|
+
const before = Date.now();
|
|
122
|
+
const promise = createIterablePromise({
|
|
123
|
+
func: () => {
|
|
124
|
+
return new Promise((resolve) => {
|
|
125
|
+
calls += 1;
|
|
126
|
+
resolve(`success #${calls}`);
|
|
127
|
+
});
|
|
128
|
+
},
|
|
129
|
+
validate: () => calls >= 2,
|
|
130
|
+
timeout: () => 2000,
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
await expect(promise).resolves.toEqual('success #2');
|
|
134
|
+
|
|
135
|
+
expect(Date.now() - before).toBeGreaterThanOrEqual(2000);
|
|
136
|
+
expect(Date.now() - before).toBeLessThanOrEqual(2010);
|
|
137
|
+
expect(calls).toBe(2);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
describe('error', () => {
|
|
142
|
+
it('gets the rejection of the given promise via reject', async () => {
|
|
143
|
+
let calls = 0;
|
|
144
|
+
|
|
145
|
+
const promise = createIterablePromise({
|
|
146
|
+
func: () => {
|
|
147
|
+
return new Promise((resolve, reject) => {
|
|
148
|
+
calls += 1;
|
|
149
|
+
if (calls <= 3) {
|
|
150
|
+
resolve('okay');
|
|
151
|
+
} else {
|
|
152
|
+
reject(new Error('nope'));
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
},
|
|
156
|
+
validate: () => false,
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
await expect(promise).rejects.toEqual(
|
|
160
|
+
expect.objectContaining({ message: 'nope' })
|
|
161
|
+
);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('gets the rejection of the given promise via throw', async () => {
|
|
165
|
+
let calls = 0;
|
|
166
|
+
|
|
167
|
+
const promise = createIterablePromise({
|
|
168
|
+
func: () => {
|
|
169
|
+
return new Promise((resolve) => {
|
|
170
|
+
calls += 1;
|
|
171
|
+
if (calls <= 3) {
|
|
172
|
+
resolve('okay');
|
|
173
|
+
} else {
|
|
174
|
+
throw new Error('nope');
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
},
|
|
178
|
+
validate: () => false,
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
await expect(promise).rejects.toEqual(
|
|
182
|
+
expect.objectContaining({ message: 'nope' })
|
|
183
|
+
);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('rejects with the given `message` when `validate` hits', async () => {
|
|
187
|
+
const MAX_RETRIES = 3;
|
|
188
|
+
let calls = 0;
|
|
189
|
+
|
|
190
|
+
const promise = createIterablePromise({
|
|
191
|
+
func: () => {
|
|
192
|
+
return new Promise((resolve) => {
|
|
193
|
+
calls += 1;
|
|
194
|
+
resolve('okay');
|
|
195
|
+
});
|
|
196
|
+
},
|
|
197
|
+
validate: () => false,
|
|
198
|
+
error: {
|
|
199
|
+
validate: () => calls >= MAX_RETRIES,
|
|
200
|
+
message: () => `Error is thrown: ${calls}/${MAX_RETRIES}`,
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
await expect(promise).rejects.toEqual(
|
|
205
|
+
expect.objectContaining({
|
|
206
|
+
message: 'Error is thrown: 3/3',
|
|
207
|
+
})
|
|
208
|
+
);
|
|
209
|
+
expect(calls).toBe(MAX_RETRIES);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('forward the response of the `func`', async () => {
|
|
213
|
+
const MAX_RETRIES = 3;
|
|
214
|
+
let calls = 0;
|
|
215
|
+
|
|
216
|
+
const promise = createIterablePromise<number>({
|
|
217
|
+
func: () => {
|
|
218
|
+
return new Promise((resolve) => {
|
|
219
|
+
calls += 1;
|
|
220
|
+
resolve(calls);
|
|
221
|
+
});
|
|
222
|
+
},
|
|
223
|
+
validate: () => false,
|
|
224
|
+
error: {
|
|
225
|
+
validate: (response) => response >= MAX_RETRIES,
|
|
226
|
+
message: (response) => `Error is thrown: ${response}/${MAX_RETRIES}`,
|
|
227
|
+
},
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
await expect(promise).rejects.toEqual(
|
|
231
|
+
expect.objectContaining({
|
|
232
|
+
message: 'Error is thrown: 3/3',
|
|
233
|
+
})
|
|
234
|
+
);
|
|
235
|
+
expect(calls).toBe(MAX_RETRIES);
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
});
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
BrowserLocalStorageCacheItem,
|
|
3
|
+
BrowserLocalStorageOptions,
|
|
4
|
+
Cache,
|
|
5
|
+
CacheEvents,
|
|
6
|
+
} from '../types';
|
|
2
7
|
|
|
3
8
|
export function createBrowserLocalStorageCache(
|
|
4
9
|
options: BrowserLocalStorageOptions
|
|
@@ -19,20 +24,61 @@ export function createBrowserLocalStorageCache(
|
|
|
19
24
|
return JSON.parse(getStorage().getItem(namespaceKey) || '{}');
|
|
20
25
|
}
|
|
21
26
|
|
|
27
|
+
function setNamespace(namespace: Record<string, any>): void {
|
|
28
|
+
getStorage().setItem(namespaceKey, JSON.stringify(namespace));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function removeOutdatedCacheItems(): void {
|
|
32
|
+
const timeToLive = options.timeToLive ? options.timeToLive * 1000 : null;
|
|
33
|
+
const namespace = getNamespace<BrowserLocalStorageCacheItem>();
|
|
34
|
+
|
|
35
|
+
const filteredNamespaceWithoutOldFormattedCacheItems = Object.fromEntries(
|
|
36
|
+
Object.entries(namespace).filter(([, cacheItem]) => {
|
|
37
|
+
return cacheItem.timestamp !== undefined;
|
|
38
|
+
})
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
setNamespace(filteredNamespaceWithoutOldFormattedCacheItems);
|
|
42
|
+
|
|
43
|
+
if (!timeToLive) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const filteredNamespaceWithoutExpiredItems = Object.fromEntries(
|
|
48
|
+
Object.entries(filteredNamespaceWithoutOldFormattedCacheItems).filter(
|
|
49
|
+
([, cacheItem]) => {
|
|
50
|
+
const currentTimestamp = new Date().getTime();
|
|
51
|
+
const isExpired = cacheItem.timestamp + timeToLive < currentTimestamp;
|
|
52
|
+
|
|
53
|
+
return !isExpired;
|
|
54
|
+
}
|
|
55
|
+
)
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
setNamespace(filteredNamespaceWithoutExpiredItems);
|
|
59
|
+
}
|
|
60
|
+
|
|
22
61
|
return {
|
|
23
62
|
get<TValue>(
|
|
24
63
|
key: Record<string, any> | string,
|
|
25
64
|
defaultValue: () => Promise<TValue>,
|
|
26
65
|
events: CacheEvents<TValue> = {
|
|
27
|
-
miss: ()
|
|
66
|
+
miss: () => Promise.resolve(),
|
|
28
67
|
}
|
|
29
68
|
): Promise<TValue> {
|
|
30
69
|
return Promise.resolve()
|
|
31
70
|
.then(() => {
|
|
32
|
-
|
|
33
|
-
const value = getNamespace<TValue>()[keyAsString];
|
|
71
|
+
removeOutdatedCacheItems();
|
|
34
72
|
|
|
35
|
-
return Promise
|
|
73
|
+
return getNamespace<Promise<BrowserLocalStorageCacheItem>>()[
|
|
74
|
+
JSON.stringify(key)
|
|
75
|
+
];
|
|
76
|
+
})
|
|
77
|
+
.then((value) => {
|
|
78
|
+
return Promise.all([
|
|
79
|
+
value ? value.value : defaultValue(),
|
|
80
|
+
value !== undefined,
|
|
81
|
+
]);
|
|
36
82
|
})
|
|
37
83
|
.then(([value, exists]) => {
|
|
38
84
|
return Promise.all([value, exists || events.miss(value)]);
|
|
@@ -47,7 +93,10 @@ export function createBrowserLocalStorageCache(
|
|
|
47
93
|
return Promise.resolve().then(() => {
|
|
48
94
|
const namespace = getNamespace();
|
|
49
95
|
|
|
50
|
-
namespace[JSON.stringify(key)] =
|
|
96
|
+
namespace[JSON.stringify(key)] = {
|
|
97
|
+
timestamp: new Date().getTime(),
|
|
98
|
+
value,
|
|
99
|
+
};
|
|
51
100
|
|
|
52
101
|
getStorage().setItem(namespaceKey, JSON.stringify(namespace));
|
|
53
102
|
|
|
@@ -11,7 +11,7 @@ function getUrlParams({
|
|
|
11
11
|
pathname,
|
|
12
12
|
}: URL): Pick<EchoResponse, 'algoliaAgent' | 'host' | 'path' | 'searchParams'> {
|
|
13
13
|
const algoliaAgent = urlSearchParams.get('x-algolia-agent') || '';
|
|
14
|
-
const searchParams = {};
|
|
14
|
+
const searchParams: Record<string, string> = {};
|
|
15
15
|
|
|
16
16
|
for (const [k, v] of urlSearchParams) {
|
|
17
17
|
if (k === 'x-algolia-agent') {
|
|
@@ -44,7 +44,7 @@ export function createEchoRequester({
|
|
|
44
44
|
data: request.data ? JSON.parse(request.data) : undefined,
|
|
45
45
|
path,
|
|
46
46
|
host,
|
|
47
|
-
algoliaAgent:
|
|
47
|
+
algoliaAgent: encodeURIComponent(algoliaAgent),
|
|
48
48
|
searchParams,
|
|
49
49
|
};
|
|
50
50
|
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { CreateIterablePromise } from './types/createIterablePromise';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Helper: Returns the promise of a given `func` to iterate on, based on a given `validate` condition.
|
|
5
|
+
*
|
|
6
|
+
* @param createIterator - The createIterator options.
|
|
7
|
+
* @param createIterator.func - The function to run, which returns a promise.
|
|
8
|
+
* @param createIterator.validate - The validator function. It receives the resolved return of `func`.
|
|
9
|
+
* @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`.
|
|
10
|
+
* @param createIterator.error - The `validate` condition to throw an error, and its message.
|
|
11
|
+
* @param createIterator.timeout - The function to decide how long to wait between iterations.
|
|
12
|
+
*/
|
|
13
|
+
export function createIterablePromise<TResponse>({
|
|
14
|
+
func,
|
|
15
|
+
validate,
|
|
16
|
+
aggregator,
|
|
17
|
+
error,
|
|
18
|
+
timeout = (): number => 0,
|
|
19
|
+
}: CreateIterablePromise<TResponse>): Promise<TResponse> {
|
|
20
|
+
const retry = (previousResponse?: TResponse): Promise<TResponse> => {
|
|
21
|
+
return new Promise<TResponse>((resolve, reject) => {
|
|
22
|
+
func(previousResponse)
|
|
23
|
+
.then((response) => {
|
|
24
|
+
if (aggregator) {
|
|
25
|
+
aggregator(response);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (validate(response)) {
|
|
29
|
+
return resolve(response);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (error && error.validate(response)) {
|
|
33
|
+
return reject(new Error(error.message(response)));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return setTimeout(() => {
|
|
37
|
+
retry(response).then(resolve).catch(reject);
|
|
38
|
+
}, timeout());
|
|
39
|
+
})
|
|
40
|
+
.catch((err) => {
|
|
41
|
+
reject(err);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
return retry();
|
|
47
|
+
}
|
|
@@ -110,12 +110,15 @@ export function createTransporter({
|
|
|
110
110
|
: {};
|
|
111
111
|
|
|
112
112
|
const queryParameters: QueryParameters = {
|
|
113
|
-
'x-algolia-agent': algoliaAgent.value,
|
|
114
113
|
...baseQueryParameters,
|
|
115
114
|
...request.queryParameters,
|
|
116
115
|
...dataQueryParameters,
|
|
117
116
|
};
|
|
118
117
|
|
|
118
|
+
if (algoliaAgent.value) {
|
|
119
|
+
queryParameters['x-algolia-agent'] = algoliaAgent.value;
|
|
120
|
+
}
|
|
121
|
+
|
|
119
122
|
if (requestOptions && requestOptions.queryParameters) {
|
|
120
123
|
for (const key of Object.keys(requestOptions.queryParameters)) {
|
|
121
124
|
// We want to keep `undefined` and `null` values,
|
|
@@ -25,7 +25,7 @@ export class ErrorWithStackTrace extends AlgoliaError {
|
|
|
25
25
|
export class RetryError extends ErrorWithStackTrace {
|
|
26
26
|
constructor(stackTrace: StackFrame[]) {
|
|
27
27
|
super(
|
|
28
|
-
'Unreachable hosts - your application id may be incorrect. If the error persists,
|
|
28
|
+
'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.',
|
|
29
29
|
stackTrace,
|
|
30
30
|
'RetryError'
|
|
31
31
|
);
|
|
@@ -35,8 +35,13 @@ export class RetryError extends ErrorWithStackTrace {
|
|
|
35
35
|
export class ApiError extends ErrorWithStackTrace {
|
|
36
36
|
status: number;
|
|
37
37
|
|
|
38
|
-
constructor(
|
|
39
|
-
|
|
38
|
+
constructor(
|
|
39
|
+
message: string,
|
|
40
|
+
status: number,
|
|
41
|
+
stackTrace: StackFrame[],
|
|
42
|
+
name = 'ApiError'
|
|
43
|
+
) {
|
|
44
|
+
super(message, stackTrace, name);
|
|
40
45
|
this.status = status;
|
|
41
46
|
}
|
|
42
47
|
}
|
|
@@ -49,3 +54,34 @@ export class DeserializationError extends AlgoliaError {
|
|
|
49
54
|
this.response = response;
|
|
50
55
|
}
|
|
51
56
|
}
|
|
57
|
+
|
|
58
|
+
export type DetailedErrorWithMessage = {
|
|
59
|
+
message: string;
|
|
60
|
+
label: string;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export type DetailedErrorWithTypeID = {
|
|
64
|
+
id: string;
|
|
65
|
+
type: string;
|
|
66
|
+
name?: string;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export type DetailedError = {
|
|
70
|
+
code: string;
|
|
71
|
+
details?: DetailedErrorWithMessage[] | DetailedErrorWithTypeID[];
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// DetailedApiError is only used by the ingestion client to return more informative error, other clients will use ApiClient.
|
|
75
|
+
export class DetailedApiError extends ApiError {
|
|
76
|
+
error: DetailedError;
|
|
77
|
+
|
|
78
|
+
constructor(
|
|
79
|
+
message: string,
|
|
80
|
+
status: number,
|
|
81
|
+
error: DetailedError,
|
|
82
|
+
stackTrace: StackFrame[]
|
|
83
|
+
) {
|
|
84
|
+
super(message, status, stackTrace, 'DetailedApiError');
|
|
85
|
+
this.error = error;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -8,7 +8,7 @@ import type {
|
|
|
8
8
|
StackFrame,
|
|
9
9
|
} from '../types';
|
|
10
10
|
|
|
11
|
-
import { ApiError, DeserializationError } from './errors';
|
|
11
|
+
import { ApiError, DeserializationError, DetailedApiError } from './errors';
|
|
12
12
|
|
|
13
13
|
export function shuffle<TData>(array: TData[]): TData[] {
|
|
14
14
|
const shuffledArray = array;
|
|
@@ -49,11 +49,11 @@ export function serializeQueryParameters(parameters: QueryParameters): string {
|
|
|
49
49
|
return Object.keys(parameters)
|
|
50
50
|
.map(
|
|
51
51
|
(key) =>
|
|
52
|
-
`${key}=${
|
|
52
|
+
`${key}=${encodeURIComponent(
|
|
53
53
|
isObjectOrArray(parameters[key])
|
|
54
54
|
? JSON.stringify(parameters[key])
|
|
55
55
|
: parameters[key]
|
|
56
|
-
}`
|
|
56
|
+
)}`
|
|
57
57
|
)
|
|
58
58
|
.join('&');
|
|
59
59
|
}
|
|
@@ -109,11 +109,19 @@ export function deserializeFailure(
|
|
|
109
109
|
{ content, status }: Response,
|
|
110
110
|
stackFrame: StackFrame[]
|
|
111
111
|
): Error {
|
|
112
|
-
let message = content;
|
|
113
112
|
try {
|
|
114
|
-
|
|
113
|
+
const parsed = JSON.parse(content);
|
|
114
|
+
if ('error' in parsed) {
|
|
115
|
+
return new DetailedApiError(
|
|
116
|
+
parsed.message,
|
|
117
|
+
status,
|
|
118
|
+
parsed.error,
|
|
119
|
+
stackFrame
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
return new ApiError(parsed.message, status, stackFrame);
|
|
115
123
|
} catch (e) {
|
|
116
124
|
// ..
|
|
117
125
|
}
|
|
118
|
-
return new ApiError(
|
|
126
|
+
return new ApiError(content, status, stackFrame);
|
|
119
127
|
}
|
|
@@ -47,12 +47,29 @@ export type BrowserLocalStorageOptions = {
|
|
|
47
47
|
*/
|
|
48
48
|
key: string;
|
|
49
49
|
|
|
50
|
+
/**
|
|
51
|
+
* The time to live for each cached item in seconds.
|
|
52
|
+
*/
|
|
53
|
+
timeToLive?: number;
|
|
54
|
+
|
|
50
55
|
/**
|
|
51
56
|
* The native local storage implementation.
|
|
52
57
|
*/
|
|
53
58
|
localStorage?: Storage;
|
|
54
59
|
};
|
|
55
60
|
|
|
61
|
+
export type BrowserLocalStorageCacheItem = {
|
|
62
|
+
/**
|
|
63
|
+
* The cache item creation timestamp.
|
|
64
|
+
*/
|
|
65
|
+
timestamp: number;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* The cache item value.
|
|
69
|
+
*/
|
|
70
|
+
value: any;
|
|
71
|
+
};
|
|
72
|
+
|
|
56
73
|
export type FallbackableCacheOptions = {
|
|
57
74
|
/**
|
|
58
75
|
* List of caches order by priority.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AlgoliaAgentOptions, TransporterOptions } from './
|
|
1
|
+
import type { AlgoliaAgentOptions, TransporterOptions } from './transporter';
|
|
2
2
|
|
|
3
3
|
export type AuthMode = 'WithinHeaders' | 'WithinQueryParameters';
|
|
4
4
|
|
|
@@ -18,6 +18,6 @@ export type CreateClientOptions = Omit<
|
|
|
18
18
|
algoliaAgents: AlgoliaAgentOptions[];
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
-
export type
|
|
21
|
+
export type ClientOptions = Partial<
|
|
22
22
|
Omit<CreateClientOptions, 'apiKey' | 'appId'>
|
|
23
23
|
>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export type IterableOptions<TResponse> = Partial<{
|
|
2
|
+
/**
|
|
3
|
+
* The function that runs right after the API call has been resolved, allows you to do anything with the response before `validate`.
|
|
4
|
+
*/
|
|
5
|
+
aggregator: (response: TResponse) => void;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The `validate` condition to throw an error and its message.
|
|
9
|
+
*/
|
|
10
|
+
error: {
|
|
11
|
+
/**
|
|
12
|
+
* The function to validate the error condition.
|
|
13
|
+
*/
|
|
14
|
+
validate: (response: TResponse) => boolean;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The error message to throw.
|
|
18
|
+
*/
|
|
19
|
+
message: (response: TResponse) => string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The function to decide how long to wait between iterations.
|
|
24
|
+
*/
|
|
25
|
+
timeout: () => number;
|
|
26
|
+
}>;
|
|
27
|
+
|
|
28
|
+
export type CreateIterablePromise<TResponse> = IterableOptions<TResponse> & {
|
|
29
|
+
/**
|
|
30
|
+
* The function to run, which returns a promise.
|
|
31
|
+
*
|
|
32
|
+
* The `previousResponse` parameter (`undefined` on the first call) allows you to build your request with incremental logic, to iterate on `page` or `cursor` for example.
|
|
33
|
+
*/
|
|
34
|
+
func: (previousResponse?: TResponse) => Promise<TResponse>;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The validator function. It receive the resolved return of the API call.
|
|
38
|
+
*/
|
|
39
|
+
validate: (response: TResponse) => boolean;
|
|
40
|
+
};
|
package/src/types/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export * from './
|
|
2
|
-
export * from './
|
|
3
|
-
export * from './
|
|
4
|
-
export * from './
|
|
5
|
-
export * from './
|
|
6
|
-
export * from './
|
|
1
|
+
export * from './cache';
|
|
2
|
+
export * from './createClient';
|
|
3
|
+
export * from './createIterablePromise';
|
|
4
|
+
export * from './host';
|
|
5
|
+
export * from './requester';
|
|
6
|
+
export * from './transporter';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { Cache } from './
|
|
2
|
-
import type { Host } from './
|
|
3
|
-
import type { Request, Requester, EndRequest, Response } from './
|
|
1
|
+
import type { Cache } from './cache';
|
|
2
|
+
import type { Host } from './host';
|
|
3
|
+
import type { Request, Requester, EndRequest, Response } from './requester';
|
|
4
4
|
|
|
5
5
|
export type Headers = Record<string, string>;
|
|
6
6
|
export type QueryParameters = Record<string, any>;
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { CreateRetryablePromiseOptions } from './types/CreateRetryablePromise';
|
|
2
|
-
export declare const DEFAULT_MAX_RETRIES = 50;
|
|
3
|
-
export declare const DEFAULT_TIMEOUT: (retryCount: number) => number;
|
|
4
|
-
/**
|
|
5
|
-
* Return a promise that retry a task until it meets the condition.
|
|
6
|
-
*
|
|
7
|
-
* @param createRetryablePromiseOptions - The createRetryablePromise options.
|
|
8
|
-
* @param createRetryablePromiseOptions.func - The function to run, which returns a promise.
|
|
9
|
-
* @param createRetryablePromiseOptions.validate - The validator function. It receives the resolved return of `func`.
|
|
10
|
-
* @param createRetryablePromiseOptions.maxRetries - The maximum number of retries. 50 by default.
|
|
11
|
-
* @param createRetryablePromiseOptions.timeout - The function to decide how long to wait between retries.
|
|
12
|
-
*/
|
|
13
|
-
export declare function createRetryablePromise<TResponse>({ func, validate, maxRetries, timeout, }: CreateRetryablePromiseOptions<TResponse>): Promise<TResponse>;
|
|
14
|
-
//# sourceMappingURL=createRetryablePromise.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"createRetryablePromise.d.ts","sourceRoot":"","sources":["../../../../packages/client-common/src/createRetryablePromise.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,gCAAgC,CAAC;AAEpF,eAAO,MAAM,mBAAmB,KAAK,CAAC;AACtC,eAAO,MAAM,eAAe,eAAgB,MAAM,KAAG,MACnB,CAAC;AAEnC;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,EAChD,IAAI,EACJ,QAAQ,EACR,UAAgC,EAChC,OAAyB,GAC1B,EAAE,6BAA6B,CAAC,SAAS,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC,CA+B/D"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Cache.d.ts","sourceRoot":"","sources":["../../../../../packages/client-common/src/types/Cache.ts"],"names":[],"mappings":"AAAA,oBAAY,KAAK,GAAG;IAClB;;OAEG;IACH,GAAG,EAAE,CAAC,MAAM,EACV,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,EACjC,YAAY,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,EACnC,MAAM,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,KACzB,OAAO,CAAC,MAAM,CAAC,CAAC;IAErB;;OAEG;IACH,GAAG,EAAE,CAAC,MAAM,EACV,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,EACjC,KAAK,EAAE,MAAM,KACV,OAAO,CAAC,MAAM,CAAC,CAAC;IAErB;;OAEG;IACH,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAE7D;;OAEG;IACH,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B,CAAC;AAEF,oBAAY,WAAW,CAAC,MAAM,IAAI;IAChC;;OAEG;IACH,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;CACvC,CAAC;AAEF,oBAAY,kBAAkB,GAAG;IAC/B;;OAEG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF,oBAAY,0BAA0B,GAAG;IACvC;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;OAEG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF,oBAAY,wBAAwB,GAAG;IACrC;;OAEG;IACH,MAAM,EAAE,KAAK,EAAE,CAAC;CACjB,CAAC"}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { AlgoliaAgentOptions, TransporterOptions } from './Transporter';
|
|
2
|
-
export declare type AuthMode = 'WithinHeaders' | 'WithinQueryParameters';
|
|
3
|
-
declare type OverriddenTransporterOptions = 'baseHeaders' | 'baseQueryParameters' | 'hosts';
|
|
4
|
-
export declare type CreateClientOptions = Omit<TransporterOptions, OverriddenTransporterOptions | 'algoliaAgent'> & Partial<Pick<TransporterOptions, OverriddenTransporterOptions>> & {
|
|
5
|
-
appId: string;
|
|
6
|
-
apiKey: string;
|
|
7
|
-
authMode?: AuthMode;
|
|
8
|
-
algoliaAgents: AlgoliaAgentOptions[];
|
|
9
|
-
};
|
|
10
|
-
export declare type InitClientOptions = Partial<Omit<CreateClientOptions, 'apiKey' | 'appId'>>;
|
|
11
|
-
export {};
|
|
12
|
-
//# sourceMappingURL=CreateClient.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"CreateClient.d.ts","sourceRoot":"","sources":["../../../../../packages/client-common/src/types/CreateClient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAE7E,oBAAY,QAAQ,GAAG,eAAe,GAAG,uBAAuB,CAAC;AAEjE,aAAK,4BAA4B,GAC7B,aAAa,GACb,qBAAqB,GACrB,OAAO,CAAC;AAEZ,oBAAY,mBAAmB,GAAG,IAAI,CACpC,kBAAkB,EAClB,4BAA4B,GAAG,cAAc,CAC9C,GACC,OAAO,CAAC,IAAI,CAAC,kBAAkB,EAAE,4BAA4B,CAAC,CAAC,GAAG;IAChE,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,aAAa,EAAE,mBAAmB,EAAE,CAAC;CACtC,CAAC;AAEJ,oBAAY,iBAAiB,GAAG,OAAO,CACrC,IAAI,CAAC,mBAAmB,EAAE,QAAQ,GAAG,OAAO,CAAC,CAC9C,CAAC"}
|