@webqit/webflo 0.11.61 → 0.20.2-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/FUNDING.yml +12 -0
- package/.github/workflows/publish.yml +48 -0
- package/.gitignore +2 -0
- package/LICENSE +2 -2
- package/README.md +71 -2050
- package/package.json +28 -13
- package/site/-/_.md +139 -0
- package/site/-/docs.old.md +2010 -0
- package/site/.vitepress/cache/deps/@braintree_sanitize-url 2.js +93 -0
- package/site/.vitepress/cache/deps/@braintree_sanitize-url.js +93 -0
- package/site/.vitepress/cache/deps/@braintree_sanitize-url.js 2.map +7 -0
- package/site/.vitepress/cache/deps/@braintree_sanitize-url.js.map +7 -0
- package/site/.vitepress/cache/deps/_metadata 2.json +85 -0
- package/site/.vitepress/cache/deps/_metadata.json +85 -0
- package/site/.vitepress/cache/deps/chunk-BUSYA2B4 2.js +9 -0
- package/site/.vitepress/cache/deps/chunk-BUSYA2B4.js +9 -0
- package/site/.vitepress/cache/deps/chunk-BUSYA2B4.js 2.map +7 -0
- package/site/.vitepress/cache/deps/chunk-BUSYA2B4.js.map +7 -0
- package/site/.vitepress/cache/deps/chunk-Q2AYPHVK 2.js +9719 -0
- package/site/.vitepress/cache/deps/chunk-Q2AYPHVK.js +9719 -0
- package/site/.vitepress/cache/deps/chunk-Q2AYPHVK.js 2.map +7 -0
- package/site/.vitepress/cache/deps/chunk-Q2AYPHVK.js.map +7 -0
- package/site/.vitepress/cache/deps/chunk-QAXAIFA7 2.js +12705 -0
- package/site/.vitepress/cache/deps/chunk-QAXAIFA7.js +12705 -0
- package/site/.vitepress/cache/deps/chunk-QAXAIFA7.js 2.map +7 -0
- package/site/.vitepress/cache/deps/chunk-QAXAIFA7.js.map +7 -0
- package/site/.vitepress/cache/deps/cytoscape 2.js +30278 -0
- package/site/.vitepress/cache/deps/cytoscape-cose-bilkent 2.js +4710 -0
- package/site/.vitepress/cache/deps/cytoscape-cose-bilkent.js +4710 -0
- package/site/.vitepress/cache/deps/cytoscape-cose-bilkent.js 2.map +7 -0
- package/site/.vitepress/cache/deps/cytoscape-cose-bilkent.js.map +7 -0
- package/site/.vitepress/cache/deps/cytoscape.js +30278 -0
- package/site/.vitepress/cache/deps/cytoscape.js 2.map +7 -0
- package/site/.vitepress/cache/deps/cytoscape.js.map +7 -0
- package/site/.vitepress/cache/deps/dayjs 2.js +285 -0
- package/site/.vitepress/cache/deps/dayjs.js +285 -0
- package/site/.vitepress/cache/deps/dayjs.js 2.map +7 -0
- package/site/.vitepress/cache/deps/dayjs.js.map +7 -0
- package/site/.vitepress/cache/deps/debug 2.js +453 -0
- package/site/.vitepress/cache/deps/debug.js +453 -0
- package/site/.vitepress/cache/deps/debug.js 2.map +7 -0
- package/site/.vitepress/cache/deps/debug.js.map +7 -0
- package/site/.vitepress/cache/deps/package 2.json +3 -0
- package/site/.vitepress/cache/deps/package.json +3 -0
- package/site/.vitepress/cache/deps/vitepress___@vue_devtools-api 2.js +4507 -0
- package/site/.vitepress/cache/deps/vitepress___@vue_devtools-api.js +4507 -0
- package/site/.vitepress/cache/deps/vitepress___@vue_devtools-api.js 2.map +7 -0
- package/site/.vitepress/cache/deps/vitepress___@vue_devtools-api.js.map +7 -0
- package/site/.vitepress/cache/deps/vitepress___@vueuse_core 2.js +584 -0
- package/site/.vitepress/cache/deps/vitepress___@vueuse_core.js +584 -0
- package/site/.vitepress/cache/deps/vitepress___@vueuse_core.js 2.map +7 -0
- package/site/.vitepress/cache/deps/vitepress___@vueuse_core.js.map +7 -0
- package/site/.vitepress/cache/deps/vitepress___@vueuse_integrations_useFocusTrap 2.js +1166 -0
- package/site/.vitepress/cache/deps/vitepress___@vueuse_integrations_useFocusTrap.js +1166 -0
- package/site/.vitepress/cache/deps/vitepress___@vueuse_integrations_useFocusTrap.js 2.map +7 -0
- package/site/.vitepress/cache/deps/vitepress___@vueuse_integrations_useFocusTrap.js.map +7 -0
- package/site/.vitepress/cache/deps/vitepress___mark__js_src_vanilla__js 2.js +1667 -0
- package/site/.vitepress/cache/deps/vitepress___mark__js_src_vanilla__js.js +1667 -0
- package/site/.vitepress/cache/deps/vitepress___mark__js_src_vanilla__js.js 2.map +7 -0
- package/site/.vitepress/cache/deps/vitepress___mark__js_src_vanilla__js.js.map +7 -0
- package/site/.vitepress/cache/deps/vitepress___minisearch 2.js +1815 -0
- package/site/.vitepress/cache/deps/vitepress___minisearch.js +1815 -0
- package/site/.vitepress/cache/deps/vitepress___minisearch.js 2.map +7 -0
- package/site/.vitepress/cache/deps/vitepress___minisearch.js.map +7 -0
- package/site/.vitepress/cache/deps/vue 2.js +344 -0
- package/site/.vitepress/cache/deps/vue.js +344 -0
- package/site/.vitepress/cache/deps/vue.js 2.map +7 -0
- package/site/.vitepress/cache/deps/vue.js.map +7 -0
- package/site/.vitepress/config.ts +147 -0
- package/site/.vitepress/theme/custom.css +50 -0
- package/site/.vitepress/theme/index.ts +6 -0
- package/site/api/webflo-fetch/FormData.md +0 -0
- package/site/api/webflo-fetch/Headers.md +0 -0
- package/site/api/webflo-fetch/LiveResponse.md +0 -0
- package/site/api/webflo-fetch/Request.md +0 -0
- package/site/api/webflo-fetch/Response.md +0 -0
- package/site/api/webflo-fetch/fetch.md +0 -0
- package/site/api/webflo-routing/HttpCookies.md +0 -0
- package/site/api/webflo-routing/HttpEvent/respondWith.md +1 -0
- package/site/api/webflo-routing/HttpEvent/waitUntil.md +1 -0
- package/site/api/webflo-routing/HttpEvent/waitUntilNavigate.md +1 -0
- package/site/api/webflo-routing/HttpEvent.md +30 -0
- package/site/api/webflo-routing/HttpSession.md +0 -0
- package/site/api/webflo-routing/HttpState.md +0 -0
- package/site/api/webflo-routing/HttpUser.md +0 -0
- package/site/api/webflo-routing/handler/fetch.md +42 -0
- package/site/api/webflo-routing/handler/next.md +54 -0
- package/site/api/webflo-routing/handler.md +119 -0
- package/site/api.md +26 -0
- package/site/contributing.md +16 -0
- package/site/docs/advanced/lifecycles.md +20 -0
- package/site/docs/advanced/redirects.md +0 -0
- package/site/docs/advanced/routing.md +1 -0
- package/site/docs/advanced.md +9 -0
- package/site/docs/concepts/realtime.md +637 -0
- package/site/docs/concepts/rendering.md +60 -0
- package/site/docs/concepts/request-response.md +47 -0
- package/site/docs/concepts/routing.md +656 -0
- package/site/docs/concepts/state.md +44 -0
- package/site/docs/concepts/templates.md +48 -0
- package/site/docs/concepts.md +97 -0
- package/site/docs/getting-started.md +378 -0
- package/site/docs/tech-stack.md +56 -0
- package/site/docs.md +100 -0
- package/site/examples/pwa.md +10 -0
- package/site/examples/web.md +11 -0
- package/site/examples.md +10 -0
- package/site/faq.md +13 -0
- package/site/guides/guide-auth.md +13 -0
- package/site/guides/guide-file-upload.md +11 -0
- package/site/guides/guide-service-worker.md +10 -0
- package/site/guides/tutorial-1-todo.md +24 -0
- package/site/guides.md +15 -0
- package/site/index.md +39 -0
- package/site/public/img/brand/logo-670x670.png +0 -0
- package/site/recipes/realtime.md +11 -0
- package/site/recipes/streaming.md +15 -0
- package/site/reference/cli.md +11 -0
- package/site/reference/config.md +13 -0
- package/site/reference/tools.md +9 -0
- package/src/Context.js +3 -11
- package/src/config-pi/deployment/Env.js +6 -19
- package/src/config-pi/deployment/Layout.js +11 -3
- package/src/config-pi/runtime/Client.js +40 -48
- package/src/config-pi/runtime/Server.js +52 -20
- package/src/config-pi/runtime/client/Worker.js +22 -20
- package/src/config-pi/static/Init.js +57 -0
- package/src/config-pi/static/index.js +2 -0
- package/src/deployment-pi/origins/index.js +1 -1
- package/src/deployment-pi/util.js +161 -0
- package/src/index.js +3 -9
- package/src/init-pi/index.js +117 -0
- package/src/init-pi/templates/pwa/app/handler.server.js +8 -0
- package/src/init-pi/templates/pwa/app/page.html +7 -0
- package/src/init-pi/templates/pwa/package.json +19 -0
- package/src/init-pi/templates/pwa/public/assets/app.css +16 -0
- package/src/init-pi/templates/pwa/public/index.html +39 -0
- package/src/init-pi/templates/pwa/public/manifest.json +29 -0
- package/src/init-pi/templates/web/app/handler.server.js +8 -0
- package/src/init-pi/templates/web/app/page.html +7 -0
- package/src/init-pi/templates/web/package.json +19 -0
- package/src/init-pi/templates/web/public/assets/app.css +16 -0
- package/src/init-pi/templates/web/public/index.html +39 -0
- package/src/runtime-pi/WebfloRuntime.js +350 -0
- package/src/runtime-pi/index.js +3 -10
- package/src/runtime-pi/webflo-client/ClientSideCookies.js +17 -0
- package/src/runtime-pi/webflo-client/ClientSideWorkport.js +63 -0
- package/src/runtime-pi/webflo-client/DeviceCapabilities.js +213 -0
- package/src/runtime-pi/webflo-client/WebfloClient.js +500 -0
- package/src/runtime-pi/webflo-client/WebfloRootClient1.js +206 -0
- package/src/runtime-pi/webflo-client/WebfloRootClient2.js +113 -0
- package/src/runtime-pi/webflo-client/WebfloSubClient.js +118 -0
- package/src/runtime-pi/webflo-client/index.js +17 -0
- package/src/runtime-pi/webflo-client/webflo-codegen.js +469 -0
- package/src/runtime-pi/webflo-client/webflo-devmode.js +243 -0
- package/src/runtime-pi/webflo-client/webflo-embedded.js +50 -0
- package/src/runtime-pi/webflo-fetch/LiveResponse.js +437 -0
- package/src/runtime-pi/webflo-fetch/cookies.js +10 -0
- package/src/runtime-pi/webflo-fetch/fetch.js +16 -0
- package/src/runtime-pi/webflo-fetch/formdata.js +54 -0
- package/src/runtime-pi/webflo-fetch/headers.js +151 -0
- package/src/runtime-pi/webflo-fetch/index.js +5 -0
- package/src/runtime-pi/webflo-fetch/message.js +49 -0
- package/src/runtime-pi/webflo-fetch/request.js +62 -0
- package/src/runtime-pi/webflo-fetch/response.js +110 -0
- package/src/runtime-pi/webflo-fetch/util.js +28 -0
- package/src/runtime-pi/webflo-messaging/WQBroadcastChannel.js +10 -0
- package/src/runtime-pi/webflo-messaging/WQMessageChannel.js +26 -0
- package/src/runtime-pi/webflo-messaging/WQMessageEvent.js +87 -0
- package/src/runtime-pi/webflo-messaging/WQMessagePort.js +38 -0
- package/src/runtime-pi/webflo-messaging/WQRelayPort.js +47 -0
- package/src/runtime-pi/webflo-messaging/WQSockPort.js +113 -0
- package/src/runtime-pi/webflo-messaging/WQStarPort.js +104 -0
- package/src/runtime-pi/webflo-messaging/wq-message-port.js +404 -0
- package/src/runtime-pi/webflo-routing/HttpCookies.js +42 -0
- package/src/runtime-pi/webflo-routing/HttpEvent.js +112 -0
- package/src/runtime-pi/webflo-routing/HttpSession.js +11 -0
- package/src/runtime-pi/webflo-routing/HttpState.js +153 -0
- package/src/runtime-pi/webflo-routing/HttpUser.js +54 -0
- package/src/runtime-pi/webflo-routing/WebfloRouter.js +245 -0
- package/src/runtime-pi/webflo-server/ServerSideCookies.js +19 -0
- package/src/runtime-pi/webflo-server/ServerSideSession.js +38 -0
- package/src/runtime-pi/webflo-server/WebfloServer.js +937 -0
- package/src/runtime-pi/webflo-server/index.js +11 -0
- package/src/runtime-pi/webflo-server/messaging/Client.js +27 -0
- package/src/runtime-pi/webflo-server/messaging/ClientRequestRealtime.js +50 -0
- package/src/runtime-pi/webflo-server/messaging/Clients.js +25 -0
- package/src/runtime-pi/webflo-server/webflo-devmode.js +326 -0
- package/src/runtime-pi/{client → webflo-url}/Url.js +27 -76
- package/src/runtime-pi/webflo-url/index.js +1 -0
- package/src/runtime-pi/webflo-url/urlpattern.js +38 -0
- package/src/runtime-pi/{util-url.js → webflo-url/util.js} +5 -43
- package/src/runtime-pi/webflo-url/xURL.js +94 -0
- package/src/runtime-pi/webflo-worker/WebfloWorker.js +234 -0
- package/src/runtime-pi/webflo-worker/WorkerSideCookies.js +19 -0
- package/src/runtime-pi/webflo-worker/WorkerSideWorkport.js +18 -0
- package/src/runtime-pi/webflo-worker/index.js +11 -0
- package/src/services-pi/index.js +2 -0
- package/src/services-pi/push/index.js +23 -0
- package/src/util.js +10 -0
- package/src/{webflo.js → webflo-cli.js} +4 -4
- package/src/runtime-pi/Application.js +0 -29
- package/src/runtime-pi/Cookies.js +0 -82
- package/src/runtime-pi/HttpEvent.js +0 -107
- package/src/runtime-pi/Router.js +0 -130
- package/src/runtime-pi/Runtime.js +0 -21
- package/src/runtime-pi/client/Application.js +0 -76
- package/src/runtime-pi/client/Context.js +0 -7
- package/src/runtime-pi/client/Router.js +0 -48
- package/src/runtime-pi/client/Runtime.js +0 -525
- package/src/runtime-pi/client/Workport.js +0 -190
- package/src/runtime-pi/client/createStorage.js +0 -58
- package/src/runtime-pi/client/generate.js +0 -481
- package/src/runtime-pi/client/index.js +0 -21
- package/src/runtime-pi/client/worker/Application.js +0 -44
- package/src/runtime-pi/client/worker/Context.js +0 -7
- package/src/runtime-pi/client/worker/Runtime.js +0 -275
- package/src/runtime-pi/client/worker/Workport.js +0 -78
- package/src/runtime-pi/client/worker/index.js +0 -21
- package/src/runtime-pi/server/Application.js +0 -101
- package/src/runtime-pi/server/Context.js +0 -16
- package/src/runtime-pi/server/Router.js +0 -159
- package/src/runtime-pi/server/Runtime.js +0 -558
- package/src/runtime-pi/server/index.js +0 -21
- package/src/runtime-pi/util-http.js +0 -86
- package/src/runtime-pi/xFormData.js +0 -24
- package/src/runtime-pi/xHeaders.js +0 -146
- package/src/runtime-pi/xRequest.js +0 -46
- package/src/runtime-pi/xRequestHeaders.js +0 -109
- package/src/runtime-pi/xResponse.js +0 -33
- package/src/runtime-pi/xResponseHeaders.js +0 -117
- package/src/runtime-pi/xURL.js +0 -105
- package/src/runtime-pi/xfetch.js +0 -23
- package/src/runtime-pi/xxHttpMessage.js +0 -102
- package/src/static-pi/index.js +0 -11
|
@@ -1,21 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* @imports
|
|
4
|
-
*/
|
|
5
1
|
import { _isString, _isNumeric, _isArray, _isTypeObject } from '@webqit/util/js/index.js';
|
|
6
|
-
if (typeof URLPattern === 'undefined') {
|
|
7
|
-
await import('urlpattern-polyfill');
|
|
8
|
-
}
|
|
9
2
|
|
|
10
|
-
export const
|
|
3
|
+
export const DeepURLSearchParams = {
|
|
11
4
|
// Parse a search params string into an object
|
|
12
|
-
|
|
5
|
+
eval(targetObject, str, delim = '&') {
|
|
13
6
|
str = str || '';
|
|
14
|
-
const target = {};
|
|
15
7
|
(str.startsWith('?') ? str.substr(1) : str)
|
|
16
8
|
.split(delim).filter(q => q).map(q => q.split('=').map(q => q.trim()))
|
|
17
|
-
.forEach(q => this.set(
|
|
18
|
-
return
|
|
9
|
+
.forEach(q => this.set(targetObject, q[0], decodeURIComponent(q[1])));
|
|
10
|
+
return targetObject;
|
|
19
11
|
},
|
|
20
12
|
// Stringify an object into a search params string
|
|
21
13
|
stringify(targetObject, delim = '&') {
|
|
@@ -28,7 +20,6 @@ export const params = {
|
|
|
28
20
|
});
|
|
29
21
|
return q.join(delim);
|
|
30
22
|
},
|
|
31
|
-
|
|
32
23
|
// Get value by path notation
|
|
33
24
|
get(targetObject, pathNotation) {
|
|
34
25
|
return this.reducePath(pathNotation, targetObject, (key, _targetObject) => {
|
|
@@ -115,33 +106,4 @@ export const path = {
|
|
|
115
106
|
dirname( path ) {
|
|
116
107
|
return this.join( path, '..' );
|
|
117
108
|
}
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
export const pattern = (pattern, baseUrl = null) => ({
|
|
121
|
-
pattern: new URLPattern(pattern, baseUrl),
|
|
122
|
-
isPattern() {
|
|
123
|
-
return Object.keys(this.pattern.keys || {}).some(compName => this.pattern.keys[compName].length);
|
|
124
|
-
},
|
|
125
|
-
test(...args) { return this.pattern.test(...args) },
|
|
126
|
-
exec(...args) {
|
|
127
|
-
let components = this.pattern.exec(...args);
|
|
128
|
-
if (!components) return;
|
|
129
|
-
components.vars = Object.keys(this.pattern.keys).reduce(({ named, unnamed }, compName) => {
|
|
130
|
-
this.pattern.keys[compName].forEach(key => {
|
|
131
|
-
let value = components[compName].groups[key.name];
|
|
132
|
-
if (typeof key.name === 'number') {
|
|
133
|
-
unnamed.push(value);
|
|
134
|
-
} else {
|
|
135
|
-
named[key.name] = value;
|
|
136
|
-
}
|
|
137
|
-
});
|
|
138
|
-
return { named, unnamed };
|
|
139
|
-
}, { named: {}, unnamed: [] });
|
|
140
|
-
components.render = str => {
|
|
141
|
-
return str.replace(/\$(\$|[0-9A-Z]+)/gi, (a, b) => {
|
|
142
|
-
return b === '$' ? '$' : (_isNumeric(b) ? components.vars.unnamed[b - 1] : components.vars.named[b]) || '';
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
return components;
|
|
146
|
-
}
|
|
147
|
-
});
|
|
109
|
+
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { _isObject } from '@webqit/util/js/index.js';
|
|
2
|
+
import { Observer } from '@webqit/quantum-js';
|
|
3
|
+
import { DeepURLSearchParams } from './util.js';
|
|
4
|
+
|
|
5
|
+
export class xURL extends URL {
|
|
6
|
+
|
|
7
|
+
// constructor
|
|
8
|
+
constructor(...args) {
|
|
9
|
+
super(...args);
|
|
10
|
+
const query = DeepURLSearchParams.eval({}, this.search);
|
|
11
|
+
const updateSearch = () => {
|
|
12
|
+
// "query" was updated. So we update "search"
|
|
13
|
+
let search = DeepURLSearchParams.stringify(query);
|
|
14
|
+
search = search ? '?' + search : '';
|
|
15
|
+
if (search !== this.search) {
|
|
16
|
+
this.search = search;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
const $this = this;
|
|
20
|
+
this._query = new Proxy(query, {
|
|
21
|
+
set(t, k, v) {
|
|
22
|
+
t[k] = v;
|
|
23
|
+
if (!$this._updatingSearch) updateSearch();
|
|
24
|
+
return true;
|
|
25
|
+
},
|
|
26
|
+
deleteProperty(t, k) {
|
|
27
|
+
delete t[k];
|
|
28
|
+
if (!$this._updatingSearch) updateSearch();
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Set search
|
|
35
|
+
set search(value) {
|
|
36
|
+
super.search = value;
|
|
37
|
+
// "search" was updated. So we update "query"
|
|
38
|
+
this._updatingSearch = true;
|
|
39
|
+
const query = DeepURLSearchParams.eval({}, value);
|
|
40
|
+
const keys_a = Object.keys(query);
|
|
41
|
+
const keys_b = Object.keys(this._query);
|
|
42
|
+
for (const k of new Set([...keys_a,...keys_b])) {
|
|
43
|
+
if (!keys_a.includes(k)) delete this.query[k];
|
|
44
|
+
if (!keys_b.includes(k)) this.query[k] = query[k];
|
|
45
|
+
}
|
|
46
|
+
this._updatingSearch = false;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Get search
|
|
50
|
+
get search() {
|
|
51
|
+
return super.search;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Get query
|
|
55
|
+
get query() {
|
|
56
|
+
return this._query;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
xURL.Observable = class extends xURL {
|
|
62
|
+
|
|
63
|
+
constructor() {
|
|
64
|
+
super(...arguments);
|
|
65
|
+
Observer.accessorize(this, [
|
|
66
|
+
'protocol',
|
|
67
|
+
'username',
|
|
68
|
+
'password',
|
|
69
|
+
'host',
|
|
70
|
+
'hostname',
|
|
71
|
+
'port',
|
|
72
|
+
'origin',
|
|
73
|
+
'pathname',
|
|
74
|
+
'search',
|
|
75
|
+
'query',
|
|
76
|
+
'hash',
|
|
77
|
+
'href',
|
|
78
|
+
]);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const _strictEven = (a, b) => {
|
|
84
|
+
if (_isObject(a) && _isObject(b)) {
|
|
85
|
+
return _strictEven(Object.keys(a), Object.keys(b))
|
|
86
|
+
&& _strictEven(Object.values(a), Object.values(b));
|
|
87
|
+
}
|
|
88
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
89
|
+
return a.length === b.length
|
|
90
|
+
&& a.reduce((recieved, item, i) => recieved && item === b[i], true);
|
|
91
|
+
}
|
|
92
|
+
return a === b;
|
|
93
|
+
};
|
|
94
|
+
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { _any } from '@webqit/util/arr/index.js';
|
|
2
|
+
import { WebfloRuntime } from '../WebfloRuntime.js';
|
|
3
|
+
import { WQBroadcastChannel } from '../webflo-messaging/WQBroadcastChannel.js';
|
|
4
|
+
import { WorkerSideWorkport } from './WorkerSideWorkport.js';
|
|
5
|
+
import { WorkerSideCookies } from './WorkerSideCookies.js';
|
|
6
|
+
import { HttpSession } from '../webflo-routing/HttpSession.js';
|
|
7
|
+
import { HttpEvent } from '../webflo-routing/HttpEvent.js';
|
|
8
|
+
import { HttpUser } from '../webflo-routing/HttpUser.js';
|
|
9
|
+
import '../webflo-fetch/index.js';
|
|
10
|
+
import '../webflo-url/index.js';
|
|
11
|
+
|
|
12
|
+
export class WebfloWorker extends WebfloRuntime {
|
|
13
|
+
|
|
14
|
+
static get HttpEvent() { return HttpEvent; }
|
|
15
|
+
|
|
16
|
+
static get HttpCookies() { return WorkerSideCookies; }
|
|
17
|
+
|
|
18
|
+
static get HttpSession() { return HttpSession; }
|
|
19
|
+
|
|
20
|
+
static get HttpUser() { return HttpUser; }
|
|
21
|
+
|
|
22
|
+
static get Workport() { return WorkerSideWorkport; }
|
|
23
|
+
|
|
24
|
+
static create(cx) {
|
|
25
|
+
return new this(this.Context.create(cx));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
#sdk = {};
|
|
29
|
+
get sdk() { return this.#sdk; }
|
|
30
|
+
|
|
31
|
+
async initialize() {
|
|
32
|
+
const instanceController = super.initialize();
|
|
33
|
+
// ONINSTALL
|
|
34
|
+
const installHandler = (event) => {
|
|
35
|
+
if (this.config.WORKER.skip_waiting) self.skipWaiting();
|
|
36
|
+
// Manage CACHE
|
|
37
|
+
if (this.config.WORKER.cache_name && (this.config.WORKER.cache_only_urls || []).length) {
|
|
38
|
+
// Add files to cache
|
|
39
|
+
event.waitUntil(self.caches.open(this.config.WORKER.cache_name).then(async cache => {
|
|
40
|
+
if (this.cx.logger) { this.cx.logger.log('[ServiceWorker] Pre-caching resources.'); }
|
|
41
|
+
for (const urls of ['cache_first_urls', 'cache_only_urls']) {
|
|
42
|
+
const _urls = (this.config.WORKER[urls] || []).map(c => c.trim()).filter(c => c && !(new URLPattern(c, self.origin)).isPattern());
|
|
43
|
+
await cache.addAll(_urls);
|
|
44
|
+
}
|
|
45
|
+
}));
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
// ONACTIVATE
|
|
49
|
+
const activateHandler = (event) => {
|
|
50
|
+
event.waitUntil(new Promise(async resolve => {
|
|
51
|
+
if (this.config.WORKER.skip_waiting) { await self.clients.claim(); }
|
|
52
|
+
// Manage CACHE
|
|
53
|
+
if (this.config.WORKER.cache_name) {
|
|
54
|
+
// Clear outdated CACHES
|
|
55
|
+
await self.caches.keys().then(keyList => {
|
|
56
|
+
return Promise.all(keyList.map(key => {
|
|
57
|
+
if (key !== this.config.WORKER.cache_name && key !== this.config.WORKER.cache_name + '_json') {
|
|
58
|
+
if (this.cx.logger) { this.cx.logger.log('[ServiceWorker] Removing old cache:', key); }
|
|
59
|
+
return self.caches.delete(key);
|
|
60
|
+
}
|
|
61
|
+
}));
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
resolve();
|
|
65
|
+
}));
|
|
66
|
+
};
|
|
67
|
+
self.addEventListener('install', installHandler, { signal: instanceController.signal });
|
|
68
|
+
self.addEventListener('activate', activateHandler, { signal: instanceController.signal });
|
|
69
|
+
this.control();
|
|
70
|
+
return instanceController;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
control() {
|
|
74
|
+
const instanceController = super.control();
|
|
75
|
+
// ONFETCH
|
|
76
|
+
const fetchHandler = (event) => {
|
|
77
|
+
// Handle special requests
|
|
78
|
+
if (!event.request.url.startsWith('http') || event.request.mode === 'navigate') {
|
|
79
|
+
return event.respondWith(fetch(event.request));
|
|
80
|
+
}
|
|
81
|
+
// Handle external requests
|
|
82
|
+
if (!event.request.url.startsWith(self.origin)) {
|
|
83
|
+
return event.respondWith(this.remoteFetch(event.request));
|
|
84
|
+
}
|
|
85
|
+
event.respondWith((async (event) => {
|
|
86
|
+
const response = await this.navigate(event.request.url, event.request, { event });
|
|
87
|
+
return response;
|
|
88
|
+
})(event));
|
|
89
|
+
};
|
|
90
|
+
const webpushHandler = (event) => {
|
|
91
|
+
if (!(self.Notification && self.Notification.permission === 'granted')) return;
|
|
92
|
+
let data;
|
|
93
|
+
try {
|
|
94
|
+
data = event.data?.json() ?? {};
|
|
95
|
+
} catch(e) { return; }
|
|
96
|
+
const { type, title, ...params } = data;
|
|
97
|
+
if (type !== 'notification') return;
|
|
98
|
+
self.registration.showNotification(title, params);
|
|
99
|
+
};
|
|
100
|
+
self.addEventListener('fetch', fetchHandler, { signal: instanceController.signal });
|
|
101
|
+
self.addEventListener('push', webpushHandler, { signal: instanceController.signal });
|
|
102
|
+
return instanceController;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async navigate(url, init = {}, detail = {}) {
|
|
106
|
+
// Resolve inputs
|
|
107
|
+
const scopeObj = { url, init, detail };
|
|
108
|
+
if (typeof scopeObj.url === 'string') {
|
|
109
|
+
scopeObj.url = new URL(scopeObj.url, self.location.origin);
|
|
110
|
+
}
|
|
111
|
+
// Create and route request
|
|
112
|
+
scopeObj.request = this.createRequest(scopeObj.url, scopeObj.init);
|
|
113
|
+
scopeObj.cookies = this.createHttpCookies({
|
|
114
|
+
request: scopeObj.request
|
|
115
|
+
});
|
|
116
|
+
scopeObj.session = this.createHttpSession({
|
|
117
|
+
store: this.#sdk.storage?.('session'),
|
|
118
|
+
request: scopeObj.request
|
|
119
|
+
});
|
|
120
|
+
const requestID = crypto.randomUUID();
|
|
121
|
+
scopeObj.clientRequestRealtime = new WQBroadcastChannel(requestID);
|
|
122
|
+
scopeObj.user = this.createHttpUser({
|
|
123
|
+
store: this.#sdk.storage?.('user'),
|
|
124
|
+
request: scopeObj.request,
|
|
125
|
+
realtime: scopeObj.clientRequestRealtime,
|
|
126
|
+
session: scopeObj.session,
|
|
127
|
+
});
|
|
128
|
+
scopeObj.httpEvent = this.createHttpEvent({
|
|
129
|
+
request: scopeObj.request,
|
|
130
|
+
realtime: scopeObj.clientRequestRealtime,
|
|
131
|
+
cookies: scopeObj.cookies,
|
|
132
|
+
session: scopeObj.session,
|
|
133
|
+
user: scopeObj.user,
|
|
134
|
+
detail: scopeObj.detail,
|
|
135
|
+
sdk: {}
|
|
136
|
+
});
|
|
137
|
+
// Dispatch for response
|
|
138
|
+
scopeObj.response = await this.dispatchNavigationEvent({
|
|
139
|
+
httpEvent: scopeObj.httpEvent,
|
|
140
|
+
crossLayerFetch: async (event) => {
|
|
141
|
+
// Was this nexted()? Tell the next layer we're in JSON mode by default
|
|
142
|
+
if (event !== scopeObj.httpEvent && !event.request.headers.has('Accept')) {
|
|
143
|
+
event.request.headers.set('Accept', 'application/json');
|
|
144
|
+
}
|
|
145
|
+
return await this.remoteFetch(event.request);
|
|
146
|
+
},
|
|
147
|
+
responseRealtime: `br:${scopeObj.httpEvent.realtime.name}`
|
|
148
|
+
});
|
|
149
|
+
return scopeObj.response;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async remoteFetch(request, ...args) {
|
|
153
|
+
if (arguments.length > 1) {
|
|
154
|
+
request = this.createRequest(request, ...args);
|
|
155
|
+
}
|
|
156
|
+
const scopeObj = {};
|
|
157
|
+
const matchUrl = (patterns, url) => _any((patterns || []).map(p => p.trim()).filter(p => p), p => (new URLPattern(p, self.origin)).test(url));
|
|
158
|
+
if (matchUrl(this.config.WORKER.cache_only_urls, request.url)) {
|
|
159
|
+
scopeObj.strategy = 'cache-only';
|
|
160
|
+
scopeObj.response = this.cacheFetch(request, { networkFallback: false, cacheRefresh: false });
|
|
161
|
+
} else if (matchUrl(this.config.WORKER.network_only_urls, request.url)) {
|
|
162
|
+
scopeObj.strategy = 'network-only';
|
|
163
|
+
scopeObj.response = this.networkFetch(request, { cacheFallback: false, cacheRefresh: false });
|
|
164
|
+
} else if (matchUrl(this.config.WORKER.cache_first_urls, request.url)) {
|
|
165
|
+
scopeObj.strategy = 'cache-first';
|
|
166
|
+
scopeObj.response = this.cacheFetch(request, { networkFallback: true, cacheRefresh: true });
|
|
167
|
+
} else if (matchUrl(this.config.WORKER.network_first_urls, request.url) || !this.config.WORKER.default_fetching_strategy) {
|
|
168
|
+
scopeObj.strategy = 'network-first';
|
|
169
|
+
scopeObj.response = this.networkFetch(request, { cacheFallback: true, cacheRefresh: true });
|
|
170
|
+
} else {
|
|
171
|
+
scopeObj.strategy = this.config.WORKER.default_fetching_strategy;
|
|
172
|
+
switch (this.config.WORKER.default_fetching_strategy) {
|
|
173
|
+
case 'cache-only':
|
|
174
|
+
scopeObj.response = this.cacheFetch(request, { networkFallback: false, cacheRefresh: false });
|
|
175
|
+
break;
|
|
176
|
+
case 'network-only':
|
|
177
|
+
scopeObj.response = this.networkFetch(request, { cacheFallback: false, cacheRefresh: false });
|
|
178
|
+
break;
|
|
179
|
+
case 'cache-first':
|
|
180
|
+
scopeObj.response = this.cacheFetch(request, { networkFallback: true, cacheRefresh: true });
|
|
181
|
+
break;
|
|
182
|
+
case 'network-first':
|
|
183
|
+
scopeObj.response = this.networkFetch(request, { cacheFallback: true, cacheRefresh: true });
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return await scopeObj.response;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async networkFetch(request, params = {}) {
|
|
191
|
+
if (!params.cacheFallback) {
|
|
192
|
+
return fetch(request);
|
|
193
|
+
}
|
|
194
|
+
return fetch(request).then((response) => {
|
|
195
|
+
if (params.cacheRefresh) this.refreshCache(request, response);
|
|
196
|
+
return response;
|
|
197
|
+
}).catch((e) => this.getRequestCache(request).then(cache => {
|
|
198
|
+
return cache.match(request);
|
|
199
|
+
}));
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async cacheFetch(request, params = {}) {
|
|
203
|
+
return this.getRequestCache(request).then(cache => cache.match(request).then((response) => {
|
|
204
|
+
// Nothing cache, use network
|
|
205
|
+
if (!response && params.networkFallback) return this.networkFetch(request, { ...params, cacheFallback: false });
|
|
206
|
+
// Note: fetch, but for refreshing purposes only... not the returned response
|
|
207
|
+
if (response && params.cacheRefresh) this.networkFetch(request, { ...params, justRefreshing: true });
|
|
208
|
+
return response;
|
|
209
|
+
}));
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async refreshCache(request, response) {
|
|
213
|
+
// Check if we received a valid response
|
|
214
|
+
if (request.method !== 'GET' || !response || response.status !== 200 || (response.type !== 'basic' && response.type !== 'cors')) {
|
|
215
|
+
return response;
|
|
216
|
+
}
|
|
217
|
+
// IMPORTANT: Clone the response. A response is a stream
|
|
218
|
+
// and because we want the browser to consume the response
|
|
219
|
+
// as well as the cache consuming the response, we need
|
|
220
|
+
// to clone it so we have two streams.
|
|
221
|
+
var responseToCache = response.clone();
|
|
222
|
+
await this.getRequestCache(request).then(cache => {
|
|
223
|
+
cache.put(request, responseToCache);
|
|
224
|
+
});
|
|
225
|
+
return response;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
async getRequestCache(request) {
|
|
229
|
+
const cacheName = request.headers.get('X-Powered-By') === '@webqit/webflo'
|
|
230
|
+
? this.config.WORKER.cache_name + '_csr'
|
|
231
|
+
: this.config.WORKER.cache_name;
|
|
232
|
+
return self.caches.open(cacheName);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { HttpCookies } from '../webflo-routing/HttpCookies.js';
|
|
2
|
+
|
|
3
|
+
export class WorkerSideCookies extends HttpCookies {
|
|
4
|
+
static create({ request }) {
|
|
5
|
+
return new this({
|
|
6
|
+
request,
|
|
7
|
+
entries: request.headers.get('Cookie', true).map((c) => [c.name, c])
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async commit(response = null) {
|
|
12
|
+
if (response) {
|
|
13
|
+
for (const cookieStr of await this.render()) {
|
|
14
|
+
response.headers.append('Set-Cookie', cookieStr);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
await super.commit();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export class WorkerSideWorkport {
|
|
2
|
+
|
|
3
|
+
showNotification(title, params = {}) {
|
|
4
|
+
return self.registration.showNotification(title, params);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
handleNotificationClick(callback) {
|
|
8
|
+
const handler = (e) => e.waitUntil(callback(e));
|
|
9
|
+
self.addEventListener('notificationclick', handler);
|
|
10
|
+
return () => self.removeEventListener('notificationclick', handler);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
handlePush(callback) {
|
|
14
|
+
const handler = (e) => e.waitUntil(callback(e));
|
|
15
|
+
self.addEventListener('push', handler);
|
|
16
|
+
return () => self.removeEventListener('notificationclick', handler);
|
|
17
|
+
}
|
|
18
|
+
}
|
package/src/services-pi/index.js
CHANGED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* imports
|
|
4
|
+
*/
|
|
5
|
+
import webpush from 'web-push';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @description
|
|
9
|
+
*/
|
|
10
|
+
export const desc = {
|
|
11
|
+
generate: 'Generate a set of VAPID keys for push notifications.',
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Reads SSL from file.
|
|
16
|
+
*
|
|
17
|
+
* @return object
|
|
18
|
+
*/
|
|
19
|
+
export async function generate() {
|
|
20
|
+
const cx = this || {};
|
|
21
|
+
const vapidKeys = webpush.generateVAPIDKeys();
|
|
22
|
+
cx.logger.log(vapidKeys);
|
|
23
|
+
}
|
package/src/util.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { _wq as __wq } from '@webqit/util/js/index.js';
|
|
2
|
+
|
|
3
|
+
export const _wq = (target, ...args) => __wq(target, 'webflo', ...args);
|
|
4
|
+
|
|
5
|
+
export const _await = (value, callback) => {
|
|
6
|
+
if (value instanceof Promise) {
|
|
7
|
+
return value.then(callback);
|
|
8
|
+
}
|
|
9
|
+
return callback(value);
|
|
10
|
+
};
|
|
@@ -10,15 +10,15 @@ import { Logger, Cli } from '@webqit/backpack';
|
|
|
10
10
|
import * as WebfloPI from './index.js';
|
|
11
11
|
|
|
12
12
|
const dirSelf = Path.dirname(Url.fileURLToPath(import.meta.url));
|
|
13
|
-
const
|
|
14
|
-
const
|
|
13
|
+
const webfloMeta = jsonFile.read(Path.join(dirSelf, '../package.json'));
|
|
14
|
+
const appMeta = jsonFile.read('./package.json');
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* @cx
|
|
18
18
|
*/
|
|
19
19
|
const cx = WebfloPI.Context.create({
|
|
20
|
-
meta: { title:
|
|
21
|
-
|
|
20
|
+
meta: { title: webfloMeta.title, version: webfloMeta.version },
|
|
21
|
+
appMeta: { ...appMeta },
|
|
22
22
|
logger: Logger,
|
|
23
23
|
config: WebfloPI.config,
|
|
24
24
|
middlewares: [ WebfloPI.deployment.origins.webhook ],
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* ---------------------------
|
|
4
|
-
* The base Application class
|
|
5
|
-
* ---------------------------
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export default class Application {
|
|
9
|
-
|
|
10
|
-
constructor(cx) {
|
|
11
|
-
this.cx = cx;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Initializes application itself.
|
|
16
|
-
*
|
|
17
|
-
* @param HttpEvent httpEvent
|
|
18
|
-
* @param Function remoteFetch
|
|
19
|
-
*
|
|
20
|
-
* @return Boolean|undefined
|
|
21
|
-
*/
|
|
22
|
-
async init(httpEvent, remoteFetch) {
|
|
23
|
-
// The app router
|
|
24
|
-
const router = new this.Router(this.cx, '/');
|
|
25
|
-
return router.route(['init'], httpEvent, {}, async event => {
|
|
26
|
-
}, remoteFetch);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
}
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* @imports
|
|
4
|
-
*/
|
|
5
|
-
import { _isString, _isObject } from "@webqit/util/js/index.js";
|
|
6
|
-
|
|
7
|
-
export default class Cookies extends Map {
|
|
8
|
-
|
|
9
|
-
constructor(...args) {
|
|
10
|
-
super(...args);
|
|
11
|
-
Object.defineProperty(this, 'inLock', { value: false, writable: true });
|
|
12
|
-
Object.defineProperty(this, 'outLock', { value: false, writable: true });
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
set(name, value) {
|
|
16
|
-
if (this.inLock) return;
|
|
17
|
-
if (this.has(name)) this.delete(name);
|
|
18
|
-
this.inLock = true;
|
|
19
|
-
// -----------------
|
|
20
|
-
let valueObj = value, valueStr = value, retrn;
|
|
21
|
-
if (_isString(value)) { valueObj = this.parseEntry(`=${ value }`)[1]; }
|
|
22
|
-
retrn = super.set(name, valueObj);
|
|
23
|
-
if (!this.outLock) {
|
|
24
|
-
if (_isObject(value)) { valueStr = this.stringifyEntry(value); }
|
|
25
|
-
append(this.headers, `${ name }=${ valueStr }`);
|
|
26
|
-
}
|
|
27
|
-
// -----------------
|
|
28
|
-
this.inLock = false;
|
|
29
|
-
return retrn;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
delete(name) {
|
|
33
|
-
if (this.inLock) return;
|
|
34
|
-
this.inLock = true;
|
|
35
|
-
// -----------------
|
|
36
|
-
let retrn = super.delete(name);
|
|
37
|
-
this.headers.delete(this.headers.cookieHeaderName);
|
|
38
|
-
for (let [ name, definition ] of this) {
|
|
39
|
-
append(this.headers, `${ name }=${ this.stringifyEntry(definition) }`);
|
|
40
|
-
}
|
|
41
|
-
// -----------------
|
|
42
|
-
this.inLock = false;
|
|
43
|
-
return retrn;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
clear() {
|
|
47
|
-
if (this.inLock) return;
|
|
48
|
-
this.inLock = true;
|
|
49
|
-
// -----------------
|
|
50
|
-
let retrn = super.clear();
|
|
51
|
-
this.headers.delete(this.headers.cookieHeaderName);
|
|
52
|
-
// -----------------
|
|
53
|
-
this.inLock = false;
|
|
54
|
-
return retrn;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
json(json = {}) {
|
|
58
|
-
if (arguments.length) {
|
|
59
|
-
this.clear();
|
|
60
|
-
for (let name in json) {
|
|
61
|
-
this.set(name, json[name])
|
|
62
|
-
}
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
for (let [ name, definition ] of this) {
|
|
66
|
-
json[name] = definition;
|
|
67
|
-
}
|
|
68
|
-
return json;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
toString() {
|
|
72
|
-
return this.headers.get(this.headers.cookieHeaderName);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function append(headers, value) {
|
|
78
|
-
let values = [value];
|
|
79
|
-
let currentValue = headers.get(headers.cookieHeaderName);
|
|
80
|
-
if (currentValue) { values.unshift(currentValue); }
|
|
81
|
-
headers.set(headers.cookieHeaderName, values.join(headers.cookieHeaderSeparator));
|
|
82
|
-
}
|