@selkirk-systems/fetch 1.1.0 → 1.2.1
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 +33 -12
- package/dist/middleware/FetchErrorHandler.js +28 -25
- package/lib/Fetch.js +42 -14
- package/lib/middleware/FetchErrorHandler.js +28 -25
- package/package.json +2 -2
package/dist/Fetch.js
CHANGED
|
@@ -50,6 +50,9 @@ export function OnOKResponse(fn) {
|
|
|
50
50
|
return fn([network, isAbort]);
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
|
+
function isServiceWorker() {
|
|
54
|
+
return self;
|
|
55
|
+
}
|
|
53
56
|
|
|
54
57
|
/**
|
|
55
58
|
* Make the fetch request with the given configuration options.
|
|
@@ -111,9 +114,11 @@ function Fetch(url, options = {}) {
|
|
|
111
114
|
// For form data posts, we want the browser to build the Content-
|
|
112
115
|
// Type for us so that it puts in both the "multipart/form-data" plus the
|
|
113
116
|
// correct, auto-generated field delimiter.
|
|
114
|
-
delete finalHeaders["content-type"];
|
|
115
|
-
|
|
116
|
-
|
|
117
|
+
//delete ( finalHeaders["content-type"] );
|
|
118
|
+
|
|
119
|
+
//finalMethod = "POST";
|
|
120
|
+
//finalBody = buildFormData( config.form );
|
|
121
|
+
finalBody = config.form;
|
|
117
122
|
} else if (config.json) {
|
|
118
123
|
finalHeaders["content-type"] = config.contentType || "application/x-json";
|
|
119
124
|
finalBody = JSON.stringify(config.json);
|
|
@@ -122,7 +127,7 @@ function Fetch(url, options = {}) {
|
|
|
122
127
|
} else {
|
|
123
128
|
finalHeaders["content-type"] = config.contentType;
|
|
124
129
|
}
|
|
125
|
-
request = new
|
|
130
|
+
request = new Request(finalUrl, {
|
|
126
131
|
headers: finalHeaders,
|
|
127
132
|
method: finalMethod,
|
|
128
133
|
body: finalBody,
|
|
@@ -136,7 +141,7 @@ function Fetch(url, options = {}) {
|
|
|
136
141
|
|
|
137
142
|
//Cache requests abort signal by url
|
|
138
143
|
cacheRequestSignal(finalUrl, finalSignal);
|
|
139
|
-
config._promiseChain = Promise.resolve(
|
|
144
|
+
config._promiseChain = Promise.resolve(fetch(request)).then(async response => {
|
|
140
145
|
deleteCachedRequestSignal(finalUrl);
|
|
141
146
|
const data = await unwrapResponseData(response);
|
|
142
147
|
if (response.ok) {
|
|
@@ -294,7 +299,12 @@ function buildFormData(form) {
|
|
|
294
299
|
function buildURL(url, templateData, params) {
|
|
295
300
|
if (url.href) return url;
|
|
296
301
|
const formattedUrl = buildURLTemplate(url, templateData);
|
|
297
|
-
|
|
302
|
+
let finalUrl;
|
|
303
|
+
if (self) {
|
|
304
|
+
finalUrl = new URL(formattedUrl);
|
|
305
|
+
} else {
|
|
306
|
+
finalUrl = new URL(formattedUrl, `${window.location.origin}${window.baseUrl || ""}`);
|
|
307
|
+
}
|
|
298
308
|
const searchParams = new URLSearchParams();
|
|
299
309
|
Object.entries(params).forEach(([key, value]) => {
|
|
300
310
|
if (Array.isArray(value)) {
|
|
@@ -428,14 +438,24 @@ export const putJsonInCache = (cache, url, json) => {
|
|
|
428
438
|
const response = new Response(JSON.stringify(json), responseObjectJson);
|
|
429
439
|
return cache.put(url, response);
|
|
430
440
|
};
|
|
431
|
-
|
|
441
|
+
const _caches = {};
|
|
432
442
|
const DATA_METHODS = {
|
|
433
443
|
"GET": null,
|
|
434
444
|
"PATCH": null,
|
|
435
445
|
"POST": null,
|
|
436
446
|
"PUT": null
|
|
437
447
|
};
|
|
438
|
-
const
|
|
448
|
+
const API_REG_EXP = /p\/.+com\/(.*?)\//;
|
|
449
|
+
export const getCacheNameFromUrl = url => {
|
|
450
|
+
const matchArray = url.toString().match(API_REG_EXP);
|
|
451
|
+
return matchArray && matchArray.length >= 1 ? matchArray[1] : null;
|
|
452
|
+
};
|
|
453
|
+
const _fetch = (url, options = {}) => {
|
|
454
|
+
const cacheName = getCacheNameFromUrl(url);
|
|
455
|
+
if (!self || !cacheName) {
|
|
456
|
+
return Fetch(url, options);
|
|
457
|
+
}
|
|
458
|
+
let _cache = _caches[cacheName];
|
|
439
459
|
function cacheResponse([response, isAbort]) {
|
|
440
460
|
const status = response.status.code;
|
|
441
461
|
const headers = response.request.headers;
|
|
@@ -451,7 +471,7 @@ const fetch = (url, options = {}) => {
|
|
|
451
471
|
...responseObjectJson
|
|
452
472
|
};
|
|
453
473
|
finalOptions.headers['Time-Cached'] = new Date().getTime();
|
|
454
|
-
const responseObj = new Response(JSON.stringify(response), finalOptions);
|
|
474
|
+
const responseObj = new Response(JSON.stringify(response.data), finalOptions);
|
|
455
475
|
_cache.put(url, responseObj);
|
|
456
476
|
dispatch(UPDATE_CACHE, {
|
|
457
477
|
url: url,
|
|
@@ -483,7 +503,7 @@ const fetch = (url, options = {}) => {
|
|
|
483
503
|
return Promise.resolve([{
|
|
484
504
|
request: null,
|
|
485
505
|
response: response,
|
|
486
|
-
data: obj
|
|
506
|
+
data: obj,
|
|
487
507
|
status: {
|
|
488
508
|
code: response.status,
|
|
489
509
|
text: response.statusText,
|
|
@@ -498,7 +518,8 @@ const fetch = (url, options = {}) => {
|
|
|
498
518
|
if (_cache) {
|
|
499
519
|
return cacheMatch();
|
|
500
520
|
}
|
|
501
|
-
return caches.open(
|
|
521
|
+
return caches.open(cacheName).then(cache => {
|
|
522
|
+
_caches[cacheName] = cache;
|
|
502
523
|
_cache = cache;
|
|
503
524
|
}).then(cacheMatch);
|
|
504
525
|
};
|
|
@@ -513,4 +534,4 @@ function expiredCache(timeCached) {
|
|
|
513
534
|
const now = new Date().getTime();
|
|
514
535
|
return Math.abs(now - timeCached) >= CACHED_EXPIRY_TIMESTAMP;
|
|
515
536
|
}
|
|
516
|
-
export default
|
|
537
|
+
export default _fetch;
|
|
@@ -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
|
@@ -17,7 +17,6 @@ const CONTENT_TYPE_DOWNLOADS = {
|
|
|
17
17
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': true
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
|
|
21
20
|
//30 minutes
|
|
22
21
|
const CACHED_EXPIRY_TIMESTAMP = 30 * 60000;
|
|
23
22
|
|
|
@@ -59,6 +58,10 @@ export function OnOKResponse( fn ) {
|
|
|
59
58
|
}
|
|
60
59
|
}
|
|
61
60
|
|
|
61
|
+
function isServiceWorker() {
|
|
62
|
+
return self;
|
|
63
|
+
}
|
|
64
|
+
|
|
62
65
|
|
|
63
66
|
/**
|
|
64
67
|
* Make the fetch request with the given configuration options.
|
|
@@ -130,10 +133,11 @@ function Fetch( url, options = {} ) {
|
|
|
130
133
|
// For form data posts, we want the browser to build the Content-
|
|
131
134
|
// Type for us so that it puts in both the "multipart/form-data" plus the
|
|
132
135
|
// correct, auto-generated field delimiter.
|
|
133
|
-
delete ( finalHeaders["content-type"] );
|
|
136
|
+
//delete ( finalHeaders["content-type"] );
|
|
134
137
|
|
|
135
|
-
finalMethod = "POST";
|
|
136
|
-
finalBody = buildFormData( config.form );
|
|
138
|
+
//finalMethod = "POST";
|
|
139
|
+
//finalBody = buildFormData( config.form );
|
|
140
|
+
finalBody = config.form;
|
|
137
141
|
|
|
138
142
|
} else if ( config.json ) {
|
|
139
143
|
|
|
@@ -149,7 +153,7 @@ function Fetch( url, options = {} ) {
|
|
|
149
153
|
finalHeaders["content-type"] = config.contentType;
|
|
150
154
|
}
|
|
151
155
|
|
|
152
|
-
request = new
|
|
156
|
+
request = new Request(
|
|
153
157
|
finalUrl,
|
|
154
158
|
{
|
|
155
159
|
headers: finalHeaders,
|
|
@@ -167,7 +171,7 @@ function Fetch( url, options = {} ) {
|
|
|
167
171
|
//Cache requests abort signal by url
|
|
168
172
|
cacheRequestSignal( finalUrl, finalSignal );
|
|
169
173
|
|
|
170
|
-
config._promiseChain = Promise.resolve(
|
|
174
|
+
config._promiseChain = Promise.resolve( fetch( request ) )
|
|
171
175
|
.then( async ( response ) => {
|
|
172
176
|
|
|
173
177
|
deleteCachedRequestSignal( finalUrl );
|
|
@@ -390,8 +394,14 @@ function buildURL( url, templateData, params ) {
|
|
|
390
394
|
if ( url.href ) return url;
|
|
391
395
|
|
|
392
396
|
const formattedUrl = buildURLTemplate( url, templateData );
|
|
397
|
+
let finalUrl;
|
|
393
398
|
|
|
394
|
-
|
|
399
|
+
if ( self ) {
|
|
400
|
+
finalUrl = new URL( formattedUrl );
|
|
401
|
+
}
|
|
402
|
+
else {
|
|
403
|
+
finalUrl = new URL( formattedUrl, `${window.location.origin}${window.baseUrl || ""}` );
|
|
404
|
+
}
|
|
395
405
|
|
|
396
406
|
const searchParams = new URLSearchParams();
|
|
397
407
|
|
|
@@ -564,7 +574,8 @@ export const putJsonInCache = ( cache, url, json ) => {
|
|
|
564
574
|
|
|
565
575
|
}
|
|
566
576
|
|
|
567
|
-
|
|
577
|
+
const _caches = {};
|
|
578
|
+
|
|
568
579
|
const DATA_METHODS = {
|
|
569
580
|
"GET": null,
|
|
570
581
|
"PATCH": null,
|
|
@@ -572,8 +583,22 @@ const DATA_METHODS = {
|
|
|
572
583
|
"PUT": null
|
|
573
584
|
}
|
|
574
585
|
|
|
575
|
-
const
|
|
586
|
+
const API_REG_EXP = /p\/.+com\/(.*?)\//;
|
|
587
|
+
|
|
588
|
+
export const getCacheNameFromUrl = ( url ) => {
|
|
589
|
+
const matchArray = url.toString().match( API_REG_EXP );
|
|
590
|
+
return matchArray && matchArray.length >= 1 ? matchArray[1] : null;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
const _fetch = ( url, options = {} ) => {
|
|
594
|
+
|
|
595
|
+
const cacheName = getCacheNameFromUrl( url );
|
|
596
|
+
|
|
597
|
+
if ( !self || !cacheName ) {
|
|
598
|
+
return Fetch( url, options );
|
|
599
|
+
}
|
|
576
600
|
|
|
601
|
+
let _cache = _caches[cacheName];
|
|
577
602
|
|
|
578
603
|
function cacheResponse( [response, isAbort] ) {
|
|
579
604
|
|
|
@@ -599,7 +624,7 @@ const fetch = ( url, options = {} ) => {
|
|
|
599
624
|
const finalOptions = { ...responseObjectJson };
|
|
600
625
|
finalOptions.headers['Time-Cached'] = new Date().getTime();
|
|
601
626
|
|
|
602
|
-
const responseObj = new Response( JSON.stringify( response ), finalOptions );
|
|
627
|
+
const responseObj = new Response( JSON.stringify( response.data ), finalOptions );
|
|
603
628
|
_cache.put( url, responseObj );
|
|
604
629
|
|
|
605
630
|
dispatch( UPDATE_CACHE, { url: url, response: response } );
|
|
@@ -650,7 +675,7 @@ const fetch = ( url, options = {} ) => {
|
|
|
650
675
|
return Promise.resolve( [{
|
|
651
676
|
request: null,
|
|
652
677
|
response: response,
|
|
653
|
-
data: obj
|
|
678
|
+
data: obj,
|
|
654
679
|
status: {
|
|
655
680
|
code: response.status,
|
|
656
681
|
text: response.statusText,
|
|
@@ -666,12 +691,15 @@ const fetch = ( url, options = {} ) => {
|
|
|
666
691
|
} )
|
|
667
692
|
}
|
|
668
693
|
|
|
669
|
-
if ( _cache ) {
|
|
670
694
|
|
|
695
|
+
|
|
696
|
+
if ( _cache ) {
|
|
671
697
|
return cacheMatch()
|
|
672
698
|
}
|
|
673
699
|
|
|
674
|
-
|
|
700
|
+
|
|
701
|
+
return caches.open( cacheName ).then( ( cache ) => {
|
|
702
|
+
_caches[cacheName] = cache;
|
|
675
703
|
_cache = cache;
|
|
676
704
|
} ).then( cacheMatch )
|
|
677
705
|
|
|
@@ -690,4 +718,4 @@ function expiredCache( timeCached ) {
|
|
|
690
718
|
return Math.abs( now - timeCached ) >= CACHED_EXPIRY_TIMESTAMP;
|
|
691
719
|
}
|
|
692
720
|
|
|
693
|
-
export default
|
|
721
|
+
export default _fetch;
|
|
@@ -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.1
|
|
3
|
+
"version": "1.2.1",
|
|
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": "319e3d6212d678f409052a628b9a28455ecc7ecd"
|
|
40
40
|
}
|