@webqit/webflo 0.9.7 → 0.10.2
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 +8 -6
- 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 +19 -18
- 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 +10 -10
- package/src/runtime-pi/xRequest.js +7 -0
- package/test/index.test.js +3 -4
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.2",
|
|
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
|
}
|
|
@@ -186,19 +186,21 @@ export default class Runtime {
|
|
|
186
186
|
* @return Response
|
|
187
187
|
*/
|
|
188
188
|
async go(url, init = {}, detail = {}) {
|
|
189
|
-
if (this._abortController) {
|
|
190
|
-
this._abortController.abort();
|
|
191
|
-
}
|
|
192
|
-
this._abortController = new AbortController();
|
|
193
|
-
this._xRedirectCode = 200;
|
|
194
|
-
// ------------
|
|
195
189
|
url = typeof url === 'string' ? new whatwag.URL(url) : url;
|
|
196
190
|
init = { referrer: this.location.href, ...init };
|
|
197
191
|
// ------------
|
|
192
|
+
// Put his forward before instantiating a request and aborting previous
|
|
193
|
+
// Same-page hash-links clicks on chrome recurse here from histroy popstate
|
|
198
194
|
if (detail.srcType !== 'init' && (_before(url.href, '#') === _before(init.referrer, '#') && (init.method || 'GET').toUpperCase() === 'GET')) {
|
|
199
195
|
return;
|
|
200
196
|
}
|
|
201
197
|
// ------------
|
|
198
|
+
if (this._abortController) {
|
|
199
|
+
this._abortController.abort();
|
|
200
|
+
}
|
|
201
|
+
this._abortController = new AbortController();
|
|
202
|
+
this._xRedirectCode = 200;
|
|
203
|
+
// ------------
|
|
202
204
|
if (['link', 'form'].includes(detail.srcType)) {
|
|
203
205
|
Observer.set(detail.src, 'active', true);
|
|
204
206
|
Observer.set(detail.submitter || {}, 'active', true);
|
|
@@ -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
|
});
|
|
@@ -80,8 +77,8 @@ export default class Worker {
|
|
|
80
77
|
if (!evt.request.url.startsWith('http')) return;
|
|
81
78
|
const deriveInit = req => [
|
|
82
79
|
'method', 'headers', 'body', 'mode', 'credentials', 'cache', 'redirect', 'referrer', 'integrity',
|
|
83
|
-
].reduce((init, prop) => ({ [prop]: req[prop], ...init }), {});
|
|
84
|
-
const requestInit = deriveInit(evt.request);
|
|
80
|
+
].reduce((init, prop) => ({ [prop]: prop === 'body' && !req.body ? req : req[prop], ...init }), {});
|
|
81
|
+
const requestInit = deriveInit(evt.request.clone());
|
|
85
82
|
evt.respondWith(this.go(evt.request.url, requestInit, { event: evt }));
|
|
86
83
|
});
|
|
87
84
|
|
|
@@ -110,9 +107,8 @@ export default class Worker {
|
|
|
110
107
|
init = { referrer: this.location.href, ...init };
|
|
111
108
|
// ------------
|
|
112
109
|
// The request object
|
|
113
|
-
let request = this.generateRequest(url.href, init);
|
|
114
|
-
if (detail.event
|
|
115
|
-
request = detail.event.request;
|
|
110
|
+
let request = await this.generateRequest(url.href, init);
|
|
111
|
+
if (detail.event) {
|
|
116
112
|
Object.defineProperty(detail.event, 'request', { value: request });
|
|
117
113
|
}
|
|
118
114
|
// The navigation event
|
|
@@ -130,14 +126,13 @@ export default class Worker {
|
|
|
130
126
|
} else {
|
|
131
127
|
response = await this.remoteFetch(httpEvent.request);
|
|
132
128
|
}
|
|
133
|
-
return response;
|
|
134
129
|
let finalResponse = this.handleResponse(httpEvent, response);
|
|
135
130
|
// Return value
|
|
136
131
|
return finalResponse;
|
|
137
132
|
}
|
|
138
133
|
|
|
139
134
|
// Generates request object
|
|
140
|
-
generateRequest(href, init) {
|
|
135
|
+
async generateRequest(href, init) {
|
|
141
136
|
// Now, the following is key:
|
|
142
137
|
// The browser likes to use "force-cache" for "navigate" requests
|
|
143
138
|
// when, for example, the back button was used.
|
|
@@ -146,7 +141,12 @@ export default class Worker {
|
|
|
146
141
|
if (init.mode === 'navigate' && init.cache === 'force-cache') {
|
|
147
142
|
init = { ...init, cache: 'default' };
|
|
148
143
|
}
|
|
149
|
-
|
|
144
|
+
if (init.method === 'POST' && init.body instanceof self.Request) {
|
|
145
|
+
init = { ...init, body: await init.body.text(), };
|
|
146
|
+
} else if (['GET', 'HEAD'].includes(init.method.toUpperCase()) && init.body) {
|
|
147
|
+
init = { ...init, body: null };
|
|
148
|
+
}
|
|
149
|
+
let request = new Request(href, init);
|
|
150
150
|
return request;
|
|
151
151
|
}
|
|
152
152
|
|
|
@@ -163,18 +163,19 @@ export default class Worker {
|
|
|
163
163
|
if (arguments.length > 1) {
|
|
164
164
|
request = this.generateRequest(request, ...args);
|
|
165
165
|
}
|
|
166
|
+
const matchUrl = (patterns, url) => _any((patterns || []).map(p => p.trim()).filter(p => p), p => urlPattern(p, self.origin).test(url));
|
|
166
167
|
const execFetch = () => {
|
|
167
|
-
if (
|
|
168
|
+
if (matchUrl(this.cx.params.cache_only_urls, request.url)) {
|
|
168
169
|
Observer.set(this.network, 'strategy', 'cache-only');
|
|
169
170
|
return this.cacheFetch(request, { networkFallback: false, cacheRefresh: false });
|
|
170
171
|
}
|
|
171
172
|
// network_only_urls
|
|
172
|
-
if (
|
|
173
|
+
if (matchUrl(this.cx.params.network_only_urls, request.url)) {
|
|
173
174
|
Observer.set(this.network, 'strategy', 'network-only');
|
|
174
175
|
return this.networkFetch(request, { cacheFallback: false, cacheRefresh: false });
|
|
175
176
|
}
|
|
176
177
|
// cache_first_urls
|
|
177
|
-
if (
|
|
178
|
+
if (matchUrl(this.cx.params.cache_first_urls, request.url)) {
|
|
178
179
|
Observer.set(this.network, 'strategy', 'cache-first');
|
|
179
180
|
return this.cacheFetch(request, { networkFallback: true, cacheRefresh: true });
|
|
180
181
|
}
|
|
@@ -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,30 +89,30 @@ 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
|
-
contentType === 'application/x-www-form-urlencoded' || contentType.startsWith('multipart/') || this.bodyAttrs.formData ? 'formData' : (
|
|
97
|
+
contentType === 'application/x-www-form-urlencoded' || contentType.startsWith('multipart/form-data') || this.bodyAttrs.formData ? 'formData' : (
|
|
98
98
|
contentType === 'text/plain' ? 'plain' : 'other'
|
|
99
99
|
)
|
|
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
|
}
|
|
@@ -62,6 +62,13 @@ const xRequest = (whatwagRequest, Headers, FormData, Blob) => class extends xHtt
|
|
|
62
62
|
return 'referrer' in this.attrs ? this.attrs.referrer : super.referrer;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
static compat(request, url = null) {
|
|
66
|
+
if (request instanceof whatwagRequest) {
|
|
67
|
+
return Object.setPrototypeOf(request, new this);
|
|
68
|
+
}
|
|
69
|
+
return new this(url, request);
|
|
70
|
+
}
|
|
71
|
+
|
|
65
72
|
};
|
|
66
73
|
|
|
67
74
|
export default xRequest;
|
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' } } );
|