@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.
Files changed (43) hide show
  1. package/package.json +5 -20
  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-livejs-transform.js +35 -0
  6. package/src/{runtime-pi/webflo-client/webflo-codegen.js → build-pi/index.js} +145 -141
  7. package/src/index.js +3 -1
  8. package/src/init-pi/index.js +6 -3
  9. package/src/init-pi/templates/pwa/package.json +2 -2
  10. package/src/init-pi/templates/web/package.json +2 -2
  11. package/src/runtime-pi/AppBootstrap.js +38 -0
  12. package/src/runtime-pi/WebfloRuntime.js +50 -47
  13. package/src/runtime-pi/apis.js +9 -0
  14. package/src/runtime-pi/index.js +2 -4
  15. package/src/runtime-pi/webflo-client/WebfloClient.js +31 -35
  16. package/src/runtime-pi/webflo-client/WebfloRootClient1.js +16 -14
  17. package/src/runtime-pi/webflo-client/WebfloSubClient.js +13 -13
  18. package/src/runtime-pi/webflo-client/bootstrap.js +37 -0
  19. package/src/runtime-pi/webflo-client/index.js +2 -8
  20. package/src/runtime-pi/webflo-client/webflo-devmode.js +3 -3
  21. package/src/runtime-pi/webflo-fetch/LiveResponse.js +127 -96
  22. package/src/runtime-pi/webflo-fetch/index.js +435 -5
  23. package/src/runtime-pi/webflo-routing/HttpCookies.js +1 -1
  24. package/src/runtime-pi/webflo-routing/HttpEvent.js +5 -6
  25. package/src/runtime-pi/webflo-routing/HttpUser.js +7 -7
  26. package/src/runtime-pi/webflo-server/ServerSideCookies.js +3 -1
  27. package/src/runtime-pi/webflo-server/ServerSideSession.js +2 -1
  28. package/src/runtime-pi/webflo-server/WebfloServer.js +98 -195
  29. package/src/runtime-pi/webflo-server/bootstrap.js +59 -0
  30. package/src/runtime-pi/webflo-server/index.js +2 -6
  31. package/src/runtime-pi/webflo-server/webflo-devmode.js +13 -24
  32. package/src/runtime-pi/webflo-worker/WebfloWorker.js +11 -15
  33. package/src/runtime-pi/webflo-worker/WorkerSideCookies.js +2 -1
  34. package/src/runtime-pi/webflo-worker/bootstrap.js +38 -0
  35. package/src/runtime-pi/webflo-worker/index.js +3 -7
  36. package/src/webflo-cli.js +1 -2
  37. package/src/runtime-pi/webflo-fetch/cookies.js +0 -10
  38. package/src/runtime-pi/webflo-fetch/fetch.js +0 -16
  39. package/src/runtime-pi/webflo-fetch/formdata.js +0 -54
  40. package/src/runtime-pi/webflo-fetch/headers.js +0 -151
  41. package/src/runtime-pi/webflo-fetch/message.js +0 -49
  42. package/src/runtime-pi/webflo-fetch/request.js +0 -62
  43. 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 { 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 */
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
- 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;
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
- 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);
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
- 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
- });
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 wqRealtime() {
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 response.parse() : response.body,
299
- status: response.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({ clientRequestRealtime, signal: abortSignal } = {}) {
366
- const response = Response.from(this.body, {
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
- if (clientRequestRealtime && this.whileLive()) {
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(clientRequestRealtime, this.body, liveResponseMessageID, { signal: abortSignal/* stop observing mutations on body when we abort */ });
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
- clientRequestRealtime.postMessage({
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;