@webqit/webflo 0.11.61-0 → 0.11.61

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.
Files changed (91) hide show
  1. package/.gitignore +7 -7
  2. package/LICENSE +20 -20
  3. package/README.md +2079 -2074
  4. package/docker/Dockerfile +42 -42
  5. package/docker/README.md +91 -91
  6. package/docker/package.json +2 -2
  7. package/package.json +80 -81
  8. package/src/Context.js +79 -79
  9. package/src/config-pi/deployment/Env.js +68 -68
  10. package/src/config-pi/deployment/Layout.js +63 -63
  11. package/src/config-pi/deployment/Origins.js +139 -139
  12. package/src/config-pi/deployment/Proxy.js +74 -74
  13. package/src/config-pi/deployment/index.js +17 -17
  14. package/src/config-pi/index.js +15 -15
  15. package/src/config-pi/runtime/Client.js +116 -98
  16. package/src/config-pi/runtime/Server.js +125 -125
  17. package/src/config-pi/runtime/client/Worker.js +109 -134
  18. package/src/config-pi/runtime/client/index.js +11 -11
  19. package/src/config-pi/runtime/index.js +17 -17
  20. package/src/config-pi/runtime/server/Headers.js +74 -74
  21. package/src/config-pi/runtime/server/Redirects.js +69 -69
  22. package/src/config-pi/runtime/server/index.js +13 -13
  23. package/src/config-pi/static/Manifest.js +319 -319
  24. package/src/config-pi/static/Ssg.js +49 -49
  25. package/src/config-pi/static/index.js +13 -13
  26. package/src/deployment-pi/index.js +10 -10
  27. package/src/deployment-pi/origins/index.js +216 -216
  28. package/src/index.js +19 -19
  29. package/src/runtime-pi/Application.js +28 -28
  30. package/src/runtime-pi/Cookies.js +81 -81
  31. package/src/runtime-pi/HttpEvent.js +106 -106
  32. package/src/runtime-pi/Router.js +130 -130
  33. package/src/runtime-pi/Runtime.js +20 -20
  34. package/src/runtime-pi/client/Application.js +76 -100
  35. package/src/runtime-pi/client/Context.js +7 -7
  36. package/src/runtime-pi/client/Router.js +48 -48
  37. package/src/runtime-pi/client/Runtime.js +524 -331
  38. package/src/runtime-pi/client/Url.js +204 -205
  39. package/src/runtime-pi/client/Workport.js +190 -178
  40. package/src/runtime-pi/client/createStorage.js +57 -56
  41. package/src/runtime-pi/client/generate.js +480 -471
  42. package/src/runtime-pi/client/index.js +21 -21
  43. package/src/runtime-pi/client/worker/Application.js +44 -44
  44. package/src/runtime-pi/client/worker/Context.js +7 -7
  45. package/src/runtime-pi/client/worker/Runtime.js +274 -268
  46. package/src/runtime-pi/client/worker/Workport.js +77 -85
  47. package/src/runtime-pi/client/worker/index.js +21 -21
  48. package/src/runtime-pi/index.js +13 -13
  49. package/src/runtime-pi/server/Application.js +100 -115
  50. package/src/runtime-pi/server/Context.js +15 -15
  51. package/src/runtime-pi/server/Router.js +159 -159
  52. package/src/runtime-pi/server/Runtime.js +557 -556
  53. package/src/runtime-pi/server/index.js +21 -21
  54. package/src/runtime-pi/util-http.js +85 -85
  55. package/src/runtime-pi/util-url.js +146 -146
  56. package/src/runtime-pi/xFormData.js +23 -23
  57. package/src/runtime-pi/xHeaders.js +145 -145
  58. package/src/runtime-pi/xRequest.js +45 -45
  59. package/src/runtime-pi/xRequestHeaders.js +108 -108
  60. package/src/runtime-pi/xResponse.js +32 -32
  61. package/src/runtime-pi/xResponseHeaders.js +116 -116
  62. package/src/runtime-pi/xURL.js +105 -105
  63. package/src/runtime-pi/xfetch.js +22 -22
  64. package/src/runtime-pi/xxHttpMessage.js +101 -101
  65. package/src/services-pi/cert/http-auth-hook.js +22 -22
  66. package/src/services-pi/cert/http-cleanup-hook.js +22 -22
  67. package/src/services-pi/cert/index.js +79 -79
  68. package/src/services-pi/index.js +8 -8
  69. package/src/static-pi/index.js +10 -10
  70. package/src/webflo.js +30 -30
  71. package/test/index.test.js +26 -26
  72. package/test/site/package.json +9 -9
  73. package/test/site/public/bundle.html +5 -5
  74. package/test/site/public/bundle.html.json +3 -3
  75. package/test/site/public/bundle.js +2 -2
  76. package/test/site/public/bundle.webflo.js +15 -15
  77. package/test/site/public/index.html +29 -29
  78. package/test/site/public/index1.html +34 -34
  79. package/test/site/public/page-2/bundle.html +4 -4
  80. package/test/site/public/page-2/bundle.js +2 -2
  81. package/test/site/public/page-2/index.html +45 -45
  82. package/test/site/public/page-2/main.html +2 -2
  83. package/test/site/public/page-4/subpage/bundle.js +2 -2
  84. package/test/site/public/page-4/subpage/index.html +30 -30
  85. package/test/site/public/sparoots.json +4 -4
  86. package/test/site/public/worker.js +3 -3
  87. package/test/site/server/index.js +15 -15
  88. package/src/runtime-pi/client/oohtml/full.js +0 -7
  89. package/src/runtime-pi/client/oohtml/namespacing.js +0 -7
  90. package/src/runtime-pi/client/oohtml/scripting.js +0 -8
  91. package/src/runtime-pi/client/oohtml/templating.js +0 -8
@@ -1,269 +1,275 @@
1
-
2
- /**
3
- * @imports
4
- */
5
- import { _any } from '@webqit/util/arr/index.js';
6
- import { Observer } from '@webqit/oohtml-ssr/apis.js';
7
- import { pattern } from '../../util-url.js';
8
- import Workport from './Workport.js';
9
- import _Runtime from '../../Runtime.js';
10
- import xRequest from "../../xRequest.js";
11
- import xResponse from "../../xResponse.js";
12
- import xfetch from '../../xfetch.js';
13
- import HttpEvent from '../../HttpEvent.js';
14
-
15
- export {
16
- HttpEvent,
17
- Observer,
18
- }
19
-
20
- /**
21
- * ---------------------------
22
- * The Runtime Initializer
23
- * ---------------------------
24
- */
25
-
26
- export default class Runtime extends _Runtime {
27
-
28
- /**
29
- * Runtime
30
- *
31
- * @param Object cx
32
- * @param Function applicationInstance
33
- *
34
- * @return void
35
- */
36
- constructor(cx, applicationInstance) {
37
- super(cx, applicationInstance);
38
- // ---------------
39
- this.mockSessionStore = {};
40
- // --------------
41
- // ONINSTALL
42
- self.addEventListener('install', evt => {
43
- if (this.cx.params.skip_waiting) { self.skipWaiting(); }
44
- // Manage CACHE
45
- if (this.cx.params.cache_name && (this.cx.params.cache_only_urls || []).length) {
46
- // Add files to cache
47
- evt.waitUntil( self.caches.open(this.cx.params.cache_name).then(cache => {
48
- if (this.cx.logger) { this.cx.logger.log('[ServiceWorker] Pre-caching resources.'); }
49
- const cache_only_urls = (this.cx.params.cache_only_urls || []).map(c => c.trim()).filter(c => c && !pattern(c, self.origin).isPattern());
50
- return cache.addAll(cache_only_urls);
51
- }) );
52
- }
53
- });
54
-
55
- // -------------
56
- // ONACTIVATE
57
- self.addEventListener('activate', evt => {
58
- evt.waitUntil( new Promise(async resolve => {
59
- if (this.cx.params.skip_waiting) { await self.clients.claim(); }
60
- // Manage CACHE
61
- if (this.cx.params.cache_name) {
62
- // Clear outdated CACHES
63
- await self.caches.keys().then(keyList => {
64
- return Promise.all(keyList.map(key => {
65
- if (key !== this.cx.params.cache_name && key !== this.cx.params.cache_name + '_json') {
66
- if (this.cx.logger) { this.cx.logger.log('[ServiceWorker] Removing old cache:', key); }
67
- return self.caches.delete(key);
68
- }
69
- }));
70
- })
71
- }
72
- resolve();
73
- }) );
74
- });
75
-
76
- // ---------------
77
- Observer.set(this, 'location', {});
78
- Observer.set(this, 'network', {});
79
- // ---------------
80
- Observer.observe(this.network, es => {
81
- //console.log('//////////', ...es.map(e => `${e.name}: ${e.value}`))
82
- });
83
-
84
- // -------------
85
- // ONFETCH
86
- self.addEventListener('fetch', event => {
87
- // URL schemes that might arrive here but not supported; e.g.: chrome-extension://
88
- if (!event.request.url.startsWith('http')) return;
89
- event.respondWith((async evt => {
90
- let requestingClient = await self.clients.get(evt.clientId);
91
- this.workport.setCurrentClient(requestingClient);
92
- const [ url, requestInit ] = await xRequest.rip(evt.request);
93
- // Now, the following is key:
94
- // The browser likes to use "force-cache" for "navigate" requests, when, e.g: re-entering your site with the back button
95
- // Problem here, force-cache forces out JSON not HTML as per webflo's design.
96
- // So, we detect this scenerio and avoid it.
97
- if (requestInit.cache === 'force-cache'/* && evt.request.mode === 'navigate' - even webflo client init call also comes with that... needs investigation */) {
98
- requestInit.cache = 'default';
99
- }
100
- return this.go(url, requestInit, { event: evt });
101
- })(event));
102
- });
103
-
104
- // -------------
105
- // Workport
106
- const workport = new Workport();
107
- Observer.set(this, 'workport', workport);
108
-
109
- // -------------
110
- // Initialize
111
- (async () => {
112
- if (!this.app.init) return;
113
- const request = this.generateRequest('/');
114
- const httpEvent = new HttpEvent(request, { srcType: 'initialization' }, (id = null, persistent = false) => this.getSession(httpEvent, id, persistent));
115
- await this.app.init(httpEvent, ( ...args ) => this.remoteFetch( ...args ));
116
- })();
117
-
118
- }
119
-
120
- /**
121
- * Performs a request.
122
- *
123
- * @param object|string url
124
- * @param object|Request init
125
- * @param object detail
126
- *
127
- * @return Response
128
- */
129
- async go(url, init = {}, detail = {}) {
130
- // ------------
131
- url = typeof url === 'string' ? new URL(url, self.location.origin) : url;
132
- if (!(init instanceof Request) && !init.referrer) {
133
- init = { referrer: this.location.href, ...init };
134
- }
135
- // ------------
136
- // The request object
137
- const request = this.generateRequest(url.href, init);
138
- if (detail.event) { Object.defineProperty(detail.event, 'request', { value: request }); }
139
- // The navigation event
140
- const httpEvent = new HttpEvent(request, detail, (id = null, persistent = false) => this.getSession(httpEvent, id, persistent));
141
- httpEvent.port.listen(message => {
142
- if (message.$type === 'handler:hints' && message.session) {
143
- // TODO: Sync session data from client
144
- return Promise.resolve();
145
- }
146
- });
147
- // Response
148
- let response;
149
- if (httpEvent.request.url.startsWith(self.origin)/* && httpEvent.request.mode === 'navigate'*/) {
150
- response = await this.app.handle(httpEvent, ( ...args ) => this.remoteFetch( ...args ));
151
- } else {
152
- response = await this.remoteFetch(httpEvent.request);
153
- }
154
- const finalResponse = await this.handleResponse(httpEvent, response);
155
- // Return value
156
- return finalResponse;
157
- }
158
-
159
- // Generates request object
160
- generateRequest(href, init = {}) {
161
- const request = new xRequest(href, init);
162
- return request;
163
- }
164
-
165
- // Generates session object
166
- getSession(e, id = null, persistent = false) {
167
- return {
168
- get: () => this.mockSessionStore,
169
- set: value => { this.mockSessionStore = value },
170
- };
171
- }
172
-
173
- // Initiates remote fetch and sets the status
174
- remoteFetch(request, ...args) {
175
- if (arguments.length > 1) {
176
- request = this.generateRequest(request, ...args);
177
- }
178
- const matchUrl = (patterns, url) => _any((patterns || []).map(p => p.trim()).filter(p => p), p => pattern(p, self.origin).test(url));
179
- const execFetch = () => {
180
- // network_first_urls
181
- if (!this.cx.params.default_fetching_strategy || this.cx.params.default_fetching_strategy === 'network-first' || matchUrl(this.cx.params.network_first_urls, request.url)) {
182
- Observer.set(this.network, 'strategy', 'network-first');
183
- return this.networkFetch(request, { cacheFallback: true, cacheRefresh: true });
184
- }
185
- // cache_first_urls
186
- if (this.cx.params.default_fetching_strategy === 'cache-first' || matchUrl(this.cx.params.cache_first_urls, request.url)) {
187
- Observer.set(this.network, 'strategy', 'cache-first');
188
- return this.cacheFetch(request, { networkFallback: true, cacheRefresh: true });
189
- }
190
- // network_only_urls
191
- if (this.cx.params.default_fetching_strategy === 'network-only' || matchUrl(this.cx.params.network_only_urls, request.url)) {
192
- Observer.set(this.network, 'strategy', 'network-only');
193
- return this.networkFetch(request, { cacheFallback: false, cacheRefresh: false });
194
- }
195
- // cache_only_urls
196
- if (this.cx.params.default_fetching_strategy === 'cache-only' || matchUrl(this.cx.params.cache_only_urls, request.url)) {
197
- Observer.set(this.network, 'strategy', 'cache-only');
198
- return this.cacheFetch(request, { networkFallback: false, cacheRefresh: false });
199
- }
200
- };
201
- let response = execFetch(request);
202
- // This catch() is NOT intended to handle failure of the fetch
203
- response.catch(e => Observer.set(this.network, 'error', e.message));
204
- // Return xResponse
205
- return response.then(_response => xResponse.compat(_response));
206
- }
207
-
208
- // Caching strategy: network_first
209
- networkFetch(request, params = {}) {
210
- if (!params.cacheFallback) {
211
- Observer.set(this.network, 'remote', true);
212
- return xfetch(request);
213
- }
214
- return xfetch(request).then(response => {
215
- if (params.cacheRefresh) this.refreshCache(request, response);
216
- Observer.set(this.network, 'remote', true);
217
- return response;
218
- }).catch(() => this.getRequestCache(request).then(cache => {
219
- Observer.set(this.network, 'cache', true);
220
- return cache.match(request);
221
- }));
222
- }
223
-
224
- // Caching strategy: cache_first
225
- cacheFetch(request, params = {}) {
226
- return this.getRequestCache(request).then(cache => cache.match(request).then(response => {
227
- // Nothing cache, use network
228
- if (!response && params.networkFallback) return this.networkFetch(request, { ...params, cacheFallback: false });
229
- // Note: fetch, but for refreshing purposes only... not the returned response
230
- if (response && params.cacheRefresh) this.networkFetch(request, { ...params, justRefreshing: true });
231
- Observer.set(this.network, 'cache', true);
232
- return response;
233
- }));
234
- }
235
-
236
- // Caches response
237
- refreshCache(request, response) {
238
- // Check if we received a valid response
239
- if (request.method !== 'GET' || !response || response.status !== 200 || (response.type !== 'basic' && response.type !== 'cors')) {
240
- return response;
241
- }
242
- // IMPORTANT: Clone the response. A response is a stream
243
- // and because we want the browser to consume the response
244
- // as well as the cache consuming the response, we need
245
- // to clone it so we have two streams.
246
- var responseToCache = response.clone();
247
- this.getRequestCache(request).then(cache => {
248
- Observer.set(this.network, 'cacheRefresh', true);
249
- cache.put(request, responseToCache);
250
- });
251
- return response;
252
- }
253
-
254
- // Returns either the regular cache or a json-specific cache
255
- getRequestCache(request) {
256
- let cacheName = request.headers.get('Accept') === 'application/json'
257
- ? this.cx.params.cache_name + '_json'
258
- : this.cx.params.cache_name;
259
- return self.caches.open(cacheName);
260
- }
261
-
262
- // Handles response object
263
- handleResponse(e, response) {
264
- if (!response && response !== 0) { response = new xResponse(null, { status: 404 }); }
265
- else if (!(response instanceof xResponse)) { response = xResponse.compat(response); }
266
- return response;
267
- }
268
-
1
+
2
+ /**
3
+ * @imports
4
+ */
5
+ import Observer from '@webqit/observer';
6
+ import { _any } from '@webqit/util/arr/index.js';
7
+ import { pattern } from '../../util-url.js';
8
+ import Workport from './Workport.js';
9
+ import _Runtime from '../../Runtime.js';
10
+ import xRequest from "../../xRequest.js";
11
+ import xResponse from "../../xResponse.js";
12
+ import xfetch from '../../xfetch.js';
13
+ import HttpEvent from '../../HttpEvent.js';
14
+
15
+ export {
16
+ HttpEvent,
17
+ Observer,
18
+ }
19
+
20
+ /**
21
+ * ---------------------------
22
+ * The Runtime Initializer
23
+ * ---------------------------
24
+ */
25
+
26
+ export default class Runtime extends _Runtime {
27
+
28
+ /**
29
+ * Runtime
30
+ *
31
+ * @param Object cx
32
+ * @param Function applicationInstance
33
+ *
34
+ * @return void
35
+ */
36
+ constructor(cx, applicationInstance) {
37
+ super(cx, applicationInstance);
38
+ // ---------------
39
+ this.mockSessionStore = {};
40
+ // --------------
41
+ // ONINSTALL
42
+ self.addEventListener('install', evt => {
43
+ if (this.cx.params.skip_waiting) { self.skipWaiting(); }
44
+ // Manage CACHE
45
+ if (this.cx.params.cache_name && (this.cx.params.cache_only_urls || []).length) {
46
+ // Add files to cache
47
+ evt.waitUntil( self.caches.open(this.cx.params.cache_name).then(async cache => {
48
+ if (this.cx.logger) { this.cx.logger.log('[ServiceWorker] Pre-caching resources.'); }
49
+ for (const urls of [ 'cache_first_urls', 'cache_only_urls' ]) {
50
+ const _urls = (this.cx.params[urls] || []).map(c => c.trim()).filter(c => c && !pattern(c, self.origin).isPattern());
51
+ await cache.addAll(_urls);
52
+ }
53
+ }) );
54
+ }
55
+ });
56
+
57
+ // -------------
58
+ // ONACTIVATE
59
+ self.addEventListener('activate', evt => {
60
+ evt.waitUntil( new Promise(async resolve => {
61
+ if (this.cx.params.skip_waiting) { await self.clients.claim(); }
62
+ // Manage CACHE
63
+ if (this.cx.params.cache_name) {
64
+ // Clear outdated CACHES
65
+ await self.caches.keys().then(keyList => {
66
+ return Promise.all(keyList.map(key => {
67
+ if (key !== this.cx.params.cache_name && key !== this.cx.params.cache_name + '_json') {
68
+ if (this.cx.logger) { this.cx.logger.log('[ServiceWorker] Removing old cache:', key); }
69
+ return self.caches.delete(key);
70
+ }
71
+ }));
72
+ })
73
+ }
74
+ resolve();
75
+ }) );
76
+ });
77
+
78
+ // ---------------
79
+ Observer.set(this, 'location', {});
80
+ Observer.set(this, 'network', {});
81
+
82
+ // -------------
83
+ // ONFETCH
84
+ self.addEventListener('fetch', event => {
85
+ // URL schemes that might arrive here but not supported; e.g.: chrome-extension://
86
+ if (!event.request.url.startsWith('http')) return;
87
+ event.respondWith((async evt => {
88
+ let requestingClient = await self.clients.get(evt.clientId);
89
+ this.workport.setCurrentClient(requestingClient);
90
+ const [ url, requestInit ] = await xRequest.rip(evt.request);
91
+ // Now, the following is key:
92
+ // The browser likes to use "force-cache" for "navigate" requests, when, e.g: re-entering your site with the back button
93
+ // Problem here, force-cache forces out JSON not HTML as per webflo's design.
94
+ // So, we detect this scenerio and avoid it.
95
+ if (requestInit.cache === 'force-cache'/* && evt.request.mode === 'navigate' - even webflo client init call also comes with that... needs investigation */) {
96
+ requestInit.cache = 'default';
97
+ }
98
+ return this.go(url, requestInit, { event: evt });
99
+ })(event));
100
+ });
101
+
102
+ // -------------
103
+ // Workport
104
+ const workport = new Workport();
105
+ Observer.set(this, 'workport', workport);
106
+
107
+ // -------------
108
+ // Initialize
109
+ (async () => {
110
+ if (!this.app.init) return;
111
+ const request = this.generateRequest('/');
112
+ const httpEvent = new HttpEvent(request, { navigationType: 'init' }, (id = null, persistent = false) => this.getSession(httpEvent, id, persistent));
113
+ await this.app.init(httpEvent, ( ...args ) => this.remoteFetch( ...args ));
114
+ })();
115
+
116
+ }
117
+
118
+ /**
119
+ * Performs a request.
120
+ *
121
+ * @param object|string url
122
+ * @param object|Request init
123
+ * @param object detail
124
+ *
125
+ * @return Response
126
+ */
127
+ async go(url, init = {}, detail = {}) {
128
+ // ------------
129
+ url = typeof url === 'string' ? new URL(url, self.location.origin) : url;
130
+ if (!(init instanceof Request) && !init.referrer) {
131
+ init = { referrer: this.location.href, ...init };
132
+ }
133
+ // ------------
134
+ // The request object
135
+ const request = this.generateRequest(url.href, init);
136
+ if (detail.event) { Object.defineProperty(detail.event, 'request', { value: request }); }
137
+ // The navigation event
138
+ const httpEvent = new HttpEvent(request, detail, (id = null, persistent = false) => this.getSession(httpEvent, id, persistent));
139
+ httpEvent.port.listen(message => {
140
+ if (message.$type === 'handler:hints' && message.session) {
141
+ // TODO: Sync session data from client
142
+ return Promise.resolve();
143
+ }
144
+ });
145
+ // Response
146
+ let response;
147
+ if (httpEvent.request.url.startsWith(self.origin)/* && httpEvent.request.mode === 'navigate'*/) {
148
+ response = await this.app.handle(httpEvent, ( ...args ) => this.remoteFetch( ...args ));
149
+ } else {
150
+ response = await this.remoteFetch(httpEvent.request);
151
+ }
152
+ const finalResponse = await this.handleResponse(httpEvent, response);
153
+ // Return value
154
+ return finalResponse;
155
+ }
156
+
157
+ // Generates request object
158
+ generateRequest(href, init = {}) {
159
+ const request = new xRequest(href, init);
160
+ return request;
161
+ }
162
+
163
+ // Generates session object
164
+ getSession(e, id = null, persistent = false) {
165
+ return {
166
+ get: () => this.mockSessionStore,
167
+ set: value => { this.mockSessionStore = value },
168
+ };
169
+ }
170
+
171
+ // Initiates remote fetch and sets the status
172
+ remoteFetch(request, ...args) {
173
+ if (arguments.length > 1) {
174
+ request = this.generateRequest(request, ...args);
175
+ }
176
+ const matchUrl = (patterns, url) => _any((patterns || []).map(p => p.trim()).filter(p => p), p => pattern(p, self.origin).test(url));
177
+ const execFetch = () => {
178
+ // cache_only_urls
179
+ if (matchUrl(this.cx.params.cache_only_urls, request.url)) {
180
+ Observer.set(this.network, 'strategy', 'cache-only');
181
+ return this.cacheFetch(request, { networkFallback: false, cacheRefresh: false });
182
+ }
183
+ // network_only_urls
184
+ if (matchUrl(this.cx.params.network_only_urls, request.url)) {
185
+ Observer.set(this.network, 'strategy', 'network-only');
186
+ return this.networkFetch(request, { cacheFallback: false, cacheRefresh: false });
187
+ }
188
+ // cache_first_urls
189
+ if (matchUrl(this.cx.params.cache_first_urls, request.url)) {
190
+ Observer.set(this.network, 'strategy', 'cache-first');
191
+ return this.cacheFetch(request, { networkFallback: true, cacheRefresh: true });
192
+ }
193
+ // network_first_urls
194
+ if (matchUrl(this.cx.params.network_first_urls, request.url) || !this.cx.params.default_fetching_strategy) {
195
+ Observer.set(this.network, 'strategy', 'network-first');
196
+ return this.networkFetch(request, { cacheFallback: true, cacheRefresh: true });
197
+ }
198
+ // Default strategy
199
+ Observer.set(this.network, 'strategy', this.cx.params.default_fetching_strategy);
200
+ switch (this.cx.params.default_fetching_strategy) {
201
+ case 'cache-only': return this.cacheFetch(request, { networkFallback: false, cacheRefresh: false });
202
+ case 'network-only': return this.networkFetch(request, { cacheFallback: false, cacheRefresh: false });
203
+ case 'cache-first': return this.cacheFetch(request, { networkFallback: true, cacheRefresh: true });
204
+ case 'network-first': return this.networkFetch(request, { cacheFallback: true, cacheRefresh: true });
205
+ }
206
+ };
207
+ let response = execFetch(request);
208
+ // This catch() is NOT intended to handle failure of the fetch
209
+ response.catch(e => Observer.set(this.network, 'error', e.message));
210
+ // Return xResponse
211
+ return response.then(_response => xResponse.compat(_response));
212
+ }
213
+
214
+ // Caching strategy: network_first
215
+ networkFetch(request, params = {}) {
216
+ if (!params.cacheFallback) {
217
+ Observer.set(this.network, 'remote', true);
218
+ return xfetch(request);
219
+ }
220
+ return xfetch(request).then(response => {
221
+ if (params.cacheRefresh) this.refreshCache(request, response);
222
+ Observer.set(this.network, 'remote', true);
223
+ return response;
224
+ }).catch(() => this.getRequestCache(request).then(cache => {
225
+ Observer.set(this.network, 'cache', true);
226
+ return cache.match(request);
227
+ }));
228
+ }
229
+
230
+ // Caching strategy: cache_first
231
+ cacheFetch(request, params = {}) {
232
+ return this.getRequestCache(request).then(cache => cache.match(request).then(response => {
233
+ // Nothing cache, use network
234
+ if (!response && params.networkFallback) return this.networkFetch(request, { ...params, cacheFallback: false });
235
+ // Note: fetch, but for refreshing purposes only... not the returned response
236
+ if (response && params.cacheRefresh) this.networkFetch(request, { ...params, justRefreshing: true });
237
+ Observer.set(this.network, 'cache', true);
238
+ return response;
239
+ }));
240
+ }
241
+
242
+ // Caches response
243
+ refreshCache(request, response) {
244
+ // Check if we received a valid response
245
+ if (request.method !== 'GET' || !response || response.status !== 200 || (response.type !== 'basic' && response.type !== 'cors')) {
246
+ return response;
247
+ }
248
+ // IMPORTANT: Clone the response. A response is a stream
249
+ // and because we want the browser to consume the response
250
+ // as well as the cache consuming the response, we need
251
+ // to clone it so we have two streams.
252
+ var responseToCache = response.clone();
253
+ this.getRequestCache(request).then(cache => {
254
+ Observer.set(this.network, 'cacheRefresh', true);
255
+ cache.put(request, responseToCache);
256
+ });
257
+ return response;
258
+ }
259
+
260
+ // Returns either the regular cache or a json-specific cache
261
+ getRequestCache(request) {
262
+ let cacheName = request.headers.get('Accept') === 'application/json'
263
+ ? this.cx.params.cache_name + '_json'
264
+ : this.cx.params.cache_name;
265
+ return self.caches.open(cacheName);
266
+ }
267
+
268
+ // Handles response object
269
+ handleResponse(e, response) {
270
+ if (!response && response !== 0) { response = new xResponse(null, { status: 404 }); }
271
+ else if (!(response instanceof xResponse)) { response = xResponse.compat(response); }
272
+ return response;
273
+ }
274
+
269
275
  }