@ethlete/query 2.1.1 → 2.2.0
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/esm2020/lib/query/query.mjs +13 -5
- package/esm2020/lib/query/query.types.mjs +1 -1
- package/esm2020/lib/query-client/query-client.types.mjs +1 -1
- package/esm2020/lib/request/request.mjs +56 -38
- package/esm2020/lib/request/request.types.mjs +1 -1
- package/esm2020/lib/request/request.util.mjs +33 -2
- package/fesm2015/ethlete-query.mjs +95 -43
- package/fesm2015/ethlete-query.mjs.map +1 -1
- package/fesm2020/ethlete-query.mjs +99 -42
- package/fesm2020/ethlete-query.mjs.map +1 -1
- package/lib/query/query.types.d.ts +3 -0
- package/lib/query-client/query-client.types.d.ts +6 -1
- package/lib/request/request.types.d.ts +19 -0
- package/lib/request/request.util.d.ts +3 -1
- package/package.json +2 -2
|
@@ -71,7 +71,7 @@ const queryStateDoesNotContainKeyError = (data) => new QueryError('005', 'The qu
|
|
|
71
71
|
const queryStateCannotTransformError = (data) => new QueryError('006', 'The query state cannot be transformed because it is not loading', data);
|
|
72
72
|
const invalidBodyError = (data) => new QueryError('007', 'The body is not valid or upsupported', data);
|
|
73
73
|
|
|
74
|
-
const isRequestError = (error) => error instanceof Object && '
|
|
74
|
+
const isRequestError = (error) => error instanceof Object && 'status' in error && 'statusText' in error && 'url' in error;
|
|
75
75
|
const buildRoute = (options) => {
|
|
76
76
|
if (options.base.endsWith('/')) {
|
|
77
77
|
throw invalidBaseRouteError(options.base);
|
|
@@ -271,6 +271,37 @@ const getResponseUrl = (xhr) => {
|
|
|
271
271
|
}
|
|
272
272
|
return null;
|
|
273
273
|
};
|
|
274
|
+
const shouldRetryRequest = (config) => {
|
|
275
|
+
const defaultRetryDelay = 1000 + 1000 * config.currentRetryCount;
|
|
276
|
+
if (config.currentRetryCount > 3) {
|
|
277
|
+
return { retry: false };
|
|
278
|
+
}
|
|
279
|
+
if (!isRequestError(config.error)) {
|
|
280
|
+
return { retry: false };
|
|
281
|
+
}
|
|
282
|
+
const { status } = config.error;
|
|
283
|
+
// Retry on 5xx errors
|
|
284
|
+
if (status >= 500) {
|
|
285
|
+
return { retry: true, delay: defaultRetryDelay };
|
|
286
|
+
}
|
|
287
|
+
// Retry on 408 or 425 errors
|
|
288
|
+
if (status === 408 /* HttpStatusCode.RequestTimeout */ || status === 425 /* HttpStatusCode.TooEarly */) {
|
|
289
|
+
return { retry: true, delay: defaultRetryDelay };
|
|
290
|
+
}
|
|
291
|
+
// Retry on 429 errors
|
|
292
|
+
if (status === 429 /* HttpStatusCode.TooManyRequests */) {
|
|
293
|
+
const retryAfter = config.headers['retry-after'] ||
|
|
294
|
+
config.headers['Retry-After'] ||
|
|
295
|
+
config.headers['x-retry-after'] ||
|
|
296
|
+
config.headers['X-Retry-After'];
|
|
297
|
+
if (retryAfter) {
|
|
298
|
+
const delay = parseInt(retryAfter) * 1000;
|
|
299
|
+
return { retry: true, delay: Number.isNaN(delay) ? defaultRetryDelay : delay };
|
|
300
|
+
}
|
|
301
|
+
return { retry: true, delay: defaultRetryDelay };
|
|
302
|
+
}
|
|
303
|
+
return { retry: false };
|
|
304
|
+
};
|
|
274
305
|
|
|
275
306
|
const XSSI_PREFIX = /^\)\]\}',?\n/;
|
|
276
307
|
const request = (config) => {
|
|
@@ -278,25 +309,32 @@ const request = (config) => {
|
|
|
278
309
|
const responseType = config.responseType || 'json';
|
|
279
310
|
const body = config.body || null;
|
|
280
311
|
const url = config.urlWithParams.split('?')[0];
|
|
312
|
+
const retryFn = config.retryFn || shouldRetryRequest;
|
|
313
|
+
let currentRetryCount = 0;
|
|
314
|
+
let retryTimeout = null;
|
|
281
315
|
return new Observable((observer) => {
|
|
282
316
|
const xhr = new XMLHttpRequest();
|
|
283
|
-
xhr.open(config.method, config.urlWithParams);
|
|
284
|
-
if (config.withCredentials) {
|
|
285
|
-
xhr.withCredentials = true;
|
|
286
|
-
}
|
|
287
|
-
forEachHeader(headers, (name, value) => xhr.setRequestHeader(name, value));
|
|
288
|
-
if (!hasHeader(headers, 'Accept')) {
|
|
289
|
-
xhr.setRequestHeader('Accept', 'application/json, text/plain, */*');
|
|
290
|
-
}
|
|
291
|
-
if (!hasHeader(headers, 'Content-Type')) {
|
|
292
|
-
const detectedType = detectContentTypeHeader(body);
|
|
293
|
-
if (detectedType) {
|
|
294
|
-
xhr.setRequestHeader('Content-Type', detectedType);
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
xhr.responseType = responseType !== 'json' ? responseType : 'text';
|
|
298
317
|
const reqBody = serializeBody(body);
|
|
299
318
|
let headerResponse = null;
|
|
319
|
+
const setupXhr = () => {
|
|
320
|
+
headerResponse = null;
|
|
321
|
+
xhr.open(config.method, config.urlWithParams);
|
|
322
|
+
if (config.withCredentials) {
|
|
323
|
+
xhr.withCredentials = true;
|
|
324
|
+
}
|
|
325
|
+
forEachHeader(headers, (name, value) => xhr.setRequestHeader(name, value));
|
|
326
|
+
if (!hasHeader(headers, 'Accept')) {
|
|
327
|
+
xhr.setRequestHeader('Accept', 'application/json, text/plain, */*');
|
|
328
|
+
}
|
|
329
|
+
if (!hasHeader(headers, 'Content-Type')) {
|
|
330
|
+
const detectedType = detectContentTypeHeader(body);
|
|
331
|
+
if (detectedType) {
|
|
332
|
+
xhr.setRequestHeader('Content-Type', detectedType);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
xhr.responseType = responseType !== 'json' ? responseType : 'text';
|
|
336
|
+
};
|
|
337
|
+
setupXhr();
|
|
300
338
|
const partialFromXhr = () => {
|
|
301
339
|
if (headerResponse !== null) {
|
|
302
340
|
return headerResponse;
|
|
@@ -307,6 +345,24 @@ const request = (config) => {
|
|
|
307
345
|
headerResponse = { headers, status: xhr.status, statusText, url: _url };
|
|
308
346
|
return headerResponse;
|
|
309
347
|
};
|
|
348
|
+
const handleRetry = (error) => {
|
|
349
|
+
const newRetryCount = currentRetryCount + 1;
|
|
350
|
+
const { headers } = partialFromXhr();
|
|
351
|
+
const { retry, delay } = retryFn({ currentRetryCount: newRetryCount, headers, error });
|
|
352
|
+
if (!retry) {
|
|
353
|
+
observer.next({ type: 'failure', headers, error });
|
|
354
|
+
observer.complete();
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
const _delay = delay ?? 1000;
|
|
358
|
+
observer.next({ type: 'delay-retry', headers, retryNumber: newRetryCount, retryDelay: _delay });
|
|
359
|
+
retryTimeout = window.setTimeout(() => {
|
|
360
|
+
setupXhr();
|
|
361
|
+
xhr.send(reqBody);
|
|
362
|
+
currentRetryCount = newRetryCount;
|
|
363
|
+
observer.next({ type: 'start', headers, isRetry: true, retryNumber: currentRetryCount, retryDelay: _delay });
|
|
364
|
+
}, _delay);
|
|
365
|
+
};
|
|
310
366
|
const onLoad = () => {
|
|
311
367
|
const { headers, statusText, url } = partialFromXhr();
|
|
312
368
|
let { status } = partialFromXhr();
|
|
@@ -347,32 +403,22 @@ const request = (config) => {
|
|
|
347
403
|
observer.complete();
|
|
348
404
|
}
|
|
349
405
|
else {
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
detail: body,
|
|
356
|
-
status,
|
|
357
|
-
statusText,
|
|
358
|
-
},
|
|
406
|
+
handleRetry({
|
|
407
|
+
url,
|
|
408
|
+
detail: body,
|
|
409
|
+
status,
|
|
410
|
+
statusText,
|
|
359
411
|
});
|
|
360
|
-
observer.complete();
|
|
361
412
|
}
|
|
362
413
|
};
|
|
363
414
|
const onError = (error) => {
|
|
364
|
-
const { url
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
detail: error,
|
|
371
|
-
status: xhr.status || 0,
|
|
372
|
-
statusText: xhr.statusText || 'Unknown Error',
|
|
373
|
-
},
|
|
415
|
+
const { url } = partialFromXhr();
|
|
416
|
+
handleRetry({
|
|
417
|
+
url,
|
|
418
|
+
detail: error,
|
|
419
|
+
status: xhr.status || 0,
|
|
420
|
+
statusText: xhr.statusText || 'Unknown Error',
|
|
374
421
|
});
|
|
375
|
-
observer.complete();
|
|
376
422
|
};
|
|
377
423
|
const onDownProgress = (event) => {
|
|
378
424
|
const { headers } = partialFromXhr();
|
|
@@ -426,6 +472,9 @@ const request = (config) => {
|
|
|
426
472
|
xhr.removeEventListener('abort', onError);
|
|
427
473
|
xhr.removeEventListener('load', onLoad);
|
|
428
474
|
xhr.removeEventListener('timeout', onError);
|
|
475
|
+
if (retryTimeout) {
|
|
476
|
+
window.clearTimeout(retryTimeout);
|
|
477
|
+
}
|
|
429
478
|
if (config.reportProgress) {
|
|
430
479
|
xhr.removeEventListener('progress', onDownProgress);
|
|
431
480
|
if (reqBody !== null && xhr.upload) {
|
|
@@ -628,23 +677,31 @@ class Query {
|
|
|
628
677
|
authHeader = this._client.authProvider.header;
|
|
629
678
|
}
|
|
630
679
|
}
|
|
680
|
+
const mergedHeaders = mergeHeaders(getDefaultHeaders(this._client.config.request?.headers, this._queryConfig.method), authHeader, this._args?.headers) || undefined;
|
|
631
681
|
request({
|
|
632
682
|
urlWithParams: this._routeWithParams,
|
|
633
683
|
method: transformMethod(this._queryConfig.method),
|
|
634
684
|
body,
|
|
635
|
-
headers:
|
|
636
|
-
cacheAdapter: this._client.config.request?.cacheAdapter,
|
|
685
|
+
headers: mergedHeaders,
|
|
637
686
|
reportProgress: this._queryConfig.reportProgress,
|
|
638
687
|
responseType: this._queryConfig.responseType,
|
|
639
688
|
withCredentials: this._queryConfig.withCredentials,
|
|
689
|
+
cacheAdapter: this._client.config.request?.cacheAdapter,
|
|
690
|
+
retryFn: this._client.config.request?.retryFn,
|
|
640
691
|
})
|
|
641
692
|
.pipe(takeUntil(this._onAbort$))
|
|
642
693
|
.subscribe({
|
|
643
694
|
next: (state) => {
|
|
644
|
-
if (state.type === 'start') {
|
|
695
|
+
if (state.type === 'start' || state.type === 'delay-retry') {
|
|
696
|
+
const newMeta = {
|
|
697
|
+
...meta,
|
|
698
|
+
isWaitingForRetry: state.type === 'delay-retry',
|
|
699
|
+
retryDelay: state.retryDelay,
|
|
700
|
+
retryNumber: state.retryNumber,
|
|
701
|
+
};
|
|
645
702
|
this._state$.next({
|
|
646
703
|
type: "LOADING" /* QueryStateType.Loading */,
|
|
647
|
-
meta,
|
|
704
|
+
meta: newMeta,
|
|
648
705
|
});
|
|
649
706
|
}
|
|
650
707
|
else if (state.type === 'download-progress') {
|
|
@@ -1869,5 +1926,5 @@ const def = () => null;
|
|
|
1869
1926
|
* Generated bundle index. Do not edit.
|
|
1870
1927
|
*/
|
|
1871
1928
|
|
|
1872
|
-
export { BasicAuthProvider, BearerAuthProvider, CustomHeaderAuthProvider, INFINITY_QUERY_TOKEN, InfinityQuery, InfinityQueryDirective, InfinityQueryTriggerDirective, Query, QueryClient, QueryDirective, QueryError, QueryField, QueryForm, QueryStore, buildErrorMessage, buildGqlCacheKey, buildQueryArrayString, buildQueryString, buildRoute, buildTimestampFromSeconds, createInfinityQueryConfig, createQueryCollection, decryptBearer, deepFreeze, def, detectContentTypeHeader, extractExpiresInSeconds, extractQuery, filterFailure, filterInvalidParams, filterNull, filterQueryStates, filterSuccess, forEachHeader, getDefaultHeaders, getResponseUrl, gql, hasHeader, invalidBaseRouteError, invalidBodyError, invalidRouteError, isBasicAuthProvider, isBearerAuthProvider, isCustomHeaderAuthProvider, isFetchResponse, isGqlQueryConfig, isParamValid, isQuery, isQueryStateCancelled, isQueryStateFailure, isQueryStateLoading, isQueryStatePrepared, isQueryStateSuccess, isRequestError, mergeHeaders, parseAllXhrResponseHeaders, pathParamsMissingInRouteFunctionError, queryStateAlreadyHasKeyError, queryStateCannotTransformError, queryStateDoesNotContainKeyError, request, serializeBody, shouldCacheQuery, skipPaginationPageParamCalculator, switchQueryCollectionState, switchQueryState, takeUntilResponse, transformGql, transformMethod, transformToBoolean, transformToBooleanArray, transformToDate, transformToDateArray, transformToNumber, transformToNumberArray, transformToSort, transformToSortQueryParam, transformToString, transformToStringArray };
|
|
1929
|
+
export { BasicAuthProvider, BearerAuthProvider, CustomHeaderAuthProvider, INFINITY_QUERY_TOKEN, InfinityQuery, InfinityQueryDirective, InfinityQueryTriggerDirective, Query, QueryClient, QueryDirective, QueryError, QueryField, QueryForm, QueryStore, buildErrorMessage, buildGqlCacheKey, buildQueryArrayString, buildQueryString, buildRoute, buildTimestampFromSeconds, createInfinityQueryConfig, createQueryCollection, decryptBearer, deepFreeze, def, detectContentTypeHeader, extractExpiresInSeconds, extractQuery, filterFailure, filterInvalidParams, filterNull, filterQueryStates, filterSuccess, forEachHeader, getDefaultHeaders, getResponseUrl, gql, hasHeader, invalidBaseRouteError, invalidBodyError, invalidRouteError, isBasicAuthProvider, isBearerAuthProvider, isCustomHeaderAuthProvider, isFetchResponse, isGqlQueryConfig, isParamValid, isQuery, isQueryStateCancelled, isQueryStateFailure, isQueryStateLoading, isQueryStatePrepared, isQueryStateSuccess, isRequestError, mergeHeaders, parseAllXhrResponseHeaders, pathParamsMissingInRouteFunctionError, queryStateAlreadyHasKeyError, queryStateCannotTransformError, queryStateDoesNotContainKeyError, request, serializeBody, shouldCacheQuery, shouldRetryRequest, skipPaginationPageParamCalculator, switchQueryCollectionState, switchQueryState, takeUntilResponse, transformGql, transformMethod, transformToBoolean, transformToBooleanArray, transformToDate, transformToDateArray, transformToNumber, transformToNumberArray, transformToSort, transformToSortQueryParam, transformToString, transformToStringArray };
|
|
1873
1930
|
//# sourceMappingURL=ethlete-query.mjs.map
|