1
- import { isRetryable , isSuccess } from './Response' ;
1
+ import type {
2
+ EndRequest ,
3
+ Host ,
4
+ Request ,
5
+ RequestOptions ,
6
+ Response ,
7
+ StackFrame ,
8
+ TransporterOptions ,
9
+ Transporter ,
10
+ } from '../types' ;
11
+
2
12
import { createStatefulHost } from './createStatefulHost' ;
3
13
import { RetryError } from './errors' ;
4
14
import {
@@ -8,20 +18,11 @@ import {
8
18
serializeHeaders ,
9
19
serializeUrl ,
10
20
} from './helpers' ;
21
+ import { isRetryable , isSuccess } from './responses' ;
11
22
import {
12
23
stackTraceWithoutCredentials ,
13
24
stackFrameWithoutCredentials ,
14
25
} from './stackTrace' ;
15
- import type {
16
- EndRequest ,
17
- Host ,
18
- Request ,
19
- RequestOptions ,
20
- Response ,
21
- StackFrame ,
22
- TransporterOptions ,
23
- Transporter ,
24
- } from './types' ;
25
26
26
27
type RetryableOptions = {
27
28
hosts : Host [ ] ;
@@ -36,6 +37,8 @@ export function createTransporter({
36
37
userAgent,
37
38
timeouts,
38
39
requester,
40
+ requestsCache,
41
+ responsesCache,
39
42
} : TransporterOptions ) : Transporter {
40
43
async function createRetryableOptions (
41
44
compatibleHosts : Host [ ]
@@ -213,6 +216,94 @@ export function createTransporter({
213
216
return retry ( [ ...options . hosts ] . reverse ( ) , options . getTimeout ) ;
214
217
}
215
218
219
+ function createRequest < TResponse > (
220
+ request : Request ,
221
+ requestOptions : RequestOptions
222
+ ) : Promise < TResponse > {
223
+ if ( request . method !== 'GET' ) {
224
+ /**
225
+ * On write requests, no cache mechanisms are applied, and we
226
+ * proxy the request immediately to the requester.
227
+ */
228
+ return retryableRequest < TResponse > ( request , requestOptions ) ;
229
+ }
230
+
231
+ const createRetryableRequest = ( ) : Promise < TResponse > => {
232
+ /**
233
+ * Then, we prepare a function factory that contains the construction of
234
+ * the retryable request. At this point, we may *not* perform the actual
235
+ * request. But we want to have the function factory ready.
236
+ */
237
+ return retryableRequest < TResponse > ( request , requestOptions ) ;
238
+ } ;
239
+
240
+ /**
241
+ * Once we have the function factory ready, we need to determine of the
242
+ * request is "cacheable" - should be cached. Note that, once again,
243
+ * the user can force this option.
244
+ */
245
+ const cacheable = Boolean ( requestOptions . cacheable || request . cacheable ) ;
246
+
247
+ /**
248
+ * If is not "cacheable", we immediatly trigger the retryable request, no
249
+ * need to check cache implementations.
250
+ */
251
+ if ( cacheable !== true ) {
252
+ return createRetryableRequest ( ) ;
253
+ }
254
+
255
+ /**
256
+ * If the request is "cacheable", we need to first compute the key to ask
257
+ * the cache implementations if this request is on progress or if the
258
+ * response already exists on the cache.
259
+ */
260
+ const key = {
261
+ request,
262
+ requestOptions,
263
+ transporter : {
264
+ queryParameters : requestOptions . queryParameters ,
265
+ headers : requestOptions . headers ,
266
+ } ,
267
+ } ;
268
+
269
+ /**
270
+ * With the computed key, we first ask the responses cache
271
+ * implemention if this request was been resolved before.
272
+ */
273
+ return responsesCache . get (
274
+ key ,
275
+ ( ) => {
276
+ /**
277
+ * If the request has never resolved before, we actually ask if there
278
+ * is a current request with the same key on progress.
279
+ */
280
+ return requestsCache . get ( key , ( ) =>
281
+ /**
282
+ * Finally, if there is no request in progress with the same key,
283
+ * this `createRetryableRequest()` will actually trigger the
284
+ * retryable request.
285
+ */
286
+ requestsCache
287
+ . set ( key , createRetryableRequest ( ) )
288
+ . then (
289
+ ( response ) => Promise . all ( [ requestsCache . delete ( key ) , response ] ) ,
290
+ ( err ) =>
291
+ Promise . all ( [ requestsCache . delete ( key ) , Promise . reject ( err ) ] )
292
+ )
293
+ . then ( ( [ _ , response ] ) => response )
294
+ ) ;
295
+ } ,
296
+ {
297
+ /**
298
+ * Of course, once we get this response back from the server, we
299
+ * tell response cache to actually store the received response
300
+ * to be used later.
301
+ */
302
+ miss : ( response ) => responsesCache . set ( key , response ) ,
303
+ }
304
+ ) ;
305
+ }
306
+
216
307
return {
217
308
hostsCache,
218
309
requester,
@@ -221,6 +312,8 @@ export function createTransporter({
221
312
baseHeaders,
222
313
baseQueryParameters,
223
314
hosts,
224
- request : retryableRequest ,
315
+ request : createRequest ,
316
+ requestsCache,
317
+ responsesCache,
225
318
} ;
226
319
}
0 commit comments