@webqit/webflo 0.8.77 → 0.9.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/package.json +5 -12
- package/src/Cli.js +131 -0
- package/src/Configurator.js +97 -0
- package/src/Context.js +76 -0
- package/src/config-pi/deployment/Env.js +69 -0
- package/src/config-pi/deployment/Layout.js +65 -0
- package/src/config-pi/deployment/Origins.js +133 -0
- package/src/config-pi/deployment/Virtualization.js +65 -0
- package/src/config-pi/deployment/index.js +18 -0
- package/src/config-pi/index.js +16 -0
- package/src/config-pi/runtime/Client.js +59 -0
- package/src/config-pi/runtime/Server.js +174 -0
- package/src/config-pi/runtime/client/Worker.js +117 -0
- package/src/config-pi/runtime/client/index.js +12 -0
- package/src/config-pi/runtime/index.js +18 -0
- package/src/config-pi/runtime/server/Headers.js +90 -0
- package/src/config-pi/runtime/server/Redirects.js +108 -0
- package/src/config-pi/runtime/server/index.js +14 -0
- package/src/config-pi/static/Manifest.js +321 -0
- package/src/config-pi/static/Ssg.js +72 -0
- package/src/config-pi/static/index.js +14 -0
- package/src/deployment-pi/index.js +10 -0
- package/src/{services → deployment-pi}/origins/index.js +88 -58
- package/src/index.js +14 -147
- package/src/{runtime → runtime-pi}/Router.js +19 -19
- package/src/runtime-pi/client/Context.js +7 -0
- package/src/{runtime → runtime-pi}/client/Router.js +2 -2
- package/src/{runtime/client/Navigator.js → runtime-pi/client/Runtime.js} +148 -103
- package/src/runtime-pi/client/RuntimeClient.js +114 -0
- package/src/{runtime → runtime-pi}/client/Storage.js +1 -1
- package/src/{runtime → runtime-pi}/client/Url.js +2 -6
- package/src/{runtime/client/WorkerClient.js → runtime-pi/client/WorkerComm.js} +2 -2
- package/src/runtime-pi/client/generate.js +242 -0
- package/src/runtime-pi/client/generate.oohtml.js +7 -0
- package/src/runtime-pi/client/index.js +18 -0
- package/src/runtime-pi/client/whatwag.js +27 -0
- package/src/runtime-pi/client/worker/Context.js +7 -0
- package/src/runtime-pi/client/worker/Worker.js +243 -0
- package/src/runtime-pi/client/worker/WorkerClient.js +46 -0
- package/src/runtime-pi/client/worker/index.js +18 -0
- package/src/runtime-pi/index.js +14 -0
- package/src/runtime-pi/server/Context.js +16 -0
- package/src/{runtime → runtime-pi}/server/Router.js +6 -6
- package/src/runtime-pi/server/Runtime.js +531 -0
- package/src/runtime-pi/server/RuntimeClient.js +103 -0
- package/src/runtime-pi/server/index.js +41 -0
- package/src/runtime-pi/server/whatwag.js +35 -0
- package/src/{runtime → runtime-pi}/util.js +0 -0
- package/src/{runtime/_FormData.js → runtime-pi/xFormData.js} +2 -2
- package/src/{runtime/_Headers.js → runtime-pi/xHeaders.js} +4 -4
- package/src/runtime-pi/xHttpEvent.js +93 -0
- package/src/runtime-pi/xHttpMessage.js +179 -0
- package/src/runtime-pi/xRequest.js +67 -0
- package/src/runtime-pi/xRequestHeaders.js +95 -0
- package/src/runtime-pi/xResponse.js +62 -0
- package/src/{runtime/_ResponseHeaders.js → runtime-pi/xResponseHeaders.js} +38 -18
- package/src/{runtime/_URL.js → runtime-pi/xURL.js} +4 -4
- package/src/runtime-pi/xfetch.js +7 -0
- package/src/{services → services-pi}/certbot/http-auth-hook.js +0 -0
- package/src/{services → services-pi}/certbot/http-cleanup-hook.js +0 -0
- package/src/{services → services-pi}/certbot/index.js +21 -15
- package/src/services-pi/index.js +9 -0
- package/src/static-pi/index.js +11 -0
- package/src/webflo.js +33 -0
- package/test/index.test.js +26 -0
- package/src/build/client/index.js +0 -261
- package/src/build/index.js +0 -5
- package/src/config/client.js +0 -191
- package/src/config/headers.js +0 -121
- package/src/config/index.js +0 -14
- package/src/config/layout.js +0 -83
- package/src/config/manifest.js +0 -341
- package/src/config/origins.js +0 -165
- package/src/config/prerendering.js +0 -100
- package/src/config/redirects.js +0 -137
- package/src/config/server.js +0 -201
- package/src/config/variables.js +0 -102
- package/src/config/vhosts.js +0 -93
- package/src/runtime/_MessageStream.js +0 -195
- package/src/runtime/_NavigationEvent.js +0 -91
- package/src/runtime/_Request.js +0 -59
- package/src/runtime/_RequestHeaders.js +0 -72
- package/src/runtime/_Response.js +0 -56
- package/src/runtime/client/NavigationEvent.js +0 -21
- package/src/runtime/client/Runtime.js +0 -126
- package/src/runtime/client/Worker.js +0 -317
- package/src/runtime/client/archive/Cache.js +0 -38
- package/src/runtime/client/archive/Http.js +0 -225
- package/src/runtime/client/archive/StdRequest.js +0 -74
- package/src/runtime/client/archive/WorkerComm.js +0 -183
- package/src/runtime/client/effects/sounds.js +0 -64
- package/src/runtime/index.js +0 -5
- package/src/runtime/server/NavigationEvent.js +0 -39
- package/src/runtime/server/Runtime.js +0 -593
- package/src/runtime/server/index.js +0 -183
- package/src/runtime/server/index.mjs +0 -10
- package/src/services/index.js +0 -6
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* @imports
|
|
4
|
-
*/
|
|
5
|
-
import _isObject from '@webqit/util/js/isObject.js';
|
|
6
|
-
import _before from '@webqit/util/str/before.js';
|
|
7
|
-
import _unique from '@webqit/util/arr/unique.js';
|
|
8
|
-
import _fetch from '@webqit/browser-pie/src/apis/fetch.js';
|
|
9
|
-
import NavigationEvent from './NavigationEvent.js';
|
|
10
|
-
import WorkerClient from './WorkerClient.js';
|
|
11
|
-
import Storage from './Storage.js';
|
|
12
|
-
import Router from './Router.js';
|
|
13
|
-
import Navigator from './Navigator.js';
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* ---------------------------
|
|
17
|
-
* The Client Initializer
|
|
18
|
-
* ---------------------------
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
export const { Observer } = window.WebQit;
|
|
22
|
-
export default function(layout, params) {
|
|
23
|
-
|
|
24
|
-
layout = {...layout};
|
|
25
|
-
params = {...params};
|
|
26
|
-
|
|
27
|
-
const session = Storage();
|
|
28
|
-
const workerClient = new WorkerClient('/worker.js', { startMessages: true });
|
|
29
|
-
const navigator = new Navigator(async (request, params, remoteFetch) => {
|
|
30
|
-
|
|
31
|
-
// The navigation event
|
|
32
|
-
const clientNavigationEvent = new NavigationEvent(
|
|
33
|
-
new NavigationEvent.Request(request),
|
|
34
|
-
session,
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
// The app router
|
|
38
|
-
const router = new Router(clientNavigationEvent.url.pathname, layout, {
|
|
39
|
-
layout,
|
|
40
|
-
onHydration: params.srcType === 'init',
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
// --------
|
|
44
|
-
// ROUTE FOR DATA
|
|
45
|
-
// --------
|
|
46
|
-
const httpMethodName = clientNavigationEvent.request.method.toLowerCase();
|
|
47
|
-
const response = await router.route([httpMethodName === 'delete' ? 'del' : httpMethodName, 'default'], clientNavigationEvent, document.state, async function(event) {
|
|
48
|
-
return remoteFetch(event.request).then(response => {
|
|
49
|
-
return new clientNavigationEvent.Response(response.body, {
|
|
50
|
-
status: response.status,
|
|
51
|
-
statusText: response.statusText,
|
|
52
|
-
headers: response.headers,
|
|
53
|
-
_proxy: {
|
|
54
|
-
url: response.url,
|
|
55
|
-
ok: response.ok,
|
|
56
|
-
redirected: response.redirected
|
|
57
|
-
},
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
}).catch(e => {
|
|
61
|
-
window.document.body.setAttribute('template', '');
|
|
62
|
-
throw e;
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
// --------
|
|
67
|
-
// Render
|
|
68
|
-
// --------
|
|
69
|
-
const data = response instanceof clientNavigationEvent.Response ? await response.data() : response;
|
|
70
|
-
await router.route('render', clientNavigationEvent, data, async function(event, data) {
|
|
71
|
-
// --------
|
|
72
|
-
// OOHTML would waiting for DOM-ready in order to be initialized
|
|
73
|
-
await new Promise(res => window.WebQit.DOM.ready(res));
|
|
74
|
-
if (!window.document.state.env) {
|
|
75
|
-
window.document.setState({
|
|
76
|
-
env: 'client',
|
|
77
|
-
onHydration: params.srcType === 'init',
|
|
78
|
-
network: navigator.network,
|
|
79
|
-
url: navigator.location,
|
|
80
|
-
session,
|
|
81
|
-
}, { update: true });
|
|
82
|
-
}
|
|
83
|
-
window.document.setState({ page: data }, { update: 'merge' });
|
|
84
|
-
window.document.body.setAttribute('template', 'page/' + clientNavigationEvent.url.pathname.split('/').filter(a => a).map(a => a + '+-').join('/'));
|
|
85
|
-
return new Promise(res => {
|
|
86
|
-
window.document.addEventListener('templatesreadystatechange', () => res(window));
|
|
87
|
-
if (window.document.templatesReadyState === 'complete') {
|
|
88
|
-
res(window);
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
// --------
|
|
94
|
-
// Render...
|
|
95
|
-
// --------
|
|
96
|
-
|
|
97
|
-
if (params.src instanceof Element) {
|
|
98
|
-
setTimeout(() => {
|
|
99
|
-
let viewportTop;
|
|
100
|
-
if (clientNavigationEvent.url.hash && (urlTarget = document.querySelector(clientNavigationEvent.url.hash))) {
|
|
101
|
-
urlTarget.scrollIntoView();
|
|
102
|
-
} else if (viewportTop = Array.from(document.querySelectorAll('[data-viewport-top]')).pop()) {
|
|
103
|
-
viewportTop.focus();
|
|
104
|
-
} else {
|
|
105
|
-
document.documentElement.classList.add('scroll-reset');
|
|
106
|
-
document.body.scrollIntoView();
|
|
107
|
-
setTimeout(() => {
|
|
108
|
-
document.documentElement.classList.remove('scroll-reset');
|
|
109
|
-
}, 600);
|
|
110
|
-
}
|
|
111
|
-
}, 0);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return response;
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
Observer.observe(session, changes => {
|
|
118
|
-
//console.log('SESSION_STATE_CHANGE', changes[0].name, changes[0].value);
|
|
119
|
-
});
|
|
120
|
-
Observer.observe(workerClient, changes => {
|
|
121
|
-
//console.log('SERVICE_WORKER_STATE_CHANGE', changes[0].name, changes[0].value);
|
|
122
|
-
});
|
|
123
|
-
Observer.observe(navigator, changes => {
|
|
124
|
-
//console.log('NAVIGATORSTATE_CHANGE', changes[0].name, changes[0].value);
|
|
125
|
-
});
|
|
126
|
-
};
|
|
@@ -1,317 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* @imports
|
|
4
|
-
*/
|
|
5
|
-
import Router from './Router.js';
|
|
6
|
-
import _isGlobe from 'is-glob';
|
|
7
|
-
import Minimatch from 'minimatch';
|
|
8
|
-
import Observer from '@webqit/observer';
|
|
9
|
-
import _isArray from '@webqit/util/js/isArray.js';
|
|
10
|
-
import _afterLast from '@webqit/util/str/afterLast.js';
|
|
11
|
-
import _after from '@webqit/util/str/after.js';
|
|
12
|
-
import _before from '@webqit/util/str/before.js';
|
|
13
|
-
import _any from '@webqit/util/arr/any.js';
|
|
14
|
-
import _copy from '@webqit/util/obj/copy.js';
|
|
15
|
-
import NavigationEvent from './NavigationEvent.js';
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* ---------------------------
|
|
19
|
-
* The Worker Initializer
|
|
20
|
-
* ---------------------------
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
export default function(layout, params) {
|
|
24
|
-
|
|
25
|
-
// Copy...
|
|
26
|
-
layout = { ...layout };
|
|
27
|
-
params = { ...params };
|
|
28
|
-
const sessionStores = Object.create(null);
|
|
29
|
-
const localStores = Object.create(null);
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* -------------
|
|
33
|
-
* ONINSTALL
|
|
34
|
-
* -------------
|
|
35
|
-
*/
|
|
36
|
-
|
|
37
|
-
self.addEventListener('install', evt => {
|
|
38
|
-
|
|
39
|
-
if (params.skip_waiting) {
|
|
40
|
-
self.skipWaiting();
|
|
41
|
-
}
|
|
42
|
-
// Manage CACHE
|
|
43
|
-
if (params.cache_name && params.static_caching_list) {
|
|
44
|
-
// Add files to cache
|
|
45
|
-
evt.waitUntil(
|
|
46
|
-
self.caches.open(params.cache_name).then(cache => {
|
|
47
|
-
if (params.lifecycle_logs) {
|
|
48
|
-
console.log('[ServiceWorker] Pre-caching resources.');
|
|
49
|
-
}
|
|
50
|
-
const cache_only_url_list = (params.cache_only_url_list || []).map(c => c.trim()).filter(c => c);//.reduce((all, url) => all.concat(Micromatch.brace(url, { expand: true })), []);
|
|
51
|
-
return cache.addAll(cache_only_url_list.filter(url => !_isGlobe(url) && !_afterLast(url, '.').includes('/')));
|
|
52
|
-
})
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* -------------
|
|
60
|
-
* ONACTIVATE
|
|
61
|
-
* -------------
|
|
62
|
-
*/
|
|
63
|
-
|
|
64
|
-
self.addEventListener('activate', evt => {
|
|
65
|
-
|
|
66
|
-
evt.waitUntil(
|
|
67
|
-
new Promise(async resolve => {
|
|
68
|
-
if (params.skip_waiting) {
|
|
69
|
-
await self.clients.claim();
|
|
70
|
-
}
|
|
71
|
-
// Manage CACHE
|
|
72
|
-
if (params.cache_name) {
|
|
73
|
-
// Clear outdated CACHES
|
|
74
|
-
await self.caches.keys().then(keyList => {
|
|
75
|
-
return Promise.all(keyList.map(key => {
|
|
76
|
-
if (key !== params.cache_name && key !== params.cache_name + '_json') {
|
|
77
|
-
if (params.lifecycle_logs) {
|
|
78
|
-
console.log('[ServiceWorker] Removing old cache:', key);
|
|
79
|
-
}
|
|
80
|
-
return self.caches.delete(key);
|
|
81
|
-
}
|
|
82
|
-
}));
|
|
83
|
-
})
|
|
84
|
-
}
|
|
85
|
-
resolve();
|
|
86
|
-
})
|
|
87
|
-
);
|
|
88
|
-
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* -------------
|
|
93
|
-
* ONFETCH
|
|
94
|
-
* -------------
|
|
95
|
-
*/
|
|
96
|
-
|
|
97
|
-
// Listen now...
|
|
98
|
-
self.addEventListener('fetch', async evt => {
|
|
99
|
-
// URL schemes that might arrive here but not supported; e.g.: chrome-extension://
|
|
100
|
-
if (!evt.request.url.startsWith('http')) return;
|
|
101
|
-
// Fetches request
|
|
102
|
-
const handleFetch = async evt => {
|
|
103
|
-
|
|
104
|
-
if (evt.request.url.startsWith(self.origin) && (evt.request.mode === 'navigate' || evt.request.headers.get('X-Powered-By') === '@webqit/webflo')) {
|
|
105
|
-
// -----------------
|
|
106
|
-
// We must patch the event object with the new request instance
|
|
107
|
-
// cos the body of the original gets consumed (thus invalidated) by the new instance
|
|
108
|
-
Object.defineProperty(evt, 'request', {
|
|
109
|
-
value: new NavigationEvent.Request(evt.request),
|
|
110
|
-
});
|
|
111
|
-
// Sync session data to cache to be available to service-worker routers
|
|
112
|
-
// Sync only takes for requests that actually do send the "$session" cookie
|
|
113
|
-
const sessionData = Observer.proxy(sessionStores[evt.clientId] || {});
|
|
114
|
-
const clientNavigationEvent = new NavigationEvent(evt.request, sessionData);
|
|
115
|
-
// -----------------
|
|
116
|
-
// The app router
|
|
117
|
-
const router = new Router(_before(evt.request.url, '?'), layout, { layout });
|
|
118
|
-
const httpMethodName = evt.request.method.toLowerCase();
|
|
119
|
-
const _response = await router.route([httpMethodName === 'delete' ? 'del' : httpMethodName, 'default'], clientNavigationEvent, null, (event, arg) => defaultFetch(evt));
|
|
120
|
-
if (!(_response instanceof Response)/* _response being a native Response instance is fine */) {
|
|
121
|
-
return new NavigationEvent.Response(_response);
|
|
122
|
-
}
|
|
123
|
-
return _response;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return defaultFetch(evt);
|
|
127
|
-
};
|
|
128
|
-
let response = handleFetch(evt);
|
|
129
|
-
evt.respondWith(response);
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
const defaultFetch = function(evt) {
|
|
133
|
-
if (_any((params.cache_only_url_list || []).map(c => c.trim()).filter(c => c), pattern => Minimatch.Minimatch(evt.request.url, pattern))) {
|
|
134
|
-
return cache_fetch(evt);
|
|
135
|
-
}
|
|
136
|
-
// Now, the following is key:
|
|
137
|
-
// The browser likes to use "force-cache" for "navigate" requests
|
|
138
|
-
// when, for example, the back button was used.
|
|
139
|
-
// Thus the origin server would still not be contacted by the self.fetch() below, leading to inconsistencies in responses.
|
|
140
|
-
// So, we detect this scenerio and avoid it.
|
|
141
|
-
if (evt.request.mode === 'navigate' && evt.request.cache === 'force-cache' && evt.request.destination === 'document') {
|
|
142
|
-
return cache_fetch(evt, false, true);
|
|
143
|
-
}
|
|
144
|
-
if (_any((params.cache_first_url_list || []).map(c => c.trim()).filter(c => c), pattern => Minimatch.Minimatch(evt.request.url, pattern))) {
|
|
145
|
-
return cache_fetch(evt, true/** cacheRefresh */);
|
|
146
|
-
}
|
|
147
|
-
if (_any((params.network_first_url_list || []).map(c => c.trim()).filter(c => c), pattern => Minimatch.Minimatch(evt.request.url, pattern))) {
|
|
148
|
-
return network_fetch(evt, true/** cacheFallback */);
|
|
149
|
-
}
|
|
150
|
-
return network_fetch(evt);
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
//evt.request.mode navigate evt.request.cache force-cache evt.request.destination document request.headers.get('Accept') text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
|
|
154
|
-
|
|
155
|
-
const getCacheName = request => request.headers.get('Accept') === 'application/json'
|
|
156
|
-
? params.cache_name + '_json'
|
|
157
|
-
: params.cache_name;
|
|
158
|
-
|
|
159
|
-
// Caching strategy: cache_first
|
|
160
|
-
const cache_fetch = (evt, cacheRefresh = false, is_Navigate_ForceCache_Document = false) => {
|
|
161
|
-
|
|
162
|
-
return self.caches.open(getCacheName(evt.request)).then(cache => {
|
|
163
|
-
return cache.match(evt.request).then(response => {
|
|
164
|
-
const force_network_fetch = evt => {
|
|
165
|
-
let request = evt.request;
|
|
166
|
-
if (is_Navigate_ForceCache_Document) {
|
|
167
|
-
let url = new URL(request.url);
|
|
168
|
-
url.searchParams.set('$force-cache', '1');
|
|
169
|
-
request = new Request(url, {
|
|
170
|
-
method: request.method,
|
|
171
|
-
headers: request.headers,
|
|
172
|
-
body: request.body,
|
|
173
|
-
mode: request.mode === 'navigate'/* throws */ ? null : request.mode,
|
|
174
|
-
credentials: request.credentials,
|
|
175
|
-
cache: request.cache,
|
|
176
|
-
redirect: request.redirect,
|
|
177
|
-
referrer: request.referrer,
|
|
178
|
-
integrity: request.integrity,
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
return self.fetch(request);
|
|
182
|
-
}
|
|
183
|
-
if (response) {
|
|
184
|
-
if (cacheRefresh) {
|
|
185
|
-
// Fetch, but return this immediately
|
|
186
|
-
force_network_fetch(evt).then(response => refreshCache(evt.request, response));
|
|
187
|
-
}
|
|
188
|
-
return response;
|
|
189
|
-
}
|
|
190
|
-
return force_network_fetch(evt).then(response => refreshCache(evt.request, response));
|
|
191
|
-
});
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
};
|
|
195
|
-
|
|
196
|
-
// Caching strategy: network_first
|
|
197
|
-
const network_fetch = (evt, cacheFallback) => {
|
|
198
|
-
if (!cacheFallback) {
|
|
199
|
-
return self.fetch(evt.request);
|
|
200
|
-
}
|
|
201
|
-
return self.fetch(evt.request).then(response => refreshCache(evt.request, response)).catch(e => {
|
|
202
|
-
return self.caches.open(getCacheName(evt.request)).then(cache => {
|
|
203
|
-
return cache.match(evt.request);
|
|
204
|
-
});
|
|
205
|
-
});
|
|
206
|
-
};
|
|
207
|
-
|
|
208
|
-
// Caches response
|
|
209
|
-
const refreshCache = (request, response) => {
|
|
210
|
-
|
|
211
|
-
// Check if we received a valid response
|
|
212
|
-
if ((request._method || request.method) !== 'GET' || !response || response.status !== 200 || (response.type !== 'basic' && response.type !== 'cors')) {
|
|
213
|
-
return response;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// IMPORTANT: Clone the response. A response is a stream
|
|
217
|
-
// and because we want the browser to consume the response
|
|
218
|
-
// as well as the cache consuming the response, we need
|
|
219
|
-
// to clone it so we have two streams.
|
|
220
|
-
var responseToCache = response.clone();
|
|
221
|
-
self.caches.open(getCacheName(request)).then(cache => {
|
|
222
|
-
if (params.lifecycle_logs) {
|
|
223
|
-
console.log('[ServiceWorker] Refreshing cache:', request.url);
|
|
224
|
-
}
|
|
225
|
-
cache.put(request, responseToCache);
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
return response;
|
|
229
|
-
};
|
|
230
|
-
|
|
231
|
-
// -----------------------------
|
|
232
|
-
|
|
233
|
-
const relay = function(evt, messageData) {
|
|
234
|
-
return self.clients.matchAll().then(clientList => {
|
|
235
|
-
clientList.forEach(client => {
|
|
236
|
-
if (client.id === evt.source.id) {
|
|
237
|
-
return;
|
|
238
|
-
}
|
|
239
|
-
client.postMessage(messageData);
|
|
240
|
-
});
|
|
241
|
-
});
|
|
242
|
-
};
|
|
243
|
-
|
|
244
|
-
self.addEventListener('message', evt => {
|
|
245
|
-
|
|
246
|
-
// Handle normally
|
|
247
|
-
const router = new Router('/', layout, { layout });
|
|
248
|
-
evt.waitUntil(
|
|
249
|
-
router.route('postmessage', evt, null, function() {
|
|
250
|
-
return self;
|
|
251
|
-
})
|
|
252
|
-
);
|
|
253
|
-
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
self.addEventListener('push', evt => {
|
|
257
|
-
const router = new Router('/', layout, { layout });
|
|
258
|
-
evt.waitUntil(
|
|
259
|
-
router.route('push', evt, null, function() {
|
|
260
|
-
return self;
|
|
261
|
-
})
|
|
262
|
-
);
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
self.addEventListener('notificationclick', evt => {
|
|
266
|
-
const router = new Router('/', layout, { layout });
|
|
267
|
-
evt.waitUntil(
|
|
268
|
-
router.route('notificationclick', evt, null, function() {
|
|
269
|
-
return self;
|
|
270
|
-
})
|
|
271
|
-
);
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
self.addEventListener('notificationclose', evt => {
|
|
275
|
-
const router = new Router('/', layout, { layout });
|
|
276
|
-
evt.waitUntil(
|
|
277
|
-
router.route('notificationclose', evt, null, function() {
|
|
278
|
-
return self;
|
|
279
|
-
})
|
|
280
|
-
);
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
};
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* @utils
|
|
287
|
-
*/
|
|
288
|
-
const matchClientUrl = (client, url) => '/' + _after(_after(client.url, '//'), '/') === url;
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
|
|
292
|
-
if (notificationData) {
|
|
293
|
-
var title = params.NOTIFICATION_TITLE || '';
|
|
294
|
-
if (_isArray(notificationData)) {
|
|
295
|
-
title = notificationData[0];
|
|
296
|
-
notificationData = notificationData[1];
|
|
297
|
-
}
|
|
298
|
-
return self.registration.showNotification(title, notificationData);
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
var cuurentClientAtUrl = self.clients.matchAll().then(clientList => {
|
|
302
|
-
// Take the user to the app... the current open window or a new window
|
|
303
|
-
return clientList.reduce((cuurentClientAtUrl, client) => {
|
|
304
|
-
return cuurentClientAtUrl || matchClientUrl(client, pathname) ? client : null;
|
|
305
|
-
}, null);
|
|
306
|
-
});
|
|
307
|
-
if (cuurentClientAtUrl) {
|
|
308
|
-
return cuurentClientAtUrl.focus();
|
|
309
|
-
}
|
|
310
|
-
return self.clients.openWindow(pathname);
|
|
311
|
-
|
|
312
|
-
return self.clients.matchAll().then(clientList => {
|
|
313
|
-
// Take the user to the app
|
|
314
|
-
return clientList.reduce((cuurentClientAtUrl, client) => cuurentClientAtUrl || matchClientUrl(client, pathname) ? client : null, null);
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
*/
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* @imports
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export function syncToCache(name, data) {
|
|
7
|
-
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
|
|
8
|
-
return caches.open(name)
|
|
9
|
-
.then(cache => cache.put('http://session.temp', new Response(blob)));
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function syncFromCache(name, del = false) {
|
|
13
|
-
return caches.open(name)
|
|
14
|
-
.then(cache => cache.match('http://session.temp')
|
|
15
|
-
.then(response => response ? response.json() : null))
|
|
16
|
-
.then(async data => (del ? await caches.delete(name) : null, data)).catch(e => null);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
const sessionData = Object.keys(clientNavigationEvent.session)
|
|
21
|
-
.reduce((obj, key) => (obj[key] = clientNavigationEvent.session[key], obj), {});
|
|
22
|
-
await syncToCache('$session', sessionData);
|
|
23
|
-
*/
|
|
24
|
-
/**
|
|
25
|
-
response.finally(async () => {
|
|
26
|
-
// Sync session data from cache that may have been exposed by service-worker routers
|
|
27
|
-
const sessionData = await syncFromCache('$session', true);
|
|
28
|
-
if (!sessionData) return;
|
|
29
|
-
const keysInCache = Object.keys(sessionData);
|
|
30
|
-
_unique(Object.keys(clientNavigationEvent.session).concat(keysInCache)).forEach(key => {
|
|
31
|
-
if (!keysInCache.includes(key)) {
|
|
32
|
-
delete clientNavigationEvent.session[key];
|
|
33
|
-
} else {
|
|
34
|
-
clientNavigationEvent.session[key] = sessionData[key];
|
|
35
|
-
}
|
|
36
|
-
});
|
|
37
|
-
});
|
|
38
|
-
*/
|
|
@@ -1,225 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* @imports
|
|
4
|
-
*/
|
|
5
|
-
import _before from '@webqit/util/str/before.js';
|
|
6
|
-
import _after from '@webqit/util/str/after.js';
|
|
7
|
-
import _toTitle from '@webqit/util/str/toTitle.js';
|
|
8
|
-
import _arrFrom from '@webqit/util/arr/from.js';
|
|
9
|
-
import { wwwFormUnserialize, wwwFormSet, wwwFormSerialize } from '../util.js';
|
|
10
|
-
import StdRequest from './StdRequest.js';
|
|
11
|
-
import { Observer } from './Runtime.js';
|
|
12
|
-
import Url from './Url.js';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* ---------------------------
|
|
16
|
-
* The Client class
|
|
17
|
-
* ---------------------------
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
export default class Http {
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Constructs a new Http instance. Typically,
|
|
24
|
-
*
|
|
25
|
-
* @param function client
|
|
26
|
-
* @param object params
|
|
27
|
-
*
|
|
28
|
-
* @return void
|
|
29
|
-
*/
|
|
30
|
-
static async createClient(client, params = {}) {
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* ----------------
|
|
34
|
-
* instance
|
|
35
|
-
* ----------------
|
|
36
|
-
*/
|
|
37
|
-
const instance = {
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Performs a request.
|
|
41
|
-
*
|
|
42
|
-
* @param object|string href
|
|
43
|
-
* @param object options
|
|
44
|
-
*
|
|
45
|
-
* @return void
|
|
46
|
-
*/
|
|
47
|
-
async go(url, options = {}) {
|
|
48
|
-
if (this.abortController) {
|
|
49
|
-
this.abortController.abort();
|
|
50
|
-
}
|
|
51
|
-
this.abortController = new AbortController();
|
|
52
|
-
let xRedirectCode = 300;
|
|
53
|
-
// Generates request object
|
|
54
|
-
let generateRequest = (url, options) => {
|
|
55
|
-
return new StdRequest(url, {
|
|
56
|
-
...options,
|
|
57
|
-
headers: {
|
|
58
|
-
'Accept': 'application/json',
|
|
59
|
-
'X-Redirect-Policy': 'manual-when-cross-origin',
|
|
60
|
-
'X-Redirect-Code': xRedirectCode,
|
|
61
|
-
'X-Powered-By': '@webqit/webflo',
|
|
62
|
-
...(options.headers || {}),
|
|
63
|
-
},
|
|
64
|
-
referrer: window.document.location.href,
|
|
65
|
-
signal: this.abortController.signal,
|
|
66
|
-
});
|
|
67
|
-
};
|
|
68
|
-
// Handles response object
|
|
69
|
-
let handleResponse = (response) => {
|
|
70
|
-
if (!response) return;
|
|
71
|
-
if (response.redirected && this.isSameOrigin(response.url)) {
|
|
72
|
-
Observer.set(this.location, { href: response.url }, {
|
|
73
|
-
detail: { isRedirect: true },
|
|
74
|
-
});
|
|
75
|
-
} else if (response.headers.get('Location') && response.status === xRedirectCode) {
|
|
76
|
-
window.location = response.headers.get('Location');
|
|
77
|
-
}
|
|
78
|
-
};
|
|
79
|
-
url = typeof url === 'string' ? { href: url } : url;
|
|
80
|
-
options = { referrer: this.location.href, ...options };
|
|
81
|
-
Observer.set(this.location, url, { detail: options, });
|
|
82
|
-
if (!(_before(url.href, '#') === _before(options.referrer, '#') && (options.method || 'GET').toUpperCase() === 'GET')) {
|
|
83
|
-
handleResponse(await client.call(this, generateRequest(url.href, options)));
|
|
84
|
-
}
|
|
85
|
-
},
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Checks if an URL is same origin.
|
|
89
|
-
*
|
|
90
|
-
* @param object|string url
|
|
91
|
-
*
|
|
92
|
-
* @return Bool
|
|
93
|
-
*/
|
|
94
|
-
isSameOrigin(url) {
|
|
95
|
-
if (typeof url === 'string') {
|
|
96
|
-
let href = url;
|
|
97
|
-
url = window.document.createElement('a');
|
|
98
|
-
url.href = href
|
|
99
|
-
}
|
|
100
|
-
return !url.origin || url.origin === this.location.origin;
|
|
101
|
-
},
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* History object
|
|
105
|
-
*/
|
|
106
|
-
get history() {
|
|
107
|
-
return window.history;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
// -----------------------
|
|
113
|
-
// Initialize network
|
|
114
|
-
Observer.set(instance, 'network', {});
|
|
115
|
-
window.addEventListener('online', () => Observer.set(instance.network, 'online', navigator.onLine));
|
|
116
|
-
window.addEventListener('offline', () => Observer.set(instance.network, 'online', navigator.onLine));
|
|
117
|
-
|
|
118
|
-
// -----------------------
|
|
119
|
-
// Initialize location
|
|
120
|
-
Observer.set(instance, 'location', new Url(window.document.location));
|
|
121
|
-
// -----------------------
|
|
122
|
-
// Syndicate changes to the browser;s location bar
|
|
123
|
-
Observer.observe(instance.location, [[ 'href' ]], ([e]) => {
|
|
124
|
-
if (e.value === 'http:' || (e.detail || {}).src === window.document.location) {
|
|
125
|
-
// Already from a "popstate" event as above, so don't push again
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
if (e.value === window.document.location.href || e.value + '/' === window.document.location.href) {
|
|
129
|
-
instance.history.replaceState(instance.history.state, '', instance.location.href);
|
|
130
|
-
} else {
|
|
131
|
-
try { instance.history.pushState(instance.history.state, '', instance.location.href); } catch(e) {}
|
|
132
|
-
}
|
|
133
|
-
}, { diff: true });
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* ----------------
|
|
137
|
-
* Navigation Interception
|
|
138
|
-
* ----------------
|
|
139
|
-
*/
|
|
140
|
-
|
|
141
|
-
// -----------------------
|
|
142
|
-
// This event is triggered by
|
|
143
|
-
// either the browser back button,
|
|
144
|
-
// the window.history.back(),
|
|
145
|
-
// the window.history.forward(),
|
|
146
|
-
// or the window.history.go() action.
|
|
147
|
-
window.addEventListener('popstate', e => {
|
|
148
|
-
// Needed to allow window.document.location
|
|
149
|
-
// to update to window.location
|
|
150
|
-
let referrer = window.document.location.href;
|
|
151
|
-
window.setTimeout(() => {
|
|
152
|
-
instance.go(Url.copy(window.document.location), { referrer, src: window.document.location, srcType: 'history', });
|
|
153
|
-
}, 0);
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
// -----------------------
|
|
157
|
-
// Capture all link-clicks
|
|
158
|
-
// and fire to this router.
|
|
159
|
-
window.addEventListener('click', e => {
|
|
160
|
-
var anchor = e.target.closest('a');
|
|
161
|
-
if (!anchor || !anchor.href) return;
|
|
162
|
-
if (!anchor.target && !anchor.download && (!anchor.origin || anchor.origin === instance.location.origin)) {
|
|
163
|
-
if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) return;
|
|
164
|
-
// Publish everything, including hash
|
|
165
|
-
instance.go(Url.copy(anchor), { src: anchor, srcType: 'link', });
|
|
166
|
-
// URLs with # will cause a natural navigation
|
|
167
|
-
// even if pointing to a different page, a natural navigation will still happen
|
|
168
|
-
// because with the Observer.set() above, window.document.location.href would have become
|
|
169
|
-
// the destination page, which makes it look like same page navigation
|
|
170
|
-
if (!anchor.href.includes('#')) {
|
|
171
|
-
e.preventDefault();
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
// -----------------------
|
|
177
|
-
// Capture all form-submit
|
|
178
|
-
// and fire to this router.
|
|
179
|
-
window.addEventListener('submit', e => {
|
|
180
|
-
var form = e.target.closest('form'), submitter = e.submitter;
|
|
181
|
-
var submitParams = [ 'action', 'enctype', 'method', 'noValidate', 'target' ].reduce((params, prop) => {
|
|
182
|
-
params[prop] = submitter && submitter.hasAttribute(`form${prop.toLowerCase()}`) ? submitter[`form${_toTitle(prop)}`] : form[prop];
|
|
183
|
-
return params;
|
|
184
|
-
}, {});
|
|
185
|
-
// We support method hacking
|
|
186
|
-
submitParams.method = (submitter && submitter.dataset.method) || form.dataset.method || submitParams.method;
|
|
187
|
-
submitParams.submitter = submitter;
|
|
188
|
-
// ---------------
|
|
189
|
-
var actionEl = window.document.createElement('a');
|
|
190
|
-
actionEl.href = submitParams.action;
|
|
191
|
-
// ---------------
|
|
192
|
-
// If not targeted and same origin...
|
|
193
|
-
if (!submitParams.target && (!actionEl.origin || actionEl.origin === instance.location.origin)) {
|
|
194
|
-
if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) return;
|
|
195
|
-
// Build data
|
|
196
|
-
var formData = new FormData(form);
|
|
197
|
-
if ((submitter || {}).name) {
|
|
198
|
-
formData.set(submitter.name, submitter.value);
|
|
199
|
-
}
|
|
200
|
-
if (submitParams.method.toUpperCase() === 'GET') {
|
|
201
|
-
var query = wwwFormUnserialize(actionEl.search);
|
|
202
|
-
Array.from(formData.entries()).forEach(_entry => {
|
|
203
|
-
wwwFormSet(query, _entry[0], _entry[1], false);
|
|
204
|
-
});
|
|
205
|
-
actionEl.search = wwwFormSerialize(query);
|
|
206
|
-
formData = null;
|
|
207
|
-
}
|
|
208
|
-
instance.go(Url.copy(actionEl), { ...submitParams, body: formData, src: form, srcType: 'form', });
|
|
209
|
-
// URLs with # will cause a natural navigation
|
|
210
|
-
// even if pointing to a different page, a natural navigation will still happen
|
|
211
|
-
// because with the Observer.set() above, window.document.location.href would have become
|
|
212
|
-
// the destination page, which makes it look like same page navigation
|
|
213
|
-
if (!actionEl.hash) {
|
|
214
|
-
e.preventDefault();
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
// -----------------------
|
|
220
|
-
// Startup route
|
|
221
|
-
instance.go(window.document.location.href, { referrer: document.referrer });
|
|
222
|
-
return instance;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
}
|