@webqit/webflo 0.11.52 → 0.11.53-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 +1 -1
- package/src/runtime-pi/client/Runtime.js +5 -3
- package/src/runtime-pi/client/worker/Runtime.js +8 -8
- package/src/runtime-pi/server/Runtime.js +15 -7
- package/src/runtime-pi/util-http.js +50 -34
- package/src/runtime-pi/xFormData.js +2 -2
- package/src/runtime-pi/xRequest.js +7 -47
- package/src/runtime-pi/xResponse.js +7 -48
- package/src/runtime-pi/xfetch.js +2 -2
package/package.json
CHANGED
|
@@ -202,14 +202,16 @@ export default class Runtime extends _Runtime {
|
|
|
202
202
|
* Performs a request.
|
|
203
203
|
*
|
|
204
204
|
* @param object|string href
|
|
205
|
-
* @param object
|
|
205
|
+
* @param object|Request init
|
|
206
206
|
* @param object src
|
|
207
207
|
*
|
|
208
208
|
* @return Response
|
|
209
209
|
*/
|
|
210
210
|
async go(url, init = {}, detail = {}) {
|
|
211
211
|
url = typeof url === 'string' ? new URL(url, this.location.origin) : url;
|
|
212
|
-
|
|
212
|
+
if (!(init instanceof Request) && !init.referrer) {
|
|
213
|
+
init = { referrer: this.location.href, ...init };
|
|
214
|
+
}
|
|
213
215
|
// ------------
|
|
214
216
|
// Put his forward before instantiating a request and aborting previous
|
|
215
217
|
// Same-page hash-links clicks on chrome recurse here from histroy popstate
|
|
@@ -226,7 +228,7 @@ export default class Runtime extends _Runtime {
|
|
|
226
228
|
// States
|
|
227
229
|
// ------------
|
|
228
230
|
Observer.set(this.network, 'error', null);
|
|
229
|
-
Observer.set(this.network, 'requesting', {
|
|
231
|
+
Observer.set(this.network, 'requesting', { init, ...detail });
|
|
230
232
|
if (['link', 'form'].includes(detail.srcType)) {
|
|
231
233
|
detail.src.state && (detail.src.state.active = true);
|
|
232
234
|
detail.submitter && detail.submitter.state && (detail.submitter.state.active = true);
|
|
@@ -89,15 +89,14 @@ export default class Runtime extends _Runtime {
|
|
|
89
89
|
event.respondWith((async (req, evt) => {
|
|
90
90
|
let requestingClient = await self.clients.get(event.clientId);
|
|
91
91
|
this.workport.setCurrentClient(requestingClient);
|
|
92
|
-
const [ url, requestInit ] = await xRequest.rip(req);
|
|
93
92
|
// Now, the following is key:
|
|
94
93
|
// The browser likes to use "force-cache" for "navigate" requests, when, e.g: re-entering your site with the back button
|
|
95
94
|
// Problem here, force-cache forces out JSON not HTML as per webflo's design.
|
|
96
95
|
// So, we detect this scenerio and avoid it.
|
|
97
96
|
if (req.cache === 'force-cache'/* && req.mode === 'navigate' - even webflo client init call also comes with that... needs investigation */) {
|
|
98
|
-
|
|
97
|
+
req = new Request(req, {cache: 'default'});
|
|
99
98
|
}
|
|
100
|
-
return this.go(url,
|
|
99
|
+
return this.go(req.url, req, { event: evt });
|
|
101
100
|
})(event.request, event));
|
|
102
101
|
});
|
|
103
102
|
|
|
@@ -121,7 +120,7 @@ export default class Runtime extends _Runtime {
|
|
|
121
120
|
* Performs a request.
|
|
122
121
|
*
|
|
123
122
|
* @param object|string url
|
|
124
|
-
* @param object
|
|
123
|
+
* @param object|Request init
|
|
125
124
|
* @param object detail
|
|
126
125
|
*
|
|
127
126
|
* @return Response
|
|
@@ -129,11 +128,12 @@ export default class Runtime extends _Runtime {
|
|
|
129
128
|
async go(url, init = {}, detail = {}) {
|
|
130
129
|
// ------------
|
|
131
130
|
url = typeof url === 'string' ? new URL(url, self.location.origin) : url;
|
|
132
|
-
init
|
|
131
|
+
if (!(init instanceof Request) && !init.referrer) {
|
|
132
|
+
init = { referrer: this.location.href, ...init };
|
|
133
|
+
}
|
|
133
134
|
// ------------
|
|
134
135
|
// The request object
|
|
135
|
-
const request =
|
|
136
|
-
|
|
136
|
+
const request = this.generateRequest(url.href, init);
|
|
137
137
|
if (detail.event) {
|
|
138
138
|
Object.defineProperty(detail.event, 'request', { value: request });
|
|
139
139
|
}
|
|
@@ -152,7 +152,7 @@ export default class Runtime extends _Runtime {
|
|
|
152
152
|
} else {
|
|
153
153
|
response = await this.remoteFetch(httpEvent.request);
|
|
154
154
|
}
|
|
155
|
-
const finalResponse = this.handleResponse(httpEvent, response);
|
|
155
|
+
const finalResponse = await this.handleResponse(httpEvent, response);
|
|
156
156
|
// Return value
|
|
157
157
|
return finalResponse;
|
|
158
158
|
}
|
|
@@ -219,7 +219,7 @@ export default class Runtime extends _Runtime {
|
|
|
219
219
|
* Performs a request.
|
|
220
220
|
*
|
|
221
221
|
* @param object|string url
|
|
222
|
-
* @param object
|
|
222
|
+
* @param object|Request init
|
|
223
223
|
* @param object detail
|
|
224
224
|
*
|
|
225
225
|
* @return Response
|
|
@@ -229,7 +229,9 @@ export default class Runtime extends _Runtime {
|
|
|
229
229
|
|
|
230
230
|
// ------------
|
|
231
231
|
url = typeof url === 'string' ? new URL(url) : url;
|
|
232
|
-
init
|
|
232
|
+
if (!(init instanceof Request) && !init.referrer) {
|
|
233
|
+
init = { referrer: this.location.href, ...init };
|
|
234
|
+
}
|
|
233
235
|
// ------------
|
|
234
236
|
const hosts = [];
|
|
235
237
|
this.servers.forEach(server => hosts.push(...server.domains));
|
|
@@ -258,7 +260,7 @@ export default class Runtime extends _Runtime {
|
|
|
258
260
|
// ------------
|
|
259
261
|
|
|
260
262
|
// ------------
|
|
261
|
-
Observer.set(this.location, url, { detail: {
|
|
263
|
+
Observer.set(this.location, url, { detail: { init, ...detail, } });
|
|
262
264
|
Observer.set(this.network, 'redirecting', null);
|
|
263
265
|
// ------------
|
|
264
266
|
|
|
@@ -299,10 +301,16 @@ export default class Runtime extends _Runtime {
|
|
|
299
301
|
url2.port = vhost.port;
|
|
300
302
|
if (vhost.proto) { url2.protocol = vhost.proto; }
|
|
301
303
|
// ---------
|
|
302
|
-
|
|
303
|
-
if (
|
|
304
|
-
|
|
305
|
-
|
|
304
|
+
let init2;
|
|
305
|
+
if (init instanceof Request) {
|
|
306
|
+
init2 = init.clone();
|
|
307
|
+
init.headers.set('Host', url2.host);
|
|
308
|
+
} else {
|
|
309
|
+
init2 = { ...init, decompress: false/* honoured in xfetch() */ };
|
|
310
|
+
if (!init2.headers) init2.headers = {};
|
|
311
|
+
init2.headers.host = url2.host;
|
|
312
|
+
delete init2.headers.connection;
|
|
313
|
+
}
|
|
306
314
|
// ---------
|
|
307
315
|
let response;
|
|
308
316
|
try {
|
|
@@ -2,62 +2,78 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* @imports
|
|
4
4
|
*/
|
|
5
|
-
import { _isString, _isNumeric, _isObject, _isPlainObject, _isArray, _isPlainArray, _isTypeObject, _isNumber } from '@webqit/util/js/index.js';
|
|
5
|
+
import { _isString, _isNumeric, _isObject, _isPlainObject, _isArray, _isPlainArray, _isTypeObject, _isNumber, _isBoolean } from '@webqit/util/js/index.js';
|
|
6
6
|
import { _before } from '@webqit/util/str/index.js';
|
|
7
7
|
import { params } from './util-url.js';
|
|
8
8
|
|
|
9
|
-
export function formatMessage(
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
export function formatMessage(message) {
|
|
10
|
+
const headers = (message.headers instanceof Headers) ? [...message.headers.keys()].reduce((_headers, name) => {
|
|
11
|
+
return { ..._headers, [name/* lower-cased */]: message.headers.get(name) };
|
|
12
|
+
}, {}) : Object.keys(message.headers || {}).reduce((_headers, name) => {
|
|
13
|
+
return { ..._headers, [name.toLowerCase()]: message.headers[name] };
|
|
14
|
+
}, {});
|
|
15
|
+
let body = message.body, type = dataType(message.body);
|
|
12
16
|
if ([ 'Blob', 'File' ].includes(type)) {
|
|
13
|
-
headers
|
|
17
|
+
!headers['content-type'] && (headers['content-type'] = body.type);
|
|
18
|
+
!headers['content-length'] && (headers['content-length'] = body.size);
|
|
14
19
|
} else if ([ 'Uint8Array', 'Uint16Array', 'Uint32Array', 'ArrayBuffer' ].includes(type)) {
|
|
15
|
-
headers
|
|
20
|
+
!headers['content-length'] && (headers['content-length'] = body.byteLength);
|
|
16
21
|
} else if (type === 'json' && _isTypeObject(body)) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
if (!headers['content-type']) {
|
|
23
|
+
const [ _body, isJsonfiable ] = formDatarizeJson(body);
|
|
24
|
+
if (isJsonfiable) {
|
|
25
|
+
body = JSON.stringify(body, (k, v) => v instanceof Error ? { ...v, message: v.message } : v);
|
|
26
|
+
headers['content-type'] = 'application/json';
|
|
27
|
+
headers['content-length'] = (new Blob([ body ])).size;
|
|
28
|
+
} else {
|
|
29
|
+
body = _body;
|
|
30
|
+
type = 'FormData';
|
|
31
|
+
}
|
|
24
32
|
}
|
|
25
33
|
} else if (type === 'json') {
|
|
26
|
-
headers
|
|
34
|
+
!headers['content-length'] && (headers['content-length'] = (body + '').length);
|
|
27
35
|
}
|
|
28
36
|
return [ body, headers, type ];
|
|
29
37
|
}
|
|
30
38
|
|
|
31
|
-
export function
|
|
32
|
-
const formData =
|
|
39
|
+
export function formDatarizeJson(data = {}, jsonfy = true) {
|
|
40
|
+
const formData = new FormData;
|
|
41
|
+
let isJsonfiable = true;
|
|
42
|
+
params.reduceValue(data, '', (value, contextPath, suggestedKeys = undefined) => {
|
|
43
|
+
if (suggestedKeys) {
|
|
44
|
+
const isJson = dataType(value) === 'json';
|
|
45
|
+
isJsonfiable = isJsonfiable && isJson;
|
|
46
|
+
return isJson && suggestedKeys;
|
|
47
|
+
}
|
|
48
|
+
if (jsonfy && [true, false, null].includes(value)) {
|
|
49
|
+
value = new Blob([value], { type: 'application/json' });
|
|
50
|
+
}
|
|
51
|
+
formData.append(contextPath, value);
|
|
52
|
+
});
|
|
53
|
+
return [ formData, isJsonfiable ];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export async function jsonfyFormData(formData, jsonfy = true) {
|
|
33
57
|
let isJsonfiable = true;
|
|
34
|
-
if (arguments.length) {
|
|
35
|
-
params.reduceValue(data, '', (value, contextPath, suggestedKeys = undefined) => {
|
|
36
|
-
if (suggestedKeys) {
|
|
37
|
-
const isJson = dataType(value) === 'json';
|
|
38
|
-
isJsonfiable = isJsonfiable && isJson;
|
|
39
|
-
return isJson && suggestedKeys;
|
|
40
|
-
}
|
|
41
|
-
formData.append(contextPath, value);
|
|
42
|
-
});
|
|
43
|
-
return [ formData, isJsonfiable ];
|
|
44
|
-
}
|
|
45
58
|
let json;
|
|
46
59
|
for (let [ name, value ] of formData.entries()) {
|
|
47
60
|
if (!json) { json = _isNumeric(_before(name, '[')) ? [] : {}; }
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
61
|
+
let type = dataType(value);
|
|
62
|
+
if (jsonfy && type === 'Blob' && value.type === 'application/json' && [4, 5].includes(value.size)) {
|
|
63
|
+
let _value = await value.text();
|
|
64
|
+
if (['true', 'false', 'null'].includes(_value)) {
|
|
65
|
+
type = 'json';
|
|
66
|
+
value = JSON.parse(_value);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
isJsonfiable = isJsonfiable && type === 'json';
|
|
54
70
|
params.set(json, name, value);
|
|
55
71
|
}
|
|
56
72
|
return [ json, isJsonfiable ];
|
|
57
73
|
}
|
|
58
74
|
|
|
59
75
|
export function dataType(value) {
|
|
60
|
-
if (_isString(value) || _isNumber(value) || value
|
|
76
|
+
if (_isString(value) || _isNumber(value) || _isBoolean(value)) return 'json';
|
|
61
77
|
if (!_isTypeObject(value)) return;
|
|
62
78
|
const toStringTag = value[Symbol.toStringTag];
|
|
63
79
|
const type = [
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* @imports
|
|
4
4
|
*/
|
|
5
|
-
import {
|
|
5
|
+
import { jsonfyFormData } from './util-http.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* The _Headers Mixin
|
|
@@ -10,7 +10,7 @@ import { formData } from './util-http.js';
|
|
|
10
10
|
export default class xFormData extends FormData {
|
|
11
11
|
|
|
12
12
|
json(data = {}) {
|
|
13
|
-
const result =
|
|
13
|
+
const result = jsonfyFormData(this, ...arguments);
|
|
14
14
|
return result[0];
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -2,53 +2,23 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* @imports
|
|
4
4
|
*/
|
|
5
|
-
import { formatMessage } from './util-http.js';
|
|
6
5
|
import mxHttpMessage from './xxHttpMessage.js';
|
|
7
6
|
import xRequestHeaders from './xRequestHeaders.js';
|
|
7
|
+
import { formatMessage } from './util-http.js';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* The xRequest Mixin
|
|
11
11
|
*/
|
|
12
12
|
export default class xRequest extends mxHttpMessage(Request, xRequestHeaders) {
|
|
13
13
|
|
|
14
|
-
constructor(input, init = {}) {
|
|
15
|
-
|
|
16
|
-
if ((
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
input = input.url;
|
|
21
|
-
init = { ...init };
|
|
22
|
-
// We should now simply copy attributes
|
|
23
|
-
[ 'headers', 'mode', 'credentials', 'cache', 'redirect', 'referrer', 'integrity' ].forEach(attr => {
|
|
24
|
-
if (!(attr in init)) { init[attr] = input[attr]; }
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
let isNavigateMode;
|
|
29
|
-
if (!(init instanceof Request)) {
|
|
30
|
-
// Init can contain "already-parsed request content"
|
|
31
|
-
if (('body' in init) && !(init.headers instanceof Headers)) {
|
|
32
|
-
const [ body, headers, type ] = formatMessage(init.body);
|
|
33
|
-
meta = { type, body: init.body };
|
|
34
|
-
init = { ...init, body, headers: { ...headers, ...(init.headers || {}), } };
|
|
35
|
-
}
|
|
36
|
-
if (init.mode === 'navigate') {
|
|
37
|
-
isNavigateMode = true;
|
|
38
|
-
init = { ...init };
|
|
39
|
-
delete init.mode;
|
|
40
|
-
}
|
|
14
|
+
constructor(input, init = {}, meta = {}) {
|
|
15
|
+
console.log('--------------------');
|
|
16
|
+
if (!(init instanceof Request) && 'body' in init) {
|
|
17
|
+
const [ body, headers, type ] = formatMessage(init);
|
|
18
|
+
meta = { ...meta, type, body: init.body };
|
|
19
|
+
init = { ...init, body, headers };
|
|
41
20
|
}
|
|
42
|
-
// ---------------
|
|
43
21
|
super(input, init, meta);
|
|
44
|
-
// ---------------
|
|
45
|
-
if (isNavigateMode) {
|
|
46
|
-
this.attrs.mode = 'navigate';
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
get mode() {
|
|
51
|
-
return 'mode' in this.attrs ? this.attrs.mode : super.mode;
|
|
52
22
|
}
|
|
53
23
|
|
|
54
24
|
static compat(request) {
|
|
@@ -58,14 +28,4 @@ export default class xRequest extends mxHttpMessage(Request, xRequestHeaders) {
|
|
|
58
28
|
}
|
|
59
29
|
}
|
|
60
30
|
|
|
61
|
-
static rip(request) {
|
|
62
|
-
const requestInit = [
|
|
63
|
-
'method', 'headers', 'mode', 'credentials', 'cache', 'redirect', 'referrer', 'integrity',
|
|
64
|
-
].reduce((init, prop) => ({ [prop]: request[prop], ...init }), {});
|
|
65
|
-
if (!['GET', 'HEAD'].includes(request.method)) {
|
|
66
|
-
requestInit.body = request.body;
|
|
67
|
-
}
|
|
68
|
-
return [ request.url, requestInit ];
|
|
69
|
-
}
|
|
70
|
-
|
|
71
31
|
}
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* @imports
|
|
4
4
|
*/
|
|
5
|
-
import { formatMessage } from './util-http.js';
|
|
6
5
|
import mxHttpMessage from './xxHttpMessage.js';
|
|
7
6
|
import xResponseHeaders from './xResponseHeaders.js';
|
|
7
|
+
import { formatMessage } from './util-http.js';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* The xResponse Mixin
|
|
@@ -12,55 +12,14 @@ import xResponseHeaders from './xResponseHeaders.js';
|
|
|
12
12
|
export default class xResponse extends mxHttpMessage(Response, xResponseHeaders) {
|
|
13
13
|
|
|
14
14
|
// construct
|
|
15
|
-
constructor(body =
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
init = { status: body.status, statusText: body.statusText, headers: body.headers, ...init };
|
|
22
|
-
if (body.status === 0) delete init.status;
|
|
23
|
-
// Inherit meta and body
|
|
24
|
-
meta = body.meta || {};
|
|
25
|
-
body = body.body;
|
|
26
|
-
} else if (!(init.headers instanceof Headers)) {
|
|
27
|
-
let headers, type, _body = body;
|
|
28
|
-
[ body, headers, type ] = formatMessage(body);
|
|
29
|
-
meta = { type, body: _body };
|
|
30
|
-
init = { ...init, headers: { ...headers, ...(init.headers || {}), } };
|
|
31
|
-
}
|
|
15
|
+
constructor(body = null, init = {}, meta = {}) {
|
|
16
|
+
if (body || body === 0) {
|
|
17
|
+
let headers, type, _body = body;
|
|
18
|
+
[ body, headers, type ] = formatMessage({ body, headers: init.headers });
|
|
19
|
+
meta = { ...init, type, body: _body };
|
|
20
|
+
init = { ...init, headers };
|
|
32
21
|
}
|
|
33
|
-
// ---------------
|
|
34
22
|
super(body, init, meta);
|
|
35
|
-
// ---------------
|
|
36
|
-
if (isResponseInput) {
|
|
37
|
-
// Through the backdoor
|
|
38
|
-
this.attrs.url = isResponseInput.url;
|
|
39
|
-
this.attrs.ok = isResponseInput.ok;
|
|
40
|
-
this.attrs.status = isResponseInput.status; // In case it was earlier deleted
|
|
41
|
-
this.attrs.type = isResponseInput.type;
|
|
42
|
-
this.attrs.redirected = isResponseInput.redirected;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
get ok() {
|
|
47
|
-
return 'ok' in this.attrs ? this.attrs.ok : super.ok;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
get status() {
|
|
51
|
-
return 'status' in this.attrs ? this.attrs.status : super.status;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
get statusText() {
|
|
55
|
-
return 'statusText' in this.attrs ? this.attrs.statusText : super.statusText;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
get type() {
|
|
59
|
-
return 'type' in this.attrs ? this.attrs.type : super.type;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
get redirected() {
|
|
63
|
-
return 'redirected' in this.attrs ? this.attrs.redirected : super.redirected;
|
|
64
23
|
}
|
|
65
24
|
|
|
66
25
|
static compat(response) {
|
package/src/runtime-pi/xfetch.js
CHANGED
|
@@ -9,8 +9,8 @@ import { formatMessage } from './util-http.js';
|
|
|
9
9
|
*/
|
|
10
10
|
const xfetch = async (url, init = {}) => {
|
|
11
11
|
if (init.body) {
|
|
12
|
-
const [ body, headers ] = formatMessage(init
|
|
13
|
-
init = { ...init, body, headers
|
|
12
|
+
const [ body, headers ] = formatMessage(init);
|
|
13
|
+
init = { ...init, body, headers, };
|
|
14
14
|
}
|
|
15
15
|
let response = await fetch(url, init), encoding;
|
|
16
16
|
if (init.decompress === false && (encoding = response.headers.get('Content-Encoding'))) {
|