@webqit/webflo 0.20.25 → 0.20.27
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 +8 -5
- package/src/build-pi/index.js +7 -5
- package/src/init-pi/index.js +0 -1
- package/src/runtime-pi/{WebfloRuntime.js → AppRuntime.js} +57 -113
- package/src/runtime-pi/webflo-client/DeviceCapabilities.js +1 -1
- package/src/runtime-pi/webflo-client/WebfloClient.js +163 -95
- package/src/runtime-pi/webflo-client/{WebfloRootClient1.js → WebfloRootClientA.js} +39 -56
- package/src/runtime-pi/webflo-client/{WebfloRootClient2.js → WebfloRootClientB.js} +3 -3
- package/src/runtime-pi/webflo-client/WebfloSubClient.js +28 -15
- package/src/runtime-pi/webflo-client/index.js +3 -3
- package/src/runtime-pi/webflo-messaging/ClientPortMixin.js +13 -0
- package/src/runtime-pi/{webflo-server/messaging/ClientRequestRealtime.js → webflo-messaging/ClientRequestPort001.js} +13 -9
- package/src/runtime-pi/webflo-messaging/ClientRequestPort010.js +4 -0
- package/src/runtime-pi/webflo-messaging/ClientRequestPort100.js +17 -0
- package/src/runtime-pi/webflo-messaging/WebfloTenancy001.js +27 -0
- package/src/runtime-pi/webflo-messaging/WebfloTenant001.js +27 -0
- package/src/runtime-pi/webflo-routing/HttpCookies101.js +53 -0
- package/src/runtime-pi/webflo-routing/HttpCookies110.js +3 -0
- package/src/runtime-pi/webflo-routing/{HttpEvent.js → HttpEvent111.js} +95 -73
- package/src/runtime-pi/webflo-routing/HttpKeyvalInterface.js +120 -0
- package/src/runtime-pi/webflo-routing/HttpSession001.js +24 -0
- package/src/runtime-pi/webflo-routing/HttpSession110.js +3 -0
- package/src/runtime-pi/webflo-routing/{HttpThread.js → HttpThread111.js} +54 -13
- package/src/runtime-pi/webflo-routing/{HttpUser.js → HttpUser111.js} +10 -23
- package/src/runtime-pi/webflo-routing/KeyvalsFactory001.js +53 -0
- package/src/runtime-pi/webflo-routing/KeyvalsFactory110.js +48 -0
- package/src/runtime-pi/webflo-routing/KeyvalsFactoryInterface.js +56 -0
- package/src/runtime-pi/webflo-routing/{WebfloRouter.js → WebfloRouter111.js} +5 -6
- package/src/runtime-pi/webflo-server/WebfloServer.js +262 -266
- package/src/runtime-pi/webflo-worker/WebfloWorker.js +97 -44
- package/src/util.js +3 -2
- package/src/runtime-pi/apis.js +0 -9
- package/src/runtime-pi/webflo-client/ClientSideCookies.js +0 -18
- package/src/runtime-pi/webflo-fetch/LiveResponse.js +0 -476
- package/src/runtime-pi/webflo-fetch/index.js +0 -419
- package/src/runtime-pi/webflo-fetch/util.js +0 -28
- package/src/runtime-pi/webflo-messaging/WQBroadcastChannel.js +0 -10
- package/src/runtime-pi/webflo-messaging/WQMessageChannel.js +0 -26
- package/src/runtime-pi/webflo-messaging/WQMessageEvent.js +0 -87
- package/src/runtime-pi/webflo-messaging/WQMessagePort.js +0 -38
- package/src/runtime-pi/webflo-messaging/WQRelayPort.js +0 -47
- package/src/runtime-pi/webflo-messaging/WQSockPort.js +0 -111
- package/src/runtime-pi/webflo-messaging/WQStarPort.js +0 -112
- package/src/runtime-pi/webflo-messaging/wq-message-port.js +0 -413
- package/src/runtime-pi/webflo-routing/HttpCookies.js +0 -43
- package/src/runtime-pi/webflo-routing/HttpSession.js +0 -11
- package/src/runtime-pi/webflo-routing/HttpState.js +0 -182
- package/src/runtime-pi/webflo-server/ServerSideCookies.js +0 -22
- package/src/runtime-pi/webflo-server/ServerSideSession.js +0 -40
- package/src/runtime-pi/webflo-server/messaging/Client.js +0 -27
- package/src/runtime-pi/webflo-server/messaging/Clients.js +0 -25
- package/src/runtime-pi/webflo-url/Url.js +0 -156
- package/src/runtime-pi/webflo-url/index.js +0 -1
- package/src/runtime-pi/webflo-url/urlpattern.js +0 -38
- package/src/runtime-pi/webflo-url/util.js +0 -109
- package/src/runtime-pi/webflo-url/xURL.js +0 -94
- package/src/runtime-pi/webflo-worker/WorkerSideCookies.js +0 -21
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import { _isObject } from '@webqit/util/js/index.js';
|
|
2
|
-
import { toWQPort, portIsMessaging, forwardPort, preProcessPostMessage, portHooksCleanup } from './wq-message-port.js';
|
|
3
|
-
import { WQMessageEvent } from './WQMessageEvent.js';
|
|
4
|
-
import { WQMessagePort } from './WQMessagePort.js';
|
|
5
|
-
import { _wq } from '../../util.js';
|
|
6
|
-
|
|
7
|
-
export class WQSockPort extends WQMessagePort {
|
|
8
|
-
|
|
9
|
-
#socket;
|
|
10
|
-
#cleanups = [];
|
|
11
|
-
|
|
12
|
-
constructor(instanceOrConnectionID) {
|
|
13
|
-
super();
|
|
14
|
-
this.#socket = typeof instanceOrConnectionID === 'string' ? new WebSocket(`/${instanceOrConnectionID}`) : instanceOrConnectionID;
|
|
15
|
-
const meta = _wq(this, 'meta');
|
|
16
|
-
Object.defineProperty(this, 'wqLifecycle', {
|
|
17
|
-
value: {
|
|
18
|
-
open: new Promise((resolve) => {
|
|
19
|
-
if (meta.get('open')) return resolve();
|
|
20
|
-
meta.set('onlineCallback', resolve);
|
|
21
|
-
}),
|
|
22
|
-
close: new Promise((resolve) => {
|
|
23
|
-
if (meta.get('close')) return resolve();
|
|
24
|
-
meta.set('offlineCallback', resolve);
|
|
25
|
-
}),
|
|
26
|
-
messaging: new Promise((resolve) => {
|
|
27
|
-
if (meta.get('messaging')) return resolve();
|
|
28
|
-
meta.set('messagingCallback', resolve);
|
|
29
|
-
}),
|
|
30
|
-
},
|
|
31
|
-
configurable: true
|
|
32
|
-
});
|
|
33
|
-
const fireOpen = () => {
|
|
34
|
-
this.dispatchEvent(new Event('open'));
|
|
35
|
-
meta.get('onlineCallback')();
|
|
36
|
-
};
|
|
37
|
-
const fireClose = () => {
|
|
38
|
-
this.dispatchEvent(new Event('close'));
|
|
39
|
-
meta.get('offlineCallback')();
|
|
40
|
-
};
|
|
41
|
-
if (this.#socket.readyState === this.#socket.constructor.OPEN) {
|
|
42
|
-
fireOpen();
|
|
43
|
-
}
|
|
44
|
-
const fireError = () => {
|
|
45
|
-
this.dispatchEvent(new Event('error'));
|
|
46
|
-
};
|
|
47
|
-
// --------
|
|
48
|
-
const handleMessage = async (event) => {
|
|
49
|
-
let json;
|
|
50
|
-
try {
|
|
51
|
-
if (!(json = JSON.parse(event.data))
|
|
52
|
-
|| !json['.wq']
|
|
53
|
-
|| !('data' in json)
|
|
54
|
-
|| !['wqEventOptions', 'wqProcessingOptions'].every((k) => _isObject(json[k]))) {
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
} catch (e) {
|
|
58
|
-
// throw a better error
|
|
59
|
-
}
|
|
60
|
-
// Build event details...
|
|
61
|
-
const { data, wqEventOptions, wqProcessingOptions } = json;
|
|
62
|
-
const eventInit = { data, wqEventOptions, ports: [] };
|
|
63
|
-
if (wqProcessingOptions.dataOriginallyUndefined) {
|
|
64
|
-
eventInit.data = undefined;
|
|
65
|
-
}
|
|
66
|
-
for (let i = 0; i < (wqProcessingOptions.numPorts || 0); i++) {
|
|
67
|
-
const channel = new MessageChannel;
|
|
68
|
-
channel.port1.start();
|
|
69
|
-
toWQPort(channel.port1);
|
|
70
|
-
this.#cleanups.push(forwardPort.call(this, '*', channel.port1, { bidirectional: true, namespace1: `${wqEventOptions.eventID}:${i}` }));
|
|
71
|
-
eventInit.ports.push(channel.port2);
|
|
72
|
-
}
|
|
73
|
-
this.dispatchEvent(new WQMessageEvent(this, eventInit));
|
|
74
|
-
};
|
|
75
|
-
const handleClose = (e) => {
|
|
76
|
-
this.#socket.removeEventListener('message', handleMessage);
|
|
77
|
-
this.#socket.removeEventListener('open', fireOpen);
|
|
78
|
-
this.#socket.removeEventListener('error', fireError);
|
|
79
|
-
this.#socket.removeEventListener('close', handleClose);
|
|
80
|
-
for (const c of this.#cleanups) {
|
|
81
|
-
c();
|
|
82
|
-
}
|
|
83
|
-
portHooksCleanup.call(this);
|
|
84
|
-
fireClose();
|
|
85
|
-
};
|
|
86
|
-
this.#socket.addEventListener('message', handleMessage);
|
|
87
|
-
this.#socket.addEventListener('open', fireOpen);
|
|
88
|
-
this.#socket.addEventListener('error', fireError);
|
|
89
|
-
this.#socket.addEventListener('close', handleClose);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
postMessage(data, transferOrOptions = []) {
|
|
93
|
-
const { transfer = [], wqEventOptions = {}, wqProcessingOptions: _, ...portOptions } = preProcessPostMessage.call(this, data, transferOrOptions);
|
|
94
|
-
const messagePorts = transfer.filter((t) => t instanceof MessagePort);
|
|
95
|
-
portIsMessaging.call(this);
|
|
96
|
-
this.#socket.send(JSON.stringify({
|
|
97
|
-
data: data === undefined ? null : data,
|
|
98
|
-
wqEventOptions,
|
|
99
|
-
wqProcessingOptions: { numPorts: messagePorts.length, dataOriginallyUndefined: data === undefined },
|
|
100
|
-
['.wq']: true,
|
|
101
|
-
}), portOptions);
|
|
102
|
-
for (let i = 0; i < messagePorts.length; i++) {
|
|
103
|
-
toWQPort(messagePorts[i]);
|
|
104
|
-
this.#cleanups.push(forwardPort.call(this, '*', messagePorts[i], { bidirectional: true, namespace1: `${wqEventOptions.eventID}:${i}` }));
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
close() {
|
|
109
|
-
return this.#socket.close();
|
|
110
|
-
}
|
|
111
|
-
}
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import { preProcessPostMessage, portIsMessaging, portHooksCleanup } from './wq-message-port.js';
|
|
2
|
-
import { WQMessagePort } from './WQMessagePort.js';
|
|
3
|
-
import { _wq } from '../../util.js';
|
|
4
|
-
|
|
5
|
-
export class WQStarPort extends WQMessagePort {
|
|
6
|
-
|
|
7
|
-
#ports = new Set;
|
|
8
|
-
|
|
9
|
-
get length() { return this.#ports.size; }
|
|
10
|
-
|
|
11
|
-
[Symbol.iterator]() { return this.#ports[Symbol.iterator](); }
|
|
12
|
-
|
|
13
|
-
constructor() {
|
|
14
|
-
super();
|
|
15
|
-
this.#startWQLifecycle();
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
#startWQLifecycle() {
|
|
19
|
-
const meta = _wq(this, 'meta');
|
|
20
|
-
let isMessaging = false;
|
|
21
|
-
Object.defineProperty(this, 'wqLifecycle', {
|
|
22
|
-
value: {
|
|
23
|
-
open: new Promise((resolve) => {
|
|
24
|
-
if (meta.get('open')) return resolve();
|
|
25
|
-
meta.set('onlineCallback', resolve);
|
|
26
|
-
}),
|
|
27
|
-
close: new Promise((resolve) => {
|
|
28
|
-
if (meta.get('close')) return resolve();
|
|
29
|
-
meta.set('offlineCallback', resolve);
|
|
30
|
-
}),
|
|
31
|
-
messaging: new Promise((resolve) => {
|
|
32
|
-
if (meta.get('messaging')) {
|
|
33
|
-
isMessaging = true;
|
|
34
|
-
return resolve();
|
|
35
|
-
}
|
|
36
|
-
meta.set('messagingCallback', () => {
|
|
37
|
-
isMessaging = true;
|
|
38
|
-
resolve()
|
|
39
|
-
});
|
|
40
|
-
}),
|
|
41
|
-
isMessaging: (() => isMessaging),
|
|
42
|
-
},
|
|
43
|
-
configurable: true
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
addPort(port, { enableBubbling = true } = {}) {
|
|
48
|
-
if (!(port instanceof WQMessagePort)) {
|
|
49
|
-
throw new TypeError('Port must be a WQMessagePort instance.');
|
|
50
|
-
}
|
|
51
|
-
if (this.#ports.has(port)) return;
|
|
52
|
-
this.#ports.add(port); // @ORDER: 1
|
|
53
|
-
const meta = _wq(this, 'meta');
|
|
54
|
-
const portMeta = _wq(port, 'meta');
|
|
55
|
-
if (enableBubbling) {
|
|
56
|
-
if (portMeta.get('parentNode')) {
|
|
57
|
-
throw new TypeError('Incoming port already has a parent node.');
|
|
58
|
-
}
|
|
59
|
-
portMeta.set('parentNode', this); // @ORDER: 2
|
|
60
|
-
}
|
|
61
|
-
// Lifecycle management
|
|
62
|
-
port.wqLifecycle.open.then(() => {
|
|
63
|
-
if (meta.get('open')) return;
|
|
64
|
-
this.dispatchEvent(new Event('open'));
|
|
65
|
-
meta.get('onlineCallback')();
|
|
66
|
-
});
|
|
67
|
-
const cleanup = () => {
|
|
68
|
-
if (!this.#ports.has(port)) return;
|
|
69
|
-
this.#ports.delete(port);
|
|
70
|
-
if (enableBubbling) {
|
|
71
|
-
portMeta.set('parentNode', null);
|
|
72
|
-
}
|
|
73
|
-
if (this.#ports.size === 0) {
|
|
74
|
-
this.dispatchEvent(new Event('close'));
|
|
75
|
-
meta.get('offlineCallback')();
|
|
76
|
-
this.#startWQLifecycle();
|
|
77
|
-
}
|
|
78
|
-
};
|
|
79
|
-
port.wqLifecycle.close.then(cleanup);
|
|
80
|
-
return cleanup;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
findPort(callback) {
|
|
84
|
-
for (const port of this.#ports) {
|
|
85
|
-
if (callback(port)) {
|
|
86
|
-
return port;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// --------
|
|
92
|
-
|
|
93
|
-
postMessage(data, transferOrOptions = []) {
|
|
94
|
-
portIsMessaging.call(this);
|
|
95
|
-
this.wqLifecycle.open.then(() => {
|
|
96
|
-
const { wqProcessingOptions, ...resstOptions } = preProcessPostMessage.call(this, data, transferOrOptions);
|
|
97
|
-
for (const port of this.#ports) {
|
|
98
|
-
if (port === wqProcessingOptions.except) continue;
|
|
99
|
-
port.postMessage(data, { wqProcessingOptions, ...resstOptions });
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
close(destroy = false) {
|
|
105
|
-
for (const port of this.#ports) {
|
|
106
|
-
port.close?.();
|
|
107
|
-
}
|
|
108
|
-
if (destroy) {
|
|
109
|
-
portHooksCleanup.call(this);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
@@ -1,413 +0,0 @@
|
|
|
1
|
-
import { _isObject, _isTypeObject } from '@webqit/util/js/index.js';
|
|
2
|
-
import { WQMessagePort, WQMessagePortInstanceTag } from './WQMessagePort.js';
|
|
3
|
-
import { isTypeStream } from '../webflo-fetch/util.js';
|
|
4
|
-
import { WQMessageEvent } from './WQMessageEvent.js';
|
|
5
|
-
import { Observer } from '@webqit/use-live';
|
|
6
|
-
import { _wq } from '../../util.js';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* transferOrOptions has the following structure:
|
|
10
|
-
* {
|
|
11
|
-
* transfer: Array<Transferable>,
|
|
12
|
-
* wqEventOptions: {
|
|
13
|
-
* eventID: String, // Optional, this is the ID of the event
|
|
14
|
-
* type: String, // The type of the event, e.g. 'message', 'request', 'mutations'
|
|
15
|
-
* live: Boolean, // If true, the data is to be transmitted as a live object
|
|
16
|
-
* bubbles: Boolean, // If true, the message bubbles up through the event system
|
|
17
|
-
* forwarded: Boolean, // Added at this.dispatchEvent() when forwarding messages
|
|
18
|
-
* },
|
|
19
|
-
* wqProcessingOptions: { // Eventually added while processing
|
|
20
|
-
* except, // Addable by anyone but added at this.dispatchEvent() when forwarding messages
|
|
21
|
-
* observing, // Added at the outermost postMessage() call when actually publishingMutations
|
|
22
|
-
* },
|
|
23
|
-
* wqObserverOptions: { // Stripped at the outermost postMessage() call
|
|
24
|
-
* signal: AbortSignal, // Optional signal to abort the live object
|
|
25
|
-
* withArrayMethodDescriptors: Boolean, // If true, array method descriptors are included in mutations
|
|
26
|
-
* },
|
|
27
|
-
* ...restOptions // Any other options that should be passed to the postMessage method
|
|
28
|
-
* }
|
|
29
|
-
*/
|
|
30
|
-
export function preProcessPostMessage(data, transferOrOptions) {
|
|
31
|
-
if (Array.isArray(transferOrOptions)) {
|
|
32
|
-
transferOrOptions = { transfer: transferOrOptions };
|
|
33
|
-
} else if (!transferOrOptions || typeof transferOrOptions !== 'object') {
|
|
34
|
-
throw new TypeError('transferOrOptions must be an array or an object');
|
|
35
|
-
}
|
|
36
|
-
let {
|
|
37
|
-
wqEventOptions = {}, // Remove and re-add after normalization
|
|
38
|
-
wqProcessingOptions = {}, // Remove and re-add after normalization
|
|
39
|
-
wqObserverOptions = {}, // Remove for use here
|
|
40
|
-
...options
|
|
41
|
-
} = transferOrOptions;
|
|
42
|
-
if (!wqEventOptions.type) {
|
|
43
|
-
// Set wqEventOptions.type
|
|
44
|
-
wqEventOptions = { ...wqEventOptions, type: 'message' };
|
|
45
|
-
}
|
|
46
|
-
if (!wqEventOptions.eventID) {
|
|
47
|
-
// Set wqEventOptions.eventID
|
|
48
|
-
wqEventOptions = { ...wqEventOptions, eventID: `${wqEventOptions.type}-${(0 | Math.random() * 9e6).toString(36)}` };
|
|
49
|
-
}
|
|
50
|
-
if (!wqProcessingOptions.observing && !wqEventOptions.forwarded && _isTypeObject(data) && wqEventOptions.live && !wqEventOptions.type?.endsWith('.mutate')) {
|
|
51
|
-
wqProcessingOptions = { ...wqProcessingOptions, observing: true }; // Set wqProcessingOptions.observing
|
|
52
|
-
publishMutations.call(this, data, wqEventOptions.eventID, wqObserverOptions);
|
|
53
|
-
}
|
|
54
|
-
// Re-combine
|
|
55
|
-
return { ...options, wqEventOptions, wqProcessingOptions };
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export function publishMutations(data, originalEventID, { signal, withArrayMethodDescriptors = true, honourDoneMutationFlags = false } = {}) {
|
|
59
|
-
if (isTypeStream(data) || !_isTypeObject(data)) {
|
|
60
|
-
throw new TypeError('data must be a plain object and not a stream');
|
|
61
|
-
}
|
|
62
|
-
if (typeof originalEventID !== 'string') {
|
|
63
|
-
throw new TypeError('originalEventID must be a non-empty string');
|
|
64
|
-
}
|
|
65
|
-
const meta = _wq(this, 'meta');
|
|
66
|
-
meta.set('mutationListeners', meta.get('mutationListeners') || new Set);
|
|
67
|
-
const mutationListeners = meta.get('mutationListeners');
|
|
68
|
-
const liveStreamController = Observer.observe(data, Observer.subtree(), (mutations) => {
|
|
69
|
-
// Ignore individual mutations made by array operations
|
|
70
|
-
if (withArrayMethodDescriptors && Array.isArray(mutations[0].target) && !mutations[0].argumentsList && !['set', 'defineProperty', 'deleteProperty'].includes(mutations[0].operation)) {
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
// Push events. Exclude the reference to target
|
|
74
|
-
let mutationsDone;
|
|
75
|
-
this.postMessage(
|
|
76
|
-
mutations.map((m) => {
|
|
77
|
-
mutationsDone = honourDoneMutationFlags && !mutationsDone && m.detail?.done;
|
|
78
|
-
return { ...m, target: undefined };
|
|
79
|
-
}),
|
|
80
|
-
{ wqEventOptions: { type: `${originalEventID}.mutate` } }
|
|
81
|
-
);
|
|
82
|
-
if (mutationsDone) {
|
|
83
|
-
liveStreamController.abort();
|
|
84
|
-
}
|
|
85
|
-
}, { signal, withArrayMethodDescriptors });
|
|
86
|
-
mutationListeners.add(liveStreamController);
|
|
87
|
-
return liveStreamController;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export function applyMutations(data, originalEventID, { signal, honourDoneMutationFlags = false } = {}) {
|
|
91
|
-
if (isTypeStream(data) || !_isTypeObject(data)) {
|
|
92
|
-
throw new TypeError('data must be a plain object and not a stream');
|
|
93
|
-
}
|
|
94
|
-
if (typeof originalEventID !== 'string') {
|
|
95
|
-
throw new TypeError('originalEventID must be a non-empty string');
|
|
96
|
-
}
|
|
97
|
-
const meta = _wq(this, 'meta');
|
|
98
|
-
meta.set('mutationListeners', meta.get('mutationListeners') || new Set);
|
|
99
|
-
const mutationListeners = meta.get('mutationListeners');
|
|
100
|
-
return new Promise((resolve) => {
|
|
101
|
-
const messageHandler = (e) => {
|
|
102
|
-
if (!e.data?.length) return;
|
|
103
|
-
let mutationsDone;
|
|
104
|
-
Observer.batch(data, () => {
|
|
105
|
-
for (const mutation of e.data) {
|
|
106
|
-
if (mutation.argumentsList) {
|
|
107
|
-
const target = !mutation.path.length ? data : Observer.get(data, Observer.path(...mutation.path));
|
|
108
|
-
Observer.proxy(target)[mutation.operation](...mutation.argumentsList);
|
|
109
|
-
} else if (mutation.key !== 'length' || ['set', 'defineProperty', 'deleteProperty'].includes(mutation.operation)) {
|
|
110
|
-
const target = mutation.path.length === 1 ? data : Observer.get(data, Observer.path(...mutation.path.slice(0, -1)));
|
|
111
|
-
if (mutation.type === 'delete') {
|
|
112
|
-
Observer.deleteProperty(target, mutation.key);
|
|
113
|
-
} else {
|
|
114
|
-
Observer.set(target, mutation.key, mutation.value);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
mutationsDone = honourDoneMutationFlags && !mutationsDone && mutation.detail?.done;
|
|
118
|
-
}
|
|
119
|
-
});
|
|
120
|
-
if (mutationsDone) {
|
|
121
|
-
cleanup();
|
|
122
|
-
}
|
|
123
|
-
};
|
|
124
|
-
this.addEventListener(`${originalEventID}.mutate`, messageHandler, { signal });
|
|
125
|
-
const cleanup = () => {
|
|
126
|
-
this.removeEventListener(`${originalEventID}.mutate`, messageHandler);
|
|
127
|
-
resolve();
|
|
128
|
-
};
|
|
129
|
-
mutationListeners.add(cleanup);
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
export function forwardPort(eventTypes, eventTarget, { resolveData = null, bidirectional = false, namespace1 = null, namespace2 = null } = {}) {
|
|
134
|
-
if (!(this instanceof WQMessagePort) || !(eventTarget instanceof WQMessagePort)) {
|
|
135
|
-
throw new Error('Both ports must be instance of WQMessagePort.');
|
|
136
|
-
}
|
|
137
|
-
if (!eventTypes) {
|
|
138
|
-
throw new Error('Event types must be specified.');
|
|
139
|
-
}
|
|
140
|
-
const meta = _wq(this, 'meta');
|
|
141
|
-
meta.set('downstreamRegistry', meta.get('downstreamRegistry') || new Set);
|
|
142
|
-
const downstreamRegistry = meta.get('downstreamRegistry');
|
|
143
|
-
const registration = { eventTarget, eventTypes, options: { resolveData, namespace1, namespace2 } };
|
|
144
|
-
downstreamRegistry.add(registration);
|
|
145
|
-
let cleanup2;
|
|
146
|
-
if (bidirectional) {
|
|
147
|
-
cleanup2 = forwardPort.call(
|
|
148
|
-
eventTarget,
|
|
149
|
-
typeof eventTypes === 'function' ? eventTypes : [].concat(eventTypes).filter((s) => s !== 'close'),
|
|
150
|
-
this,
|
|
151
|
-
{ resolveData, bidirectional: false, namespace1: namespace2, namespace2: namespace1 }
|
|
152
|
-
);
|
|
153
|
-
}
|
|
154
|
-
return () => {
|
|
155
|
-
downstreamRegistry.delete(registration);
|
|
156
|
-
cleanup2?.();
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
export function forwardEvent(event) {
|
|
161
|
-
if (event.propagationStopped) return;
|
|
162
|
-
const meta = _wq(this, 'meta');
|
|
163
|
-
if (meta.get('parentNode') instanceof EventTarget && (
|
|
164
|
-
event.bubbles || meta.get('parentNode').findPort?.((port) => port === this) && event instanceof WQMessageEvent)
|
|
165
|
-
) {
|
|
166
|
-
meta.get('parentNode').dispatchEvent(event);
|
|
167
|
-
}
|
|
168
|
-
if (!meta.has('downstreamRegistry')) return;
|
|
169
|
-
const downstreamRegistry = meta.get('downstreamRegistry');
|
|
170
|
-
if (event instanceof WQMessageEvent || event.type === 'close' || event.type.endsWith(':close')) {
|
|
171
|
-
const { type, eventID, data, live, bubbles, ports } = event;
|
|
172
|
-
const called = new WeakSet;
|
|
173
|
-
for (const { eventTarget, eventTypes, options } of downstreamRegistry) {
|
|
174
|
-
if (called.has(eventTarget)) continue;
|
|
175
|
-
let matches, $type = type;
|
|
176
|
-
if (options.namespace1) {
|
|
177
|
-
[, $type] = (new RegExp(`^${options.namespace1.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}:([^:]+)$`)).exec(type) || [];
|
|
178
|
-
if (!$type) continue;
|
|
179
|
-
}
|
|
180
|
-
if (typeof eventTypes === 'function') {
|
|
181
|
-
matches = eventTypes($type, this, eventTarget, options);
|
|
182
|
-
} else {
|
|
183
|
-
matches = [].concat(eventTypes).find((t) => {
|
|
184
|
-
return t === $type || ($type !== 'close' && t === '*'); // Star shouldn't imply close
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
if (!matches) continue;
|
|
188
|
-
called.add(eventTarget);
|
|
189
|
-
if ($type === 'close') {
|
|
190
|
-
eventTarget.close?.();
|
|
191
|
-
} else {
|
|
192
|
-
eventTarget.postMessage(options.resolveData ? options.resolveData(data, this, eventTarget, options) : data, {
|
|
193
|
-
transfers: ports,
|
|
194
|
-
wqEventOptions: { type: options.namespace2 ? `${options.namespace2}:${$type}` : $type, eventID, bubbles, live, forwarded: true },
|
|
195
|
-
wqProcessingOptions: { except: this } // IMPORTANT, in BroadcastChannel scenarios
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
export function portHooksCleanup() {
|
|
203
|
-
const meta = _wq(this, 'meta');
|
|
204
|
-
// 1. Destry listenersRegistry
|
|
205
|
-
for (const args of meta.get('listenersRegistry') || []) {
|
|
206
|
-
this.removeEventListener(...args);
|
|
207
|
-
}
|
|
208
|
-
meta.get('listenersRegistry')?.clear();
|
|
209
|
-
// 2. Destroy downstreamRegistry
|
|
210
|
-
meta.get('downstreamRegistry')?.clear();
|
|
211
|
-
// 3. Destroy mutationListeners
|
|
212
|
-
for (const mutationListener of meta.get('mutationListeners') || []) {
|
|
213
|
-
if (typeof mutationListener === 'function') {
|
|
214
|
-
mutationListener();
|
|
215
|
-
} else if (mutationListener instanceof AbortController) {
|
|
216
|
-
mutationListener.abort();
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
meta.get('mutationListeners')?.clear();
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
export function toWQPort(port) {
|
|
223
|
-
if (WQMessagePortInstanceTag in port) {
|
|
224
|
-
return port;
|
|
225
|
-
}
|
|
226
|
-
const portMeta = _wq(port, 'meta');
|
|
227
|
-
const messageHandler = (e) => {
|
|
228
|
-
if (!_isObject(e.data) || !e.data['.wq']) return;
|
|
229
|
-
// Handle lifecycle events from the other end
|
|
230
|
-
if (!portMeta.get('open')) {
|
|
231
|
-
// On first message, whether just an "open" ping or not...
|
|
232
|
-
// Fire an "open" event on self
|
|
233
|
-
portMeta.set('open', true);
|
|
234
|
-
port.dispatchEvent(new Event('open'));
|
|
235
|
-
portMeta.get('onlineCallback')?.();
|
|
236
|
-
}
|
|
237
|
-
if (!portMeta.get('close') && e.data.ping === 'close') {
|
|
238
|
-
// On receiving a "close" ping
|
|
239
|
-
// Fire a "close" event on self
|
|
240
|
-
portClose.call(port);
|
|
241
|
-
}
|
|
242
|
-
// Stop here if just a ping event
|
|
243
|
-
if (['open', 'close'].includes(e.data.ping)) {
|
|
244
|
-
e.stopImmediatePropagation();
|
|
245
|
-
return;
|
|
246
|
-
}
|
|
247
|
-
// Do event rewrites and the Webflo live object magic
|
|
248
|
-
if (e.type === 'message' && 'data' in e.data && ['wqEventOptions', 'wqProcessingOptions'].every((k) => _isObject(e.data[k]))) {
|
|
249
|
-
e.stopImmediatePropagation();
|
|
250
|
-
const event = new WQMessageEvent(port, {
|
|
251
|
-
data: e.data.data,
|
|
252
|
-
wqEventOptions: e.data.wqEventOptions,
|
|
253
|
-
wqProcessingOptions: e.data.wqProcessingOptions,
|
|
254
|
-
ports: e.ports,
|
|
255
|
-
});
|
|
256
|
-
port.dispatchEvent(event);
|
|
257
|
-
forwardEvent.call(port, event);
|
|
258
|
-
return;
|
|
259
|
-
}
|
|
260
|
-
};
|
|
261
|
-
port.addEventListener('message', messageHandler);
|
|
262
|
-
portMeta.set('messageHandler', messageHandler);
|
|
263
|
-
// Only after the above native methods usages...
|
|
264
|
-
let isMessaging = false;
|
|
265
|
-
Object.defineProperty(port, 'wqLifecycle', {
|
|
266
|
-
value: {
|
|
267
|
-
open: new Promise((resolve) => {
|
|
268
|
-
if (portMeta.get('open')) return resolve();
|
|
269
|
-
portMeta.set('onlineCallback', resolve);
|
|
270
|
-
}),
|
|
271
|
-
close: new Promise((resolve) => {
|
|
272
|
-
if (portMeta.get('close')) return resolve();
|
|
273
|
-
portMeta.set('offlineCallback', resolve);
|
|
274
|
-
}),
|
|
275
|
-
messaging: new Promise((resolve) => {
|
|
276
|
-
if (portMeta.get('messaging')) {
|
|
277
|
-
isMessaging = true;
|
|
278
|
-
return resolve();
|
|
279
|
-
}
|
|
280
|
-
portMeta.set('messagingCallback', () => {
|
|
281
|
-
isMessaging = true;
|
|
282
|
-
resolve()
|
|
283
|
-
});
|
|
284
|
-
}),
|
|
285
|
-
isMessaging: (() => isMessaging),
|
|
286
|
-
}
|
|
287
|
-
});
|
|
288
|
-
Object.defineProperties(port, prototypeExtensions);
|
|
289
|
-
// Set the tag
|
|
290
|
-
port[WQMessagePortInstanceTag] = true;
|
|
291
|
-
return port;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
export function internalAddEventListener(args) {
|
|
295
|
-
const meta = _wq(this, 'meta');
|
|
296
|
-
meta.set('listenersRegistry', meta.get('listenersRegistry') || new Set);
|
|
297
|
-
meta.get('listenersRegistry').add(args);
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
export function postRequest(data, callback, options = {}) {
|
|
301
|
-
const { eventOptions2 = {}, transfer = [], ...$options } = options;
|
|
302
|
-
const { signal, once } = eventOptions2;
|
|
303
|
-
const messageChannel = new MessageChannel;
|
|
304
|
-
messageChannel.port1.start();
|
|
305
|
-
toWQPort(messageChannel.port1);
|
|
306
|
-
messageChannel.port1.addEventListener('message', (e) => callback(e), { signal, once });
|
|
307
|
-
return this.postMessage(data, { ...$options, transfer: [messageChannel.port2].concat(transfer) });
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
export function handleRequests(type, listener, options = {}) {
|
|
311
|
-
const $listener = async (e) => {
|
|
312
|
-
const response = await listener(e);
|
|
313
|
-
for (const port of e.ports) {
|
|
314
|
-
port.postMessage(response);
|
|
315
|
-
}
|
|
316
|
-
};
|
|
317
|
-
this.addEventListener(type, $listener, options);
|
|
318
|
-
return () => {
|
|
319
|
-
this.removeEventListener(type, $listener, options);
|
|
320
|
-
};
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
export function portIsMessaging() {
|
|
324
|
-
const meta = _wq(this, 'meta');
|
|
325
|
-
if (!meta.get('messaging')) {
|
|
326
|
-
meta.set('messaging', true);
|
|
327
|
-
meta.get('messagingCallback')?.();
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
function autoPortOpen() {
|
|
332
|
-
const meta = _wq(this, 'meta');
|
|
333
|
-
if (!meta.get('open')) {
|
|
334
|
-
meta.set('open', true);
|
|
335
|
-
this.postMessage({
|
|
336
|
-
['.wq']: true,
|
|
337
|
-
ping: 'open',
|
|
338
|
-
});
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
function portClose() {
|
|
343
|
-
// Unregister custom event rewrites
|
|
344
|
-
const meta = _wq(this, 'meta');
|
|
345
|
-
this.removeEventListener('message', meta.get('messageHandler'));
|
|
346
|
-
meta.set('close', true);
|
|
347
|
-
meta.get('offlineCallback')?.();
|
|
348
|
-
portHooksCleanup.call(this);
|
|
349
|
-
this.dispatchEvent(new Event('close'));
|
|
350
|
-
// Unset the tag
|
|
351
|
-
this[WQMessagePortInstanceTag] = false;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
const prototypeOriginals = {
|
|
355
|
-
addEventListener: MessagePort.prototype.addEventListener,
|
|
356
|
-
postMessage: MessagePort.prototype.postMessage,
|
|
357
|
-
close: MessagePort.prototype.close,
|
|
358
|
-
onmessage: Object.getOwnPropertyDescriptor(MessagePort.prototype, 'onmessage'),
|
|
359
|
-
};
|
|
360
|
-
|
|
361
|
-
const prototypeExtensions = {
|
|
362
|
-
wqForwardPort: {
|
|
363
|
-
value: function (eventTypes, eventTarget, { resolveData = null, bidirectional = false, namespace1 = null, namespace2 = null } = {}) {
|
|
364
|
-
return forwardPort.call(this, eventTypes, eventTarget, { resolveData, bidirectional, namespace1, namespace2 });
|
|
365
|
-
}
|
|
366
|
-
},
|
|
367
|
-
// --------
|
|
368
|
-
addEventListener: {
|
|
369
|
-
value: function (...args) {
|
|
370
|
-
// On first interaction,
|
|
371
|
-
// ping the other end with "open"
|
|
372
|
-
autoPortOpen.call(this);
|
|
373
|
-
internalAddEventListener.call(this, args);
|
|
374
|
-
return prototypeOriginals.addEventListener.call(this, ...args);
|
|
375
|
-
},
|
|
376
|
-
},
|
|
377
|
-
onmessage: {
|
|
378
|
-
get: function () {
|
|
379
|
-
return prototypeOriginals.onmessage.get.call(this);
|
|
380
|
-
},
|
|
381
|
-
set: function (value) {
|
|
382
|
-
this.start();
|
|
383
|
-
autoPortOpen.call(this);
|
|
384
|
-
return prototypeOriginals.onmessage.set.call(this, value);
|
|
385
|
-
},
|
|
386
|
-
},
|
|
387
|
-
postMessage: {
|
|
388
|
-
value: function (data, transferOrOptions = {}) {
|
|
389
|
-
portIsMessaging.call(this);
|
|
390
|
-
const { wqEventOptions, wqProcessingOptions: _, ...portOptions } = preProcessPostMessage.call(this, data, transferOrOptions);
|
|
391
|
-
return prototypeOriginals.postMessage.call(this, {
|
|
392
|
-
data,
|
|
393
|
-
wqEventOptions,
|
|
394
|
-
wqProcessingOptions: {},
|
|
395
|
-
['.wq']: true,
|
|
396
|
-
}, portOptions);
|
|
397
|
-
}
|
|
398
|
-
},
|
|
399
|
-
postRequest: { value: postRequest },
|
|
400
|
-
handleRequests: { value: handleRequests },
|
|
401
|
-
close: {
|
|
402
|
-
value: function () {
|
|
403
|
-
// On close, tear down and fire "close" event on self
|
|
404
|
-
// and ping the other end with "close"
|
|
405
|
-
portClose.call(this);
|
|
406
|
-
this.postMessage({
|
|
407
|
-
['.wq']: true,
|
|
408
|
-
ping: 'close',
|
|
409
|
-
});
|
|
410
|
-
return prototypeOriginals.close.call(this);
|
|
411
|
-
}
|
|
412
|
-
},
|
|
413
|
-
};
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { _even } from '@webqit/util/obj/index.js';
|
|
2
|
-
import { _isObject } from '@webqit/util/js/index.js';
|
|
3
|
-
import { renderCookieObjToString } from '../webflo-fetch/index.js';
|
|
4
|
-
import { HttpState } from './HttpState.js';
|
|
5
|
-
|
|
6
|
-
export class HttpCookies extends HttpState {
|
|
7
|
-
|
|
8
|
-
#originals;
|
|
9
|
-
|
|
10
|
-
constructor({ request, thread = null, entries = [] }) {
|
|
11
|
-
entries = [...entries].map(([key, value]) => [key, !_isObject(value) ? { name: key, value } : value]);
|
|
12
|
-
super({ store: new Map(entries), request, thread });
|
|
13
|
-
this.#originals = new Map(entries);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
async set(key, value) {
|
|
17
|
-
if (!_isObject(value)) { value = { name: key, value }; }
|
|
18
|
-
return await super.set(key, value);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
async get(key, withDetail = false) {
|
|
22
|
-
if (!withDetail) return (await super.get(key))?.value;
|
|
23
|
-
return await super.get(key);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
async render() {
|
|
27
|
-
const entries = await Promise.all((await this.keys()).concat(this.#originals.keys()).map(async (key) => {
|
|
28
|
-
const a = this.#originals.get(key);
|
|
29
|
-
const b = await this.get(key, true);
|
|
30
|
-
if (a === b || (_isObject(a) && _isObject(b) && _even(a, b))) {
|
|
31
|
-
// Same
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
if ([undefined, null].includes(b)) {
|
|
35
|
-
// Deleted
|
|
36
|
-
return { name: key, value: '', maxAge: 0 };
|
|
37
|
-
}
|
|
38
|
-
// Added or modified
|
|
39
|
-
return { name: key, ...(await this.get(key, true)) };
|
|
40
|
-
})).then((entries) => entries.filter((e) => e));
|
|
41
|
-
return entries.map((e) => renderCookieObjToString(e));
|
|
42
|
-
}
|
|
43
|
-
}
|