@webqit/webflo 0.20.4-next.2 → 0.20.4-next.4
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 +13 -34
- 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-uselive-transform.js +42 -0
- package/src/{runtime-pi/webflo-client/webflo-codegen.js → build-pi/index.js} +148 -142
- package/src/index.js +3 -1
- package/src/init-pi/index.js +7 -4
- package/src/init-pi/templates/pwa/.gitignore +6 -0
- package/src/init-pi/templates/pwa/.webqit/webflo/client.json +15 -0
- package/src/init-pi/templates/pwa/.webqit/webflo/layout.json +7 -0
- package/src/init-pi/templates/pwa/package.json +2 -2
- package/src/init-pi/templates/pwa/public/manifest.json +2 -2
- package/src/init-pi/templates/web/.gitignore +6 -0
- package/src/init-pi/templates/web/.webqit/webflo/client.json +12 -0
- package/src/init-pi/templates/web/.webqit/webflo/layout.json +7 -0
- package/src/init-pi/templates/web/package.json +2 -2
- package/src/runtime-pi/AppBootstrap.js +38 -0
- package/src/runtime-pi/WebfloRuntime.js +68 -56
- package/src/runtime-pi/apis.js +9 -0
- package/src/runtime-pi/index.js +2 -4
- package/src/runtime-pi/webflo-client/DeviceCapabilities.js +1 -1
- package/src/runtime-pi/webflo-client/WebfloClient.js +33 -36
- package/src/runtime-pi/webflo-client/WebfloRootClient1.js +23 -17
- package/src/runtime-pi/webflo-client/WebfloRootClient2.js +1 -1
- package/src/runtime-pi/webflo-client/WebfloSubClient.js +14 -14
- package/src/runtime-pi/webflo-client/bootstrap.js +38 -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 +154 -116
- package/src/runtime-pi/webflo-fetch/index.js +436 -5
- package/src/runtime-pi/webflo-messaging/wq-message-port.js +1 -1
- package/src/runtime-pi/webflo-routing/HttpCookies.js +1 -1
- package/src/runtime-pi/webflo-routing/HttpEvent.js +12 -11
- package/src/runtime-pi/webflo-routing/HttpUser.js +7 -7
- package/src/runtime-pi/webflo-routing/WebfloRouter.js +12 -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 +138 -200
- 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 +24 -31
- package/src/runtime-pi/webflo-url/Url.js +1 -1
- package/src/runtime-pi/webflo-url/xURL.js +1 -1
- 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 +39 -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
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import Fs from 'fs';
|
|
2
|
+
import Path from 'path';
|
|
3
|
+
import {
|
|
4
|
+
readLayoutConfig,
|
|
5
|
+
readEnvConfig,
|
|
6
|
+
readClientConfig,
|
|
7
|
+
readWorkerConfig,
|
|
8
|
+
scanRoots,
|
|
9
|
+
scanRouteHandlers,
|
|
10
|
+
} from '../../deployment-pi/util.js';
|
|
11
|
+
|
|
12
|
+
export async function bootstrap(cx, offset = '') {
|
|
13
|
+
const $init = Fs.existsSync('./init.client.js')
|
|
14
|
+
? Path.resolve('./init.client.js')
|
|
15
|
+
: null;
|
|
16
|
+
const config = {
|
|
17
|
+
LAYOUT: await readLayoutConfig(cx),
|
|
18
|
+
ENV: await readEnvConfig(cx),
|
|
19
|
+
CLIENT: await readClientConfig(cx),
|
|
20
|
+
WORKER: await readWorkerConfig(cx),
|
|
21
|
+
};
|
|
22
|
+
if (config.CLIENT.copy_public_variables) {
|
|
23
|
+
const publicEnvPattern = /(?:^|_)PUBLIC(?:_|$)/;
|
|
24
|
+
config.ENV.data = config.ENV.data || {};
|
|
25
|
+
for (const key in process.env) {
|
|
26
|
+
if (publicEnvPattern.test(key)) {
|
|
27
|
+
config.ENV.data[key] = process.env[key];
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const routes = {};
|
|
32
|
+
const $roots = Fs.existsSync(config.LAYOUT.PUBLIC_DIR) ? scanRoots(config.LAYOUT.PUBLIC_DIR, 'index.html') : [];
|
|
33
|
+
scanRouteHandlers(config.LAYOUT, 'client', (file, route) => {
|
|
34
|
+
routes[route] = file;
|
|
35
|
+
}, offset, $roots);
|
|
36
|
+
const outdir = Path.join(config.LAYOUT.PUBLIC_DIR, offset);
|
|
37
|
+
return { $init, config, routes, $roots, $sparoots: $roots, outdir, offset };
|
|
38
|
+
}
|
|
@@ -2,16 +2,10 @@ import { WebfloRootClient1 } from './WebfloRootClient1.js';
|
|
|
2
2
|
import { WebfloRootClient2 } from './WebfloRootClient2.js';
|
|
3
3
|
import { WebfloSubClient } from './WebfloSubClient.js';
|
|
4
4
|
|
|
5
|
-
export async function start() {
|
|
5
|
+
export async function start(bootstrap) {
|
|
6
6
|
const WebfloRootClient = window.navigation ? WebfloRootClient2 : WebfloRootClient1;
|
|
7
|
-
const instance = WebfloRootClient.create(
|
|
7
|
+
const instance = WebfloRootClient.create(bootstrap, document);
|
|
8
8
|
await instance.initialize();
|
|
9
9
|
WebfloSubClient.defineElement();
|
|
10
10
|
return instance;
|
|
11
11
|
}
|
|
12
|
-
|
|
13
|
-
export {
|
|
14
|
-
WebfloRootClient1,
|
|
15
|
-
WebfloRootClient2,
|
|
16
|
-
WebfloSubClient
|
|
17
|
-
}
|
|
@@ -45,7 +45,7 @@ export class WebfloHMR {
|
|
|
45
45
|
if (event.actionableEffect === 'unlink') {
|
|
46
46
|
delete this.#app.routes[event.affectedRoute];
|
|
47
47
|
} else {
|
|
48
|
-
this.#app.routes[event.affectedRoute] = `/@
|
|
48
|
+
this.#app.routes[event.affectedRoute] = `/@hmr?src=${event.affectedHandler}&t=${Date.now()}`;
|
|
49
49
|
}
|
|
50
50
|
statuses.routesAffected.add(event.affectedRoute);
|
|
51
51
|
} else if (event.realm === 'worker') {
|
|
@@ -203,7 +203,7 @@ export class WebfloHMR {
|
|
|
203
203
|
}
|
|
204
204
|
const $url = node.$url;
|
|
205
205
|
const url = encodeURIComponent($url.href.replace(`${$url.origin}/`, '')); // preserving origin query strings
|
|
206
|
-
const urlRewrite = `/@
|
|
206
|
+
const urlRewrite = `/@hmr?src=${url}&t=${Date.now()}`;
|
|
207
207
|
if (node.matches(this.#selectors.remoteStyleSheet)) {
|
|
208
208
|
node.setAttribute('href', urlRewrite);
|
|
209
209
|
return 1;
|
|
@@ -216,7 +216,7 @@ export class WebfloHMR {
|
|
|
216
216
|
}
|
|
217
217
|
|
|
218
218
|
async loadHTMLModule(url) {
|
|
219
|
-
const urlRewrite = `/@
|
|
219
|
+
const urlRewrite = `/@hmr?src=${url}?t=${Date.now()}`;
|
|
220
220
|
const fileContents = await fetch(urlRewrite).then((res) => res.text()).catch(() => null);
|
|
221
221
|
if (fileContents === null) return null;
|
|
222
222
|
const temp = document.createElement('template');
|
|
@@ -1,16 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Observer, LiveMode } from '@webqit/use-live';
|
|
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
|
-
|
|
12
|
+
[Symbol.toStringTag] = 'LiveResponse';
|
|
11
13
|
|
|
12
14
|
static test(data) {
|
|
13
|
-
if (data instanceof LiveResponse
|
|
15
|
+
if (data instanceof LiveResponse
|
|
16
|
+
|| data?.[Symbol.toStringTag] === 'LiveResponse') {
|
|
14
17
|
return 'LiveResponse';
|
|
15
18
|
}
|
|
16
19
|
if (data instanceof Response) {
|
|
@@ -19,121 +22,133 @@ export class LiveResponse extends EventTarget {
|
|
|
19
22
|
if (isGenerator(data)) {
|
|
20
23
|
return 'Generator';
|
|
21
24
|
}
|
|
22
|
-
if (data instanceof
|
|
23
|
-
|
|
25
|
+
if (data instanceof LiveMode
|
|
26
|
+
|| data?.[Symbol.toStringTag] === 'LiveMode') {
|
|
27
|
+
return 'LiveMode';
|
|
24
28
|
}
|
|
25
29
|
return 'Default';
|
|
26
30
|
}
|
|
27
31
|
|
|
28
|
-
static from(data, ...args) {
|
|
29
|
-
if (data
|
|
32
|
+
static async from(data, ...args) {
|
|
33
|
+
if (this.test(data) === 'LiveResponse') {
|
|
30
34
|
return data.clone(...args);
|
|
31
35
|
}
|
|
32
|
-
if (data
|
|
33
|
-
return this.fromResponse(data, ...args);
|
|
36
|
+
if (this.test(data) === 'Response') {
|
|
37
|
+
return await this.fromResponse(data, ...args);
|
|
34
38
|
}
|
|
35
|
-
if (
|
|
36
|
-
return this.fromGenerator(data, ...args);
|
|
39
|
+
if (this.test(data) === 'Generator') {
|
|
40
|
+
return await this.fromGenerator(data, ...args);
|
|
37
41
|
}
|
|
38
|
-
if (data
|
|
39
|
-
return this.
|
|
42
|
+
if (this.test(data) === 'LiveMode') {
|
|
43
|
+
return this.fromLiveMode(data, ...args);
|
|
40
44
|
}
|
|
41
45
|
return new this(data, ...args);
|
|
42
46
|
}
|
|
43
47
|
|
|
44
|
-
static fromResponse(response, options = {}) {
|
|
48
|
+
static async fromResponse(response, options = {}) {
|
|
45
49
|
if (!(response instanceof Response)) {
|
|
46
50
|
throw new Error('Argument must be a Response instance.');
|
|
47
51
|
}
|
|
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;
|
|
52
|
+
|
|
53
|
+
const body = await responseShim.prototype.parse.value.call(response);
|
|
54
|
+
|
|
55
|
+
// Instance
|
|
56
|
+
const instance = new this(body, {
|
|
57
|
+
status: responseShim.prototype.status.get.call(response),
|
|
58
|
+
statusText: response.statusText,
|
|
59
|
+
headers: response.headers,
|
|
60
|
+
done: false,
|
|
61
|
+
...options,
|
|
85
62
|
});
|
|
63
|
+
const responseMeta = _wq(response, 'meta');
|
|
64
|
+
_wq(instance).set('meta', responseMeta);
|
|
65
|
+
|
|
66
|
+
// Generator binding
|
|
67
|
+
if (this.hasBackground(response) === 2) {
|
|
68
|
+
const backgroundPort = this.getBackground(response);
|
|
69
|
+
if (_isTypeObject(body) && !isTypeStream(body)) {
|
|
70
|
+
applyMutations.call(backgroundPort,
|
|
71
|
+
body,
|
|
72
|
+
response.headers.get('X-Live-Response-Message-ID').trim(),
|
|
73
|
+
{ signal: instance.#abortController.signal }
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
// Capture subsequent frames?
|
|
77
|
+
backgroundPort.addEventListener('response.replace', (e) => {
|
|
78
|
+
const { body, ...options } = e.data;
|
|
79
|
+
instance.#replaceWith(body, options);
|
|
80
|
+
}, { signal: instance.#abortController.signal });
|
|
81
|
+
backgroundPort.addEventListener('close', () => {
|
|
82
|
+
instance.#extendLifecycle(Promise.resolve());
|
|
83
|
+
}, { once: true, signal: instance.#abortController.signal });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Data props
|
|
87
|
+
instance.#type = response.type;
|
|
88
|
+
instance.#redirected = response.redirected;
|
|
89
|
+
instance.#url = response.url;
|
|
90
|
+
// Lifecycle props
|
|
91
|
+
instance.#generator = response;
|
|
92
|
+
instance.#generatorType = 'Response';
|
|
93
|
+
|
|
94
|
+
return instance;
|
|
86
95
|
}
|
|
87
96
|
|
|
88
|
-
static fromGenerator(gen, options = {}) {
|
|
97
|
+
static async fromGenerator(gen, options = {}) {
|
|
89
98
|
if (!isGenerator(gen)) {
|
|
90
99
|
throw new Error('Argument must be a generator or async generator.');
|
|
91
100
|
}
|
|
92
|
-
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
101
|
+
|
|
102
|
+
const firstFrame = await gen.next();
|
|
103
|
+
const firstValue = await firstFrame.value;
|
|
104
|
+
|
|
105
|
+
if (firstValue instanceof Response && firstFrame.done && options.done !== false/* && value.responsesOK*/) {
|
|
106
|
+
return firstValue;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
let instance;
|
|
110
|
+
let frame = firstFrame;
|
|
111
|
+
let value = firstValue;
|
|
112
|
+
let $$await;
|
|
113
|
+
|
|
114
|
+
const $options = { done: firstFrame.done, ...options };
|
|
115
|
+
if (this.test(value) === 'LiveResponse') {
|
|
116
|
+
instance = new this;
|
|
117
|
+
const responseMeta = _wq(value, 'meta');
|
|
118
|
+
_wq(instance).set('meta', responseMeta);
|
|
119
|
+
$$await = instance.#replaceWith(value, $options);
|
|
120
|
+
} else {
|
|
121
|
+
instance = await this.from/*important*/(value, $options);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
(async function () {
|
|
125
|
+
await $$await;
|
|
126
|
+
instance.#generator = gen;
|
|
127
|
+
instance.#generatorType = 'Generator';
|
|
128
|
+
while (!frame.done && !options.done && !instance.#abortController.signal.aborted) {
|
|
129
|
+
frame = await gen.next();
|
|
130
|
+
value = await frame.value;
|
|
131
|
+
if (!instance.#abortController.signal.aborted) {
|
|
132
|
+
await instance.#replaceWith(value, { done: frame.done });
|
|
107
133
|
}
|
|
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
|
-
});
|
|
134
|
+
}
|
|
135
|
+
})();
|
|
136
|
+
|
|
125
137
|
return instance;
|
|
126
138
|
}
|
|
127
139
|
|
|
128
|
-
static
|
|
129
|
-
if (!(
|
|
130
|
-
throw new Error('Argument must be a
|
|
140
|
+
static async fromLiveMode(liveMode, options = {}) {
|
|
141
|
+
if (!this.test(liveMode) === 'LiveMode') {
|
|
142
|
+
throw new Error('Argument must be a UseLive LiveMode instance.');
|
|
143
|
+
}
|
|
144
|
+
const instance = new this;
|
|
145
|
+
await instance.replaceWith(liveMode.value, { done: false, ...options });
|
|
146
|
+
if (instance.#generatorType === 'Default') {
|
|
147
|
+
instance.#generator = liveMode;
|
|
148
|
+
instance.#generatorType = 'LiveMode';
|
|
131
149
|
}
|
|
132
|
-
const instance = new this(qState.value, { done: false, ...options });
|
|
133
|
-
instance.#generator = qState;
|
|
134
|
-
instance.#generatorType = 'Quantum';
|
|
135
150
|
Observer.observe(
|
|
136
|
-
|
|
151
|
+
liveMode,
|
|
137
152
|
'value',
|
|
138
153
|
(e) => instance.#replaceWith(e.value),
|
|
139
154
|
{ signal: instance.#abortController.signal }
|
|
@@ -141,6 +156,33 @@ export class LiveResponse extends EventTarget {
|
|
|
141
156
|
return instance;
|
|
142
157
|
}
|
|
143
158
|
|
|
159
|
+
static hasBackground(respone) {
|
|
160
|
+
let liveLevel = (respone.headers?.get?.('X-Background-Messaging-Port')?.trim() || _wq(respone, 'meta').has('background_port')) && 1 || 0;
|
|
161
|
+
liveLevel += respone.headers?.get?.('X-Live-Response-Message-ID')?.trim() && 1 || 0;
|
|
162
|
+
return liveLevel;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
static getBackground(respone) {
|
|
166
|
+
if (!/Response/.test(this.test(respone) )) return;
|
|
167
|
+
const responseMeta = _wq(respone, 'meta');
|
|
168
|
+
if (!responseMeta.has('background_port')) {
|
|
169
|
+
const value = respone.headers.get('X-Background-Messaging-Port')?.trim();
|
|
170
|
+
if (value) {
|
|
171
|
+
const [proto, portID] = value.split(':');
|
|
172
|
+
let backgroundPort;
|
|
173
|
+
if (proto === 'br') {
|
|
174
|
+
backgroundPort = new WQBroadcastChannel(portID);
|
|
175
|
+
} else if (proto === 'ws') {
|
|
176
|
+
backgroundPort = new WQSockPort(portID);
|
|
177
|
+
} else {
|
|
178
|
+
throw new Error(`Unknown background messaging protocol: ${proto}`);
|
|
179
|
+
}
|
|
180
|
+
responseMeta.set('background_port', backgroundPort);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return responseMeta.get('background_port');
|
|
184
|
+
}
|
|
185
|
+
|
|
144
186
|
#generator = null;
|
|
145
187
|
#generatorType = 'Default';
|
|
146
188
|
get generatorType() { return this.#generatorType; }
|
|
@@ -193,18 +235,10 @@ export class LiveResponse extends EventTarget {
|
|
|
193
235
|
|
|
194
236
|
/* Level 3 props */
|
|
195
237
|
|
|
196
|
-
get
|
|
197
|
-
return responseRealtime.call(this);
|
|
198
|
-
}
|
|
238
|
+
get background() { return this.constructor.getBackground(this); }
|
|
199
239
|
|
|
200
240
|
/* Lifecycle methods */
|
|
201
241
|
|
|
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
242
|
whileLive(returningThePromise = false) {
|
|
209
243
|
if (returningThePromise) {
|
|
210
244
|
return this.#lifeCycleResolutionPromise;
|
|
@@ -294,9 +328,10 @@ export class LiveResponse extends EventTarget {
|
|
|
294
328
|
const execReplaceWithResponse = async (response, options) => {
|
|
295
329
|
this.#generator = response;
|
|
296
330
|
this.#generatorType = response instanceof Response ? 'Response' : 'LiveResponse';
|
|
331
|
+
const body = response instanceof Response ? await responseShim.prototype.parse.value.call(response) : response.body;
|
|
297
332
|
execReplaceWith({
|
|
298
|
-
body
|
|
299
|
-
status:
|
|
333
|
+
body,
|
|
334
|
+
status: responseShim.prototype.status.get.call(response),
|
|
300
335
|
statusText: response.statusText,
|
|
301
336
|
headers: response.headers,
|
|
302
337
|
...options,
|
|
@@ -304,7 +339,7 @@ export class LiveResponse extends EventTarget {
|
|
|
304
339
|
redirected: response.redirected,
|
|
305
340
|
url: response.url,
|
|
306
341
|
});
|
|
307
|
-
if (response
|
|
342
|
+
if (this.test(response) === 'LiveResponse') {
|
|
308
343
|
response.addEventListener('replace', () => execReplaceWith(response), { signal: this.#abortController.signal });
|
|
309
344
|
return await response.whileLive(true);
|
|
310
345
|
}
|
|
@@ -332,7 +367,7 @@ export class LiveResponse extends EventTarget {
|
|
|
332
367
|
};
|
|
333
368
|
|
|
334
369
|
let donePromise;
|
|
335
|
-
if (
|
|
370
|
+
if (/Response/.test(body)) {
|
|
336
371
|
if (frameClosure) {
|
|
337
372
|
throw new Error('frameClosure unsupported for inputs of type response.');
|
|
338
373
|
}
|
|
@@ -362,21 +397,25 @@ export class LiveResponse extends EventTarget {
|
|
|
362
397
|
await this.#replaceWith(body, ...args);
|
|
363
398
|
}
|
|
364
399
|
|
|
365
|
-
toResponse({
|
|
366
|
-
const response =
|
|
400
|
+
toResponse({ client: clientPort, signal: abortSignal } = {}) {
|
|
401
|
+
const response = responseShim.from.value(this.body, {
|
|
367
402
|
status: this.status,
|
|
368
403
|
statusText: this.statusText,
|
|
369
404
|
headers: this.headers,
|
|
370
405
|
});
|
|
406
|
+
|
|
371
407
|
const responseMeta = _wq(this, 'meta');
|
|
372
408
|
_wq(response).set('meta', responseMeta);
|
|
373
|
-
|
|
409
|
+
|
|
410
|
+
if (clientPort && this.whileLive()) {
|
|
374
411
|
const liveResponseMessageID = Date.now().toString();
|
|
375
412
|
response.headers.set('X-Live-Response-Message-ID', liveResponseMessageID);
|
|
413
|
+
|
|
376
414
|
// Publish mutations
|
|
377
415
|
if (_isTypeObject(this.body) && !isTypeStream(this.body)) {
|
|
378
|
-
publishMutations.call(
|
|
416
|
+
publishMutations.call(clientPort, this.body, liveResponseMessageID, { signal: abortSignal/* stop observing mutations on body when we abort */ });
|
|
379
417
|
}
|
|
418
|
+
|
|
380
419
|
// Publish replacements?
|
|
381
420
|
const replaceHandler = () => {
|
|
382
421
|
const headers = Object.fromEntries([...this.headers.entries()]);
|
|
@@ -384,7 +423,7 @@ export class LiveResponse extends EventTarget {
|
|
|
384
423
|
delete headers['set-cookie'];
|
|
385
424
|
console.warn('Warning: The "set-cookie" header is not supported for security reasons and has been removed from the response.');
|
|
386
425
|
}
|
|
387
|
-
|
|
426
|
+
clientPort.postMessage({
|
|
388
427
|
body: this.body,
|
|
389
428
|
status: this.status,
|
|
390
429
|
statusText: this.statusText,
|
|
@@ -392,6 +431,7 @@ export class LiveResponse extends EventTarget {
|
|
|
392
431
|
done: !this.whileLive(),
|
|
393
432
|
}, { wqEventOptions: { type: 'response.replace', live: true/*gracefully ignored if not an object*/ }, observerOptions: { signal: abortSignal/* stop observing mutations on body when we abort */ } });
|
|
394
433
|
};
|
|
434
|
+
|
|
395
435
|
this.addEventListener('replace', replaceHandler, { signal: abortSignal/* stop listening when we abort */ });
|
|
396
436
|
}
|
|
397
437
|
return response;
|
|
@@ -406,8 +446,8 @@ export class LiveResponse extends EventTarget {
|
|
|
406
446
|
}));
|
|
407
447
|
}
|
|
408
448
|
|
|
409
|
-
|
|
410
|
-
const state = new
|
|
449
|
+
toLiveMode({ signal: abortSignal } = {}) {
|
|
450
|
+
const state = new LiveModeX;
|
|
411
451
|
const replaceHandler = () => Observer.defineProperty(state, 'value', { value: this.body, enumerable: true, configurable: true });
|
|
412
452
|
this.addEventListener('replace', replaceHandler, { signal: abortSignal });
|
|
413
453
|
replaceHandler();
|
|
@@ -429,9 +469,7 @@ export const isGenerator = (obj) => {
|
|
|
429
469
|
typeof obj?.return === 'function';
|
|
430
470
|
};
|
|
431
471
|
|
|
432
|
-
class
|
|
472
|
+
class LiveModeX extends LiveMode {
|
|
433
473
|
constructor() { }
|
|
434
|
-
|
|
474
|
+
abort() { }
|
|
435
475
|
}
|
|
436
|
-
|
|
437
|
-
globalThis.LiveResponse = LiveResponse;
|