@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 CHANGED
@@ -12,7 +12,7 @@
12
12
  "vanila-javascript"
13
13
  ],
14
14
  "homepage": "https://webqit.io/tooling/webflo",
15
- "version": "0.11.52",
15
+ "version": "0.11.53-0",
16
16
  "license": "MIT",
17
17
  "repository": {
18
18
  "type": "git",
@@ -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 init
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
- init = { referrer: this.location.href, ...init };
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', { ...init, ...detail });
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
- requestInit.cache = 'default';
97
+ req = new Request(req, {cache: 'default'});
99
98
  }
100
- return this.go(url, requestInit, { event: evt });
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 init
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 = { referrer: this.location.href, ...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 = await this.generateRequest(url.href, init);
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 init
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 = { referrer: this.location.href, ...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: { ...init, ...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
- const init2 = { ...init, decompress: false/* honoured in xfetch() */ };
303
- if (!init2.headers) init2.headers = {};
304
- init2.headers.host = url2.host;
305
- delete init2.headers.connection;
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(body) {
10
- let type = dataType(body);
11
- let headers = {};
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 = { 'Content-Type': body.type, 'Content-Length': body.size, };
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 = { 'Content-Length': body.byteLength, };
20
+ !headers['content-length'] && (headers['content-length'] = body.byteLength);
16
21
  } else if (type === 'json' && _isTypeObject(body)) {
17
- const [ _body, isJsonfiable ] = formData(body);
18
- if (isJsonfiable) {
19
- body = JSON.stringify(body, (k, v) => v instanceof Error ? { ...v, message: v.message } : v);
20
- headers = { 'Content-Type': 'application/json', 'Content-Length': (new Blob([ body ])).size, };
21
- } else {
22
- body = _body;
23
- type = 'FormData';
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 = { 'Content-Length': (body + '').length, };
34
+ !headers['content-length'] && (headers['content-length'] = (body + '').length);
27
35
  }
28
36
  return [ body, headers, type ];
29
37
  }
30
38
 
31
- export function formData(data = {}) {
32
- const formData = this instanceof FormData ? this : new 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
- const isJson = dataType(value) === 'json';
49
- isJsonfiable = isJsonfiable && isJson;
50
- if (value === 'false') { value = false; }
51
- if (value === 'true') { value = true; }
52
- if (value === 'null') { value = null; }
53
- if (value === 'undefined') { value = undefined; }
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 === null) return 'json';
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 { formData } from './util-http.js';
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 = formData.call(this, ...arguments);
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
- let meta = {};
16
- if ((input instanceof Request)) {
17
- // On method change...
18
- if (init.method && input.method !== init.method.toUpperCase() && [ 'GET', 'HEAD' ].includes(init.method.toUpperCase())) {
19
- // Body must not be inherited.
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 = undefined, init = {}) {
16
- let meta = {}, isResponseInput;
17
- if (arguments.length) {
18
- if (body instanceof Response) {
19
- isResponseInput = body;
20
- // Inherit init
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) {
@@ -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.body);
13
- init = { ...init, body, headers: { ...headers, ...(init.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'))) {