@webqit/webflo 0.11.61 → 1.0.0

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 (66) hide show
  1. package/package.json +1 -1
  2. package/src/{Context.js → AbstractContext.js} +1 -9
  3. package/src/deployment-pi/origins/index.js +1 -1
  4. package/src/index.js +1 -9
  5. package/src/runtime-pi/HttpEvent.js +101 -81
  6. package/src/runtime-pi/HttpUser.js +126 -0
  7. package/src/runtime-pi/MessagingOverBroadcast.js +9 -0
  8. package/src/runtime-pi/MessagingOverChannel.js +85 -0
  9. package/src/runtime-pi/MessagingOverSocket.js +106 -0
  10. package/src/runtime-pi/MultiportMessagingAPI.js +81 -0
  11. package/src/runtime-pi/WebfloCookieStorage.js +27 -0
  12. package/src/runtime-pi/WebfloEventTarget.js +39 -0
  13. package/src/runtime-pi/WebfloMessageEvent.js +58 -0
  14. package/src/runtime-pi/WebfloMessagingAPI.js +69 -0
  15. package/src/runtime-pi/{Router.js → WebfloRouter.js} +3 -34
  16. package/src/runtime-pi/WebfloRuntime.js +52 -0
  17. package/src/runtime-pi/WebfloStorage.js +109 -0
  18. package/src/runtime-pi/client/ClientMessaging.js +5 -0
  19. package/src/runtime-pi/client/Context.js +2 -6
  20. package/src/runtime-pi/client/CookieStorage.js +17 -0
  21. package/src/runtime-pi/client/Router.js +3 -13
  22. package/src/runtime-pi/client/SessionStorage.js +33 -0
  23. package/src/runtime-pi/client/Url.js +24 -72
  24. package/src/runtime-pi/client/WebfloClient.js +544 -0
  25. package/src/runtime-pi/client/WebfloRootClient1.js +179 -0
  26. package/src/runtime-pi/client/WebfloRootClient2.js +109 -0
  27. package/src/runtime-pi/client/WebfloSubClient.js +165 -0
  28. package/src/runtime-pi/client/Workport.js +89 -161
  29. package/src/runtime-pi/client/generate.js +1 -1
  30. package/src/runtime-pi/client/index.js +13 -18
  31. package/src/runtime-pi/client/worker/ClientMessaging.js +5 -0
  32. package/src/runtime-pi/client/worker/Context.js +2 -6
  33. package/src/runtime-pi/client/worker/CookieStorage.js +17 -0
  34. package/src/runtime-pi/client/worker/SessionStorage.js +13 -0
  35. package/src/runtime-pi/client/worker/WebfloWorker.js +294 -0
  36. package/src/runtime-pi/client/worker/Workport.js +13 -73
  37. package/src/runtime-pi/client/worker/index.js +7 -18
  38. package/src/runtime-pi/index.js +1 -8
  39. package/src/runtime-pi/server/ClientMessaging.js +18 -0
  40. package/src/runtime-pi/server/ClientMessagingRegistry.js +57 -0
  41. package/src/runtime-pi/server/Context.js +2 -6
  42. package/src/runtime-pi/server/CookieStorage.js +17 -0
  43. package/src/runtime-pi/server/Router.js +2 -68
  44. package/src/runtime-pi/server/SessionStorage.js +53 -0
  45. package/src/runtime-pi/server/WebfloServer.js +755 -0
  46. package/src/runtime-pi/server/index.js +7 -18
  47. package/src/runtime-pi/util-http.js +268 -32
  48. package/src/runtime-pi/xURL.js +25 -22
  49. package/src/runtime-pi/xfetch.js +2 -2
  50. package/src/runtime-pi/Application.js +0 -29
  51. package/src/runtime-pi/Cookies.js +0 -82
  52. package/src/runtime-pi/Runtime.js +0 -21
  53. package/src/runtime-pi/client/Application.js +0 -76
  54. package/src/runtime-pi/client/Runtime.js +0 -525
  55. package/src/runtime-pi/client/createStorage.js +0 -58
  56. package/src/runtime-pi/client/worker/Application.js +0 -44
  57. package/src/runtime-pi/client/worker/Runtime.js +0 -275
  58. package/src/runtime-pi/server/Application.js +0 -101
  59. package/src/runtime-pi/server/Runtime.js +0 -558
  60. package/src/runtime-pi/xFormData.js +0 -24
  61. package/src/runtime-pi/xHeaders.js +0 -146
  62. package/src/runtime-pi/xRequest.js +0 -46
  63. package/src/runtime-pi/xRequestHeaders.js +0 -109
  64. package/src/runtime-pi/xResponse.js +0 -33
  65. package/src/runtime-pi/xResponseHeaders.js +0 -117
  66. package/src/runtime-pi/xxHttpMessage.js +0 -102
@@ -0,0 +1,179 @@
1
+ import { WebfloClient } from './WebfloClient.js';
2
+ import { Context } from './Context.js';
3
+
4
+ const { Observer } = webqit;
5
+
6
+ export class WebfloRootClient1 extends WebfloClient {
7
+
8
+ static get Context() { return Context; }
9
+
10
+ static create(host, cx = {}) {
11
+ return new this(host, this.Context.create(cx));
12
+ }
13
+
14
+ #cx;
15
+ get cx() { return this.#cx; }
16
+
17
+ constructor(host, cx) {
18
+ if (!(host instanceof Document)) {
19
+ throw new Error('Argument #1 must be a Document instance');
20
+ }
21
+ super(host);
22
+ if (!(cx instanceof this.constructor.Context)) {
23
+ throw new Error('Argument #2 must be a Webflo Context instance');
24
+ }
25
+ this.#cx = cx;
26
+ }
27
+
28
+ initialize() {
29
+ // Main initializations
30
+ let undoControl = super.initialize();
31
+ // Bind global prompt handlers
32
+ const promptsHandler = (e) => {
33
+ e.stopPropagation();
34
+ setTimeout(() => {
35
+ if (e.defaultPrevented || e.immediatePropagationStopped) return;
36
+ let message = e.data;
37
+ if (e.data?.message) {
38
+ message = e.data.message + (e.data.details ? `\r\n${e.data.details}` : '');
39
+ }
40
+ window.queueMicrotask(() => {
41
+ if (e.type === 'confirm') {
42
+ e.respondWith(confirm(message));
43
+ } else if (e.type === 'prompt') {
44
+ e.respondWith(prompt(message));
45
+ }
46
+ });
47
+ }, 10);
48
+ };
49
+ this.backgroundMessaging.handleMessages('confirm', promptsHandler);
50
+ this.backgroundMessaging.handleMessages('prompt', promptsHandler);
51
+ // Respond to background activity request at pageload
52
+ const scope = {};
53
+ if (scope.backgroundMessagingMeta = document.querySelector('meta[name="X-Background-Messaging"]')) {
54
+ scope.backgroundMessaging = this.$createBackgroundMessagingFrom(scope.backgroundMessagingMeta.content);
55
+ this.backgroundMessaging.add(scope.backgroundMessaging);
56
+ }
57
+ if (scope.hydrationData = document.querySelector('script[rel="hydration"][type="application/json"]')) {
58
+ try {
59
+ const hydrationDataJson = JSON.parse((scope.hydrationData.textContent + '').trim());
60
+ const httpEvent = this.constructor.HttpEvent.create(null, { url: this.location.href});
61
+ window.queueMicrotask(() => {
62
+ this.render(httpEvent, hydrationDataJson);
63
+ });
64
+ } catch(e) {}
65
+ }
66
+ // Service Worker && COMM
67
+ if (this.cx.params.service_worker?.filename) {
68
+ const { public_base_url: base, service_worker: { filename, ...restServiceWorkerParams } } = this.cx.params;
69
+ const { vapid_key_env, push_registration_url_env, ..._restServiceWorkerParams } = restServiceWorkerParams;
70
+ const swParams = {
71
+ ..._restServiceWorkerParams,
72
+ VAPID_PUBLIC_KEY: this.cx.params.env[vapid_key_env],
73
+ PUSH_REGISTRATION_PUBLIC_URL: this.cx.params.env[push_registration_url_env],
74
+ startMessages: true
75
+ };
76
+ //this.workport.registerServiceWorker(base + filename, swParams);
77
+ }
78
+ if (window.opener) {
79
+ // Window opener pinging
80
+ const $undoControl = undoControl;
81
+ const beforeunloadHandler = () => {
82
+ window.opener.postMessage('close');
83
+ };
84
+ window.addEventListener('beforeunload', beforeunloadHandler);
85
+ undoControl = () => {
86
+ window.removeEventListener('beforeunload', beforeunloadHandler);
87
+ $undoControl();
88
+ };
89
+ }
90
+ return undoControl
91
+ }
92
+
93
+ /**
94
+ * The following methods
95
+ * are not to be inherited
96
+ * by sub classes
97
+ */
98
+
99
+ control() {
100
+ // IMPORTANT: we're calling super.controlClassic()
101
+ const undoControl = super.controlClassic((newHref) => {
102
+ try {
103
+ // Save current scroll position
104
+ this.host.history.replaceState({
105
+ ...(this.currentEntry()?.getState?.() || {}),
106
+ scrollPosition: this.host === window.document ? [window.scrollX, window.scrollY] : [this.host.scrollLeft, this.host.scrollTop,],
107
+ }, '', this.location.href);
108
+ } catch (e) { }
109
+ // Do actual location update
110
+ try { this.host.history.pushState({}, '', newHref); } catch (e) { }
111
+ });
112
+ // ONPOPSTATE
113
+ const popstateHandler = (e) => {
114
+ if (this.isHashChange(location)) {
115
+ Observer.set(this.location, 'href', location.href);
116
+ return;
117
+ }
118
+ // Navigation details
119
+ const detail = {
120
+ navigationType: 'traverse',
121
+ navigationOrigins: [],
122
+ destination: this._asEntry(e.state),
123
+ source: this.currentEntry(),
124
+ userInitiated: true,
125
+ };
126
+ // Traversal?
127
+ // Push
128
+ this.navigate(location.href, {}, detail);
129
+ };
130
+ window.addEventListener('popstate', popstateHandler);
131
+ return () => {
132
+ this.host.removeEventListener('popstate', popstateHandler);
133
+ undoControl();
134
+ };
135
+ }
136
+
137
+ reload() {
138
+ return window.history.reload();
139
+ }
140
+
141
+ back() {
142
+ return window.history.back();
143
+ }
144
+
145
+ forward() {
146
+ return window.history.forward();
147
+ }
148
+
149
+ traverseTo(...args) {
150
+ return window.history.go(...args);
151
+ }
152
+
153
+ entries() {
154
+ return window.history;
155
+ }
156
+
157
+ currentEntry() {
158
+ return this._asEntry(history.state);
159
+ }
160
+
161
+ async updateCurrentEntry(params, url = null) {
162
+ window.history.replaceState(params.state, '', url);
163
+ }
164
+
165
+ async push(url, state = {}) {
166
+ if (typeof url === 'string' && url.startsWith('&')) { url = this.location.href.split('#')[0] + (this.location.href.includes('?') ? url : url.replace('&', '?')); }
167
+ url = new URL(url, this.location.href);
168
+ window.history.pushState(state, '', url.href);
169
+ Observer.set(this.location, 'href', url.href);
170
+ }
171
+
172
+ async applyPostRenderState(httpEvent) {
173
+ const destinationState = httpEvent.detail.destination?.getState() || {};
174
+ if (destinationState.scrollPosition?.length) {
175
+ window.scroll(...destinationState.scrollPosition);
176
+ (document.querySelector('[autofocus]') || document.body).focus();
177
+ }
178
+ }
179
+ }
@@ -0,0 +1,109 @@
1
+ import { WebfloRootClient1 } from './WebfloRootClient1.js';
2
+
3
+ const { Observer } = webqit;
4
+
5
+ export class WebfloRootClient2 extends WebfloRootClient1 {
6
+
7
+ control() {
8
+ // Detect source elements
9
+ let navigationOrigins = [];
10
+ // Capture all link-clicks
11
+ const clickHandler = (e) => {
12
+ if (!this._canIntercept(e)) return;
13
+ let anchorEl = e.target.closest('a');
14
+ if (!anchorEl || !anchorEl.href || anchorEl.target) return;
15
+ navigationOrigins = [anchorEl, null, anchorEl.closest('[navigationcontext]')];
16
+ };
17
+ // Capture all form-submits
18
+ const submitHandler = (e) => {
19
+ if (!this._canIntercept(e)) return;
20
+ navigationOrigins = [e.submitter, e.target.closest('form'), e.target.closest('[navigationcontext]')];
21
+ };
22
+ // Handle navigation event which happens after the above
23
+ const navigateHandler = (e) => {
24
+ if (!e.canIntercept || e.downloadRequest !== null) return;
25
+ if (e.hashChange) {
26
+ Observer.set(this.location, 'href', e.destination.url);
27
+ return;
28
+ }
29
+ const { navigationType, destination, signal, formData, info, userInitiated } = e;
30
+ if (formData && navigationOrigins[1]?.hasAttribute('webflo-no-intercept')) return;
31
+ if (formData && (navigationOrigins[0] || {}).name) { formData.set(navigationOrigins[0].name, navigationOrigins[0].value); }
32
+ // Navigation details
33
+ const detail = {
34
+ navigationType,
35
+ navigationOrigins,
36
+ destination,
37
+ source: this.currentEntry(),
38
+ userInitiated,
39
+ info
40
+ };
41
+ navigationOrigins = [];
42
+ // Traversal?
43
+ // Push
44
+ const url = new URL(destination.url, this.location.href);
45
+ const init = {
46
+ method: formData && 'POST' || 'GET',
47
+ body: formData,
48
+ signal
49
+ };
50
+ this.updateCurrentEntry({
51
+ state: {
52
+ ...(this.currentEntry().getState() || {}),
53
+ scrollPosition: [window.scrollX, window.scrollY],
54
+ }
55
+ });
56
+ const runtime = this;
57
+ e.intercept({
58
+ scroll: 'after-transition',
59
+ focusReset: 'after-transition',
60
+ async handler() { await runtime.navigate(url, init, detail); },
61
+ });
62
+ };
63
+ window.addEventListener('click', clickHandler);
64
+ window.addEventListener('submit', submitHandler);
65
+ window.navigation.addEventListener('navigate', navigateHandler);
66
+ return () => {
67
+ this.host.removeEventListener('click', clickHandler);
68
+ this.host.removeEventListener('submit', submitHandler);
69
+ window.navigation.removeEventListener('navigate', navigateHandler);
70
+ };
71
+ }
72
+
73
+ reload(params) {
74
+ return window.navigation.reload(params);
75
+ }
76
+
77
+ back() {
78
+ return window.navigation.canGoBack && window.navigation.back();
79
+ }
80
+
81
+ forward() {
82
+ return window.navigation.canGoForward && window.navigation.forward();
83
+ }
84
+
85
+ traverseTo(...args) {
86
+ return window.navigation.traverseTo(...args);
87
+ }
88
+
89
+ entries() {
90
+ return window.navigation.entries();
91
+ }
92
+
93
+ currentEntry() {
94
+ return window.navigation.currentEntry;
95
+ }
96
+
97
+ async updateCurrentEntry(params, url = null) {
98
+ if (!url || url === window.navigation.currentEntry.url) {
99
+ window.navigation.updateCurrentEntry(params);
100
+ } else { await window.navigation.navigate(url, { ...params, history: 'replace' }).committed; }
101
+ }
102
+
103
+ async push(url, state = {}) {
104
+ if (typeof url === 'string' && url.startsWith('&')) { url = this.location.href.split('#')[0] + (this.location.href.includes('?') ? url : url.replace('&', '?')); }
105
+ url = new URL(url, this.location.href);
106
+ await window.navigation.navigate(url.href, state).committed;
107
+ Observer.set(this.location, 'href', url.href);
108
+ }
109
+ }
@@ -0,0 +1,165 @@
1
+ import { WebfloClient } from './WebfloClient.js';
2
+ import { Url } from './Url.js';
3
+
4
+ const { Observer } = webqit;
5
+
6
+ export class WebfloSubClient extends WebfloClient {
7
+
8
+ static defineElement() {
9
+ const embedTagNames = 'webflo-embedded';
10
+ window.customElements.define(embedTagNames, class extends HTMLElement {
11
+
12
+ #superRuntime;
13
+ #webfloControllerUninitialize;
14
+ #location;
15
+ #reflectAction;
16
+
17
+ static get observedAttributes() { return ['location']; }
18
+
19
+ get startupFlight() {
20
+ return this.hasAttribute('location');
21
+ }
22
+
23
+ get location() {
24
+ if (!this.#location) {
25
+ this.#location = new URL(this.getAttribute('location') || '', window.location.origin);
26
+ }
27
+ return this.#location;
28
+ }
29
+
30
+ set location(value) {
31
+ if (!(value instanceof URL)) {
32
+ value = new URL(value, window.location.origin);
33
+ }
34
+ if (value.href === this.location.href) return;
35
+ this.#location = value;
36
+ this.setAttribute('location', value.href.replace(value.origin, ''));
37
+ if (!this.#reflectAction) {
38
+ this.webfloRuntime.navigate(value);
39
+ }
40
+ }
41
+
42
+ reflectLocation(location) {
43
+ this.#reflectAction = true;
44
+ this.location = location;
45
+ this.#reflectAction = false;
46
+ }
47
+
48
+ attributeChangedCallback(name, oldValue, newValue) {
49
+ if (oldValue === newValue) return;
50
+ this.location = newValue;
51
+ }
52
+
53
+ connectedCallback() {
54
+ this.#superRuntime = (this.parentNode?.closest(embedTagNames) || document).webfloRuntime;
55
+ this.#webfloControllerUninitialize = WebfloSubClient.create(this, this.#superRuntime).initialize();
56
+ }
57
+
58
+ disconnectedCallback() {
59
+ this.#webfloControllerUninitialize();
60
+ }
61
+ });
62
+ }
63
+
64
+ static create(host, superRuntime) {
65
+ return new this(host, superRuntime);
66
+ }
67
+
68
+ #superRuntime;
69
+ get superRuntime() { return this.#superRuntime; }
70
+
71
+ get cx() { return this.#superRuntime.cx; }
72
+
73
+ get workport() { return this.#superRuntime.workport; }
74
+
75
+ constructor(host, superRuntime) {
76
+ if (!(host instanceof HTMLElement)) {
77
+ throw new Error('Argument #1 must be a HTMLElement instance');
78
+ }
79
+ super(host);
80
+ if (!(superRuntime instanceof WebfloClient)) {
81
+ throw new Error('Argument #2 must be a Webflo Client instance');
82
+ }
83
+ this.#superRuntime = superRuntime;
84
+ }
85
+
86
+ initialize() {
87
+ if (this.host.location.origin !== window.location.origin) {
88
+ throw new Error(`Webflo embeddable origin violation in "${window.location}"`);
89
+ }
90
+ const uncontrols = super.initialize();
91
+ this.backgroundMessaging.setParent(this.#superRuntime.backgroundMessaging);
92
+ this.navigate(this.location.href);
93
+ return () => {
94
+ if (this.backgroundMessaging.parentNode === this.#superRuntime.backgroundMessaging) {
95
+ this.backgroundMessaging.setParent(null);
96
+ }
97
+ uncontrols();
98
+ };
99
+ }
100
+
101
+ control() {
102
+ return super.controlClassic((newHref) => {
103
+ this.host.reflectLocation(newHref);
104
+ });
105
+ }
106
+
107
+ reload(params) {
108
+ }
109
+
110
+ back() {
111
+ }
112
+
113
+ forward() {
114
+ }
115
+
116
+ traverseTo(...args) {
117
+ }
118
+
119
+ entries() {
120
+ }
121
+
122
+ currentEntry() {
123
+ }
124
+
125
+ async updateCurrentEntry(params, url = null) {
126
+ this.host.reflectLocation(url);
127
+ }
128
+
129
+ async push(url, state = {}) {
130
+ }
131
+
132
+ hardRedirect(location, backgroundMessaging = null) {
133
+ location = typeof location === 'string' ? new URL(location, this.location.origin) : location;
134
+ const width = Math.min(800, window.innerWidth);
135
+ const height = Math.min(600, window.innerHeight);
136
+ const left = (window.outerWidth - width) / 2;
137
+ const top = (window.outerHeight - height) / 2;
138
+ const popup = window.open(location, '_blank', `popup=true,width=${width},height=${height},left=${left},top=${top}`);
139
+ if (backgroundMessaging) {
140
+ backgroundMessaging.postMessage('keepAlive');
141
+ Observer.set(this.navigator, 'redirecting', new Url/*NOT URL*/(location), { diff: true });
142
+ backgroundMessaging.addEventListener('close', (e) => {
143
+ Observer.set(this.navigator, 'redirecting', null);
144
+ popup.postMessage('timeout:5');
145
+ setTimeout(() => {
146
+ popup.close();
147
+ }, 5000);
148
+ });
149
+ window.addEventListener('message', (e) => {
150
+ if (e.source === popup && e.data === 'close') {
151
+ backgroundMessaging.close();
152
+ }
153
+ });
154
+ }
155
+ }
156
+
157
+ async applyPostRenderState(httpEvent) {
158
+ if (httpEvent.url.hash) {
159
+ this.host.querySelector(httpEvent.url.hash)?.scrollIntoView();
160
+ } else {
161
+ this.host.scrollTo(0, 0);
162
+ }
163
+ (this.host.querySelector('[autofocus]') || this.host).focus();
164
+ }
165
+ }