@webqit/webflo 0.20.27 → 0.20.29

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 CHANGED
@@ -12,7 +12,7 @@
12
12
  "vanila-javascript"
13
13
  ],
14
14
  "homepage": "https://webqit.io/tooling/webflo",
15
- "version": "0.20.27",
15
+ "version": "0.20.29",
16
16
  "license": "MIT",
17
17
  "repository": {
18
18
  "type": "git",
@@ -41,7 +41,7 @@
41
41
  "dependencies": {
42
42
  "@octokit/webhooks": "^7.15.1",
43
43
  "@webqit/backpack": "^0.1.12",
44
- "@webqit/fetch-plus": "^0.1.20",
44
+ "@webqit/fetch-plus": "^0.1.23",
45
45
  "@webqit/keyval": "^0.2.17",
46
46
  "@webqit/observer": "^3.8.14",
47
47
  "@webqit/oohtml-ssr": "^2.2.2",
@@ -155,7 +155,7 @@ export class AppRuntime {
155
155
  if (!this.isClientSide
156
156
  && response instanceof LiveResponse) {
157
157
  // Must convert to Response on the server-side before returning
158
- const outgoingResponse = response.toResponse({ port: httpEvent.client });
158
+ const outgoingResponse = response.toResponse({ port: httpEvent.client, signal: httpEvent.signal });
159
159
  return outgoingResponse;
160
160
  }
161
161
 
@@ -168,11 +168,11 @@ export class AppRuntime {
168
168
  && response.headers.get('Location')) return;
169
169
 
170
170
  const status = await httpEvent.thread.consume('status', true);
171
- if (!status.length) return;
171
+ if (!status) return;
172
172
 
173
173
  httpEvent.waitUntil(httpEvent.client.readyStateChange('open').then(async () => {
174
- await new Promise((r) => setTimeout(r, 100));
175
- httpEvent.client.postMessage(status, { type: 'alert' });
174
+ await new Promise((r) => setTimeout(r, 500));
175
+ httpEvent.client.postMessage(status, { type: 'status' });
176
176
  await new Promise((r) => setTimeout(r, 100));
177
177
  }));
178
178
  }
@@ -1,6 +1,8 @@
1
1
  import { _before, _toTitle } from '@webqit/util/str/index.js';
2
- import { LiveResponse, RequestPlus } from '@webqit/fetch-plus';
2
+ import { Observer } from '@webqit/observer';
3
+ import { URLPlus } from '@webqit/url-plus';
3
4
  import { StarPort } from '@webqit/port-plus';
5
+ import { LiveResponse, RequestPlus } from '@webqit/fetch-plus';
4
6
  import { HttpThread111 } from '../webflo-routing/HttpThread111.js';
5
7
  import { HttpCookies101 } from '../webflo-routing/HttpCookies101.js';
6
8
  import { HttpCookies110 } from '../webflo-routing/HttpCookies110.js';
@@ -11,8 +13,6 @@ import { WebfloRouter111 } from '../webflo-routing/WebfloRouter111.js';
11
13
  import { KeyvalsFactory110 } from '../webflo-routing/KeyvalsFactory110.js';
12
14
  import { ClientRequestPort100 } from '../webflo-messaging/ClientRequestPort100.js';
13
15
  import { AppRuntime } from '../AppRuntime.js';
14
- import { Observer } from '@webqit/observer';
15
- import { URLPlus } from '@webqit/url-plus';
16
16
  import { _meta } from '../../util.js';
17
17
 
18
18
  export class WebfloClient extends AppRuntime {
@@ -69,29 +69,52 @@ export class WebfloClient extends AppRuntime {
69
69
 
70
70
  // ----------
71
71
  // Bind prompt handlers
72
- const promptsHandler = (e) => {
72
+ const dialogHandler = (e) => {
73
73
  window.queueMicrotask(() => {
74
74
  if (e.defaultPrevented) return;
75
+
75
76
  if (e.type === 'confirm') {
76
- if (e.data?.message) {
77
- e.respondWith(confirm(e.data.message));
78
- }
77
+ const dialogElement = document.createElement('wq-confirm');
78
+ dialogElement.toggleAttribute('wq-default', true);
79
+
80
+ dialogElement.render(e.data);
81
+ document.body.append(dialogElement);
82
+ dialogElement.showPopover();
83
+
84
+ dialogElement.addEventListener('response', (r) => {
85
+ e.respondWith(r.data);
86
+ setTimeout(() => dialogElement.remove(), 300);
87
+ }, { once: true });
79
88
  } else if (e.type === 'prompt') {
80
- if (e.data?.message) {
81
- e.respondWith(prompt(e.data.message));
82
- }
83
- } else if (e.type === 'alert') {
84
- for (const item of [].concat(e.data)) {
85
- if (item?.message) {
86
- alert(item.message);
87
- }
88
- }
89
+ const dialogElement = document.createElement('wq-prompt');
90
+ dialogElement.toggleAttribute('wq-default', true);
91
+
92
+ dialogElement.render(e.data);
93
+ document.body.append(dialogElement);
94
+ dialogElement.showPopover();
95
+
96
+ dialogElement.addEventListener('response', (r) => {
97
+ e.respondWith(r.data);
98
+ setTimeout(() => dialogElement.remove(), 300);
99
+ }, { once: true });
100
+ } else if (e.type === 'status') {
101
+ const dialogElement = document.createElement('wq-toast');
102
+ dialogElement.toggleAttribute('wq-default', true);
103
+
104
+ dialogElement.render(e.data);
105
+ document.body.append(dialogElement);
106
+ dialogElement.showPopover();
107
+
108
+ dialogElement.addEventListener('toggle', (t) => {
109
+ if (t.newState !== 'closed') return;
110
+ dialogElement.remove();
111
+ }, { once: true });
89
112
  }
90
113
  });
91
114
  };
92
- this.background.addEventListener('confirm', promptsHandler, { signal: instanceController.signal });
93
- this.background.addEventListener('prompt', promptsHandler, { signal: instanceController.signal });
94
- this.background.addEventListener('alert', promptsHandler, { signal: instanceController.signal });
115
+ this.background.addEventListener('confirm', dialogHandler, { signal: instanceController.signal });
116
+ this.background.addEventListener('prompt', dialogHandler, { signal: instanceController.signal });
117
+ this.background.addEventListener('status', dialogHandler, { signal: instanceController.signal });
95
118
 
96
119
  // ----------
97
120
  // Call default-init
@@ -237,8 +260,6 @@ export class WebfloClient extends AppRuntime {
237
260
 
238
261
  _canIntercept(e) { return !(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey); }
239
262
 
240
- #xRedirectCode = 200;
241
-
242
263
  isHashChange(urlObj) { return _before(this.location.href, '#') === _before(urlObj.href, '#') && (this.location.href.includes('#') || urlObj.href.includes('#')); }
243
264
 
244
265
  isSpaRoute(urlObj) {
@@ -269,7 +290,6 @@ export class WebfloClient extends AppRuntime {
269
290
  const request = super.createRequest(href, init);
270
291
  request.headers.set('Accept', 'application/json');
271
292
  request.headers.set('X-Redirect-Policy', 'manual-when-cross-spa');
272
- request.headers.set('X-Redirect-Code', this.#xRedirectCode);
273
293
  request.headers.set('X-Powered-By', '@webqit/webflo');
274
294
  return request;
275
295
  }
@@ -308,7 +328,7 @@ export class WebfloClient extends AppRuntime {
308
328
  if (typeof cookieStore === 'undefined') {
309
329
  const entries = document.cookie.split(';').map((c) => c.split('=').map((s) => s.trim()));
310
330
  const store = this.#keyvals.create({ type: 'inmemory', path: ['cookies', scopeObj.tenantID], origins });
311
- entries.forEach(([key, value]) => store.set(key, { value }));
331
+ entries.forEach(([key, value]) => store.set({ key, value }));
312
332
  const initial = Object.fromEntries(entries);
313
333
  scopeObj.cookies = HttpCookies101.create({
314
334
  context: { handlersRegistry: this.#keyvals.getHandlers('cookies', true) },
@@ -443,23 +463,28 @@ export class WebfloClient extends AppRuntime {
443
463
  let response = await super.dispatchNavigationEvent({ httpEvent, crossLayerFetch, clientPortB });
444
464
 
445
465
  // Extract interactive. mode handling
446
- const handleInteractiveMode = () => {
466
+ const handleInteractiveMode = (returnImmediate = false) => {
447
467
  return new Promise(async (resolve) => {
448
468
  // Must come as first thing
449
469
  const backgroundPort = LiveResponse.getPort(response);
450
470
  this.background.addPort(backgroundPort);
451
-
471
+
452
472
  const liveResponse = response instanceof LiveResponse
453
473
  ? response
454
474
  : LiveResponse.from(response);
455
475
 
456
- liveResponse.addEventListener('replace', (e) => {
476
+ await liveResponse.readyStateChange('live');
477
+ // IMPORTANT: ensures we're listening to subsequent changes.
478
+
479
+ liveResponse.addEventListener('replace', () => {
457
480
  if (liveResponse.headers.get('Location')) {
458
481
  this.processRedirect(liveResponse);
459
482
  } else {
460
483
  resolve(liveResponse);
461
484
  }
462
485
  }, { signal: httpEvent.signal });
486
+
487
+ if (returnImmediate) resolve(liveResponse);
463
488
  });
464
489
  };
465
490
 
@@ -498,7 +523,7 @@ export class WebfloClient extends AppRuntime {
498
523
 
499
524
  // Obtain and connect clientPortB as first thing
500
525
  if (LiveResponse.hasPort(response)) {
501
- response = await handleInteractiveMode();
526
+ response = await handleInteractiveMode(true);
502
527
  }
503
528
  }
504
529
 
@@ -507,14 +532,9 @@ export class WebfloClient extends AppRuntime {
507
532
 
508
533
  async processRedirect(response) {
509
534
  // Normalize redirect
510
- let statusCode = response.status;
511
- const xActualRedirectCode = parseInt(response.headers.get('X-Redirect-Code'));
512
-
513
- if (xActualRedirectCode && statusCode === this.#xRedirectCode) {
514
- const responseMeta = _meta(response);
515
- responseMeta.set('status', xActualRedirectCode); // @NOTE 1
516
- statusCode = xActualRedirectCode;
517
- }
535
+ const statusCode = response.headers.has('X-Redirect-Code')
536
+ ? parseInt(response.headers.get('X-Redirect-Code'))
537
+ : response.status;
518
538
 
519
539
  // Trigger redirect
520
540
  if ([302, 301].includes(statusCode)) {
@@ -100,9 +100,9 @@ export class WebfloRootClientA extends WebfloClient {
100
100
  scopeObj.response.headers.set(name, metaElement.content?.trim() || '');
101
101
  }
102
102
 
103
- if (scopeObj.response.port) {
104
- this.background.addPort(scopeObj.response.port);
105
- }
103
+ // Must come as first thing
104
+ const backgroundPort = LiveResponse.getPort(scopeObj.response);
105
+ if (backgroundPort) this.background.addPort(backgroundPort);
106
106
 
107
107
  if (scopeObj.response.body || scopeObj.response.port) {
108
108
  const httpEvent = HttpEvent111.create({ request: this.createRequest(this.location.href) }, true);
@@ -1,15 +1,10 @@
1
1
  import { Observer } from '@webqit/observer';
2
2
  import { LiveResponse } from '@webqit/fetch-plus';
3
3
  import { WebfloClient } from './WebfloClient.js';
4
- import { defineElement } from './webflo-embedded.js';
5
4
  import { _meta } from '../../util.js';
6
5
 
7
6
  export class WebfloSubClient extends WebfloClient {
8
7
 
9
- static defineElement() {
10
- defineElement(this);
11
- }
12
-
13
8
  static create(superRuntime, host) {
14
9
  return new this(superRuntime, host);
15
10
  }
@@ -67,7 +62,7 @@ export class WebfloSubClient extends WebfloClient {
67
62
  const locationCallback = (newHref) => {
68
63
  this.host.reflectLocation(newHref);
69
64
  };
70
-
65
+
71
66
  return super.controlClassic/*IMPORTANT*/(locationCallback);
72
67
  }
73
68
 
@@ -99,33 +94,35 @@ export class WebfloSubClient extends WebfloClient {
99
94
 
100
95
  async redirect(location, response = null) {
101
96
  location = typeof location === 'string' ? new URL(location, this.location.origin) : location;
102
-
97
+
103
98
  const width = Math.min(800, window.innerWidth);
104
99
  const height = Math.min(600, window.innerHeight);
105
100
  const left = (window.outerWidth - width) / 2;
106
101
  const top = (window.outerHeight - height) / 2;
107
102
  const popup = window.open(location, '_blank', `popup=true,width=${width},height=${height},left=${left},top=${top}`);
108
-
109
- const backgroundPort = response instanceof LiveResponse
103
+
104
+ const backgroundPort = response instanceof LiveResponse
110
105
  ? response.port
111
106
  : LiveResponse.getPort(response);
107
+
112
108
  if (backgroundPort) {
113
109
  Observer.set(this.navigator, 'redirecting', new URL(location), { diff: true });
114
-
115
- backgroundPort.readyStateChange('close').then((e) => {
116
- window.removeEventListener('message', windowMessageHandler);
117
-
110
+ backgroundPort.readyStateChange('close').then(() => {
118
111
  Observer.set(this.navigator, 'redirecting', null);
119
- popup.postMessage('timeout:5');
120
-
121
- setTimeout(() => {
122
- popup.close();
123
- }, 5000);
124
112
  });
125
-
113
+
126
114
  const windowMessageHandler = (e) => {
127
- if (e.source === popup && e.data === 'close') {
128
- backgroundPort.close();
115
+ if (e.source === popup) {
116
+ window.removeEventListener('message', windowMessageHandler);
117
+ if (e.data === 'canclose') {
118
+ popup.postMessage('timeout:5');
119
+ setTimeout(() => {
120
+ popup.close();
121
+ }, 5000);
122
+ }
123
+ if (e.data === 'closed') {
124
+ backgroundPort.close();
125
+ }
129
126
  }
130
127
  };
131
128
 
@@ -1,11 +1,15 @@
1
1
  import { WebfloRootClientA } from './WebfloRootClientA.js';
2
2
  import { WebfloRootClientB } from './WebfloRootClientB.js';
3
- import { WebfloSubClient } from './WebfloSubClient.js';
3
+ import { defineElement } from './webflo-embedded.js';
4
+ import { defineElements } from './webflo-elements.js';
4
5
 
5
6
  export async function start(bootstrap) {
6
7
  const WebfloRootClient = window.navigation ? WebfloRootClientB : WebfloRootClientA;
7
8
  const instance = WebfloRootClient.create(bootstrap, document);
8
9
  await instance.initialize();
9
- WebfloSubClient.defineElement();
10
+
11
+ defineElement();
12
+ defineElements();
13
+
10
14
  return instance;
11
15
  }