@webqit/webflo 0.8.48-0 → 0.8.52
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/docker/Dockerfile +26 -0
- package/docker/README.md +77 -0
- package/package.json +9 -8
- package/src/{cmd/client.js → build/client/index.js} +24 -11
- package/src/build/index.js +5 -0
- package/src/index.js +47 -45
- package/src/runtime/Router.js +130 -0
- package/src/runtime/_FormData.js +60 -0
- package/src/runtime/_Headers.js +88 -0
- package/src/runtime/_MessageStream.js +191 -0
- package/src/runtime/_NavigationEvent.js +89 -0
- package/src/runtime/_Request.js +61 -0
- package/src/runtime/_RequestHeaders.js +72 -0
- package/src/runtime/_Response.js +56 -0
- package/src/runtime/_ResponseHeaders.js +81 -0
- package/src/runtime/_URL.js +111 -0
- package/src/{modules → runtime}/client/Cache.js +0 -0
- package/src/{modules → runtime}/client/Http.js +21 -6
- package/src/runtime/client/NavigationEvent.js +20 -0
- package/src/runtime/client/Router.js +47 -0
- package/src/{modules/client/Client.js → runtime/client/Runtime.js} +32 -25
- package/src/{modules → runtime}/client/StdRequest.js +4 -8
- package/src/{modules → runtime}/client/Storage.js +6 -6
- package/src/{modules → runtime}/client/Url.js +0 -0
- package/src/{modules → runtime}/client/Worker.js +11 -49
- package/src/{modules → runtime}/client/WorkerClient.js +5 -38
- package/src/runtime/client/WorkerComm.js +183 -0
- package/src/runtime/client/effects/sounds.js +64 -0
- package/src/runtime/index.js +5 -0
- package/src/runtime/server/NavigationEvent.js +38 -0
- package/src/runtime/server/Router.js +158 -0
- package/src/{modules/server/Server.js → runtime/server/Runtime.js} +177 -85
- package/src/{cmd/server.js → runtime/server/index.js} +9 -9
- package/src/{modules/server/start.mjs → runtime/server/index.mjs} +2 -2
- package/src/{modules → runtime}/util.js +7 -7
- package/src/{modules → services}/certbot/http-auth-hook.js +0 -0
- package/src/{modules → services}/certbot/http-cleanup-hook.js +0 -0
- package/src/{cmd/certbot.js → services/certbot/index.js} +2 -3
- package/src/services/index.js +6 -0
- package/src/{cmd/origins.js → services/origins/index.js} +5 -5
- package/src/cmd/index.js +0 -8
- package/src/modules/NavigationEvent.js +0 -53
- package/src/modules/Response.js +0 -98
- package/src/modules/XURL.js +0 -125
- package/src/modules/client/Router.js +0 -123
- package/src/modules/server/Router.js +0 -231
- package/src/modules/server/StdIncomingMessage.js +0 -73
|
@@ -6,6 +6,7 @@ import Fs from 'fs';
|
|
|
6
6
|
import Path from 'path';
|
|
7
7
|
import Http from 'http';
|
|
8
8
|
import Https from 'https';
|
|
9
|
+
import Formidable from 'formidable';
|
|
9
10
|
import QueryString from 'querystring';
|
|
10
11
|
import Sessions from 'client-sessions';
|
|
11
12
|
import _each from '@webqit/util/obj/each.js';
|
|
@@ -13,12 +14,13 @@ import _arrFrom from '@webqit/util/arr/from.js';
|
|
|
13
14
|
import _promise from '@webqit/util/js/promise.js';
|
|
14
15
|
import _isObject from '@webqit/util/js/isObject.js';
|
|
15
16
|
import _isArray from '@webqit/util/js/isArray.js';
|
|
16
|
-
import
|
|
17
|
+
import { _isString, _isPlainObject, _isPlainArray } from '@webqit/util/js/index.js';
|
|
18
|
+
import _delay from '@webqit/util/js/delay.js';
|
|
19
|
+
import { slice as _streamSlice } from 'stream-slice';
|
|
17
20
|
import { v4 as uuidv4, v5 as uuidv5 } from 'uuid';
|
|
18
21
|
import * as config from '../../config/index.js';
|
|
19
|
-
import * as
|
|
20
|
-
import NavigationEvent from '
|
|
21
|
-
import StdIncomingMessage from './StdIncomingMessage.js';
|
|
22
|
+
import * as services from '../../services/index.js';
|
|
23
|
+
import NavigationEvent from './NavigationEvent.js';
|
|
22
24
|
import Router from './Router.js';
|
|
23
25
|
|
|
24
26
|
/**
|
|
@@ -108,7 +110,7 @@ export default async function(Ui, flags = {}) {
|
|
|
108
110
|
|
|
109
111
|
if (!flags['https-only']) {
|
|
110
112
|
|
|
111
|
-
Http.createServer(
|
|
113
|
+
Http.createServer((request, response) => {
|
|
112
114
|
if (setup.server.shared) {
|
|
113
115
|
var _setup;
|
|
114
116
|
if (_setup = getVSetup(request, response)) {
|
|
@@ -135,7 +137,7 @@ export default async function(Ui, flags = {}) {
|
|
|
135
137
|
|
|
136
138
|
if (!flags['http-only'] && setup.server.https.port) {
|
|
137
139
|
|
|
138
|
-
const httpsServer = Https.createServer(
|
|
140
|
+
const httpsServer = Https.createServer((request, response) => {
|
|
139
141
|
if (setup.server.shared) {
|
|
140
142
|
var _setup;
|
|
141
143
|
if (_setup = getVSetup(request, response)) {
|
|
@@ -205,7 +207,58 @@ export async function run(instanceSetup, hostSetup, request, response, Ui, flags
|
|
|
205
207
|
// Request parsing
|
|
206
208
|
// --------
|
|
207
209
|
|
|
208
|
-
const
|
|
210
|
+
const fullUrl = protocol + '://' + request.headers.host + request.url;
|
|
211
|
+
const requestInit = { method: request.method, headers: request.headers };
|
|
212
|
+
if (request.method !== 'GET' && request.method !== 'HEAD') {
|
|
213
|
+
requestInit.body = await new Promise((resolve, reject) => {
|
|
214
|
+
var formidable = new Formidable.IncomingForm({ multiples: true, allowEmptyFiles: false, keepExtensions: true });
|
|
215
|
+
formidable.parse(request, (error, fields, files) => {
|
|
216
|
+
if (error) {
|
|
217
|
+
reject(error);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
if (request.headers['content-type'] === 'application/json') {
|
|
221
|
+
return resolve(fields);
|
|
222
|
+
}
|
|
223
|
+
const formData = new NavigationEvent.globals.FormData;
|
|
224
|
+
Object.keys(fields).forEach(name => {
|
|
225
|
+
if (Array.isArray(fields[name])) {
|
|
226
|
+
const values = Array.isArray(fields[name][0])
|
|
227
|
+
? fields[name][0]/* bugly a nested array when there are actually more than entry */
|
|
228
|
+
: fields[name];
|
|
229
|
+
values.forEach(value => {
|
|
230
|
+
formData.append(!name.endsWith(']') ? name + '[]' : name, value);
|
|
231
|
+
});
|
|
232
|
+
} else {
|
|
233
|
+
formData.append(name, fields[name]);
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
Object.keys(files).forEach(name => {
|
|
237
|
+
const fileCompat = file => {
|
|
238
|
+
// IMPORTANT
|
|
239
|
+
// Path up the "formidable" file in a way that "formdata-node"
|
|
240
|
+
// to can translate it into its own file instance
|
|
241
|
+
file[Symbol.toStringTag] = 'File';
|
|
242
|
+
file.stream = () => Fs.createReadStream(file.path);
|
|
243
|
+
// Done pathcing
|
|
244
|
+
return file;
|
|
245
|
+
}
|
|
246
|
+
if (Array.isArray(files[name])) {
|
|
247
|
+
files[name].forEach(value => {
|
|
248
|
+
formData.append(name, fileCompat(value));
|
|
249
|
+
});
|
|
250
|
+
} else {
|
|
251
|
+
formData.append(name, fileCompat(files[name]));
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
resolve(formData);
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
// The Formidabble thing in NavigationEvent class would still need
|
|
259
|
+
// a reference to the Nodejs request
|
|
260
|
+
const _request = new NavigationEvent.Request(fullUrl, requestInit);
|
|
261
|
+
const serverNavigationEvent = new NavigationEvent(_request, request._session);
|
|
209
262
|
const $context = {
|
|
210
263
|
rdr: null,
|
|
211
264
|
layout: hostSetup.layout,
|
|
@@ -226,10 +279,10 @@ export async function run(instanceSetup, hostSetup, request, response, Ui, flags
|
|
|
226
279
|
isAppend = headerName.startsWith('+') ? (headerName = headerName.substr(1), true) : false,
|
|
227
280
|
isPrepend = headerName.endsWith('+') ? (headerName = headerName.substr(0, headerName.length - 1), true) : false;
|
|
228
281
|
if (isAppend || isPrepend) {
|
|
229
|
-
headerValue = [ serverNavigationEvent.request.headers
|
|
282
|
+
headerValue = [ serverNavigationEvent.request.headers.get(headerName) || '' , headerValue].filter(v => v);
|
|
230
283
|
headerValue = isPrepend ? headerValue.reverse().join(',') : headerValue.join(',');
|
|
231
284
|
}
|
|
232
|
-
return { name:headerName, value:headerValue };
|
|
285
|
+
return { name: headerName, value: headerValue };
|
|
233
286
|
}
|
|
234
287
|
|
|
235
288
|
// -------------------
|
|
@@ -251,7 +304,7 @@ export async function run(instanceSetup, hostSetup, request, response, Ui, flags
|
|
|
251
304
|
|
|
252
305
|
$context.headers.filter(header => header.type === 'request').forEach(header => {
|
|
253
306
|
const { name, value } = resolveSetHeader(header);
|
|
254
|
-
serverNavigationEvent.request.headers
|
|
307
|
+
serverNavigationEvent.request.headers.set(name, value);
|
|
255
308
|
});
|
|
256
309
|
|
|
257
310
|
// -------------------
|
|
@@ -262,20 +315,15 @@ export async function run(instanceSetup, hostSetup, request, response, Ui, flags
|
|
|
262
315
|
|
|
263
316
|
try {
|
|
264
317
|
|
|
265
|
-
// -------------------
|
|
266
|
-
// Handle autodeploy events
|
|
267
|
-
// -------------------
|
|
268
|
-
|
|
269
318
|
// The app router
|
|
270
319
|
const router = new Router(serverNavigationEvent.url.pathname, hostSetup.layout, $context);
|
|
271
320
|
|
|
272
321
|
// --------
|
|
273
322
|
// ROUTE FOR DEPLOY
|
|
274
323
|
// --------
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
var exitCode = await router.route('deploy', serverNavigationEvent, payload, function(_payload) {
|
|
324
|
+
if (services.origins) {
|
|
325
|
+
await services.origins.hook(Ui, serverNavigationEvent, async (payload, defaultPeployFn) => {
|
|
326
|
+
var exitCode = await router.route('deploy', serverNavigationEvent, payload, function(event, _payload) {
|
|
279
327
|
return defaultPeployFn(_payload);
|
|
280
328
|
});
|
|
281
329
|
// -----------
|
|
@@ -289,48 +337,46 @@ export async function run(instanceSetup, hostSetup, request, response, Ui, flags
|
|
|
289
337
|
// --------
|
|
290
338
|
// ROUTE FOR DATA
|
|
291
339
|
// --------
|
|
292
|
-
|
|
293
340
|
const httpMethodName = serverNavigationEvent.request.method.toLowerCase();
|
|
294
|
-
$context.response = await router.route([httpMethodName === 'delete' ? 'del' : httpMethodName, 'default'], serverNavigationEvent, null, async function() {
|
|
295
|
-
var file = await router.fetch(
|
|
341
|
+
$context.response = await router.route([httpMethodName === 'delete' ? 'del' : httpMethodName, 'default'], serverNavigationEvent, null, async function(event) {
|
|
342
|
+
var file = await router.fetch(event);
|
|
296
343
|
// JSON request should ignore static files
|
|
297
|
-
if (file && !
|
|
344
|
+
if (file && !event.request.headers.accept.match(file.headers.contentType)) {
|
|
298
345
|
return;
|
|
299
346
|
}
|
|
300
347
|
// ----------------
|
|
301
348
|
// PRE-RENDERING
|
|
302
349
|
// ----------------
|
|
303
|
-
if (file && file.contentType === 'text/html' && (file.body + '').startsWith(`<!-- PRE-RENDERED -->`)) {
|
|
304
|
-
|
|
305
|
-
if (!prerenderMatch) {
|
|
350
|
+
if (file && file.headers.contentType === 'text/html' && (file.body + '').startsWith(`<!-- PRE-RENDERED -->`)) {
|
|
351
|
+
if (config.prerendering && !(await !config.prerendering.match(serverNavigationEvent.url.pathname, flags, hostSetup.layout))) {
|
|
306
352
|
router.deletePreRendered(file.filename);
|
|
307
353
|
return;
|
|
308
354
|
}
|
|
309
355
|
}
|
|
310
356
|
return file;
|
|
311
357
|
});
|
|
358
|
+
|
|
359
|
+
// --------
|
|
312
360
|
if (!($context.response instanceof serverNavigationEvent.Response)) {
|
|
313
|
-
$context.response = new serverNavigationEvent.Response(
|
|
361
|
+
$context.response = new serverNavigationEvent.Response($context.response);
|
|
314
362
|
}
|
|
363
|
+
// --------
|
|
315
364
|
|
|
316
365
|
// --------
|
|
317
366
|
// API CALL OR PAGE REQUEST?
|
|
318
367
|
// --------
|
|
319
368
|
|
|
320
|
-
if (!$context.response.
|
|
321
|
-
|
|
322
|
-
&& !$context.response.static
|
|
323
|
-
&& _isTypeObject($context.response.body)) {
|
|
324
|
-
if (serverNavigationEvent.request.accepts.type('text/html')) {
|
|
369
|
+
if (!$context.response.meta.static && (_isPlainObject($context.response.original) || _isPlainArray($context.response.original))) {
|
|
370
|
+
if (serverNavigationEvent.request.headers.accept.match('text/html')) {
|
|
325
371
|
// --------
|
|
326
372
|
// Render
|
|
327
373
|
// --------
|
|
328
|
-
|
|
374
|
+
var rendering = await router.route('render', serverNavigationEvent, $context.response.original, async function(event, data) {
|
|
329
375
|
// --------
|
|
330
376
|
if (!hostSetup.layout.renderFileCache) {
|
|
331
377
|
hostSetup.layout.renderFileCache = {};
|
|
332
378
|
}
|
|
333
|
-
var renderFile, pathnameSplit =
|
|
379
|
+
var renderFile, pathnameSplit = event.url.pathname.split('/');
|
|
334
380
|
while ((renderFile = Path.join(hostSetup.layout.ROOT, hostSetup.layout.PUBLIC_DIR, './' + pathnameSplit.join('/'), 'index.html'))
|
|
335
381
|
&& pathnameSplit.length && (hostSetup.layout.renderFileCache[renderFile] === false || !(hostSetup.layout.renderFileCache[renderFile] && Fs.existsSync(renderFile)))) {
|
|
336
382
|
hostSetup.layout.renderFileCache[renderFile] === false;
|
|
@@ -339,7 +385,7 @@ export async function run(instanceSetup, hostSetup, request, response, Ui, flags
|
|
|
339
385
|
hostSetup.layout.renderFileCache[renderFile] === true;
|
|
340
386
|
const instanceParams = QueryString.stringify({
|
|
341
387
|
SOURCE: renderFile,
|
|
342
|
-
URL:
|
|
388
|
+
URL: event.url.href,
|
|
343
389
|
ROOT: hostSetup.layout.ROOT,
|
|
344
390
|
});
|
|
345
391
|
const { window } = await import('@webqit/pseudo-browser/instance.js?' + instanceParams);
|
|
@@ -351,8 +397,8 @@ export async function run(instanceSetup, hostSetup, request, response, Ui, flags
|
|
|
351
397
|
env: 'server',
|
|
352
398
|
}, {update: true});
|
|
353
399
|
}
|
|
354
|
-
window.document.setState({page: data, url:
|
|
355
|
-
window.document.body.setAttribute('template', 'page/' +
|
|
400
|
+
window.document.setState({page: data, url: event.url}, {update: 'merge'});
|
|
401
|
+
window.document.body.setAttribute('template', 'page/' + event.url.pathname.split('/').filter(a => a).map(a => a + '+-').join('/'));
|
|
356
402
|
return new Promise(res => {
|
|
357
403
|
window.document.addEventListener('templatesreadystatechange', () => res(window));
|
|
358
404
|
if (window.document.templatesReadyState === 'complete') {
|
|
@@ -364,26 +410,14 @@ export async function run(instanceSetup, hostSetup, request, response, Ui, flags
|
|
|
364
410
|
// Serialize rendering?
|
|
365
411
|
// --------
|
|
366
412
|
if (_isObject(rendering) && rendering.document) {
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
resolve(new serverNavigationEvent.Response({
|
|
370
|
-
...$context.response,
|
|
371
|
-
contentType: 'text/html',
|
|
372
|
-
body: rendering.print(),
|
|
373
|
-
}));
|
|
374
|
-
}, 1000);
|
|
375
|
-
});
|
|
376
|
-
} else {
|
|
377
|
-
if (!(rendering instanceof serverNavigationEvent.Response)) {
|
|
378
|
-
throw new Error('render() must return a window object or a response Object corresponding to event.Response')
|
|
379
|
-
}
|
|
380
|
-
$context.response = rendering;
|
|
413
|
+
await _delay(1000);
|
|
414
|
+
rendering = rendering.print();
|
|
381
415
|
}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
416
|
+
if (!_isString(rendering)) throw new Error('render() must return a window object or a string response.')
|
|
417
|
+
$context.response = new serverNavigationEvent.Response(rendering, {
|
|
418
|
+
status: $context.response.status,
|
|
419
|
+
headers: { ...$context.response.headers.json(), contentType: 'text/html' },
|
|
420
|
+
});
|
|
387
421
|
}
|
|
388
422
|
}
|
|
389
423
|
|
|
@@ -392,17 +426,32 @@ export async function run(instanceSetup, hostSetup, request, response, Ui, flags
|
|
|
392
426
|
// --------
|
|
393
427
|
|
|
394
428
|
if (!response.headersSent) {
|
|
429
|
+
|
|
430
|
+
// -------------------
|
|
431
|
+
// Streaming headers
|
|
432
|
+
// -------------------
|
|
433
|
+
// Chrome needs this for audio elements to play
|
|
434
|
+
response.setHeader('Accept-Ranges', 'bytes');
|
|
435
|
+
/*
|
|
436
|
+
if ($context.response.headers.contentLength && !$context.response.headers.contentRange) {
|
|
437
|
+
$context.response.headers.contentRange = `bytes 0-${$context.response.headers.contentLength}/${$context.response.headers.contentLength}`;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
*/
|
|
395
441
|
// -------------------
|
|
396
442
|
// Automatic response headers
|
|
397
443
|
// -------------------
|
|
444
|
+
//response.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
|
|
445
|
+
//response.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
|
|
398
446
|
$context.headers.filter(header => header.type === 'response').forEach(header => {
|
|
399
447
|
const { name, value } = resolveSetHeader(header);
|
|
400
448
|
response.setHeader(name, value);
|
|
401
449
|
});
|
|
450
|
+
|
|
402
451
|
// -------------------
|
|
403
|
-
// Route response
|
|
452
|
+
// Route response cookies
|
|
404
453
|
// -------------------
|
|
405
|
-
const cookieAtts = ['Expires', 'Max-Age', 'Domain', 'Path', 'Secure', 'HttpOnly', 'SameSite' ];
|
|
454
|
+
const cookieAtts = [ 'Expires', 'Max-Age', 'Domain', 'Path', 'Secure', 'HttpOnly', 'SameSite' ];
|
|
406
455
|
const setCookies = (cookies, nameContext = null) => {
|
|
407
456
|
_each(cookies, (name, cookie) => {
|
|
408
457
|
name = nameContext ? `${nameContext}[${name}]` : name;
|
|
@@ -416,9 +465,7 @@ export async function run(instanceSetup, hostSetup, request, response, Ui, flags
|
|
|
416
465
|
var __attr = cookieAtts.reduce((match, _attr) => match || (
|
|
417
466
|
[_attr.toLowerCase(), _attr.replace('-', '').toLowerCase()].includes(attr.toLowerCase()) ? _attr : null
|
|
418
467
|
), null);
|
|
419
|
-
if (!__attr) {
|
|
420
|
-
throw new Error(`Invalid cookie attribute: ${attr}`);
|
|
421
|
-
}
|
|
468
|
+
if (!__attr) throw new Error(`Invalid cookie attribute: ${attr}`);
|
|
422
469
|
expr += cookie[attr] === true ? `; ${__attr}` : `; ${__attr}=${cookie[attr]}`;
|
|
423
470
|
});
|
|
424
471
|
response.setHeader('Set-Cookie', expr);
|
|
@@ -427,51 +474,96 @@ export async function run(instanceSetup, hostSetup, request, response, Ui, flags
|
|
|
427
474
|
}
|
|
428
475
|
});
|
|
429
476
|
};
|
|
430
|
-
|
|
431
|
-
|
|
477
|
+
|
|
478
|
+
// -------------------
|
|
479
|
+
// Route response headers
|
|
480
|
+
// -------------------
|
|
481
|
+
_each($context.response.headers.json(), (name, value) => {
|
|
482
|
+
if ([ 'autoindex', 'filename', 'static' ].includes(name)) return;
|
|
483
|
+
if (name === 'set-cookie') {
|
|
432
484
|
setCookies(value);
|
|
433
485
|
} else {
|
|
434
486
|
if (name.toLowerCase() === 'location' && !$context.response.status) {
|
|
435
|
-
|
|
487
|
+
response.statusCode = 302 /* Temporary */;
|
|
436
488
|
}
|
|
437
489
|
response.setHeader(name, value);
|
|
438
490
|
}
|
|
439
491
|
});
|
|
440
|
-
|
|
441
|
-
// Status code
|
|
442
|
-
// -------------------
|
|
443
|
-
if ($context.response.status) {
|
|
444
|
-
response.statusCode = $context.response.status;
|
|
445
|
-
}
|
|
492
|
+
|
|
446
493
|
// -------------------
|
|
447
494
|
// Send
|
|
448
495
|
// -------------------
|
|
449
|
-
if ($context.response.
|
|
450
|
-
response.end(
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
496
|
+
if ($context.response.headers.redirect) {
|
|
497
|
+
response.end();
|
|
498
|
+
} else if ($context.response.original !== undefined && $context.response.original !== null) {
|
|
499
|
+
response.statusCode = $context.response.status;
|
|
500
|
+
response.statusMessage = $context.response.statusText;
|
|
501
|
+
|
|
502
|
+
// ----------------
|
|
503
|
+
// SENDING RESPONSE
|
|
504
|
+
// ----------------
|
|
505
|
+
var body = $context.response.body;
|
|
506
|
+
if ((body instanceof serverNavigationEvent.globals.ReadableStream)
|
|
507
|
+
|| (ArrayBuffer.isView(body) && (body = serverNavigationEvent.globals.ReadableStream.from(body)))) {
|
|
508
|
+
|
|
509
|
+
// We support streaming
|
|
510
|
+
const rangeRequest = serverNavigationEvent.request.headers.range;
|
|
511
|
+
if (rangeRequest) {
|
|
512
|
+
// ...in partials
|
|
513
|
+
const totalLength = $context.response.headers.contentLength;
|
|
514
|
+
// Validate offsets
|
|
515
|
+
if (rangeRequest[0] < 0 || (totalLength && rangeRequest[0] > totalLength)
|
|
516
|
+
|| (rangeRequest[1] > -1 && (rangeRequest[1] <= rangeRequest[0] || (totalLength && rangeRequest[1] >= totalLength)))) {
|
|
517
|
+
response.statusCode = 416;
|
|
518
|
+
response.setHeader('Content-Range', `bytes */${totalLength || '*'}`);
|
|
519
|
+
response.setHeader('Content-Length', 0);
|
|
520
|
+
response.end();
|
|
521
|
+
} else {
|
|
522
|
+
if (totalLength) {
|
|
523
|
+
rangeRequest.clamp(totalLength);
|
|
524
|
+
}
|
|
525
|
+
// Set new headers
|
|
526
|
+
response.writeHead(206, {
|
|
527
|
+
'Content-Range': `bytes ${rangeRequest[0]}-${rangeRequest[1]}/${totalLength || '*'}`,
|
|
528
|
+
'Content-Length': rangeRequest[1] - rangeRequest[0] + 1,
|
|
529
|
+
});
|
|
530
|
+
body
|
|
531
|
+
.pipe(_streamSlice(rangeRequest[0], rangeRequest[1]))
|
|
532
|
+
.pipe(response);
|
|
533
|
+
}
|
|
534
|
+
} else {
|
|
535
|
+
// ...as a whole
|
|
536
|
+
body.pipe(response);
|
|
537
|
+
}
|
|
538
|
+
} else {
|
|
539
|
+
// The default
|
|
540
|
+
if ($context.response.headers.contentType === 'application/json') {
|
|
541
|
+
body += '';
|
|
542
|
+
}
|
|
543
|
+
response.end(body);
|
|
544
|
+
}
|
|
545
|
+
|
|
455
546
|
// ----------------
|
|
456
547
|
// PRE-RENDERING
|
|
457
548
|
// ----------------
|
|
458
|
-
if (!$context.response.filename && $context.response.contentType === 'text/html') {
|
|
549
|
+
if (!$context.response.meta.filename && $context.response.headers.contentType === 'text/html') {
|
|
459
550
|
var prerenderMatch = config.prerendering ? await !config.prerendering.match(serverNavigationEvent.url.pathname, flags, hostSetup.layout) : null;
|
|
460
551
|
if (prerenderMatch) {
|
|
461
|
-
router.putPreRendered(serverNavigationEvent.url.pathname, `<!-- PRE-RENDERED -->\r\n` + $context.response.
|
|
552
|
+
router.putPreRendered(serverNavigationEvent.url.pathname, `<!-- PRE-RENDERED -->\r\n` + $context.response.original);
|
|
462
553
|
}
|
|
463
554
|
}
|
|
464
|
-
} else if (!$context.response.redirect) {
|
|
465
|
-
response.statusCode = 404;
|
|
555
|
+
} else if (!$context.response.headers.redirect) {
|
|
556
|
+
response.statusCode = $context.response.status !== 200 ? $context.response.status : 404;
|
|
466
557
|
response.end(`${serverNavigationEvent.request.url} not found!`);
|
|
467
558
|
}
|
|
559
|
+
|
|
468
560
|
}
|
|
469
561
|
|
|
470
562
|
} catch(e) {
|
|
471
563
|
|
|
472
564
|
$context.fatal = e;
|
|
473
|
-
response.statusCode =
|
|
474
|
-
response.end(`Internal server error
|
|
565
|
+
response.statusCode = 500;
|
|
566
|
+
response.end(`Internal server error: ${e.errorCode}`);
|
|
475
567
|
|
|
476
568
|
}
|
|
477
569
|
|
|
@@ -485,12 +577,12 @@ export async function run(instanceSetup, hostSetup, request, response, Ui, flags
|
|
|
485
577
|
Ui.log(''
|
|
486
578
|
+ '[' + (hostSetup.vh ? Ui.style.keyword(hostSetup.vh.host) + '][' : '') + Ui.style.comment((new Date).toUTCString()) + '] '
|
|
487
579
|
+ Ui.style.keyword(protocol.toUpperCase() + ' ' + serverNavigationEvent.request.method) + ' '
|
|
488
|
-
+ Ui.style.url(serverNavigationEvent.request.url) + ($context.response && $context.response.
|
|
489
|
-
+ (' (' + Ui.style.comment($context.response && $context.response.contentType ? $context.response.contentType : 'unknown') + ') ')
|
|
580
|
+
+ Ui.style.url(serverNavigationEvent.request.url) + ($context.response && ($context.response.meta || {}).autoIndex ? Ui.style.comment((!serverNavigationEvent.request.url.endsWith('/') ? '/' : '') + $context.response.meta.autoIndex) : '') + ' '
|
|
581
|
+
+ (' (' + Ui.style.comment($context.response && ($context.response.headers || {}).contentType ? $context.response.headers.contentType : 'unknown') + ') ')
|
|
490
582
|
+ (
|
|
491
|
-
[404, 500].includes(response.statusCode)
|
|
583
|
+
[ 404, 500 ].includes(response.statusCode)
|
|
492
584
|
? Ui.style.err(response.statusCode + ($context.fatal ? ` [ERROR]: ${$context.fatal.error || $context.fatal.toString()}` : ``))
|
|
493
|
-
: Ui.style.val(response.statusCode) + ((response.statusCode + '').startsWith('3') ? ' - ' + Ui.style.val(response.getHeader('Location')) : '')
|
|
585
|
+
: Ui.style.val(response.statusCode) + ((response.statusCode + '').startsWith('3') ? ' - ' + Ui.style.val(response.getHeader('Location')) : ' (' + Ui.style.keyword(response.getHeader('Content-Range') || response.statusMessage) + ')')
|
|
494
586
|
)
|
|
495
587
|
);
|
|
496
588
|
}
|
|
@@ -7,8 +7,8 @@ import Path from 'path';
|
|
|
7
7
|
import Pm2 from 'pm2';
|
|
8
8
|
import _promise from '@webqit/util/js/promise.js';
|
|
9
9
|
import * as DotJson from '@webqit/backpack/src/dotfiles/DotJson.js';
|
|
10
|
-
import * as server from '
|
|
11
|
-
import
|
|
10
|
+
import * as server from '../../config/server.js'
|
|
11
|
+
import Runtime from './Runtime.js';
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* @description
|
|
@@ -26,7 +26,7 @@ export const desc = {
|
|
|
26
26
|
export async function start(Ui, flags = {}, layout = {}) {
|
|
27
27
|
const config = await server.read(flags, layout);
|
|
28
28
|
const currentDir = Path.dirname(Url.fileURLToPath(import.meta.url));
|
|
29
|
-
const script = Path.resolve(currentDir, '
|
|
29
|
+
const script = Path.resolve(currentDir, './index.mjs');
|
|
30
30
|
// -------------------
|
|
31
31
|
// Splash screen
|
|
32
32
|
// -------------------
|
|
@@ -34,7 +34,7 @@ export async function start(Ui, flags = {}, layout = {}) {
|
|
|
34
34
|
// -------------------
|
|
35
35
|
// Splash screen
|
|
36
36
|
// -------------------
|
|
37
|
-
const WEBFLO = DotJson.read(Path.join(currentDir, '
|
|
37
|
+
const WEBFLO = DotJson.read(Path.join(currentDir, '../../../package.json'));
|
|
38
38
|
Ui.banner(WEBFLO.title, WEBFLO.version);
|
|
39
39
|
Ui.log('');
|
|
40
40
|
Ui.log(Ui.f`${'-------------------------------'}`);
|
|
@@ -51,7 +51,7 @@ export async function start(Ui, flags = {}, layout = {}) {
|
|
|
51
51
|
Ui.log(Ui.f`${'-------------------------------'}`);
|
|
52
52
|
Ui.log('');
|
|
53
53
|
if (processName) {
|
|
54
|
-
Ui.success(`
|
|
54
|
+
Ui.success(`Runtime running in ${Ui.style.keyword('production')}; ${(processAutoRestart ? 'will' : 'wo\'nt')} autorestart on crash!`);
|
|
55
55
|
Ui.info(Ui.f`Process name: ${processName}`);
|
|
56
56
|
Ui.log('');
|
|
57
57
|
}
|
|
@@ -61,7 +61,7 @@ export async function start(Ui, flags = {}, layout = {}) {
|
|
|
61
61
|
if (flags.env !== 'prod' && flags.watch) {
|
|
62
62
|
var nodemon, ecpt;
|
|
63
63
|
try {
|
|
64
|
-
nodemon = await import('nodemon');
|
|
64
|
+
nodemon = await import(Path.resolve('./node_modules/nodemon/lib/nodemon.js'));
|
|
65
65
|
} catch(e) {
|
|
66
66
|
ecpt = e;
|
|
67
67
|
}
|
|
@@ -109,7 +109,7 @@ export async function start(Ui, flags = {}, layout = {}) {
|
|
|
109
109
|
});
|
|
110
110
|
});
|
|
111
111
|
}
|
|
112
|
-
await
|
|
112
|
+
await Runtime.call(null, Ui, flags);
|
|
113
113
|
showRunning();
|
|
114
114
|
};
|
|
115
115
|
|
|
@@ -124,7 +124,7 @@ export function stop(Ui, name, flags = {}) {
|
|
|
124
124
|
if (err) {
|
|
125
125
|
Ui.error(err);
|
|
126
126
|
} else {
|
|
127
|
-
Ui.success(Ui.f`
|
|
127
|
+
Ui.success(Ui.f`Runtime ${flags.kill ? 'killed' : 'stopped'}: ${name}`);
|
|
128
128
|
}
|
|
129
129
|
resolve();
|
|
130
130
|
};
|
|
@@ -148,7 +148,7 @@ export function restart(Ui, name, flags = {}) {
|
|
|
148
148
|
if (err) {
|
|
149
149
|
Ui.error(err);
|
|
150
150
|
} else {
|
|
151
|
-
Ui.success(Ui.f`
|
|
151
|
+
Ui.success(Ui.f`Runtime restarted: ${name}`);
|
|
152
152
|
}
|
|
153
153
|
resolve();
|
|
154
154
|
});
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import parseArgs from '@webqit/backpack/src/cli/parseArgs.js';
|
|
6
6
|
import Ui from '@webqit/backpack/src/cli/Ui.js';
|
|
7
|
-
import
|
|
7
|
+
import Runtime from './Runtime.js';
|
|
8
8
|
|
|
9
9
|
const { flags } = parseArgs(process.argv);
|
|
10
|
-
|
|
10
|
+
Runtime.call(null, Ui, flags);
|
|
@@ -9,7 +9,7 @@ import _isObject from '@webqit/util/js/isObject.js';
|
|
|
9
9
|
import _beforeLast from '@webqit/util/str/beforeLast.js';
|
|
10
10
|
import _afterLast from '@webqit/util/str/afterLast.js';
|
|
11
11
|
import _arrFrom from '@webqit/util/arr/from.js';
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
/**
|
|
14
14
|
* ---------------
|
|
15
15
|
* @wwwFormPathUnserializeCallback
|
|
@@ -77,23 +77,23 @@ export function wwwFormUnserialize(str, target = {}, delim = '&') {
|
|
|
77
77
|
* ---------------
|
|
78
78
|
*/
|
|
79
79
|
|
|
80
|
-
export function wwwFormPathSerializeCallback(wwwFormPath, value, callback) {
|
|
81
|
-
if (_isObject(value) || _isArray(value)) {
|
|
80
|
+
export function wwwFormPathSerializeCallback(wwwFormPath, value, callback, shouldSerialize = null) {
|
|
81
|
+
if ((_isObject(value) || _isArray(value)) && (!shouldSerialize || shouldSerialize(value, wwwFormPath))) {
|
|
82
82
|
var isArr = _isArray(value);
|
|
83
83
|
Object.keys(value).forEach(key => {
|
|
84
|
-
wwwFormPathSerializeCallback(`${wwwFormPath}[${
|
|
84
|
+
wwwFormPathSerializeCallback(`${wwwFormPath}[${key}]`, value[key], callback, shouldSerialize);
|
|
85
85
|
});
|
|
86
86
|
} else {
|
|
87
87
|
callback(wwwFormPath, !value && value !== 0 ? '' : value);
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
export function wwwFormSerialize(form, delim = '&') {
|
|
91
|
+
export function wwwFormSerialize(form, delim = '&', shouldSerialize = null) {
|
|
92
92
|
var q = [];
|
|
93
93
|
Object.keys(form).forEach(key => {
|
|
94
94
|
wwwFormPathSerializeCallback(key, form[key], (_wwwFormPath, _value) => {
|
|
95
95
|
q.push(`${_wwwFormPath}=${encodeURIComponent(_value)}`);
|
|
96
|
-
});
|
|
96
|
+
}, shouldSerialize);
|
|
97
97
|
});
|
|
98
98
|
return q.join(delim);
|
|
99
99
|
}
|
|
@@ -131,4 +131,4 @@ export const path = {
|
|
|
131
131
|
dirname(path) {
|
|
132
132
|
return this.join(path, "..");
|
|
133
133
|
}
|
|
134
|
-
};
|
|
134
|
+
};
|
|
File without changes
|
|
File without changes
|
|
@@ -3,10 +3,9 @@
|
|
|
3
3
|
* imports
|
|
4
4
|
*/
|
|
5
5
|
import Fs from 'fs';
|
|
6
|
-
import Readline from 'readline';
|
|
7
6
|
import { spawn } from 'child_process';
|
|
8
7
|
import _arrFrom from '@webqit/util/arr/from.js';
|
|
9
|
-
import * as _server from '
|
|
8
|
+
import * as _server from '../../config/server.js';
|
|
10
9
|
|
|
11
10
|
/**
|
|
12
11
|
* @description
|
|
@@ -72,4 +71,4 @@ export async function generate(Ui, allDomains, flags = {}, layout = {}) {
|
|
|
72
71
|
}
|
|
73
72
|
process.exit();
|
|
74
73
|
});
|
|
75
|
-
}
|
|
74
|
+
}
|
|
@@ -10,7 +10,7 @@ import _beforeLast from '@webqit/util/str/beforeLast.js';
|
|
|
10
10
|
import _isObject from '@webqit/util/js/isObject.js';
|
|
11
11
|
import SimpleGit from 'simple-git';
|
|
12
12
|
import Webhooks from '@octokit/webhooks';
|
|
13
|
-
import * as origins from '
|
|
13
|
+
import * as origins from '../../config/origins.js';
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* @description
|
|
@@ -64,9 +64,9 @@ export async function deploy(Ui, origin, flags = {}, layout = {}) {
|
|
|
64
64
|
Fs.mkdirSync(origin.deploy_path, {recursive: true});
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
|
-
git.cwd(origin.deploy_path);
|
|
67
|
+
await git.cwd(origin.deploy_path);
|
|
68
68
|
// Must come after git.cwd()
|
|
69
|
-
git.init();
|
|
69
|
+
await git.init();
|
|
70
70
|
|
|
71
71
|
const hosts = {
|
|
72
72
|
github: 'https://github.com',
|
|
@@ -133,7 +133,7 @@ export async function deploy(Ui, origin, flags = {}, layout = {}) {
|
|
|
133
133
|
return pull();
|
|
134
134
|
}
|
|
135
135
|
});
|
|
136
|
-
}
|
|
136
|
+
}
|
|
137
137
|
|
|
138
138
|
/**
|
|
139
139
|
* @hook
|
|
@@ -183,4 +183,4 @@ export async function hook(Ui, event, deployCallback, flags = {}, layout = {}) {
|
|
|
183
183
|
payload: submits.payload /* JSON object */,
|
|
184
184
|
});
|
|
185
185
|
}
|
|
186
|
-
}
|
|
186
|
+
}
|