@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.
Files changed (47) hide show
  1. package/docker/Dockerfile +26 -0
  2. package/docker/README.md +77 -0
  3. package/package.json +9 -8
  4. package/src/{cmd/client.js → build/client/index.js} +24 -11
  5. package/src/build/index.js +5 -0
  6. package/src/index.js +47 -45
  7. package/src/runtime/Router.js +130 -0
  8. package/src/runtime/_FormData.js +60 -0
  9. package/src/runtime/_Headers.js +88 -0
  10. package/src/runtime/_MessageStream.js +191 -0
  11. package/src/runtime/_NavigationEvent.js +89 -0
  12. package/src/runtime/_Request.js +61 -0
  13. package/src/runtime/_RequestHeaders.js +72 -0
  14. package/src/runtime/_Response.js +56 -0
  15. package/src/runtime/_ResponseHeaders.js +81 -0
  16. package/src/runtime/_URL.js +111 -0
  17. package/src/{modules → runtime}/client/Cache.js +0 -0
  18. package/src/{modules → runtime}/client/Http.js +21 -6
  19. package/src/runtime/client/NavigationEvent.js +20 -0
  20. package/src/runtime/client/Router.js +47 -0
  21. package/src/{modules/client/Client.js → runtime/client/Runtime.js} +32 -25
  22. package/src/{modules → runtime}/client/StdRequest.js +4 -8
  23. package/src/{modules → runtime}/client/Storage.js +6 -6
  24. package/src/{modules → runtime}/client/Url.js +0 -0
  25. package/src/{modules → runtime}/client/Worker.js +11 -49
  26. package/src/{modules → runtime}/client/WorkerClient.js +5 -38
  27. package/src/runtime/client/WorkerComm.js +183 -0
  28. package/src/runtime/client/effects/sounds.js +64 -0
  29. package/src/runtime/index.js +5 -0
  30. package/src/runtime/server/NavigationEvent.js +38 -0
  31. package/src/runtime/server/Router.js +158 -0
  32. package/src/{modules/server/Server.js → runtime/server/Runtime.js} +177 -85
  33. package/src/{cmd/server.js → runtime/server/index.js} +9 -9
  34. package/src/{modules/server/start.mjs → runtime/server/index.mjs} +2 -2
  35. package/src/{modules → runtime}/util.js +7 -7
  36. package/src/{modules → services}/certbot/http-auth-hook.js +0 -0
  37. package/src/{modules → services}/certbot/http-cleanup-hook.js +0 -0
  38. package/src/{cmd/certbot.js → services/certbot/index.js} +2 -3
  39. package/src/services/index.js +6 -0
  40. package/src/{cmd/origins.js → services/origins/index.js} +5 -5
  41. package/src/cmd/index.js +0 -8
  42. package/src/modules/NavigationEvent.js +0 -53
  43. package/src/modules/Response.js +0 -98
  44. package/src/modules/XURL.js +0 -125
  45. package/src/modules/client/Router.js +0 -123
  46. package/src/modules/server/Router.js +0 -231
  47. 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 _isTypeObject from '@webqit/util/js/isTypeObject.js';
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 cmd from '../../cmd/index.js';
20
- import NavigationEvent from '../NavigationEvent.js';
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({IncomingMessage: StdIncomingMessage}, (request, response) => {
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({IncomingMessage: StdIncomingMessage}, (request, response) => {
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 serverNavigationEvent = new NavigationEvent(request, protocol + '://' + request.headers.host + request.url, request._session);
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[headerName] || '' , headerValue].filter(v => v);
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[name] = value;
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
- if (cmd.origins) {
277
- await cmd.origins.hook(Ui, serverNavigationEvent, async (payload, defaultPeployFn) => {
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(serverNavigationEvent);
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 && !serverNavigationEvent.request.accepts.type(file.contentType)) {
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
- var prerenderMatch = config.prerendering ? await !config.prerendering.match(serverNavigationEvent.url.pathname, flags, hostSetup.layout) : null;
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({ body: $context.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.contentType
321
- && !$context.response.redirect
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
- const rendering = await router.route('render', serverNavigationEvent, $context.response.body, async function(data) {
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 = serverNavigationEvent.url.pathname.split('/');
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: serverNavigationEvent.url.href,
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: serverNavigationEvent.url}, {update: 'merge'});
355
- window.document.body.setAttribute('template', 'page/' + serverNavigationEvent.url.pathname.split('/').filter(a => a).map(a => a + '+-').join('/'));
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
- $context.response = await _promise(resolve => {
368
- setTimeout(() => {
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
- } else if (serverNavigationEvent.request.accepts.type('application/json')) {
383
- // --------
384
- // JSONfy
385
- // --------
386
- $context.response.contentType = 'application/json';
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 headers
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
- _each($context.response.headers || {}, (name, value) => {
431
- if (name.toLowerCase() === 'set-cookie') {
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
- $context.response.status = 302 /* Temporary */;
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.body !== undefined) {
450
- response.end(
451
- $context.response.contentType === 'application/json' && _isTypeObject($context.response.body)
452
- ? JSON.stringify($context.response.body)
453
- : $context.response.body
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.body);
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 = e.errorCode || 500;
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.body && $context.response.autoIndex ? Ui.style.comment((!serverNavigationEvent.request.url.endsWith('/') ? '/' : '') + $context.response.autoIndex) : '') + ' '
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 '../config/server.js'
11
- import Server from '../modules/server/Server.js';
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, '../modules/server/start.mjs');
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, '../../package.json'));
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(`Server running in ${Ui.style.keyword('production')}; ${(processAutoRestart ? 'will' : 'wo\'nt')} autorestart on crash!`);
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 Server.call(null, Ui, flags);
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`Server ${flags.kill ? 'killed' : 'stopped'}: ${name}`);
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`Server restarted: ${name}`);
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 Server from './Server.js';
7
+ import Runtime from './Runtime.js';
8
8
 
9
9
  const { flags } = parseArgs(process.argv);
10
- Server.call(null, Ui, flags);
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}[${!isArr ? key : ''}]`, value[key], callback);
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
+ };
@@ -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 '../config/server.js';
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
+ }
@@ -0,0 +1,6 @@
1
+
2
+ /**
3
+ * exports
4
+ */
5
+ export * as certbot from './certbot/index.js';
6
+ export * as origins from './origins/index.js';
@@ -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 '../config/origins.js'
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
+ }
package/src/cmd/index.js DELETED
@@ -1,8 +0,0 @@
1
-
2
- /**
3
- * exports
4
- */
5
- export * as certbot from './certbot.js';
6
- export * as client from './client.js';
7
- export * as origins from './origins.js';
8
- export * as server from './server.js';