@webqit/webflo 0.11.61-0 → 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 (118) hide show
  1. package/.gitignore +7 -7
  2. package/LICENSE +20 -20
  3. package/README.md +2079 -2074
  4. package/docker/Dockerfile +42 -42
  5. package/docker/README.md +91 -91
  6. package/docker/package.json +2 -2
  7. package/package.json +80 -81
  8. package/src/{Context.js → AbstractContext.js} +71 -79
  9. package/src/config-pi/deployment/Env.js +68 -68
  10. package/src/config-pi/deployment/Layout.js +63 -63
  11. package/src/config-pi/deployment/Origins.js +139 -139
  12. package/src/config-pi/deployment/Proxy.js +74 -74
  13. package/src/config-pi/deployment/index.js +17 -17
  14. package/src/config-pi/index.js +15 -15
  15. package/src/config-pi/runtime/Client.js +116 -98
  16. package/src/config-pi/runtime/Server.js +125 -125
  17. package/src/config-pi/runtime/client/Worker.js +109 -134
  18. package/src/config-pi/runtime/client/index.js +11 -11
  19. package/src/config-pi/runtime/index.js +17 -17
  20. package/src/config-pi/runtime/server/Headers.js +74 -74
  21. package/src/config-pi/runtime/server/Redirects.js +69 -69
  22. package/src/config-pi/runtime/server/index.js +13 -13
  23. package/src/config-pi/static/Manifest.js +319 -319
  24. package/src/config-pi/static/Ssg.js +49 -49
  25. package/src/config-pi/static/index.js +13 -13
  26. package/src/deployment-pi/index.js +10 -10
  27. package/src/deployment-pi/origins/index.js +216 -216
  28. package/src/index.js +11 -19
  29. package/src/runtime-pi/HttpEvent.js +126 -106
  30. package/src/runtime-pi/HttpUser.js +126 -0
  31. package/src/runtime-pi/MessagingOverBroadcast.js +9 -0
  32. package/src/runtime-pi/MessagingOverChannel.js +85 -0
  33. package/src/runtime-pi/MessagingOverSocket.js +106 -0
  34. package/src/runtime-pi/MultiportMessagingAPI.js +81 -0
  35. package/src/runtime-pi/WebfloCookieStorage.js +27 -0
  36. package/src/runtime-pi/WebfloEventTarget.js +39 -0
  37. package/src/runtime-pi/WebfloMessageEvent.js +58 -0
  38. package/src/runtime-pi/WebfloMessagingAPI.js +69 -0
  39. package/src/runtime-pi/{Router.js → WebfloRouter.js} +99 -130
  40. package/src/runtime-pi/WebfloRuntime.js +52 -0
  41. package/src/runtime-pi/WebfloStorage.js +109 -0
  42. package/src/runtime-pi/client/ClientMessaging.js +5 -0
  43. package/src/runtime-pi/client/Context.js +3 -7
  44. package/src/runtime-pi/client/CookieStorage.js +17 -0
  45. package/src/runtime-pi/client/Router.js +38 -48
  46. package/src/runtime-pi/client/SessionStorage.js +33 -0
  47. package/src/runtime-pi/client/Url.js +156 -205
  48. package/src/runtime-pi/client/WebfloClient.js +544 -0
  49. package/src/runtime-pi/client/WebfloRootClient1.js +179 -0
  50. package/src/runtime-pi/client/WebfloRootClient2.js +109 -0
  51. package/src/runtime-pi/client/WebfloSubClient.js +165 -0
  52. package/src/runtime-pi/client/Workport.js +118 -178
  53. package/src/runtime-pi/client/generate.js +480 -471
  54. package/src/runtime-pi/client/index.js +16 -21
  55. package/src/runtime-pi/client/worker/ClientMessaging.js +5 -0
  56. package/src/runtime-pi/client/worker/Context.js +3 -7
  57. package/src/runtime-pi/client/worker/CookieStorage.js +17 -0
  58. package/src/runtime-pi/client/worker/SessionStorage.js +13 -0
  59. package/src/runtime-pi/client/worker/WebfloWorker.js +294 -0
  60. package/src/runtime-pi/client/worker/Workport.js +17 -85
  61. package/src/runtime-pi/client/worker/index.js +10 -21
  62. package/src/runtime-pi/index.js +6 -13
  63. package/src/runtime-pi/server/ClientMessaging.js +18 -0
  64. package/src/runtime-pi/server/ClientMessagingRegistry.js +57 -0
  65. package/src/runtime-pi/server/Context.js +11 -15
  66. package/src/runtime-pi/server/CookieStorage.js +17 -0
  67. package/src/runtime-pi/server/Router.js +93 -159
  68. package/src/runtime-pi/server/SessionStorage.js +53 -0
  69. package/src/runtime-pi/server/WebfloServer.js +755 -0
  70. package/src/runtime-pi/server/index.js +10 -21
  71. package/src/runtime-pi/util-http.js +322 -86
  72. package/src/runtime-pi/util-url.js +146 -146
  73. package/src/runtime-pi/xURL.js +108 -105
  74. package/src/runtime-pi/xfetch.js +22 -22
  75. package/src/services-pi/cert/http-auth-hook.js +22 -22
  76. package/src/services-pi/cert/http-cleanup-hook.js +22 -22
  77. package/src/services-pi/cert/index.js +79 -79
  78. package/src/services-pi/index.js +8 -8
  79. package/src/static-pi/index.js +10 -10
  80. package/src/webflo.js +30 -30
  81. package/test/index.test.js +26 -26
  82. package/test/site/package.json +9 -9
  83. package/test/site/public/bundle.html +5 -5
  84. package/test/site/public/bundle.html.json +3 -3
  85. package/test/site/public/bundle.js +2 -2
  86. package/test/site/public/bundle.webflo.js +15 -15
  87. package/test/site/public/index.html +29 -29
  88. package/test/site/public/index1.html +34 -34
  89. package/test/site/public/page-2/bundle.html +4 -4
  90. package/test/site/public/page-2/bundle.js +2 -2
  91. package/test/site/public/page-2/index.html +45 -45
  92. package/test/site/public/page-2/main.html +2 -2
  93. package/test/site/public/page-4/subpage/bundle.js +2 -2
  94. package/test/site/public/page-4/subpage/index.html +30 -30
  95. package/test/site/public/sparoots.json +4 -4
  96. package/test/site/public/worker.js +3 -3
  97. package/test/site/server/index.js +15 -15
  98. package/src/runtime-pi/Application.js +0 -29
  99. package/src/runtime-pi/Cookies.js +0 -82
  100. package/src/runtime-pi/Runtime.js +0 -21
  101. package/src/runtime-pi/client/Application.js +0 -100
  102. package/src/runtime-pi/client/Runtime.js +0 -332
  103. package/src/runtime-pi/client/createStorage.js +0 -57
  104. package/src/runtime-pi/client/oohtml/full.js +0 -7
  105. package/src/runtime-pi/client/oohtml/namespacing.js +0 -7
  106. package/src/runtime-pi/client/oohtml/scripting.js +0 -8
  107. package/src/runtime-pi/client/oohtml/templating.js +0 -8
  108. package/src/runtime-pi/client/worker/Application.js +0 -44
  109. package/src/runtime-pi/client/worker/Runtime.js +0 -269
  110. package/src/runtime-pi/server/Application.js +0 -116
  111. package/src/runtime-pi/server/Runtime.js +0 -557
  112. package/src/runtime-pi/xFormData.js +0 -24
  113. package/src/runtime-pi/xHeaders.js +0 -146
  114. package/src/runtime-pi/xRequest.js +0 -46
  115. package/src/runtime-pi/xRequestHeaders.js +0 -109
  116. package/src/runtime-pi/xResponse.js +0 -33
  117. package/src/runtime-pi/xResponseHeaders.js +0 -117
  118. package/src/runtime-pi/xxHttpMessage.js +0 -102
@@ -0,0 +1,39 @@
1
+ export class WebfloEventTarget extends EventTarget {
2
+
3
+ #parentNode;
4
+ #params;
5
+ #listenersRegistry = new Set;
6
+
7
+ get parentNode() { return this.#parentNode; }
8
+ get params() { return this.#params; }
9
+ get length() { return this.#listenersRegistry.size; }
10
+
11
+ constructor(parentNode, params = {}) {
12
+ super();
13
+ this.#parentNode = parentNode;
14
+ this.#params = params;
15
+ }
16
+
17
+ setParent(parentNode) {
18
+ this.#parentNode = parentNode;
19
+ }
20
+
21
+ dispatchEvent(event) {
22
+ const returnValue = super.dispatchEvent(event);
23
+ if (this.#parentNode instanceof EventTarget && !event.defaultPrevented && !event.propagationStopped) {
24
+ this.#parentNode.dispatchEvent(event);
25
+ }
26
+ return returnValue;
27
+ }
28
+
29
+ addEventListener(...args) {
30
+ this.#listenersRegistry.add(args);
31
+ return super.addEventListener(...args);
32
+ }
33
+
34
+ $destroy() {
35
+ for (const listenerArgs of this.#listenersRegistry) {
36
+ this.removeEventListener(...listenerArgs);
37
+ }
38
+ }
39
+ }
@@ -0,0 +1,58 @@
1
+
2
+ export class WebfloMessageEvent extends Event {
3
+
4
+ #originalTarget;
5
+ get originalTarget() { return this.#originalTarget; }
6
+
7
+ get runtime() {
8
+ let parentNode = this.#originalTarget;
9
+ do {
10
+ if (parentNode.runtime) return parentNode.runtime;
11
+ } while (parentNode = parentNode.parentNode)
12
+ }
13
+
14
+ #data;
15
+ get data() { return this.#data; }
16
+
17
+ #ports = [];
18
+ get ports() { return this.#ports; }
19
+
20
+ constructor(originalTarget, messageType, message, ports) {
21
+ super(messageType);
22
+ this.#originalTarget = originalTarget;
23
+ this.#data = message;
24
+ this.#ports = ports;
25
+ }
26
+
27
+ #immediatePropagationStopped = false;
28
+ get immediatePropagationStopped() { return this.#immediatePropagationStopped; }
29
+
30
+ stopImmediatePropagation() {
31
+ this.#immediatePropagationStopped = true;
32
+ this.#propagationStopped = true;
33
+ super.stopImmediatePropagation();
34
+ }
35
+
36
+ #propagationStopped = false;
37
+ get propagationStopped() { return this.#propagationStopped; }
38
+
39
+ stopPropagation() {
40
+ this.#propagationStopped = true;
41
+ super.stopPropagation();
42
+ }
43
+
44
+ #defaultPrevented = false;
45
+ get defaultPrevented() { return this.#defaultPrevented; }
46
+
47
+ preventDefault() {
48
+ this.#defaultPrevented = true;
49
+ super.preventDefault();
50
+ }
51
+
52
+ respondWith(data, transferOrOptions = []) {
53
+ for (const port of this.ports) {
54
+ port.postMessage(data, transferOrOptions);
55
+ }
56
+ return !!this.ports.length;
57
+ }
58
+ }
@@ -0,0 +1,69 @@
1
+ import { WebfloEventTarget } from './WebfloEventTarget.js';
2
+
3
+ export class WebfloMessagingAPI extends WebfloEventTarget {
4
+
5
+ #isConnected = false;
6
+ isConnected() { return this.#isConnected; }
7
+
8
+ #isSending = false;
9
+ isMessaging() { return this.#isSending || !!this.length; }
10
+
11
+ #hooks = new Set;
12
+ on(eventName, callback, { once = false } = {}) {
13
+ if (eventName === 'connected' && this.#isConnected) {
14
+ callback();
15
+ if (once) {
16
+ return;
17
+ }
18
+ }
19
+ const hook = { eventName, callback, once };
20
+ this.#hooks.add(hook);
21
+ return () => this.#hooks.delete(hook);
22
+ }
23
+
24
+ $emit(eventName, arg) {
25
+ if (eventName === 'connected') {
26
+ this.#isConnected = true;
27
+ }
28
+ for (const hook of this.#hooks) {
29
+ if (hook.eventName !== eventName) continue;
30
+ hook.callback(arg);
31
+ if (hook.once) {
32
+ this.#hooks.delete(hook);
33
+ }
34
+ }
35
+ }
36
+
37
+ /* ----------------- */
38
+
39
+ postMessage(data, transferOrOptions = []) {
40
+ this.#isSending = true;
41
+ }
42
+
43
+ postRequest(message, callback, options = {}) {
44
+ const { signal, once, ...$options } = options;
45
+ const messageChannel = new MessageChannel;
46
+ messageChannel.port1.addEventListener('message', (e) => callback(e), {
47
+ signal,
48
+ once
49
+ });
50
+ messageChannel.port1.start();
51
+ return this.postMessage(message, { ...$options, transfer: [ messageChannel.port2 ].concat($options.transfer || []) });
52
+ }
53
+
54
+ handleMessages(type, listener, options = {}) {
55
+ this.addEventListener(type, listener, options);
56
+ return () => {
57
+ this.removeEventListener(type, listener, options);
58
+ };
59
+ }
60
+
61
+ handleRequests(type, listener, options = {}) {
62
+ return this.handleMessages(type, async (e) => {
63
+ const response = await listener(e);
64
+ for (const p of e.ports) {
65
+ p.postMessage(response);
66
+ }
67
+ }, options);
68
+ }
69
+ }
@@ -1,130 +1,99 @@
1
-
2
- /**
3
- * @imports
4
- */
5
- import { _isString, _isFunction, _isArray } from '@webqit/util/js/index.js';
6
- import { _from as _arrFrom } from '@webqit/util/arr/index.js';
7
-
8
- /**
9
- * ---------------------------
10
- * The Router class
11
- * ---------------------------
12
- */
13
-
14
- export default class Router {
15
-
16
- /**
17
- * Constructs a new Router instance
18
- * over route definitions.
19
- *
20
- * @param Context cx
21
- * @param String|Array path
22
- *
23
- * @return void
24
- */
25
- constructor(cx, path = []) {
26
- this.cx = cx;
27
- this.path = _isArray(path) ? path : (path + '').split('/').filter(a => a);
28
- }
29
-
30
- /**
31
- * Performs dynamic routing
32
- *
33
- * @param array|string method
34
- * @param Object event
35
- * @param any arg
36
- * @param function _default
37
- * @param function remoteFetch
38
- *
39
- * @return object
40
- */
41
- async route(method, event, arg, _default, remoteFetch = null) {
42
-
43
- const $this = this;
44
- const $runtime = this.cx.runtime;
45
-
46
- // ----------------
47
- // The loop
48
- // ----------------
49
- const next = async function(thisTick) {
50
- const thisContext = { runtime: $runtime };
51
- if (!thisTick.trail || thisTick.trail.length < thisTick.destination.length) {
52
- thisTick = await $this.readTick(thisTick);
53
- // -------------
54
- thisContext.pathname = `/${thisTick.trail.join('/')}`;
55
- thisContext.stepname = thisTick.trail[thisTick.trail.length - 1];
56
- $this.finalizeHandlerContext(thisContext, thisTick);
57
- // -------------
58
- if (thisTick.exports) {
59
- // Broadcast any hints exported by handler
60
- if (thisTick.exports.hints) { await event.port.post({ ...thisTick.exports.hints, $type: 'handler:hints' }); }
61
- const methods = _arrFrom(thisTick.method).map(m => m === 'default' ? m : m.toUpperCase());
62
- const handler = _isFunction(thisTick.exports) && methods.includes('default') ? thisTick.exports : methods.reduce((_handler, name) => _handler || thisTick.exports[name], null);
63
- if (handler) {
64
- // -------------
65
- // Dynamic response
66
- // -------------
67
- const _next = async (..._args) => {
68
- const nextTick = { ...thisTick, arg: _args[0] };
69
- if (_args.length > 1) {
70
- let _url = _args[1], _request, requestInit = { ...(_args[2] || {}) };
71
- if (_args[1] instanceof nextTick.event.Request) {
72
- _request = _args[1];
73
- _url = _request.url;
74
- } else if (!_isString(_url)) {
75
- throw new Error('Router redirect url must be a string!');
76
- }
77
- let newDestination = _url.startsWith('/') ? _url : $this.pathJoin(`/${thisTick.trail.join('/')}`, _url);
78
- if (newDestination.startsWith('../')) {
79
- throw new Error('Router redirect cannot traverse beyond the routing directory! (' + _url + ' >> ' + newDestination + ')');
80
- }
81
- if (requestInit.method) {
82
- nextTick.method = requestInit.method;
83
- if (_isArray(requestInit.method)) {
84
- requestInit.method = requestInit.method[0];
85
- }
86
- }
87
- if (_request) {
88
- nextTick.event = await thisTick.event.with(newDestination, _request, requestInit);
89
- } else {
90
- nextTick.event = await thisTick.event.with(newDestination, requestInit);
91
- }
92
- nextTick.source = thisTick.destination.join('/');
93
- nextTick.destination = newDestination.split('?').shift().split('/').map(a => a.trim()).filter(a => a);
94
- nextTick.trail = _args[1].startsWith('/') ? [] : thisTick.trail.reduce((_commonRoot, _seg, i) => _commonRoot.length === i && _seg === nextTick.destination[i] ? _commonRoot.concat(_seg) : _commonRoot, []);
95
- nextTick.trailOnFile = thisTick.trailOnFile.slice(0, nextTick.trail.length);
96
- }
97
- return next(nextTick);
98
- };
99
- // -------------
100
- const nextPathname = thisTick.destination.slice(thisTick.trail.length);
101
- _next.pathname = nextPathname.join('/');
102
- _next.stepname = nextPathname[0];
103
- // -------------
104
- return await handler.call(thisContext, thisTick.event, thisTick.arg, _next/*next*/, remoteFetch);
105
- }
106
- // Handler not found but exports found
107
- return next(thisTick);
108
- } else if ((thisTick.currentSegmentOnFile || {}).dirExists) {
109
- // Exports not found but directory found
110
- return next(thisTick);
111
- }
112
- }
113
- // -------------
114
- // Local file
115
- // -------------
116
- if (_default) {
117
- return await _default.call(thisContext, thisTick.event, thisTick.arg, remoteFetch);
118
- }
119
- };
120
-
121
- return next({
122
- destination: this.path,
123
- event,
124
- method,
125
- arg,
126
- });
127
-
128
- }
129
-
130
- }
1
+ import { _isString, _isFunction, _isArray } from '@webqit/util/js/index.js';
2
+ import { _from as _arrFrom } from '@webqit/util/arr/index.js';
3
+
4
+ export class WebfloRouter {
5
+
6
+ constructor(cx, path = []) {
7
+ this.cx = cx;
8
+ this.path = _isArray(path) ? path : (path + '').split('/').filter(a => a);
9
+ }
10
+
11
+ async route(method, event, arg, _default, remoteFetch = null) {
12
+
13
+ const $this = this;
14
+ const $runtime = this.cx.runtime;
15
+
16
+ // ----------------
17
+ // The loop
18
+ // ----------------
19
+ const next = async function(thisTick) {
20
+ const thisContext = { runtime: $runtime };
21
+ if (!thisTick.trail || thisTick.trail.length < thisTick.destination.length) {
22
+ thisTick = await $this.readTick(thisTick);
23
+ // -------------
24
+ thisContext.pathname = `/${thisTick.trail.join('/')}`;
25
+ thisContext.stepname = thisTick.trail[thisTick.trail.length - 1];
26
+ $this.finalizeHandlerContext(thisContext, thisTick);
27
+ // -------------
28
+ if (thisTick.exports) {
29
+ // Broadcast any hints exported by handler
30
+ //@obsolete if (thisTick.exports.hints) { await event.port.post({ ...thisTick.exports.hints, $type: 'handler:hints' }); }
31
+ const methods = _arrFrom(thisTick.method).map(m => m === 'default' ? m : m.toUpperCase());
32
+ const handler = _isFunction(thisTick.exports) && methods.includes('default') ? thisTick.exports : methods.reduce((_handler, name) => _handler || thisTick.exports[name], null);
33
+ if (handler) {
34
+ // -------------
35
+ // Dynamic response
36
+ // -------------
37
+ const _next = async (..._args) => {
38
+ const nextTick = { ...thisTick, arg: _args[0] };
39
+ if (_args.length > 1) {
40
+ let _url = _args[1], _request, requestInit = { ...(_args[2] || {}) };
41
+ if (_args[1] instanceof Request) {
42
+ _request = _args[1];
43
+ _url = _request.url;
44
+ } else if (!_isString(_url)) {
45
+ throw new Error('Router redirect url must be a string!');
46
+ }
47
+ let newDestination = _url.startsWith('/') ? _url : $this.pathJoin(`/${thisTick.trail.join('/')}`, _url);
48
+ if (newDestination.startsWith('../')) {
49
+ throw new Error('Router redirect cannot traverse beyond the routing directory! (' + _url + ' >> ' + newDestination + ')');
50
+ }
51
+ if (requestInit.method) {
52
+ nextTick.method = requestInit.method;
53
+ if (_isArray(requestInit.method)) {
54
+ requestInit.method = requestInit.method[0];
55
+ }
56
+ }
57
+ if (_request) {
58
+ nextTick.event = await thisTick.event.with(newDestination, _request, requestInit);
59
+ } else {
60
+ nextTick.event = await thisTick.event.with(newDestination, requestInit);
61
+ }
62
+ nextTick.source = thisTick.destination.join('/');
63
+ nextTick.destination = newDestination.split('?').shift().split('/').map(a => a.trim()).filter(a => a);
64
+ nextTick.trail = _args[1].startsWith('/') ? [] : thisTick.trail.reduce((_commonRoot, _seg, i) => _commonRoot.length === i && _seg === nextTick.destination[i] ? _commonRoot.concat(_seg) : _commonRoot, []);
65
+ nextTick.trailOnFile = thisTick.trailOnFile.slice(0, nextTick.trail.length);
66
+ }
67
+ return next(nextTick);
68
+ };
69
+ // -------------
70
+ const nextPathname = thisTick.destination.slice(thisTick.trail.length);
71
+ _next.pathname = nextPathname.join('/');
72
+ _next.stepname = nextPathname[0];
73
+ // -------------
74
+ return await handler.call(thisContext, thisTick.event, thisTick.arg, _next/*next*/, remoteFetch);
75
+ }
76
+ // Handler not found but exports found
77
+ return next(thisTick);
78
+ } else if ((thisTick.currentSegmentOnFile || {}).dirExists) {
79
+ // Exports not found but directory found
80
+ return next(thisTick);
81
+ }
82
+ }
83
+ // -------------
84
+ // Local file
85
+ // -------------
86
+ if (_default) {
87
+ return await _default.call(thisContext, thisTick.event, thisTick.arg, remoteFetch);
88
+ }
89
+ };
90
+
91
+ return next({
92
+ destination: this.path,
93
+ event,
94
+ method,
95
+ arg,
96
+ });
97
+
98
+ }
99
+ }
@@ -0,0 +1,52 @@
1
+ import { _isObject } from '@webqit/util/js/index.js';
2
+
3
+ export class WebfloRuntime {
4
+
5
+ async dispatch(httpEvent, context, crossLayerFetch) {
6
+ // Exec routing
7
+ const router = new this.constructor.Router(this.cx, httpEvent.url.pathname);
8
+ const route = async () => {
9
+ return await router.route([httpEvent.request.method, 'default'], httpEvent, context, async (event) => {
10
+ return crossLayerFetch(event);
11
+ }, (...args) => this.remoteFetch(...args));
12
+ };
13
+ try {
14
+ // Route for response
15
+ return await (this.cx.middlewares || []).concat(route).reverse().reduce((next, fn) => {
16
+ return () => fn.call(this.cx, httpEvent, router, next);
17
+ }, null)();
18
+
19
+ } catch (e) {
20
+ console.error(e);
21
+ return new Response(null, { status: 500, statusText: e.message });
22
+ }
23
+ }
24
+
25
+ async normalizeResponse(httpEvent, response, forceCommit = false) {
26
+ // Normalize response
27
+ if (!(response instanceof Response)) {
28
+ response = typeof response === 'undefined'
29
+ ? new Response(null, { status: 404 })
30
+ : Response.create(response);
31
+ }
32
+ // Commit data
33
+ for (const storage of [httpEvent.cookies, httpEvent.session, httpEvent.storage]) {
34
+ await storage?.commit?.(response, forceCommit);
35
+ }
36
+ return response;
37
+ }
38
+
39
+ async execPush(clientPort, data) {
40
+ if (data instanceof Response) {
41
+ if ([301, 302, 303, 307, 308].includes(data.status) && data.headers.has('Location')) {
42
+ clientPort.postMessage(data.headers.get('Location'), { messageType: 'redirect' });
43
+ return;
44
+ }
45
+ data = await data.parse();
46
+ }
47
+ if (!_isObject(data)) {
48
+ throw new Error('Response not serializable');
49
+ }
50
+ clientPort.postMessage(data, { messageType: 'response' });
51
+ }
52
+ }
@@ -0,0 +1,109 @@
1
+ import { _isObject } from '@webqit/util/js/index.js';
2
+ import { _even } from '@webqit/util/obj/index.js';
3
+
4
+ export class WebfloStorage extends Map {
5
+
6
+ #request;
7
+ #session;
8
+
9
+ constructor(request, session, iterable = []) {
10
+ super(iterable);
11
+ this.#request = request;
12
+ this.#session = session === true ? this : session;
13
+ }
14
+
15
+ #originals;
16
+ saveOriginals() { this.#originals = new Map(this); }
17
+
18
+ getDeleted() {
19
+ if (!this.#originals) return [];
20
+ return [...this.#originals.keys()].filter((k) => {
21
+ return !this.has(k);
22
+ });
23
+ }
24
+
25
+ getAdded() {
26
+ if (!this.#originals) return [...this.keys()];
27
+ return [...new Set([...this.keys(), ...this.#originals.keys()])].filter((k) => {
28
+ return !this.#originals.has(k) || (this.has(k) && ((a, b) => _isObject(a) && _isObject(b) ? !_even(a, b) : a !== b)(this.get(k, true), this.#originals.get(k)));
29
+ });
30
+ }
31
+
32
+ commit() {
33
+ this.saveOriginals();
34
+ }
35
+
36
+ #handlers = new Map;
37
+ #reverseHandlers = new Map;
38
+ defineHandler(attr, ...handlers) {
39
+ let registry = this.#handlers;
40
+ if (handlers[0] === false) {
41
+ registry = this.#reverseHandlers;
42
+ handlers.shift();
43
+ }
44
+ const $handlers = [];
45
+ for (let handler of handlers) {
46
+ if (typeof handler === 'function') {
47
+ handler = { callback: handler };
48
+ } else if (typeof handler === 'string') {
49
+ handler = { url: handler };
50
+ } else if (typeof handler?.callback !== 'function' && typeof handler?.url !== 'string') {
51
+ throw new Error(`Handler must be either an URL or a function or an object specifying either an URL (handler.url) or a function (handler.callback)`);
52
+ }
53
+ $handlers.push(handler);
54
+ }
55
+ registry.set(attr, $handlers);
56
+ }
57
+
58
+ defineReverseHandler(attr, ...handlers) {
59
+ return this.defineHandler(attr, false, ...handlers);
60
+ }
61
+
62
+ getHandlers() { return this.#handlers; }
63
+
64
+ getReverseHandlers() { return this.#reverseHandlers; }
65
+
66
+ async require(attrs, callback = null, noNulls = false) {
67
+ const entries = [];
68
+ main: for await (const attr of [].concat(attrs)) {
69
+ if (!this.has(attr) || (noNulls && [undefined, null].includes(this.get(attr)))) {
70
+ const handlers = this.#handlers.get(attr);
71
+ if (!handlers) {
72
+ throw new Error(`No handler defined for the user attribute: ${attr}`);
73
+ }
74
+ for (let i = 0; i < handlers.length; i ++) {
75
+ const handler = handlers[i];
76
+ if (handler.callback) {
77
+ const returnValue = await handler.callback(this, attr);
78
+ if (returnValue instanceof Response) {
79
+ return returnValue;
80
+ }
81
+ if ((typeof returnValue === 'undefined' || (noNulls && returnValue === null)) && i < handlers.length - 1) {
82
+ continue;
83
+ }
84
+ entries.push(returnValue);
85
+ continue main;
86
+ }
87
+ const urlRewrite = new URL(handler.url, this.#request.url);
88
+ if (!urlRewrite.searchParams.has('success-redirect')) {
89
+ urlRewrite.searchParams.set('success-redirect', this.#request.url.replace(urlRewrite.origin, ''));
90
+ }
91
+ if (handler.message) {
92
+ if (!this.#session) {
93
+ throw new Error('Storage type does not support redirect messages');
94
+ }
95
+ const messageID = (0 | Math.random() * 9e6).toString(36);
96
+ urlRewrite.searchParams.set('redirect-message', messageID);
97
+ this.#session.set(`redirect-message:${messageID}`, { status: { type: handler.type || 'info', message: handler.message }});
98
+ }
99
+ return new Response(null, { status: 302, headers: {
100
+ Location: urlRewrite
101
+ }});
102
+ }
103
+ }
104
+ entries.push(this.get(attr));
105
+ }
106
+ if (callback) return await callback(...entries);
107
+ return entries;
108
+ }
109
+ }
@@ -0,0 +1,5 @@
1
+ import { MessagingOverChannel } from '../MessagingOverChannel.js';
2
+
3
+ export class ClientMessaging extends MessagingOverChannel {
4
+ get runtime() { return this.parentNode; }
5
+ }
@@ -1,7 +1,3 @@
1
-
2
- /**
3
- * @imports
4
- */
5
- import _Contex from '../../Context.js';
6
-
7
- export default class Context extends _Contex {}
1
+ import { AbstractContext } from '../../AbstractContext.js';
2
+
3
+ export class Context extends AbstractContext {}
@@ -0,0 +1,17 @@
1
+ import { WebfloCookieStorage } from '../WebfloCookieStorage.js';
2
+
3
+ export class CookieStorage extends WebfloCookieStorage {
4
+ static create(request) {
5
+ return new this(
6
+ request,
7
+ document.cookie.split(';').map((c) => c.split('=').map((s) => s.trim()))
8
+ );
9
+ }
10
+
11
+ commit(response) {
12
+ for (const cookieStr of this.render()) {
13
+ document.cookie = cookieStr;
14
+ }
15
+ super.commit();
16
+ }
17
+ }