@webqit/webflo 0.11.39 → 0.11.40
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 +2 -3
- package/src/Context.js +3 -3
- package/src/config-pi/deployment/Layout.js +0 -1
- package/src/runtime-pi/{RuntimeClient.js → Application.js} +2 -2
- package/src/runtime-pi/HttpEvent.js +106 -0
- package/src/runtime-pi/Router.js +2 -3
- package/src/runtime-pi/Runtime.js +3 -3
- package/src/runtime-pi/client/{RuntimeClient.js → Application.js} +12 -4
- package/src/runtime-pi/client/Router.js +4 -3
- package/src/runtime-pi/client/Runtime.js +37 -59
- package/src/runtime-pi/client/Url.js +3 -3
- package/src/runtime-pi/client/Workport.js +1 -1
- package/src/runtime-pi/client/{Storage.js → createStorage.js} +3 -3
- package/src/runtime-pi/client/generate.js +5 -3
- package/src/runtime-pi/client/index.js +4 -4
- package/src/runtime-pi/client/worker/{WorkerClient.js → Application.js} +12 -8
- package/src/runtime-pi/client/worker/{Worker.js → Runtime.js} +25 -27
- package/src/runtime-pi/client/worker/index.js +6 -6
- package/src/runtime-pi/server/{RuntimeClient.js → Application.js} +8 -8
- package/src/runtime-pi/server/Router.js +3 -2
- package/src/runtime-pi/server/Runtime.js +41 -98
- package/src/runtime-pi/server/index.js +4 -4
- package/src/runtime-pi/util-http.js +70 -0
- package/src/runtime-pi/util-url.js +147 -0
- package/src/runtime-pi/xFormData.js +10 -46
- package/src/runtime-pi/xHeaders.js +2 -11
- package/src/runtime-pi/xRequest.js +29 -42
- package/src/runtime-pi/xRequestHeaders.js +20 -23
- package/src/runtime-pi/xResponse.js +19 -15
- package/src/runtime-pi/xResponseHeaders.js +41 -43
- package/src/runtime-pi/xURL.js +71 -77
- package/src/runtime-pi/xfetch.js +15 -6
- package/src/runtime-pi/xxHttpMessage.js +102 -0
- package/src/runtime-pi/client/whatwag.js +0 -27
- package/src/runtime-pi/server/whatwag.js +0 -35
- package/src/runtime-pi/util.js +0 -162
- package/src/runtime-pi/xHttpEvent.js +0 -101
- package/src/runtime-pi/xHttpMessage.js +0 -171
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
* @imports
|
|
4
4
|
*/
|
|
5
5
|
import Router from '../Router.js';
|
|
6
|
-
import
|
|
6
|
+
import _Application from '../../Application.js';
|
|
7
7
|
|
|
8
|
-
export default class
|
|
8
|
+
export default class Application extends _Application {
|
|
9
9
|
|
|
10
10
|
// Returns router class
|
|
11
11
|
get Router() {
|
|
@@ -27,14 +27,18 @@ export default class RuntimeClient extends _WorkerClient {
|
|
|
27
27
|
// --------
|
|
28
28
|
// ROUTE FOR DATA
|
|
29
29
|
// --------
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
return router.route([httpEvent.request.method, 'default'], httpEvent, {}, async event => {
|
|
31
|
+
if (event !== httpEvent) {
|
|
32
|
+
// This was nexted()
|
|
33
|
+
if (!event.request.headers.has('Accept')) {
|
|
34
|
+
event.request.headers.set('Accept', 'application/json');
|
|
35
|
+
}
|
|
36
|
+
if (event.request.body && !event.request.headers.has('Content-Type')) {
|
|
37
|
+
event.request.headers.set('Content-Type', 'application/json');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
32
40
|
return remoteFetch(event.request);
|
|
33
41
|
}, remoteFetch);
|
|
34
|
-
if (!(response instanceof httpEvent.Response)) {
|
|
35
|
-
response = httpEvent.Response.compat(response);
|
|
36
|
-
}
|
|
37
|
-
return response;
|
|
38
42
|
};
|
|
39
43
|
return handle();
|
|
40
44
|
}
|
|
@@ -3,42 +3,38 @@
|
|
|
3
3
|
* @imports
|
|
4
4
|
*/
|
|
5
5
|
import { _any } from '@webqit/util/arr/index.js';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { Observer } from '@webqit/oohtml-ssr/apis.js';
|
|
7
|
+
import { pattern } from '../../util-url.js';
|
|
8
8
|
import Workport from './Workport.js';
|
|
9
|
-
import
|
|
9
|
+
import _Runtime from '../../Runtime.js';
|
|
10
|
+
import xRequest from "../../xRequest.js";
|
|
11
|
+
import xResponse from "../../xResponse.js";
|
|
12
|
+
import xfetch from '../../xfetch.js';
|
|
13
|
+
import HttpEvent from '../../HttpEvent.js';
|
|
10
14
|
|
|
11
15
|
export {
|
|
12
|
-
URL,
|
|
13
|
-
FormData,
|
|
14
|
-
ReadableStream,
|
|
15
|
-
RequestHeaders,
|
|
16
|
-
ResponseHeaders,
|
|
17
|
-
Request,
|
|
18
|
-
Response,
|
|
19
|
-
fetch,
|
|
20
16
|
HttpEvent,
|
|
21
17
|
Observer,
|
|
22
|
-
}
|
|
18
|
+
}
|
|
23
19
|
|
|
24
20
|
/**
|
|
25
21
|
* ---------------------------
|
|
26
|
-
* The
|
|
22
|
+
* The Runtime Initializer
|
|
27
23
|
* ---------------------------
|
|
28
24
|
*/
|
|
29
25
|
|
|
30
|
-
export default class
|
|
26
|
+
export default class Runtime extends _Runtime {
|
|
31
27
|
|
|
32
28
|
/**
|
|
33
29
|
* Runtime
|
|
34
30
|
*
|
|
35
31
|
* @param Object cx
|
|
36
|
-
* @param Function
|
|
32
|
+
* @param Function applicationInstance
|
|
37
33
|
*
|
|
38
34
|
* @return void
|
|
39
35
|
*/
|
|
40
|
-
constructor(cx,
|
|
41
|
-
super(cx,
|
|
36
|
+
constructor(cx, applicationInstance) {
|
|
37
|
+
super(cx, applicationInstance);
|
|
42
38
|
// ---------------
|
|
43
39
|
this.mockSessionStore = {};
|
|
44
40
|
// --------------
|
|
@@ -50,7 +46,7 @@ export default class Worker extends _Worker {
|
|
|
50
46
|
// Add files to cache
|
|
51
47
|
evt.waitUntil( self.caches.open(this.cx.params.cache_name).then(cache => {
|
|
52
48
|
if (this.cx.logger) { this.cx.logger.log('[ServiceWorker] Pre-caching resources.'); }
|
|
53
|
-
const cache_only_urls = (this.cx.params.cache_only_urls || []).map(c => c.trim()).filter(c => c && !
|
|
49
|
+
const cache_only_urls = (this.cx.params.cache_only_urls || []).map(c => c.trim()).filter(c => c && !pattern(c, self.origin).isPattern());
|
|
54
50
|
return cache.addAll(cache_only_urls);
|
|
55
51
|
}) );
|
|
56
52
|
}
|
|
@@ -93,7 +89,7 @@ export default class Worker extends _Worker {
|
|
|
93
89
|
event.respondWith((async (req, evt) => {
|
|
94
90
|
let requestingClient = await self.clients.get(event.clientId);
|
|
95
91
|
this.workport.setCurrentClient(requestingClient);
|
|
96
|
-
const [ url, requestInit ] = await
|
|
92
|
+
const [ url, requestInit ] = await xRequest.rip(req);
|
|
97
93
|
// Now, the following is key:
|
|
98
94
|
// The browser likes to use "force-cache" for "navigate" requests, when, e.g: re-entering your site with the back button
|
|
99
95
|
// Problem here, force-cache forces out JSON not HTML as per webflo's design.
|
|
@@ -113,10 +109,10 @@ export default class Worker extends _Worker {
|
|
|
113
109
|
// -------------
|
|
114
110
|
// Initialize
|
|
115
111
|
(async () => {
|
|
116
|
-
if (!this.
|
|
112
|
+
if (!this.app.init) return;
|
|
117
113
|
const request = this.generateRequest('/');
|
|
118
114
|
const httpEvent = new HttpEvent(request, { srcType: 'initialization' }, (id = null, persistent = false) => this.getSession(httpEvent, id, persistent));
|
|
119
|
-
await this.
|
|
115
|
+
await this.app.init(httpEvent, ( ...args ) => this.remoteFetch( ...args ));
|
|
120
116
|
})();
|
|
121
117
|
|
|
122
118
|
}
|
|
@@ -132,11 +128,12 @@ export default class Worker extends _Worker {
|
|
|
132
128
|
*/
|
|
133
129
|
async go(url, init = {}, detail = {}) {
|
|
134
130
|
// ------------
|
|
135
|
-
url = typeof url === 'string' ? new URL(url) : url;
|
|
131
|
+
url = typeof url === 'string' ? new URL(url, self.location.origin) : url;
|
|
136
132
|
init = { referrer: this.location.href, ...init };
|
|
137
133
|
// ------------
|
|
138
134
|
// The request object
|
|
139
135
|
const request = await this.generateRequest(url.href, init);
|
|
136
|
+
|
|
140
137
|
if (detail.event) {
|
|
141
138
|
Object.defineProperty(detail.event, 'request', { value: request });
|
|
142
139
|
}
|
|
@@ -151,7 +148,7 @@ export default class Worker extends _Worker {
|
|
|
151
148
|
// Response
|
|
152
149
|
let response;
|
|
153
150
|
if (httpEvent.request.url.startsWith(self.origin)/* && httpEvent.request.mode === 'navigate'*/) {
|
|
154
|
-
response = await this.
|
|
151
|
+
response = await this.app.handle(httpEvent, ( ...args ) => this.remoteFetch( ...args ));
|
|
155
152
|
} else {
|
|
156
153
|
response = await this.remoteFetch(httpEvent.request);
|
|
157
154
|
}
|
|
@@ -162,7 +159,7 @@ export default class Worker extends _Worker {
|
|
|
162
159
|
|
|
163
160
|
// Generates request object
|
|
164
161
|
generateRequest(href, init = {}) {
|
|
165
|
-
const request = new
|
|
162
|
+
const request = new xRequest(href, init);
|
|
166
163
|
return request;
|
|
167
164
|
}
|
|
168
165
|
|
|
@@ -179,7 +176,7 @@ export default class Worker extends _Worker {
|
|
|
179
176
|
if (arguments.length > 1) {
|
|
180
177
|
request = this.generateRequest(request, ...args);
|
|
181
178
|
}
|
|
182
|
-
const matchUrl = (patterns, url) => _any((patterns || []).map(p => p.trim()).filter(p => p), p =>
|
|
179
|
+
const matchUrl = (patterns, url) => _any((patterns || []).map(p => p.trim()).filter(p => p), p => pattern(p, self.origin).test(url));
|
|
183
180
|
const execFetch = () => {
|
|
184
181
|
// network_first_urls
|
|
185
182
|
if (!this.cx.params.default_fetching_strategy || this.cx.params.default_fetching_strategy === 'network-first' || matchUrl(this.cx.params.network_first_urls, request.url)) {
|
|
@@ -206,7 +203,7 @@ export default class Worker extends _Worker {
|
|
|
206
203
|
// This catch() is NOT intended to handle failure of the fetch
|
|
207
204
|
response.catch(e => Observer.set(this.network, 'error', e.message));
|
|
208
205
|
// Return xResponse
|
|
209
|
-
return response.then(_response =>
|
|
206
|
+
return response.then(_response => xResponse.compat(_response));
|
|
210
207
|
}
|
|
211
208
|
|
|
212
209
|
// Caching strategy: network_first
|
|
@@ -265,7 +262,8 @@ export default class Worker extends _Worker {
|
|
|
265
262
|
|
|
266
263
|
// Handles response object
|
|
267
264
|
handleResponse(e, response) {
|
|
268
|
-
if (
|
|
265
|
+
if (typeof response === 'undefined') { response = new xResponse(undefined, { status: 404 }); }
|
|
266
|
+
else if (!(response instanceof xResponse)) { response = xResponse.compat(response); }
|
|
269
267
|
return response;
|
|
270
268
|
}
|
|
271
269
|
|
|
@@ -3,19 +3,19 @@
|
|
|
3
3
|
* @imports
|
|
4
4
|
*/
|
|
5
5
|
import Context from './Context.js';
|
|
6
|
-
import
|
|
7
|
-
import
|
|
6
|
+
import Application from './Application.js';
|
|
7
|
+
import Runtime from './Runtime.js';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* @start
|
|
11
11
|
*/
|
|
12
|
-
export async function start(
|
|
12
|
+
export async function start(applicationInstance = null) {
|
|
13
13
|
const cx = this || {};
|
|
14
|
-
const
|
|
15
|
-
return new
|
|
14
|
+
const defaultApplicationInstance = _cx => new Application(_cx);
|
|
15
|
+
return new Runtime(Context.create(cx), applicationInstance || defaultApplicationInstance);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* @APIS
|
|
20
20
|
*/
|
|
21
|
-
export * as APIS from './
|
|
21
|
+
export * as APIS from './Runtime.js';
|
|
@@ -6,12 +6,12 @@ import Fs from 'fs';
|
|
|
6
6
|
import Path from 'path';
|
|
7
7
|
import QueryString from 'querystring';
|
|
8
8
|
import Router from './Router.js';
|
|
9
|
-
import
|
|
9
|
+
import _Application from '../Application.js';
|
|
10
10
|
|
|
11
|
-
export default class
|
|
11
|
+
export default class Application extends _Application {
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
14
|
+
* Application
|
|
15
15
|
*
|
|
16
16
|
* @param Context cx
|
|
17
17
|
*/
|
|
@@ -40,8 +40,7 @@ export default class RuntimeClient extends _RuntimeClient {
|
|
|
40
40
|
// --------
|
|
41
41
|
// ROUTE FOR DATA
|
|
42
42
|
// --------
|
|
43
|
-
|
|
44
|
-
let response = await router.route([httpMethodName, 'default'], httpEvent, {}, async event => {
|
|
43
|
+
let response = await router.route([httpEvent.request.method, 'default'], httpEvent, {}, async event => {
|
|
45
44
|
return router.file(event);
|
|
46
45
|
}, remoteFetch);
|
|
47
46
|
if (!(response instanceof httpEvent.Response)) {
|
|
@@ -50,14 +49,15 @@ export default class RuntimeClient extends _RuntimeClient {
|
|
|
50
49
|
// --------
|
|
51
50
|
// Rendering
|
|
52
51
|
// --------
|
|
53
|
-
if (response.ok && response.
|
|
52
|
+
if (response.ok && response.meta.type === 'json' && typeof response.meta.body === 'object' && response.meta.body && httpEvent.request.headers.accept.match('text/html')) {
|
|
54
53
|
let rendering = await this.render(httpEvent, router, response);
|
|
55
54
|
if (typeof rendering !== 'string' && !(typeof rendering === 'object' && rendering && typeof rendering.toString === 'function')) {
|
|
56
55
|
throw new Error('render() must return a string response or an object that implements toString()..');
|
|
57
56
|
}
|
|
58
|
-
|
|
57
|
+
rendering = rendering.toString();
|
|
58
|
+
response = new httpEvent.Response(rendering, {
|
|
59
|
+
headers: { ...response.headers.json(), contentType: 'text/html', contentLength: (new Blob([rendering]).size) },
|
|
59
60
|
status: response.status,
|
|
60
|
-
headers: { ...response.headers.json(), contentType: 'text/html' },
|
|
61
61
|
});
|
|
62
62
|
}
|
|
63
63
|
|
|
@@ -17,6 +17,7 @@ import _Router from '../Router.js';
|
|
|
17
17
|
export default class Router extends _Router {
|
|
18
18
|
|
|
19
19
|
async readTick(thisTick) {
|
|
20
|
+
thisTick = { ...thisTick };
|
|
20
21
|
if (thisTick.trail) {
|
|
21
22
|
thisTick.currentSegment = thisTick.destination[thisTick.trail.length];
|
|
22
23
|
thisTick.currentSegmentOnFile = [ thisTick.currentSegment, '-' ].reduce((_segmentOnFile, _seg) => {
|
|
@@ -27,8 +28,8 @@ export default class Router extends _Router {
|
|
|
27
28
|
Fs.existsSync(Path.join(this.cx.CWD, this.cx.layout.SERVER_DIR, _currentPath)) ? { seg: _seg, dirExists: true } : _segmentOnFile
|
|
28
29
|
);
|
|
29
30
|
}, { seg: null });
|
|
30
|
-
thisTick.trail.
|
|
31
|
-
thisTick.trailOnFile.
|
|
31
|
+
thisTick.trail = thisTick.trail.concat(thisTick.currentSegment);
|
|
32
|
+
thisTick.trailOnFile = thisTick.trailOnFile.concat(thisTick.currentSegmentOnFile.seg);
|
|
32
33
|
thisTick.exports = thisTick.currentSegmentOnFile.index ? await import(Url.pathToFileURL(thisTick.currentSegmentOnFile.index)) : undefined;
|
|
33
34
|
} else {
|
|
34
35
|
thisTick.trail = [];
|
|
@@ -6,44 +6,22 @@ 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';
|
|
10
9
|
import Sessions from 'client-sessions';
|
|
11
10
|
import { Observer } from '@webqit/oohtml-ssr/apis.js';
|
|
12
11
|
import { _each } from '@webqit/util/obj/index.js';
|
|
13
12
|
import { _isEmpty } from '@webqit/util/js/index.js';
|
|
14
13
|
import { _from as _arrFrom, _any } from '@webqit/util/arr/index.js';
|
|
15
14
|
import { slice as _streamSlice } from 'stream-slice';
|
|
16
|
-
import {
|
|
17
|
-
import
|
|
18
|
-
import xURL from '../xURL.js';
|
|
19
|
-
import xFormData from "../xFormData.js";
|
|
20
|
-
import xRequestHeaders from "../xRequestHeaders.js";
|
|
21
|
-
import xResponseHeaders from "../xResponseHeaders.js";
|
|
15
|
+
import { Readable as _ReadableStream } from 'stream';
|
|
16
|
+
import { pattern } from '../util-url.js';
|
|
22
17
|
import xRequest from "../xRequest.js";
|
|
23
18
|
import xResponse from "../xResponse.js";
|
|
24
19
|
import xfetch from '../xfetch.js';
|
|
25
|
-
import
|
|
20
|
+
import HttpEvent from '../HttpEvent.js';
|
|
26
21
|
import _Runtime from '../Runtime.js';
|
|
27
22
|
|
|
28
|
-
const URL = xURL(whatwag.URL);
|
|
29
|
-
const FormData = xFormData(whatwag.FormData);
|
|
30
|
-
const ReadableStream = whatwag.ReadableStream;
|
|
31
|
-
const RequestHeaders = xRequestHeaders(whatwag.Headers);
|
|
32
|
-
const ResponseHeaders = xResponseHeaders(whatwag.Headers);
|
|
33
|
-
const Request = xRequest(whatwag.Request, RequestHeaders, FormData, whatwag.Blob);
|
|
34
|
-
const Response = xResponse(whatwag.Response, ResponseHeaders, FormData, whatwag.Blob);
|
|
35
|
-
const fetch = xfetch(whatwag.fetch);
|
|
36
|
-
const HttpEvent = xHttpEvent(Request, Response, URL);
|
|
37
|
-
|
|
38
23
|
export {
|
|
39
|
-
|
|
40
|
-
FormData,
|
|
41
|
-
ReadableStream,
|
|
42
|
-
RequestHeaders,
|
|
43
|
-
ResponseHeaders,
|
|
44
|
-
Request,
|
|
45
|
-
Response,
|
|
46
|
-
fetch,
|
|
24
|
+
//fetch,
|
|
47
25
|
HttpEvent,
|
|
48
26
|
Observer,
|
|
49
27
|
}
|
|
@@ -54,18 +32,18 @@ export default class Runtime extends _Runtime {
|
|
|
54
32
|
* Runtime
|
|
55
33
|
*
|
|
56
34
|
* @param Object cx
|
|
57
|
-
* @param Function
|
|
35
|
+
* @param Function applicationInstance
|
|
58
36
|
*
|
|
59
37
|
* @return void
|
|
60
38
|
*/
|
|
61
|
-
constructor(cx,
|
|
62
|
-
super(cx,
|
|
39
|
+
constructor(cx, applicationInstance) {
|
|
40
|
+
super(cx, applicationInstance);
|
|
63
41
|
// ---------------
|
|
64
42
|
this.ready = (async () => {
|
|
65
43
|
// ---------------
|
|
66
44
|
const resolveContextObj = async (cx, force = false) => {
|
|
67
|
-
if (_isEmpty(cx.server) || force) { cx.server = await (new cx.config.runtime.Server(cx)).read(); }
|
|
68
45
|
if (_isEmpty(cx.layout) || force) { cx.layout = await (new cx.config.deployment.Layout(cx)).read(); }
|
|
46
|
+
if (_isEmpty(cx.server) || force) { cx.server = await (new cx.config.runtime.Server(cx)).read(); }
|
|
69
47
|
if (_isEmpty(cx.env) || force) { cx.env = await (new cx.config.deployment.Env(cx)).read(); }
|
|
70
48
|
};
|
|
71
49
|
await resolveContextObj(this.cx);
|
|
@@ -88,6 +66,7 @@ export default class Runtime extends _Runtime {
|
|
|
88
66
|
if (vhost.path) {
|
|
89
67
|
cx = this.cx.constructor.create(this.cx, Path.join(this.cx.CWD, vhost.path));
|
|
90
68
|
await resolveContextObj(cx, true);
|
|
69
|
+
cx.dict.key = true;
|
|
91
70
|
// From the server that's most likely to be active
|
|
92
71
|
port || (port = cx.server.https.port || cx.server.port);
|
|
93
72
|
// The domain list that corresponds to the specified resolved port
|
|
@@ -145,6 +124,7 @@ export default class Runtime extends _Runtime {
|
|
|
145
124
|
}
|
|
146
125
|
// ---------------
|
|
147
126
|
const handleRequest = async (proto, request, response) => {
|
|
127
|
+
request[Symbol.toStringTag] = 'ReadableStream';
|
|
148
128
|
const [ fullUrl, requestInit ] = await this.parseNodeRequest(proto, request);
|
|
149
129
|
let clientResponse = await this.go(fullUrl, requestInit, { request, response });
|
|
150
130
|
if (response.headersSent) return;
|
|
@@ -158,9 +138,12 @@ export default class Runtime extends _Runtime {
|
|
|
158
138
|
if (clientResponse.headers.location) {
|
|
159
139
|
return response.end();
|
|
160
140
|
}
|
|
161
|
-
if ((clientResponse.body instanceof
|
|
141
|
+
if ((clientResponse.body instanceof _ReadableStream)) {
|
|
162
142
|
return clientResponse.body.pipe(response);
|
|
163
143
|
}
|
|
144
|
+
if ((clientResponse.body instanceof ReadableStream)) {
|
|
145
|
+
return _ReadableStream.from(clientResponse.body).pipe(response);
|
|
146
|
+
}
|
|
164
147
|
let body = clientResponse.body;
|
|
165
148
|
if (clientResponse.headers.contentType === 'application/json') {
|
|
166
149
|
body += '';
|
|
@@ -195,12 +178,12 @@ export default class Runtime extends _Runtime {
|
|
|
195
178
|
}
|
|
196
179
|
this.cx.logger.info(``);
|
|
197
180
|
}
|
|
198
|
-
if (this.
|
|
199
|
-
const request = this.generateRequest('/');
|
|
181
|
+
if (this.app && this.app.init) {
|
|
182
|
+
const request = this.generateRequest('http://localhost/');
|
|
200
183
|
const httpEvent = new HttpEvent(request, { srcType: 'initialization' }, (id = 'session', options = { duration: 60 * 60 * 24, activeDuration: 60 * 60 * 24 }, callback = null) => {
|
|
201
184
|
return this.getSession(this.cx, httpEvent, id, options, callback);
|
|
202
185
|
});
|
|
203
|
-
await this.
|
|
186
|
+
await this.app.init(httpEvent, ( ...args ) => this.remoteFetch( ...args ));
|
|
204
187
|
}
|
|
205
188
|
})();
|
|
206
189
|
// ---------------
|
|
@@ -220,55 +203,14 @@ export default class Runtime extends _Runtime {
|
|
|
220
203
|
* @return Array
|
|
221
204
|
*/
|
|
222
205
|
async parseNodeRequest(proto, request) {
|
|
223
|
-
let url = request.url;
|
|
224
206
|
// Detected when using manual proxy setting in a browser
|
|
225
|
-
if (url.startsWith(`http://${ request.headers.host }`) || url.startsWith(`https://${ request.headers.host }`)) {
|
|
226
|
-
url = url.split(request.headers.host)[1];
|
|
207
|
+
if (request.url.startsWith(`http://${ request.headers.host }`) || request.url.startsWith(`https://${ request.headers.host }`)) {
|
|
208
|
+
request.url = request.url.split(request.headers.host)[1];
|
|
227
209
|
}
|
|
228
|
-
const fullUrl = proto + '://' + request.headers.host + url;
|
|
210
|
+
const fullUrl = proto + '://' + request.headers.host + request.url;
|
|
229
211
|
const requestInit = { method: request.method, headers: request.headers };
|
|
230
|
-
if (
|
|
231
|
-
requestInit.body =
|
|
232
|
-
var formidable = new Formidable.IncomingForm({ multiples: true, allowEmptyFiles: true, keepExtensions: true });
|
|
233
|
-
formidable.parse(request, (error, fields, files) => {
|
|
234
|
-
if (error) { return reject(error); }
|
|
235
|
-
if (request.headers['content-type'] === 'application/json') {
|
|
236
|
-
return resolve(fields);
|
|
237
|
-
}
|
|
238
|
-
const formData = new FormData;
|
|
239
|
-
Object.keys(fields).forEach(name => {
|
|
240
|
-
if (Array.isArray(fields[name])) {
|
|
241
|
-
const values = Array.isArray(fields[name][0])
|
|
242
|
-
? fields[name][0]/* bugly a nested array when there are actually more than entry */
|
|
243
|
-
: fields[name];
|
|
244
|
-
values.forEach(value => {
|
|
245
|
-
formData.append(!name.endsWith(']') ? name + '[]' : name, value);
|
|
246
|
-
});
|
|
247
|
-
} else {
|
|
248
|
-
formData.append(name, fields[name]);
|
|
249
|
-
}
|
|
250
|
-
});
|
|
251
|
-
Object.keys(files).forEach(name => {
|
|
252
|
-
const fileCompat = file => {
|
|
253
|
-
// IMPORTANT
|
|
254
|
-
// Path up the "formidable" file in a way that "formdata-node"
|
|
255
|
-
// to can translate it into its own file instance
|
|
256
|
-
file[Symbol.toStringTag] = 'File';
|
|
257
|
-
file.stream = () => Fs.createReadStream(file.path);
|
|
258
|
-
// Done pathcing
|
|
259
|
-
return file;
|
|
260
|
-
}
|
|
261
|
-
if (Array.isArray(files[name])) {
|
|
262
|
-
files[name].forEach(value => {
|
|
263
|
-
formData.append(name, fileCompat(value));
|
|
264
|
-
});
|
|
265
|
-
} else {
|
|
266
|
-
formData.append(name, fileCompat(files[name]));
|
|
267
|
-
}
|
|
268
|
-
});
|
|
269
|
-
resolve(formData);
|
|
270
|
-
});
|
|
271
|
-
});
|
|
212
|
+
if (!['GET', 'HEAD'].includes(request.method)) {
|
|
213
|
+
requestInit.body = request;
|
|
272
214
|
}
|
|
273
215
|
return [ fullUrl, requestInit ];
|
|
274
216
|
}
|
|
@@ -286,7 +228,7 @@ export default class Runtime extends _Runtime {
|
|
|
286
228
|
await this.ready;
|
|
287
229
|
|
|
288
230
|
// ------------
|
|
289
|
-
url = typeof url === 'string' ? new
|
|
231
|
+
url = typeof url === 'string' ? new URL(url) : url;
|
|
290
232
|
init = { referrer: this.location.href, ...init };
|
|
291
233
|
// ------------
|
|
292
234
|
const hosts = [];
|
|
@@ -309,10 +251,10 @@ export default class Runtime extends _Runtime {
|
|
|
309
251
|
exit = { status: 302, headers: { Location: ( url.hostname = `www.${ url.hostname }`, url.href ) } };
|
|
310
252
|
} else if (this.cx.config.runtime.server.Redirects) {
|
|
311
253
|
exit = ((await (new this.cx.config.runtime.server.Redirects(this.cx)).read()).entries || []).reduce((_rdr, entry) => {
|
|
312
|
-
return _rdr || ((_rdr =
|
|
254
|
+
return _rdr || ((_rdr = pattern(entry.from, url.origin).exec(url.href)) && { status: entry.code || 302, headers: { Location: _rdr.render(entry.to) } });
|
|
313
255
|
}, null);
|
|
314
256
|
}
|
|
315
|
-
if (exit) { return new
|
|
257
|
+
if (exit) { return new xResponse(null, exit); }
|
|
316
258
|
// ------------
|
|
317
259
|
|
|
318
260
|
// ------------
|
|
@@ -323,7 +265,7 @@ export default class Runtime extends _Runtime {
|
|
|
323
265
|
// ------------
|
|
324
266
|
// Automatically-added headers
|
|
325
267
|
const autoHeaders = this.cx.config.runtime.server.Headers
|
|
326
|
-
? ((await (new this.cx.config.runtime.server.Headers(this.cx)).read()).entries || []).filter(entry =>
|
|
268
|
+
? ((await (new this.cx.config.runtime.server.Headers(this.cx)).read()).entries || []).filter(entry => pattern(entry.url, url.origin).exec(url.href))
|
|
327
269
|
: [];
|
|
328
270
|
// The request object
|
|
329
271
|
const request = this.generateRequest(url.href, init, autoHeaders.filter(header => header.type === 'request'));
|
|
@@ -334,10 +276,10 @@ export default class Runtime extends _Runtime {
|
|
|
334
276
|
// Response
|
|
335
277
|
let response, finalResponse;
|
|
336
278
|
try {
|
|
337
|
-
response = await this.
|
|
279
|
+
response = await this.app.handle(httpEvent, ( ...args ) => this.remoteFetch( ...args ));
|
|
338
280
|
finalResponse = await this.handleResponse(this.cx, httpEvent, response, autoHeaders.filter(header => header.type === 'response'));
|
|
339
281
|
} catch(e) {
|
|
340
|
-
finalResponse = new
|
|
282
|
+
finalResponse = new xResponse(null, { status: 500, statusText: e.message });
|
|
341
283
|
console.error(e);
|
|
342
284
|
}
|
|
343
285
|
// Logging
|
|
@@ -353,19 +295,20 @@ export default class Runtime extends _Runtime {
|
|
|
353
295
|
// Fetch from proxied host
|
|
354
296
|
async proxyGo(vhost, url, init) {
|
|
355
297
|
// ---------
|
|
356
|
-
const url2 = new
|
|
298
|
+
const url2 = new URL(url);
|
|
357
299
|
url2.port = vhost.port;
|
|
358
300
|
if (vhost.proto) { url2.protocol = vhost.proto; }
|
|
359
301
|
// ---------
|
|
360
|
-
const init2 = { ...init,
|
|
302
|
+
const init2 = { ...init, decompress: false/* honoured in xfetch() */ };
|
|
361
303
|
if (!init2.headers) init2.headers = {};
|
|
362
304
|
init2.headers.host = url2.host;
|
|
305
|
+
delete init2.headers.connection;
|
|
363
306
|
// ---------
|
|
364
307
|
let response;
|
|
365
308
|
try {
|
|
366
309
|
response = await this.remoteFetch(url2, init2);
|
|
367
310
|
} catch(e) {
|
|
368
|
-
response = new
|
|
311
|
+
response = new xResponse(null, { status: 500, statusText: e.message });
|
|
369
312
|
console.error(e);
|
|
370
313
|
}
|
|
371
314
|
if (this.cx.logger) {
|
|
@@ -378,7 +321,7 @@ export default class Runtime extends _Runtime {
|
|
|
378
321
|
|
|
379
322
|
// Generates request object
|
|
380
323
|
generateRequest(href, init = {}, autoHeaders = []) {
|
|
381
|
-
const request = new
|
|
324
|
+
const request = new xRequest(href, init);
|
|
382
325
|
this._autoHeaders(request.headers, autoHeaders);
|
|
383
326
|
return request;
|
|
384
327
|
}
|
|
@@ -422,24 +365,24 @@ export default class Runtime extends _Runtime {
|
|
|
422
365
|
let href = request;
|
|
423
366
|
if (request instanceof Request) {
|
|
424
367
|
href = request.url;
|
|
425
|
-
} else if (request instanceof
|
|
368
|
+
} else if (request instanceof URL) {
|
|
426
369
|
href = request.href;
|
|
427
370
|
}
|
|
428
371
|
Observer.set(this.network, 'remote', href);
|
|
429
|
-
const _response =
|
|
372
|
+
const _response = xfetch(request, ...args);
|
|
430
373
|
// This catch() is NOT intended to handle failure of the fetch
|
|
431
374
|
_response.catch(e => Observer.set(this.network, 'error', e.message));
|
|
432
375
|
// Save a reference to this
|
|
433
376
|
return _response.then(async response => {
|
|
434
377
|
// Stop loading status
|
|
435
378
|
Observer.set(this.network, 'remote', false);
|
|
436
|
-
return new
|
|
379
|
+
return new xResponse(response);
|
|
437
380
|
});
|
|
438
381
|
}
|
|
439
382
|
|
|
440
383
|
// Handles response object
|
|
441
384
|
async handleResponse(cx, e, response, autoHeaders = []) {
|
|
442
|
-
if (!(response instanceof
|
|
385
|
+
if (!(response instanceof xResponse)) { response = new xResponse(response); }
|
|
443
386
|
Observer.set(this.network, 'remote', false);
|
|
444
387
|
Observer.set(this.network, 'error', null);
|
|
445
388
|
|
|
@@ -461,7 +404,7 @@ export default class Runtime extends _Runtime {
|
|
|
461
404
|
if (response.headers.location) {
|
|
462
405
|
const xRedirectPolicy = e.request.headers.get('X-Redirect-Policy');
|
|
463
406
|
const xRedirectCode = e.request.headers.get('X-Redirect-Code') || 300;
|
|
464
|
-
const destinationUrl = new
|
|
407
|
+
const destinationUrl = new URL(response.headers.location, e.url.origin);
|
|
465
408
|
const isSameOriginRedirect = destinationUrl.origin === e.url.origin;
|
|
466
409
|
let isSameSpaRedirect, sparootsFile = Path.join(cx.CWD, cx.layout.PUBLIC_DIR, 'sparoots.json');
|
|
467
410
|
if (isSameOriginRedirect && xRedirectPolicy === 'manual-when-cross-spa' && Fs.existsSync(sparootsFile)) {
|
|
@@ -483,7 +426,7 @@ export default class Runtime extends _Runtime {
|
|
|
483
426
|
|
|
484
427
|
// ----------------
|
|
485
428
|
// 404
|
|
486
|
-
if (response.
|
|
429
|
+
if (response.meta.body === undefined || response.meta.body === null) {
|
|
487
430
|
response.attrs.status = response.status !== 200 ? response.status : 404;
|
|
488
431
|
response.attrs.statusText = `${e.request.url} not found!`;
|
|
489
432
|
return response;
|
|
@@ -507,7 +450,7 @@ export default class Runtime extends _Runtime {
|
|
|
507
450
|
// Body
|
|
508
451
|
let rangeRequest, body = response.body;
|
|
509
452
|
if ((rangeRequest = e.request.headers.range) && !response.headers.get('Content-Range')
|
|
510
|
-
&& ((body instanceof ReadableStream) || (ArrayBuffer.isView(body) && (body =
|
|
453
|
+
&& ((body instanceof ReadableStream) || (ArrayBuffer.isView(body) && (body = _ReadableStream.from(body))))) {
|
|
511
454
|
// ...in partials
|
|
512
455
|
const totalLength = response.headers.contentLength || 0;
|
|
513
456
|
const ranges = await rangeRequest.reduce(async (_ranges, range) => {
|
|
@@ -540,7 +483,7 @@ export default class Runtime extends _Runtime {
|
|
|
540
483
|
});
|
|
541
484
|
} else {
|
|
542
485
|
// TODO: of ranges.parts is more than one, return multipart/byteranges
|
|
543
|
-
response = new
|
|
486
|
+
response = new xResponse(ranges.parts[0].body, {
|
|
544
487
|
status: 206,
|
|
545
488
|
statusText: response.statusText,
|
|
546
489
|
headers: response.headers,
|
|
@@ -3,16 +3,16 @@
|
|
|
3
3
|
* @imports
|
|
4
4
|
*/
|
|
5
5
|
import Context from './Context.js';
|
|
6
|
-
import
|
|
6
|
+
import Application from './Application.js';
|
|
7
7
|
import Runtime from './Runtime.js';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* @start
|
|
11
11
|
*/
|
|
12
|
-
export async function start(
|
|
12
|
+
export async function start(applicationInstance = null) {
|
|
13
13
|
const cx = this || {};
|
|
14
|
-
const
|
|
15
|
-
return new Runtime(Context.create(cx),
|
|
14
|
+
const defaultApplicationInstance = _cx => new Application(_cx);
|
|
15
|
+
return new Runtime(Context.create(cx), applicationInstance || defaultApplicationInstance);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
/**
|