@webqit/webflo 1.0.19 → 1.0.20
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 +4 -3
- package/src/config-pi/runtime/Client.js +50 -46
- package/src/config-pi/runtime/Server.js +37 -14
- package/src/config-pi/runtime/client/Worker.js +22 -20
- package/src/runtime-pi/HttpEvent.js +34 -19
- package/src/runtime-pi/HttpUser.js +35 -36
- package/src/runtime-pi/WebfloCookieStorage.js +8 -8
- package/src/runtime-pi/WebfloRouter.js +2 -2
- package/src/runtime-pi/WebfloRuntime.js +7 -2
- package/src/runtime-pi/WebfloStorage.js +47 -16
- package/src/runtime-pi/client/Capabilities.js +211 -0
- package/src/runtime-pi/client/CookieStorage.js +2 -2
- package/src/runtime-pi/client/SessionStorage.js +2 -2
- package/src/runtime-pi/client/WebfloClient.js +15 -23
- package/src/runtime-pi/client/WebfloRootClient1.js +55 -34
- package/src/runtime-pi/client/WebfloRootClient2.js +2 -2
- package/src/runtime-pi/client/WebfloSubClient.js +9 -5
- package/src/runtime-pi/client/Workport.js +64 -91
- package/src/runtime-pi/client/generate.js +25 -16
- package/src/runtime-pi/client/index.js +3 -2
- package/src/runtime-pi/client/worker/CookieStorage.js +2 -2
- package/src/runtime-pi/client/worker/SessionStorage.js +1 -1
- package/src/runtime-pi/client/worker/WebfloWorker.js +70 -56
- package/src/runtime-pi/client/worker/index.js +3 -2
- package/src/runtime-pi/server/CookieStorage.js +2 -2
- package/src/runtime-pi/server/SessionStorage.js +3 -3
- package/src/runtime-pi/server/WebfloServer.js +26 -11
- package/src/runtime-pi/server/index.js +1 -0
- package/src/runtime-pi/util-http.js +15 -2
- package/src/services-pi/index.js +2 -0
- package/src/services-pi/push/index.js +23 -0
- package/src/static-pi/index.js +1 -1
|
@@ -1,118 +1,91 @@
|
|
|
1
|
-
|
|
1
|
+
import { _isObject } from '@webqit/util/js/index.js';
|
|
2
|
+
import { WebfloMessagingAPI } from '../WebfloMessagingAPI.js';
|
|
3
|
+
import { WebfloMessageEvent } from '../WebfloMessageEvent.js';
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
#swParams = {};
|
|
5
|
+
export class Workport extends WebfloMessagingAPI {
|
|
5
6
|
|
|
6
|
-
#
|
|
7
|
-
get
|
|
8
|
-
#swRegistration;
|
|
7
|
+
#registration;
|
|
8
|
+
get registration() { return this.#registration; }
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
10
|
+
#params;
|
|
11
|
+
get params() { return this.#params; }
|
|
12
|
+
|
|
13
|
+
#ready;
|
|
14
|
+
get ready() { return this.#ready; }
|
|
15
|
+
|
|
16
|
+
#active;
|
|
17
|
+
get active() { return this.#active; }
|
|
18
|
+
|
|
19
|
+
static async initialize(parentNode, file, params = {}) {
|
|
20
|
+
const registration = (await navigator.serviceWorker.getRegistration())
|
|
21
|
+
|| (await navigator.serviceWorker.register(file, { scope: '/', ...params }));
|
|
22
|
+
return new this(parentNode, registration, params);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
#messageHandler;
|
|
26
|
+
constructor(parentNode, registration, params = {}) {
|
|
27
|
+
super(parentNode, params);
|
|
28
|
+
this.#registration = registration;
|
|
29
|
+
this.#params = params;
|
|
30
|
+
this.#ready = navigator.serviceWorker ? navigator.serviceWorker.ready : new Promise(() => {});
|
|
18
31
|
// Helper that updates instance's state
|
|
19
32
|
const stateChange = (target) => {
|
|
20
33
|
// target.state can be any of: "parsed", "installing", "installed", "activating", "activated", "redundant"
|
|
21
34
|
if (target.state === 'redundant') {
|
|
22
|
-
//this.remove(target);
|
|
23
35
|
} else if (target.state === 'activated') {
|
|
24
|
-
|
|
36
|
+
const existing = this.#active;
|
|
37
|
+
this.#active = target;
|
|
38
|
+
if (!existing) {
|
|
39
|
+
this.$emit('connected');
|
|
40
|
+
}
|
|
25
41
|
}
|
|
26
42
|
}
|
|
27
43
|
// We're always installing at first for a new service worker.
|
|
28
44
|
// An existing service would immediately be active
|
|
29
|
-
const
|
|
30
|
-
if (
|
|
31
|
-
stateChange(
|
|
32
|
-
|
|
45
|
+
const initial = this.#registration.active || this.#registration.waiting || this.#registration.installing;
|
|
46
|
+
if (initial) {
|
|
47
|
+
stateChange(initial);
|
|
48
|
+
initial.addEventListener('statechange', (e) => stateChange(e.target));
|
|
33
49
|
// "updatefound" event - a new worker that will control
|
|
34
50
|
// this page is installing somewhere
|
|
35
|
-
this.#
|
|
51
|
+
this.#registration.addEventListener('updatefound', () => {
|
|
36
52
|
// If updatefound is fired, it means that there's
|
|
37
53
|
// a new service worker being installed.
|
|
38
|
-
stateChange(this.#
|
|
39
|
-
this.#
|
|
54
|
+
stateChange(this.#registration.installing);
|
|
55
|
+
this.#registration.installing.addEventListener('statechange', (e) => stateChange(e.target));
|
|
40
56
|
});
|
|
41
57
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return await new Promise(async (resolve, reject) => {
|
|
46
|
-
const permissionResult = Notification.requestPermission(resolve);
|
|
47
|
-
if (permissionResult) {
|
|
48
|
-
permissionResult.then(resolve, reject);
|
|
58
|
+
this.#messageHandler = async (event) => {
|
|
59
|
+
if (!_isObject(event.data) || !['messageType', 'message'].every((k) => k in event.data)) {
|
|
60
|
+
return;
|
|
49
61
|
}
|
|
50
|
-
|
|
62
|
+
this.dispatchEvent(new WorkerMessageEvent(
|
|
63
|
+
this,
|
|
64
|
+
event.data.messageType,
|
|
65
|
+
event.data.message,
|
|
66
|
+
event.ports
|
|
67
|
+
));
|
|
68
|
+
};
|
|
69
|
+
navigator.serviceWorker.addEventListener('message', this.#messageHandler);
|
|
51
70
|
}
|
|
52
71
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
return new Notification(title, params);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
async pushSubscription(autoPrompt = true) {
|
|
62
|
-
if (!this.#swRegistration) {
|
|
63
|
-
throw new Error(`Service worker not registered`);
|
|
64
|
-
}
|
|
65
|
-
await this.#swReady;
|
|
66
|
-
const pushManager = (await this.#swRegistration).pushManager;
|
|
67
|
-
let subscription = await pushManager.getSubscription();
|
|
68
|
-
if (!subscription && autoPrompt && this.#swParams.VAPID_PUBLIC_KEY) {
|
|
69
|
-
subscription = await pushManager.subscribe({
|
|
70
|
-
userVisibleOnly: true,
|
|
71
|
-
applicationServerKey: urlBase64ToUint8Array(this.#swParams.VAPID_PUBLIC_KEY),
|
|
72
|
-
});
|
|
73
|
-
if (this.#swParams.PUSH_REGISTRATION_PUBLIC_URL) {
|
|
74
|
-
await fetch(this.#swParams.PUSH_REGISTRATION_PUBLIC_URL, {
|
|
75
|
-
method: 'POST',
|
|
76
|
-
headers: { 'Content-Type': 'application/json', },
|
|
77
|
-
body: JSON.stringify(subscription)
|
|
78
|
-
});
|
|
72
|
+
postMessage(message, transferOrOptions = []) {
|
|
73
|
+
this.on('connected', () => {
|
|
74
|
+
if (Array.isArray(transferOrOptions)) {
|
|
75
|
+
transferOrOptions = { transfer: transferOrOptions };
|
|
79
76
|
}
|
|
80
|
-
|
|
81
|
-
|
|
77
|
+
const { messageType = 'message', ...options } = transferOrOptions;
|
|
78
|
+
return this.#active.postMessage({
|
|
79
|
+
messageType,
|
|
80
|
+
message
|
|
81
|
+
}, options);
|
|
82
|
+
});
|
|
83
|
+
super.postMessage(message, transferOrOptions);
|
|
82
84
|
}
|
|
83
85
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
throw new Error(`Service worker not registered`);
|
|
87
|
-
}
|
|
88
|
-
await this.#swReady;
|
|
89
|
-
const pushManager = (await this.#swRegistration).pushManager;
|
|
90
|
-
const subscription = await pushManager.getSubscription();
|
|
91
|
-
if (subscription) {
|
|
92
|
-
subscription.unsubscribe();
|
|
93
|
-
if (subscription && this.#swParams.PUSH_REGISTRATION_PUBLIC_URL) {
|
|
94
|
-
await fetch(this.#swParams.PUSH_REGISTRATION_PUBLIC_URL, {
|
|
95
|
-
method: 'DELETE',
|
|
96
|
-
headers: { 'Content-Type': 'application/json', },
|
|
97
|
-
body: JSON.stringify(subscription)
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
}
|
|
86
|
+
close() {
|
|
87
|
+
navigator.serviceWorker.removeEventListener('message', this.#messageHandler);
|
|
101
88
|
}
|
|
102
89
|
}
|
|
103
90
|
|
|
104
|
-
|
|
105
|
-
function urlBase64ToUint8Array(base64String) {
|
|
106
|
-
var padding = '='.repeat((4 - base64String.length % 4) % 4);
|
|
107
|
-
var base64 = (base64String + padding)
|
|
108
|
-
.replace(/\-/g, '+')
|
|
109
|
-
.replace(/_/g, '/');
|
|
110
|
-
|
|
111
|
-
var rawData = window.atob(base64);
|
|
112
|
-
var outputArray = new Uint8Array(rawData.length);
|
|
113
|
-
|
|
114
|
-
for (var i = 0; i < rawData.length; ++i) {
|
|
115
|
-
outputArray[i] = rawData.charCodeAt(i);
|
|
116
|
-
}
|
|
117
|
-
return outputArray;
|
|
118
|
-
}
|
|
91
|
+
export class WorkerMessageEvent extends WebfloMessageEvent {}
|
|
@@ -25,13 +25,14 @@ export async function generate() {
|
|
|
25
25
|
if (!cx.config.deployment?.Layout) {
|
|
26
26
|
throw new Error(`The Client configurator "config.deployment.Layout" is required in context.`);
|
|
27
27
|
}
|
|
28
|
+
const env = {};
|
|
28
29
|
const clientConfig = await (new cx.config.runtime.Client(cx)).read();
|
|
29
|
-
clientConfig.env =
|
|
30
|
-
if (clientConfig.service_worker
|
|
30
|
+
clientConfig.env = env;
|
|
31
|
+
if (clientConfig.capabilities?.service_worker && !cx.config.runtime.client?.Worker) {
|
|
31
32
|
throw new Error(`The Service Worker configurator "config.runtime.client.Worker" is required in context.`);
|
|
32
33
|
}
|
|
33
34
|
const workerConfig = await (new cx.config.runtime.client.Worker(cx)).read();
|
|
34
|
-
workerConfig.env =
|
|
35
|
+
workerConfig.env = env;
|
|
35
36
|
// -----------
|
|
36
37
|
if (!cx.config.deployment?.Layout) {
|
|
37
38
|
throw new Error(`The Layout configurator "config.deployment.Layout" is required in context.`);
|
|
@@ -42,20 +43,15 @@ export async function generate() {
|
|
|
42
43
|
const dirClient = Path.resolve(cx.CWD || '', layoutConfig.CLIENT_DIR);
|
|
43
44
|
const dirWorker = Path.resolve(cx.CWD || '', layoutConfig.WORKER_DIR);
|
|
44
45
|
const dirSelf = Path.dirname(Url.fileURLToPath(import.meta.url)).replace(/\\/g, '/');
|
|
45
|
-
if (clientConfig.
|
|
46
|
+
if (clientConfig.copy_public_variables) {
|
|
46
47
|
if (!cx.config.deployment?.Env) {
|
|
47
48
|
throw new Error(`The Layout configurator "config.deployment.Env" is required in context to bundle public env.`);
|
|
48
49
|
}
|
|
49
50
|
const envConfig = await (new cx.config.deployment.Env(cx)).read();
|
|
50
|
-
const env = { ...envConfig.entries, ...process.env };
|
|
51
|
-
for (const key in env) {
|
|
51
|
+
const $env = { ...envConfig.entries, ...process.env };
|
|
52
|
+
for (const key in $env) {
|
|
52
53
|
if (!key.includes('PUBLIC_') && !key.includes('_PUBLIC')) continue;
|
|
53
|
-
|
|
54
|
-
clientConfig.env[key] = env[key];
|
|
55
|
-
}
|
|
56
|
-
if (workerConfig.bundle_public_env) {
|
|
57
|
-
workerConfig.env[key] = env[key];
|
|
58
|
-
}
|
|
54
|
+
env[key] = $env[key];
|
|
59
55
|
}
|
|
60
56
|
}
|
|
61
57
|
// -----------
|
|
@@ -114,7 +110,14 @@ export async function generate() {
|
|
|
114
110
|
gen.code.push(`const { start } = webqit.Webflo`);
|
|
115
111
|
// ------------------
|
|
116
112
|
// Bundle
|
|
117
|
-
|
|
113
|
+
const paramsObj = structuredClone(clientConfig);
|
|
114
|
+
if (paramsObj.capabilities?.service_worker) {
|
|
115
|
+
paramsObj.capabilities.service_worker = {
|
|
116
|
+
filename: workerConfig.filename,
|
|
117
|
+
scope: workerConfig.scope
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
declareStart.call(cx, gen, dirClient, dirPublic, paramsObj, spaRouting);
|
|
118
121
|
await bundle.call(cx, gen, Path.join(dirPublic, outfileMain), true/* asModule */);
|
|
119
122
|
// ------------------
|
|
120
123
|
// Embed/unembed
|
|
@@ -192,8 +195,14 @@ export async function generate() {
|
|
|
192
195
|
}
|
|
193
196
|
}
|
|
194
197
|
}
|
|
195
|
-
|
|
196
|
-
|
|
198
|
+
const paramsObj = structuredClone(workerConfig);
|
|
199
|
+
if (clientConfig.capabilities?.webpush) {
|
|
200
|
+
paramsObj.capabilities = {
|
|
201
|
+
webpush: true
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
declareStart.call(cx, gen, dirWorker, dirPublic, paramsObj, workerRouting);
|
|
205
|
+
await bundle.call(cx, gen, Path.join(dirPublic, workerroot, workerConfig.filename));
|
|
197
206
|
// ------------------
|
|
198
207
|
// Recurse
|
|
199
208
|
workerGraphCallback && workerGraphCallback(workerroot, subworkerroots);
|
|
@@ -214,7 +223,7 @@ export async function generate() {
|
|
|
214
223
|
await generateClient();
|
|
215
224
|
Fs.existsSync(sparootsFile) && Fs.unlinkSync(sparootsFile);
|
|
216
225
|
}
|
|
217
|
-
if (clientConfig.service_worker
|
|
226
|
+
if (clientConfig.capabilities?.service_worker) {
|
|
218
227
|
await generateWorker('/');
|
|
219
228
|
}
|
|
220
229
|
}
|
|
@@ -2,11 +2,12 @@ import { WebfloRootClient1 } from './WebfloRootClient1.js';
|
|
|
2
2
|
import { WebfloRootClient2 } from './WebfloRootClient2.js';
|
|
3
3
|
import { WebfloSubClient } from './WebfloSubClient.js';
|
|
4
4
|
|
|
5
|
-
export function start() {
|
|
5
|
+
export async function start() {
|
|
6
6
|
const WebfloRootClient = window.navigation ? WebfloRootClient2 : WebfloRootClient1;
|
|
7
7
|
const instance = WebfloRootClient.create(document, this || {});
|
|
8
|
-
instance.initialize();
|
|
8
|
+
await instance.initialize();
|
|
9
9
|
WebfloSubClient.defineElement();
|
|
10
|
+
return instance;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
export { WebfloSubClient } from './WebfloSubClient.js';
|
|
@@ -8,10 +8,10 @@ export class CookieStorage extends WebfloCookieStorage {
|
|
|
8
8
|
);
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
commit(response) {
|
|
11
|
+
async commit(response) {
|
|
12
12
|
for (const cookieStr of this.render()) {
|
|
13
13
|
response.headers.append('Set-Cookie', cookieStr);
|
|
14
14
|
}
|
|
15
|
-
super.commit();
|
|
15
|
+
await super.commit();
|
|
16
16
|
}
|
|
17
17
|
}
|
|
@@ -25,13 +25,13 @@ export class WebfloWorker extends WebfloRuntime {
|
|
|
25
25
|
|
|
26
26
|
static get SessionStorage() { return SessionStorage; }
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
static get HttpUser() { return HttpUser; }
|
|
29
29
|
|
|
30
30
|
static get Workport() { return Workport; }
|
|
31
31
|
|
|
32
32
|
static create(cx) {
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
return new this(this.Context.create(cx));
|
|
34
|
+
}
|
|
35
35
|
|
|
36
36
|
#cx;
|
|
37
37
|
get cx() { return this.#cx; }
|
|
@@ -44,7 +44,7 @@ export class WebfloWorker extends WebfloRuntime {
|
|
|
44
44
|
this.#cx = cx;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
initialize() {
|
|
47
|
+
async initialize() {
|
|
48
48
|
// ONINSTALL
|
|
49
49
|
const installHandler = (event) => {
|
|
50
50
|
if (this.cx.params.skip_waiting) self.skipWaiting();
|
|
@@ -53,7 +53,7 @@ export class WebfloWorker extends WebfloRuntime {
|
|
|
53
53
|
// Add files to cache
|
|
54
54
|
event.waitUntil(self.caches.open(this.cx.params.cache_name).then(async cache => {
|
|
55
55
|
if (this.cx.logger) { this.cx.logger.log('[ServiceWorker] Pre-caching resources.'); }
|
|
56
|
-
for (const urls of [
|
|
56
|
+
for (const urls of ['cache_first_urls', 'cache_only_urls']) {
|
|
57
57
|
const _urls = (this.cx.params[urls] || []).map(c => c.trim()).filter(c => c && !pattern(c, self.origin).isPattern());
|
|
58
58
|
await cache.addAll(_urls);
|
|
59
59
|
}
|
|
@@ -74,7 +74,7 @@ export class WebfloWorker extends WebfloRuntime {
|
|
|
74
74
|
return self.caches.delete(key);
|
|
75
75
|
}
|
|
76
76
|
}));
|
|
77
|
-
})
|
|
77
|
+
})
|
|
78
78
|
}
|
|
79
79
|
resolve();
|
|
80
80
|
}));
|
|
@@ -82,11 +82,11 @@ export class WebfloWorker extends WebfloRuntime {
|
|
|
82
82
|
self.addEventListener('install', installHandler);
|
|
83
83
|
self.addEventListener('activate', activateHandler);
|
|
84
84
|
const uncontrols = this.control();
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
85
|
+
return () => {
|
|
86
|
+
self.removeEventListener('install', installHandler);
|
|
87
|
+
self.removeEventListener('activate', activateHandler);
|
|
88
88
|
uncontrols();
|
|
89
|
-
|
|
89
|
+
};
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
control() {
|
|
@@ -112,42 +112,54 @@ export class WebfloWorker extends WebfloRuntime {
|
|
|
112
112
|
event.respondWith(this.navigate(event.request.url, event.request, { event }));
|
|
113
113
|
}
|
|
114
114
|
};
|
|
115
|
+
const webpushHandler = (event) => {
|
|
116
|
+
if (!(self.Notification && self.Notification.permission === 'granted')) return;
|
|
117
|
+
let data;
|
|
118
|
+
try {
|
|
119
|
+
data = event.data?.json() ?? {};
|
|
120
|
+
} catch(e) { return; }
|
|
121
|
+
const { type, title, ...params } = data;
|
|
122
|
+
if (type !== 'notification') return;
|
|
123
|
+
self.registration.showNotification(title, params);
|
|
124
|
+
};
|
|
115
125
|
self.addEventListener('fetch', fetchHandler);
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
126
|
+
self.addEventListener('push', webpushHandler);
|
|
127
|
+
return () => {
|
|
128
|
+
self.removeEventListener('fetch', fetchHandler);
|
|
129
|
+
self.removeEventListener('push', webpushHandler);
|
|
130
|
+
};
|
|
119
131
|
}
|
|
120
132
|
|
|
121
|
-
|
|
133
|
+
createRequest(href, init = {}) {
|
|
122
134
|
if (init instanceof Request && init.url === (href.href || href)) {
|
|
123
135
|
return init;
|
|
124
136
|
}
|
|
125
137
|
return new Request(href, init);
|
|
126
|
-
|
|
138
|
+
}
|
|
127
139
|
|
|
128
140
|
async navigate(url, init = {}, detail = {}) {
|
|
129
141
|
// Resolve inputs
|
|
130
|
-
|
|
142
|
+
const scope = { url, init, detail };
|
|
131
143
|
if (typeof scope.url === 'string') {
|
|
132
144
|
scope.url = new URL(scope.url, self.location.origin);
|
|
133
145
|
}
|
|
134
146
|
// ---------------
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
147
|
+
// Event lifecycle
|
|
148
|
+
scope.eventLifecyclePromises = new Set;
|
|
149
|
+
scope.eventLifecycleHooks = {
|
|
150
|
+
waitUntil: (promise) => {
|
|
151
|
+
promise = Promise.resolve(promise);
|
|
152
|
+
scope.eventLifecyclePromises.add(promise);
|
|
153
|
+
scope.eventLifecyclePromises.dirty = true;
|
|
154
|
+
promise.then(() => scope.eventLifecyclePromises.delete(promise));
|
|
155
|
+
},
|
|
156
|
+
respondWith: async (response) => {
|
|
157
|
+
if (scope.eventLifecyclePromises.dirty && !scope.eventLifecyclePromises.size) {
|
|
158
|
+
throw new Error('Final response already sent');
|
|
159
|
+
}
|
|
160
|
+
return await this.execPush(scope.clientMessaging, response);
|
|
161
|
+
},
|
|
162
|
+
};
|
|
151
163
|
// Create and route request
|
|
152
164
|
scope.request = this.createRequest(scope.url, scope.init);
|
|
153
165
|
scope.cookies = this.constructor.CookieStorage.create(scope.request);
|
|
@@ -165,13 +177,15 @@ export class WebfloWorker extends WebfloRuntime {
|
|
|
165
177
|
cookies: scope.cookies,
|
|
166
178
|
session: scope.session,
|
|
167
179
|
user: scope.user,
|
|
168
|
-
client: scope.clientMessaging
|
|
180
|
+
client: scope.clientMessaging,
|
|
181
|
+
sdk: {}
|
|
169
182
|
});
|
|
183
|
+
await this.setup(scope.httpEvent);
|
|
170
184
|
// Restore session before dispatching
|
|
171
|
-
if (scope.request.method === 'GET'
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
scope.session.delete(`redirect-message:${scope.redirectMessageID}`);
|
|
185
|
+
if (scope.request.method === 'GET'
|
|
186
|
+
&& (scope.redirectMessageID = scope.httpEvent.url.query['redirect-message'])
|
|
187
|
+
&& (scope.redirectMessage = scope.session.get(`redirect-message:${scope.redirectMessageID}`))) {
|
|
188
|
+
await scope.session.delete(`redirect-message:${scope.redirectMessageID}`);
|
|
175
189
|
}
|
|
176
190
|
// Dispatch for response
|
|
177
191
|
scope.response = await this.dispatch(scope.httpEvent, {}, async (event) => {
|
|
@@ -182,30 +196,30 @@ export class WebfloWorker extends WebfloRuntime {
|
|
|
182
196
|
return await this.remoteFetch(event.request);
|
|
183
197
|
});
|
|
184
198
|
// ---------------
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
199
|
+
// Response processing
|
|
200
|
+
scope.response = await this.normalizeResponse(scope.httpEvent, scope.response);
|
|
201
|
+
scope.hasBackgroundActivity = scope.clientMessaging.isMessaging() || scope.eventLifecyclePromises.size || (scope.redirectMessage && !scope.response.headers.get('Location'));
|
|
188
202
|
if (scope.hasBackgroundActivity) {
|
|
189
203
|
scope.response.headers.set('X-Background-Messaging', `ch:${scope.clientMessaging.port.name}`);
|
|
190
|
-
|
|
204
|
+
}
|
|
191
205
|
if (scope.response.headers.get('Location')) {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
206
|
+
if (scope.redirectMessage) {
|
|
207
|
+
scope.session.set(`redirect-message:${scope.redirectMessageID}`, scope.redirectMessage);
|
|
208
|
+
}
|
|
195
209
|
} else {
|
|
196
210
|
if (scope.redirectMessage) {
|
|
197
|
-
|
|
198
|
-
|
|
211
|
+
scope.eventLifecycleHooks.respondWith(scope.redirectMessage);
|
|
212
|
+
}
|
|
199
213
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
214
|
+
Promise.all([...scope.eventLifecyclePromises]).then(() => {
|
|
215
|
+
if (scope.clientMessaging.isMessaging()) {
|
|
216
|
+
scope.clientMessaging.on('connected', () => {
|
|
217
|
+
setTimeout(() => {
|
|
218
|
+
scope.clientMessaging.close();
|
|
219
|
+
}, 100);
|
|
220
|
+
});
|
|
221
|
+
} else scope.clientMessaging.close();
|
|
222
|
+
});
|
|
209
223
|
return scope.response;
|
|
210
224
|
}
|
|
211
225
|
|
|
@@ -287,7 +301,7 @@ export class WebfloWorker extends WebfloRuntime {
|
|
|
287
301
|
|
|
288
302
|
async getRequestCache(request) {
|
|
289
303
|
const cacheName = request.headers.get('Accept') === 'application/json'
|
|
290
|
-
? this.cx.params.cache_name + '_json'
|
|
304
|
+
? this.cx.params.cache_name + '_json'
|
|
291
305
|
: this.cx.params.cache_name;
|
|
292
306
|
return self.caches.open(cacheName);
|
|
293
307
|
}
|
|
@@ -8,10 +8,10 @@ export class CookieStorage extends WebfloCookieStorage {
|
|
|
8
8
|
);
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
commit(response) {
|
|
11
|
+
async commit(response) {
|
|
12
12
|
for (const cookieStr of this.render()) {
|
|
13
13
|
response.headers.append('Set-Cookie', cookieStr);
|
|
14
14
|
}
|
|
15
|
-
super.commit();
|
|
15
|
+
await super.commit();
|
|
16
16
|
}
|
|
17
17
|
}
|
|
@@ -44,10 +44,10 @@ export class SessionStorage extends WebfloStorage {
|
|
|
44
44
|
return this.#sessionID;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
commit(response, force = false) {
|
|
47
|
+
async commit(response, force = false) {
|
|
48
48
|
if (response.headers.get('Set-Cookie', true).find((c) => c.name === '__sessid')) return;
|
|
49
|
-
if (!force && !this.getAdded().length && !this.getDeleted().length) return;
|
|
49
|
+
//if (!force && !this.getAdded().length && !this.getDeleted().length) return;
|
|
50
50
|
response.headers.append('Set-Cookie', `__sessid=${this.#sessionID}; Path=/; Secure; HttpOnly; SameSite=Lax; Max-Age=31536000`);
|
|
51
|
-
super.commit();
|
|
51
|
+
await super.commit();
|
|
52
52
|
}
|
|
53
53
|
}
|