@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.
Files changed (56) hide show
  1. package/package.json +13 -34
  2. package/site/docs/concepts/realtime.md +45 -44
  3. package/site/docs/getting-started.md +40 -40
  4. package/src/{Context.js → CLIContext.js} +9 -8
  5. package/src/build-pi/esbuild-plugin-uselive-transform.js +42 -0
  6. package/src/{runtime-pi/webflo-client/webflo-codegen.js → build-pi/index.js} +148 -142
  7. package/src/index.js +3 -1
  8. package/src/init-pi/index.js +7 -4
  9. package/src/init-pi/templates/pwa/.gitignore +6 -0
  10. package/src/init-pi/templates/pwa/.webqit/webflo/client.json +15 -0
  11. package/src/init-pi/templates/pwa/.webqit/webflo/layout.json +7 -0
  12. package/src/init-pi/templates/pwa/package.json +2 -2
  13. package/src/init-pi/templates/pwa/public/manifest.json +2 -2
  14. package/src/init-pi/templates/web/.gitignore +6 -0
  15. package/src/init-pi/templates/web/.webqit/webflo/client.json +12 -0
  16. package/src/init-pi/templates/web/.webqit/webflo/layout.json +7 -0
  17. package/src/init-pi/templates/web/package.json +2 -2
  18. package/src/runtime-pi/AppBootstrap.js +38 -0
  19. package/src/runtime-pi/WebfloRuntime.js +68 -56
  20. package/src/runtime-pi/apis.js +9 -0
  21. package/src/runtime-pi/index.js +2 -4
  22. package/src/runtime-pi/webflo-client/DeviceCapabilities.js +1 -1
  23. package/src/runtime-pi/webflo-client/WebfloClient.js +33 -36
  24. package/src/runtime-pi/webflo-client/WebfloRootClient1.js +23 -17
  25. package/src/runtime-pi/webflo-client/WebfloRootClient2.js +1 -1
  26. package/src/runtime-pi/webflo-client/WebfloSubClient.js +14 -14
  27. package/src/runtime-pi/webflo-client/bootstrap.js +38 -0
  28. package/src/runtime-pi/webflo-client/index.js +2 -8
  29. package/src/runtime-pi/webflo-client/webflo-devmode.js +3 -3
  30. package/src/runtime-pi/webflo-fetch/LiveResponse.js +154 -116
  31. package/src/runtime-pi/webflo-fetch/index.js +436 -5
  32. package/src/runtime-pi/webflo-messaging/wq-message-port.js +1 -1
  33. package/src/runtime-pi/webflo-routing/HttpCookies.js +1 -1
  34. package/src/runtime-pi/webflo-routing/HttpEvent.js +12 -11
  35. package/src/runtime-pi/webflo-routing/HttpUser.js +7 -7
  36. package/src/runtime-pi/webflo-routing/WebfloRouter.js +12 -7
  37. package/src/runtime-pi/webflo-server/ServerSideCookies.js +3 -1
  38. package/src/runtime-pi/webflo-server/ServerSideSession.js +2 -1
  39. package/src/runtime-pi/webflo-server/WebfloServer.js +138 -200
  40. package/src/runtime-pi/webflo-server/bootstrap.js +59 -0
  41. package/src/runtime-pi/webflo-server/index.js +2 -6
  42. package/src/runtime-pi/webflo-server/webflo-devmode.js +24 -31
  43. package/src/runtime-pi/webflo-url/Url.js +1 -1
  44. package/src/runtime-pi/webflo-url/xURL.js +1 -1
  45. package/src/runtime-pi/webflo-worker/WebfloWorker.js +11 -15
  46. package/src/runtime-pi/webflo-worker/WorkerSideCookies.js +2 -1
  47. package/src/runtime-pi/webflo-worker/bootstrap.js +39 -0
  48. package/src/runtime-pi/webflo-worker/index.js +3 -7
  49. package/src/webflo-cli.js +1 -2
  50. package/src/runtime-pi/webflo-fetch/cookies.js +0 -10
  51. package/src/runtime-pi/webflo-fetch/fetch.js +0 -16
  52. package/src/runtime-pi/webflo-fetch/formdata.js +0 -54
  53. package/src/runtime-pi/webflo-fetch/headers.js +0 -151
  54. package/src/runtime-pi/webflo-fetch/message.js +0 -49
  55. package/src/runtime-pi/webflo-fetch/request.js +0 -62
  56. 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(this || {}, document);
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] = `/@dev?src=${event.affectedHandler}&t=${Date.now()}`;
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 = `/@dev?src=${url}&t=${Date.now()}`;
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 = `/@dev?src=${url}?t=${Date.now()}`;
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 { State, Observer } from '@webqit/quantum-js';
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 { responseRealtime } from './response.js';
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 */
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 State) {
23
- return 'Quantum';
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 instanceof LiveResponse) {
32
+ static async from(data, ...args) {
33
+ if (this.test(data) === 'LiveResponse') {
30
34
  return data.clone(...args);
31
35
  }
32
- if (data instanceof Response) {
33
- return this.fromResponse(data, ...args);
36
+ if (this.test(data) === 'Response') {
37
+ return await this.fromResponse(data, ...args);
34
38
  }
35
- if (isGenerator(data)) {
36
- return this.fromGenerator(data, ...args);
39
+ if (this.test(data) === 'Generator') {
40
+ return await this.fromGenerator(data, ...args);
37
41
  }
38
- if (data instanceof State) {
39
- return this.fromQuantum(data, ...args);
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
- return response.parse().then((body) => {
49
- // Instance
50
- const instance = new this(body, {
51
- status: response.status,
52
- statusText: response.statusText,
53
- headers: response.headers,
54
- done: false,
55
- ...options,
56
- });
57
- const responseMeta = _wq(response, 'meta');
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
- const $firstFrame = gen.next();
93
- const instance = _await($firstFrame, (frame) => {
94
- return _await(frame.value, (value) => {
95
- const $options = { done: frame.done, ...options };
96
- let instance, $$await;
97
- if (value instanceof Response && frame.done && options.done !== false/* && value.responsesOK*/) {
98
- return value;
99
- }
100
- if (value instanceof LiveResponse) {
101
- instance = new this;
102
- const responseMeta = _wq(value, 'meta');
103
- _wq(instance).set('meta', responseMeta);
104
- $$await = instance.#replaceWith(value, $options);
105
- } else {
106
- instance = this.from/*important*/(value, $options);
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
- return _await(instance, (instance) => {
109
- (async function () {
110
- await $$await;
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 fromQuantum(qState, options = {}) {
129
- if (!(qState instanceof State)) {
130
- throw new Error('Argument must be a Quantum State instance.');
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
- qState,
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 wqRealtime() {
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: response instanceof Response ? await response.parse() : response.body,
299
- status: response.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 instanceof LiveResponse) {
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 (body instanceof Response || body instanceof LiveResponse) {
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({ clientRequestRealtime, signal: abortSignal } = {}) {
366
- const response = Response.from(this.body, {
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
- if (clientRequestRealtime && this.whileLive()) {
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(clientRequestRealtime, this.body, liveResponseMessageID, { signal: abortSignal/* stop observing mutations on body when we abort */ });
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
- clientRequestRealtime.postMessage({
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
- toQuantum({ signal: abortSignal } = {}) {
410
- const state = new StateX;
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 StateX extends State {
472
+ class LiveModeX extends LiveMode {
433
473
  constructor() { }
434
- dispose() { }
474
+ abort() { }
435
475
  }
436
-
437
- globalThis.LiveResponse = LiveResponse;