@webqit/webflo 0.9.6 → 0.10.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 +9 -5
- package/src/config-pi/runtime/Client.js +14 -0
- package/src/config-pi/runtime/Server.js +1 -63
- package/src/config-pi/runtime/server/Headers.js +0 -13
- package/src/config-pi/runtime/server/Redirects.js +2 -37
- package/src/config-pi/static/Ssg.js +1 -22
- package/src/index.js +2 -0
- package/src/runtime-pi/client/Runtime.js +0 -1
- package/src/runtime-pi/client/RuntimeClient.js +6 -4
- package/src/runtime-pi/client/generate.js +55 -37
- package/src/runtime-pi/client/worker/Worker.js +16 -21
- package/src/runtime-pi/client/worker/WorkerClient.js +1 -1
- package/src/runtime-pi/server/Router.js +43 -44
- package/src/runtime-pi/server/Runtime.js +10 -13
- package/src/runtime-pi/util.js +36 -8
- package/src/runtime-pi/xHttpMessage.js +9 -9
- package/src/runtime-pi/xResponse.js +7 -0
- package/test/index.test.js +3 -4
- package/vm +0 -58
package/package.json
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"vanila-javascript"
|
|
13
13
|
],
|
|
14
14
|
"homepage": "https://webqit.io/tooling/webflo",
|
|
15
|
-
"version": "0.
|
|
15
|
+
"version": "0.10.0",
|
|
16
16
|
"license": "MIT",
|
|
17
17
|
"repository": {
|
|
18
18
|
"type": "git",
|
|
@@ -40,17 +40,21 @@
|
|
|
40
40
|
"@webqit/oohtml-ssr": "^1.0.3",
|
|
41
41
|
"@webqit/util": "^0.8.9",
|
|
42
42
|
"client-sessions": "^0.8.0",
|
|
43
|
+
"esbuild": "^0.14.38",
|
|
43
44
|
"form-data-encoder": "^1.6.0",
|
|
44
45
|
"formdata-node": "^4.3.0",
|
|
45
46
|
"formidable": "^2.0.0-dev.20200131.2",
|
|
46
|
-
"is-glob": "^4.0.1",
|
|
47
|
-
"micromatch": "^3.1.10",
|
|
48
47
|
"mime-types": "^2.1.33",
|
|
49
|
-
"minimatch": "^3.0.4",
|
|
50
48
|
"node-fetch": "^2.6.1",
|
|
51
49
|
"simple-git": "^2.20.1",
|
|
52
50
|
"stream-slice": "^0.1.2",
|
|
53
|
-
"
|
|
51
|
+
"urlpattern-polyfill": "^4.0.3"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"chai": "^4.3.6",
|
|
55
|
+
"coveralls": "^3.1.1",
|
|
56
|
+
"mocha": "^10.0.0",
|
|
57
|
+
"mocha-lcov-reporter": "^1.3.0"
|
|
54
58
|
},
|
|
55
59
|
"author": "Oxford Harrison <oxharris.dev@gmail.com>",
|
|
56
60
|
"maintainers": [
|
|
@@ -22,9 +22,11 @@ export default class Client extends Configurator {
|
|
|
22
22
|
// Defaults merger
|
|
23
23
|
withDefaults(config) {
|
|
24
24
|
return _merge(true, {
|
|
25
|
+
bundle_filename: 'bundle.js',
|
|
25
26
|
support_oohtml: true,
|
|
26
27
|
support_service_worker: true,
|
|
27
28
|
worker_scope: '/',
|
|
29
|
+
worker_filename: 'worker.js',
|
|
28
30
|
}, config);
|
|
29
31
|
}
|
|
30
32
|
|
|
@@ -32,6 +34,12 @@ export default class Client extends Configurator {
|
|
|
32
34
|
questions(config, choices = {}) {
|
|
33
35
|
// Questions
|
|
34
36
|
return [
|
|
37
|
+
{
|
|
38
|
+
name: 'bundle_filename',
|
|
39
|
+
type: 'text',
|
|
40
|
+
message: 'Specify the bundle filename',
|
|
41
|
+
initial: config.bundle_filename,
|
|
42
|
+
},
|
|
35
43
|
{
|
|
36
44
|
name: 'support_oohtml',
|
|
37
45
|
type: 'toggle',
|
|
@@ -54,6 +62,12 @@ export default class Client extends Configurator {
|
|
|
54
62
|
message: 'Specify the Service Worker scope',
|
|
55
63
|
initial: config.worker_scope,
|
|
56
64
|
},
|
|
65
|
+
{
|
|
66
|
+
name: 'worker_filename',
|
|
67
|
+
type: (prev, answers) => answers.support_service_worker ? 'text' : null,
|
|
68
|
+
message: 'Specify the Service Worker filename',
|
|
69
|
+
initial: config.worker_filename,
|
|
70
|
+
},
|
|
57
71
|
];
|
|
58
72
|
}
|
|
59
73
|
}
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* imports
|
|
4
4
|
*/
|
|
5
|
-
import Path from 'path';
|
|
6
5
|
import { _merge } from '@webqit/util/obj/index.js';
|
|
7
6
|
import Configurator from '../../Configurator.js';
|
|
8
7
|
|
|
@@ -29,14 +28,6 @@ export default class Server extends Configurator {
|
|
|
29
28
|
certdoms: ['*'],
|
|
30
29
|
force: false,
|
|
31
30
|
},
|
|
32
|
-
process: {
|
|
33
|
-
name: Path.basename(process.cwd()),
|
|
34
|
-
errfile: '',
|
|
35
|
-
outfile: '',
|
|
36
|
-
exec_mode: 'fork',
|
|
37
|
-
autorestart: true,
|
|
38
|
-
merge_logs: false,
|
|
39
|
-
},
|
|
40
31
|
force_www: '',
|
|
41
32
|
shared: false,
|
|
42
33
|
}, config);
|
|
@@ -46,10 +37,6 @@ export default class Server extends Configurator {
|
|
|
46
37
|
questions(config, choices = {}) {
|
|
47
38
|
// Choices
|
|
48
39
|
const CHOICES = _merge({
|
|
49
|
-
exec_mode: [
|
|
50
|
-
{value: 'fork',},
|
|
51
|
-
{value: 'cluster',},
|
|
52
|
-
],
|
|
53
40
|
force_www: [
|
|
54
41
|
{value: '', title: 'do nothing'},
|
|
55
42
|
{value: 'add',},
|
|
@@ -67,10 +54,10 @@ export default class Server extends Configurator {
|
|
|
67
54
|
},
|
|
68
55
|
{
|
|
69
56
|
name: 'https',
|
|
70
|
-
initial: config.https,
|
|
71
57
|
controls: {
|
|
72
58
|
name: 'https',
|
|
73
59
|
},
|
|
60
|
+
initial: config.https,
|
|
74
61
|
questions: [
|
|
75
62
|
{
|
|
76
63
|
name: 'port',
|
|
@@ -105,55 +92,6 @@ export default class Server extends Configurator {
|
|
|
105
92
|
},
|
|
106
93
|
],
|
|
107
94
|
},
|
|
108
|
-
{
|
|
109
|
-
name: 'process',
|
|
110
|
-
initial: config.process,
|
|
111
|
-
controls: {
|
|
112
|
-
name: 'background process',
|
|
113
|
-
},
|
|
114
|
-
questions: [
|
|
115
|
-
{
|
|
116
|
-
name: 'name',
|
|
117
|
-
type: 'text',
|
|
118
|
-
message: 'Enter a name for process',
|
|
119
|
-
validation: ['important'],
|
|
120
|
-
},
|
|
121
|
-
{
|
|
122
|
-
name: 'errfile',
|
|
123
|
-
type: 'text',
|
|
124
|
-
message: 'Enter path to error file',
|
|
125
|
-
validation: ['important'],
|
|
126
|
-
},
|
|
127
|
-
{
|
|
128
|
-
name: 'outfile',
|
|
129
|
-
type: 'text',
|
|
130
|
-
message: 'Enter path to output file',
|
|
131
|
-
validation: ['important'],
|
|
132
|
-
},
|
|
133
|
-
{
|
|
134
|
-
name: 'exec_mode',
|
|
135
|
-
type: 'select',
|
|
136
|
-
message: 'Select exec mode',
|
|
137
|
-
choices: CHOICES.exec_mode,
|
|
138
|
-
validation: ['important'],
|
|
139
|
-
},
|
|
140
|
-
{
|
|
141
|
-
name: 'autorestart',
|
|
142
|
-
type: 'toggle',
|
|
143
|
-
message: 'Server autorestart on crash?',
|
|
144
|
-
active: 'YES',
|
|
145
|
-
inactive: 'NO',
|
|
146
|
-
initial: config.autorestart,
|
|
147
|
-
},
|
|
148
|
-
{
|
|
149
|
-
name: 'merge_logs',
|
|
150
|
-
type: 'toggle',
|
|
151
|
-
message: 'Server merge logs?',
|
|
152
|
-
active: 'YES',
|
|
153
|
-
inactive: 'NO',
|
|
154
|
-
},
|
|
155
|
-
],
|
|
156
|
-
},
|
|
157
95
|
{
|
|
158
96
|
name: 'force_www',
|
|
159
97
|
type: 'select',
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
* imports
|
|
4
4
|
*/
|
|
5
5
|
import Url from 'url';
|
|
6
|
-
import Micromatch from 'micromatch';
|
|
7
6
|
import { _merge } from '@webqit/util/obj/index.js';
|
|
8
7
|
import { _isObject } from '@webqit/util/js/index.js';
|
|
9
8
|
import Configurator from '../../../Configurator.js';
|
|
@@ -27,18 +26,6 @@ export default class Headers extends Configurator {
|
|
|
27
26
|
}, config);
|
|
28
27
|
}
|
|
29
28
|
|
|
30
|
-
// Match
|
|
31
|
-
async match(url) {
|
|
32
|
-
if (!_isObject(url)) {
|
|
33
|
-
url = Url.parse(url);
|
|
34
|
-
}
|
|
35
|
-
return ((await this.read()).entries || []).filter(header => {
|
|
36
|
-
var regex = Micromatch.makeRe(header.url, {dot: true});
|
|
37
|
-
var rootMatch = url.pathname.split('/').filter(seg => seg).map(seg => seg.trim()).reduce((str, seg) => str.endsWith(' ') ? str : ((str = str + '/' + seg) && str.match(regex) ? str + ' ' : str), '');
|
|
38
|
-
return rootMatch.endsWith(' ');
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
|
|
42
29
|
// Questions generator
|
|
43
30
|
questions(config, choices = {}) {
|
|
44
31
|
const CHOICES = _merge({
|
|
@@ -5,8 +5,7 @@
|
|
|
5
5
|
import Url from 'url';
|
|
6
6
|
import { _merge } from '@webqit/util/obj/index.js';
|
|
7
7
|
import { _after } from '@webqit/util/str/index.js';
|
|
8
|
-
import { _isObject } from '@webqit/util/js/index.js';
|
|
9
|
-
import Micromatch from 'micromatch';
|
|
8
|
+
import { _isObject, _isNumeric } from '@webqit/util/js/index.js';
|
|
10
9
|
import Configurator from '../../../Configurator.js';
|
|
11
10
|
|
|
12
11
|
export default class Redirects extends Configurator {
|
|
@@ -28,33 +27,6 @@ export default class Redirects extends Configurator {
|
|
|
28
27
|
}, config);
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
// Match
|
|
32
|
-
async match(url) {
|
|
33
|
-
if (!_isObject(url)) {
|
|
34
|
-
url = Url.parse(url);
|
|
35
|
-
}
|
|
36
|
-
return ((await this.read()).entries || []).reduce((match, rdr) => {
|
|
37
|
-
if (match) {
|
|
38
|
-
return match;
|
|
39
|
-
}
|
|
40
|
-
var regex = Micromatch.makeRe(rdr.from, {dot: true});
|
|
41
|
-
var rootMatch = url.pathname.split('/').filter(seg => seg).map(seg => seg.trim()).reduce((str, seg) => str.endsWith(' ') ? str : ((str = str + '/' + seg) && str.match(regex) ? str + ' ' : str), '');
|
|
42
|
-
if (rootMatch.endsWith(' ')) {
|
|
43
|
-
var leaf = _after(url.pathname, rootMatch.trim());
|
|
44
|
-
var [ target, targetQuery ] = rdr.to.split('?');
|
|
45
|
-
if (rdr.reuseQuery) {
|
|
46
|
-
targetQuery = [(url.search || '').substr(1), targetQuery].filter(str => str).join('&');
|
|
47
|
-
}
|
|
48
|
-
// ---------------
|
|
49
|
-
return {
|
|
50
|
-
target: target + leaf + (targetQuery ? (leaf.endsWith('?') || leaf.endsWith('&') ? '' : (leaf.includes('?') ? '&' : '?')) + targetQuery : ''),
|
|
51
|
-
query: targetQuery,
|
|
52
|
-
code: rdr.code,
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
}, null);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
30
|
// Questions generator
|
|
59
31
|
questions(config, choices = {}) {
|
|
60
32
|
// Choices
|
|
@@ -86,19 +58,12 @@ export default class Redirects extends Configurator {
|
|
|
86
58
|
message: 'Enter "to" URL',
|
|
87
59
|
validation: ['important'],
|
|
88
60
|
},
|
|
89
|
-
{
|
|
90
|
-
name: 'reuseQuery',
|
|
91
|
-
type: 'toggle',
|
|
92
|
-
message: 'Reuse query parameters from matched URL in destination URL?',
|
|
93
|
-
active: 'YES',
|
|
94
|
-
inactive: 'NO',
|
|
95
|
-
},
|
|
96
61
|
{
|
|
97
62
|
name: 'code',
|
|
98
63
|
type: 'select',
|
|
99
64
|
choices: CHOICES.code,
|
|
100
65
|
message: 'Enter redirect code',
|
|
101
|
-
validation: ['number'
|
|
66
|
+
validation: ['number'],
|
|
102
67
|
},
|
|
103
68
|
],
|
|
104
69
|
},
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { _merge } from '@webqit/util/obj/index.js';
|
|
6
6
|
import { _isObject } from '@webqit/util/js/index.js';
|
|
7
|
-
import Micromatch from 'micromatch';
|
|
8
7
|
import Configurator from '../../Configurator.js';
|
|
9
8
|
|
|
10
9
|
export default class Ssg extends Configurator {
|
|
@@ -26,26 +25,6 @@ export default class Ssg extends Configurator {
|
|
|
26
25
|
}, config);
|
|
27
26
|
}
|
|
28
27
|
|
|
29
|
-
// Match
|
|
30
|
-
async match(url) {
|
|
31
|
-
var pathname = url;
|
|
32
|
-
if (_isObject(url)) {
|
|
33
|
-
pathname = url.pathname;
|
|
34
|
-
}
|
|
35
|
-
return ((await this.read()).entries || []).reduce((match, prerend) => {
|
|
36
|
-
if (match) {
|
|
37
|
-
return match;
|
|
38
|
-
}
|
|
39
|
-
var regex = Micromatch.makeRe(prerend.page, {dot: true});
|
|
40
|
-
var rootMatch = pathname.split('/').filter(seg => seg).map(seg => seg.trim()).reduce((str, seg) => str.endsWith(' ') ? str : ((str = str + '/' + seg) && str.match(regex) ? str + ' ' : str), '');
|
|
41
|
-
if (rootMatch.endsWith(' ')) {
|
|
42
|
-
return {
|
|
43
|
-
url: prerend.page,
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
}, null);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
28
|
// Questions generator
|
|
50
29
|
questions(config, choices = {}) {
|
|
51
30
|
// Questions
|
|
@@ -59,7 +38,7 @@ export default class Ssg extends Configurator {
|
|
|
59
38
|
initial: config.entries,
|
|
60
39
|
questions: [
|
|
61
40
|
{
|
|
62
|
-
name: '
|
|
41
|
+
name: 'url',
|
|
63
42
|
type: 'text',
|
|
64
43
|
message: 'Page URL',
|
|
65
44
|
validation: ['important'],
|
package/src/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import * as config from './config-pi/index.js';
|
|
|
6
6
|
import * as deployment from './deployment-pi/index.js';
|
|
7
7
|
import * as runtime from './runtime-pi/index.js';
|
|
8
8
|
import * as services from './services-pi/index.js';
|
|
9
|
+
import Context from './Context.js';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* @exports
|
|
@@ -15,4 +16,5 @@ export {
|
|
|
15
16
|
deployment,
|
|
16
17
|
runtime,
|
|
17
18
|
services,
|
|
19
|
+
Context,
|
|
18
20
|
}
|
|
@@ -147,7 +147,6 @@ export default class Runtime {
|
|
|
147
147
|
this.go(Url.copy(actionEl), {
|
|
148
148
|
method: submitParams.method,
|
|
149
149
|
body: formData,
|
|
150
|
-
headers: { contentType: submitParams.enctype },
|
|
151
150
|
}, { ...submitParams, src: form, srcType: 'form', });
|
|
152
151
|
// URLs with # will cause a natural navigation
|
|
153
152
|
// even if pointing to a different page, a natural navigation will still happen
|
|
@@ -15,10 +15,12 @@ export default class RuntimeClient {
|
|
|
15
15
|
*/
|
|
16
16
|
constructor(cx) {
|
|
17
17
|
this.cx = cx;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
if (this.cx.support_service_worker) {
|
|
19
|
+
const workerComm = new WorkerComm(this.cx.worker_filename, { scope: this.cx.worker_scope, startMessages: true });
|
|
20
|
+
Observer.observe(workerComm, changes => {
|
|
21
|
+
//console.log('SERVICE_WORKER_STATE_CHANGE', changes[0].name, changes[0].value);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
/**
|
|
@@ -5,10 +5,11 @@
|
|
|
5
5
|
import Fs from 'fs';
|
|
6
6
|
import Url from 'url';
|
|
7
7
|
import Path from 'path';
|
|
8
|
-
import Webpack from 'webpack';
|
|
9
8
|
import { _beforeLast } from '@webqit/util/str/index.js';
|
|
10
9
|
import { _isObject, _isArray } from '@webqit/util/js/index.js';
|
|
11
10
|
import * as DotJs from '@webqit/backpack/src/dotfiles/DotJs.js';
|
|
11
|
+
import { gzipSync, brotliCompressSync } from 'zlib';
|
|
12
|
+
import EsBuild from 'esbuild';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* @generate
|
|
@@ -29,7 +30,7 @@ export async function generate() {
|
|
|
29
30
|
throw new Error(`The Layout configurator "config.deployment.Layout" is required in context.`);
|
|
30
31
|
}
|
|
31
32
|
const layoutConfig = await (new cx.config.deployment.Layout(cx)).read();
|
|
32
|
-
const
|
|
33
|
+
const dirPublic = Path.resolve(cx.CWD || '', layoutConfig.PUBLIC_DIR);
|
|
33
34
|
const dirSelf = Path.dirname(Url.fileURLToPath(import.meta.url)).replace(/\\/g, '/');
|
|
34
35
|
// -----------
|
|
35
36
|
// Generate client build
|
|
@@ -37,13 +38,13 @@ export async function generate() {
|
|
|
37
38
|
if (clientConfig.support_oohtml) {
|
|
38
39
|
genClient.imports = { [`${dirSelf}/generate.oohtml.js`]: null, ...genClient.imports };
|
|
39
40
|
}
|
|
40
|
-
await bundle.call(cx, genClient, {
|
|
41
|
+
await bundle.call(cx, genClient, `${dirPublic}/${clientConfig.bundle_filename}`, true/* asModule */);
|
|
41
42
|
cx.logger && cx.logger.log('');
|
|
42
43
|
// -----------
|
|
43
44
|
// Generate worker build
|
|
44
45
|
if (clientConfig.support_service_worker) {
|
|
45
46
|
let genWorker = getGen.call(cx, `${dirSelf}/worker`, layoutConfig.WORKER_DIR, workerConfig, `The Worker Build.`);
|
|
46
|
-
await bundle.call(cx, genWorker, {
|
|
47
|
+
await bundle.call(cx, genWorker, `${dirPublic}/${clientConfig.worker_filename}`);
|
|
47
48
|
cx.logger && cx.logger.log('');
|
|
48
49
|
}
|
|
49
50
|
}
|
|
@@ -181,14 +182,16 @@ function declareParamsObj(gen, paramsObj, varName = null, indentation = 0) {
|
|
|
181
182
|
* Bundle generated file
|
|
182
183
|
*
|
|
183
184
|
* @param object gen
|
|
184
|
-
* @param
|
|
185
|
+
* @param String outfile
|
|
185
186
|
* @param boolean asModule
|
|
186
187
|
*
|
|
187
188
|
* @return Promise
|
|
188
189
|
*/
|
|
189
|
-
function bundle(gen,
|
|
190
|
+
async function bundle(gen, outfile, asModule = false) {
|
|
190
191
|
const cx = this || {};
|
|
191
|
-
const
|
|
192
|
+
const compression = cx.flags.compress;
|
|
193
|
+
const moduleFile = `${_beforeLast(outfile, '.')}.esm.js`;
|
|
194
|
+
|
|
192
195
|
// ------------------
|
|
193
196
|
// >> Show waiting...
|
|
194
197
|
if (cx.logger) {
|
|
@@ -200,43 +203,58 @@ function bundle(gen, output, asModule = false) {
|
|
|
200
203
|
} else {
|
|
201
204
|
DotJs.write(gen, moduleFile, 'ES Module file');
|
|
202
205
|
}
|
|
206
|
+
|
|
203
207
|
// ----------------
|
|
204
208
|
// >> Webpack config
|
|
205
|
-
const bundlingConfig = {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
209
|
+
const bundlingConfig = {
|
|
210
|
+
entryPoints: [ moduleFile ],
|
|
211
|
+
outfile,
|
|
212
|
+
bundle: true,
|
|
213
|
+
minify: true,
|
|
214
|
+
banner: { js: '/** @webqit/webflo */', },
|
|
215
|
+
footer: { js: '', },
|
|
216
|
+
format: 'esm',
|
|
217
|
+
};
|
|
218
|
+
if (!asModule) {
|
|
219
|
+
// Support top-level await
|
|
220
|
+
// See: https://github.com/evanw/esbuild/issues/253#issuecomment-826147115
|
|
221
|
+
bundlingConfig.banner.js += '(async () => {';
|
|
222
|
+
bundlingConfig.footer.js += '})();';
|
|
213
223
|
}
|
|
224
|
+
|
|
214
225
|
// ----------------
|
|
215
226
|
// The bundling process
|
|
216
|
-
|
|
217
|
-
|
|
227
|
+
let waiting;
|
|
228
|
+
if (cx.logger) {
|
|
229
|
+
waiting = cx.logger.waiting(`Bundling...`);
|
|
230
|
+
cx.logger.log('');
|
|
231
|
+
cx.logger.log('> Bundling...');
|
|
232
|
+
cx.logger.info(cx.logger.f`FROM: ${bundlingConfig.entryPoints[0]}`);
|
|
233
|
+
cx.logger.info(cx.logger.f`TO: ${bundlingConfig.outfile}`);
|
|
234
|
+
waiting.start();
|
|
235
|
+
}
|
|
236
|
+
// Run
|
|
237
|
+
await EsBuild.build(bundlingConfig);
|
|
238
|
+
if (waiting) waiting.stop();
|
|
239
|
+
// Remove moduleFile build
|
|
240
|
+
Fs.unlinkSync(bundlingConfig.entryPoints[0]);
|
|
241
|
+
|
|
242
|
+
// ----------------
|
|
243
|
+
// Compress...
|
|
244
|
+
if (compression) {
|
|
218
245
|
if (cx.logger) {
|
|
219
|
-
waiting = cx.logger.waiting(`
|
|
220
|
-
cx.logger.log('');
|
|
221
|
-
cx.logger.log('> Bundling...');
|
|
222
|
-
cx.logger.info(cx.logger.f`FROM: ${bundlingConfig.entry}`);
|
|
223
|
-
cx.logger.info(cx.logger.f`TO: ${bundlingConfig.output.path + '/' + bundlingConfig.output.filename}`);
|
|
224
|
-
cx.logger.log('');
|
|
246
|
+
waiting = cx.logger.waiting(`Compressing...`);
|
|
225
247
|
waiting.start();
|
|
226
248
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
249
|
+
const contents = Fs.readFileSync(bundlingConfig.outfile);
|
|
250
|
+
const gzip = gzipSync(contents, {});
|
|
251
|
+
const brotli = brotliCompressSync(contents, {});
|
|
252
|
+
Fs.writeFileSync(`${bundlingConfig.outfile}.gz`, gzip);
|
|
253
|
+
Fs.writeFileSync(`${bundlingConfig.outfile}.br`, brotli);
|
|
254
|
+
if (waiting) {
|
|
230
255
|
waiting.stop();
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
let log = stats.toString({ colors: true, });
|
|
236
|
-
cx.logger && cx.logger.log(log);
|
|
237
|
-
// Remove moduleFile build
|
|
238
|
-
Fs.unlinkSync(bundlingConfig.entry);
|
|
239
|
-
resolve(log);
|
|
240
|
-
});
|
|
241
|
-
});
|
|
256
|
+
cx.logger.log('');
|
|
257
|
+
cx.logger.log('> Compression: .gz, .br');
|
|
258
|
+
}
|
|
259
|
+
}
|
|
242
260
|
}
|
|
@@ -2,12 +2,9 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* @imports
|
|
4
4
|
*/
|
|
5
|
-
import _isGlobe from 'is-glob';
|
|
6
|
-
import Minimatch from 'minimatch';
|
|
7
5
|
import { _any } from '@webqit/util/arr/index.js';
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import { Observer } from '../Runtime.js';
|
|
6
|
+
import { HttpEvent, Request, Response, Observer } from '../Runtime.js';
|
|
7
|
+
import { urlPattern } from '../../util.js';
|
|
11
8
|
|
|
12
9
|
/**
|
|
13
10
|
* ---------------------------
|
|
@@ -46,8 +43,8 @@ export default class Worker {
|
|
|
46
43
|
// Add files to cache
|
|
47
44
|
evt.waitUntil( self.caches.open(this.cx.params.cache_name).then(cache => {
|
|
48
45
|
if (this.cx.logger) { this.cx.logger.log('[ServiceWorker] Pre-caching resources.'); }
|
|
49
|
-
const cache_only_urls = (this.cx.params.cache_only_urls || []).map(c => c.trim()).filter(c => c);
|
|
50
|
-
return cache.addAll(cache_only_urls
|
|
46
|
+
const cache_only_urls = (this.cx.params.cache_only_urls || []).map(c => c.trim()).filter(c => c && !c.endsWith('/') && !urlPattern(c, self.origin).isPattern());
|
|
47
|
+
return cache.addAll(cache_only_urls);
|
|
51
48
|
}) );
|
|
52
49
|
}
|
|
53
50
|
});
|
|
@@ -77,10 +74,11 @@ export default class Worker {
|
|
|
77
74
|
// ONFETCH
|
|
78
75
|
self.addEventListener('fetch', async evt => {
|
|
79
76
|
// URL schemes that might arrive here but not supported; e.g.: chrome-extension://
|
|
80
|
-
if (!evt.request.url.startsWith('http')
|
|
81
|
-
const
|
|
77
|
+
if (!evt.request.url.startsWith('http')) return;
|
|
78
|
+
const deriveInit = req => [
|
|
82
79
|
'method', 'headers', 'body', 'mode', 'credentials', 'cache', 'redirect', 'referrer', 'integrity',
|
|
83
|
-
].reduce((init, prop) => ({ [prop]:
|
|
80
|
+
].reduce((init, prop) => ({ [prop]: req[prop], ...init }), {});
|
|
81
|
+
const requestInit = deriveInit(evt.request);
|
|
84
82
|
evt.respondWith(this.go(evt.request.url, requestInit, { event: evt }));
|
|
85
83
|
});
|
|
86
84
|
|
|
@@ -91,6 +89,7 @@ export default class Worker {
|
|
|
91
89
|
Observer.observe(this.network, es => {
|
|
92
90
|
//console.log('//////////', ...es.map(e => `${e.name}: ${e.value}`))
|
|
93
91
|
});
|
|
92
|
+
|
|
94
93
|
}
|
|
95
94
|
|
|
96
95
|
/**
|
|
@@ -111,7 +110,7 @@ export default class Worker {
|
|
|
111
110
|
let request = this.generateRequest(url.href, init);
|
|
112
111
|
if (detail.event instanceof self.Request) {
|
|
113
112
|
request = detail.event.request;
|
|
114
|
-
|
|
113
|
+
Object.defineProperty(detail.event, 'request', { value: request });
|
|
115
114
|
}
|
|
116
115
|
// The navigation event
|
|
117
116
|
let httpEvent = new HttpEvent(request, detail, (id = null, persistent = false) => this.getSession(httpEvent, id, persistent));
|
|
@@ -160,18 +159,19 @@ export default class Worker {
|
|
|
160
159
|
if (arguments.length > 1) {
|
|
161
160
|
request = this.generateRequest(request, ...args);
|
|
162
161
|
}
|
|
162
|
+
const matchUrl = (patterns, url) => _any((patterns || []).map(p => p.trim()).filter(p => p), p => urlPattern(p, self.origin).test(url));
|
|
163
163
|
const execFetch = () => {
|
|
164
|
-
if (
|
|
164
|
+
if (matchUrl(this.cx.params.cache_only_urls, request.url)) {
|
|
165
165
|
Observer.set(this.network, 'strategy', 'cache-only');
|
|
166
166
|
return this.cacheFetch(request, { networkFallback: false, cacheRefresh: false });
|
|
167
167
|
}
|
|
168
168
|
// network_only_urls
|
|
169
|
-
if (
|
|
169
|
+
if (matchUrl(this.cx.params.network_only_urls, request.url)) {
|
|
170
170
|
Observer.set(this.network, 'strategy', 'network-only');
|
|
171
171
|
return this.networkFetch(request, { cacheFallback: false, cacheRefresh: false });
|
|
172
172
|
}
|
|
173
173
|
// cache_first_urls
|
|
174
|
-
if (
|
|
174
|
+
if (matchUrl(this.cx.params.cache_first_urls, request.url)) {
|
|
175
175
|
Observer.set(this.network, 'strategy', 'cache-first');
|
|
176
176
|
return this.cacheFetch(request, { networkFallback: true, cacheRefresh: true });
|
|
177
177
|
}
|
|
@@ -182,7 +182,7 @@ export default class Worker {
|
|
|
182
182
|
// This catch() is NOT intended to handle failure of the fetch
|
|
183
183
|
response.catch(e => Observer.set(this.network, 'error', e.message));
|
|
184
184
|
// Return xResponse
|
|
185
|
-
return response.then(_response =>
|
|
185
|
+
return response.then(_response => Response.compat(_response));
|
|
186
186
|
}
|
|
187
187
|
|
|
188
188
|
// Caching strategy: cache_first
|
|
@@ -199,11 +199,6 @@ export default class Worker {
|
|
|
199
199
|
|
|
200
200
|
// Caching strategy: network_first
|
|
201
201
|
networkFetch(request, params = {}) {
|
|
202
|
-
if (params.forceNetwork) {
|
|
203
|
-
let url = new URL(request.url);
|
|
204
|
-
url.searchParams.set('$force-cache', '1');
|
|
205
|
-
request.attr.url = url.toString();
|
|
206
|
-
}
|
|
207
202
|
if (!params.cacheFallback) {
|
|
208
203
|
Observer.set(this.network, 'remote', true);
|
|
209
204
|
return self.fetch(request);
|
|
@@ -246,7 +241,7 @@ export default class Worker {
|
|
|
246
241
|
|
|
247
242
|
// Handles response object
|
|
248
243
|
handleResponse(e, response) {
|
|
249
|
-
if (!(response instanceof Response)) { response =
|
|
244
|
+
if (!(response instanceof Response)) { response = Response.compat(response); }
|
|
250
245
|
return response;
|
|
251
246
|
}
|
|
252
247
|
|
|
@@ -35,7 +35,7 @@ export default class WorkerClient {
|
|
|
35
35
|
return remoteFetch(event.request);
|
|
36
36
|
}, remoteFetch);
|
|
37
37
|
if (!(response instanceof httpEvent.Response)) {
|
|
38
|
-
response =
|
|
38
|
+
response = httpEvent.Response.compat(response);
|
|
39
39
|
}
|
|
40
40
|
return response;
|
|
41
41
|
};
|
|
@@ -54,56 +54,55 @@ export default class Router extends _Router {
|
|
|
54
54
|
/**
|
|
55
55
|
* Reads a static file from the public directory.
|
|
56
56
|
*
|
|
57
|
-
* @param ServerNavigationEvent
|
|
57
|
+
* @param ServerNavigationEvent httpEvent
|
|
58
58
|
*
|
|
59
59
|
* @return Promise
|
|
60
60
|
*/
|
|
61
|
-
file(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (Fs.existsSync(
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
61
|
+
file(httpEvent) {
|
|
62
|
+
let filename = Path.join(this.cx.CWD, this.cx.layout.PUBLIC_DIR, decodeURIComponent(httpEvent.url.pathname));
|
|
63
|
+
let index, ext = Path.parse(httpEvent.url.pathname).ext;
|
|
64
|
+
// if is a directory search for index file matching the extention
|
|
65
|
+
if (!ext && Fs.existsSync(filename) && Fs.lstatSync(filename).isDirectory()) {
|
|
66
|
+
ext = '.html';
|
|
67
|
+
index = `index${ext}`;
|
|
68
|
+
filename = Path.join(filename, index);
|
|
69
|
+
}
|
|
70
|
+
let enc, acceptEncs = [], supportedEncs = { gzip: '.gz', br: '.br' };
|
|
71
|
+
// based on the URL path, extract the file extention. e.g. .js, .doc, ...
|
|
72
|
+
// and process encoding
|
|
73
|
+
if ((acceptEncs = (httpEvent.request.headers.get('Accept-Encoding') || '').split(',').map(e => e.trim())).length
|
|
74
|
+
&& (enc = acceptEncs.reduce((prev, _enc) => prev || (Fs.existsSync(filename + supportedEncs[_enc]) && _enc), null))) {
|
|
75
|
+
filename = filename + supportedEncs[enc];
|
|
76
|
+
} else {
|
|
77
|
+
if (!Fs.existsSync(filename)) return;
|
|
78
|
+
if (Object.values(supportedEncs).includes(ext)) {
|
|
79
|
+
enc = Object.keys(supportedEncs).reduce((prev, _enc) => prev || (supportedEncs[_enc] === ext && _enc), null);
|
|
80
|
+
ext = Path.parse(filename.substring(0, filename.length - ext.length)).ext;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// read file from file system
|
|
84
|
+
return new Promise(resolve => {
|
|
85
|
+
Fs.readFile(filename, function(err, data) {
|
|
86
|
+
let response;
|
|
87
|
+
if (err) {
|
|
88
|
+
response = new httpEvent.Response(null, { status: 500, statusText: `Error reading static file: ${filename}` } );
|
|
89
|
+
} else {
|
|
90
|
+
// if the file is found, set Content-type and send data
|
|
91
|
+
const type = Mime.lookup(ext);
|
|
92
|
+
response = new httpEvent.Response(data, { headers: {
|
|
93
|
+
contentType: type === 'application/javascript' ? 'text/javascript' : type,
|
|
94
|
+
contentLength: Buffer.byteLength(data),
|
|
95
|
+
} });
|
|
96
|
+
if (enc) {
|
|
97
|
+
response.headers.set('Content-Encoding', enc);
|
|
78
98
|
}
|
|
79
99
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
errorCode: 500,
|
|
85
|
-
error: 'Error reading static file: ' + filename + '.',
|
|
86
|
-
});
|
|
87
|
-
} else {
|
|
88
|
-
|
|
89
|
-
// if the file is found, set Content-type and send data
|
|
90
|
-
const type = Mime.lookup(ext);
|
|
91
|
-
resolve( new event.Response(data, {
|
|
92
|
-
headers: {
|
|
93
|
-
contentType: type === 'application/javascript' ? 'text/javascript' : type,
|
|
94
|
-
contentLength: Buffer.byteLength(data),
|
|
95
|
-
},
|
|
96
|
-
meta: {
|
|
97
|
-
filename: _filename,
|
|
98
|
-
static: true,
|
|
99
|
-
autoIndex,
|
|
100
|
-
}
|
|
101
|
-
} ) );
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
});
|
|
100
|
+
response.attrs.filename = filename;
|
|
101
|
+
response.attrs.static = true;
|
|
102
|
+
response.attrs.index = index;
|
|
103
|
+
resolve(response);
|
|
105
104
|
});
|
|
106
|
-
}
|
|
105
|
+
});
|
|
107
106
|
}
|
|
108
107
|
|
|
109
108
|
/**
|
|
@@ -11,8 +11,9 @@ import Sessions from 'client-sessions';
|
|
|
11
11
|
import { Observer } from '@webqit/oohtml-ssr/apis.js';
|
|
12
12
|
import { _each } from '@webqit/util/obj/index.js';
|
|
13
13
|
import { _isEmpty } from '@webqit/util/js/index.js';
|
|
14
|
-
import { _from as _arrFrom } from '@webqit/util/arr/index.js';
|
|
14
|
+
import { _from as _arrFrom, _any } from '@webqit/util/arr/index.js';
|
|
15
15
|
import { slice as _streamSlice } from 'stream-slice';
|
|
16
|
+
import { urlPattern } from '../util.js';
|
|
16
17
|
import * as whatwag from './whatwag.js';
|
|
17
18
|
import xURL from '../xURL.js';
|
|
18
19
|
import xFormData from "../xFormData.js";
|
|
@@ -276,14 +277,16 @@ export default class Runtime {
|
|
|
276
277
|
rdr = { status: 302, headers: { Location: ( url.hostname = url.hostname.substr(4), url.href ) } };
|
|
277
278
|
} else if (!url.hostname.startsWith('www.') && _context.server.force_www === 'add') {
|
|
278
279
|
rdr = { status: 302, headers: { Location: ( url.hostname = `www.${url.hostname}`, url.href ) } };
|
|
279
|
-
} else if (_context.config.
|
|
280
|
-
rdr =
|
|
280
|
+
} else if (_context.config.runtime.server.Redirects) {
|
|
281
|
+
rdr = ((await (new _context.config.runtime.server.Redirects(_context)).read()).entries || []).reduce((_rdr, entry) => {
|
|
282
|
+
return _rdr || ((_rdr = urlPattern(entry.from, url.origin).exec(url.href)) && { status: entry.code || 302, headers: { Location: _rdr.render(entry.to) } });
|
|
283
|
+
}, null);
|
|
281
284
|
}
|
|
282
285
|
if (rdr) {
|
|
283
|
-
return Response(null, rdr);
|
|
286
|
+
return new Response(null, rdr);
|
|
284
287
|
}
|
|
285
|
-
const autoHeaders = _context.config.Headers
|
|
286
|
-
? await (new _context.config.Headers(_context)).
|
|
288
|
+
const autoHeaders = _context.config.runtime.server.Headers
|
|
289
|
+
? ((await (new _context.config.runtime.server.Headers(_context)).read()).entries || []).filter(entry => urlPattern(entry.url, url.origin).exec(url.href))
|
|
287
290
|
: [];
|
|
288
291
|
// ------------
|
|
289
292
|
|
|
@@ -377,7 +380,6 @@ export default class Runtime {
|
|
|
377
380
|
|
|
378
381
|
// ----------------
|
|
379
382
|
// Mock-Cookies?
|
|
380
|
-
// ----------------
|
|
381
383
|
if (!(e.detail.request && e.detail.response)) {
|
|
382
384
|
for (let cookieName of Object.getOwnPropertyNames(this.mockSessionStore)) {
|
|
383
385
|
response.headers.set('Set-Cookie', `${cookieName}=1`); // We just want to know availability... not validity, as this is understood to be for testing purposes only
|
|
@@ -386,13 +388,11 @@ export default class Runtime {
|
|
|
386
388
|
|
|
387
389
|
// ----------------
|
|
388
390
|
// Auto-Headers
|
|
389
|
-
// ----------------
|
|
390
391
|
response.headers.set('Accept-Ranges', 'bytes');
|
|
391
392
|
this._autoHeaders(response.headers, autoHeaders);
|
|
392
393
|
|
|
393
394
|
// ----------------
|
|
394
395
|
// Redirects
|
|
395
|
-
// ----------------
|
|
396
396
|
if (response.headers.redirect) {
|
|
397
397
|
let xRedirectPolicy = e.request.headers.get('X-Redirect-Policy');
|
|
398
398
|
let xRedirectCode = e.request.headers.get('X-Redirect-Code') || 300;
|
|
@@ -410,7 +410,6 @@ export default class Runtime {
|
|
|
410
410
|
|
|
411
411
|
// ----------------
|
|
412
412
|
// 404
|
|
413
|
-
// ----------------
|
|
414
413
|
if (response.bodyAttrs.input === undefined || response.bodyAttrs.input === null) {
|
|
415
414
|
response.attrs.status = response.status !== 200 ? response.status : 404;
|
|
416
415
|
response.attrs.statusText = `${e.request.url} not found!`;
|
|
@@ -419,7 +418,6 @@ export default class Runtime {
|
|
|
419
418
|
|
|
420
419
|
// ----------------
|
|
421
420
|
// Not acceptable
|
|
422
|
-
// ----------------
|
|
423
421
|
if (e.request.headers.get('Accept') && !e.request.headers.accept.match(response.headers.contentType)) {
|
|
424
422
|
response.attrs.status = 406;
|
|
425
423
|
return response;
|
|
@@ -428,14 +426,12 @@ export default class Runtime {
|
|
|
428
426
|
// ----------------
|
|
429
427
|
// Important no-caching
|
|
430
428
|
// for non-"get" requests
|
|
431
|
-
// ----------------
|
|
432
429
|
if (e.request.method !== 'GET' && !response.headers.get('Cache-Control')) {
|
|
433
430
|
response.headers.set('Cache-Control', 'no-store');
|
|
434
431
|
}
|
|
435
432
|
|
|
436
433
|
// ----------------
|
|
437
434
|
// Body
|
|
438
|
-
// ----------------
|
|
439
435
|
let rangeRequest, body = response.body;
|
|
440
436
|
if ((rangeRequest = e.request.headers.range) && !response.headers.get('Content-Range')
|
|
441
437
|
&& ((body instanceof ReadableStream) || (ArrayBuffer.isView(body) && (body = ReadableStream.from(body))))) {
|
|
@@ -501,6 +497,7 @@ export default class Runtime {
|
|
|
501
497
|
log.push(style.url(e.request.url));
|
|
502
498
|
if (response.attrs.hint) log.push(`(${style.comment(response.attrs.hint)})`);
|
|
503
499
|
if (response.headers.contentType) log.push(`(${style.comment(response.headers.contentType)})`);
|
|
500
|
+
if (response.headers.get('Content-Encoding')) log.push(`(${style.comment(response.headers.get('Content-Encoding'))})`);
|
|
504
501
|
if (errorCode) log.push(style.err(`${errorCode} ${response.statusText}`));
|
|
505
502
|
else log.push(style.val(`${statusCode} ${response.statusText}`));
|
|
506
503
|
if (redirectCode) log.push(`- ${style.url(response.headers.redirect)}`);
|
package/src/runtime-pi/util.js
CHANGED
|
@@ -2,14 +2,13 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* @imports
|
|
4
4
|
*/
|
|
5
|
-
import _isArray from '@webqit/util/js/
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
import
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
5
|
+
import { _isString, _isObject, _isNumeric, _isArray } from '@webqit/util/js/index.js';
|
|
6
|
+
import { _beforeLast, _afterLast } from '@webqit/util/str/index.js';
|
|
7
|
+
import { _from as _arrFrom } from '@webqit/util/arr/index.js';
|
|
8
|
+
if (typeof URLPattern === 'undefined') {
|
|
9
|
+
await import('urlpattern-polyfill');
|
|
10
|
+
}
|
|
11
|
+
|
|
13
12
|
/**
|
|
14
13
|
* ---------------
|
|
15
14
|
* @wwwFormPathUnserializeCallback
|
|
@@ -132,3 +131,32 @@ export const path = {
|
|
|
132
131
|
return this.join(path, "..");
|
|
133
132
|
}
|
|
134
133
|
};
|
|
134
|
+
|
|
135
|
+
export const urlPattern = (pattern, baseUrl = null) => ({
|
|
136
|
+
pattern: new URLPattern(pattern, baseUrl),
|
|
137
|
+
isPattern() {
|
|
138
|
+
return Object.keys(this.pattern.keys).some(compName => this.pattern.keys[compName].length);
|
|
139
|
+
},
|
|
140
|
+
test(...args) { this.pattern.test(...args) },
|
|
141
|
+
exec(...args) {
|
|
142
|
+
let components = this.pattern.exec(...args);
|
|
143
|
+
if (!components) return;
|
|
144
|
+
components.vars = Object.keys(this.pattern.keys).reduce(({ named, unnamed }, compName) => {
|
|
145
|
+
this.pattern.keys[compName].forEach(key => {
|
|
146
|
+
let value = components[compName].groups[key.name];
|
|
147
|
+
if (typeof key.name === 'number') {
|
|
148
|
+
unnamed.push(value);
|
|
149
|
+
} else {
|
|
150
|
+
named[key.name] = value;
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
return { named, unnamed };
|
|
154
|
+
}, { named: {}, unnamed: [] });
|
|
155
|
+
components.render = str => {
|
|
156
|
+
return str.replace(/\$(\$|[0-9A-Z]+)/gi, (a, b) => {
|
|
157
|
+
return b === '$' ? '$' : (_isNumeric(b) ? components.vars.unnamed[b - 1] : components.vars.named[b]) || '';
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
return components;
|
|
161
|
+
}
|
|
162
|
+
});
|
|
@@ -89,10 +89,10 @@ const xHttpMessage = (whatwagHttpMessage, Headers, FormData) => {
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
// Resolve
|
|
92
|
-
|
|
93
|
-
if (!this.bodyAttrs.
|
|
94
|
-
this.bodyAttrs.
|
|
95
|
-
var messageInstance = this,
|
|
92
|
+
jsonfy(force = false) {
|
|
93
|
+
if (!this.bodyAttrs.jsonfied || force) {
|
|
94
|
+
this.bodyAttrs.jsonfied = new Promise(async (resolve, reject) => {
|
|
95
|
+
var messageInstance = this, jsonfied, contentType = messageInstance.headers.get('content-type') || '';
|
|
96
96
|
var type = contentType === 'application/json' || this.bodyAttrs.json ? 'json' : (
|
|
97
97
|
contentType === 'application/x-www-form-urlencoded' || contentType.startsWith('multipart/') || this.bodyAttrs.formData ? 'formData' : (
|
|
98
98
|
contentType === 'text/plain' ? 'plain' : 'other'
|
|
@@ -100,19 +100,19 @@ const xHttpMessage = (whatwagHttpMessage, Headers, FormData) => {
|
|
|
100
100
|
);
|
|
101
101
|
try {
|
|
102
102
|
if (type === 'formData') {
|
|
103
|
-
|
|
103
|
+
jsonfied = (await messageInstance.formData()).json();
|
|
104
104
|
} else {
|
|
105
|
-
|
|
105
|
+
jsonfied = type === 'json' ? await messageInstance.json() : (
|
|
106
106
|
type === 'plain' ? await messageInstance.text() : messageInstance.body
|
|
107
107
|
);
|
|
108
108
|
}
|
|
109
|
-
resolve(
|
|
109
|
+
resolve(jsonfied);
|
|
110
110
|
} catch(e) {
|
|
111
111
|
reject(e);
|
|
112
112
|
}
|
|
113
113
|
});
|
|
114
114
|
}
|
|
115
|
-
return this.bodyAttrs.
|
|
115
|
+
return this.bodyAttrs.jsonfied;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
};
|
|
@@ -173,7 +173,7 @@ export function encodeBody(body, FormData, Blob) {
|
|
|
173
173
|
contentLength: (new Blob([ detailsObj.body ])).size, // Buffer.byteLength(detailsObj.body, 'utf8') isn't cross-environment
|
|
174
174
|
};
|
|
175
175
|
}
|
|
176
|
-
detailsObj.
|
|
176
|
+
detailsObj.jsonfied = body;
|
|
177
177
|
}
|
|
178
178
|
return detailsObj;
|
|
179
179
|
}
|
|
@@ -57,6 +57,13 @@ const xResponse = (whatwagResponse, Headers, FormData, Blob) => class extends xH
|
|
|
57
57
|
return 'redirected' in this.attrs ? this.attrs.redirected : super.redirected;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
static compat(response) {
|
|
61
|
+
if (response instanceof whatwagResponse) {
|
|
62
|
+
return Object.setPrototypeOf(response, new this);
|
|
63
|
+
}
|
|
64
|
+
return new this(response);
|
|
65
|
+
}
|
|
66
|
+
|
|
60
67
|
};
|
|
61
68
|
|
|
62
69
|
export default xResponse;
|
package/test/index.test.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @imports
|
|
3
3
|
*/
|
|
4
|
-
import Context from
|
|
5
|
-
import * as WebfloPI from '../src/index.js';
|
|
4
|
+
import { config, runtime, Context } from '../src/index.js';
|
|
6
5
|
|
|
7
6
|
let client = {
|
|
8
7
|
handle: function(httpEvent) {
|
|
@@ -19,8 +18,8 @@ let client = {
|
|
|
19
18
|
},
|
|
20
19
|
};
|
|
21
20
|
|
|
22
|
-
const cx = Context.create({ config:
|
|
21
|
+
const cx = Context.create({ config: config, });
|
|
23
22
|
const clientCallback = (_cx, hostName, defaultClientCallback) => client;
|
|
24
|
-
const app = await
|
|
23
|
+
const app = await runtime.server.start.call(cx, clientCallback);
|
|
25
24
|
|
|
26
25
|
const response = await app.go('http://localhost/', { headers: { range: 'bytes=0-5, 6' } } );
|
package/vm
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
-------------------
|
|
2
|
-
sudo app-get install git
|
|
3
|
-
sudo app-get install node
|
|
4
|
-
-------------------
|
|
5
|
-
git clone ...
|
|
6
|
-
cd
|
|
7
|
-
node i
|
|
8
|
-
sudo npm i -g @webqit/webflo
|
|
9
|
-
sudo npm i -g @webqit/oohtml-cli
|
|
10
|
-
webflo config origins
|
|
11
|
-
webflo config server
|
|
12
|
-
sudo webflo start
|
|
13
|
-
-------------------
|
|
14
|
-
sudo iptables -I INPUT -p tcp -m tcp --dport 80 -j ACCEPT
|
|
15
|
-
sudo iptables -I INPUT -p tcp -m tcp --dport 443 -j ACCEPT
|
|
16
|
-
List port-forwarding
|
|
17
|
-
sudo iptables -t nat -L
|
|
18
|
-
Add port-forwarding
|
|
19
|
-
sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 3000
|
|
20
|
-
sudo iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 4200
|
|
21
|
-
Delete port-forwarding
|
|
22
|
-
sudo iptables -t nat -D PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 3000
|
|
23
|
-
sudo sh -c "iptables-save > /etc/iptables.rules"
|
|
24
|
-
sudo apt-get install iptables-persistent
|
|
25
|
-
-------------------
|
|
26
|
-
sudo apt-get update &&
|
|
27
|
-
sudo apt-get install software-properties-common &&
|
|
28
|
-
sudo add-apt-repository universe &&
|
|
29
|
-
sudo add-apt-repository ppa:certbot/certbot &&
|
|
30
|
-
sudo apt-get update
|
|
31
|
-
|
|
32
|
-
sudo apt-get install certbot
|
|
33
|
-
https://dev.to/omergulen/step-by-step-node-express-ssl-certificate-run-https-server-from-scratch-in-5-steps-5b87
|
|
34
|
-
-------------------
|
|
35
|
-
sudo certbot certonly --standalone
|
|
36
|
-
sudo certbot certonly --webroot
|
|
37
|
-
/etc/letsencrypt/live/webqit.io/fullchain.pem
|
|
38
|
-
/etc/letsencrypt/live/webqit.io/privkey.pem
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
sudo certbot certonly --webroot -w /home/webqit_io/www/neatly.fit/public -d neatly.fit
|
|
43
|
-
sudo certbot certonly --webroot -w /home/webqit_io/www/neatly.fit/public -d neatly.fit --debug-challenges
|
|
44
|
-
sudo certbot-auto certonly --manual --preferred-challenges dns -d neatly.fit --debug-challenges
|
|
45
|
-
|
|
46
|
-
sudo certbot run -a webroot -i apache -w /home/webqit_io/www/neatly.fit/public -d neatly.fit --debug-challenges
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
/.well-known/acme-challenge
|
|
52
|
-
/www/neatly.fit/public/.well-known/acme-challenge
|
|
53
|
-
certbot certonly --manual --preferred-challenges http -d neatly.fit --manual-auth-hook certbot-http-auth-hook --manual-cleanup-hook certbot-http-cleanup-hook --debug-challenges
|
|
54
|
-
|
|
55
|
-
An unexpected error occurred:
|
|
56
|
-
There were too many requests of a given type :: Error creating new order :: too many failed authorizations recently: see https://letsencrypt.org/docs/rate-limits/
|
|
57
|
-
|
|
58
|
-
sudo launchctl list | grep postgres
|