@webqit/webflo 0.20.4-next.2 → 0.20.4-next.3
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 -20
- package/site/docs/concepts/realtime.md +45 -44
- package/site/docs/getting-started.md +40 -40
- package/src/{Context.js → CLIContext.js} +9 -8
- package/src/build-pi/esbuild-plugin-livejs-transform.js +35 -0
- package/src/{runtime-pi/webflo-client/webflo-codegen.js → build-pi/index.js} +145 -141
- package/src/index.js +3 -1
- package/src/init-pi/index.js +6 -3
- package/src/init-pi/templates/pwa/package.json +2 -2
- package/src/init-pi/templates/web/package.json +2 -2
- package/src/runtime-pi/AppBootstrap.js +38 -0
- package/src/runtime-pi/WebfloRuntime.js +50 -47
- package/src/runtime-pi/apis.js +9 -0
- package/src/runtime-pi/index.js +2 -4
- package/src/runtime-pi/webflo-client/WebfloClient.js +31 -35
- package/src/runtime-pi/webflo-client/WebfloRootClient1.js +16 -14
- package/src/runtime-pi/webflo-client/WebfloSubClient.js +13 -13
- package/src/runtime-pi/webflo-client/bootstrap.js +37 -0
- package/src/runtime-pi/webflo-client/index.js +2 -8
- package/src/runtime-pi/webflo-client/webflo-devmode.js +3 -3
- package/src/runtime-pi/webflo-fetch/LiveResponse.js +127 -96
- package/src/runtime-pi/webflo-fetch/index.js +435 -5
- package/src/runtime-pi/webflo-routing/HttpCookies.js +1 -1
- package/src/runtime-pi/webflo-routing/HttpEvent.js +5 -6
- package/src/runtime-pi/webflo-routing/HttpUser.js +7 -7
- package/src/runtime-pi/webflo-server/ServerSideCookies.js +3 -1
- package/src/runtime-pi/webflo-server/ServerSideSession.js +2 -1
- package/src/runtime-pi/webflo-server/WebfloServer.js +98 -195
- package/src/runtime-pi/webflo-server/bootstrap.js +59 -0
- package/src/runtime-pi/webflo-server/index.js +2 -6
- package/src/runtime-pi/webflo-server/webflo-devmode.js +13 -24
- package/src/runtime-pi/webflo-worker/WebfloWorker.js +11 -15
- package/src/runtime-pi/webflo-worker/WorkerSideCookies.js +2 -1
- package/src/runtime-pi/webflo-worker/bootstrap.js +38 -0
- package/src/runtime-pi/webflo-worker/index.js +3 -7
- package/src/webflo-cli.js +1 -2
- package/src/runtime-pi/webflo-fetch/cookies.js +0 -10
- package/src/runtime-pi/webflo-fetch/fetch.js +0 -16
- package/src/runtime-pi/webflo-fetch/formdata.js +0 -54
- package/src/runtime-pi/webflo-fetch/headers.js +0 -151
- package/src/runtime-pi/webflo-fetch/message.js +0 -49
- package/src/runtime-pi/webflo-fetch/request.js +0 -62
- package/src/runtime-pi/webflo-fetch/response.js +0 -110
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { State, Observer } from '@webqit/quantum-js';
|
|
2
2
|
import { _isObject, _isTypeObject } from '@webqit/util/js/index.js';
|
|
3
3
|
import { publishMutations, applyMutations } from '../webflo-messaging/wq-message-port.js';
|
|
4
|
-
import {
|
|
4
|
+
import { WQBroadcastChannel } from '../webflo-messaging/WQBroadcastChannel.js';
|
|
5
|
+
import { WQSockPort } from '../webflo-messaging/WQSockPort.js';
|
|
6
|
+
import { response as responseShim } from './index.js';
|
|
5
7
|
import { _wq, _await } from '../../util.js';
|
|
6
8
|
import { isTypeStream } from './util.js';
|
|
7
9
|
|
|
8
10
|
export class LiveResponse extends EventTarget {
|
|
9
11
|
|
|
10
|
-
/* STATIC methods for Input sources */
|
|
11
|
-
|
|
12
12
|
static test(data) {
|
|
13
13
|
if (data instanceof LiveResponse) {
|
|
14
14
|
return 'LiveResponse';
|
|
@@ -25,15 +25,15 @@ export class LiveResponse extends EventTarget {
|
|
|
25
25
|
return 'Default';
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
static from(data, ...args) {
|
|
28
|
+
static async from(data, ...args) {
|
|
29
29
|
if (data instanceof LiveResponse) {
|
|
30
30
|
return data.clone(...args);
|
|
31
31
|
}
|
|
32
32
|
if (data instanceof Response) {
|
|
33
|
-
return this.fromResponse(data, ...args);
|
|
33
|
+
return await this.fromResponse(data, ...args);
|
|
34
34
|
}
|
|
35
35
|
if (isGenerator(data)) {
|
|
36
|
-
return this.fromGenerator(data, ...args);
|
|
36
|
+
return await this.fromGenerator(data, ...args);
|
|
37
37
|
}
|
|
38
38
|
if (data instanceof State) {
|
|
39
39
|
return this.fromQuantum(data, ...args);
|
|
@@ -41,91 +41,99 @@ export class LiveResponse extends EventTarget {
|
|
|
41
41
|
return new this(data, ...args);
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
static fromResponse(response, options = {}) {
|
|
44
|
+
static async fromResponse(response, options = {}) {
|
|
45
45
|
if (!(response instanceof Response)) {
|
|
46
46
|
throw new Error('Argument must be a Response instance.');
|
|
47
47
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
_wq(instance).set('meta', responseMeta);
|
|
59
|
-
// Generator binding
|
|
60
|
-
if (response.isLive() === 2) {
|
|
61
|
-
if (_isTypeObject(body) && !isTypeStream(body)) {
|
|
62
|
-
applyMutations.call(response.wqRealtime,
|
|
63
|
-
body,
|
|
64
|
-
response.headers.get('X-Live-Response-Message-ID').trim(),
|
|
65
|
-
{ signal: instance.#abortController.signal }
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
|
-
// Capture subsequent frames?
|
|
69
|
-
response.wqRealtime.addEventListener('response.replace', (e) => {
|
|
70
|
-
const { body, ...options } = e.data;
|
|
71
|
-
instance.#replaceWith(body, options);
|
|
72
|
-
}, { signal: instance.#abortController.signal });
|
|
73
|
-
response.wqRealtime.addEventListener('close', () => {
|
|
74
|
-
instance.#extendLifecycle(Promise.resolve());
|
|
75
|
-
}, { once: true, signal: instance.#abortController.signal });
|
|
76
|
-
}
|
|
77
|
-
// Data props
|
|
78
|
-
instance.#type = response.type;
|
|
79
|
-
instance.#redirected = response.redirected;
|
|
80
|
-
instance.#url = response.url;
|
|
81
|
-
// Lifecycle props
|
|
82
|
-
instance.#generator = response;
|
|
83
|
-
instance.#generatorType = 'Response';
|
|
84
|
-
return instance;
|
|
48
|
+
|
|
49
|
+
const body = await responseShim.prototype.parse.value.call(response);
|
|
50
|
+
|
|
51
|
+
// Instance
|
|
52
|
+
const instance = new this(body, {
|
|
53
|
+
status: responseShim.prototype.status.get.call(response),
|
|
54
|
+
statusText: response.statusText,
|
|
55
|
+
headers: response.headers,
|
|
56
|
+
done: false,
|
|
57
|
+
...options,
|
|
85
58
|
});
|
|
59
|
+
const responseMeta = _wq(response, 'meta');
|
|
60
|
+
_wq(instance).set('meta', responseMeta);
|
|
61
|
+
|
|
62
|
+
// Generator binding
|
|
63
|
+
if (this.hasBackground(response) === 2) {
|
|
64
|
+
const backgroundPort = this.getBackground(response);
|
|
65
|
+
if (_isTypeObject(body) && !isTypeStream(body)) {
|
|
66
|
+
applyMutations.call(backgroundPort,
|
|
67
|
+
body,
|
|
68
|
+
response.headers.get('X-Live-Response-Message-ID').trim(),
|
|
69
|
+
{ signal: instance.#abortController.signal }
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
// Capture subsequent frames?
|
|
73
|
+
backgroundPort.addEventListener('response.replace', (e) => {
|
|
74
|
+
const { body, ...options } = e.data;
|
|
75
|
+
instance.#replaceWith(body, options);
|
|
76
|
+
}, { signal: instance.#abortController.signal });
|
|
77
|
+
backgroundPort.addEventListener('close', () => {
|
|
78
|
+
instance.#extendLifecycle(Promise.resolve());
|
|
79
|
+
}, { once: true, signal: instance.#abortController.signal });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Data props
|
|
83
|
+
instance.#type = response.type;
|
|
84
|
+
instance.#redirected = response.redirected;
|
|
85
|
+
instance.#url = response.url;
|
|
86
|
+
// Lifecycle props
|
|
87
|
+
instance.#generator = response;
|
|
88
|
+
instance.#generatorType = 'Response';
|
|
89
|
+
|
|
90
|
+
return instance;
|
|
86
91
|
}
|
|
87
92
|
|
|
88
|
-
static fromGenerator(gen, options = {}) {
|
|
93
|
+
static async fromGenerator(gen, options = {}) {
|
|
89
94
|
if (!isGenerator(gen)) {
|
|
90
95
|
throw new Error('Argument must be a generator or async generator.');
|
|
91
96
|
}
|
|
92
|
-
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
97
|
+
|
|
98
|
+
const firstFrame = await gen.next();
|
|
99
|
+
const firstValue = await firstFrame.value;
|
|
100
|
+
|
|
101
|
+
if (firstValue instanceof Response && firstFrame.done && options.done !== false/* && value.responsesOK*/) {
|
|
102
|
+
return firstValue;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
let instance;
|
|
106
|
+
let frame = firstFrame;
|
|
107
|
+
let value = firstValue;
|
|
108
|
+
let $$await;
|
|
109
|
+
|
|
110
|
+
const $options = { done: firstFrame.done, ...options };
|
|
111
|
+
if (value instanceof LiveResponse) {
|
|
112
|
+
instance = new this;
|
|
113
|
+
const responseMeta = _wq(value, 'meta');
|
|
114
|
+
_wq(instance).set('meta', responseMeta);
|
|
115
|
+
$$await = instance.#replaceWith(value, $options);
|
|
116
|
+
} else {
|
|
117
|
+
instance = await this.from/*important*/(value, $options);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
(async function () {
|
|
121
|
+
await $$await;
|
|
122
|
+
instance.#generator = gen;
|
|
123
|
+
instance.#generatorType = 'Generator';
|
|
124
|
+
while (!frame.done && !options.done && !instance.#abortController.signal.aborted) {
|
|
125
|
+
frame = await gen.next();
|
|
126
|
+
value = await frame.value;
|
|
127
|
+
if (!instance.#abortController.signal.aborted) {
|
|
128
|
+
await instance.#replaceWith(value, { done: frame.done });
|
|
107
129
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
instance.#generator = gen;
|
|
112
|
-
instance.#generatorType = 'Generator';
|
|
113
|
-
while (!frame.done && !options.done && !instance.#abortController.signal.aborted) {
|
|
114
|
-
frame = await gen.next();
|
|
115
|
-
value = await frame.value;
|
|
116
|
-
if (!instance.#abortController.signal.aborted) {
|
|
117
|
-
await instance.#replaceWith(value, { done: frame.done });
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
})();
|
|
121
|
-
return instance;
|
|
122
|
-
});
|
|
123
|
-
});
|
|
124
|
-
});
|
|
130
|
+
}
|
|
131
|
+
})();
|
|
132
|
+
|
|
125
133
|
return instance;
|
|
126
134
|
}
|
|
127
135
|
|
|
128
|
-
static fromQuantum(qState, options = {}) {
|
|
136
|
+
static async fromQuantum(qState, options = {}) {
|
|
129
137
|
if (!(qState instanceof State)) {
|
|
130
138
|
throw new Error('Argument must be a Quantum State instance.');
|
|
131
139
|
}
|
|
@@ -141,6 +149,34 @@ export class LiveResponse extends EventTarget {
|
|
|
141
149
|
return instance;
|
|
142
150
|
}
|
|
143
151
|
|
|
152
|
+
static hasBackground(respone) {
|
|
153
|
+
let liveLevel = (respone.headers?.get?.('X-Background-Messaging-Port')?.trim() || _wq(respone, 'meta').has('background_port')) && 1 || 0;
|
|
154
|
+
liveLevel += respone.headers?.get?.('X-Live-Response-Message-ID')?.trim() && 1 || 0;
|
|
155
|
+
return liveLevel;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
static getBackground(respone) {
|
|
159
|
+
if (!(respone instanceof Response)
|
|
160
|
+
&& !(respone instanceof LiveResponse)) return;
|
|
161
|
+
const responseMeta = _wq(respone, 'meta');
|
|
162
|
+
if (!responseMeta.has('background_port')) {
|
|
163
|
+
const value = respone.headers.get('X-Background-Messaging-Port')?.trim();
|
|
164
|
+
if (value) {
|
|
165
|
+
const [proto, portID] = value.split(':');
|
|
166
|
+
let backgroundPort;
|
|
167
|
+
if (proto === 'br') {
|
|
168
|
+
backgroundPort = new WQBroadcastChannel(portID);
|
|
169
|
+
} else if (proto === 'ws') {
|
|
170
|
+
backgroundPort = new WQSockPort(portID);
|
|
171
|
+
} else {
|
|
172
|
+
throw new Error(`Unknown background messaging protocol: ${proto}`);
|
|
173
|
+
}
|
|
174
|
+
responseMeta.set('background_port', backgroundPort);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return responseMeta.get('background_port');
|
|
178
|
+
}
|
|
179
|
+
|
|
144
180
|
#generator = null;
|
|
145
181
|
#generatorType = 'Default';
|
|
146
182
|
get generatorType() { return this.#generatorType; }
|
|
@@ -193,18 +229,10 @@ export class LiveResponse extends EventTarget {
|
|
|
193
229
|
|
|
194
230
|
/* Level 3 props */
|
|
195
231
|
|
|
196
|
-
get
|
|
197
|
-
return responseRealtime.call(this);
|
|
198
|
-
}
|
|
232
|
+
get background() { return this.constructor.getBackground(this); }
|
|
199
233
|
|
|
200
234
|
/* Lifecycle methods */
|
|
201
235
|
|
|
202
|
-
isLive() {
|
|
203
|
-
let liveLevel = (this.headers.get('X-Background-Messaging-Port')?.trim() || _wq(this, 'meta').has('wqRealtime')) && 1 || 0;
|
|
204
|
-
liveLevel += this.headers.get('X-Live-Response-Message-ID')?.trim() && 1 || 0;
|
|
205
|
-
return liveLevel;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
236
|
whileLive(returningThePromise = false) {
|
|
209
237
|
if (returningThePromise) {
|
|
210
238
|
return this.#lifeCycleResolutionPromise;
|
|
@@ -295,8 +323,8 @@ export class LiveResponse extends EventTarget {
|
|
|
295
323
|
this.#generator = response;
|
|
296
324
|
this.#generatorType = response instanceof Response ? 'Response' : 'LiveResponse';
|
|
297
325
|
execReplaceWith({
|
|
298
|
-
body: response instanceof Response ? await
|
|
299
|
-
status:
|
|
326
|
+
body: response instanceof Response ? await responseShim.prototype.parse.value.call(response) : response.body,
|
|
327
|
+
status: responseShim.prototype.status.get.call(response),
|
|
300
328
|
statusText: response.statusText,
|
|
301
329
|
headers: response.headers,
|
|
302
330
|
...options,
|
|
@@ -362,21 +390,25 @@ export class LiveResponse extends EventTarget {
|
|
|
362
390
|
await this.#replaceWith(body, ...args);
|
|
363
391
|
}
|
|
364
392
|
|
|
365
|
-
toResponse({
|
|
366
|
-
const response =
|
|
393
|
+
toResponse({ client: clientPort, signal: abortSignal } = {}) {
|
|
394
|
+
const response = responseShim.from.value(this.body, {
|
|
367
395
|
status: this.status,
|
|
368
396
|
statusText: this.statusText,
|
|
369
397
|
headers: this.headers,
|
|
370
398
|
});
|
|
399
|
+
|
|
371
400
|
const responseMeta = _wq(this, 'meta');
|
|
372
401
|
_wq(response).set('meta', responseMeta);
|
|
373
|
-
|
|
402
|
+
|
|
403
|
+
if (clientPort && this.whileLive()) {
|
|
374
404
|
const liveResponseMessageID = Date.now().toString();
|
|
375
405
|
response.headers.set('X-Live-Response-Message-ID', liveResponseMessageID);
|
|
406
|
+
|
|
376
407
|
// Publish mutations
|
|
377
408
|
if (_isTypeObject(this.body) && !isTypeStream(this.body)) {
|
|
378
|
-
publishMutations.call(
|
|
409
|
+
publishMutations.call(clientPort, this.body, liveResponseMessageID, { signal: abortSignal/* stop observing mutations on body when we abort */ });
|
|
379
410
|
}
|
|
411
|
+
|
|
380
412
|
// Publish replacements?
|
|
381
413
|
const replaceHandler = () => {
|
|
382
414
|
const headers = Object.fromEntries([...this.headers.entries()]);
|
|
@@ -384,7 +416,7 @@ export class LiveResponse extends EventTarget {
|
|
|
384
416
|
delete headers['set-cookie'];
|
|
385
417
|
console.warn('Warning: The "set-cookie" header is not supported for security reasons and has been removed from the response.');
|
|
386
418
|
}
|
|
387
|
-
|
|
419
|
+
clientPort.postMessage({
|
|
388
420
|
body: this.body,
|
|
389
421
|
status: this.status,
|
|
390
422
|
statusText: this.statusText,
|
|
@@ -392,6 +424,7 @@ export class LiveResponse extends EventTarget {
|
|
|
392
424
|
done: !this.whileLive(),
|
|
393
425
|
}, { wqEventOptions: { type: 'response.replace', live: true/*gracefully ignored if not an object*/ }, observerOptions: { signal: abortSignal/* stop observing mutations on body when we abort */ } });
|
|
394
426
|
};
|
|
427
|
+
|
|
395
428
|
this.addEventListener('replace', replaceHandler, { signal: abortSignal/* stop listening when we abort */ });
|
|
396
429
|
}
|
|
397
430
|
return response;
|
|
@@ -433,5 +466,3 @@ class StateX extends State {
|
|
|
433
466
|
constructor() { }
|
|
434
467
|
dispose() { }
|
|
435
468
|
}
|
|
436
|
-
|
|
437
|
-
globalThis.LiveResponse = LiveResponse;
|