@webqit/webflo 0.8.45 → 0.8.49
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/docker/Dockerfile +25 -0
- package/docker/README.md +69 -0
- package/package.json +9 -5
- package/src/cmd/client.js +68 -97
- package/src/cmd/origins.js +2 -2
- package/src/modules/Router.js +130 -0
- package/src/modules/_FormData.js +60 -0
- package/src/modules/_Headers.js +88 -0
- package/src/modules/_MessageStream.js +191 -0
- package/src/modules/_NavigationEvent.js +89 -0
- package/src/modules/_Request.js +61 -0
- package/src/modules/_RequestHeaders.js +72 -0
- package/src/modules/_Response.js +56 -0
- package/src/modules/_ResponseHeaders.js +81 -0
- package/src/modules/_URL.js +111 -0
- package/src/modules/client/Cache.js +38 -0
- package/src/modules/client/Client.js +51 -22
- package/src/modules/client/Http.js +26 -11
- package/src/modules/client/NavigationEvent.js +20 -0
- package/src/modules/client/Router.js +30 -104
- package/src/modules/client/StdRequest.js +34 -33
- package/src/modules/client/Storage.js +56 -0
- package/src/modules/client/Url.js +1 -1
- package/src/modules/client/Worker.js +74 -68
- package/src/modules/client/WorkerClient.js +102 -0
- package/src/modules/client/WorkerComm.js +183 -0
- package/src/modules/client/effects/sounds.js +64 -0
- package/src/modules/server/NavigationEvent.js +38 -0
- package/src/modules/server/Router.js +53 -124
- package/src/modules/server/Server.js +195 -87
- package/src/modules/util.js +7 -7
- package/src/modules/NavigationEvent.js +0 -32
- package/src/modules/Response.js +0 -98
- package/src/modules/XURL.js +0 -125
- package/src/modules/client/ClientNavigationEvent.js +0 -22
- package/src/modules/client/Push.js +0 -84
- package/src/modules/server/ServerNavigationEvent.js +0 -23
- package/src/modules/server/StdIncomingMessage.js +0 -70
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* @imports
|
|
4
4
|
*/
|
|
5
|
+
import { OOHTML, Observer } from '@webqit/pseudo-browser/index2.js';
|
|
5
6
|
import _isObject from '@webqit/util/js/isObject.js';
|
|
6
7
|
import _before from '@webqit/util/str/before.js';
|
|
8
|
+
import _unique from '@webqit/util/arr/unique.js';
|
|
7
9
|
import _fetch from '@webqit/browser-pie/src/apis/fetch.js';
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
+
import NavigationEvent from './NavigationEvent.js';
|
|
11
|
+
import WorkerClient from './WorkerClient.js';
|
|
12
|
+
import Storage from './Storage.js';
|
|
10
13
|
import Router from './Router.js';
|
|
11
14
|
import Http from './Http.js';
|
|
12
15
|
|
|
@@ -26,6 +29,12 @@ OOHTML.call(window);
|
|
|
26
29
|
|
|
27
30
|
export default function(layout, params) {
|
|
28
31
|
|
|
32
|
+
const session = Storage();
|
|
33
|
+
const workerClient = new WorkerClient('/worker.js', { startMessages: true });
|
|
34
|
+
Observer.observe(workerClient, changes => {
|
|
35
|
+
console.log('SERVICE_WORKER_STATE', changes[0].name, changes[0].value);
|
|
36
|
+
});
|
|
37
|
+
|
|
29
38
|
// Copy..
|
|
30
39
|
layout = {...layout};
|
|
31
40
|
params = {...params};
|
|
@@ -38,6 +47,7 @@ export default function(layout, params) {
|
|
|
38
47
|
* Apply routing
|
|
39
48
|
* ----------------
|
|
40
49
|
*/
|
|
50
|
+
|
|
41
51
|
Http.createClient(async function(request, event = null) {
|
|
42
52
|
|
|
43
53
|
const httpInstance = this;
|
|
@@ -47,7 +57,6 @@ export default function(layout, params) {
|
|
|
47
57
|
// -------------------
|
|
48
58
|
|
|
49
59
|
// The srvice object
|
|
50
|
-
const clientNavigationEvent = new ClientNavigationEvent(request);
|
|
51
60
|
const $context = {
|
|
52
61
|
layout,
|
|
53
62
|
onHydration: !event && (await window.WebQit.OOHTML.meta.get('isomorphic')),
|
|
@@ -55,6 +64,7 @@ export default function(layout, params) {
|
|
|
55
64
|
}
|
|
56
65
|
|
|
57
66
|
// The app router
|
|
67
|
+
const clientNavigationEvent = new NavigationEvent(request, session);
|
|
58
68
|
const requestPath = clientNavigationEvent.url.pathname;
|
|
59
69
|
const router = new Router(requestPath, layout, $context);
|
|
60
70
|
if (networkProgressOngoing) {
|
|
@@ -68,32 +78,54 @@ export default function(layout, params) {
|
|
|
68
78
|
// ROUTE FOR DATA
|
|
69
79
|
// --------
|
|
70
80
|
const httpMethodName = clientNavigationEvent.request.method.toLowerCase();
|
|
71
|
-
$context.response = await router.route([httpMethodName === 'delete' ? 'del' : httpMethodName, 'default'],
|
|
81
|
+
$context.response = await router.route([httpMethodName === 'delete' ? 'del' : httpMethodName, 'default'], clientNavigationEvent, document.state, async function(event) {
|
|
72
82
|
// -----------------
|
|
73
83
|
var networkProgress = networkProgressOngoing = new RequestHandle();
|
|
74
|
-
networkProgress.setActive(true);
|
|
84
|
+
networkProgress.setActive(true, event.request._method || event.request.method);
|
|
75
85
|
// -----------------
|
|
76
|
-
const headers =
|
|
86
|
+
const headers = event.request.headers;
|
|
77
87
|
if (!headers.get('Accept')) {
|
|
78
88
|
headers.set('Accept', 'application/json');
|
|
89
|
+
}
|
|
90
|
+
if (!headers.get('Cache-Control')) {
|
|
79
91
|
headers.set('Cache-Control', 'no-store');
|
|
80
92
|
}
|
|
81
|
-
|
|
93
|
+
// -----------------
|
|
94
|
+
// Sync session data to cache to be available to service-worker routers
|
|
95
|
+
const response = fetch(event.request, {}, networkProgress.updateProgress.bind(networkProgress));
|
|
96
|
+
// -----------------
|
|
82
97
|
// -----------------
|
|
83
98
|
response.catch(e => networkProgress.throw(e.message));
|
|
84
|
-
return response.then(
|
|
99
|
+
return response.then(async _response => {
|
|
100
|
+
_response = new clientNavigationEvent.Response(_response.body, {
|
|
101
|
+
status: _response.status,
|
|
102
|
+
statusText: _response.statusText,
|
|
103
|
+
headers: _response.headers,
|
|
104
|
+
_proxy: {
|
|
105
|
+
url: _response.url,
|
|
106
|
+
ok: _response.ok,
|
|
107
|
+
redirected: _response.redirected
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
// Save a reference to this
|
|
111
|
+
$context.responseClone = _response;
|
|
112
|
+
// Return a promise that never resolves as a new response is underway
|
|
85
113
|
if (!networkProgress.active) {
|
|
86
114
|
return new Promise(() => {});
|
|
87
115
|
}
|
|
116
|
+
// Stop loading status
|
|
88
117
|
networkProgress.setActive(false);
|
|
89
|
-
return
|
|
118
|
+
return _response;
|
|
90
119
|
});
|
|
91
|
-
}
|
|
120
|
+
});
|
|
121
|
+
if ($context.response instanceof clientNavigationEvent.Response) {
|
|
122
|
+
$context.response = await $context.response.jsonBuild();
|
|
123
|
+
}
|
|
92
124
|
|
|
93
125
|
// --------
|
|
94
126
|
// Render
|
|
95
127
|
// --------
|
|
96
|
-
const rendering = await router.route('render',
|
|
128
|
+
const rendering = await router.route('render', clientNavigationEvent, $context.response, async function(event, data) {
|
|
97
129
|
// --------
|
|
98
130
|
// OOHTML would waiting for DOM-ready in order to be initialized
|
|
99
131
|
await new Promise(res => window.WebQit.DOM.ready(res));
|
|
@@ -103,9 +135,10 @@ export default function(layout, params) {
|
|
|
103
135
|
onHydration: $context.onHydration,
|
|
104
136
|
network: networkWatch,
|
|
105
137
|
url: httpInstance.location,
|
|
138
|
+
session,
|
|
106
139
|
}, { update: true });
|
|
107
140
|
}
|
|
108
|
-
window.document.setState({page: data}, { update: 'merge' });
|
|
141
|
+
window.document.setState({ page: data }, { update: 'merge' });
|
|
109
142
|
window.document.body.setAttribute('template', 'page/' + requestPath.split('/').filter(a => a).map(a => a + '+-').join('/'));
|
|
110
143
|
return new Promise(res => {
|
|
111
144
|
window.document.addEventListener('templatesreadystatechange', () => res(window));
|
|
@@ -113,19 +146,13 @@ export default function(layout, params) {
|
|
|
113
146
|
res(window);
|
|
114
147
|
}
|
|
115
148
|
});
|
|
116
|
-
}
|
|
149
|
+
});
|
|
117
150
|
|
|
118
151
|
// --------
|
|
119
152
|
// Render...
|
|
120
153
|
// --------
|
|
121
154
|
|
|
122
|
-
if (_isObject(
|
|
123
|
-
//$context.response = window.print();
|
|
124
|
-
} else {
|
|
125
|
-
//$context.response = rendering;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (event && _isObject(event.detail) && (event.detail.src instanceof Element) && /* do only on url path change */ _before(event.value, '?') !== _before(event.oldValue, '?')) {
|
|
155
|
+
if (!document.activeElement && event && _isObject(event.detail) && (event.detail.src instanceof Element) && /* do only on url path change */ _before(event.value, '?') !== _before(event.oldValue, '?')) {
|
|
129
156
|
setTimeout(() => {
|
|
130
157
|
var urlTarget;
|
|
131
158
|
if (clientNavigationEvent.url.hash && (urlTarget = document.querySelector(clientNavigationEvent.url.hash))) {
|
|
@@ -147,18 +174,20 @@ export default function(layout, params) {
|
|
|
147
174
|
|
|
148
175
|
}
|
|
149
176
|
|
|
177
|
+
return $context.responseClone;
|
|
150
178
|
});
|
|
151
179
|
|
|
152
180
|
};
|
|
153
181
|
|
|
154
|
-
const networkWatch = {progress: {}, online: navigator.onLine};
|
|
182
|
+
const networkWatch = { progress: {}, online: navigator.onLine };
|
|
155
183
|
class RequestHandle {
|
|
156
|
-
setActive(state) {
|
|
184
|
+
setActive(state, method = '') {
|
|
157
185
|
if (this.active === false) {
|
|
158
186
|
return;
|
|
159
187
|
}
|
|
160
188
|
this.active = state;
|
|
161
189
|
Observer.set(networkWatch, {
|
|
190
|
+
method,
|
|
162
191
|
error: '',
|
|
163
192
|
progress: {
|
|
164
193
|
active: state,
|
|
@@ -27,7 +27,7 @@ export default class Http {
|
|
|
27
27
|
*
|
|
28
28
|
* @return void
|
|
29
29
|
*/
|
|
30
|
-
static createClient(client, params = {}) {
|
|
30
|
+
static async createClient(client, params = {}) {
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* ----------------
|
|
@@ -107,15 +107,22 @@ export default class Http {
|
|
|
107
107
|
window.addEventListener('submit', e => {
|
|
108
108
|
var actionEl = window.document.createElement('a'),
|
|
109
109
|
form = e.target.closest('form'),
|
|
110
|
-
submits = _arrFrom(form.elements).filter(el => el.matches('button,input[type="submit"],input[type="image"]'));
|
|
111
|
-
var submitParams = ['action', 'enctype', 'method', 'noValidate', 'target'].reduce((params, prop) => {
|
|
110
|
+
submits = [e.submitter]; //_arrFrom(form.elements).filter(el => el.matches('button,input[type="submit"],input[type="image"]'));
|
|
111
|
+
var submitParams = [ 'action', 'enctype', 'method', 'noValidate', 'target' ].reduce((params, prop) => {
|
|
112
112
|
params[prop] = submits.reduce((val, el) => val || (el.hasAttribute(`form${prop.toLowerCase()}`) ? el[`form${_toTitle(prop)}`] : null), null) || form[prop];
|
|
113
113
|
return params;
|
|
114
114
|
}, {});
|
|
115
|
+
// We support method hacking
|
|
116
|
+
// ---------------
|
|
117
|
+
submitParams.method = e.submitter.dataset.method || form.dataset.method || submitParams.method;
|
|
118
|
+
// ---------------
|
|
115
119
|
if ((actionEl.href = submitParams.action) && !submitParams.target
|
|
116
120
|
// Same origin... but...
|
|
117
121
|
&& (!actionEl.origin || actionEl.origin === instance.location.origin)) {
|
|
118
122
|
var formData = new FormData(form);
|
|
123
|
+
if (e.submitter.name) {
|
|
124
|
+
formData.set(e.submitter.name, e.submitter.value);
|
|
125
|
+
}
|
|
119
126
|
if (submitParams.method === 'get') {
|
|
120
127
|
var query = wwwFormUnserialize(actionEl.search);
|
|
121
128
|
Array.from(formData.entries()).forEach(_entry => {
|
|
@@ -129,7 +136,7 @@ export default class Http {
|
|
|
129
136
|
}
|
|
130
137
|
// Publish everything, including hash
|
|
131
138
|
Observer.set(instance.location, Url.copy(actionEl), {
|
|
132
|
-
detail: {type: 'form', src: form, submitParams, data: formData},
|
|
139
|
+
detail: { type: 'form', src: form, submitParams, data: formData },
|
|
133
140
|
});
|
|
134
141
|
// URLs with # will cause a natural navigation
|
|
135
142
|
// even if pointing to a different page, a natural navigation will still happen
|
|
@@ -151,7 +158,7 @@ export default class Http {
|
|
|
151
158
|
// -----------------------
|
|
152
159
|
// Syndicate changes to
|
|
153
160
|
// the browser;s location bar
|
|
154
|
-
Observer.observe(instance.location, [['href']], e => {
|
|
161
|
+
Observer.observe(instance.location, [[ 'href' ]], e => {
|
|
155
162
|
e = e[0];
|
|
156
163
|
if ((e.detail || {}).src === window.document.location) {
|
|
157
164
|
// Already from a "popstate" event as above, so don't push again
|
|
@@ -173,27 +180,35 @@ export default class Http {
|
|
|
173
180
|
var options = {
|
|
174
181
|
method: (detail.submitParams || detail.src || {}).method || 'get',
|
|
175
182
|
body: detail.data,
|
|
176
|
-
headers: { 'X-Powered-By': '@webqit/webflo', },
|
|
183
|
+
headers: { ...(detail.headers || {}), 'X-Powered-By': '@webqit/webflo', },
|
|
177
184
|
referrer,
|
|
178
185
|
};
|
|
179
|
-
if (detail.accept) {
|
|
180
|
-
options.headers.accept = detail.accept;
|
|
181
|
-
}
|
|
182
186
|
return new StdRequest(url, options);
|
|
183
187
|
};
|
|
188
|
+
const handleResponse = response => {
|
|
189
|
+
if (response && response.redirected) {
|
|
190
|
+
var actionEl = window.document.createElement('a');
|
|
191
|
+
if ((actionEl.href = response.url) && (!actionEl.origin || actionEl.origin === instance.location.origin)) {
|
|
192
|
+
Observer.set(instance.location, { href: response.url }, {
|
|
193
|
+
detail: { follow: false },
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
};
|
|
184
198
|
// ----------------------------------
|
|
185
199
|
|
|
186
200
|
// Observe location and route
|
|
187
201
|
Observer.observe(instance.location, [['href']], async e => {
|
|
188
202
|
e = e[0];
|
|
189
203
|
var detail = e.detail || {};
|
|
204
|
+
if (detail.follow === false) return;
|
|
190
205
|
var method = (detail.submitParams || detail.src || {}).method;
|
|
191
206
|
if ((_before(e.value, '#') !== _before(e.oldValue, '#')) || (method && method.toUpperCase() !== 'GET')) {
|
|
192
|
-
return await client.call(instance, createRequest(e.value, e.oldValue, e), e);
|
|
207
|
+
return handleResponse(await client.call(instance, createRequest(e.value, e.oldValue, e), e));
|
|
193
208
|
}
|
|
194
209
|
}, {diff: false /* method might be the difference */});
|
|
195
210
|
// Startup route
|
|
196
|
-
client.call(instance, createRequest(window.document.location.href, document.referrer));
|
|
211
|
+
handleResponse(await client.call(instance, createRequest(window.document.location.href, document.referrer)));
|
|
197
212
|
|
|
198
213
|
return instance;
|
|
199
214
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* @imports
|
|
4
|
+
*/
|
|
5
|
+
import _NavigationEvent from '../_NavigationEvent.js';
|
|
6
|
+
import _FormData from '../_FormData.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The ClientNavigationEvent class
|
|
10
|
+
*/
|
|
11
|
+
export default _NavigationEvent({
|
|
12
|
+
URL,
|
|
13
|
+
Request,
|
|
14
|
+
Response,
|
|
15
|
+
Headers,
|
|
16
|
+
FormData: _FormData(FormData),
|
|
17
|
+
File,
|
|
18
|
+
Blob,
|
|
19
|
+
ReadableStream,
|
|
20
|
+
});
|
|
@@ -2,11 +2,8 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* @imports
|
|
4
4
|
*/
|
|
5
|
-
import _isString from '@webqit/util/js/isString.js';
|
|
6
|
-
import _isFunction from '@webqit/util/js/isFunction.js';
|
|
7
|
-
import _isArray from '@webqit/util/js/isArray.js';
|
|
8
|
-
import _arrFrom from '@webqit/util/arr/from.js';
|
|
9
5
|
import { path as Path } from '../util.js';
|
|
6
|
+
import _Router from '../Router.js';
|
|
10
7
|
|
|
11
8
|
/**
|
|
12
9
|
* ---------------------------
|
|
@@ -14,108 +11,37 @@ import { path as Path } from '../util.js';
|
|
|
14
11
|
* ---------------------------
|
|
15
12
|
*/
|
|
16
13
|
|
|
17
|
-
export default class Router {
|
|
14
|
+
export default class Router extends _Router {
|
|
18
15
|
|
|
19
|
-
|
|
20
|
-
* Constructs a new Router instance
|
|
21
|
-
* over route definitions.
|
|
22
|
-
*
|
|
23
|
-
* @param string|array path
|
|
24
|
-
* @param object layout
|
|
25
|
-
* @param object context
|
|
26
|
-
*
|
|
27
|
-
* @return void
|
|
28
|
-
*/
|
|
29
|
-
constructor(path, layout, context) {
|
|
30
|
-
this.path = _isArray(path) ? path : (path + '').split('/').filter(a => a);
|
|
31
|
-
this.layout = layout;
|
|
32
|
-
this.context = context;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Performs dynamic routing.
|
|
37
|
-
*
|
|
38
|
-
* @param array|string target
|
|
39
|
-
* @param array argsA
|
|
40
|
-
* @param any input
|
|
41
|
-
* @param function _default
|
|
42
|
-
* @param array argsB
|
|
43
|
-
*
|
|
44
|
-
* @return object
|
|
45
|
-
*/
|
|
46
|
-
async route(target, argsA, input, _default, argsB = []) {
|
|
47
|
-
|
|
48
|
-
target = _arrFrom(target);
|
|
16
|
+
async readTick(thisTick) {
|
|
49
17
|
var routeTree = this.layout;
|
|
50
|
-
var
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
18
|
+
var routePaths = Object.keys(this.layout);
|
|
19
|
+
if (thisTick.trail) {
|
|
20
|
+
thisTick.currentSegment = thisTick.destination[thisTick.trail.length];
|
|
21
|
+
thisTick.currentSegmentOnFile = [ thisTick.currentSegment, '-' ].reduce((_segmentOnFile, _seg) => {
|
|
22
|
+
if (_segmentOnFile.index) return _segmentOnFile;
|
|
23
|
+
var _currentPath = `/${thisTick.trailOnFile.concat(_seg).join('/')}`;
|
|
24
|
+
return routeTree[_currentPath] ? { seg: _seg, index: _currentPath } : (
|
|
25
|
+
routePaths.filter(p => p.startsWith(`${_currentPath}/`)).length ? { seg: _seg, dirExists: true } : _segmentOnFile
|
|
26
|
+
);
|
|
27
|
+
}, { seg: null });
|
|
28
|
+
thisTick.trail.push(thisTick.currentSegment);
|
|
29
|
+
thisTick.trailOnFile.push(thisTick.currentSegmentOnFile.seg);
|
|
30
|
+
thisTick.exports = routeTree[thisTick.currentSegmentOnFile.index];
|
|
31
|
+
} else {
|
|
32
|
+
thisTick.trail = [];
|
|
33
|
+
thisTick.trailOnFile = [];
|
|
34
|
+
thisTick.currentSegmentOnFile = { index: '/' };
|
|
35
|
+
thisTick.exports = routeTree['/'];
|
|
36
|
+
}
|
|
37
|
+
return thisTick;
|
|
38
|
+
}
|
|
65
39
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
// -------------
|
|
70
|
-
// Dynamic response
|
|
71
|
-
// -------------
|
|
72
|
-
const _next = (..._args) => {
|
|
73
|
-
var _index;
|
|
74
|
-
if (_args.length > 1) {
|
|
75
|
-
if (!_isString(_args[1])) {
|
|
76
|
-
throw new Error('Router redirect must be a string!');
|
|
77
|
-
}
|
|
78
|
-
var newPath = Path.join(path.slice(0, index).join('/'), _args[1]);
|
|
79
|
-
if (newPath.startsWith('../')) {
|
|
80
|
-
throw new Error('Router redirect cannot traverse beyond the routing directory! (' + _args[1] + ' >> ' + newPath + ')');
|
|
81
|
-
}
|
|
82
|
-
_args[1] = newPath.split('/').map(a => a.trim()).filter(a => a);
|
|
83
|
-
_index = path.slice(0, index).reduce((build, seg, i) => build.length === i && seg === _args[1][i] ? build.concat(seg) : build, []).length;
|
|
84
|
-
} else {
|
|
85
|
-
_args[1] = path;
|
|
86
|
-
_index = index;
|
|
87
|
-
}
|
|
88
|
-
return next(_index + 1, ..._args);
|
|
89
|
-
};
|
|
90
|
-
_next.pathname = path.slice(index).join('/');
|
|
91
|
-
_next.stepname = _next.pathname.split('/').shift();
|
|
92
|
-
// -------------
|
|
93
|
-
const _this = {
|
|
94
|
-
pathname: '/' + path.slice(0, index).join('/'),
|
|
95
|
-
...context
|
|
96
|
-
};
|
|
97
|
-
_this.stepname = _this.pathname.split('/').pop();
|
|
98
|
-
// -------------
|
|
99
|
-
return await func.bind(_this)(...argsA.concat([input, _next/*next*/].concat(argsB)));
|
|
100
|
-
} else {
|
|
101
|
-
return next(index + 1, input, path);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
40
|
+
finalizeHandlerContext(context, thisTick) {
|
|
41
|
+
return context.dirname = thisTick.currentSegmentOnFile.index;
|
|
42
|
+
}
|
|
104
43
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
// -------------
|
|
109
|
-
const defaultThis = {pathname: '/' + path.join('/'), ...context};
|
|
110
|
-
return await _default.call(defaultThis, input);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// -------------
|
|
114
|
-
// Recieved response or undefined
|
|
115
|
-
// -------------
|
|
116
|
-
return;
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
return next(0, input, this.path);
|
|
120
|
-
}
|
|
44
|
+
pathJoin(...args) {
|
|
45
|
+
return Path.join(...args);
|
|
46
|
+
}
|
|
121
47
|
};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* @imports
|
|
4
4
|
*/
|
|
5
|
-
import { _isTypeObject } from '@webqit/util/js/index.js';
|
|
5
|
+
import { _isTypeObject, _isFunction } from '@webqit/util/js/index.js';
|
|
6
6
|
import { wwwFormUnserialize, wwwFormSet } from '../util.js';
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -35,39 +35,40 @@ export default class StdRequest extends Request {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
// Payload
|
|
38
|
-
parse() {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
38
|
+
parse(force = false) {
|
|
39
|
+
if (!this._submits || force) {
|
|
40
|
+
this._submits = new Promise(async resolve => {
|
|
41
|
+
var request = this.clone();
|
|
42
|
+
var contentType = request.headers['content-type'] || '';
|
|
43
|
+
var submits = {
|
|
44
|
+
inputs: {},
|
|
45
|
+
files: {},
|
|
46
|
+
type: contentType === 'application/x-www-form-urlencoded' || contentType.startsWith('multipart/') || (!contentType && !['get'].includes(request.method.toLowerCase())) ? 'form-data'
|
|
47
|
+
: (contentType === 'application/json' ? 'json'
|
|
48
|
+
: (contentType === 'text/plain' ? 'plain'
|
|
49
|
+
: 'other')),
|
|
50
|
+
};
|
|
51
|
+
if (submits.type === 'form-data') {
|
|
52
|
+
try {
|
|
53
|
+
const payload = await request.formData();
|
|
54
|
+
for(var [ name, value ] of payload.entries()) {
|
|
55
|
+
if (value instanceof File) {
|
|
56
|
+
wwwFormSet(submits.files, name, value);
|
|
57
|
+
} else {
|
|
58
|
+
wwwFormSet(submits.inputs, name, value);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
} catch(e) { }
|
|
62
|
+
} else {
|
|
63
|
+
submits.inputs = await (submits.type === 'json'
|
|
64
|
+
? request.json() : (
|
|
65
|
+
submits.type === 'plain' ? request.text() : request.arrayBuffer()
|
|
66
|
+
)
|
|
64
67
|
)
|
|
65
|
-
)
|
|
66
|
-
if (submits.type === 'json' && _isTypeObject(submits.payload)) {
|
|
67
|
-
submits.inputs = inputs;
|
|
68
68
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
69
|
+
resolve(submits);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return this._submits;
|
|
72
73
|
}
|
|
73
74
|
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @imports
|
|
5
|
+
*/
|
|
6
|
+
import { Observer } from '@webqit/pseudo-browser/index2.js';
|
|
7
|
+
import { _isString, _isUndefined } from '@webqit/util/js/index.js';
|
|
8
|
+
|
|
9
|
+
export default function(persistent = false) {
|
|
10
|
+
|
|
11
|
+
const storeType = persistent ? 'localStorage' : 'sessionStorage';
|
|
12
|
+
if (!window[storeType]) {
|
|
13
|
+
throw new Error(`The specified Web Storage API ${storeType} is invalid or not supported`)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const _storage = {};
|
|
17
|
+
Observer.intercept(_storage, (event, received, next) => {
|
|
18
|
+
if (event.type === 'get' && _isString(event.name)) {
|
|
19
|
+
const value = window[storeType].getItem(event.name);
|
|
20
|
+
return !_isUndefined(value) ? JSON.parse(value) : value;
|
|
21
|
+
}
|
|
22
|
+
if (event.type === 'set') {
|
|
23
|
+
window[storeType].setItem(event.name, !_isUndefined(event.value) ? JSON.stringify(event.value) : event.value);
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
if (event.type === 'deleteProperty') {
|
|
27
|
+
window[storeType].removeItem(event.name);
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
if (event.type === 'has') {
|
|
31
|
+
for(var i = 0; i < window[storeType].length; i ++){
|
|
32
|
+
if (window[storeType].key(i) === event.name) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
if (event.type === 'ownKeys') {
|
|
39
|
+
var keys = [];
|
|
40
|
+
for(var i = 0; i < window[storeType].length; i ++){
|
|
41
|
+
keys.push(window[storeType].key(i));
|
|
42
|
+
};
|
|
43
|
+
return keys;
|
|
44
|
+
}
|
|
45
|
+
if (event.type === 'getOwnPropertyDescriptor') {
|
|
46
|
+
return { enumerable: true, configurable: true };
|
|
47
|
+
}
|
|
48
|
+
return next();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
return Observer.proxy(_storage);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export {
|
|
55
|
+
Observer,
|
|
56
|
+
}
|