@webqit/webflo 0.20.56 → 0.20.57
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 +5 -4
- package/src/util.js +1 -0
- package/src/webflo-build/index.js +24 -8
- package/src/webflo-config/runtime/Client.js +3 -3
- package/src/webflo-runtime/webflo-client/DeviceCapabilities.js +373 -165
- package/src/webflo-runtime/webflo-client/WebfloClient.js +5 -1
- package/src/webflo-runtime/webflo-client/WebfloRootClientA.js +4 -11
- package/src/webflo-runtime/webflo-client/WebfloRootClientB.js +9 -1
- package/src/webflo-runtime/webflo-messaging/ClientRequestPort001.js +10 -8
- package/src/webflo-runtime/webflo-messaging/WebfloTenancy001.js +17 -9
- package/src/webflo-runtime/webflo-messaging/WebfloTenant001.js +7 -3
- package/src/webflo-runtime/webflo-routing/HttpEvent111.js +44 -3
- package/src/webflo-runtime/webflo-routing/HttpUser111.js +1 -3
- package/src/webflo-runtime/webflo-routing/WebfloRouter111.js +7 -4
- package/src/webflo-runtime/webflo-server/WebfloServer.js +76 -33
- package/src/webflo-runtime/webflo-server/webflo-devmode.js +15 -27
|
@@ -1,150 +1,396 @@
|
|
|
1
|
-
import { Observer } from '@webqit/observer';
|
|
1
|
+
import { Observer, ListenerRegistry, Descriptor } from '@webqit/observer';
|
|
2
2
|
|
|
3
3
|
export class DeviceCapabilities {
|
|
4
4
|
|
|
5
|
-
#runtime;
|
|
6
5
|
#params;
|
|
7
6
|
|
|
8
7
|
#exposed = {};
|
|
9
8
|
get exposed() { return this.#exposed; }
|
|
10
9
|
|
|
11
|
-
|
|
10
|
+
static async recognizesPermission(q) {
|
|
11
|
+
try {
|
|
12
|
+
await navigator.permissions.query(this.resolveQuery(q));
|
|
13
|
+
return true;
|
|
14
|
+
} catch (e) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
12
18
|
|
|
13
|
-
static
|
|
14
|
-
|
|
19
|
+
static resolveQuery(q) {
|
|
20
|
+
if (typeof q === 'string') {
|
|
21
|
+
q = { name: q };
|
|
22
|
+
} else q = { ...q };
|
|
23
|
+
if (q.name === 'push' && !q.userVisibleOnly) {
|
|
24
|
+
q = { ...q, userVisibleOnly: true };
|
|
25
|
+
}
|
|
26
|
+
if (q.name === 'top-level-storage-access' && !q.requestedOrigin) {
|
|
27
|
+
q = { ...q, requestedOrigin: window.location.origin };
|
|
28
|
+
}
|
|
29
|
+
return q;
|
|
30
|
+
}
|
|
15
31
|
|
|
16
|
-
|
|
17
|
-
|
|
32
|
+
constructor(params = {}) {
|
|
33
|
+
this.#params = params;
|
|
18
34
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
Observer.set(instance.#exposed, 'custom_install', e);
|
|
35
|
+
if (Array.isArray(this.#params.exposed) && this.#params.exposed.length) {
|
|
36
|
+
for (const capName of this.#params.exposed) {
|
|
37
|
+
const queryName = capName.trim().replace(/_/g, '-');
|
|
38
|
+
const capInstance = this.query(queryName);
|
|
39
|
+
this.#exposed[capName] = capInstance;
|
|
25
40
|
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
close() {
|
|
45
|
+
this.#capInstances.forEach((c) => c.dispose());
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
#capInstances = new Map;
|
|
49
|
+
|
|
50
|
+
query(query) {
|
|
51
|
+
const _q = this.constructor.resolveQuery(query);
|
|
52
|
+
|
|
53
|
+
if (this.#capInstances.has(_q.name)) {
|
|
54
|
+
return this.#capInstances.get(_q.name);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let capInstance;
|
|
58
|
+
switch (_q.name) {
|
|
59
|
+
case 'pwa-install':
|
|
60
|
+
capInstance = new PWAInstallCapability;
|
|
61
|
+
case 'geolocation':
|
|
62
|
+
capInstance = new GeolocationCapability;
|
|
63
|
+
case 'notification':
|
|
64
|
+
//capInstance = new NotificationCapability;
|
|
65
|
+
case 'push':
|
|
66
|
+
//capInstance = new PushCapability;
|
|
67
|
+
case 'storage-access':
|
|
68
|
+
//capInstance = new StorageAccessCapability;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
this.#capInstances.set(_q.name, capInstance);
|
|
72
|
+
|
|
73
|
+
return capInstance;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* DeviceCapability
|
|
79
|
+
*/
|
|
80
|
+
|
|
81
|
+
class DeviceCapability {
|
|
82
|
+
|
|
83
|
+
#listenersRegistry;
|
|
84
|
+
_cleanups = [];
|
|
85
|
+
|
|
86
|
+
constructor() {
|
|
87
|
+
this.#listenersRegistry = ListenerRegistry.getInstance(this, true);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
_fire(props, exec) {
|
|
91
|
+
const oldValues = {};
|
|
92
|
+
for (const prop of props) {
|
|
93
|
+
oldValues[prop] = this[prop];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (exec) exec();
|
|
97
|
+
|
|
98
|
+
const descriptors = props.map((prop) => {
|
|
99
|
+
return new Descriptor(this, {
|
|
100
|
+
type: 'set',
|
|
101
|
+
key: prop,
|
|
102
|
+
value: this[prop],
|
|
103
|
+
oldValue: oldValues[prop],
|
|
104
|
+
isUpdate: true,
|
|
105
|
+
related: props.slice(),
|
|
106
|
+
operation: 'set',
|
|
107
|
+
detail: null,
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
this.#listenersRegistry.emit(descriptors);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
dispose() {
|
|
115
|
+
this._cleanups.forEach((c) => c());
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* PWAInstallCapability
|
|
121
|
+
*/
|
|
122
|
+
|
|
123
|
+
class PWAInstallCapability extends DeviceCapability {
|
|
124
|
+
|
|
125
|
+
#status = 'unknown';
|
|
126
|
+
#displayMode = null;
|
|
127
|
+
|
|
128
|
+
get status() { return this.#status; }
|
|
129
|
+
get displayMode() { return this.#displayMode; }
|
|
130
|
+
|
|
131
|
+
#pwaInstallEvent;
|
|
132
|
+
|
|
133
|
+
constructor() {
|
|
134
|
+
super();
|
|
135
|
+
|
|
136
|
+
// --------------- beforeinstallprompt event
|
|
137
|
+
|
|
138
|
+
const onbeforeinstallprompt = (e) => {
|
|
139
|
+
this.#pwaInstallEvent = e;
|
|
140
|
+
this.#pwaInstallEvent.preventDefault();
|
|
141
|
+
this._fire(['status'], () => {
|
|
142
|
+
this.#status = 'prompt-available';
|
|
143
|
+
});
|
|
26
144
|
};
|
|
145
|
+
|
|
27
146
|
window.addEventListener('beforeinstallprompt', onbeforeinstallprompt);
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
//
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
147
|
+
this._cleanups.push(() => window.removeEventListener('beforeinstallprompt', onbeforeinstallprompt));
|
|
148
|
+
|
|
149
|
+
// --------------- appinstalled event
|
|
150
|
+
|
|
151
|
+
const onappinstalled = async () => {
|
|
152
|
+
this._fire(['status'], () => {
|
|
153
|
+
this.#status = 'installed';
|
|
154
|
+
});
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
window.addEventListener('appinstalled', onappinstalled);
|
|
158
|
+
this._cleanups.push(() => window.removeEventListener('appinstalled', onappinstalled));
|
|
159
|
+
|
|
160
|
+
// --------------- displayMode
|
|
161
|
+
|
|
162
|
+
const _setDisplayMode = (dm) => {
|
|
163
|
+
this._fire(['displayMode'], () => {
|
|
164
|
+
this.#displayMode = dm;
|
|
165
|
+
});
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const handleDisplayMode = () => {
|
|
169
|
+
if (document.referrer.startsWith('android-app://')) {
|
|
170
|
+
_setDisplayMode('twa');
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
for (const dm of ['browser', 'standalone', 'minimal-ui', 'fullscreen', 'window-controls-overlay']) {
|
|
175
|
+
const mediaQuery = window.matchMedia(`(display-mode: ${dm})`);
|
|
176
|
+
|
|
177
|
+
if (mediaQuery.matches) {
|
|
178
|
+
_setDisplayMode(dm);
|
|
179
|
+
|
|
180
|
+
mediaQuery.addEventListener('change', handleDisplayMode, { once: true });
|
|
181
|
+
this._cleanups.push(() => mediaQuery.removeEventListener('change', handleDisplayMode));
|
|
182
|
+
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
handleDisplayMode();
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async prompt() {
|
|
192
|
+
if (!this.#pwaInstallEvent) return;
|
|
193
|
+
|
|
194
|
+
await this.#pwaInstallEvent.prompt?.();
|
|
195
|
+
const { outcome } = await this.#pwaInstallEvent.userChoice;
|
|
196
|
+
|
|
197
|
+
this._fire(['status'], () => {
|
|
198
|
+
this.#status = outcome;
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
this.#pwaInstallEvent = null;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* GeolocationCapability
|
|
207
|
+
*/
|
|
208
|
+
|
|
209
|
+
class GeolocationCapability extends DeviceCapability {
|
|
210
|
+
|
|
211
|
+
#status = !navigator.geolocation ? 'unsupported' : 'unknown';
|
|
212
|
+
get status() { return this.#status; }
|
|
213
|
+
|
|
214
|
+
constructor() {
|
|
215
|
+
super();
|
|
216
|
+
if (!navigator.geolocation) return;
|
|
217
|
+
|
|
218
|
+
navigator.permissions.query({ name: 'geolocation' }).then((permissionStatus) => {
|
|
219
|
+
const handleChange = () => {
|
|
220
|
+
this._fire(['status'], () => {
|
|
221
|
+
this.#status = permissionStatus.state;
|
|
222
|
+
});
|
|
223
|
+
if (permissionStatus.state === 'granted') {
|
|
224
|
+
this.#initEnqueued();
|
|
225
|
+
}
|
|
40
226
|
};
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
227
|
+
|
|
228
|
+
handleChange();
|
|
229
|
+
permissionStatus.addEventListener('change', handleChange);
|
|
230
|
+
this._cleanups.push(() => permissionStatus.removeEventListener('change', handleChange));
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
#queryQueue = new Set;
|
|
235
|
+
|
|
236
|
+
#initEnqueued() {
|
|
237
|
+
this.#queryQueue.forEach((cb) => cb());
|
|
238
|
+
this.#queryQueue.clear();
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
async read({ live = false, ...options } = {}) {
|
|
242
|
+
const _options = {
|
|
243
|
+
enableHighAccuracy: true,
|
|
244
|
+
...options
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
const process = () => {
|
|
248
|
+
return new Promise((res) => {
|
|
249
|
+
const locationData = {};
|
|
250
|
+
let resolved = false;
|
|
251
|
+
|
|
252
|
+
const successCallback = (pos) => {
|
|
253
|
+
let coords = pos.coords.toJSON();
|
|
254
|
+
|
|
255
|
+
if (!options.detailed) {
|
|
256
|
+
coords = {
|
|
257
|
+
lng: coords.longitude,
|
|
258
|
+
lat: coords.latitude
|
|
259
|
+
};
|
|
61
260
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
// --------
|
|
68
|
-
// Exposure
|
|
69
|
-
if (Array.isArray(instance.#params.exposed) && instance.#params.exposed.length) {
|
|
70
|
-
const [permissions, cleanup] = await instance.query(instance.#params.exposed.map((s) => s.trim()), true);
|
|
71
|
-
instance.#exposed = permissions;
|
|
72
|
-
instance.#cleanups.push(cleanup);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return instance;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
async query(query, live = false) {
|
|
79
|
-
const permissions = {}, cleanups = [];
|
|
80
|
-
for (let q of [].concat(query)) {
|
|
81
|
-
q = this.resolveQuery(q);
|
|
82
|
-
// ------
|
|
83
|
-
// Display mode
|
|
84
|
-
if (q.name === 'display-mode') {
|
|
85
|
-
const handleDisplayMode = () => {
|
|
86
|
-
if (document.referrer.startsWith('android-app://')) {
|
|
87
|
-
Observer.set(permissions, 'display_mode', 'twa');
|
|
88
|
-
return;
|
|
261
|
+
|
|
262
|
+
if (!Object.keys(coords).every((n) => coords[n] === locationData[n])) {
|
|
263
|
+
Observer.set(locationData, coords);
|
|
89
264
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
if (live) {
|
|
95
|
-
mediaQuery.addEventListener('change', handleDisplayMode, { once: true });
|
|
96
|
-
cleanups.push(() => mediaQuery.removeEventListener('change', handleDisplayMode));
|
|
97
|
-
}
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
265
|
+
|
|
266
|
+
if (!resolved) {
|
|
267
|
+
res(locationData);
|
|
268
|
+
resolved = true;
|
|
100
269
|
}
|
|
101
270
|
};
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
try {
|
|
108
|
-
const permissionStatus = await navigator.permissions.query(q);
|
|
109
|
-
permissions[permissionStatus.name.replace(/-/g, '_')] = permissionStatus.state;
|
|
271
|
+
|
|
272
|
+
const errrorCallback = (e) => {
|
|
273
|
+
console.error(e);
|
|
274
|
+
};
|
|
275
|
+
|
|
110
276
|
if (live) {
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
277
|
+
const watchID = navigator.geolocation.watchPosition(
|
|
278
|
+
successCallback,
|
|
279
|
+
errrorCallback,
|
|
280
|
+
_options
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
this._cleanups.push(() => navigator.geolocation.clearWatch(watchID));
|
|
284
|
+
if (_options.signal) {
|
|
285
|
+
_options.signal.addEventListener('abort', () => navigator.geolocation.clearWatch(watchID));
|
|
286
|
+
}
|
|
287
|
+
} else {
|
|
288
|
+
navigator.geolocation.getCurrentPosition(
|
|
289
|
+
successCallback,
|
|
290
|
+
errrorCallback,
|
|
291
|
+
_options
|
|
292
|
+
);
|
|
116
293
|
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
|
|
294
|
+
});
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
if (this.status !== 'granted') {
|
|
298
|
+
if (live) {
|
|
299
|
+
return new Promise((res) => {
|
|
300
|
+
const handle = async () => {
|
|
301
|
+
const liveData = await process();
|
|
302
|
+
res(liveData);
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
this.#queryQueue.add(handle);
|
|
306
|
+
});
|
|
120
307
|
}
|
|
308
|
+
|
|
309
|
+
return null;
|
|
121
310
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
311
|
+
|
|
312
|
+
return await process();
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
prompt() {
|
|
316
|
+
if (this.status === 'granted') return;
|
|
317
|
+
|
|
318
|
+
navigator.geolocation.getCurrentPosition(
|
|
319
|
+
() => { },
|
|
320
|
+
() => { },
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
dispose() {
|
|
325
|
+
this.#queryQueue.clear();
|
|
326
|
+
super.dispose();
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
/*
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
// Public base64 to Uint
|
|
345
|
+
function urlBase64ToUint8Array(base64String) {
|
|
346
|
+
const padding = '='.repeat((4 - base64String.length % 4) % 4);
|
|
347
|
+
const base64 = (base64String + padding)
|
|
348
|
+
.replace(/\-/g, '+')
|
|
349
|
+
.replace(/_/g, '/');
|
|
350
|
+
const rawData = window.atob(base64);
|
|
351
|
+
const outputArray = new Uint8Array(rawData.length);
|
|
352
|
+
for (let i = 0; i < rawData.length; ++i) {
|
|
353
|
+
outputArray[i] = rawData.charCodeAt(i);
|
|
354
|
+
}
|
|
355
|
+
return outputArray;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
async #initWebpush() {
|
|
363
|
+
if (!this.#runtime?.env('GENERIC_PUBLIC_WEBHOOK_URL')) return;
|
|
364
|
+
|
|
365
|
+
try {
|
|
366
|
+
const pushPermissionStatus = await navigator.permissions.query({ name: 'push', userVisibleOnly: true });
|
|
367
|
+
const pushPermissionStatusHandler = async () => {
|
|
368
|
+
const reg = await navigator.serviceWorker.ready;
|
|
369
|
+
const pushManager = reg.pushManager;
|
|
370
|
+
|
|
371
|
+
const eventPayload = pushPermissionStatus.state === 'granted'
|
|
372
|
+
? { type: 'push.subscribe', data: await pushManager.getSubscription() }
|
|
373
|
+
: { type: 'push.unsubscribe' };
|
|
374
|
+
|
|
375
|
+
if (eventPayload.type === 'push.subscribe' && !eventPayload.data) {
|
|
376
|
+
return window.queueMicrotask(pushPermissionStatusHandler);
|
|
142
377
|
}
|
|
143
|
-
|
|
144
|
-
|
|
378
|
+
|
|
379
|
+
await fetch(this.#runtime.env('GENERIC_PUBLIC_WEBHOOK_URL'), {
|
|
380
|
+
method: 'POST',
|
|
381
|
+
headers: { 'Content-Type': 'application/json' },
|
|
382
|
+
body: JSON.stringify(eventPayload),
|
|
383
|
+
}).catch(() => { });
|
|
145
384
|
}
|
|
146
|
-
|
|
147
|
-
|
|
385
|
+
|
|
386
|
+
pushPermissionStatus.addEventListener('change', pushPermissionStatusHandler);
|
|
387
|
+
this.#cleanups.push(() => pushPermissionStatus.removeEventListener('change', pushPermissionStatusHandler));
|
|
388
|
+
} catch (e) { }
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
// notification
|
|
148
394
|
if (name === 'notification') {
|
|
149
395
|
return await new Promise(async (resolve, reject) => {
|
|
150
396
|
const permissionResult = Notification.requestPermission(resolve);
|
|
@@ -153,36 +399,16 @@ export class DeviceCapabilities {
|
|
|
153
399
|
}
|
|
154
400
|
});
|
|
155
401
|
}
|
|
156
|
-
|
|
157
|
-
//
|
|
402
|
+
|
|
403
|
+
// webpush
|
|
158
404
|
if (name === 'push') {
|
|
159
|
-
const
|
|
405
|
+
const reg = await navigator.serviceWorker.ready;
|
|
406
|
+
const pushManager = reg.pushManager;
|
|
160
407
|
const subscription = (await pushManager.getSubscription()) || await pushManager.subscribe(params);
|
|
161
408
|
return subscription;
|
|
162
409
|
}
|
|
163
|
-
}
|
|
164
410
|
|
|
165
|
-
async supports(q) {
|
|
166
|
-
try {
|
|
167
|
-
await navigator.permissions.query(this.resolveQuery(q));
|
|
168
|
-
return true;
|
|
169
|
-
} catch(e) {
|
|
170
|
-
return false;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
411
|
|
|
174
|
-
resolveQuery(q) {
|
|
175
|
-
if (typeof q === 'string') {
|
|
176
|
-
q = { name: q };
|
|
177
|
-
}
|
|
178
|
-
if (q.name === 'push' && !q.userVisibleOnly) {
|
|
179
|
-
q = { ...q, userVisibleOnly: true };
|
|
180
|
-
}
|
|
181
|
-
if (q.name === 'top-level-storage-access' && !q.requestedOrigin) {
|
|
182
|
-
q = { ...q, requestedOrigin: window.location.origin };
|
|
183
|
-
}
|
|
184
|
-
return q;
|
|
185
|
-
}
|
|
186
412
|
|
|
187
413
|
resolveRequest(name, params = {}) {
|
|
188
414
|
if (name === 'push') {
|
|
@@ -195,22 +421,4 @@ export class DeviceCapabilities {
|
|
|
195
421
|
}
|
|
196
422
|
return params;
|
|
197
423
|
}
|
|
198
|
-
|
|
199
|
-
close() {
|
|
200
|
-
this.#cleanups.forEach((c) => c());
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// Public base64 to Uint
|
|
205
|
-
function urlBase64ToUint8Array(base64String) {
|
|
206
|
-
const padding = '='.repeat((4 - base64String.length % 4) % 4);
|
|
207
|
-
const base64 = (base64String + padding)
|
|
208
|
-
.replace(/\-/g, '+')
|
|
209
|
-
.replace(/_/g, '/');
|
|
210
|
-
const rawData = window.atob(base64);
|
|
211
|
-
const outputArray = new Uint8Array(rawData.length);
|
|
212
|
-
for (let i = 0; i < rawData.length; ++i) {
|
|
213
|
-
outputArray[i] = rawData.charCodeAt(i);
|
|
214
|
-
}
|
|
215
|
-
return outputArray;
|
|
216
|
-
}
|
|
424
|
+
*/
|
|
@@ -55,7 +55,11 @@ export class WebfloClient extends AppRuntime {
|
|
|
55
55
|
rel: 'unrelated',
|
|
56
56
|
phase: 0
|
|
57
57
|
};
|
|
58
|
-
this.#background = new StarPort({
|
|
58
|
+
this.#background = new StarPort({
|
|
59
|
+
handshake: 1,
|
|
60
|
+
postAwaitsOpen: false/* new additions shouldn't inherit old, initial messages */,
|
|
61
|
+
autoClose: false
|
|
62
|
+
});
|
|
59
63
|
}
|
|
60
64
|
|
|
61
65
|
async initialize() {
|
|
@@ -9,12 +9,6 @@ import { WebfloHMR } from './webflo-devmode.js';
|
|
|
9
9
|
|
|
10
10
|
export class WebfloRootClientA extends WebfloClient {
|
|
11
11
|
|
|
12
|
-
static get Workport() { return ClientSideWorkport; }
|
|
13
|
-
|
|
14
|
-
static get DeviceCapabilities() { return DeviceCapabilities; }
|
|
15
|
-
|
|
16
|
-
static get DeviceViewport() { return DeviceViewport; }
|
|
17
|
-
|
|
18
12
|
static create(bootstrap, host) {
|
|
19
13
|
return new this(bootstrap, host);
|
|
20
14
|
}
|
|
@@ -77,16 +71,15 @@ export class WebfloRootClientA extends WebfloClient {
|
|
|
77
71
|
const cleanups = [];
|
|
78
72
|
instanceController.signal.addEventListener('abort', () => cleanups.forEach((c) => c()), { once: true });
|
|
79
73
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
this.#viewport = new this.constructor.DeviceViewport();
|
|
74
|
+
this.#viewport = new DeviceViewport();
|
|
83
75
|
|
|
84
|
-
this.#capabilities =
|
|
76
|
+
this.#capabilities = new DeviceCapabilities(this.config.CLIENT.capabilities, this);
|
|
77
|
+
await this.#capabilities.ready;
|
|
85
78
|
cleanups.push(() => this.#capabilities.close());
|
|
86
79
|
|
|
87
80
|
if (this.config.CLIENT.capabilities?.service_worker) {
|
|
88
81
|
const { filename, ...restServiceWorkerParams } = this.config.WORKER;
|
|
89
|
-
|
|
82
|
+
ClientSideWorkport.initialize(null, filename, restServiceWorkerParams).then((workport) => {
|
|
90
83
|
this.#workport = workport;
|
|
91
84
|
cleanups.push(() => this.#workport.close());
|
|
92
85
|
});
|
|
@@ -5,8 +5,10 @@ export class WebfloRootClientB extends WebfloRootClientA {
|
|
|
5
5
|
|
|
6
6
|
control() {
|
|
7
7
|
const instanceController = super.controlSuper/*IMPORTANT*/();
|
|
8
|
+
|
|
8
9
|
// Detect source elements
|
|
9
10
|
let navigationOrigins = [];
|
|
11
|
+
|
|
10
12
|
// Capture all link-clicks
|
|
11
13
|
const clickHandler = (e) => {
|
|
12
14
|
if (!this._canIntercept(e) || e.defaultPrevented) return;
|
|
@@ -14,11 +16,13 @@ export class WebfloRootClientB extends WebfloRootClientA {
|
|
|
14
16
|
if (!anchorEl || !anchorEl.href || anchorEl.target) return;
|
|
15
17
|
navigationOrigins = [anchorEl, null, anchorEl.closest('[navigationcontext]')];
|
|
16
18
|
};
|
|
19
|
+
|
|
17
20
|
// Capture all form-submits
|
|
18
21
|
const submitHandler = (e) => {
|
|
19
22
|
if (!this._canIntercept(e) || e.defaultPrevented) return;
|
|
20
23
|
navigationOrigins = [e.submitter, e.target.closest('form'), e.target.closest('[navigationcontext]')];
|
|
21
24
|
};
|
|
25
|
+
|
|
22
26
|
// Handle navigation event which happens after the above
|
|
23
27
|
const navigateHandler = (e) => {
|
|
24
28
|
if (!e.canIntercept
|
|
@@ -47,6 +51,7 @@ export class WebfloRootClientB extends WebfloRootClientA {
|
|
|
47
51
|
info
|
|
48
52
|
};
|
|
49
53
|
navigationOrigins = [];
|
|
54
|
+
|
|
50
55
|
// Traversal?
|
|
51
56
|
// Push
|
|
52
57
|
const url = new URL(destination.url, this.location.href);
|
|
@@ -55,6 +60,7 @@ export class WebfloRootClientB extends WebfloRootClientA {
|
|
|
55
60
|
body: formData,
|
|
56
61
|
//signal TODO: auto-aborts on a redirect response which thus fails to parse
|
|
57
62
|
};
|
|
63
|
+
|
|
58
64
|
const runtime = this;
|
|
59
65
|
e.intercept({
|
|
60
66
|
scroll: 'after-transition',
|
|
@@ -64,10 +70,12 @@ export class WebfloRootClientB extends WebfloRootClientA {
|
|
|
64
70
|
},
|
|
65
71
|
});
|
|
66
72
|
};
|
|
73
|
+
|
|
67
74
|
window.addEventListener('click', clickHandler, { signal: instanceController.signal });
|
|
68
75
|
window.addEventListener('submit', submitHandler, { signal: instanceController.signal });
|
|
69
76
|
window.navigation.addEventListener('navigate', navigateHandler, { signal: instanceController.signal });
|
|
70
|
-
|
|
77
|
+
|
|
78
|
+
return instanceController;
|
|
71
79
|
}
|
|
72
80
|
|
|
73
81
|
reload(params) {
|