@webqit/webflo 0.8.71-0 → 0.8.72-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 +1 -1
- package/src/runtime/_MessageStream.js +9 -9
- package/src/runtime/_NavigationEvent.js +6 -4
- package/src/runtime/client/Http.js +3 -1
- package/src/runtime/client/NavigationEvent.js +1 -0
- package/src/runtime/client/Runtime.js +1 -1
- package/src/runtime/server/NavigationEvent.js +3 -2
- package/src/runtime/server/Runtime.js +66 -91
package/package.json
CHANGED
|
@@ -104,26 +104,26 @@ const _MessageStream = (NativeMessageStream, Headers, FormData) => {
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
// Payload
|
|
107
|
-
|
|
108
|
-
if (!this._typedDataCache.
|
|
109
|
-
this._typedDataCache.
|
|
110
|
-
var request = this,
|
|
107
|
+
data(force = false) {
|
|
108
|
+
if (!this._typedDataCache.data || force) {
|
|
109
|
+
this._typedDataCache.data = new Promise(async resolve => {
|
|
110
|
+
var request = this, data, contentType = request.headers.get('content-type') || '';
|
|
111
111
|
var type = contentType === 'application/json' || this._typedDataCache.json ? 'json' : (
|
|
112
112
|
contentType === 'application/x-www-form-urlencoded' || contentType.startsWith('multipart/') || this._typedDataCache.formData || (!contentType && !['get'].includes((request.method || '').toLowerCase())) ? 'formData' : (
|
|
113
113
|
contentType === 'text/plain' ? 'plain' : 'other'
|
|
114
114
|
)
|
|
115
115
|
);
|
|
116
116
|
if (type === 'formData') {
|
|
117
|
-
|
|
117
|
+
data = (await request.formData()).json();
|
|
118
118
|
} else {
|
|
119
|
-
|
|
119
|
+
data = type === 'json' ? await request.json() : (
|
|
120
120
|
type === 'plain' ? await request.text() : request.body
|
|
121
121
|
);
|
|
122
122
|
}
|
|
123
|
-
resolve(
|
|
123
|
+
resolve(data);
|
|
124
124
|
});
|
|
125
125
|
}
|
|
126
|
-
return this._typedDataCache.
|
|
126
|
+
return this._typedDataCache.data;
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
};
|
|
@@ -178,7 +178,7 @@ export function encodeBody(body, globals) {
|
|
|
178
178
|
contentLength: Buffer.byteLength(detailsObj.body, 'utf8'), // Buffer.from(string).length
|
|
179
179
|
};
|
|
180
180
|
}
|
|
181
|
-
detailsObj.
|
|
181
|
+
detailsObj.data = body;
|
|
182
182
|
}
|
|
183
183
|
return detailsObj;
|
|
184
184
|
}
|
|
@@ -22,12 +22,14 @@ const _NavigationEvent = globals => {
|
|
|
22
22
|
/**
|
|
23
23
|
* Initializes a new NavigationEvent instance.
|
|
24
24
|
*
|
|
25
|
-
* @param Request
|
|
26
|
-
* @param Object
|
|
25
|
+
* @param Request _request
|
|
26
|
+
* @param Object _session
|
|
27
|
+
* @param Function _sessionFactory
|
|
27
28
|
*/
|
|
28
|
-
constructor(_request, _session = null) {
|
|
29
|
+
constructor(_request, _session = {}, _sessionFactory = null) {
|
|
29
30
|
this._request = _request;
|
|
30
31
|
this._session = _session;
|
|
32
|
+
this.sessionFactory = _sessionFactory;
|
|
31
33
|
// -------
|
|
32
34
|
this.URL = URL;
|
|
33
35
|
// -------
|
|
@@ -73,7 +75,7 @@ const _NavigationEvent = globals => {
|
|
|
73
75
|
init._proxy.referrer = this.request.url;
|
|
74
76
|
request = new NavigationEvent.Request(this._request, init);
|
|
75
77
|
}
|
|
76
|
-
return new NavigationEvent(request, this._session);
|
|
78
|
+
return new NavigationEvent(request, this._session, this.sessionFactory);
|
|
77
79
|
}
|
|
78
80
|
|
|
79
81
|
}
|
|
@@ -81,7 +81,7 @@ export default class Http {
|
|
|
81
81
|
var anchor, href;
|
|
82
82
|
if ((anchor = e.target.closest('a')) && (href = anchor.href)
|
|
83
83
|
// And not towards any target nor have a download directive
|
|
84
|
-
&& !anchor.target && !anchor.download
|
|
84
|
+
&& !anchor.target && !anchor.download && !href.includes('/my-account')
|
|
85
85
|
// Same origin... but...
|
|
86
86
|
&& (!anchor.origin || anchor.origin === instance.location.origin)) {
|
|
87
87
|
if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) {
|
|
@@ -187,6 +187,8 @@ export default class Http {
|
|
|
187
187
|
};
|
|
188
188
|
const handleResponse = response => {
|
|
189
189
|
if (response && response.redirected) {
|
|
190
|
+
window.location = response.url;
|
|
191
|
+
return;
|
|
190
192
|
var actionEl = window.document.createElement('a');
|
|
191
193
|
if ((actionEl.href = response.url) && (!actionEl.origin || actionEl.origin === instance.location.origin)) {
|
|
192
194
|
Observer.set(instance.location, { href: response.url }, {
|
|
@@ -111,7 +111,7 @@ export default function(layout, params) {
|
|
|
111
111
|
});
|
|
112
112
|
});
|
|
113
113
|
if ($context.response instanceof clientNavigationEvent.Response) {
|
|
114
|
-
$context.response = await $context.response.
|
|
114
|
+
$context.response = await $context.response.data();
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
// --------
|
|
@@ -6,7 +6,7 @@ import { URL } from 'url';
|
|
|
6
6
|
import { Readable } from "stream";
|
|
7
7
|
import { FormData, File, Blob } from 'formdata-node';
|
|
8
8
|
import { FormDataEncoder } from 'form-data-encoder';
|
|
9
|
-
import { Request, Response, Headers } from 'node-fetch';
|
|
9
|
+
import fetch, { Request, Response, Headers } from 'node-fetch';
|
|
10
10
|
import _NavigationEvent from '../_NavigationEvent.js';
|
|
11
11
|
import _FormData from '../_FormData.js';
|
|
12
12
|
|
|
@@ -34,5 +34,6 @@ export default _NavigationEvent({
|
|
|
34
34
|
File,
|
|
35
35
|
Blob,
|
|
36
36
|
ReadableStream: Readable,
|
|
37
|
-
FormDataEncoder
|
|
37
|
+
FormDataEncoder,
|
|
38
|
+
fetch
|
|
38
39
|
});
|
|
@@ -34,33 +34,11 @@ import Router from './Router.js';
|
|
|
34
34
|
export default async function(Ui, flags = {}) {
|
|
35
35
|
|
|
36
36
|
const layout = await config.layout.read(flags, {});
|
|
37
|
-
const setup = {
|
|
37
|
+
const v_setup = {}, setup = {
|
|
38
38
|
layout,
|
|
39
39
|
server: await config.server.read(flags, layout),
|
|
40
40
|
variables: await config.variables.read(flags, layout),
|
|
41
41
|
};
|
|
42
|
-
|
|
43
|
-
if (!setup.server.shared && setup.variables.autoload !== false) {
|
|
44
|
-
Object.keys(setup.variables.entries).forEach(key => {
|
|
45
|
-
if (!(key in process.env) || setup.variables.autoload === 2) {
|
|
46
|
-
process.env[key] = setup.variables.entries[key];
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const getSessionInitializer = (sesskey, hostname = null) => {
|
|
52
|
-
const secret = sesskey || (hostname ? uuidv5(hostname, uuidv4()) : uuidv4());
|
|
53
|
-
return Sessions({
|
|
54
|
-
cookieName: '_session', // cookie name dictates the key name added to the request object
|
|
55
|
-
secret, // should be a large unguessable string
|
|
56
|
-
duration: 24 * 60 * 60 * 1000, // how long the session will stay valid in ms
|
|
57
|
-
activeDuration: 1000 * 60 * 5 // if expiresIn < activeDuration, the session will be extended by activeDuration milliseconds
|
|
58
|
-
});
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
const instanceSetup = setup;
|
|
62
|
-
|
|
63
|
-
const v_setup = {};
|
|
64
42
|
if (setup.server.shared) {
|
|
65
43
|
await Promise.all(((await config.vhosts.read(flags, setup.layout)).entries || []).map(vh => new Promise(async resolve => {
|
|
66
44
|
const vlayout = await config.layout.read(flags, {ROOT: Path.join(setup.layout.ROOT, vh.path)});
|
|
@@ -70,86 +48,57 @@ export default async function(Ui, flags = {}) {
|
|
|
70
48
|
variables: await config.variables.read(flags, vlayout),
|
|
71
49
|
vh,
|
|
72
50
|
};
|
|
73
|
-
v_setup[vh.host].sessionInit = getSessionInitializer(v_setup[vh.host].variables.entries.sesskey, vh.host),
|
|
74
51
|
resolve();
|
|
75
52
|
})));
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
|
|
53
|
+
} else if (setup.variables.autoload !== false) {
|
|
54
|
+
Object.keys(setup.variables.entries).forEach(key => {
|
|
55
|
+
if (!(key in process.env) || setup.variables.autoload === 2) {
|
|
56
|
+
process.env[key] = setup.variables.entries[key];
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
79
60
|
|
|
80
61
|
// ---------------------------------------------
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
if (
|
|
85
|
-
|
|
62
|
+
|
|
63
|
+
function handleRequest(protocol, request, response) {
|
|
64
|
+
let _setup = setup, hostname = (request.headers.host || '').split(':')[0];
|
|
65
|
+
if (setup.server.shared) {
|
|
66
|
+
if (!(_setup = v_setup[hostname])
|
|
67
|
+
&& ((hostname.startsWith('www.') && (_setup = v_setup[hostname.substr(4)]) && _setup.server.force_www)
|
|
68
|
+
&& (!hostname.startsWith('www.') && (_setup = v_setup['www.' + hostname]) && _setup.server.force_www))) {
|
|
69
|
+
response.statusCode = 500;
|
|
70
|
+
response.end('Unrecognized host');
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
86
73
|
}
|
|
87
|
-
if (
|
|
88
|
-
|
|
89
|
-
|
|
74
|
+
if (protocol === 'http' && _setup.server.https.force && !flags['http-only'] && /** main server */setup.server.https.port) {
|
|
75
|
+
response.statusCode = 302;
|
|
76
|
+
response.setHeader('Location', 'https://' + request.headers.host + request.url);
|
|
77
|
+
response.end();
|
|
78
|
+
return;
|
|
90
79
|
}
|
|
91
|
-
response.statusCode = 500;
|
|
92
|
-
response.end('Unrecognized host');
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
const goOrForceWww = (setup, request, response, protocol) => {
|
|
96
|
-
var hostname = request.headers.host || '';
|
|
97
80
|
if (hostname.startsWith('www.') && setup.server.force_www === 'remove') {
|
|
98
81
|
response.statusCode = 302;
|
|
99
82
|
response.setHeader('Location', protocol + '://' + hostname.substr(4) + request.url);
|
|
100
83
|
response.end();
|
|
101
|
-
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (!hostname.startsWith('www.') && setup.server.force_www === 'add') {
|
|
102
87
|
response.statusCode = 302;
|
|
103
88
|
response.setHeader('Location', protocol + '://www.' + hostname + request.url);
|
|
104
89
|
response.end();
|
|
105
|
-
|
|
106
|
-
setup.sessionInit(request, response, () => {});
|
|
107
|
-
run(instanceSetup, setup, request, response, Ui, flags, protocol);
|
|
90
|
+
return;
|
|
108
91
|
}
|
|
92
|
+
run(_setup, request, response, Ui, flags, protocol);
|
|
109
93
|
};
|
|
110
94
|
|
|
111
95
|
// ---------------------------------------------
|
|
112
|
-
|
|
113
|
-
if (!flags['https-only']) {
|
|
114
|
-
|
|
115
|
-
Http.createServer((request, response) => {
|
|
116
|
-
if (setup.server.shared) {
|
|
117
|
-
var _setup;
|
|
118
|
-
if (_setup = getVSetup(request, response)) {
|
|
119
|
-
goOrForceHttps(_setup, request, response);
|
|
120
|
-
}
|
|
121
|
-
} else {
|
|
122
|
-
goOrForceHttps(setup, request, response);
|
|
123
|
-
}
|
|
124
|
-
}).listen(process.env.PORT || setup.server.port);
|
|
125
|
-
|
|
126
|
-
const goOrForceHttps = ($setup, $request, $response) => {
|
|
127
|
-
if ($setup.server.https.force && !flags['http-only'] && /** main server */setup.server.https.port) {
|
|
128
|
-
$response.statusCode = 302;
|
|
129
|
-
$response.setHeader('Location', 'https://' + $request.headers.host + $request.url);
|
|
130
|
-
$response.end();
|
|
131
|
-
} else {
|
|
132
|
-
goOrForceWww($setup, $request, $response, 'http');
|
|
133
|
-
}
|
|
134
|
-
};
|
|
135
96
|
|
|
97
|
+
if (!flags['https-only']) {
|
|
98
|
+
Http.createServer((request, response) => handleRequest('http', request, response)).listen(process.env.PORT || setup.server.port);
|
|
136
99
|
}
|
|
137
|
-
|
|
138
|
-
// ---------------------------------------------
|
|
139
|
-
|
|
140
100
|
if (!flags['http-only'] && setup.server.https.port) {
|
|
141
|
-
|
|
142
|
-
const httpsServer = Https.createServer((request, response) => {
|
|
143
|
-
if (setup.server.shared) {
|
|
144
|
-
var _setup;
|
|
145
|
-
if (_setup = getVSetup(request, response)) {
|
|
146
|
-
goOrForceWww(_setup, request, response, 'https');
|
|
147
|
-
}
|
|
148
|
-
} else {
|
|
149
|
-
goOrForceWww(setup, request, response, 'https');
|
|
150
|
-
}
|
|
151
|
-
});
|
|
152
|
-
|
|
101
|
+
const httpsServer = Https.createServer((request, response) => handleRequest('https', request, response));
|
|
153
102
|
if (setup.server.shared) {
|
|
154
103
|
_each(v_setup, (host, _setup) => {
|
|
155
104
|
if (Fs.existsSync(_setup.server.https.keyfile)) {
|
|
@@ -185,7 +134,6 @@ export default async function(Ui, flags = {}) {
|
|
|
185
134
|
});
|
|
186
135
|
}
|
|
187
136
|
}
|
|
188
|
-
|
|
189
137
|
httpsServer.listen(process.env.PORT2 || setup.server.https.port);
|
|
190
138
|
}
|
|
191
139
|
};
|
|
@@ -193,7 +141,6 @@ export default async function(Ui, flags = {}) {
|
|
|
193
141
|
/**
|
|
194
142
|
* The Server.
|
|
195
143
|
*
|
|
196
|
-
* @param Object instanceSetup
|
|
197
144
|
* @param Object hostSetup
|
|
198
145
|
* @param Request request
|
|
199
146
|
* @param Response response
|
|
@@ -203,13 +150,12 @@ export default async function(Ui, flags = {}) {
|
|
|
203
150
|
*
|
|
204
151
|
* @return void
|
|
205
152
|
*/
|
|
206
|
-
export async function run(
|
|
153
|
+
export async function run(hostSetup, request, response, Ui, flags = {}, protocol = 'http') {
|
|
207
154
|
|
|
208
155
|
// --------
|
|
209
156
|
// Request parsing
|
|
210
157
|
// --------
|
|
211
158
|
|
|
212
|
-
const fullUrl = protocol + '://' + request.headers.host + request.url;
|
|
213
159
|
const requestInit = { method: request.method, headers: request.headers };
|
|
214
160
|
if (request.method !== 'GET' && request.method !== 'HEAD') {
|
|
215
161
|
requestInit.body = await new Promise((resolve, reject) => {
|
|
@@ -257,10 +203,38 @@ export async function run(instanceSetup, hostSetup, request, response, Ui, flags
|
|
|
257
203
|
});
|
|
258
204
|
});
|
|
259
205
|
}
|
|
260
|
-
|
|
261
|
-
//
|
|
206
|
+
|
|
207
|
+
// --------
|
|
208
|
+
// NavigationEvent instance
|
|
209
|
+
// --------
|
|
210
|
+
|
|
211
|
+
const fullUrl = protocol + '://' + request.headers.host + request.url;
|
|
262
212
|
const _request = new NavigationEvent.Request(fullUrl, requestInit);
|
|
263
|
-
const
|
|
213
|
+
const _sessionFactory = function(id, params = {}, callback = null) {
|
|
214
|
+
let factory, secret = hostSetup.variables.entries.SESSION_KEY;
|
|
215
|
+
Sessions({
|
|
216
|
+
duration: 0, // how long the session will stay valid in ms
|
|
217
|
+
activeDuration: 0, // if expiresIn < activeDuration, the session will be extended by activeDuration milliseconds
|
|
218
|
+
...params,
|
|
219
|
+
cookieName: id, // cookie name dictates the key name added to the request object
|
|
220
|
+
secret, // should be a large unguessable string
|
|
221
|
+
})(request, response, e => {
|
|
222
|
+
factory = Object.getOwnPropertyDescriptor(request, id);
|
|
223
|
+
if (callback) {
|
|
224
|
+
callback(e, factory);
|
|
225
|
+
} else if (e) {
|
|
226
|
+
// TODO
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
// Where theres no error, factory is available Sync
|
|
230
|
+
return !callback ? factory : undefined;
|
|
231
|
+
};
|
|
232
|
+
const serverNavigationEvent = new NavigationEvent(
|
|
233
|
+
_request,
|
|
234
|
+
_sessionFactory('_session', {duration: 60 * 60 * 24 * 30}).get(),
|
|
235
|
+
_sessionFactory
|
|
236
|
+
);
|
|
237
|
+
|
|
264
238
|
const $context = {
|
|
265
239
|
rdr: null,
|
|
266
240
|
layout: hostSetup.layout,
|
|
@@ -485,7 +459,7 @@ export async function run(instanceSetup, hostSetup, request, response, Ui, flags
|
|
|
485
459
|
if (name === 'set-cookie') {
|
|
486
460
|
setCookies(value);
|
|
487
461
|
} else {
|
|
488
|
-
if (name.toLowerCase() === 'location' &&
|
|
462
|
+
if (name.toLowerCase() === 'location' && $context.response.status === 200) {
|
|
489
463
|
response.statusCode = 302 /* Temporary */;
|
|
490
464
|
}
|
|
491
465
|
response.setHeader(name, value);
|
|
@@ -496,6 +470,7 @@ export async function run(instanceSetup, hostSetup, request, response, Ui, flags
|
|
|
496
470
|
// Send
|
|
497
471
|
// -------------------
|
|
498
472
|
if ($context.response.headers.redirect) {
|
|
473
|
+
response.statusCode = $context.response.status;
|
|
499
474
|
response.end();
|
|
500
475
|
} else if ($context.response.original !== undefined && $context.response.original !== null) {
|
|
501
476
|
response.statusCode = $context.response.status;
|