@webqit/webflo 0.11.61 → 1.0.1
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/{Context.js → AbstractContext.js} +1 -9
- package/src/deployment-pi/origins/index.js +1 -1
- package/src/index.js +1 -9
- package/src/runtime-pi/HttpEvent.js +101 -81
- package/src/runtime-pi/HttpUser.js +126 -0
- package/src/runtime-pi/MessagingOverBroadcast.js +9 -0
- package/src/runtime-pi/MessagingOverChannel.js +85 -0
- package/src/runtime-pi/MessagingOverSocket.js +106 -0
- package/src/runtime-pi/MultiportMessagingAPI.js +81 -0
- package/src/runtime-pi/WebfloCookieStorage.js +27 -0
- package/src/runtime-pi/WebfloEventTarget.js +39 -0
- package/src/runtime-pi/WebfloMessageEvent.js +58 -0
- package/src/runtime-pi/WebfloMessagingAPI.js +69 -0
- package/src/runtime-pi/{Router.js → WebfloRouter.js} +3 -34
- package/src/runtime-pi/WebfloRuntime.js +52 -0
- package/src/runtime-pi/WebfloStorage.js +109 -0
- package/src/runtime-pi/client/ClientMessaging.js +5 -0
- package/src/runtime-pi/client/Context.js +2 -6
- package/src/runtime-pi/client/CookieStorage.js +17 -0
- package/src/runtime-pi/client/Router.js +3 -13
- package/src/runtime-pi/client/SessionStorage.js +33 -0
- package/src/runtime-pi/client/Url.js +24 -72
- package/src/runtime-pi/client/WebfloClient.js +544 -0
- package/src/runtime-pi/client/WebfloRootClient1.js +179 -0
- package/src/runtime-pi/client/WebfloRootClient2.js +109 -0
- package/src/runtime-pi/client/WebfloSubClient.js +165 -0
- package/src/runtime-pi/client/Workport.js +89 -161
- package/src/runtime-pi/client/generate.js +3 -3
- package/src/runtime-pi/client/index.js +13 -18
- package/src/runtime-pi/client/worker/ClientMessaging.js +5 -0
- package/src/runtime-pi/client/worker/Context.js +2 -6
- package/src/runtime-pi/client/worker/CookieStorage.js +17 -0
- package/src/runtime-pi/client/worker/SessionStorage.js +13 -0
- package/src/runtime-pi/client/worker/WebfloWorker.js +294 -0
- package/src/runtime-pi/client/worker/Workport.js +13 -73
- package/src/runtime-pi/client/worker/index.js +7 -18
- package/src/runtime-pi/index.js +1 -8
- package/src/runtime-pi/server/ClientMessaging.js +18 -0
- package/src/runtime-pi/server/ClientMessagingRegistry.js +57 -0
- package/src/runtime-pi/server/Context.js +2 -6
- package/src/runtime-pi/server/CookieStorage.js +17 -0
- package/src/runtime-pi/server/Router.js +2 -68
- package/src/runtime-pi/server/SessionStorage.js +53 -0
- package/src/runtime-pi/server/WebfloServer.js +755 -0
- package/src/runtime-pi/server/index.js +7 -18
- package/src/runtime-pi/util-http.js +268 -32
- package/src/runtime-pi/xURL.js +25 -22
- package/src/runtime-pi/xfetch.js +2 -2
- package/src/runtime-pi/Application.js +0 -29
- package/src/runtime-pi/Cookies.js +0 -82
- package/src/runtime-pi/Runtime.js +0 -21
- package/src/runtime-pi/client/Application.js +0 -76
- package/src/runtime-pi/client/Runtime.js +0 -525
- package/src/runtime-pi/client/createStorage.js +0 -58
- package/src/runtime-pi/client/worker/Application.js +0 -44
- package/src/runtime-pi/client/worker/Runtime.js +0 -275
- package/src/runtime-pi/server/Application.js +0 -101
- package/src/runtime-pi/server/Runtime.js +0 -558
- package/src/runtime-pi/xFormData.js +0 -24
- package/src/runtime-pi/xHeaders.js +0 -146
- package/src/runtime-pi/xRequest.js +0 -46
- package/src/runtime-pi/xRequestHeaders.js +0 -109
- package/src/runtime-pi/xResponse.js +0 -33
- package/src/runtime-pi/xResponseHeaders.js +0 -117
- package/src/runtime-pi/xxHttpMessage.js +0 -102
|
@@ -1,21 +1,10 @@
|
|
|
1
|
+
import { WebfloServer } from './WebfloServer.js';
|
|
1
2
|
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import Context from './Context.js';
|
|
6
|
-
import Application from './Application.js';
|
|
7
|
-
import Runtime from './Runtime.js';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* @start
|
|
11
|
-
*/
|
|
12
|
-
export async function start(applicationInstance = null) {
|
|
13
|
-
const cx = this || {};
|
|
14
|
-
const defaultApplicationInstance = _cx => new Application(_cx);
|
|
15
|
-
return new Runtime(Context.create(cx), applicationInstance || defaultApplicationInstance);
|
|
3
|
+
export async function start() {
|
|
4
|
+
const instance = WebfloServer.create(this || {});
|
|
5
|
+
await instance.initialize();
|
|
16
6
|
}
|
|
17
7
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
export * as APIS from './Runtime.js';
|
|
8
|
+
export {
|
|
9
|
+
WebfloServer
|
|
10
|
+
}
|
|
@@ -1,30 +1,40 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* @imports
|
|
4
|
-
*/
|
|
5
1
|
import { _isString, _isNumeric, _isObject, _isPlainObject, _isArray, _isPlainArray, _isTypeObject, _isNumber, _isBoolean } from '@webqit/util/js/index.js';
|
|
6
|
-
import { _before } from '@webqit/util/str/index.js';
|
|
2
|
+
import { _after, _before } from '@webqit/util/str/index.js';
|
|
3
|
+
import { _from as _arrFrom } from '@webqit/util/arr/index.js';
|
|
7
4
|
import { params } from './util-url.js';
|
|
8
5
|
|
|
9
|
-
export function
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
6
|
+
export function dataType(value) {
|
|
7
|
+
if (_isString(value) || _isNumber(value) || _isBoolean(value)) return 'json';
|
|
8
|
+
if (!_isTypeObject(value)) return;
|
|
9
|
+
const toStringTag = value[Symbol.toStringTag];
|
|
10
|
+
const type = [
|
|
11
|
+
'Uint8Array', 'Uint16Array', 'Uint32Array', 'ArrayBuffer', 'Blob', 'File', 'FormData', 'Stream', 'ReadableStream'
|
|
12
|
+
].reduce((_toStringTag, type) => _toStringTag || (toStringTag === type ? type : null), null);
|
|
13
|
+
if (type) return type;
|
|
14
|
+
if ((_isObject(value) && _isPlainObject(value)) || (_isArray(value) && _isPlainArray(value)) || 'toString' in value) {
|
|
15
|
+
return 'json';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function renderHttpMessageInit(httpMessageInit) {
|
|
20
|
+
const headers = (httpMessageInit.headers instanceof Headers) ? [...httpMessageInit.headers.keys()].reduce((_headers, name) => {
|
|
21
|
+
return { ..._headers, [name/* lower-cased */]: httpMessageInit.headers.get(name) };
|
|
22
|
+
}, {}) : Object.keys(httpMessageInit.headers || {}).reduce((_headers, name) => {
|
|
23
|
+
return { ..._headers, [name.toLowerCase()]: httpMessageInit.headers[name] };
|
|
14
24
|
}, {});
|
|
15
|
-
let body =
|
|
16
|
-
if ([
|
|
25
|
+
let body = httpMessageInit.body, type = dataType(httpMessageInit.body);
|
|
26
|
+
if (['Blob', 'File'].includes(type)) {
|
|
17
27
|
!headers['content-type'] && (headers['content-type'] = body.type);
|
|
18
28
|
!headers['content-length'] && (headers['content-length'] = body.size);
|
|
19
|
-
} else if ([
|
|
29
|
+
} else if (['Uint8Array', 'Uint16Array', 'Uint32Array', 'ArrayBuffer'].includes(type)) {
|
|
20
30
|
!headers['content-length'] && (headers['content-length'] = body.byteLength);
|
|
21
31
|
} else if (type === 'json' && _isTypeObject(body)) {
|
|
22
32
|
if (!headers['content-type']) {
|
|
23
|
-
const [
|
|
33
|
+
const [_body, isJsonfiable] = createFormDataFromJson(body, true/*jsonfy*/, true/*getIsJsonfiable*/);
|
|
24
34
|
if (isJsonfiable) {
|
|
25
35
|
body = JSON.stringify(body, (k, v) => v instanceof Error ? { ...v, message: v.message } : v);
|
|
26
36
|
headers['content-type'] = 'application/json';
|
|
27
|
-
headers['content-length'] = (new Blob([
|
|
37
|
+
headers['content-length'] = (new Blob([body])).size;
|
|
28
38
|
} else {
|
|
29
39
|
body = _body;
|
|
30
40
|
type = 'FormData';
|
|
@@ -33,10 +43,24 @@ export function formatMessage(message) {
|
|
|
33
43
|
} else if (type === 'json') {
|
|
34
44
|
!headers['content-length'] && (headers['content-length'] = (body + '').length);
|
|
35
45
|
}
|
|
36
|
-
return
|
|
46
|
+
return { body, headers, $type: type };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function parseHttpMessage(httpMessage) {
|
|
50
|
+
let result;
|
|
51
|
+
const contentType = httpMessage.headers.get('Content-Type') || '';
|
|
52
|
+
if (contentType === 'application/x-www-form-urlencoded' || contentType.startsWith('multipart/form-data')) {
|
|
53
|
+
const formData = await httpMessage.formData();
|
|
54
|
+
result = await formData?.json();
|
|
55
|
+
} else if (contentType.startsWith('application/json')/*can include charset*/) {
|
|
56
|
+
result = await httpMessage.json();
|
|
57
|
+
} else /*if (contentType === 'text/plain')*/ {
|
|
58
|
+
result = await httpMessage.text();
|
|
59
|
+
}
|
|
60
|
+
return result;
|
|
37
61
|
}
|
|
38
62
|
|
|
39
|
-
export function
|
|
63
|
+
export function createFormDataFromJson(data = {}, jsonfy = true, getIsJsonfiable = false) {
|
|
40
64
|
const formData = new FormData;
|
|
41
65
|
let isJsonfiable = true;
|
|
42
66
|
params.reduceValue(data, '', (value, contextPath, suggestedKeys = undefined) => {
|
|
@@ -50,13 +74,14 @@ export function formDatarizeJson(data = {}, jsonfy = true) {
|
|
|
50
74
|
}
|
|
51
75
|
formData.append(contextPath, value);
|
|
52
76
|
});
|
|
53
|
-
return [
|
|
77
|
+
if (getIsJsonfiable) return [formData, isJsonfiable];
|
|
78
|
+
return formData;
|
|
54
79
|
}
|
|
55
80
|
|
|
56
|
-
export async function
|
|
81
|
+
export async function renderFormDataToJson(formData, jsonfy = true, getIsJsonfiable = false) {
|
|
57
82
|
let isJsonfiable = true;
|
|
58
83
|
let json;
|
|
59
|
-
for (let [
|
|
84
|
+
for (let [name, value] of formData.entries()) {
|
|
60
85
|
if (!json) { json = _isNumeric(_before(name, '[')) ? [] : {}; }
|
|
61
86
|
let type = dataType(value);
|
|
62
87
|
if (jsonfy && ['Blob', 'File'].includes(type) && value.type === 'application/json' && [4, 5].includes(value.size)) {
|
|
@@ -69,18 +94,229 @@ export async function jsonfyFormData(formData, jsonfy = true) {
|
|
|
69
94
|
isJsonfiable = isJsonfiable && type === 'json';
|
|
70
95
|
params.set(json, name, value);
|
|
71
96
|
}
|
|
72
|
-
return [
|
|
97
|
+
if (getIsJsonfiable) return [json, isJsonfiable];
|
|
98
|
+
return json;
|
|
73
99
|
}
|
|
74
100
|
|
|
75
|
-
export function
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
101
|
+
export function renderCookieObj(cookieObj) {
|
|
102
|
+
const attrsArr = [`${cookieObj.name}=${/*encodeURIComponent*/(cookieObj.value)}`];
|
|
103
|
+
for (const attrName in cookieObj) {
|
|
104
|
+
if (['name', 'value'].includes(attrName)) continue;
|
|
105
|
+
let _attrName = attrName[0].toUpperCase() + attrName.substring(1);
|
|
106
|
+
if (_attrName === 'MaxAge') { _attrName = 'Max-Age' };
|
|
107
|
+
attrsArr.push(cookieObj[attrName] === true ? _attrName : `${_attrName}=${cookieObj[attrName]}`);
|
|
108
|
+
}
|
|
109
|
+
return attrsArr.join(';');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* Request */
|
|
113
|
+
|
|
114
|
+
Object.defineProperties(Request, {
|
|
115
|
+
create: {
|
|
116
|
+
value: function (url, init = {}) {
|
|
117
|
+
if (url instanceof Request) return url;
|
|
118
|
+
let $$type, $$body = init.body;
|
|
119
|
+
if ('body' in init) {
|
|
120
|
+
const { body, headers, $type } = renderHttpMessageInit(init);
|
|
121
|
+
init = { ...init, body, headers };
|
|
122
|
+
$$type = $type;
|
|
123
|
+
}
|
|
124
|
+
const instance = new Request(url, init);
|
|
125
|
+
instance.meta.body = $$body;
|
|
126
|
+
instance.meta.type = $$type;
|
|
127
|
+
return instance;
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
copy: {
|
|
132
|
+
value: async function (request, init = {}) {
|
|
133
|
+
const requestInit = [
|
|
134
|
+
'method', 'headers', 'mode', 'credentials', 'cache', 'redirect', 'referrer', 'integrity',
|
|
135
|
+
].reduce((init, prop) => ({ [prop]: request[prop], ...init }), {});
|
|
136
|
+
if (!['GET', 'HEAD'].includes(init.method?.toUpperCase() || request.method)) {
|
|
137
|
+
requestInit.body = await request.clone().arrayBuffer();
|
|
138
|
+
}
|
|
139
|
+
if (requestInit.mode === 'navigate') {
|
|
140
|
+
requestInit.mode = 'cors';
|
|
141
|
+
}
|
|
142
|
+
return { url: request.url, ...requestInit };
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
Object.defineProperties(Request.prototype, {
|
|
148
|
+
parse: { value: function() { return parseHttpMessage(this); } },
|
|
149
|
+
meta: { get: function() { if (!this._meta) this._meta = {}; return this._meta; } }
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
/* Response */
|
|
153
|
+
|
|
154
|
+
Object.defineProperties(Response, {
|
|
155
|
+
create: {
|
|
156
|
+
value: function (body, init = {}) {
|
|
157
|
+
if (body instanceof Response) return body;
|
|
158
|
+
let $type, $body = body;
|
|
159
|
+
if (body || body === 0) {
|
|
160
|
+
let headers;
|
|
161
|
+
({ body, headers, $type } = renderHttpMessageInit({ body, headers: init.headers }));
|
|
162
|
+
init = { ...init, headers };
|
|
163
|
+
}
|
|
164
|
+
const instance = new Response(body, init);
|
|
165
|
+
instance.meta.body = $body;
|
|
166
|
+
instance.meta.type = $type;
|
|
167
|
+
return instance;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
const statusGet = Object.getOwnPropertyDescriptor(Response.prototype, 'status');
|
|
173
|
+
Object.defineProperties(Response.prototype, {
|
|
174
|
+
parse: { value: function() { return parseHttpMessage(this); } },
|
|
175
|
+
meta: { get: function() { if (!this._meta) this._meta = {}; return this._meta; } },
|
|
176
|
+
status: { get: function() { return this.meta.status || statusGet.get.call(this); } }
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
/* Headers */
|
|
180
|
+
|
|
181
|
+
const { set: headerSet, append: headerAppend, get: headerGet } = Headers.prototype;
|
|
182
|
+
Object.defineProperties(Headers.prototype, {
|
|
183
|
+
set: {
|
|
184
|
+
value: function (name, value) {
|
|
185
|
+
// -------------------------
|
|
186
|
+
// Format "Set-Cookie" response header
|
|
187
|
+
if (/^Set-Cookie$/i.test(name) && _isObject(value)) {
|
|
188
|
+
value = renderCookieObj(value);
|
|
189
|
+
}
|
|
190
|
+
// -------------------------
|
|
191
|
+
// Format "Cookie" request header
|
|
192
|
+
if (/Cookie/i.test(name) && _isTypeObject(value)) {
|
|
193
|
+
value = [].concat(value).map(renderCookieObj).join(';');
|
|
194
|
+
}
|
|
195
|
+
// -------------------------
|
|
196
|
+
// Format "Content-Range" response header?
|
|
197
|
+
if (/^Content-Range$/i.test(name) && Array.isArray(value)) {
|
|
198
|
+
if (value.length < 2 || !value[0].includes('-')) {
|
|
199
|
+
throw new Error(`A Content-Range array must be in the format: [ 'start-end', 'total' ]`);
|
|
200
|
+
}
|
|
201
|
+
value = `bytes ${value.join('/')}`;
|
|
202
|
+
}
|
|
203
|
+
// -------------------------
|
|
204
|
+
// Format "Range" request header?
|
|
205
|
+
if (/^Range$/i.test(name)) {
|
|
206
|
+
let rangeArr = [];
|
|
207
|
+
_arrFrom(value).forEach((range, i) => {
|
|
208
|
+
let rangeStr = Array.isArray(range) ? range.join('-') : range + '';
|
|
209
|
+
if (i === 0 && !rangeStr.includes('bytes=')) {
|
|
210
|
+
rangeStr = `bytes=${rangeStr}`;
|
|
211
|
+
}
|
|
212
|
+
rangeArr.push(rangeStr);
|
|
213
|
+
});
|
|
214
|
+
value = rangeArr.join(', ');
|
|
215
|
+
}
|
|
216
|
+
// -------------------------
|
|
217
|
+
// Format "Accept" request header?
|
|
218
|
+
if (/^Accept$/i.test(name) && Array.isArray(value)) {
|
|
219
|
+
value = value.join(',');
|
|
220
|
+
}
|
|
221
|
+
// -------------------------
|
|
222
|
+
return headerSet.call(this, name, value);
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
|
|
226
|
+
append: {
|
|
227
|
+
value: function (name, value) {
|
|
228
|
+
// -------------------------
|
|
229
|
+
// Format "Set-Cookie" response header
|
|
230
|
+
if (/^Set-Cookie$/i.test(name) && _isObject(value)) {
|
|
231
|
+
value = renderCookieObj(value);
|
|
232
|
+
}
|
|
233
|
+
// -------------------------
|
|
234
|
+
return headerAppend.call(this, name, value);
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
|
|
238
|
+
get: {
|
|
239
|
+
value: function (name, parsed = false) {
|
|
240
|
+
let value = headerGet.call(this, name);
|
|
241
|
+
// -------------------------
|
|
242
|
+
// Parse "Set-Cookie" response header
|
|
243
|
+
if (/^Set-Cookie$/i.test(name) && parsed) {
|
|
244
|
+
value = this.getSetCookie()/*IMPORTANT*/.map((str) => {
|
|
245
|
+
const [cookieDefinition, attrsStr] = str.split(';');
|
|
246
|
+
const [name, value] = cookieDefinition.split('=').map((s) => s.trim());
|
|
247
|
+
const cookieObj = { name, value: /*decodeURIComponent*/(value), };
|
|
248
|
+
attrsStr && attrsStr.split(/\;/g).map(attrStr => attrStr.trim().split('=')).forEach(attrsArr => {
|
|
249
|
+
cookieObj[attrsArr[0][0].toLowerCase() + attrsArr[0].substring(1).replace('-', '')] = attrsArr.length === 1 ? true : attrsArr[1];
|
|
250
|
+
});
|
|
251
|
+
return cookieObj;
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
// -------------------------
|
|
255
|
+
// Parse "Cookie" request header
|
|
256
|
+
if (/^Cookie$/i.test(name) && parsed) {
|
|
257
|
+
value = value?.split(';').map((str) => {
|
|
258
|
+
const [name, value] = str.split('=').map((s) => s.trim());
|
|
259
|
+
return { name, value: /*decodeURIComponent*/(value), };
|
|
260
|
+
}) || [];
|
|
261
|
+
}
|
|
262
|
+
// -------------------------
|
|
263
|
+
// Parse "Content-Range" response header?
|
|
264
|
+
if (/^Content-Range$/i.test(name) && value && parsed) {
|
|
265
|
+
value = _after(value, 'bytes ').split('/');
|
|
266
|
+
}
|
|
267
|
+
// -------------------------
|
|
268
|
+
// Parse "Range" request header?
|
|
269
|
+
if (/^Range$/i.test(name) && parsed) {
|
|
270
|
+
value = !value ? [] : _after(value, 'bytes=').split(',').map((rangeStr) => {
|
|
271
|
+
let range = rangeStr.trim().split('-');
|
|
272
|
+
range[0] = range[0] ? parseInt(range[0], 10) : undefined;
|
|
273
|
+
if (range[1]) {
|
|
274
|
+
range[1] = parseInt(range[1], 10);
|
|
275
|
+
}
|
|
276
|
+
range.clamp = max => {
|
|
277
|
+
if (range[1] > max - 1 || range[1] === undefined) {
|
|
278
|
+
range[1] = max - 1;
|
|
279
|
+
}
|
|
280
|
+
if (range[0] === undefined) range[0] = range[1] ? max - range[1] - 1 : 0;
|
|
281
|
+
};
|
|
282
|
+
return range;
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
// -------------------------
|
|
286
|
+
// Parse "Accept" request header?
|
|
287
|
+
if (/^Accept$/i.test(name) && parsed) {
|
|
288
|
+
const list = value && value.split(',')
|
|
289
|
+
.map((a) => (a = a.trim().split(';').map(a => a.trim()), [a.shift(), parseFloat((a.pop() || '1').replace('q=', ''))]))
|
|
290
|
+
.sort((a, b) => a[1] > b[1] ? -1 : 1) || [];
|
|
291
|
+
value = {
|
|
292
|
+
match(mime) {
|
|
293
|
+
mime = (mime + '').split('/');
|
|
294
|
+
return list.reduce((prev, entry) => prev || (
|
|
295
|
+
(entry = entry[0].split('/')) && [0, 1].every(i => ((mime[i] === entry[i]) || mime[i] === '*' || entry[i] === '*'))
|
|
296
|
+
), false);
|
|
297
|
+
},
|
|
298
|
+
toString() {
|
|
299
|
+
return value;
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
// -------------------------
|
|
304
|
+
return value;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
/* FormData */
|
|
310
|
+
|
|
311
|
+
Object.defineProperties(FormData, {
|
|
312
|
+
json: { value: createFormDataFromJson }
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
Object.defineProperties(FormData.prototype, {
|
|
316
|
+
json: {
|
|
317
|
+
value: async function (data = {}) {
|
|
318
|
+
const result = await renderFormDataToJson(this, ...arguments);
|
|
319
|
+
return result;
|
|
320
|
+
}
|
|
85
321
|
}
|
|
86
|
-
}
|
|
322
|
+
});
|
package/src/runtime-pi/xURL.js
CHANGED
|
@@ -15,40 +15,43 @@ export default class xURL extends URL {
|
|
|
15
15
|
// constructor
|
|
16
16
|
constructor(...args) {
|
|
17
17
|
super(...args);
|
|
18
|
-
|
|
19
|
-
const updateSearch =
|
|
18
|
+
const query = params.parse(this.search);
|
|
19
|
+
const updateSearch = () => {
|
|
20
20
|
// "query" was updated. So we update "search"
|
|
21
|
-
|
|
21
|
+
let search = params.stringify(query);
|
|
22
22
|
search = search ? '?' + search : '';
|
|
23
23
|
if (search !== this.search) {
|
|
24
24
|
this.search = search;
|
|
25
25
|
}
|
|
26
26
|
};
|
|
27
|
-
this
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
})
|
|
41
|
-
};
|
|
27
|
+
const $this = this;
|
|
28
|
+
this._query = new Proxy(query, {
|
|
29
|
+
set(t, k, v) {
|
|
30
|
+
t[k] = v;
|
|
31
|
+
if (!$this._updatingSearch) updateSearch();
|
|
32
|
+
return true;
|
|
33
|
+
},
|
|
34
|
+
deleteProperty(t, k) {
|
|
35
|
+
delete t[k];
|
|
36
|
+
if (!$this._updatingSearch) updateSearch();
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
});
|
|
42
40
|
}
|
|
43
41
|
|
|
44
42
|
// Set search
|
|
45
43
|
set search(value) {
|
|
46
44
|
super.search = value;
|
|
47
45
|
// "search" was updated. So we update "query"
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
this._updatingSearch = true;
|
|
47
|
+
const query = params.parse(value);
|
|
48
|
+
const keys_a = Object.keys(query);
|
|
49
|
+
const keys_b = Object.keys(this._query);
|
|
50
|
+
for (const k of new Set([...keys_a,...keys_b])) {
|
|
51
|
+
if (!keys_a.includes(k)) delete this.query[k];
|
|
52
|
+
if (!keys_b.includes(k)) this.query[k] = query[k];
|
|
51
53
|
}
|
|
54
|
+
this._updatingSearch = false;
|
|
52
55
|
}
|
|
53
56
|
|
|
54
57
|
// Get search
|
|
@@ -58,7 +61,7 @@ export default class xURL extends URL {
|
|
|
58
61
|
|
|
59
62
|
// Get query
|
|
60
63
|
get query() {
|
|
61
|
-
return this.
|
|
64
|
+
return this._query;
|
|
62
65
|
}
|
|
63
66
|
|
|
64
67
|
};
|
package/src/runtime-pi/xfetch.js
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* @imports
|
|
4
4
|
*/
|
|
5
|
-
import {
|
|
5
|
+
import { renderHttpMessageInit } from './util-http.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* The xfetch Mixin
|
|
9
9
|
*/
|
|
10
10
|
const xfetch = async (url, init = {}) => {
|
|
11
11
|
if (init.body) {
|
|
12
|
-
const
|
|
12
|
+
const { body, headers } = renderHttpMessageInit(init);
|
|
13
13
|
init = { ...init, body, headers, };
|
|
14
14
|
}
|
|
15
15
|
let response = await fetch(url, init), encoding;
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* ---------------------------
|
|
4
|
-
* The base Application class
|
|
5
|
-
* ---------------------------
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export default class Application {
|
|
9
|
-
|
|
10
|
-
constructor(cx) {
|
|
11
|
-
this.cx = cx;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Initializes application itself.
|
|
16
|
-
*
|
|
17
|
-
* @param HttpEvent httpEvent
|
|
18
|
-
* @param Function remoteFetch
|
|
19
|
-
*
|
|
20
|
-
* @return Boolean|undefined
|
|
21
|
-
*/
|
|
22
|
-
async init(httpEvent, remoteFetch) {
|
|
23
|
-
// The app router
|
|
24
|
-
const router = new this.Router(this.cx, '/');
|
|
25
|
-
return router.route(['init'], httpEvent, {}, async event => {
|
|
26
|
-
}, remoteFetch);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
}
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* @imports
|
|
4
|
-
*/
|
|
5
|
-
import { _isString, _isObject } from "@webqit/util/js/index.js";
|
|
6
|
-
|
|
7
|
-
export default class Cookies extends Map {
|
|
8
|
-
|
|
9
|
-
constructor(...args) {
|
|
10
|
-
super(...args);
|
|
11
|
-
Object.defineProperty(this, 'inLock', { value: false, writable: true });
|
|
12
|
-
Object.defineProperty(this, 'outLock', { value: false, writable: true });
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
set(name, value) {
|
|
16
|
-
if (this.inLock) return;
|
|
17
|
-
if (this.has(name)) this.delete(name);
|
|
18
|
-
this.inLock = true;
|
|
19
|
-
// -----------------
|
|
20
|
-
let valueObj = value, valueStr = value, retrn;
|
|
21
|
-
if (_isString(value)) { valueObj = this.parseEntry(`=${ value }`)[1]; }
|
|
22
|
-
retrn = super.set(name, valueObj);
|
|
23
|
-
if (!this.outLock) {
|
|
24
|
-
if (_isObject(value)) { valueStr = this.stringifyEntry(value); }
|
|
25
|
-
append(this.headers, `${ name }=${ valueStr }`);
|
|
26
|
-
}
|
|
27
|
-
// -----------------
|
|
28
|
-
this.inLock = false;
|
|
29
|
-
return retrn;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
delete(name) {
|
|
33
|
-
if (this.inLock) return;
|
|
34
|
-
this.inLock = true;
|
|
35
|
-
// -----------------
|
|
36
|
-
let retrn = super.delete(name);
|
|
37
|
-
this.headers.delete(this.headers.cookieHeaderName);
|
|
38
|
-
for (let [ name, definition ] of this) {
|
|
39
|
-
append(this.headers, `${ name }=${ this.stringifyEntry(definition) }`);
|
|
40
|
-
}
|
|
41
|
-
// -----------------
|
|
42
|
-
this.inLock = false;
|
|
43
|
-
return retrn;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
clear() {
|
|
47
|
-
if (this.inLock) return;
|
|
48
|
-
this.inLock = true;
|
|
49
|
-
// -----------------
|
|
50
|
-
let retrn = super.clear();
|
|
51
|
-
this.headers.delete(this.headers.cookieHeaderName);
|
|
52
|
-
// -----------------
|
|
53
|
-
this.inLock = false;
|
|
54
|
-
return retrn;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
json(json = {}) {
|
|
58
|
-
if (arguments.length) {
|
|
59
|
-
this.clear();
|
|
60
|
-
for (let name in json) {
|
|
61
|
-
this.set(name, json[name])
|
|
62
|
-
}
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
for (let [ name, definition ] of this) {
|
|
66
|
-
json[name] = definition;
|
|
67
|
-
}
|
|
68
|
-
return json;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
toString() {
|
|
72
|
-
return this.headers.get(this.headers.cookieHeaderName);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function append(headers, value) {
|
|
78
|
-
let values = [value];
|
|
79
|
-
let currentValue = headers.get(headers.cookieHeaderName);
|
|
80
|
-
if (currentValue) { values.unshift(currentValue); }
|
|
81
|
-
headers.set(headers.cookieHeaderName, values.join(headers.cookieHeaderSeparator));
|
|
82
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* @imports
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { _isFunction } from "@webqit/util/js/index.js";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* ---------------------------
|
|
10
|
-
* The base Runtime class
|
|
11
|
-
* ---------------------------
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
export default class Runtime {
|
|
15
|
-
constructor(cx, applicationInstance) {
|
|
16
|
-
this.cx = cx;
|
|
17
|
-
this.cx.runtime = this;
|
|
18
|
-
this.app = _isFunction(applicationInstance) ? applicationInstance(this.cx) : applicationInstance;
|
|
19
|
-
if (!this.app || !this.app.handle) throw new Error(`Application instance must define a ".handle()" method.`);
|
|
20
|
-
}
|
|
21
|
-
}
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* @imports
|
|
4
|
-
*/
|
|
5
|
-
import Router from './Router.js';
|
|
6
|
-
import _Application from '../Application.js';
|
|
7
|
-
|
|
8
|
-
export default class Application extends _Application {
|
|
9
|
-
|
|
10
|
-
// Returns router class
|
|
11
|
-
get Router() {
|
|
12
|
-
return Router;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Handles HTTP events.
|
|
17
|
-
*
|
|
18
|
-
* @param HttpEvent httpEvent
|
|
19
|
-
* @param Function remoteFetch
|
|
20
|
-
*
|
|
21
|
-
* @return Response
|
|
22
|
-
*/
|
|
23
|
-
async handle(httpEvent, remoteFetch) {
|
|
24
|
-
// The app router
|
|
25
|
-
const router = new this.Router(this.cx, httpEvent.url.pathname);
|
|
26
|
-
const handle = async () => {
|
|
27
|
-
let bindingsConfig;
|
|
28
|
-
if (window.webqit?.oohtml?.configs) { ( { BINDINGS_API: { api: bindingsConfig } = {}, } = window.webqit.oohtml.configs ); }
|
|
29
|
-
return router.route([httpEvent.request.method, 'default'], httpEvent, { ...( ( bindingsConfig && document[bindingsConfig.bindings] ) || {} ) }, async event => {
|
|
30
|
-
if (event !== httpEvent) {
|
|
31
|
-
// This was nexted()
|
|
32
|
-
if (!event.request.headers.has('Accept')) {
|
|
33
|
-
event.request.headers.set('Accept', 'application/json');
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return remoteFetch(event.request);
|
|
37
|
-
}, remoteFetch);
|
|
38
|
-
};
|
|
39
|
-
return await (this.cx.middlewares || []).concat(handle).reverse().reduce((next, fn) => {
|
|
40
|
-
return () => fn.call(this.cx, httpEvent, router, next);
|
|
41
|
-
}, null)();
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Renderer
|
|
45
|
-
async render(httpEvent, response) {
|
|
46
|
-
let data = await response.jsonfy();
|
|
47
|
-
const router = new this.Router(this.cx, httpEvent.url.pathname);
|
|
48
|
-
return await router.route('render', httpEvent, data, async (httpEvent, data) => {
|
|
49
|
-
if (window.webqit?.dom) { await new Promise(res => window.webqit.dom.ready(res)); }
|
|
50
|
-
if (window.webqit?.oohtml?.configs) {
|
|
51
|
-
const {
|
|
52
|
-
CONTEXT_API: { attr: contextConfig } = {},
|
|
53
|
-
BINDINGS_API: { api: bindingsConfig } = {},
|
|
54
|
-
HTML_IMPORTS: { attr: modulesContextAttrs } = {},
|
|
55
|
-
} = window.webqit.oohtml.configs;
|
|
56
|
-
if ( bindingsConfig ) {
|
|
57
|
-
window.document[ bindingsConfig.bind ]({
|
|
58
|
-
env: 'client', state: this.cx.runtime, ...(data || {})
|
|
59
|
-
}, { diff: true });
|
|
60
|
-
}
|
|
61
|
-
let routingContext;
|
|
62
|
-
if ( modulesContextAttrs ) {
|
|
63
|
-
routingContext = window.document.body.querySelector(`[${ window.CSS.escape( contextConfig.contextname ) }="app"]`) || window.document.body;
|
|
64
|
-
const prevRoute = routingContext.getAttribute( modulesContextAttrs.importscontext );
|
|
65
|
-
const newRoute = '/' + `routes/${ httpEvent.url.pathname }`.split('/').map(a => a.trim()).filter(a => a).join('/');
|
|
66
|
-
const rel = prevRoute === newRoute ? 'same' : ( `${ prevRoute }/`.startsWith( `${ newRoute }/` ) ? 'parent' : ( `${ newRoute }/`.startsWith( `${ prevRoute }/` ) ? 'child' : 'unrelated' ) );
|
|
67
|
-
routingContext.setAttribute( modulesContextAttrs.importscontext, newRoute);
|
|
68
|
-
routingContext.setAttribute( `prev-${ modulesContextAttrs.importscontext }`, prevRoute );
|
|
69
|
-
routingContext.setAttribute( 'importscontext-transition-type', rel );
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
return window;
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|