@webqit/webflo 1.0.17 → 1.0.19
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/HttpEvent.js +44 -0
- package/src/runtime-pi/WebfloRouter.js +3 -2
- package/src/runtime-pi/WebfloRuntime.js +21 -18
- package/src/runtime-pi/client/WebfloClient.js +6 -6
- package/src/runtime-pi/client/WebfloSubClient.js +0 -1
- package/src/runtime-pi/client/worker/WebfloWorker.js +1 -1
- package/src/runtime-pi/server/WebfloServer.js +7 -3
package/package.json
CHANGED
|
@@ -100,6 +100,50 @@ export class HttpEvent {
|
|
|
100
100
|
return [301, 302, 303, 307, 308].includes(this.#response?.status);
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
async stream(callback, { interval = 3000, maxClock = 30, crossNavigation = false } = {}) {
|
|
104
|
+
return new Promise((res) => {
|
|
105
|
+
const state = { connected: false, navigatedAway: false };
|
|
106
|
+
const start = () => {
|
|
107
|
+
const poll = async (maxClock) => {
|
|
108
|
+
await new Promise(($res) => setTimeout($res, interval));
|
|
109
|
+
if (maxClock === 0 || !state.connected || state.navigatedAway) {
|
|
110
|
+
res(callback());
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
await callback(async (response, endOfStream = false) => {
|
|
114
|
+
if (endOfStream) {
|
|
115
|
+
res(response);
|
|
116
|
+
} else {
|
|
117
|
+
await this.client.postMessage(response, { messageType: 'response' });
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
poll(typeof maxClock === 'number' && maxClock > 0 ? --maxClock : maxClock);
|
|
121
|
+
};
|
|
122
|
+
poll(maxClock);
|
|
123
|
+
};
|
|
124
|
+
// Life cycle management
|
|
125
|
+
this.client.on('connected', () => {
|
|
126
|
+
state.connected = true;
|
|
127
|
+
start();
|
|
128
|
+
});
|
|
129
|
+
this.client.on('empty', () => {
|
|
130
|
+
state.connected = false;
|
|
131
|
+
});
|
|
132
|
+
this.client.handleMessages('navigation', (e) => {
|
|
133
|
+
if (!crossNavigation
|
|
134
|
+
|| (crossNavigation === -1 && e.data.pathname === this.url.pathname)
|
|
135
|
+
|| (typeof crossNavigation === 'function' && !crossNavigation(e.data))) {
|
|
136
|
+
state.navigatedAway = true;
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
setTimeout(() => {
|
|
140
|
+
if (!state.connected) {
|
|
141
|
+
res();
|
|
142
|
+
}
|
|
143
|
+
}, 30000/*30sec*/);
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
103
147
|
async with(url, init = {}) {
|
|
104
148
|
if (!this.request) {
|
|
105
149
|
return new HttpEvent(this, { ...this.#init, url });
|
|
@@ -8,7 +8,7 @@ export class WebfloRouter {
|
|
|
8
8
|
this.path = _isArray(path) ? path : (path + '').split('/').filter(a => a);
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
async route(method, event, arg, _default, remoteFetch = null) {
|
|
11
|
+
async route(method, event, arg, _default, remoteFetch = null, requestLifecycle = null) {
|
|
12
12
|
|
|
13
13
|
const $this = this;
|
|
14
14
|
const $runtime = this.cx.runtime;
|
|
@@ -74,9 +74,10 @@ export class WebfloRouter {
|
|
|
74
74
|
_next.stepname = nextPathname[0];
|
|
75
75
|
// -------------
|
|
76
76
|
return new Promise(async (res) => {
|
|
77
|
-
thisTick.event.onRespondWith = (response) => {
|
|
77
|
+
thisTick.event.onRespondWith = async (response) => {
|
|
78
78
|
thisTick.event.onRespondWith = null;
|
|
79
79
|
res(response);
|
|
80
|
+
await requestLifecycle.responsePromise;
|
|
80
81
|
};
|
|
81
82
|
const $returnValue = Promise.resolve(handler.call(thisContext, thisTick.event, thisTick.arg, _next/*next*/, remoteFetch));
|
|
82
83
|
// This should listen first before waitUntil's listener
|
|
@@ -3,24 +3,27 @@ import { _isObject } from '@webqit/util/js/index.js';
|
|
|
3
3
|
export class WebfloRuntime {
|
|
4
4
|
|
|
5
5
|
async dispatch(httpEvent, context, crossLayerFetch) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
6
|
+
const requestLifecycle = {};
|
|
7
|
+
requestLifecycle.responsePromise = new Promise(async (res) => {
|
|
8
|
+
// Exec routing
|
|
9
|
+
const router = new this.constructor.Router(this.cx, httpEvent.url.pathname);
|
|
10
|
+
const route = async () => {
|
|
11
|
+
return await router.route([httpEvent.request.method, 'default'], httpEvent, context, async (event) => {
|
|
12
|
+
return crossLayerFetch(event);
|
|
13
|
+
}, (...args) => this.remoteFetch(...args), requestLifecycle);
|
|
14
|
+
};
|
|
15
|
+
try {
|
|
16
|
+
// Route for response
|
|
17
|
+
res(await (this.cx.middlewares || []).concat(route).reverse().reduce((next, fn) => {
|
|
18
|
+
return () => fn.call(this.cx, httpEvent, router, next);
|
|
19
|
+
}, null)());
|
|
20
|
+
} catch (e) {
|
|
21
|
+
console.error(e);
|
|
22
|
+
res(new Response(null, { status: 500, statusText: e.message }));
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
return await requestLifecycle.responsePromise;
|
|
26
|
+
}
|
|
24
27
|
|
|
25
28
|
async normalizeResponse(httpEvent, response, forceCommit = false) {
|
|
26
29
|
// Normalize response
|
|
@@ -369,11 +369,11 @@ export class WebfloClient extends WebfloRuntime {
|
|
|
369
369
|
scope.context = {};
|
|
370
370
|
if (window.webqit?.oohtml?.configs) {
|
|
371
371
|
const { BINDINGS_API: { api: bindingsConfig } = {}, } = window.webqit.oohtml.configs;
|
|
372
|
-
scope.context = this.host[bindingsConfig.bindings]
|
|
372
|
+
scope.context = this.host[bindingsConfig.bindings] || {};
|
|
373
373
|
}
|
|
374
|
-
if (scope.request.method === 'GET') {
|
|
375
|
-
// Ping
|
|
376
|
-
this.#backgroundMessaging.postMessage('navigation');
|
|
374
|
+
if (scope.request.method === 'GET' || (scope.request.method === 'POST' && scope.url.pathname !== this.location.pathname)) {
|
|
375
|
+
// Ping existing background process
|
|
376
|
+
this.#backgroundMessaging.postMessage({ ...Url.copy(scope.url), method: scope.request.method }, { messageType: 'navigation' });
|
|
377
377
|
}
|
|
378
378
|
// Dispatch for response
|
|
379
379
|
scope.response = await this.dispatch(scope.httpEvent, scope.context, async (event) => {
|
|
@@ -385,7 +385,7 @@ export class WebfloClient extends WebfloRuntime {
|
|
|
385
385
|
});
|
|
386
386
|
// ---------------
|
|
387
387
|
// Response processing
|
|
388
|
-
scope.hasBackgroundActivity = scope.eventLifecyclePromises.size || (scope.redirectMessage && !(scope.response instanceof Response && scope.response.headers.get('Location')));
|
|
388
|
+
scope.hasBackgroundActivity = scope.clientMessaging.isMessaging() || scope.eventLifecyclePromises.size || (scope.redirectMessage && !(scope.response instanceof Response && scope.response.headers.get('Location')));
|
|
389
389
|
scope.response = await this.normalizeResponse(scope.httpEvent, scope.response);
|
|
390
390
|
if (scope.response.headers.get('Location')) {
|
|
391
391
|
if (scope.redirectMessage) {
|
|
@@ -436,7 +436,7 @@ export class WebfloClient extends WebfloRuntime {
|
|
|
436
436
|
// Only render now
|
|
437
437
|
if ([202/*Accepted*/, 304/*Not Modified*/].includes(scope.response.status)) {
|
|
438
438
|
if (scope.backgroundMessaging) {
|
|
439
|
-
scope.backgroundMessaging.addEventListener('response', () => {
|
|
439
|
+
scope.backgroundMessaging.addEventListener('response', (e) => {
|
|
440
440
|
scope.resetStates();
|
|
441
441
|
});
|
|
442
442
|
return;
|
|
@@ -141,7 +141,6 @@ export class WebfloSubClient extends WebfloClient {
|
|
|
141
141
|
const top = (window.outerHeight - height) / 2;
|
|
142
142
|
const popup = window.open(location, '_blank', `popup=true,width=${width},height=${height},left=${left},top=${top}`);
|
|
143
143
|
if (backgroundMessaging) {
|
|
144
|
-
backgroundMessaging.postMessage('keepAlive');
|
|
145
144
|
Observer.set(this.navigator, 'redirecting', new Url/*NOT URL*/(location), { diff: true });
|
|
146
145
|
backgroundMessaging.addEventListener('close', (e) => {
|
|
147
146
|
Observer.set(this.navigator, 'redirecting', null);
|
|
@@ -183,7 +183,7 @@ export class WebfloWorker extends WebfloRuntime {
|
|
|
183
183
|
});
|
|
184
184
|
// ---------------
|
|
185
185
|
// Response processing
|
|
186
|
-
scope.hasBackgroundActivity = scope.eventLifecyclePromises.size || (scope.redirectMessage && !(scope.response instanceof Response && scope.response.headers.get('Location')));
|
|
186
|
+
scope.hasBackgroundActivity = scope.clientMessaging.isMessaging() || scope.eventLifecyclePromises.size || (scope.redirectMessage && !(scope.response instanceof Response && scope.response.headers.get('Location')));
|
|
187
187
|
scope.response = await this.normalizeResponse(scope.httpEvent, scope.response, scope.hasBackgroundActivity);
|
|
188
188
|
if (scope.hasBackgroundActivity) {
|
|
189
189
|
scope.response.headers.set('X-Background-Messaging', `ch:${scope.clientMessaging.port.name}`);
|
|
@@ -485,7 +485,9 @@ export class WebfloServer extends WebfloRuntime {
|
|
|
485
485
|
promise = Promise.resolve(promise);
|
|
486
486
|
scope.eventLifecyclePromises.add(promise);
|
|
487
487
|
scope.eventLifecyclePromises.dirty = true;
|
|
488
|
-
promise.then(() =>
|
|
488
|
+
promise.then(() => {
|
|
489
|
+
scope.eventLifecyclePromises.delete(promise);
|
|
490
|
+
});
|
|
489
491
|
},
|
|
490
492
|
respondWith: async (response, isRedirectMessage = false) => {
|
|
491
493
|
if (!isRedirectMessage && scope.eventLifecyclePromises.dirty && !scope.eventLifecyclePromises.size) {
|
|
@@ -533,7 +535,7 @@ export class WebfloServer extends WebfloRuntime {
|
|
|
533
535
|
});
|
|
534
536
|
// ---------------
|
|
535
537
|
// Response processing
|
|
536
|
-
scope.hasBackgroundActivity = scope.eventLifecyclePromises.size || (scope.redirectMessage && !(scope.response instanceof Response && scope.response.headers.get('Location')));
|
|
538
|
+
scope.hasBackgroundActivity = scope.clientMessaging.isMessaging() || scope.eventLifecyclePromises.size || (scope.redirectMessage && !(scope.response instanceof Response && scope.response.headers.get('Location')));
|
|
537
539
|
scope.response = await this.normalizeResponse(scope.httpEvent, scope.response, scope.hasBackgroundActivity);
|
|
538
540
|
if (scope.hasBackgroundActivity) {
|
|
539
541
|
scope.response.headers.set('X-Background-Messaging', `ws:${scope.clientMessaging.portID}`);
|
|
@@ -562,7 +564,9 @@ export class WebfloServer extends WebfloRuntime {
|
|
|
562
564
|
scope.clientMessaging.close();
|
|
563
565
|
}, 100);
|
|
564
566
|
});
|
|
565
|
-
} else
|
|
567
|
+
} else {
|
|
568
|
+
scope.clientMessaging.close();
|
|
569
|
+
}
|
|
566
570
|
});
|
|
567
571
|
return scope.response;
|
|
568
572
|
}
|