@webqit/webflo 0.11.21 → 0.11.24

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