@webqit/webflo 0.20.11-next.0 → 0.20.11
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/WebfloRuntime.js +43 -55
- package/src/runtime-pi/webflo-client/ClientSideCookies.js +2 -1
- package/src/runtime-pi/webflo-client/WebfloClient.js +36 -25
- package/src/runtime-pi/webflo-client/WebfloRootClient2.js +1 -1
- package/src/runtime-pi/webflo-fetch/index.js +1 -34
- package/src/runtime-pi/webflo-routing/HttpCookies.js +3 -3
- package/src/runtime-pi/webflo-routing/HttpEvent.js +49 -5
- package/src/runtime-pi/webflo-routing/HttpSession.js +2 -2
- package/src/runtime-pi/webflo-routing/HttpState.js +24 -19
- package/src/runtime-pi/webflo-routing/HttpThread.js +81 -0
- package/src/runtime-pi/webflo-routing/HttpUser.js +4 -4
- package/src/runtime-pi/webflo-routing/WebfloRouter.js +9 -13
- package/src/runtime-pi/webflo-server/ServerSideCookies.js +2 -1
- package/src/runtime-pi/webflo-server/ServerSideSession.js +4 -3
- package/src/runtime-pi/webflo-server/WebfloServer.js +17 -11
- package/src/runtime-pi/webflo-worker/WebfloWorker.js +15 -12
- package/src/runtime-pi/webflo-worker/WorkerSideCookies.js +2 -1
package/package.json
CHANGED
|
@@ -2,6 +2,10 @@ import { WebfloRouter } from './webflo-routing/WebfloRouter.js';
|
|
|
2
2
|
import { response as responseShim, headers as headersShim } from './webflo-fetch/index.js';
|
|
3
3
|
import { LiveResponse } from './webflo-fetch/LiveResponse.js';
|
|
4
4
|
import { AppBootstrap } from './AppBootstrap.js';
|
|
5
|
+
import { HttpEvent } from './webflo-routing/HttpEvent.js';
|
|
6
|
+
import { HttpThread } from './webflo-routing/HttpThread.js';
|
|
7
|
+
import { HttpSession } from './webflo-routing/HttpSession.js';
|
|
8
|
+
import { HttpUser } from './webflo-routing/HttpUser.js';
|
|
5
9
|
import { _wq } from '../util.js';
|
|
6
10
|
|
|
7
11
|
export class WebfloRuntime {
|
|
@@ -11,6 +15,14 @@ export class WebfloRuntime {
|
|
|
11
15
|
|
|
12
16
|
static get Router() { return WebfloRouter; }
|
|
13
17
|
|
|
18
|
+
static get HttpEvent() { return HttpEvent; }
|
|
19
|
+
|
|
20
|
+
static get HttpThread() { return HttpThread; }
|
|
21
|
+
|
|
22
|
+
static get HttpSession() { return HttpSession; }
|
|
23
|
+
|
|
24
|
+
static get HttpUser() { return HttpUser; }
|
|
25
|
+
|
|
14
26
|
static create(bootstrap) { return new this(bootstrap); }
|
|
15
27
|
|
|
16
28
|
#bootstrap;
|
|
@@ -72,34 +84,33 @@ export class WebfloRuntime {
|
|
|
72
84
|
return new Request(href, init);
|
|
73
85
|
}
|
|
74
86
|
|
|
75
|
-
|
|
76
|
-
return this.constructor.
|
|
87
|
+
createHttpThread({ store, threadID, ...rest }) {
|
|
88
|
+
return this.constructor.HttpThread.create({ store, threadID, ...rest });
|
|
77
89
|
}
|
|
78
90
|
|
|
79
|
-
|
|
80
|
-
return this.constructor.
|
|
91
|
+
createHttpCookies({ request, thread, ...rest }) {
|
|
92
|
+
return this.constructor.HttpCookies.create({ request, thread, ...rest });
|
|
81
93
|
}
|
|
82
94
|
|
|
83
|
-
|
|
84
|
-
return this.constructor.
|
|
95
|
+
createHttpSession({ store, request, thread, ...rest }) {
|
|
96
|
+
return this.constructor.HttpSession.create({ store, request, thread, ...rest });
|
|
85
97
|
}
|
|
86
98
|
|
|
87
|
-
|
|
88
|
-
return this.constructor.
|
|
99
|
+
createHttpUser({ store, request, thread, client, ...rest }) {
|
|
100
|
+
return this.constructor.HttpUser.create({ store, request, thread, client, ...rest });
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
createHttpEvent({ request, thread, cookies, session, user, client, detail, signal, state, ...rest }) {
|
|
104
|
+
return this.constructor.HttpEvent.create(null, { request, thread, cookies, session, user, client, detail, signal, state, ...rest });
|
|
89
105
|
}
|
|
90
106
|
|
|
91
107
|
async dispatchNavigationEvent({ httpEvent, crossLayerFetch, clientPortB }) {
|
|
92
108
|
const { flags: FLAGS, logger: LOGGER } = this.cx;
|
|
93
|
-
|
|
94
|
-
if (httpEvent.request.method === 'GET' && httpEvent.url.query['_rid']) {
|
|
95
|
-
const requestMeta = _wq(httpEvent.request, 'meta');
|
|
96
|
-
requestMeta.set('redirectID', httpEvent.url.query['_rid']);
|
|
97
|
-
requestMeta.set('carries', [].concat(await httpEvent.session.get(`carry-store:${requestMeta.get('redirectID')}`) || []));
|
|
98
|
-
await httpEvent.session.delete(`carry-store:${requestMeta.get('redirectID')}`);
|
|
99
|
-
}
|
|
109
|
+
|
|
100
110
|
// Dispatch event
|
|
101
111
|
const router = new this.constructor.Router(this, httpEvent.url.pathname);
|
|
102
|
-
await router.route(['SETUP'], httpEvent
|
|
112
|
+
await router.route(['SETUP'], httpEvent);
|
|
113
|
+
|
|
103
114
|
// Do proper routing for respone
|
|
104
115
|
const response = await new Promise(async (resolve) => {
|
|
105
116
|
let autoLiveResponse, response;
|
|
@@ -113,6 +124,7 @@ export class WebfloRuntime {
|
|
|
113
124
|
return await router.route(routeMethods, httpEvent, crossLayerFetch, remoteFetch);
|
|
114
125
|
};
|
|
115
126
|
const fullRoutingPipeline = this.bootstrap.middlewares.concat(route);
|
|
127
|
+
|
|
116
128
|
try {
|
|
117
129
|
response = await fullRoutingPipeline.reverse().reduce((next, fn) => {
|
|
118
130
|
return () => fn.call(this.cx, httpEvent, next);
|
|
@@ -121,14 +133,17 @@ export class WebfloRuntime {
|
|
|
121
133
|
console.error(e);
|
|
122
134
|
response = new Response(null, { status: 500, statusText: e.message });
|
|
123
135
|
}
|
|
136
|
+
|
|
124
137
|
if (!/Response/.test(LiveResponse.test(response))) {
|
|
125
|
-
const isLifecyleComplete = httpEvent.lifeCycleComplete();
|
|
138
|
+
const isLifecyleComplete = httpEvent.lifeCycleComplete() ?? true;
|
|
126
139
|
response = LiveResponse.test(response) !== 'Default' || !isLifecyleComplete
|
|
127
140
|
? await LiveResponse.from(response, { done: isLifecyleComplete })
|
|
128
141
|
: responseShim.from.value(response);
|
|
129
142
|
}
|
|
143
|
+
|
|
130
144
|
// Any "carry" data?
|
|
131
145
|
await this.handleCarries(httpEvent, response);
|
|
146
|
+
|
|
132
147
|
// Resolve now...
|
|
133
148
|
if (autoLiveResponse) {
|
|
134
149
|
await autoLiveResponse.replaceWith(response, { done: true });
|
|
@@ -148,7 +163,7 @@ export class WebfloRuntime {
|
|
|
148
163
|
|
|
149
164
|
// Send the X-Background-Messaging-Port header
|
|
150
165
|
// This server's event lifecycle management
|
|
151
|
-
if (!httpEvent.lifeCycleComplete()) {
|
|
166
|
+
if (!(httpEvent.lifeCycleComplete() ?? true)) {
|
|
152
167
|
if (this.isClientSide) {
|
|
153
168
|
const responseMeta = _wq(response, 'meta');
|
|
154
169
|
responseMeta.set('background_port', clientPortB);
|
|
@@ -191,44 +206,17 @@ export class WebfloRuntime {
|
|
|
191
206
|
}
|
|
192
207
|
|
|
193
208
|
async handleCarries(httpEvent, response) {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
// Save back to URL
|
|
199
|
-
if (requestMeta.get('carries')?.length) {
|
|
200
|
-
await httpEvent.session.set(`carry-store:${requestMeta.get('redirectID')}`, requestMeta.get('carries'));
|
|
201
|
-
requestMeta.set('carries', []);
|
|
202
|
-
}
|
|
203
|
-
// Stash current byte of "carry"
|
|
204
|
-
if (responseMeta.has('carry')) {
|
|
205
|
-
const $url = new URL(response.headers.get('Location'), httpEvent.request.url);
|
|
206
|
-
if ($url.searchParams.has('_rid')) {
|
|
207
|
-
// If the URL already has a rid, append the new one
|
|
208
|
-
const existingRedirectID = $url.searchParams.get('_rid');
|
|
209
|
-
const existingData = await httpEvent.session.get(`carry-store:${existingRedirectID}`);
|
|
210
|
-
const combinedData = [].concat(responseMeta.get('carry'), existingData || []);
|
|
211
|
-
// Save the combined data back to the session
|
|
212
|
-
await httpEvent.session.set(`carry-store:${existingRedirectID}`, combinedData);
|
|
213
|
-
} else {
|
|
214
|
-
// If not, create a new rid
|
|
215
|
-
const redirectID = (0 | Math.random() * 9e6).toString(36);
|
|
216
|
-
$url.searchParams.set('_rid', redirectID);
|
|
217
|
-
await httpEvent.session.set(`carry-store:${redirectID}`, [].concat(responseMeta.get('carry')));
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
} else {
|
|
209
|
+
if (!response.headers.get('Location')) {
|
|
210
|
+
const status = await httpEvent.thread.consume('status');
|
|
211
|
+
await httpEvent.thread.clear();
|
|
212
|
+
if (!status) return;
|
|
221
213
|
// Fire redirect message?
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
}, { once: true });
|
|
229
|
-
}));
|
|
230
|
-
}
|
|
231
|
-
requestMeta.set('carries', []);
|
|
214
|
+
httpEvent.waitUntil(new Promise((resolve) => {
|
|
215
|
+
httpEvent.client.wqLifecycle.open.then(async () => {
|
|
216
|
+
httpEvent.client.postMessage(status, { wqEventOptions: { type: 'alert' } });
|
|
217
|
+
resolve();
|
|
218
|
+
}, { once: true });
|
|
219
|
+
}));
|
|
232
220
|
}
|
|
233
221
|
}
|
|
234
222
|
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { HttpCookies } from '../webflo-routing/HttpCookies.js';
|
|
2
2
|
|
|
3
3
|
export class ClientSideCookies extends HttpCookies {
|
|
4
|
-
static create({ request }) {
|
|
4
|
+
static create({ request, thread }) {
|
|
5
5
|
return new this({
|
|
6
6
|
request,
|
|
7
|
+
thread,
|
|
7
8
|
entries: document.cookie.split(';').map((c) => c.split('=').map((s) => s.trim()))
|
|
8
9
|
});
|
|
9
10
|
}
|
|
@@ -7,9 +7,6 @@ import { response as responseShim } from '../webflo-fetch/index.js';
|
|
|
7
7
|
import { LiveResponse } from '../webflo-fetch/LiveResponse.js';
|
|
8
8
|
import { WQStarPort } from '../webflo-messaging/WQStarPort.js';
|
|
9
9
|
import { ClientSideCookies } from './ClientSideCookies.js';
|
|
10
|
-
import { HttpSession } from '../webflo-routing/HttpSession.js';
|
|
11
|
-
import { HttpEvent } from '../webflo-routing/HttpEvent.js';
|
|
12
|
-
import { HttpUser } from '../webflo-routing/HttpUser.js';
|
|
13
10
|
import { Url } from '../webflo-url/Url.js';
|
|
14
11
|
import { _wq } from '../../util.js';
|
|
15
12
|
import '../webflo-fetch/index.js';
|
|
@@ -17,14 +14,8 @@ import '../webflo-url/index.js';
|
|
|
17
14
|
|
|
18
15
|
export class WebfloClient extends WebfloRuntime {
|
|
19
16
|
|
|
20
|
-
static get HttpEvent() { return HttpEvent; }
|
|
21
|
-
|
|
22
17
|
static get HttpCookies() { return ClientSideCookies; }
|
|
23
18
|
|
|
24
|
-
static get HttpSession() { return HttpSession; }
|
|
25
|
-
|
|
26
|
-
static get HttpUser() { return HttpUser; }
|
|
27
|
-
|
|
28
19
|
#host;
|
|
29
20
|
get host() { return this.#host; }
|
|
30
21
|
|
|
@@ -68,19 +59,23 @@ export class WebfloClient extends WebfloRuntime {
|
|
|
68
59
|
// Bind prompt handlers
|
|
69
60
|
const promptsHandler = (e) => {
|
|
70
61
|
const message = e.data?.message
|
|
71
|
-
? e.data.message
|
|
62
|
+
? e.data.message
|
|
72
63
|
: e.data;
|
|
73
64
|
const execPromp = () => {
|
|
65
|
+
if (e.defaultPrevented) return;
|
|
74
66
|
if (e.type === 'confirm') {
|
|
75
67
|
e.wqRespondWith(confirm(message));
|
|
76
68
|
} else if (e.type === 'prompt') {
|
|
77
69
|
e.wqRespondWith(prompt(message));
|
|
70
|
+
} else if (e.type === 'alert') {
|
|
71
|
+
alert(message);
|
|
78
72
|
}
|
|
79
73
|
};
|
|
80
74
|
window.queueMicrotask(execPromp);
|
|
81
75
|
};
|
|
82
76
|
this.background.addEventListener('confirm', promptsHandler, { signal: instanceController.signal });
|
|
83
77
|
this.background.addEventListener('prompt', promptsHandler, { signal: instanceController.signal });
|
|
78
|
+
this.background.addEventListener('alert', promptsHandler, { signal: instanceController.signal });
|
|
84
79
|
await this.setupCapabilities();
|
|
85
80
|
this.control();
|
|
86
81
|
await this.hydrate();
|
|
@@ -242,7 +237,8 @@ export class WebfloClient extends WebfloRuntime {
|
|
|
242
237
|
#prevEvent;
|
|
243
238
|
createHttpEvent(init, singleton = true) {
|
|
244
239
|
if (singleton && this.#prevEvent) {
|
|
245
|
-
|
|
240
|
+
// TODO
|
|
241
|
+
//this.#prevEvent.abort();
|
|
246
242
|
}
|
|
247
243
|
const httpEvent = super.createHttpEvent(init);
|
|
248
244
|
this.$instanceController.signal.addEventListener('abort', () => httpEvent.abort(), { once: true });
|
|
@@ -266,20 +262,30 @@ export class WebfloClient extends WebfloRuntime {
|
|
|
266
262
|
}
|
|
267
263
|
// Create and route request
|
|
268
264
|
scopeObj.request = this.createRequest(scopeObj.url, scopeObj.init);
|
|
265
|
+
scopeObj.thread = this.createHttpThread({
|
|
266
|
+
store: this.createStorage('thread'),
|
|
267
|
+
threadId: scopeObj.url.searchParams.get('_thread'),
|
|
268
|
+
realm: 1
|
|
269
|
+
});
|
|
269
270
|
scopeObj.cookies = this.createHttpCookies({
|
|
270
|
-
request: scopeObj.request
|
|
271
|
+
request: scopeObj.request,
|
|
272
|
+
thread: scopeObj.thread,
|
|
273
|
+
realm: 1
|
|
271
274
|
});
|
|
272
275
|
scopeObj.session = this.createHttpSession({
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
+
store: this.createStorage('session'),
|
|
277
|
+
request: scopeObj.request,
|
|
278
|
+
thread: scopeObj.thread,
|
|
279
|
+
realm: 1
|
|
280
|
+
});
|
|
276
281
|
const wqMessageChannel = new WQMessageChannel;
|
|
277
282
|
scopeObj.clientRequestRealtime = wqMessageChannel.port1;
|
|
278
283
|
scopeObj.user = this.createHttpUser({
|
|
279
284
|
store: this.createStorage('user'),
|
|
280
285
|
request: scopeObj.request,
|
|
286
|
+
thread: scopeObj.thread,
|
|
281
287
|
client: scopeObj.clientRequestRealtime,
|
|
282
|
-
|
|
288
|
+
realm: 1
|
|
283
289
|
});
|
|
284
290
|
if (window.webqit?.oohtml?.configs) {
|
|
285
291
|
const { BINDINGS_API: { api: bindingsConfig } = {}, } = window.webqit.oohtml.configs;
|
|
@@ -287,6 +293,7 @@ export class WebfloClient extends WebfloRuntime {
|
|
|
287
293
|
}
|
|
288
294
|
scopeObj.httpEvent = this.createHttpEvent({
|
|
289
295
|
request: scopeObj.request,
|
|
296
|
+
thread: scopeObj.thread,
|
|
290
297
|
client: scopeObj.clientRequestRealtime,
|
|
291
298
|
cookies: scopeObj.cookies,
|
|
292
299
|
session: scopeObj.session,
|
|
@@ -294,6 +301,7 @@ export class WebfloClient extends WebfloRuntime {
|
|
|
294
301
|
detail: scopeObj.detail,
|
|
295
302
|
signal: init.signal,
|
|
296
303
|
state: scopeObj.UIState,
|
|
304
|
+
realm: 1
|
|
297
305
|
}, true);
|
|
298
306
|
// Set pre-request states
|
|
299
307
|
Observer.set(this.navigator, {
|
|
@@ -434,15 +442,18 @@ export class WebfloClient extends WebfloRuntime {
|
|
|
434
442
|
async transitionUI(updateCallback) {
|
|
435
443
|
if (document.startViewTransition && this.withViewTransitions) {
|
|
436
444
|
const synthesizeWhile = window.webqit?.realdom?.synthesizeWhile || ((callback) => callback());
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
445
|
+
return new Promise(async (resolve) => {
|
|
446
|
+
await synthesizeWhile(async () => {
|
|
447
|
+
Observer.set(this.transition, 'phase', 1);
|
|
448
|
+
const viewTransition = document.startViewTransition(updateCallback);
|
|
449
|
+
try { await viewTransition.updateCallbackDone; } catch (e) { console.log(e); }
|
|
450
|
+
Observer.set(this.transition, 'phase', 2);
|
|
451
|
+
try { await viewTransition.ready; } catch (e) { console.log(e); }
|
|
452
|
+
Observer.set(this.transition, 'phase', 3);
|
|
453
|
+
try { await viewTransition.finished; } catch (e) { console.log(e); }
|
|
454
|
+
Observer.set(this.transition, 'phase', 0);
|
|
455
|
+
resolve();
|
|
456
|
+
});
|
|
446
457
|
});
|
|
447
458
|
} else await updateCallback();
|
|
448
459
|
}
|
|
@@ -48,7 +48,7 @@ export class WebfloRootClient2 extends WebfloRootClient1 {
|
|
|
48
48
|
const init = {
|
|
49
49
|
method: formData && 'POST' || 'GET',
|
|
50
50
|
body: formData,
|
|
51
|
-
signal
|
|
51
|
+
//signal TODO: auto-aborts on a redirect response which thus fails to parse
|
|
52
52
|
};
|
|
53
53
|
this.updateCurrentEntry({
|
|
54
54
|
state: {
|
|
@@ -140,40 +140,6 @@ export const response = {
|
|
|
140
140
|
return instance;
|
|
141
141
|
}
|
|
142
142
|
},
|
|
143
|
-
redirect: {
|
|
144
|
-
value: function (url, status = 302) {
|
|
145
|
-
if (typeof url !== 'string' && !(url instanceof URL)) {
|
|
146
|
-
throw new Error('Redirect URL must be a string or URL!');
|
|
147
|
-
}
|
|
148
|
-
if (typeof status !== 'number') {
|
|
149
|
-
throw new Error('Redirect code must be a number!');
|
|
150
|
-
}
|
|
151
|
-
return new Response(null, { status, headers: { Location: url } });
|
|
152
|
-
}
|
|
153
|
-
},
|
|
154
|
-
redirectWith: {
|
|
155
|
-
value: function (url, ...args) {
|
|
156
|
-
if (typeof url !== 'string' && !(url instanceof URL)) {
|
|
157
|
-
throw new Error('Redirect URL must be a string or URL!');
|
|
158
|
-
}
|
|
159
|
-
let status = 302;
|
|
160
|
-
if (!_isObject(args[0])) {
|
|
161
|
-
status = args.shift();
|
|
162
|
-
}
|
|
163
|
-
if (typeof status !== 'number') {
|
|
164
|
-
throw new Error('Redirect code must be a number!');
|
|
165
|
-
}
|
|
166
|
-
if (args.some((arg) => !_isObject(arg))) {
|
|
167
|
-
throw new Error('Redirect arguments must be objects!');
|
|
168
|
-
}
|
|
169
|
-
const responseInstance = new Response(null, { status, headers: { Location: url } });
|
|
170
|
-
if (args.length) {
|
|
171
|
-
const responseMeta = _wq(responseInstance, 'meta');
|
|
172
|
-
responseMeta.set('carry', args);
|
|
173
|
-
}
|
|
174
|
-
return responseInstance;
|
|
175
|
-
}
|
|
176
|
-
},
|
|
177
143
|
prototype: {
|
|
178
144
|
status: {
|
|
179
145
|
get: function () {
|
|
@@ -393,6 +359,7 @@ export function renderHttpMessageInit(httpMessageInit) {
|
|
|
393
359
|
}
|
|
394
360
|
|
|
395
361
|
export async function parseHttpMessage(httpMessage) {
|
|
362
|
+
if (!httpMessage.body) return null;
|
|
396
363
|
let result;
|
|
397
364
|
const contentType = httpMessage.headers.get('Content-Type') || '';
|
|
398
365
|
if (contentType === 'application/x-www-form-urlencoded' || contentType.startsWith('multipart/form-data')) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { _isObject } from '@webqit/util/js/index.js';
|
|
2
1
|
import { _even } from '@webqit/util/obj/index.js';
|
|
2
|
+
import { _isObject } from '@webqit/util/js/index.js';
|
|
3
3
|
import { renderCookieObjToString } from '../webflo-fetch/index.js';
|
|
4
4
|
import { HttpState } from './HttpState.js';
|
|
5
5
|
|
|
@@ -7,9 +7,9 @@ export class HttpCookies extends HttpState {
|
|
|
7
7
|
|
|
8
8
|
#originals;
|
|
9
9
|
|
|
10
|
-
constructor({ request, entries = [] }) {
|
|
10
|
+
constructor({ request, thread = null, entries = [] }) {
|
|
11
11
|
entries = [...entries].map(([key, value]) => [key, !_isObject(value) ? { name: key, value } : value]);
|
|
12
|
-
super({ store: new Map(entries), request,
|
|
12
|
+
super({ store: new Map(entries), request, thread });
|
|
13
13
|
this.#originals = new Map(entries);
|
|
14
14
|
}
|
|
15
15
|
|
|
@@ -26,10 +26,14 @@ export class HttpEvent {
|
|
|
26
26
|
});
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
get signal() { return this.#abortController.signal; }
|
|
30
|
+
|
|
29
31
|
get url() { return this.#url; }
|
|
30
32
|
|
|
31
33
|
get request() { return this.#init.request; }
|
|
32
34
|
|
|
35
|
+
get thread() { return this.#init.thread; }
|
|
36
|
+
|
|
33
37
|
get client() { return this.#init.client; }
|
|
34
38
|
|
|
35
39
|
get cookies() { return this.#init.cookies; }
|
|
@@ -40,8 +44,6 @@ export class HttpEvent {
|
|
|
40
44
|
|
|
41
45
|
get detail() { return this.#init.detail; }
|
|
42
46
|
|
|
43
|
-
get signal() { return this.#abortController.signal; }
|
|
44
|
-
|
|
45
47
|
get state() { return { ...(this.#init.state || {}) }; }
|
|
46
48
|
|
|
47
49
|
#lifecyclePromises = new Set;
|
|
@@ -58,6 +60,9 @@ export class HttpEvent {
|
|
|
58
60
|
if (this.#lifecyclePromises.dirty && !this.#lifecyclePromises.size) {
|
|
59
61
|
throw new Error('Event lifecycle already complete.');
|
|
60
62
|
}
|
|
63
|
+
if (this.#parentEvent) {
|
|
64
|
+
this.#parentEvent.waitUntil(promise);
|
|
65
|
+
}
|
|
61
66
|
promise = Promise.resolve(promise);
|
|
62
67
|
this.#lifecyclePromises.add(promise);
|
|
63
68
|
this.#lifecyclePromises.dirty = true;
|
|
@@ -78,8 +83,11 @@ export class HttpEvent {
|
|
|
78
83
|
if (returningThePromise) {
|
|
79
84
|
return this.#lifeCycleResolutionPromise;
|
|
80
85
|
}
|
|
81
|
-
|
|
82
|
-
|
|
86
|
+
if (this.#lifecyclePromises.dirty === undefined) {
|
|
87
|
+
// Hasn't been initialized yet
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
return !this.#lifecyclePromises.size;
|
|
83
91
|
}
|
|
84
92
|
|
|
85
93
|
async waitUntil(promise) {
|
|
@@ -97,13 +105,49 @@ export class HttpEvent {
|
|
|
97
105
|
await this.#internalLiveResponse.replaceWith(data, ...args);
|
|
98
106
|
}
|
|
99
107
|
|
|
108
|
+
async redirect(url, status = 302) {
|
|
109
|
+
if (typeof url !== 'string' && !(url instanceof URL)) {
|
|
110
|
+
throw new Error('Redirect URL must be a string or URL!');
|
|
111
|
+
}
|
|
112
|
+
let options = {};
|
|
113
|
+
if (_isObject(status)) {
|
|
114
|
+
({ status = 302, ...options } = status);
|
|
115
|
+
}
|
|
116
|
+
if (typeof status !== 'number') {
|
|
117
|
+
throw new Error('Redirect code must be a number!');
|
|
118
|
+
}
|
|
119
|
+
return await this.respondWith(null, { status, ...options, headers: { Location: url } });
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async redirectWith(url, data, status = 302) {
|
|
123
|
+
if (typeof url !== 'string' && !(url instanceof URL)) {
|
|
124
|
+
throw new Error('Redirect URL must be a string or URL!');
|
|
125
|
+
}
|
|
126
|
+
let options = {};
|
|
127
|
+
if (_isObject(status)) {
|
|
128
|
+
({ status = 302, ...options } = status);
|
|
129
|
+
}
|
|
130
|
+
if (typeof status !== 'number') {
|
|
131
|
+
throw new Error('Redirect code must be a number!');
|
|
132
|
+
}
|
|
133
|
+
//-----
|
|
134
|
+
const urlRewrite = new URL(url, this.request.url);
|
|
135
|
+
const newThread = this.thread.extend(urlRewrite.searchParams.get('_thread'));
|
|
136
|
+
urlRewrite.searchParams.set('_thread', newThread.threadID);
|
|
137
|
+
await newThread.append('back', this.request.url.replace(urlRewrite.origin, ''));
|
|
138
|
+
for (const [key, value] of Object.entries(data)) {
|
|
139
|
+
await newThread.append(key, value);
|
|
140
|
+
}
|
|
141
|
+
//-----
|
|
142
|
+
return await this.respondWith(null, { status, ...options, headers: { Location: urlRewrite.href } });
|
|
143
|
+
}
|
|
144
|
+
|
|
100
145
|
clone(init = {}) {
|
|
101
146
|
return this.constructor.create(this.#parentEvent, { ...this.#init, ...init });
|
|
102
147
|
}
|
|
103
148
|
|
|
104
149
|
extend(init = {}) {
|
|
105
150
|
const instance = this.constructor.create(this/*Main difference from clone*/, { ...this.#init, ...(init || {}) });
|
|
106
|
-
if (init !== false) this.#extendLifecycle(instance.lifeCycleComplete(true));
|
|
107
151
|
return instance;
|
|
108
152
|
}
|
|
109
153
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { HttpState } from './HttpState.js';
|
|
2
2
|
|
|
3
3
|
export class HttpSession extends HttpState {
|
|
4
|
-
static create({ store, request }) {
|
|
4
|
+
static create({ store, request, thread }) {
|
|
5
5
|
return new this({
|
|
6
6
|
store,
|
|
7
7
|
request,
|
|
8
|
-
|
|
8
|
+
thread
|
|
9
9
|
});
|
|
10
10
|
}
|
|
11
11
|
}
|
|
@@ -2,16 +2,16 @@ import { _isObject } from '@webqit/util/js/index.js';
|
|
|
2
2
|
import { _even } from '@webqit/util/obj/index.js';
|
|
3
3
|
|
|
4
4
|
export class HttpState {
|
|
5
|
-
|
|
5
|
+
|
|
6
6
|
#store;
|
|
7
7
|
#request;
|
|
8
|
-
#
|
|
8
|
+
#thread;
|
|
9
9
|
#modified = false;
|
|
10
10
|
|
|
11
|
-
constructor({ store, request,
|
|
11
|
+
constructor({ store, request, thread }) {
|
|
12
12
|
this.#store = store || new Map;
|
|
13
13
|
this.#request = request;
|
|
14
|
-
this.#
|
|
14
|
+
this.#thread = thread === true ? this : thread;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
async has(key) { return await this.#store.has(key); }
|
|
@@ -59,7 +59,7 @@ export class HttpState {
|
|
|
59
59
|
|
|
60
60
|
async forEach(callback) { (await this.entries()).forEach(([key, value], i) => callback(value, key, i)); }
|
|
61
61
|
|
|
62
|
-
[
|
|
62
|
+
[Symbol.iterator]() { return this.entries().then((entries) => entries[Symbol.iterator]()); }
|
|
63
63
|
|
|
64
64
|
get size() { return this.#store.sizs; }
|
|
65
65
|
|
|
@@ -97,9 +97,15 @@ export class HttpState {
|
|
|
97
97
|
handler = { callback: handler };
|
|
98
98
|
} else if (typeof handler === 'string') {
|
|
99
99
|
handler = { url: handler };
|
|
100
|
-
} else if (
|
|
100
|
+
} else if (!(_isObject(handler) && (handler = { ...handler }))
|
|
101
|
+
|| typeof handler.callback !== 'function' && typeof handler.url !== 'string') {
|
|
101
102
|
throw new Error(`Handler must be either an URL or a function or an object specifying either an URL (handler.url) or a function (handler.callback)`);
|
|
102
103
|
}
|
|
104
|
+
if (_isObject(handler.with)) {
|
|
105
|
+
handler.with = { ...handler.with };
|
|
106
|
+
} else if (handler.with) {
|
|
107
|
+
throw new Error(`The "with" parameter must be a valid JSON object`);
|
|
108
|
+
}
|
|
103
109
|
$handlers.push(handler);
|
|
104
110
|
}
|
|
105
111
|
this.#handlers.set(attr, $handlers);
|
|
@@ -115,7 +121,7 @@ export class HttpState {
|
|
|
115
121
|
if (!handlers) {
|
|
116
122
|
throw new Error(`No handler defined for the user attribute: ${attr}`);
|
|
117
123
|
}
|
|
118
|
-
for (let i = 0; i < handlers.length; i
|
|
124
|
+
for (let i = 0; i < handlers.length; i++) {
|
|
119
125
|
const handler = handlers[i];
|
|
120
126
|
if (handler.callback) {
|
|
121
127
|
const returnValue = await handler.callback(this, attr);
|
|
@@ -129,20 +135,19 @@ export class HttpState {
|
|
|
129
135
|
continue main;
|
|
130
136
|
}
|
|
131
137
|
const urlRewrite = new URL(handler.url, this.#request.url);
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if (handler.
|
|
136
|
-
|
|
137
|
-
|
|
138
|
+
const newThread = this.#thread.extend(urlRewrite.searchParams.get('_thread'));
|
|
139
|
+
urlRewrite.searchParams.set('_thread', newThread.threadID);
|
|
140
|
+
await newThread.append('back', this.#request.url.replace(urlRewrite.origin, ''));
|
|
141
|
+
if (handler.with) {
|
|
142
|
+
for (const [key, value] of Object.entries(handler.with)) {
|
|
143
|
+
await newThread.append(key, value);
|
|
138
144
|
}
|
|
139
|
-
const messageID = (0 | Math.random() * 9e6).toString(36);
|
|
140
|
-
urlRewrite.searchParams.set('redirect-message', messageID);
|
|
141
|
-
await this.#session.set(`redirect-message:${messageID}`, { status: { type: handler.type || 'info', message: handler.message }});
|
|
142
145
|
}
|
|
143
|
-
return new Response(null, {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
+
return new Response(null, {
|
|
147
|
+
status: 302, headers: {
|
|
148
|
+
Location: urlRewrite
|
|
149
|
+
}
|
|
150
|
+
});
|
|
146
151
|
}
|
|
147
152
|
}
|
|
148
153
|
entries.push(await this.get(attr));
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
export class HttpThread {
|
|
2
|
+
|
|
3
|
+
static create({ store, threadID, realm }) {
|
|
4
|
+
if (!threadID || !(new RegExp(`^wq\\.${realm}\\.`)).test(threadID)) {
|
|
5
|
+
threadID = `wq.${realm}.${crypto.randomUUID()}`;
|
|
6
|
+
}
|
|
7
|
+
return new this({ store, threadID, realm });
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
#store;
|
|
11
|
+
#threadID;
|
|
12
|
+
#realm;
|
|
13
|
+
|
|
14
|
+
get threadID() { return this.#threadID; }
|
|
15
|
+
|
|
16
|
+
constructor({ store, threadID, realm }) {
|
|
17
|
+
this.#store = store || new Map;
|
|
18
|
+
this.#threadID = threadID;
|
|
19
|
+
this.#realm = realm;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
extend(_threadID = null) {
|
|
23
|
+
return this.constructor.create({
|
|
24
|
+
store: this.#store,
|
|
25
|
+
threadID: _threadID,
|
|
26
|
+
realm: this.#realm
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async keys() {
|
|
31
|
+
const thread = await this.#store.get(this.#threadID) || {};
|
|
32
|
+
return Object.keys(thread);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async has(key, filter = null) {
|
|
36
|
+
if (filter === true || !filter) return (await this.keys()).includes(key);
|
|
37
|
+
const thread = await this.#store.get(this.#threadID) || {};
|
|
38
|
+
const values = [].concat(thread[key] ?? []);
|
|
39
|
+
return values.findIndex(filter) !== -1;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async append(key, value) {
|
|
43
|
+
const thread = await this.#store.get(this.#threadID) || {};
|
|
44
|
+
thread[key] = [].concat(thread[key] ?? []);
|
|
45
|
+
thread[key].push(value);
|
|
46
|
+
await this.#store.set(this.#threadID, thread);
|
|
47
|
+
return this;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async consume(key, filter = null) {
|
|
51
|
+
const thread = await this.#store.get(this.#threadID) || {};
|
|
52
|
+
const values = [].concat(thread[key] ?? []);
|
|
53
|
+
|
|
54
|
+
let value;
|
|
55
|
+
if (filter === true) {
|
|
56
|
+
delete thread[key];
|
|
57
|
+
value = values;
|
|
58
|
+
} else if (filter) {
|
|
59
|
+
const i = values.findIndex(filter);
|
|
60
|
+
if (i !== -1) {
|
|
61
|
+
value = values.splice(i, 1)[0];
|
|
62
|
+
}
|
|
63
|
+
} else { value = values.pop(); }
|
|
64
|
+
|
|
65
|
+
if (!values.length) {
|
|
66
|
+
delete thread[key];
|
|
67
|
+
}
|
|
68
|
+
if (!Object.keys(thread).length) {
|
|
69
|
+
await this.#store.delete(this.#threadID);
|
|
70
|
+
} else {
|
|
71
|
+
await this.#store.set(this.#threadID, thread);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return value;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async clear() {
|
|
78
|
+
await this.#store.delete(this.#threadID);
|
|
79
|
+
return this;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -2,17 +2,17 @@ import { HttpState } from './HttpState.js';
|
|
|
2
2
|
|
|
3
3
|
export class HttpUser extends HttpState {
|
|
4
4
|
|
|
5
|
-
static create({ store, request,
|
|
6
|
-
return new this({ store, request,
|
|
5
|
+
static create({ store, request, thread, client }) {
|
|
6
|
+
return new this({ store, request, thread, client });
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
#client;
|
|
10
10
|
|
|
11
|
-
constructor({ store, request,
|
|
11
|
+
constructor({ store, request, thread, client }) {
|
|
12
12
|
super({
|
|
13
13
|
store,
|
|
14
14
|
request,
|
|
15
|
-
|
|
15
|
+
thread
|
|
16
16
|
});
|
|
17
17
|
this.#client = client;
|
|
18
18
|
}
|
|
@@ -15,6 +15,13 @@ export class WebfloRouter {
|
|
|
15
15
|
|
|
16
16
|
async route(method, event, _default = null, remoteFetch = null) {
|
|
17
17
|
const $this = this;
|
|
18
|
+
const callWebfloDefault = async (thisContext, thisTick) => {
|
|
19
|
+
let returnValue;
|
|
20
|
+
if (_default) {
|
|
21
|
+
returnValue = await _default.call(thisContext, thisTick.event, remoteFetch);
|
|
22
|
+
}
|
|
23
|
+
return returnValue;
|
|
24
|
+
};
|
|
18
25
|
// ----------------
|
|
19
26
|
// The loop
|
|
20
27
|
// ----------------
|
|
@@ -33,10 +40,7 @@ export class WebfloRouter {
|
|
|
33
40
|
return next(thisTick);
|
|
34
41
|
}
|
|
35
42
|
// Exports not found and directory not found
|
|
36
|
-
|
|
37
|
-
return await _default.call(thisContext, thisTick.event, remoteFetch);
|
|
38
|
-
}
|
|
39
|
-
return;
|
|
43
|
+
return callWebfloDefault(thisContext, thisTick);
|
|
40
44
|
}
|
|
41
45
|
// -------------
|
|
42
46
|
// Broadcast any hints exported by handler
|
|
@@ -190,15 +194,7 @@ export class WebfloRouter {
|
|
|
190
194
|
}
|
|
191
195
|
});
|
|
192
196
|
}
|
|
193
|
-
|
|
194
|
-
if (_default) {
|
|
195
|
-
returnValue = await _default.call(thisContext, thisTick.event, remoteFetch);
|
|
196
|
-
}
|
|
197
|
-
try {
|
|
198
|
-
// IMPORTANT: Explicitly terminate the event lifecycle if nothing extends it
|
|
199
|
-
await thisTick.event.waitUntil();
|
|
200
|
-
} catch(e) {}
|
|
201
|
-
return returnValue;
|
|
197
|
+
return callWebfloDefault(thisContext, thisTick);
|
|
202
198
|
};
|
|
203
199
|
|
|
204
200
|
return next({
|
|
@@ -2,10 +2,11 @@ import { headers as headersShim } from '../webflo-fetch/index.js';
|
|
|
2
2
|
import { HttpCookies } from '../webflo-routing/HttpCookies.js';
|
|
3
3
|
|
|
4
4
|
export class ServerSideCookies extends HttpCookies {
|
|
5
|
-
static create({ request }) {
|
|
5
|
+
static create({ request, thread }) {
|
|
6
6
|
const cookies = headersShim.get.value.call(request.headers, 'Cookie', true);
|
|
7
7
|
return new this({
|
|
8
8
|
request,
|
|
9
|
+
thread,
|
|
9
10
|
entries: cookies.map((c) => [c.name, c])
|
|
10
11
|
});
|
|
11
12
|
}
|
|
@@ -3,10 +3,11 @@ import { headers as headersShim } from '../webflo-fetch/index.js';
|
|
|
3
3
|
|
|
4
4
|
export class ServerSideSession extends HttpSession {
|
|
5
5
|
|
|
6
|
-
static create({ store, request, sessionID, ttl }) {
|
|
6
|
+
static create({ store, request, thread, sessionID, ttl }) {
|
|
7
7
|
return new this({
|
|
8
8
|
store,
|
|
9
9
|
request,
|
|
10
|
+
thread,
|
|
10
11
|
sessionID,
|
|
11
12
|
ttl
|
|
12
13
|
});
|
|
@@ -16,14 +17,14 @@ export class ServerSideSession extends HttpSession {
|
|
|
16
17
|
get sessionID() { return this.#sessionID; }
|
|
17
18
|
#ttl;
|
|
18
19
|
|
|
19
|
-
constructor({ store, request, sessionID, ttl }) {
|
|
20
|
+
constructor({ store, request, thread, sessionID, ttl }) {
|
|
20
21
|
if (!sessionID) {
|
|
21
22
|
throw new Error(`sessionID is required`);
|
|
22
23
|
}
|
|
23
24
|
super({
|
|
24
25
|
store,
|
|
25
26
|
request,
|
|
26
|
-
|
|
27
|
+
thread,
|
|
27
28
|
});
|
|
28
29
|
this.#sessionID = sessionID;
|
|
29
30
|
this.#ttl = ttl;
|
|
@@ -20,8 +20,6 @@ import { WebfloRuntime } from '../WebfloRuntime.js';
|
|
|
20
20
|
import { WQSockPort } from '../webflo-messaging/WQSockPort.js';
|
|
21
21
|
import { ServerSideCookies } from './ServerSideCookies.js';
|
|
22
22
|
import { ServerSideSession } from './ServerSideSession.js';
|
|
23
|
-
import { HttpEvent } from '../webflo-routing/HttpEvent.js';
|
|
24
|
-
import { HttpUser } from '../webflo-routing/HttpUser.js';
|
|
25
23
|
import { response as responseShim, headers as headersShim } from '../webflo-fetch/index.js';
|
|
26
24
|
import { UseLiveTransform } from '../../build-pi/esbuild-plugin-uselive-transform.js';
|
|
27
25
|
import { createWindow } from '@webqit/oohtml-ssr';
|
|
@@ -31,14 +29,10 @@ import '../webflo-url/index.js';
|
|
|
31
29
|
|
|
32
30
|
export class WebfloServer extends WebfloRuntime {
|
|
33
31
|
|
|
34
|
-
static get HttpEvent() { return HttpEvent; }
|
|
35
|
-
|
|
36
32
|
static get HttpCookies() { return ServerSideCookies; }
|
|
37
33
|
|
|
38
34
|
static get HttpSession() { return ServerSideSession; }
|
|
39
35
|
|
|
40
|
-
static get HttpUser() { return HttpUser; }
|
|
41
|
-
|
|
42
36
|
static create(bootstrap) {
|
|
43
37
|
return new this(bootstrap);
|
|
44
38
|
}
|
|
@@ -648,33 +642,45 @@ export class WebfloServer extends WebfloRuntime {
|
|
|
648
642
|
// Request processing
|
|
649
643
|
scopeObj.autoHeaders = HEADERS.entries.filter((entry) => (new URLPattern(entry.url, url.origin)).exec(url.href)) || [];
|
|
650
644
|
scopeObj.request = this.createRequest(scopeObj.url.href, scopeObj.init, scopeObj.autoHeaders.filter((header) => header.type === 'request'));
|
|
651
|
-
scopeObj.cookies = this.createHttpCookies({
|
|
652
|
-
request: scopeObj.request
|
|
653
|
-
});
|
|
654
645
|
scopeObj.clientID = this.identifyIncoming(scopeObj.request, true);
|
|
655
646
|
scopeObj.client = this.#clients.getClient(scopeObj.clientID, true);
|
|
656
647
|
scopeObj.clientPortID = crypto.randomUUID();
|
|
657
648
|
scopeObj.clientRequestRealtime = scopeObj.client.createRequestRealtime(scopeObj.clientPortID, scopeObj.request.url);
|
|
658
649
|
scopeObj.sessionTTL = this.env('SESSION_TTL') || 2592000/*30days*/;
|
|
650
|
+
scopeObj.thread = this.createHttpThread({
|
|
651
|
+
store: this.createStorage(`${scopeObj.url.host}/thread:${scopeObj.clientID}`, scopeObj.sessionTTL),
|
|
652
|
+
threadID: scopeObj.url.searchParams.get('_thread'),
|
|
653
|
+
realm: 3
|
|
654
|
+
});
|
|
655
|
+
scopeObj.cookies = this.createHttpCookies({
|
|
656
|
+
request: scopeObj.request,
|
|
657
|
+
thread: scopeObj.thread,
|
|
658
|
+
realm: 3
|
|
659
|
+
});
|
|
659
660
|
scopeObj.session = this.createHttpSession({
|
|
660
661
|
store: this.createStorage(`${scopeObj.url.host}/session:${scopeObj.clientID}`, scopeObj.sessionTTL),
|
|
661
662
|
request: scopeObj.request,
|
|
663
|
+
thread: scopeObj.thread,
|
|
662
664
|
sessionID: scopeObj.clientID,
|
|
663
|
-
ttl: scopeObj.sessionTTL
|
|
665
|
+
ttl: scopeObj.sessionTTL,
|
|
666
|
+
realm: 3
|
|
664
667
|
});
|
|
665
668
|
scopeObj.user = this.createHttpUser({
|
|
666
669
|
store: this.createStorage(`${scopeObj.url.host}/user:${scopeObj.clientID}`, scopeObj.sessionTTL),
|
|
667
670
|
request: scopeObj.request,
|
|
671
|
+
thread: scopeObj.thread,
|
|
668
672
|
client: scopeObj.clientRequestRealtime,
|
|
669
|
-
|
|
673
|
+
realm: 3
|
|
670
674
|
});
|
|
671
675
|
scopeObj.httpEvent = this.createHttpEvent({
|
|
672
676
|
request: scopeObj.request,
|
|
677
|
+
thread: scopeObj.thread,
|
|
673
678
|
client: scopeObj.clientRequestRealtime,
|
|
674
679
|
cookies: scopeObj.cookies,
|
|
675
680
|
session: scopeObj.session,
|
|
676
681
|
user: scopeObj.user,
|
|
677
682
|
detail: scopeObj.detail,
|
|
683
|
+
realm: 3
|
|
678
684
|
});
|
|
679
685
|
// Dispatch for response
|
|
680
686
|
scopeObj.response = await this.dispatchNavigationEvent({
|
|
@@ -4,22 +4,13 @@ import { response as responseShim } from '../webflo-fetch/index.js';
|
|
|
4
4
|
import { WQBroadcastChannel } from '../webflo-messaging/WQBroadcastChannel.js';
|
|
5
5
|
import { WorkerSideWorkport } from './WorkerSideWorkport.js';
|
|
6
6
|
import { WorkerSideCookies } from './WorkerSideCookies.js';
|
|
7
|
-
import { HttpSession } from '../webflo-routing/HttpSession.js';
|
|
8
|
-
import { HttpEvent } from '../webflo-routing/HttpEvent.js';
|
|
9
|
-
import { HttpUser } from '../webflo-routing/HttpUser.js';
|
|
10
7
|
import '../webflo-fetch/index.js';
|
|
11
8
|
import '../webflo-url/index.js';
|
|
12
9
|
|
|
13
10
|
export class WebfloWorker extends WebfloRuntime {
|
|
14
11
|
|
|
15
|
-
static get HttpEvent() { return HttpEvent; }
|
|
16
|
-
|
|
17
12
|
static get HttpCookies() { return WorkerSideCookies; }
|
|
18
13
|
|
|
19
|
-
static get HttpSession() { return HttpSession; }
|
|
20
|
-
|
|
21
|
-
static get HttpUser() { return HttpUser; }
|
|
22
|
-
|
|
23
14
|
static get Workport() { return WorkerSideWorkport; }
|
|
24
15
|
|
|
25
16
|
async initialize() {
|
|
@@ -106,28 +97,40 @@ export class WebfloWorker extends WebfloRuntime {
|
|
|
106
97
|
}
|
|
107
98
|
// Create and route request
|
|
108
99
|
scopeObj.request = this.createRequest(scopeObj.url, scopeObj.init);
|
|
100
|
+
scopeObj.thread = this.createHttpThread({
|
|
101
|
+
store: this.createStorage('thread'),
|
|
102
|
+
threadId: scopeObj.url.searchParams.get('_thread'),
|
|
103
|
+
realm: 2
|
|
104
|
+
});
|
|
109
105
|
scopeObj.cookies = this.createHttpCookies({
|
|
110
|
-
request: scopeObj.request
|
|
106
|
+
request: scopeObj.request,
|
|
107
|
+
thread: scopeObj.thread,
|
|
108
|
+
realm: 2
|
|
111
109
|
});
|
|
112
110
|
scopeObj.session = this.createHttpSession({
|
|
113
111
|
store: this.createStorage('session'),
|
|
114
|
-
request: scopeObj.request
|
|
112
|
+
request: scopeObj.request,
|
|
113
|
+
thread: scopeObj.thread,
|
|
114
|
+
realm: 2
|
|
115
115
|
});
|
|
116
116
|
const requestID = crypto.randomUUID();
|
|
117
117
|
scopeObj.clientRequestRealtime = new WQBroadcastChannel(requestID);
|
|
118
118
|
scopeObj.user = this.createHttpUser({
|
|
119
119
|
store: this.createStorage('user'),
|
|
120
120
|
request: scopeObj.request,
|
|
121
|
+
thread: scopeObj.thread,
|
|
121
122
|
client: scopeObj.clientRequestRealtime,
|
|
122
|
-
|
|
123
|
+
realm: 2
|
|
123
124
|
});
|
|
124
125
|
scopeObj.httpEvent = this.createHttpEvent({
|
|
125
126
|
request: scopeObj.request,
|
|
127
|
+
thread: scopeObj.thread,
|
|
126
128
|
client: scopeObj.clientRequestRealtime,
|
|
127
129
|
cookies: scopeObj.cookies,
|
|
128
130
|
session: scopeObj.session,
|
|
129
131
|
user: scopeObj.user,
|
|
130
132
|
detail: scopeObj.detail,
|
|
133
|
+
realm: 2
|
|
131
134
|
});
|
|
132
135
|
// Dispatch for response
|
|
133
136
|
scopeObj.response = await this.dispatchNavigationEvent({
|
|
@@ -2,9 +2,10 @@ import { HttpCookies } from '../webflo-routing/HttpCookies.js';
|
|
|
2
2
|
import { headers as headersShim } from '../webflo-fetch/index.js';
|
|
3
3
|
|
|
4
4
|
export class WorkerSideCookies extends HttpCookies {
|
|
5
|
-
static create({ request }) {
|
|
5
|
+
static create({ request, thread }) {
|
|
6
6
|
return new this({
|
|
7
7
|
request,
|
|
8
|
+
thread,
|
|
8
9
|
entries: headersShim.get.value.call(request.headers, 'Cookie', true).map((c) => [c.name, c])
|
|
9
10
|
});
|
|
10
11
|
}
|