@webqit/webflo 0.8.76 → 0.9.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/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} +143 -102
- package/src/runtime-pi/client/RuntimeClient.js +114 -0
- package/src/{runtime → runtime-pi}/client/Storage.js +8 -7
- 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 +102 -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 -61
- package/src/runtime/_RequestHeaders.js +0 -72
- package/src/runtime/_Response.js +0 -56
- package/src/runtime/client/Cache.js +0 -38
- package/src/runtime/client/Http.js +0 -225
- package/src/runtime/client/NavigationEvent.js +0 -21
- package/src/runtime/client/Runtime.js +0 -126
- package/src/runtime/client/StdRequest.js +0 -74
- package/src/runtime/client/Worker.js +0 -312
- package/src/runtime/client/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
package/src/index.js
CHANGED
|
@@ -1,151 +1,18 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
|
|
3
2
|
/**
|
|
4
|
-
* imports
|
|
3
|
+
* @imports
|
|
5
4
|
*/
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import * as DotJson from '@webqit/backpack/src/dotfiles/DotJson.js';
|
|
11
|
-
import { Promptx } from '@webqit/backpack/src/cli/Promptx.js';
|
|
12
|
-
import * as build from './build/index.js';
|
|
13
|
-
import * as config from './config/index.js';
|
|
14
|
-
import * as runtime from './runtime/index.js';
|
|
15
|
-
import * as services from './services/index.js';
|
|
5
|
+
import * as config from './config-pi/index.js';
|
|
6
|
+
import * as deployment from './deployment-pi/index.js';
|
|
7
|
+
import * as runtime from './runtime-pi/index.js';
|
|
8
|
+
import * as services from './services-pi/index.js';
|
|
16
9
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
// ------------------------------------------
|
|
27
|
-
|
|
28
|
-
const { command, keywords, flags, options, ellipsis } = parseArgs(process.argv);
|
|
29
|
-
|
|
30
|
-
// ------------------------------------------
|
|
31
|
-
|
|
32
|
-
console.log('');
|
|
33
|
-
|
|
34
|
-
(async function() {
|
|
35
|
-
const layout = await config.layout.read({});
|
|
36
|
-
layout.PKG = DotJson.read('./package.json');
|
|
37
|
-
switch(command) {
|
|
38
|
-
|
|
39
|
-
// --------------------------
|
|
40
|
-
|
|
41
|
-
case 'build':
|
|
42
|
-
build.client.build(Ui, flags, layout);
|
|
43
|
-
break;
|
|
44
|
-
|
|
45
|
-
// --------------------------
|
|
46
|
-
|
|
47
|
-
case 'config':
|
|
48
|
-
|
|
49
|
-
var domain = Object.keys(keywords)[0];
|
|
50
|
-
// ----------------
|
|
51
|
-
if (!domain && ellipsis) {
|
|
52
|
-
domain = await Promptx({
|
|
53
|
-
name: 'domain',
|
|
54
|
-
type: 'select',
|
|
55
|
-
choices: Object.keys(config).map(c => ({value: c})),
|
|
56
|
-
message: 'Please select a configuration domain',
|
|
57
|
-
}).then(d => d.domain);
|
|
58
|
-
}
|
|
59
|
-
if (!domain || !config[domain]) {
|
|
60
|
-
Ui.log(Ui.f`Please add a configuration domain to the ${command} command. For options, use the ellipsis ${'...'}`);
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
// ----------------
|
|
64
|
-
const data = await config[domain].read(flags, layout);
|
|
65
|
-
Promptx(await config[domain].questions(data, {}, layout)).then(async _data => {
|
|
66
|
-
await config[domain].write(_merge(data, _data), flags, layout);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
break;
|
|
70
|
-
|
|
71
|
-
// --------------------------
|
|
72
|
-
|
|
73
|
-
case 'start':
|
|
74
|
-
runtime.server.start(Ui, flags, layout);
|
|
75
|
-
break;
|
|
76
|
-
|
|
77
|
-
case 'stop':
|
|
78
|
-
case 'restart':
|
|
79
|
-
var _runtime = Object.keys(keywords)[0];
|
|
80
|
-
// ----------------
|
|
81
|
-
if (!_runtime && ellipsis) {
|
|
82
|
-
_runtime = await Promptx({
|
|
83
|
-
name: 'runtime',
|
|
84
|
-
type: 'select',
|
|
85
|
-
choices: (await runtime.server.processes(Ui)).map(r => ({title: r.name, description: r.status, value: r.name})).concat({description: 'All of the above', value: 'all'}),
|
|
86
|
-
message: 'Please select a runtime name',
|
|
87
|
-
}).then(d => d.runtime);
|
|
88
|
-
}
|
|
89
|
-
// ----------------
|
|
90
|
-
await runtime.server[command](Ui, _runtime || 'all', flags);
|
|
91
|
-
process.exit();
|
|
92
|
-
break;
|
|
93
|
-
|
|
94
|
-
case 'processes':
|
|
95
|
-
const processes = await runtime.server.processes(Ui, flags);
|
|
96
|
-
Ui.title(`SERVERS`);
|
|
97
|
-
if (processes.length) {
|
|
98
|
-
processes.forEach(service => {
|
|
99
|
-
Ui.log(Ui.f`> ${service}`);
|
|
100
|
-
});
|
|
101
|
-
} else {
|
|
102
|
-
Ui.log(Ui.f`> (empty)`);
|
|
103
|
-
}
|
|
104
|
-
process.exit();
|
|
105
|
-
break;
|
|
106
|
-
|
|
107
|
-
// --------------------------
|
|
108
|
-
|
|
109
|
-
case 'deploy':
|
|
110
|
-
var origin = Object.keys(keywords)[0],
|
|
111
|
-
options;
|
|
112
|
-
// ----------------
|
|
113
|
-
if (!origin && ellipsis) {
|
|
114
|
-
if (!(options = (await config.origins.read(flags, layout)).REPOS) || _isEmpty(options)) {
|
|
115
|
-
Ui.log(Ui.f`Please configure an origin (${'webflo config ...'}) to use the ${'deploy'} command.`);
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
origin = await Promptx({
|
|
119
|
-
name: 'origin',
|
|
120
|
-
type: 'select',
|
|
121
|
-
choices: options.map(r => ({value: r.TAG})),
|
|
122
|
-
message: 'Please select a origin',
|
|
123
|
-
}).then(d => d.origin);
|
|
124
|
-
}
|
|
125
|
-
if (!origin) {
|
|
126
|
-
Ui.log(Ui.f`Please add an origin name to the ${command} command. For options, use the ellipsis ${'...'}`);
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
// ----------------
|
|
130
|
-
services.origins.deploy(Ui, origin, flags, layout);
|
|
131
|
-
break;
|
|
132
|
-
|
|
133
|
-
// --------------------------
|
|
134
|
-
|
|
135
|
-
case 'cert':
|
|
136
|
-
var domains = Object.keys(keywords);
|
|
137
|
-
services.certbot.generate(Ui, domains, flags, layout);
|
|
138
|
-
break;
|
|
139
|
-
|
|
140
|
-
case 'help':
|
|
141
|
-
default:
|
|
142
|
-
Ui.title(`NAVIGATOR HELP`);
|
|
143
|
-
Ui.log('');
|
|
144
|
-
Ui.log(Ui.f`Say ${'webflo'} <${'command'}>`);
|
|
145
|
-
Ui.log('');
|
|
146
|
-
Ui.log(Ui.f`Where <${'command'}> is one of:`);
|
|
147
|
-
Ui.log(Ui.f`${commands}`);
|
|
148
|
-
Ui.log('');
|
|
149
|
-
Ui.log(Ui.f`You may also refer to the Webflo DOCS as ${'https://webqit.io/tooling/webflo'}`);
|
|
150
|
-
}
|
|
151
|
-
})();
|
|
10
|
+
/**
|
|
11
|
+
* @exports
|
|
12
|
+
*/
|
|
13
|
+
export {
|
|
14
|
+
config,
|
|
15
|
+
deployment,
|
|
16
|
+
runtime,
|
|
17
|
+
services,
|
|
18
|
+
}
|
|
@@ -2,10 +2,8 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* @imports
|
|
4
4
|
*/
|
|
5
|
-
import _isString from '@webqit/util/js/
|
|
6
|
-
import
|
|
7
|
-
import _isArray from '@webqit/util/js/isArray.js';
|
|
8
|
-
import _arrFrom from '@webqit/util/arr/from.js';
|
|
5
|
+
import { _isString, _isFunction, _isArray } from '@webqit/util/js/index.js';
|
|
6
|
+
import { _from as _arrFrom } from '@webqit/util/arr/index.js';
|
|
9
7
|
|
|
10
8
|
/**
|
|
11
9
|
* ---------------------------
|
|
@@ -19,16 +17,14 @@ export default class Router {
|
|
|
19
17
|
* Constructs a new Router instance
|
|
20
18
|
* over route definitions.
|
|
21
19
|
*
|
|
22
|
-
* @param
|
|
23
|
-
* @param
|
|
24
|
-
* @param object context
|
|
20
|
+
* @param Context cx
|
|
21
|
+
* @param String|Array path
|
|
25
22
|
*
|
|
26
23
|
* @return void
|
|
27
24
|
*/
|
|
28
|
-
constructor(
|
|
25
|
+
constructor(cx, path) {
|
|
26
|
+
this.cx = cx;
|
|
29
27
|
this.path = _isArray(path) ? path : (path + '').split('/').filter(a => a);
|
|
30
|
-
this.layout = layout;
|
|
31
|
-
this.context = context;
|
|
32
28
|
}
|
|
33
29
|
|
|
34
30
|
/**
|
|
@@ -38,10 +34,11 @@ export default class Router {
|
|
|
38
34
|
* @param Object event
|
|
39
35
|
* @param any arg
|
|
40
36
|
* @param function _default
|
|
37
|
+
* @param function remoteFetch
|
|
41
38
|
*
|
|
42
39
|
* @return object
|
|
43
40
|
*/
|
|
44
|
-
|
|
41
|
+
async route(method, event, arg, _default, remoteFetch = null) {
|
|
45
42
|
|
|
46
43
|
const $this = this;
|
|
47
44
|
|
|
@@ -49,15 +46,17 @@ export default class Router {
|
|
|
49
46
|
// The loop
|
|
50
47
|
// ----------------
|
|
51
48
|
const next = async function(thisTick) {
|
|
52
|
-
const
|
|
49
|
+
const thisContext = {};
|
|
53
50
|
if (!thisTick.trail || thisTick.trail.length < thisTick.destination.length) {
|
|
54
51
|
thisTick = await $this.readTick(thisTick);
|
|
55
52
|
// -------------
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
$this.finalizeHandlerContext(
|
|
53
|
+
thisContext.pathname = `/${thisTick.trail.join('/')}`;
|
|
54
|
+
thisContext.stepname = thisTick.trail[thisTick.trail.length - 1];
|
|
55
|
+
$this.finalizeHandlerContext(thisContext, thisTick);
|
|
59
56
|
// -------------
|
|
60
57
|
if (thisTick.exports) {
|
|
58
|
+
// Broadcast any hints exported by handler
|
|
59
|
+
if (thisTick.exports.hints) { await event.port.post({ ...thisTick.exports.hints, $type: 'handler:hints' }); }
|
|
61
60
|
const methods = _arrFrom(thisTick.method);
|
|
62
61
|
const handler = _isFunction(thisTick.exports) && methods.includes('default') ? thisTick.exports : methods.reduce((_handler, name) => _handler || thisTick.exports[name.toLowerCase()], null);
|
|
63
62
|
if (handler) {
|
|
@@ -102,7 +101,7 @@ export default class Router {
|
|
|
102
101
|
_next.pathname = nextPathname.join('/');
|
|
103
102
|
_next.stepname = nextPathname[0];
|
|
104
103
|
// -------------
|
|
105
|
-
return await handler.call(
|
|
104
|
+
return await handler.call(thisContext, thisTick.event, thisTick.arg, _next/*next*/, remoteFetch);
|
|
106
105
|
}
|
|
107
106
|
// Handler not found but exports found
|
|
108
107
|
return next(thisTick);
|
|
@@ -115,7 +114,7 @@ export default class Router {
|
|
|
115
114
|
// Local file
|
|
116
115
|
// -------------
|
|
117
116
|
if (_default) {
|
|
118
|
-
return await _default.call(
|
|
117
|
+
return await _default.call(thisContext, thisTick.event, thisTick.arg, remoteFetch);
|
|
119
118
|
}
|
|
120
119
|
};
|
|
121
120
|
|
|
@@ -126,5 +125,6 @@ export default class Router {
|
|
|
126
125
|
arg,
|
|
127
126
|
});
|
|
128
127
|
|
|
129
|
-
}
|
|
130
|
-
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
}
|
|
@@ -14,8 +14,8 @@ import _Router from '../Router.js';
|
|
|
14
14
|
export default class Router extends _Router {
|
|
15
15
|
|
|
16
16
|
async readTick(thisTick) {
|
|
17
|
-
var routeTree = this.layout;
|
|
18
|
-
var routePaths = Object.keys(this.layout);
|
|
17
|
+
var routeTree = this.cx.layout;
|
|
18
|
+
var routePaths = Object.keys(this.cx.layout);
|
|
19
19
|
if (thisTick.trail) {
|
|
20
20
|
thisTick.currentSegment = thisTick.destination[thisTick.trail.length];
|
|
21
21
|
thisTick.currentSegmentOnFile = [ thisTick.currentSegment, '-' ].reduce((_segmentOnFile, _seg) => {
|
|
@@ -2,22 +2,64 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* @imports
|
|
4
4
|
*/
|
|
5
|
-
import _before from '@webqit/util/str/
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import { Observer } from './Runtime.js';
|
|
5
|
+
import { _before, _toTitle } from '@webqit/util/str/index.js';
|
|
6
|
+
import { Observer } from '@webqit/oohtml-ssr/apis.js';
|
|
7
|
+
import Storage from './Storage.js';
|
|
9
8
|
import Url from './Url.js';
|
|
9
|
+
import { wwwFormUnserialize, wwwFormSet, wwwFormSerialize } from '../util.js';
|
|
10
|
+
import * as whatwag from './whatwag.js';
|
|
11
|
+
import xURL from '../xURL.js';
|
|
12
|
+
import xFormData from "../xFormData.js";
|
|
13
|
+
import xRequestHeaders from "../xRequestHeaders.js";
|
|
14
|
+
import xResponseHeaders from "../xResponseHeaders.js";
|
|
15
|
+
import xRequest from "../xRequest.js";
|
|
16
|
+
import xResponse from "../xResponse.js";
|
|
17
|
+
import xfetch from '../xfetch.js';
|
|
18
|
+
import xHttpEvent from '../xHttpEvent.js';
|
|
19
|
+
|
|
20
|
+
const URL = xURL(whatwag.URL);
|
|
21
|
+
const FormData = xFormData(whatwag.FormData);
|
|
22
|
+
const ReadableStream = whatwag.ReadableStream;
|
|
23
|
+
const RequestHeaders = xRequestHeaders(whatwag.Headers);
|
|
24
|
+
const ResponseHeaders = xResponseHeaders(whatwag.Headers);
|
|
25
|
+
const Request = xRequest(whatwag.Request, RequestHeaders, FormData);
|
|
26
|
+
const Response = xResponse(whatwag.Response, ResponseHeaders, FormData);
|
|
27
|
+
const fetch = xfetch(whatwag.fetch);
|
|
28
|
+
const HttpEvent = xHttpEvent(Request, Response, URL);
|
|
29
|
+
|
|
30
|
+
export {
|
|
31
|
+
URL,
|
|
32
|
+
FormData,
|
|
33
|
+
ReadableStream,
|
|
34
|
+
RequestHeaders,
|
|
35
|
+
ResponseHeaders,
|
|
36
|
+
Request,
|
|
37
|
+
Response,
|
|
38
|
+
fetch,
|
|
39
|
+
HttpEvent,
|
|
40
|
+
Observer,
|
|
41
|
+
}
|
|
10
42
|
|
|
11
|
-
export default class
|
|
43
|
+
export default class Runtime {
|
|
12
44
|
|
|
13
|
-
|
|
14
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Runtime
|
|
47
|
+
*
|
|
48
|
+
* @param Object cx
|
|
49
|
+
* @param Function clientCallback
|
|
50
|
+
*
|
|
51
|
+
* @return void
|
|
52
|
+
*/
|
|
53
|
+
constructor(cx, clientCallback) {
|
|
15
54
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
55
|
+
// ---------------
|
|
56
|
+
this.cx = cx;
|
|
57
|
+
this.clients = new Map;
|
|
58
|
+
// ---------------
|
|
59
|
+
this.cx.runtime = this;
|
|
60
|
+
let client = clientCallback(this.cx, '*');
|
|
61
|
+
if (!client || !client.handle) throw new Error(`Application instance must define a ".handle()" method.`);
|
|
62
|
+
this.clients.set('*', client);
|
|
21
63
|
|
|
22
64
|
// -----------------------
|
|
23
65
|
// Initialize location
|
|
@@ -113,26 +155,16 @@ export default class Navigator {
|
|
|
113
155
|
}
|
|
114
156
|
});
|
|
115
157
|
|
|
116
|
-
/**
|
|
117
|
-
* ----------------
|
|
118
|
-
* Navigator network
|
|
119
|
-
* ----------------
|
|
120
|
-
*/
|
|
121
|
-
|
|
122
158
|
// -----------------------
|
|
123
159
|
// Initialize network
|
|
124
160
|
Observer.set(this, 'network', {});
|
|
125
161
|
window.addEventListener('online', () => Observer.set(this.network, 'online', navigator.onLine));
|
|
126
162
|
window.addEventListener('offline', () => Observer.set(this.network, 'online', navigator.onLine));
|
|
127
163
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
*/
|
|
133
|
-
|
|
134
|
-
this.go(this.location, { srcType: 'init' });
|
|
135
|
-
}
|
|
164
|
+
// ---------------
|
|
165
|
+
this.go(this.location, {}, { srcType: 'init' });
|
|
166
|
+
// ---------------
|
|
167
|
+
}
|
|
136
168
|
|
|
137
169
|
/**
|
|
138
170
|
* History object
|
|
@@ -145,91 +177,100 @@ export default class Navigator {
|
|
|
145
177
|
* Performs a request.
|
|
146
178
|
*
|
|
147
179
|
* @param object|string href
|
|
148
|
-
* @param object
|
|
180
|
+
* @param object init
|
|
181
|
+
* @param object src
|
|
149
182
|
*
|
|
150
|
-
* @return
|
|
183
|
+
* @return Response
|
|
151
184
|
*/
|
|
152
|
-
async go(url,
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
headers: {
|
|
159
|
-
'Accept': 'application/json',
|
|
160
|
-
'Cache-Control': 'no-store',
|
|
161
|
-
'X-Redirect-Policy': 'manual-when-cross-origin',
|
|
162
|
-
'X-Redirect-Code': xRedirectCode,
|
|
163
|
-
'X-Powered-By': '@webqit/webflo',
|
|
164
|
-
...(params.headers || {}),
|
|
165
|
-
},
|
|
166
|
-
referrer: window.document.location.href,
|
|
167
|
-
signal: this._abortController.signal,
|
|
168
|
-
});
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
// Initiates remote fetch and sets the status
|
|
172
|
-
const remoteRequest = request => {
|
|
173
|
-
Observer.set(this.network, 'remote', true);
|
|
174
|
-
let _response = fetch(request);
|
|
175
|
-
// This catch() is NOT intended to handle failure of the fetch
|
|
176
|
-
_response.catch(e => Observer.set(this.network, 'error', e.message));
|
|
177
|
-
// Save a reference to this
|
|
178
|
-
return _response.then(async response => {
|
|
179
|
-
// Stop loading status
|
|
180
|
-
Observer.set(this.network, 'remote', false);
|
|
181
|
-
return response;
|
|
182
|
-
});
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
// Handles response object
|
|
186
|
-
const handleResponse = async (response, params) => {
|
|
187
|
-
response = await response;
|
|
188
|
-
Observer.set(this.network, 'remote', false);
|
|
189
|
-
Observer.set(this.network, 'error', null);
|
|
190
|
-
if (['link', 'form'].includes(params.srcType)) {
|
|
191
|
-
Observer.set(params.src, 'active', false);
|
|
192
|
-
Observer.set(params.submitter || {}, 'active', false);
|
|
193
|
-
}
|
|
194
|
-
if (!response) return;
|
|
195
|
-
if (response.redirected && this.isSameOrigin(response.url)) {
|
|
196
|
-
Observer.set(this.location, { href: response.url }, {
|
|
197
|
-
detail: { isRedirect: true },
|
|
198
|
-
});
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
let location = response.headers.get('Location');
|
|
202
|
-
if (location && response.status === xRedirectCode) {
|
|
203
|
-
Observer.set(this.network, 'redirecting', location);
|
|
204
|
-
window.location = location;
|
|
205
|
-
}
|
|
206
|
-
};
|
|
207
|
-
|
|
185
|
+
async go(url, init = {}, detail = {}) {
|
|
186
|
+
if (this._abortController) {
|
|
187
|
+
this._abortController.abort();
|
|
188
|
+
}
|
|
189
|
+
this._abortController = new AbortController();
|
|
190
|
+
this._xRedirectCode = 300;
|
|
208
191
|
// ------------
|
|
209
|
-
url = typeof url === 'string' ?
|
|
210
|
-
|
|
192
|
+
url = typeof url === 'string' ? new whatwag.URL(url) : url;
|
|
193
|
+
init = { referrer: this.location.href, ...init };
|
|
211
194
|
// ------------
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
Observer.set(
|
|
195
|
+
if (detail.srcType !== 'init' && (_before(url.href, '#') === _before(init.referrer, '#') && (init.method || 'GET').toUpperCase() === 'GET')) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
// ------------
|
|
199
|
+
if (['link', 'form'].includes(detail.srcType)) {
|
|
200
|
+
Observer.set(detail.src, 'active', true);
|
|
201
|
+
Observer.set(detail.submitter || {}, 'active', true);
|
|
218
202
|
}
|
|
219
203
|
// ------------
|
|
204
|
+
Observer.set(this.location, url, { detail: { ...init, ...detail }, });
|
|
205
|
+
Observer.set(this.network, 'redirecting', null);
|
|
206
|
+
// ------------
|
|
207
|
+
// The request object
|
|
208
|
+
let request = this.generateRequest(url.href, init);
|
|
209
|
+
// The navigation event
|
|
210
|
+
let httpEvent = new HttpEvent(request, detail, (id = null, persistent = false) => this.getSession(httpEvent, id, persistent));
|
|
211
|
+
// Response
|
|
212
|
+
let response = await this.clients.get('*').handle(httpEvent, (...args) => this.remoteFetch(...args));
|
|
213
|
+
let finalResponse = this.handleResponse(httpEvent, response);
|
|
214
|
+
// Return value
|
|
215
|
+
return finalResponse;
|
|
216
|
+
}
|
|
220
217
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
218
|
+
// Generates request object
|
|
219
|
+
generateRequest(href, init) {
|
|
220
|
+
return new Request(href, {
|
|
221
|
+
signal: this._abortController.signal,
|
|
222
|
+
...init,
|
|
223
|
+
headers: {
|
|
224
|
+
'Accept': 'application/json',
|
|
225
|
+
'X-Redirect-Policy': 'manual-when-cross-origin',
|
|
226
|
+
'X-Redirect-Code': this._xRedirectCode,
|
|
227
|
+
'X-Powered-By': '@webqit/webflo',
|
|
228
|
+
...(init.headers || {}),
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
}
|
|
226
232
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
233
|
+
// Generates session object
|
|
234
|
+
getSession(e, id = null, persistent = false) {
|
|
235
|
+
return Storage(id, persistent);
|
|
236
|
+
}
|
|
230
237
|
|
|
231
|
-
|
|
232
|
-
|
|
238
|
+
// Initiates remote fetch and sets the status
|
|
239
|
+
remoteFetch(request) {
|
|
240
|
+
Observer.set(this.network, 'remote', true);
|
|
241
|
+
let _response = fetch(request);
|
|
242
|
+
// This catch() is NOT intended to handle failure of the fetch
|
|
243
|
+
_response.catch(e => Observer.set(this.network, 'error', e.message));
|
|
244
|
+
// Return xResponse
|
|
245
|
+
return _response.then(async response => {
|
|
246
|
+
// Stop loading status
|
|
247
|
+
Observer.set(this.network, 'remote', false);
|
|
248
|
+
return new Response(response);
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Handles response object
|
|
253
|
+
handleResponse(e, response) {
|
|
254
|
+
if (!(response instanceof Response)) { response = new Response(response); }
|
|
255
|
+
Observer.set(this.network, 'remote', false);
|
|
256
|
+
Observer.set(this.network, 'error', null);
|
|
257
|
+
if (['link', 'form'].includes(e.detail.srcType)) {
|
|
258
|
+
Observer.set(e.detail.src, 'active', false);
|
|
259
|
+
Observer.set(e.detail.submitter || {}, 'active', false);
|
|
260
|
+
}
|
|
261
|
+
if (response.redirected && this.isSameOrigin(response.url)) {
|
|
262
|
+
Observer.set(this.location, { href: response.url }, {
|
|
263
|
+
detail: { isRedirect: true },
|
|
264
|
+
});
|
|
265
|
+
} else {
|
|
266
|
+
let location = response.headers.get('Location');
|
|
267
|
+
if (location && response.status === this._xRedirectCode) {
|
|
268
|
+
Observer.set(this.network, 'redirecting', location);
|
|
269
|
+
window.location = location;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return response;
|
|
273
|
+
}
|
|
233
274
|
|
|
234
275
|
/**
|
|
235
276
|
* Checks if an URL is same origin.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* @imports
|
|
4
|
+
*/
|
|
5
|
+
import { Observer } from './Runtime.js';
|
|
6
|
+
import WorkerComm from './WorkerComm.js';
|
|
7
|
+
import Router from './Router.js';
|
|
8
|
+
|
|
9
|
+
export default class RuntimeClient {
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* RuntimeClient
|
|
13
|
+
*
|
|
14
|
+
* @param Context cx
|
|
15
|
+
*/
|
|
16
|
+
constructor(cx) {
|
|
17
|
+
this.cx = cx;
|
|
18
|
+
const workerComm = new WorkerComm('/worker.js', { startMessages: true });
|
|
19
|
+
Observer.observe(workerComm, changes => {
|
|
20
|
+
//console.log('SERVICE_WORKER_STATE_CHANGE', changes[0].name, changes[0].value);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Handles HTTP events.
|
|
26
|
+
*
|
|
27
|
+
* @param HttpEvent httpEvent
|
|
28
|
+
* @param Function remoteFetch
|
|
29
|
+
*
|
|
30
|
+
* @return Response
|
|
31
|
+
*/
|
|
32
|
+
async handle(httpEvent, remoteFetch) {
|
|
33
|
+
// The app router
|
|
34
|
+
const router = new Router(this.cx, httpEvent.url.pathname);
|
|
35
|
+
const handle = async () => {
|
|
36
|
+
// --------
|
|
37
|
+
// ROUTE FOR DATA
|
|
38
|
+
// --------
|
|
39
|
+
let httpMethodName = httpEvent.request.method.toLowerCase();
|
|
40
|
+
let response = await router.route([httpMethodName === 'delete' ? 'del' : httpMethodName, 'default'], httpEvent, {}, async event => {
|
|
41
|
+
return remoteFetch(event.request);
|
|
42
|
+
}, remoteFetch);
|
|
43
|
+
if (!(response instanceof httpEvent.Response)) {
|
|
44
|
+
response = new httpEvent.Response(response);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// --------
|
|
48
|
+
// Rendering
|
|
49
|
+
// --------
|
|
50
|
+
if (response.ok && response.headers.contentType === 'application/json') {
|
|
51
|
+
await this.render(httpEvent, response, router);
|
|
52
|
+
await this.scrollIntoView(httpEvent);
|
|
53
|
+
} else if (!response.ok) {
|
|
54
|
+
await this.unrender();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return response;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// --------
|
|
61
|
+
// PIPE THROUGH MIDDLEWARES
|
|
62
|
+
// --------
|
|
63
|
+
return (this.cx.middlewares || []).concat(handle).reverse().reduce((next, fn) => {
|
|
64
|
+
return () => fn.call(this.cx, httpEvent, router, next);
|
|
65
|
+
}, null)();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Renderer
|
|
69
|
+
async render(httpEvent, response, router) {
|
|
70
|
+
let data = await response.json();
|
|
71
|
+
return router.route('render', httpEvent, data, async (httpEvent, data) => {
|
|
72
|
+
// --------
|
|
73
|
+
// OOHTML would waiting for DOM-ready in order to be initialized
|
|
74
|
+
await new Promise(res => window.WebQit.DOM.ready(res));
|
|
75
|
+
if (!window.document.state.env) {
|
|
76
|
+
window.document.setState({
|
|
77
|
+
env: 'client',
|
|
78
|
+
onHydration: (httpEvent.detail || {}).srcType === 'init',
|
|
79
|
+
network: this.cx.runtime.network,
|
|
80
|
+
url: this.cx.runtime.location,
|
|
81
|
+
}, { update: true });
|
|
82
|
+
}
|
|
83
|
+
window.document.setState({ page: data }, { update: 'merge' });
|
|
84
|
+
window.document.body.setAttribute('template', 'page/' + httpEvent.url.pathname.split('/').filter(a => a).map(a => a + '+-').join('/'));
|
|
85
|
+
await new Promise(res => (window.document.templatesReadyState === 'complete' && res(), window.document.addEventListener('templatesreadystatechange', res)));
|
|
86
|
+
return true;
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Unrender
|
|
91
|
+
async unrender() {
|
|
92
|
+
window.document.setState({ page: {} }, { update: 'merge' });
|
|
93
|
+
window.document.body.setAttribute('template', '');
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Normalize scroll position
|
|
97
|
+
async scrollIntoView(httpEvent) {
|
|
98
|
+
if (!(httpEvent.detail.src instanceof Element)) return;
|
|
99
|
+
await new Promise(res => setTimeout(res, 10));
|
|
100
|
+
let viewportTop;
|
|
101
|
+
if (httpEvent.url.hash && (urlTarget = document.querySelector(httpEvent.url.hash))) {
|
|
102
|
+
urlTarget.scrollIntoView();
|
|
103
|
+
} else if (viewportTop = Array.from(document.querySelectorAll('[data-viewport-top]')).pop()) {
|
|
104
|
+
viewportTop.focus();
|
|
105
|
+
} else {
|
|
106
|
+
document.documentElement.classList.add('scroll-reset');
|
|
107
|
+
document.body.scrollIntoView();
|
|
108
|
+
await new Promise(res => setTimeout(res, 600));
|
|
109
|
+
document.documentElement.classList.remove('scroll-reset');
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
}
|
|
114
|
+
|