@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
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "APP_TITLE",
|
|
3
|
+
"short_name": "APP_TITLE",
|
|
4
|
+
"description": "APP_DESCRIPTION",
|
|
5
|
+
"start_url": "/",
|
|
6
|
+
"display": "standalone",
|
|
7
|
+
"orientation": "portrait",
|
|
8
|
+
"background_color": "#4e40ce",
|
|
9
|
+
"theme_color": "#4e40ce",
|
|
10
|
+
"scope": "/",
|
|
11
|
+
"icons": [
|
|
12
|
+
{
|
|
13
|
+
"src": "/assets/...",
|
|
14
|
+
"sizes": "192x192",
|
|
15
|
+
"type": "image/png"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"src": "/assets/...",
|
|
19
|
+
"sizes": "512x512",
|
|
20
|
+
"type": "image/png",
|
|
21
|
+
"purpose": "maskable"
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
"categories": [],
|
|
25
|
+
"related_applications": [],
|
|
26
|
+
"prefer_related_applications": false,
|
|
27
|
+
"lang": "en",
|
|
28
|
+
"dir": "ltr"
|
|
29
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "",
|
|
3
|
+
"name": "",
|
|
4
|
+
"description": "",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build:html": "oohtml bundle --recursive --outdir=public/assets --auto-embed=app",
|
|
8
|
+
"build:js": "webflo generate::client --recursive --outdir=public/assets --auto-embed",
|
|
9
|
+
"build": "npm run build:html && npm run build:js",
|
|
10
|
+
"dev": "webflo start --dev --build-sensitivity=2",
|
|
11
|
+
"start": "webflo start"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@webqit/webflo": "file:../../webflo"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@webqit/oohtml-cli": "latest"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en-US">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="UTF-8">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
7
|
+
<title render="#text: data?.titleBar || data?.title">APP_TITLE</title>
|
|
8
|
+
<meta name="description" content="APP_DESCRIPTION">
|
|
9
|
+
<meta name="apple-mobile-web-app-title" content="APP_TITLE">
|
|
10
|
+
<meta name="color-scheme" content="dark">
|
|
11
|
+
<meta name="theme-color" content="#313131">
|
|
12
|
+
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
|
13
|
+
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
14
|
+
<meta name="mobile-web-app-capable" content="yes">
|
|
15
|
+
<!--
|
|
16
|
+
<link rel="icon" href="/assets/..." type="image/png">
|
|
17
|
+
<link rel="apple-touch-icon" href="/assets/...">
|
|
18
|
+
<link rel="manifest" href="/manifest.json">
|
|
19
|
+
-->
|
|
20
|
+
<script crossorigin ssr src="https://unpkg.com/@webqit/oohtml@latest/dist/main.lite.js"></script>
|
|
21
|
+
<template def="app" ssr src="/assets/app.html" by="oohtml-cli"></template>
|
|
22
|
+
<script type="module" src="/assets/app.js" by="webflo"></script>
|
|
23
|
+
<link rel="stylesheet" href="/assets/app.css">
|
|
24
|
+
</head>
|
|
25
|
+
|
|
26
|
+
<body importscontext="/app">
|
|
27
|
+
|
|
28
|
+
<h1>
|
|
29
|
+
<span><?{ data.title }?></span><br>
|
|
30
|
+
<span><?{ data.greeting }?></span>
|
|
31
|
+
</h1>
|
|
32
|
+
|
|
33
|
+
<import ref="#page.html">
|
|
34
|
+
Import will show here...
|
|
35
|
+
</import>
|
|
36
|
+
|
|
37
|
+
</body>
|
|
38
|
+
|
|
39
|
+
</html>
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import { Context } from '../Context.js';
|
|
2
|
+
import { WebfloRouter } from './webflo-routing/WebfloRouter.js';
|
|
3
|
+
import { _wq } from '../util.js';
|
|
4
|
+
|
|
5
|
+
export class WebfloRuntime {
|
|
6
|
+
|
|
7
|
+
#instanceController = new AbortController;
|
|
8
|
+
get $instanceController() { return this.#instanceController; }
|
|
9
|
+
|
|
10
|
+
static get Context() { return Context; }
|
|
11
|
+
|
|
12
|
+
static get Router() { return WebfloRouter; }
|
|
13
|
+
|
|
14
|
+
#cx;
|
|
15
|
+
get cx() { return this.#cx; }
|
|
16
|
+
|
|
17
|
+
#config;
|
|
18
|
+
get config() { return this.#config; }
|
|
19
|
+
|
|
20
|
+
#routes;
|
|
21
|
+
get routes() { return this.#routes; }
|
|
22
|
+
|
|
23
|
+
constructor(cx) {
|
|
24
|
+
if (!(cx instanceof this.constructor.Context)) {
|
|
25
|
+
throw new Error('Argument #1 must be a Webflo Context instance');
|
|
26
|
+
}
|
|
27
|
+
this.#cx = cx;
|
|
28
|
+
this.#config = this.#cx.config;
|
|
29
|
+
this.#routes = this.#cx.routes;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
env(key) {
|
|
33
|
+
const { ENV } = this.config;
|
|
34
|
+
return key in ENV.mappings
|
|
35
|
+
? ENV.data[ENV.mappings[key]]
|
|
36
|
+
: ENV.data[key];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async initialize() {
|
|
40
|
+
// Do init work
|
|
41
|
+
return this.#instanceController;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async setupCapabilities() {
|
|
45
|
+
// Do init work
|
|
46
|
+
return this.#instanceController;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async hydrate() {
|
|
50
|
+
// Do hydration work
|
|
51
|
+
return this.#instanceController;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
control() {
|
|
55
|
+
// Do control work
|
|
56
|
+
return this.#instanceController;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
createRequest(href, init = {}) {
|
|
60
|
+
return new Request(href, init);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
createHttpCookies({ request, ...rest }) {
|
|
64
|
+
return this.constructor.HttpCookies.create({ request, ...rest });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
createHttpSession({ store, request, ...rest }) {
|
|
68
|
+
return this.constructor.HttpSession.create({ store, request, ...rest });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
createHttpUser({ store, request, session, realtime, ...rest }) {
|
|
72
|
+
return this.constructor.HttpUser.create({ store, request, session, realtime, ...rest });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
createHttpEvent({ request, cookies, session, user, realtime, sdk, detail, signal, state, ...rest }) {
|
|
76
|
+
return this.constructor.HttpEvent.create(null, { request, cookies, session, user, realtime, sdk, detail, signal, state, ...rest });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async dispatchNavigationEvent({ httpEvent, crossLayerFetch, responseRealtime }) {
|
|
80
|
+
const { flags: FLAGS } = this.cx;
|
|
81
|
+
// Resolve rid before dispatching
|
|
82
|
+
if (httpEvent.request.method === 'GET' && httpEvent.url.query['_rid']) {
|
|
83
|
+
const requestMeta = _wq(httpEvent.request, 'meta');
|
|
84
|
+
requestMeta.set('redirectID', httpEvent.url.query['_rid']);
|
|
85
|
+
requestMeta.set('carries', [].concat(await httpEvent.session.get(`carry-store:${requestMeta.get('redirectID')}`) || []));
|
|
86
|
+
await httpEvent.session.delete(`carry-store:${requestMeta.get('redirectID')}`);
|
|
87
|
+
}
|
|
88
|
+
// Dispatch event
|
|
89
|
+
const router = new this.constructor.Router(this, httpEvent.url.pathname);
|
|
90
|
+
await router.route(['SETUP'], httpEvent);
|
|
91
|
+
// Do proper routing for respone
|
|
92
|
+
const response = await new Promise(async (resolve) => {
|
|
93
|
+
let autoLiveResponse, response;
|
|
94
|
+
httpEvent.realtime.wqLifecycle.messaging.then(() => {
|
|
95
|
+
autoLiveResponse = new LiveResponse(null, { status: 202, statusText: 'Accepted', done: false });
|
|
96
|
+
resolve(autoLiveResponse);
|
|
97
|
+
});
|
|
98
|
+
const route = async () => {
|
|
99
|
+
const routeMethods = [httpEvent.request.method, 'default'];
|
|
100
|
+
const remoteFetch = (...args) => this.remoteFetch(...args);
|
|
101
|
+
return await router.route(routeMethods, httpEvent, crossLayerFetch, remoteFetch);
|
|
102
|
+
};
|
|
103
|
+
const fullRoutingPipeline = (this.cx.middlewares || []).concat(route);
|
|
104
|
+
try {
|
|
105
|
+
response = await fullRoutingPipeline.reverse().reduce((next, fn) => {
|
|
106
|
+
return () => fn.call(this.cx, httpEvent, router, next);
|
|
107
|
+
}, null)()/*immediately calling the first*/;
|
|
108
|
+
} catch (e) {
|
|
109
|
+
console.error(e);
|
|
110
|
+
response = new Response(null, { status: 500, statusText: e.message });
|
|
111
|
+
}
|
|
112
|
+
if (!(response instanceof LiveResponse) && !(response instanceof Response)) {
|
|
113
|
+
response = LiveResponse.test(response) === 'Default'
|
|
114
|
+
? Response.from(response)
|
|
115
|
+
: await LiveResponse.from(response, { responsesOK: true });
|
|
116
|
+
}
|
|
117
|
+
// Any "carry" data?
|
|
118
|
+
//await this.handleCarries(httpEvent, response);
|
|
119
|
+
// Resolve now...
|
|
120
|
+
if (autoLiveResponse) {
|
|
121
|
+
await autoLiveResponse.replaceWith(response, { done: true });
|
|
122
|
+
} else {
|
|
123
|
+
resolve(response);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
// Commit data in the exact order. Reason: in how they depend on each other
|
|
127
|
+
for (const storage of [httpEvent.user, httpEvent.session, httpEvent.cookies]) {
|
|
128
|
+
await storage?.commit?.(response, FLAGS['dev']);
|
|
129
|
+
}
|
|
130
|
+
if (response instanceof LiveResponse && response.whileLive()) {
|
|
131
|
+
httpEvent.waitUntil(response.whileLive(true));
|
|
132
|
+
} else {
|
|
133
|
+
httpEvent.waitUntil(Promise.resolve());
|
|
134
|
+
await null; // We need the above resolved before we move on
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Send the X-Background-Messaging-Port header
|
|
138
|
+
// This server's event lifecycle management
|
|
139
|
+
if (!httpEvent.lifeCycleComplete()) {
|
|
140
|
+
if (this.isClientSide) {
|
|
141
|
+
const responseMeta = _wq(response, 'meta');
|
|
142
|
+
responseMeta.set('wqRealtime', responseRealtime);
|
|
143
|
+
} else {
|
|
144
|
+
const upstreamBackgroundMessagingPort = response.headers.get('X-Background-Messaging-Port');
|
|
145
|
+
response.headers.set('X-Background-Messaging-Port', responseRealtime);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// On navigation:
|
|
149
|
+
// Abort httpEvent.realtime and httpEvent itself
|
|
150
|
+
httpEvent.realtime.addEventListener('navigate', (e) => {
|
|
151
|
+
setTimeout(() => { // Allow for global handlers to see the events
|
|
152
|
+
if (e.defaultPrevented) {
|
|
153
|
+
console.log(`Client Messaging Port on ${httpEvent.request.url} not auto-closed on user navigation.`);
|
|
154
|
+
} else {
|
|
155
|
+
httpEvent.realtime.close();
|
|
156
|
+
httpEvent.abort();
|
|
157
|
+
}
|
|
158
|
+
}, 0);
|
|
159
|
+
});
|
|
160
|
+
// On close:
|
|
161
|
+
// Abort httpEvent itself
|
|
162
|
+
httpEvent.realtime.wqLifecycle.close.then(() => {
|
|
163
|
+
httpEvent.abort();
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// On ROOT event complete:
|
|
167
|
+
// Close httpEvent.realtime
|
|
168
|
+
httpEvent.lifeCycleComplete(true).then(() => {
|
|
169
|
+
httpEvent.realtime.close();
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (!this.isClientSide && response instanceof LiveResponse) {
|
|
174
|
+
// Must convert to Response on the server-side before returning
|
|
175
|
+
return response.toResponse({ clientRequestRealtime: httpEvent.realtime });
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return response;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async handleCarries(httpEvent, response) {
|
|
182
|
+
const requestMeta = _wq(httpEvent.request, 'meta');
|
|
183
|
+
const responseMeta = _wq(response, 'meta');
|
|
184
|
+
if (response.headers.get('Location')) {
|
|
185
|
+
// Don't emit the supposedly incoming "carries"
|
|
186
|
+
// Save back to URL
|
|
187
|
+
if (requestMeta.get('carries')?.length) {
|
|
188
|
+
await httpEvent.session.set(`carry-store:${requestMeta.get('redirectID')}`, requestMeta.get('carries'));
|
|
189
|
+
requestMeta.set('carries', []);
|
|
190
|
+
}
|
|
191
|
+
// Stash current byte of "carry"
|
|
192
|
+
if (responseMeta.has('carry')) {
|
|
193
|
+
const $url = new URL(response.headers.get('Location'), httpEvent.request.url);
|
|
194
|
+
if ($url.searchParams.has('_rid')) {
|
|
195
|
+
// If the URL already has a rid, append the new one
|
|
196
|
+
const existingRedirectID = $url.searchParams.get('_rid');
|
|
197
|
+
const existingData = await httpEvent.session.get(`carry-store:${existingRedirectID}`);
|
|
198
|
+
const combinedData = [].concat(responseMeta.get('carry'), existingData || []);
|
|
199
|
+
// Save the combined data back to the session
|
|
200
|
+
await httpEvent.session.set(`carry-store:${existingRedirectID}`, combinedData);
|
|
201
|
+
} else {
|
|
202
|
+
// If not, create a new rid
|
|
203
|
+
const redirectID = (0 | Math.random() * 9e6).toString(36);
|
|
204
|
+
$url.searchParams.set('_rid', redirectID);
|
|
205
|
+
await httpEvent.session.set(`carry-store:${redirectID}`, [].concat(responseMeta.get('carry')));
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
} else {
|
|
209
|
+
// Fire redirect message?
|
|
210
|
+
const flashResponses = requestMeta.get('carries')?.map((c) => c.response).filter((r) => r);
|
|
211
|
+
if (flashResponses?.length) {
|
|
212
|
+
httpEvent.waitUntil(new Promise((resolve) => {
|
|
213
|
+
httpEvent.realtime.wqLifecycle.open.then(() => {
|
|
214
|
+
httpEvent.realtime.postMessage(flashResponses, { wqEventOptions: { type: 'flash' } });
|
|
215
|
+
resolve();
|
|
216
|
+
}, { once: true });
|
|
217
|
+
}));
|
|
218
|
+
}
|
|
219
|
+
requestMeta.set('carries', []);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
streamSlice(stream, { start, end }) {
|
|
224
|
+
let bytesRead = 0;
|
|
225
|
+
const reader = stream.getReader();
|
|
226
|
+
return new ReadableStream({
|
|
227
|
+
async pull(controller) {
|
|
228
|
+
while (true) {
|
|
229
|
+
const { done, value } = await reader.read();
|
|
230
|
+
if (done) {
|
|
231
|
+
controller.close();
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
let chunk = value;
|
|
235
|
+
let chunkStart = bytesRead;
|
|
236
|
+
let chunkEnd = bytesRead + chunk.length;
|
|
237
|
+
if (chunkEnd <= start) {
|
|
238
|
+
// skip entire chunk
|
|
239
|
+
bytesRead += chunk.length;
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
if (chunkStart > end) {
|
|
243
|
+
// past requested range
|
|
244
|
+
controller.close();
|
|
245
|
+
break;
|
|
246
|
+
}
|
|
247
|
+
// slice relevant part
|
|
248
|
+
let sliceStart = Math.max(start - chunkStart, 0);
|
|
249
|
+
let sliceEnd = Math.min(end - chunkStart + 1, chunk.length);
|
|
250
|
+
controller.enqueue(chunk.subarray(sliceStart, sliceEnd));
|
|
251
|
+
bytesRead += chunk.length;
|
|
252
|
+
if (chunkEnd > end) {
|
|
253
|
+
controller.close();
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
},
|
|
258
|
+
cancel(reason) {
|
|
259
|
+
reader.cancel(reason);
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
streamJoin(streams, contentType, totalLength) {
|
|
265
|
+
// Single stream?
|
|
266
|
+
if (streams.length === 1) {
|
|
267
|
+
return streams[0];
|
|
268
|
+
}
|
|
269
|
+
// Multipart!
|
|
270
|
+
const boundary = `WEBFLO_BOUNDARY_${Math.random().toString(36).slice(2)}`;
|
|
271
|
+
const encoder = new TextEncoder();
|
|
272
|
+
// Generator for multipart chunks
|
|
273
|
+
async function* generateMultipart() {
|
|
274
|
+
for (const { stream, start, end } of streams) {
|
|
275
|
+
// Boundary + headers for each part
|
|
276
|
+
yield encoder.encode(`--${boundary}\r\n`);
|
|
277
|
+
yield encoder.encode(
|
|
278
|
+
`Content-Type: ${contentType}\r\n` +
|
|
279
|
+
`Content-Range: bytes ${start}-${end}/${totalLength}\r\n\r\n`
|
|
280
|
+
);
|
|
281
|
+
// Stream the sliced body
|
|
282
|
+
const reader = stream.getReader();
|
|
283
|
+
while (true) {
|
|
284
|
+
const { done, value } = await reader.read();
|
|
285
|
+
if (done) break;
|
|
286
|
+
yield value;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
yield encoder.encode('\r\n');
|
|
290
|
+
}
|
|
291
|
+
// Final boundary
|
|
292
|
+
yield encoder.encode(`--${boundary}--\r\n`);
|
|
293
|
+
}
|
|
294
|
+
// Create ReadableStream from async generator
|
|
295
|
+
return {
|
|
296
|
+
stream: new ReadableStream({
|
|
297
|
+
async pull(controller) {
|
|
298
|
+
const gen = generateMultipart();
|
|
299
|
+
while (true) {
|
|
300
|
+
const { done, value } = await gen.next();
|
|
301
|
+
if (done) {
|
|
302
|
+
controller.close();
|
|
303
|
+
break;
|
|
304
|
+
}
|
|
305
|
+
controller.enqueue(value);
|
|
306
|
+
}
|
|
307
|
+
},
|
|
308
|
+
cancel(reason) {
|
|
309
|
+
// Handle cancellation if needed
|
|
310
|
+
}
|
|
311
|
+
}),
|
|
312
|
+
boundary,
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
createStreamingResponse(httpEvent, readStream, stats) {
|
|
317
|
+
let response;
|
|
318
|
+
const requestRange = httpEvent.request.headers.get('Range', true); // Parses the Range header
|
|
319
|
+
if (requestRange.length) {
|
|
320
|
+
const streams = requestRange.reduce((streams, range) => {
|
|
321
|
+
if (!streams) return;
|
|
322
|
+
const [start, end] = range.render(stats.size); // Resolve offsets
|
|
323
|
+
const currentStart = (streams[streams.length - 1]?.end || -1) + 1;
|
|
324
|
+
if (!range.isValid(currentStart, stats.size)) return; // Only after rendering()
|
|
325
|
+
return streams.concat({ start, end, stream: readStream({ start, end }) });
|
|
326
|
+
}, []);
|
|
327
|
+
if (!streams) {
|
|
328
|
+
return new Response(null, {
|
|
329
|
+
status: 416, statusText: 'Requested Range Not Satisfiable', headers: {
|
|
330
|
+
'Content-Range': `bytes */${stats.size}`,
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
const streamJoin = this.streamJoin(streams, stats.mime, stats.size);
|
|
335
|
+
response = new Response(streamJoin.stream, { status: 206, statusText: 'Partial Content' });
|
|
336
|
+
if (streamJoin.boundary) {
|
|
337
|
+
response.headers.set('Content-Type', `multipart/byteranges; boundary=${streamJoin.boundary}`);
|
|
338
|
+
} else {
|
|
339
|
+
response.headers.set('Content-Type', stats.mime);
|
|
340
|
+
response.headers.set('Content-Range', `bytes ${streamJoin.start}-${streamJoin.end}/${stats.size}`);
|
|
341
|
+
response.headers.set('Content-Length', streamJoin.end - streamJoin.start + 1);
|
|
342
|
+
}
|
|
343
|
+
} else {
|
|
344
|
+
response = new Response(readStream(), { status: 200, statusText: 'OK' });
|
|
345
|
+
response.headers.set('Content-Type', stats.mime);
|
|
346
|
+
response.headers.set('Content-Length', stats.size);
|
|
347
|
+
}
|
|
348
|
+
return response;
|
|
349
|
+
}
|
|
350
|
+
}
|
package/src/runtime-pi/index.js
CHANGED
|
@@ -1,14 +1,7 @@
|
|
|
1
|
+
import * as server from './webflo-server/index.js';
|
|
2
|
+
import * as client from './webflo-client/webflo-codegen.js';
|
|
1
3
|
|
|
2
|
-
/**
|
|
3
|
-
* @imports
|
|
4
|
-
*/
|
|
5
|
-
import * as server from './server/index.js';
|
|
6
|
-
import * as client from './client/generate.js';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* @exports
|
|
10
|
-
*/
|
|
11
4
|
export {
|
|
12
5
|
server,
|
|
13
|
-
client
|
|
6
|
+
client
|
|
14
7
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { HttpCookies } from '../webflo-routing/HttpCookies.js';
|
|
2
|
+
|
|
3
|
+
export class ClientSideCookies extends HttpCookies {
|
|
4
|
+
static create({ request }) {
|
|
5
|
+
return new this({
|
|
6
|
+
request,
|
|
7
|
+
entries: document.cookie.split(';').map((c) => c.split('=').map((s) => s.trim()))
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async commit(response) {
|
|
12
|
+
for (const cookieStr of await this.render()) {
|
|
13
|
+
document.cookie = cookieStr;
|
|
14
|
+
}
|
|
15
|
+
await super.commit();
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export class ClientSideWorkport extends EventTarget {
|
|
2
|
+
|
|
3
|
+
#registration;
|
|
4
|
+
get registration() { return this.#registration; }
|
|
5
|
+
|
|
6
|
+
#ready;
|
|
7
|
+
get ready() { return this.#ready; }
|
|
8
|
+
|
|
9
|
+
#active;
|
|
10
|
+
get active() { return this.#active; }
|
|
11
|
+
|
|
12
|
+
static async initialize(parentNode, file, params = {}) {
|
|
13
|
+
const registration = (await navigator.serviceWorker.getRegistration())
|
|
14
|
+
|| (await navigator.serviceWorker.register(file, { scope: '/', ...params }));
|
|
15
|
+
return new this(parentNode, registration, params);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
#messageHandler;
|
|
19
|
+
constructor(parentNode, registration, params = {}) {
|
|
20
|
+
super(parentNode, params);
|
|
21
|
+
this.#registration = registration;
|
|
22
|
+
this.#ready = navigator.serviceWorker ? navigator.serviceWorker.ready : new Promise(() => {});
|
|
23
|
+
// Helper that updates instance's state
|
|
24
|
+
const stateChange = (target) => {
|
|
25
|
+
// target.state can be any of: "parsed", "installing", "installed", "activating", "activated", "redundant"
|
|
26
|
+
if (target.state === 'redundant') {
|
|
27
|
+
} else if (target.state === 'activated') {
|
|
28
|
+
const existing = this.#active;
|
|
29
|
+
this.#active = target;
|
|
30
|
+
if (!existing) {
|
|
31
|
+
this.dispatchEvent(new Event('open'));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
// We're always installing at first for a new service worker.
|
|
36
|
+
// An existing service would immediately be active
|
|
37
|
+
const initial = this.#registration.active || this.#registration.waiting || this.#registration.installing;
|
|
38
|
+
if (initial) {
|
|
39
|
+
stateChange(initial);
|
|
40
|
+
initial.addEventListener('statechange', (e) => stateChange(e.target));
|
|
41
|
+
// "updatefound" event - a new worker that will control
|
|
42
|
+
// this page is installing somewhere
|
|
43
|
+
this.#registration.addEventListener('updatefound', () => {
|
|
44
|
+
// If updatefound is fired, it means that there's
|
|
45
|
+
// a new service worker being installed.
|
|
46
|
+
stateChange(this.#registration.installing);
|
|
47
|
+
this.#registration.installing.addEventListener('statechange', (e) => stateChange(e.target));
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
this.#messageHandler = (event) => {
|
|
51
|
+
this.dispatchEvent(event);
|
|
52
|
+
};
|
|
53
|
+
navigator.serviceWorker.addEventListener('message', this.#messageHandler);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
postMessage(data, transferOrOptions = []) {
|
|
57
|
+
return this.#active?.postMessage(data, transferOrOptions);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
close() {
|
|
61
|
+
navigator.serviceWorker.removeEventListener('message', this.#messageHandler);
|
|
62
|
+
}
|
|
63
|
+
}
|