@selkirk-systems/fetch 1.0.11 → 1.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/dist/Fetch.js +131 -8
- package/dist/constants/FetchConstants.js +3 -0
- package/dist/index.js +1 -1
- package/dist/middleware/FetchErrorHandler.js +29 -26
- package/lib/Fetch.js +182 -8
- package/lib/constants/FetchConstants.js +3 -0
- package/lib/index.js +1 -1
- package/lib/middleware/FetchErrorHandler.js +29 -26
- package/package.json +2 -2
- package/lib/constants/ErrorConstants.js +0 -1
package/dist/Fetch.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
//Inspired by https://www.bennadel.com/blog/4180-canceling-api-requests-using-fetch-and-abortcontroller-in-javascript.htm
|
|
2
2
|
|
|
3
3
|
import Download from './Download';
|
|
4
|
+
import { DELETE_FROM_CACHE, UPDATE_CACHE } from './constants/FetchConstants';
|
|
4
5
|
import FetchErrorHandler from './middleware/FetchErrorHandler';
|
|
6
|
+
import { dispatch, serializeData } from "@selkirk-systems/state-management";
|
|
5
7
|
|
|
6
8
|
// Regular expression patterns for testing content-type response headers.
|
|
7
9
|
const RE_CONTENT_TYPE_JSON = /^application\/(x-)?json/i;
|
|
@@ -12,6 +14,10 @@ const CONTENT_TYPE_DOWNLOADS = {
|
|
|
12
14
|
'application/pdf': true,
|
|
13
15
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': true
|
|
14
16
|
};
|
|
17
|
+
|
|
18
|
+
//30 minutes
|
|
19
|
+
const CACHED_EXPIRY_TIMESTAMP = 30 * 60000;
|
|
20
|
+
|
|
15
21
|
//We store the original promise.catch so we can override it in some
|
|
16
22
|
//scenarios when we want to swallow errors vs bubble them up.
|
|
17
23
|
const ORIGINAL_CATCH_FN = Promise.prototype.catch;
|
|
@@ -44,6 +50,10 @@ export function OnOKResponse(fn) {
|
|
|
44
50
|
return fn([network, isAbort]);
|
|
45
51
|
};
|
|
46
52
|
}
|
|
53
|
+
function isServiceWorker() {
|
|
54
|
+
return self;
|
|
55
|
+
}
|
|
56
|
+
|
|
47
57
|
/**
|
|
48
58
|
* Make the fetch request with the given configuration options.
|
|
49
59
|
*
|
|
@@ -59,7 +69,7 @@ export function OnOKResponse(fn) {
|
|
|
59
69
|
* - status.text
|
|
60
70
|
* - status.isAbort
|
|
61
71
|
*/
|
|
62
|
-
|
|
72
|
+
function Fetch(url, options = {}) {
|
|
63
73
|
const config = {
|
|
64
74
|
downloadFileName: null,
|
|
65
75
|
contentType: "application/json",
|
|
@@ -104,9 +114,11 @@ export default function Fetch(url, options = {}) {
|
|
|
104
114
|
// For form data posts, we want the browser to build the Content-
|
|
105
115
|
// Type for us so that it puts in both the "multipart/form-data" plus the
|
|
106
116
|
// correct, auto-generated field delimiter.
|
|
107
|
-
delete finalHeaders["content-type"];
|
|
108
|
-
|
|
109
|
-
|
|
117
|
+
//delete ( finalHeaders["content-type"] );
|
|
118
|
+
|
|
119
|
+
//finalMethod = "POST";
|
|
120
|
+
//finalBody = buildFormData( config.form );
|
|
121
|
+
finalBody = config.form;
|
|
110
122
|
} else if (config.json) {
|
|
111
123
|
finalHeaders["content-type"] = config.contentType || "application/x-json";
|
|
112
124
|
finalBody = JSON.stringify(config.json);
|
|
@@ -115,7 +127,7 @@ export default function Fetch(url, options = {}) {
|
|
|
115
127
|
} else {
|
|
116
128
|
finalHeaders["content-type"] = config.contentType;
|
|
117
129
|
}
|
|
118
|
-
request = new
|
|
130
|
+
request = new Request(finalUrl, {
|
|
119
131
|
headers: finalHeaders,
|
|
120
132
|
method: finalMethod,
|
|
121
133
|
body: finalBody,
|
|
@@ -129,7 +141,7 @@ export default function Fetch(url, options = {}) {
|
|
|
129
141
|
|
|
130
142
|
//Cache requests abort signal by url
|
|
131
143
|
cacheRequestSignal(finalUrl, finalSignal);
|
|
132
|
-
config._promiseChain = Promise.resolve(
|
|
144
|
+
config._promiseChain = Promise.resolve(fetch(request)).then(async response => {
|
|
133
145
|
deleteCachedRequestSignal(finalUrl);
|
|
134
146
|
const data = await unwrapResponseData(response);
|
|
135
147
|
if (response.ok) {
|
|
@@ -287,7 +299,12 @@ function buildFormData(form) {
|
|
|
287
299
|
function buildURL(url, templateData, params) {
|
|
288
300
|
if (url.href) return url;
|
|
289
301
|
const formattedUrl = buildURLTemplate(url, templateData);
|
|
290
|
-
|
|
302
|
+
let finalUrl;
|
|
303
|
+
if (self) {
|
|
304
|
+
finalUrl = new URL(formattedUrl);
|
|
305
|
+
} else {
|
|
306
|
+
finalUrl = new URL(formattedUrl, `${window.location.origin}${window.baseUrl || ""}`);
|
|
307
|
+
}
|
|
291
308
|
const searchParams = new URLSearchParams();
|
|
292
309
|
Object.entries(params).forEach(([key, value]) => {
|
|
293
310
|
if (Array.isArray(value)) {
|
|
@@ -403,4 +420,110 @@ function normalizeTransportError(transportError, request, config) {
|
|
|
403
420
|
isAbort: isAbort
|
|
404
421
|
}
|
|
405
422
|
};
|
|
406
|
-
}
|
|
423
|
+
}
|
|
424
|
+
const responseObjectJson = {
|
|
425
|
+
status: 200,
|
|
426
|
+
headers: {
|
|
427
|
+
'Content-Type': 'application/json'
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
export const getCacheByName = async name => {
|
|
431
|
+
try {
|
|
432
|
+
return caches.open(name);
|
|
433
|
+
} catch (err) {
|
|
434
|
+
throw err;
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
export const putJsonInCache = (cache, url, json) => {
|
|
438
|
+
const response = new Response(JSON.stringify(json), responseObjectJson);
|
|
439
|
+
return cache.put(url, response);
|
|
440
|
+
};
|
|
441
|
+
let _cache = null;
|
|
442
|
+
const DATA_METHODS = {
|
|
443
|
+
"GET": null,
|
|
444
|
+
"PATCH": null,
|
|
445
|
+
"POST": null,
|
|
446
|
+
"PUT": null
|
|
447
|
+
};
|
|
448
|
+
const _fetch = (url, options = {}) => {
|
|
449
|
+
if (self) {
|
|
450
|
+
return Fetch(url, options);
|
|
451
|
+
}
|
|
452
|
+
function cacheResponse([response, isAbort]) {
|
|
453
|
+
const status = response.status.code;
|
|
454
|
+
const headers = response.request.headers;
|
|
455
|
+
const method = response.request.method;
|
|
456
|
+
if (status >= 200 && status < 400 && headers.get('content-type') === "application/json") {
|
|
457
|
+
if (DATA_METHODS.hasOwnProperty(method) || method === "GET") {
|
|
458
|
+
const data = serializeData(response.data);
|
|
459
|
+
if (data.page && !data.items || data.items && data.items.length === 0) {
|
|
460
|
+
deleteFromCache(_cache, url, response);
|
|
461
|
+
return [response, isAbort];
|
|
462
|
+
}
|
|
463
|
+
const finalOptions = {
|
|
464
|
+
...responseObjectJson
|
|
465
|
+
};
|
|
466
|
+
finalOptions.headers['Time-Cached'] = new Date().getTime();
|
|
467
|
+
const responseObj = new Response(JSON.stringify(response), finalOptions);
|
|
468
|
+
_cache.put(url, responseObj);
|
|
469
|
+
dispatch(UPDATE_CACHE, {
|
|
470
|
+
url: url,
|
|
471
|
+
response: response
|
|
472
|
+
});
|
|
473
|
+
return [response, isAbort];
|
|
474
|
+
}
|
|
475
|
+
if (method === "DELETE") {
|
|
476
|
+
deleteFromCache(_cache, url, response);
|
|
477
|
+
return [response, isAbort];
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
return [response, isAbort];
|
|
481
|
+
}
|
|
482
|
+
function cacheMatch() {
|
|
483
|
+
//HANDLE: Data updates, always return fresh data
|
|
484
|
+
if (options.method && DATA_METHODS.hasOwnProperty(options.method)) {
|
|
485
|
+
return Fetch(url, options).then(cacheResponse);
|
|
486
|
+
}
|
|
487
|
+
return _cache.match(url).then(response => {
|
|
488
|
+
if (response) {
|
|
489
|
+
const timeCached = response.headers.get('Time-Cached');
|
|
490
|
+
if (expiredCache(timeCached)) {
|
|
491
|
+
_cache.delete(url);
|
|
492
|
+
return Fetch(url, options).then(cacheResponse);
|
|
493
|
+
}
|
|
494
|
+
Fetch(url, options).then(cacheResponse);
|
|
495
|
+
return unwrapResponseData(response).then(obj => {
|
|
496
|
+
return Promise.resolve([{
|
|
497
|
+
request: null,
|
|
498
|
+
response: response,
|
|
499
|
+
data: obj.data,
|
|
500
|
+
status: {
|
|
501
|
+
code: response.status,
|
|
502
|
+
text: response.statusText,
|
|
503
|
+
isAbort: false
|
|
504
|
+
}
|
|
505
|
+
}, false]);
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
return Fetch(url, options).then(cacheResponse);
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
if (_cache) {
|
|
512
|
+
return cacheMatch();
|
|
513
|
+
}
|
|
514
|
+
return caches.open('fetch').then(cache => {
|
|
515
|
+
_cache = cache;
|
|
516
|
+
}).then(cacheMatch);
|
|
517
|
+
};
|
|
518
|
+
function deleteFromCache(_cache, url, response) {
|
|
519
|
+
_cache.delete(url);
|
|
520
|
+
dispatch(DELETE_FROM_CACHE, {
|
|
521
|
+
url: url,
|
|
522
|
+
response: response
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
function expiredCache(timeCached) {
|
|
526
|
+
const now = new Date().getTime();
|
|
527
|
+
return Math.abs(now - timeCached) >= CACHED_EXPIRY_TIMESTAMP;
|
|
528
|
+
}
|
|
529
|
+
export default _fetch;
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { dispatch } from "@selkirk-systems/state-management";
|
|
2
|
-
import { ADD_ERROR } from "../constants/
|
|
2
|
+
import { ADD_ERROR } from "../constants/FetchConstants";
|
|
3
3
|
const handler = err => response => options => next => {
|
|
4
4
|
if (err) {
|
|
5
5
|
dispatch(ADD_ERROR, err);
|
|
@@ -8,30 +8,33 @@ const handler = err => response => options => next => {
|
|
|
8
8
|
return next(null, response);
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
-
//
|
|
12
|
-
|
|
13
|
-
// NOTE:
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
11
|
+
//HANDLE: Not being called from a service worker
|
|
12
|
+
if (!self) {
|
|
13
|
+
// NOTE: event name is all lower case as per DOM convention
|
|
14
|
+
window.addEventListener("unhandledrejection", function (e) {
|
|
15
|
+
// NOTE: e.preventDefault() must be manually called to prevent the default
|
|
16
|
+
// action which is currently to log the stack trace to console.warn
|
|
17
|
+
e.preventDefault();
|
|
18
|
+
// NOTE: parameters are properties of the event detail property
|
|
19
|
+
var reason = e.reason || e.detail.reason;
|
|
20
|
+
//var promise = e.detail.promise;
|
|
21
|
+
// See Promise.onPossiblyUnhandledRejection for parameter documentation
|
|
22
|
+
console.groupEnd();
|
|
23
|
+
console.groupEnd();
|
|
24
|
+
console.groupEnd();
|
|
25
|
+
throw reason;
|
|
26
|
+
});
|
|
25
27
|
|
|
26
|
-
// NOTE: event name is all lower case as per DOM convention
|
|
27
|
-
window.addEventListener("rejectionhandled", function (e) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
});
|
|
28
|
+
// NOTE: event name is all lower case as per DOM convention
|
|
29
|
+
window.addEventListener("rejectionhandled", function (e) {
|
|
30
|
+
// NOTE: e.preventDefault() must be manually called prevent the default
|
|
31
|
+
// action which is currently unset (but might be set to something in the future)
|
|
32
|
+
e.preventDefault();
|
|
33
|
+
// NOTE: parameters are properties of the event detail property
|
|
34
|
+
var promise = e.reason || e.detail.promise;
|
|
35
|
+
// See Promise.onUnhandledRejectionHandled for parameter documentation
|
|
36
|
+
console.groupEnd();
|
|
37
|
+
console.log("REJECTION HANDLED", promise);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
37
40
|
export default handler;
|
package/lib/Fetch.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
//Inspired by https://www.bennadel.com/blog/4180-canceling-api-requests-using-fetch-and-abortcontroller-in-javascript.htm
|
|
2
2
|
|
|
3
3
|
import Download from './Download';
|
|
4
|
+
import { DELETE_FROM_CACHE, UPDATE_CACHE } from './constants/FetchConstants';
|
|
4
5
|
import FetchErrorHandler from './middleware/FetchErrorHandler';
|
|
6
|
+
import { dispatch, serializeData } from "@selkirk-systems/state-management";
|
|
5
7
|
|
|
6
8
|
// Regular expression patterns for testing content-type response headers.
|
|
7
9
|
const RE_CONTENT_TYPE_JSON = /^application\/(x-)?json/i;
|
|
@@ -14,6 +16,10 @@ const CONTENT_TYPE_DOWNLOADS = {
|
|
|
14
16
|
'application/pdf': true,
|
|
15
17
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': true
|
|
16
18
|
}
|
|
19
|
+
|
|
20
|
+
//30 minutes
|
|
21
|
+
const CACHED_EXPIRY_TIMESTAMP = 30 * 60000;
|
|
22
|
+
|
|
17
23
|
//We store the original promise.catch so we can override it in some
|
|
18
24
|
//scenarios when we want to swallow errors vs bubble them up.
|
|
19
25
|
const ORIGINAL_CATCH_FN = Promise.prototype.catch;
|
|
@@ -51,6 +57,12 @@ export function OnOKResponse( fn ) {
|
|
|
51
57
|
return fn( [network, isAbort] );
|
|
52
58
|
}
|
|
53
59
|
}
|
|
60
|
+
|
|
61
|
+
function isServiceWorker() {
|
|
62
|
+
return self;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
|
|
54
66
|
/**
|
|
55
67
|
* Make the fetch request with the given configuration options.
|
|
56
68
|
*
|
|
@@ -66,7 +78,7 @@ export function OnOKResponse( fn ) {
|
|
|
66
78
|
* - status.text
|
|
67
79
|
* - status.isAbort
|
|
68
80
|
*/
|
|
69
|
-
|
|
81
|
+
function Fetch( url, options = {} ) {
|
|
70
82
|
|
|
71
83
|
const config = {
|
|
72
84
|
downloadFileName: null,
|
|
@@ -121,10 +133,11 @@ export default function Fetch( url, options = {} ) {
|
|
|
121
133
|
// For form data posts, we want the browser to build the Content-
|
|
122
134
|
// Type for us so that it puts in both the "multipart/form-data" plus the
|
|
123
135
|
// correct, auto-generated field delimiter.
|
|
124
|
-
delete ( finalHeaders["content-type"] );
|
|
136
|
+
//delete ( finalHeaders["content-type"] );
|
|
125
137
|
|
|
126
|
-
finalMethod = "POST";
|
|
127
|
-
finalBody = buildFormData( config.form );
|
|
138
|
+
//finalMethod = "POST";
|
|
139
|
+
//finalBody = buildFormData( config.form );
|
|
140
|
+
finalBody = config.form;
|
|
128
141
|
|
|
129
142
|
} else if ( config.json ) {
|
|
130
143
|
|
|
@@ -140,7 +153,7 @@ export default function Fetch( url, options = {} ) {
|
|
|
140
153
|
finalHeaders["content-type"] = config.contentType;
|
|
141
154
|
}
|
|
142
155
|
|
|
143
|
-
request = new
|
|
156
|
+
request = new Request(
|
|
144
157
|
finalUrl,
|
|
145
158
|
{
|
|
146
159
|
headers: finalHeaders,
|
|
@@ -158,7 +171,7 @@ export default function Fetch( url, options = {} ) {
|
|
|
158
171
|
//Cache requests abort signal by url
|
|
159
172
|
cacheRequestSignal( finalUrl, finalSignal );
|
|
160
173
|
|
|
161
|
-
config._promiseChain = Promise.resolve(
|
|
174
|
+
config._promiseChain = Promise.resolve( fetch( request ) )
|
|
162
175
|
.then( async ( response ) => {
|
|
163
176
|
|
|
164
177
|
deleteCachedRequestSignal( finalUrl );
|
|
@@ -381,8 +394,14 @@ function buildURL( url, templateData, params ) {
|
|
|
381
394
|
if ( url.href ) return url;
|
|
382
395
|
|
|
383
396
|
const formattedUrl = buildURLTemplate( url, templateData );
|
|
397
|
+
let finalUrl;
|
|
384
398
|
|
|
385
|
-
|
|
399
|
+
if ( self ) {
|
|
400
|
+
finalUrl = new URL( formattedUrl );
|
|
401
|
+
}
|
|
402
|
+
else {
|
|
403
|
+
finalUrl = new URL( formattedUrl, `${window.location.origin}${window.baseUrl || ""}` );
|
|
404
|
+
}
|
|
386
405
|
|
|
387
406
|
const searchParams = new URLSearchParams();
|
|
388
407
|
|
|
@@ -529,4 +548,159 @@ function normalizeTransportError( transportError, request, config ) {
|
|
|
529
548
|
}
|
|
530
549
|
} );
|
|
531
550
|
|
|
532
|
-
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
const responseObjectJson = {
|
|
554
|
+
status: 200,
|
|
555
|
+
headers: {
|
|
556
|
+
'Content-Type': 'application/json'
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
export const getCacheByName = async ( name ) => {
|
|
561
|
+
try {
|
|
562
|
+
return caches.open( name );
|
|
563
|
+
} catch ( err ) {
|
|
564
|
+
throw ( err );
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
export const putJsonInCache = ( cache, url, json ) => {
|
|
570
|
+
|
|
571
|
+
const response = new Response( JSON.stringify( json ), responseObjectJson );
|
|
572
|
+
|
|
573
|
+
return cache.put( url, response )
|
|
574
|
+
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
let _cache = null;
|
|
578
|
+
const DATA_METHODS = {
|
|
579
|
+
"GET": null,
|
|
580
|
+
"PATCH": null,
|
|
581
|
+
"POST": null,
|
|
582
|
+
"PUT": null
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
const _fetch = ( url, options = {} ) => {
|
|
586
|
+
|
|
587
|
+
if ( self ) {
|
|
588
|
+
return Fetch( url, options );
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
function cacheResponse( [response, isAbort] ) {
|
|
592
|
+
|
|
593
|
+
const status = response.status.code;
|
|
594
|
+
const headers = response.request.headers;
|
|
595
|
+
const method = response.request.method;
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
if ( status >= 200 && status < 400 && headers.get( 'content-type' ) === "application/json" ) {
|
|
599
|
+
|
|
600
|
+
if ( DATA_METHODS.hasOwnProperty( method ) || method === "GET" ) {
|
|
601
|
+
|
|
602
|
+
const data = serializeData( response.data );
|
|
603
|
+
|
|
604
|
+
if ( data.page && !data.items || data.items && data.items.length === 0 ) {
|
|
605
|
+
|
|
606
|
+
deleteFromCache( _cache, url, response );
|
|
607
|
+
|
|
608
|
+
return [response, isAbort];
|
|
609
|
+
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
const finalOptions = { ...responseObjectJson };
|
|
613
|
+
finalOptions.headers['Time-Cached'] = new Date().getTime();
|
|
614
|
+
|
|
615
|
+
const responseObj = new Response( JSON.stringify( response ), finalOptions );
|
|
616
|
+
_cache.put( url, responseObj );
|
|
617
|
+
|
|
618
|
+
dispatch( UPDATE_CACHE, { url: url, response: response } );
|
|
619
|
+
|
|
620
|
+
return [response, isAbort];
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
if ( method === "DELETE" ) {
|
|
624
|
+
|
|
625
|
+
deleteFromCache( _cache, url, response );
|
|
626
|
+
return [response, isAbort];
|
|
627
|
+
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
return [response, isAbort];
|
|
634
|
+
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
function cacheMatch() {
|
|
638
|
+
|
|
639
|
+
//HANDLE: Data updates, always return fresh data
|
|
640
|
+
if ( options.method && DATA_METHODS.hasOwnProperty( options.method ) ) {
|
|
641
|
+
|
|
642
|
+
return Fetch( url, options ).then( cacheResponse );
|
|
643
|
+
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
|
|
647
|
+
return _cache.match( url ).then( ( response ) => {
|
|
648
|
+
|
|
649
|
+
if ( response ) {
|
|
650
|
+
|
|
651
|
+
const timeCached = response.headers.get( 'Time-Cached' );
|
|
652
|
+
|
|
653
|
+
if ( expiredCache( timeCached ) ) {
|
|
654
|
+
|
|
655
|
+
_cache.delete( url );
|
|
656
|
+
return Fetch( url, options ).then( cacheResponse );
|
|
657
|
+
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
Fetch( url, options ).then( cacheResponse )
|
|
661
|
+
|
|
662
|
+
return unwrapResponseData( response ).then( ( obj ) => {
|
|
663
|
+
return Promise.resolve( [{
|
|
664
|
+
request: null,
|
|
665
|
+
response: response,
|
|
666
|
+
data: obj.data,
|
|
667
|
+
status: {
|
|
668
|
+
code: response.status,
|
|
669
|
+
text: response.statusText,
|
|
670
|
+
isAbort: false
|
|
671
|
+
},
|
|
672
|
+
}, false] )
|
|
673
|
+
} )
|
|
674
|
+
|
|
675
|
+
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
return Fetch( url, options ).then( cacheResponse );
|
|
679
|
+
} )
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
if ( _cache ) {
|
|
683
|
+
|
|
684
|
+
return cacheMatch()
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
return caches.open( 'fetch' ).then( ( cache ) => {
|
|
688
|
+
_cache = cache;
|
|
689
|
+
} ).then( cacheMatch )
|
|
690
|
+
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
function deleteFromCache( _cache, url, response ) {
|
|
694
|
+
|
|
695
|
+
_cache.delete( url );
|
|
696
|
+
dispatch( DELETE_FROM_CACHE, { url: url, response: response } );
|
|
697
|
+
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
function expiredCache( timeCached ) {
|
|
701
|
+
const now = new Date().getTime();
|
|
702
|
+
|
|
703
|
+
return Math.abs( now - timeCached ) >= CACHED_EXPIRY_TIMESTAMP;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
export default _fetch;
|
package/lib/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { dispatch } from "@selkirk-systems/state-management";
|
|
2
|
-
import { ADD_ERROR } from "../constants/
|
|
2
|
+
import { ADD_ERROR } from "../constants/FetchConstants";
|
|
3
3
|
|
|
4
4
|
const handler = ( err ) => ( response ) => ( options ) => ( next ) => {
|
|
5
5
|
if ( err ) {
|
|
@@ -11,32 +11,35 @@ const handler = ( err ) => ( response ) => ( options ) => ( next ) => {
|
|
|
11
11
|
return next( null, response );
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
-
//
|
|
15
|
-
|
|
16
|
-
// NOTE:
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
14
|
+
//HANDLE: Not being called from a service worker
|
|
15
|
+
if ( !self ) {
|
|
16
|
+
// NOTE: event name is all lower case as per DOM convention
|
|
17
|
+
window.addEventListener( "unhandledrejection", function ( e ) {
|
|
18
|
+
// NOTE: e.preventDefault() must be manually called to prevent the default
|
|
19
|
+
// action which is currently to log the stack trace to console.warn
|
|
20
|
+
e.preventDefault();
|
|
21
|
+
// NOTE: parameters are properties of the event detail property
|
|
22
|
+
var reason = e.reason || e.detail.reason;
|
|
23
|
+
//var promise = e.detail.promise;
|
|
24
|
+
// See Promise.onPossiblyUnhandledRejection for parameter documentation
|
|
25
|
+
console.groupEnd();
|
|
26
|
+
console.groupEnd();
|
|
27
|
+
console.groupEnd();
|
|
28
|
+
throw reason;
|
|
29
|
+
} );
|
|
28
30
|
|
|
29
|
-
// NOTE: event name is all lower case as per DOM convention
|
|
30
|
-
window.addEventListener( "rejectionhandled", function ( e ) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
} );
|
|
31
|
+
// NOTE: event name is all lower case as per DOM convention
|
|
32
|
+
window.addEventListener( "rejectionhandled", function ( e ) {
|
|
33
|
+
// NOTE: e.preventDefault() must be manually called prevent the default
|
|
34
|
+
// action which is currently unset (but might be set to something in the future)
|
|
35
|
+
e.preventDefault();
|
|
36
|
+
// NOTE: parameters are properties of the event detail property
|
|
37
|
+
var promise = e.reason || e.detail.promise;
|
|
38
|
+
// See Promise.onUnhandledRejectionHandled for parameter documentation
|
|
39
|
+
console.groupEnd();
|
|
40
|
+
console.log( "REJECTION HANDLED", promise );
|
|
41
|
+
} );
|
|
42
|
+
}
|
|
40
43
|
|
|
41
44
|
export default handler;
|
|
42
45
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@selkirk-systems/fetch",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Abortable fetch library",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"author": "Marcos Bernal <mbernal@selkirksystems.com>",
|
|
@@ -36,5 +36,5 @@
|
|
|
36
36
|
"peerDependencies": {
|
|
37
37
|
"@selkirk-systems/state-management": ">=1.0.0"
|
|
38
38
|
},
|
|
39
|
-
"gitHead": "
|
|
39
|
+
"gitHead": "5898e1276808bd3fe25d44f3935befe00c54513e"
|
|
40
40
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const ADD_ERROR = "ADD_ERROR";
|