@webqit/webflo 0.11.61 → 0.20.2-next.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 (235) hide show
  1. package/.github/FUNDING.yml +12 -0
  2. package/.github/workflows/publish.yml +48 -0
  3. package/.gitignore +2 -0
  4. package/LICENSE +2 -2
  5. package/README.md +71 -2050
  6. package/package.json +28 -13
  7. package/site/-/_.md +139 -0
  8. package/site/-/docs.old.md +2010 -0
  9. package/site/.vitepress/cache/deps/@braintree_sanitize-url 2.js +93 -0
  10. package/site/.vitepress/cache/deps/@braintree_sanitize-url.js +93 -0
  11. package/site/.vitepress/cache/deps/@braintree_sanitize-url.js 2.map +7 -0
  12. package/site/.vitepress/cache/deps/@braintree_sanitize-url.js.map +7 -0
  13. package/site/.vitepress/cache/deps/_metadata 2.json +85 -0
  14. package/site/.vitepress/cache/deps/_metadata.json +85 -0
  15. package/site/.vitepress/cache/deps/chunk-BUSYA2B4 2.js +9 -0
  16. package/site/.vitepress/cache/deps/chunk-BUSYA2B4.js +9 -0
  17. package/site/.vitepress/cache/deps/chunk-BUSYA2B4.js 2.map +7 -0
  18. package/site/.vitepress/cache/deps/chunk-BUSYA2B4.js.map +7 -0
  19. package/site/.vitepress/cache/deps/chunk-Q2AYPHVK 2.js +9719 -0
  20. package/site/.vitepress/cache/deps/chunk-Q2AYPHVK.js +9719 -0
  21. package/site/.vitepress/cache/deps/chunk-Q2AYPHVK.js 2.map +7 -0
  22. package/site/.vitepress/cache/deps/chunk-Q2AYPHVK.js.map +7 -0
  23. package/site/.vitepress/cache/deps/chunk-QAXAIFA7 2.js +12705 -0
  24. package/site/.vitepress/cache/deps/chunk-QAXAIFA7.js +12705 -0
  25. package/site/.vitepress/cache/deps/chunk-QAXAIFA7.js 2.map +7 -0
  26. package/site/.vitepress/cache/deps/chunk-QAXAIFA7.js.map +7 -0
  27. package/site/.vitepress/cache/deps/cytoscape 2.js +30278 -0
  28. package/site/.vitepress/cache/deps/cytoscape-cose-bilkent 2.js +4710 -0
  29. package/site/.vitepress/cache/deps/cytoscape-cose-bilkent.js +4710 -0
  30. package/site/.vitepress/cache/deps/cytoscape-cose-bilkent.js 2.map +7 -0
  31. package/site/.vitepress/cache/deps/cytoscape-cose-bilkent.js.map +7 -0
  32. package/site/.vitepress/cache/deps/cytoscape.js +30278 -0
  33. package/site/.vitepress/cache/deps/cytoscape.js 2.map +7 -0
  34. package/site/.vitepress/cache/deps/cytoscape.js.map +7 -0
  35. package/site/.vitepress/cache/deps/dayjs 2.js +285 -0
  36. package/site/.vitepress/cache/deps/dayjs.js +285 -0
  37. package/site/.vitepress/cache/deps/dayjs.js 2.map +7 -0
  38. package/site/.vitepress/cache/deps/dayjs.js.map +7 -0
  39. package/site/.vitepress/cache/deps/debug 2.js +453 -0
  40. package/site/.vitepress/cache/deps/debug.js +453 -0
  41. package/site/.vitepress/cache/deps/debug.js 2.map +7 -0
  42. package/site/.vitepress/cache/deps/debug.js.map +7 -0
  43. package/site/.vitepress/cache/deps/package 2.json +3 -0
  44. package/site/.vitepress/cache/deps/package.json +3 -0
  45. package/site/.vitepress/cache/deps/vitepress___@vue_devtools-api 2.js +4507 -0
  46. package/site/.vitepress/cache/deps/vitepress___@vue_devtools-api.js +4507 -0
  47. package/site/.vitepress/cache/deps/vitepress___@vue_devtools-api.js 2.map +7 -0
  48. package/site/.vitepress/cache/deps/vitepress___@vue_devtools-api.js.map +7 -0
  49. package/site/.vitepress/cache/deps/vitepress___@vueuse_core 2.js +584 -0
  50. package/site/.vitepress/cache/deps/vitepress___@vueuse_core.js +584 -0
  51. package/site/.vitepress/cache/deps/vitepress___@vueuse_core.js 2.map +7 -0
  52. package/site/.vitepress/cache/deps/vitepress___@vueuse_core.js.map +7 -0
  53. package/site/.vitepress/cache/deps/vitepress___@vueuse_integrations_useFocusTrap 2.js +1166 -0
  54. package/site/.vitepress/cache/deps/vitepress___@vueuse_integrations_useFocusTrap.js +1166 -0
  55. package/site/.vitepress/cache/deps/vitepress___@vueuse_integrations_useFocusTrap.js 2.map +7 -0
  56. package/site/.vitepress/cache/deps/vitepress___@vueuse_integrations_useFocusTrap.js.map +7 -0
  57. package/site/.vitepress/cache/deps/vitepress___mark__js_src_vanilla__js 2.js +1667 -0
  58. package/site/.vitepress/cache/deps/vitepress___mark__js_src_vanilla__js.js +1667 -0
  59. package/site/.vitepress/cache/deps/vitepress___mark__js_src_vanilla__js.js 2.map +7 -0
  60. package/site/.vitepress/cache/deps/vitepress___mark__js_src_vanilla__js.js.map +7 -0
  61. package/site/.vitepress/cache/deps/vitepress___minisearch 2.js +1815 -0
  62. package/site/.vitepress/cache/deps/vitepress___minisearch.js +1815 -0
  63. package/site/.vitepress/cache/deps/vitepress___minisearch.js 2.map +7 -0
  64. package/site/.vitepress/cache/deps/vitepress___minisearch.js.map +7 -0
  65. package/site/.vitepress/cache/deps/vue 2.js +344 -0
  66. package/site/.vitepress/cache/deps/vue.js +344 -0
  67. package/site/.vitepress/cache/deps/vue.js 2.map +7 -0
  68. package/site/.vitepress/cache/deps/vue.js.map +7 -0
  69. package/site/.vitepress/config.ts +147 -0
  70. package/site/.vitepress/theme/custom.css +50 -0
  71. package/site/.vitepress/theme/index.ts +6 -0
  72. package/site/api/webflo-fetch/FormData.md +0 -0
  73. package/site/api/webflo-fetch/Headers.md +0 -0
  74. package/site/api/webflo-fetch/LiveResponse.md +0 -0
  75. package/site/api/webflo-fetch/Request.md +0 -0
  76. package/site/api/webflo-fetch/Response.md +0 -0
  77. package/site/api/webflo-fetch/fetch.md +0 -0
  78. package/site/api/webflo-routing/HttpCookies.md +0 -0
  79. package/site/api/webflo-routing/HttpEvent/respondWith.md +1 -0
  80. package/site/api/webflo-routing/HttpEvent/waitUntil.md +1 -0
  81. package/site/api/webflo-routing/HttpEvent/waitUntilNavigate.md +1 -0
  82. package/site/api/webflo-routing/HttpEvent.md +30 -0
  83. package/site/api/webflo-routing/HttpSession.md +0 -0
  84. package/site/api/webflo-routing/HttpState.md +0 -0
  85. package/site/api/webflo-routing/HttpUser.md +0 -0
  86. package/site/api/webflo-routing/handler/fetch.md +42 -0
  87. package/site/api/webflo-routing/handler/next.md +54 -0
  88. package/site/api/webflo-routing/handler.md +119 -0
  89. package/site/api.md +26 -0
  90. package/site/contributing.md +16 -0
  91. package/site/docs/advanced/lifecycles.md +20 -0
  92. package/site/docs/advanced/redirects.md +0 -0
  93. package/site/docs/advanced/routing.md +1 -0
  94. package/site/docs/advanced.md +9 -0
  95. package/site/docs/concepts/realtime.md +637 -0
  96. package/site/docs/concepts/rendering.md +60 -0
  97. package/site/docs/concepts/request-response.md +47 -0
  98. package/site/docs/concepts/routing.md +656 -0
  99. package/site/docs/concepts/state.md +44 -0
  100. package/site/docs/concepts/templates.md +48 -0
  101. package/site/docs/concepts.md +97 -0
  102. package/site/docs/getting-started.md +378 -0
  103. package/site/docs/tech-stack.md +56 -0
  104. package/site/docs.md +100 -0
  105. package/site/examples/pwa.md +10 -0
  106. package/site/examples/web.md +11 -0
  107. package/site/examples.md +10 -0
  108. package/site/faq.md +13 -0
  109. package/site/guides/guide-auth.md +13 -0
  110. package/site/guides/guide-file-upload.md +11 -0
  111. package/site/guides/guide-service-worker.md +10 -0
  112. package/site/guides/tutorial-1-todo.md +24 -0
  113. package/site/guides.md +15 -0
  114. package/site/index.md +39 -0
  115. package/site/public/img/brand/logo-670x670.png +0 -0
  116. package/site/recipes/realtime.md +11 -0
  117. package/site/recipes/streaming.md +15 -0
  118. package/site/reference/cli.md +11 -0
  119. package/site/reference/config.md +13 -0
  120. package/site/reference/tools.md +9 -0
  121. package/src/Context.js +3 -11
  122. package/src/config-pi/deployment/Env.js +6 -19
  123. package/src/config-pi/deployment/Layout.js +11 -3
  124. package/src/config-pi/runtime/Client.js +40 -48
  125. package/src/config-pi/runtime/Server.js +52 -20
  126. package/src/config-pi/runtime/client/Worker.js +22 -20
  127. package/src/config-pi/static/Init.js +57 -0
  128. package/src/config-pi/static/index.js +2 -0
  129. package/src/deployment-pi/origins/index.js +1 -1
  130. package/src/deployment-pi/util.js +161 -0
  131. package/src/index.js +3 -9
  132. package/src/init-pi/index.js +117 -0
  133. package/src/init-pi/templates/pwa/app/handler.server.js +8 -0
  134. package/src/init-pi/templates/pwa/app/page.html +7 -0
  135. package/src/init-pi/templates/pwa/package.json +19 -0
  136. package/src/init-pi/templates/pwa/public/assets/app.css +16 -0
  137. package/src/init-pi/templates/pwa/public/index.html +39 -0
  138. package/src/init-pi/templates/pwa/public/manifest.json +29 -0
  139. package/src/init-pi/templates/web/app/handler.server.js +8 -0
  140. package/src/init-pi/templates/web/app/page.html +7 -0
  141. package/src/init-pi/templates/web/package.json +19 -0
  142. package/src/init-pi/templates/web/public/assets/app.css +16 -0
  143. package/src/init-pi/templates/web/public/index.html +39 -0
  144. package/src/runtime-pi/WebfloRuntime.js +350 -0
  145. package/src/runtime-pi/index.js +3 -10
  146. package/src/runtime-pi/webflo-client/ClientSideCookies.js +17 -0
  147. package/src/runtime-pi/webflo-client/ClientSideWorkport.js +63 -0
  148. package/src/runtime-pi/webflo-client/DeviceCapabilities.js +213 -0
  149. package/src/runtime-pi/webflo-client/WebfloClient.js +500 -0
  150. package/src/runtime-pi/webflo-client/WebfloRootClient1.js +206 -0
  151. package/src/runtime-pi/webflo-client/WebfloRootClient2.js +113 -0
  152. package/src/runtime-pi/webflo-client/WebfloSubClient.js +118 -0
  153. package/src/runtime-pi/webflo-client/index.js +17 -0
  154. package/src/runtime-pi/webflo-client/webflo-codegen.js +469 -0
  155. package/src/runtime-pi/webflo-client/webflo-devmode.js +243 -0
  156. package/src/runtime-pi/webflo-client/webflo-embedded.js +50 -0
  157. package/src/runtime-pi/webflo-fetch/LiveResponse.js +437 -0
  158. package/src/runtime-pi/webflo-fetch/cookies.js +10 -0
  159. package/src/runtime-pi/webflo-fetch/fetch.js +16 -0
  160. package/src/runtime-pi/webflo-fetch/formdata.js +54 -0
  161. package/src/runtime-pi/webflo-fetch/headers.js +151 -0
  162. package/src/runtime-pi/webflo-fetch/index.js +5 -0
  163. package/src/runtime-pi/webflo-fetch/message.js +49 -0
  164. package/src/runtime-pi/webflo-fetch/request.js +62 -0
  165. package/src/runtime-pi/webflo-fetch/response.js +110 -0
  166. package/src/runtime-pi/webflo-fetch/util.js +28 -0
  167. package/src/runtime-pi/webflo-messaging/WQBroadcastChannel.js +10 -0
  168. package/src/runtime-pi/webflo-messaging/WQMessageChannel.js +26 -0
  169. package/src/runtime-pi/webflo-messaging/WQMessageEvent.js +87 -0
  170. package/src/runtime-pi/webflo-messaging/WQMessagePort.js +38 -0
  171. package/src/runtime-pi/webflo-messaging/WQRelayPort.js +47 -0
  172. package/src/runtime-pi/webflo-messaging/WQSockPort.js +113 -0
  173. package/src/runtime-pi/webflo-messaging/WQStarPort.js +104 -0
  174. package/src/runtime-pi/webflo-messaging/wq-message-port.js +404 -0
  175. package/src/runtime-pi/webflo-routing/HttpCookies.js +42 -0
  176. package/src/runtime-pi/webflo-routing/HttpEvent.js +112 -0
  177. package/src/runtime-pi/webflo-routing/HttpSession.js +11 -0
  178. package/src/runtime-pi/webflo-routing/HttpState.js +153 -0
  179. package/src/runtime-pi/webflo-routing/HttpUser.js +54 -0
  180. package/src/runtime-pi/webflo-routing/WebfloRouter.js +245 -0
  181. package/src/runtime-pi/webflo-server/ServerSideCookies.js +19 -0
  182. package/src/runtime-pi/webflo-server/ServerSideSession.js +38 -0
  183. package/src/runtime-pi/webflo-server/WebfloServer.js +937 -0
  184. package/src/runtime-pi/webflo-server/index.js +11 -0
  185. package/src/runtime-pi/webflo-server/messaging/Client.js +27 -0
  186. package/src/runtime-pi/webflo-server/messaging/ClientRequestRealtime.js +50 -0
  187. package/src/runtime-pi/webflo-server/messaging/Clients.js +25 -0
  188. package/src/runtime-pi/webflo-server/webflo-devmode.js +326 -0
  189. package/src/runtime-pi/{client → webflo-url}/Url.js +27 -76
  190. package/src/runtime-pi/webflo-url/index.js +1 -0
  191. package/src/runtime-pi/webflo-url/urlpattern.js +38 -0
  192. package/src/runtime-pi/{util-url.js → webflo-url/util.js} +5 -43
  193. package/src/runtime-pi/webflo-url/xURL.js +94 -0
  194. package/src/runtime-pi/webflo-worker/WebfloWorker.js +234 -0
  195. package/src/runtime-pi/webflo-worker/WorkerSideCookies.js +19 -0
  196. package/src/runtime-pi/webflo-worker/WorkerSideWorkport.js +18 -0
  197. package/src/runtime-pi/webflo-worker/index.js +11 -0
  198. package/src/services-pi/index.js +2 -0
  199. package/src/services-pi/push/index.js +23 -0
  200. package/src/util.js +10 -0
  201. package/src/{webflo.js → webflo-cli.js} +4 -4
  202. package/src/runtime-pi/Application.js +0 -29
  203. package/src/runtime-pi/Cookies.js +0 -82
  204. package/src/runtime-pi/HttpEvent.js +0 -107
  205. package/src/runtime-pi/Router.js +0 -130
  206. package/src/runtime-pi/Runtime.js +0 -21
  207. package/src/runtime-pi/client/Application.js +0 -76
  208. package/src/runtime-pi/client/Context.js +0 -7
  209. package/src/runtime-pi/client/Router.js +0 -48
  210. package/src/runtime-pi/client/Runtime.js +0 -525
  211. package/src/runtime-pi/client/Workport.js +0 -190
  212. package/src/runtime-pi/client/createStorage.js +0 -58
  213. package/src/runtime-pi/client/generate.js +0 -481
  214. package/src/runtime-pi/client/index.js +0 -21
  215. package/src/runtime-pi/client/worker/Application.js +0 -44
  216. package/src/runtime-pi/client/worker/Context.js +0 -7
  217. package/src/runtime-pi/client/worker/Runtime.js +0 -275
  218. package/src/runtime-pi/client/worker/Workport.js +0 -78
  219. package/src/runtime-pi/client/worker/index.js +0 -21
  220. package/src/runtime-pi/server/Application.js +0 -101
  221. package/src/runtime-pi/server/Context.js +0 -16
  222. package/src/runtime-pi/server/Router.js +0 -159
  223. package/src/runtime-pi/server/Runtime.js +0 -558
  224. package/src/runtime-pi/server/index.js +0 -21
  225. package/src/runtime-pi/util-http.js +0 -86
  226. package/src/runtime-pi/xFormData.js +0 -24
  227. package/src/runtime-pi/xHeaders.js +0 -146
  228. package/src/runtime-pi/xRequest.js +0 -46
  229. package/src/runtime-pi/xRequestHeaders.js +0 -109
  230. package/src/runtime-pi/xResponse.js +0 -33
  231. package/src/runtime-pi/xResponseHeaders.js +0 -117
  232. package/src/runtime-pi/xURL.js +0 -105
  233. package/src/runtime-pi/xfetch.js +0 -23
  234. package/src/runtime-pi/xxHttpMessage.js +0 -102
  235. package/src/static-pi/index.js +0 -11
@@ -0,0 +1,153 @@
1
+ import { _isObject } from '@webqit/util/js/index.js';
2
+ import { _even } from '@webqit/util/obj/index.js';
3
+
4
+ export class HttpState {
5
+
6
+ #store;
7
+ #request;
8
+ #session;
9
+ #modified = false;
10
+
11
+ constructor({ store, request, session = null }) {
12
+ this.#store = store || new Map;
13
+ this.#request = request;
14
+ this.#session = session === true ? this : session;
15
+ }
16
+
17
+ async has(key) { return await this.#store.has(key); }
18
+
19
+ async get(key) { return await this.#store.get(key); }
20
+
21
+ async set(key, value) {
22
+ await this.#store.set(key, value);
23
+ this.#modified = true;
24
+ await this.emit(key, value);
25
+ return this;
26
+ }
27
+
28
+ async delete(key) {
29
+ await this.#store.delete(key);
30
+ this.#modified = true;
31
+ await this.emit(key);
32
+ return this;
33
+ }
34
+
35
+ async clear() {
36
+ await this.#store.clear();
37
+ this.#modified = true;
38
+ await this.emit();
39
+ return this;
40
+ }
41
+
42
+ async keys() { return [...await this.#store.keys()]; }
43
+
44
+ async values() { return [...await this.#store.values()]; }
45
+
46
+ async entries() { return [...await this.#store.entries()]; }
47
+
48
+ async json(arg = null) {
49
+ if (!arguments.length || typeof arg === 'boolean') {
50
+ return Object.fromEntries(await this.#store.entries());
51
+ }
52
+ if (!_isObject(arg)) {
53
+ throw new Error(`Argument must be a valid JSON object`);
54
+ }
55
+ return await Promise.all(Object.entries(arg).map(([key, value]) => {
56
+ return this.set(key, value);
57
+ }));
58
+ }
59
+
60
+ async forEach(callback) { (await this.entries()).forEach(([key, value], i) => callback(value, key, i)); }
61
+
62
+ [ Symbol.iterator ]() { return this.entries().then((entries) => entries[ Symbol.iterator ]()); }
63
+
64
+ get size() { return this.#store.sizs; }
65
+
66
+ async commit() {
67
+ this.#modified = false;
68
+ }
69
+
70
+ #listeners = new Set;
71
+ observe(attr, handler) {
72
+ const args = { attr, handler };
73
+ this.#listeners.add(args);
74
+ return () => {
75
+ this.#listeners.delete(args);
76
+ }
77
+ }
78
+
79
+ async emit(attr, value) {
80
+ const returnValues = [];
81
+ for (const { attr: $attr, handler } of this.#listeners) {
82
+ if (arguments.length && $attr !== attr) continue;
83
+ if (arguments.length > 1) {
84
+ returnValues.push(handler(value));
85
+ } else {
86
+ returnValues.push(handler());
87
+ }
88
+ }
89
+ return Promise.all(returnValues);
90
+ }
91
+
92
+ #handlers = new Map;
93
+ defineHandler(attr, ...handlers) {
94
+ const $handlers = [];
95
+ for (let handler of handlers) {
96
+ if (typeof handler === 'function') {
97
+ handler = { callback: handler };
98
+ } else if (typeof handler === 'string') {
99
+ handler = { url: handler };
100
+ } else if (typeof handler?.callback !== 'function' && typeof handler?.url !== 'string') {
101
+ 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)`);
102
+ }
103
+ $handlers.push(handler);
104
+ }
105
+ this.#handlers.set(attr, $handlers);
106
+ }
107
+
108
+ getHandlers() { return this.#handlers; }
109
+
110
+ async require(attrs, callback = null, noNulls = false) {
111
+ const entries = [];
112
+ main: for await (const attr of [].concat(attrs)) {
113
+ if (!(await this.has(attr)) || (noNulls && [undefined, null].includes(await this.get(attr)))) {
114
+ const handlers = this.#handlers.get(attr);
115
+ if (!handlers) {
116
+ throw new Error(`No handler defined for the user attribute: ${attr}`);
117
+ }
118
+ for (let i = 0; i < handlers.length; i ++) {
119
+ const handler = handlers[i];
120
+ if (handler.callback) {
121
+ const returnValue = await handler.callback(this, attr);
122
+ if (returnValue instanceof Response) {
123
+ return returnValue;
124
+ }
125
+ if ((typeof returnValue === 'undefined' || (noNulls && returnValue === null)) && i < handlers.length - 1) {
126
+ continue;
127
+ }
128
+ entries.push(returnValue);
129
+ continue main;
130
+ }
131
+ const urlRewrite = new URL(handler.url, this.#request.url);
132
+ if (!urlRewrite.searchParams.has('success-redirect')) {
133
+ urlRewrite.searchParams.set('success-redirect', this.#request.url.replace(urlRewrite.origin, ''));
134
+ }
135
+ if (handler.message) {
136
+ if (!this.#session) {
137
+ throw new Error('Storage type does not support redirect messages');
138
+ }
139
+ const messageID = (0 | Math.random() * 9e6).toString(36);
140
+ urlRewrite.searchParams.set('redirect-message', messageID);
141
+ await this.#session.set(`redirect-message:${messageID}`, { status: { type: handler.type || 'info', message: handler.message }});
142
+ }
143
+ return new Response(null, { status: 302, headers: {
144
+ Location: urlRewrite
145
+ }});
146
+ }
147
+ }
148
+ entries.push(await this.get(attr));
149
+ }
150
+ if (callback) return await callback(...entries);
151
+ return entries;
152
+ }
153
+ }
@@ -0,0 +1,54 @@
1
+ import { HttpState } from './HttpState.js';
2
+
3
+ export class HttpUser extends HttpState {
4
+
5
+ static create({ store, request, realtime, session }) {
6
+ return new this({ store, request, realtime, session });
7
+ }
8
+
9
+ #realtime;
10
+
11
+ constructor({ store, request, realtime, session }) {
12
+ super({
13
+ store,
14
+ request,
15
+ session
16
+ });
17
+ this.#realtime = realtime;
18
+ }
19
+
20
+ async isSignedIn() {
21
+ return await this.has('id');
22
+ }
23
+
24
+ async signIn(...args) {
25
+ return await this.require(
26
+ ['id'].concat(typeof args[0] === 'string' || Array.isArray(args[0]) ? args.unshift() : []),
27
+ ...args
28
+ );
29
+ }
30
+
31
+ async signOut() {
32
+ await this.clear();
33
+ }
34
+
35
+ async confirm(data, callback, options = {}) {
36
+ return await new Promise((resolve) => {
37
+ this.#realtime.postRequest(
38
+ data,
39
+ (event) => resolve(callback ? callback(event) : event),
40
+ { ...options, wqEventOptions: { type: 'confirm' } }
41
+ );
42
+ });
43
+ }
44
+
45
+ async prompt(data, callback, options = {}) {
46
+ return await new Promise((resolve) => {
47
+ this.#realtime.postRequest(
48
+ data,
49
+ (event) => resolve(callback ? callback(event) : event),
50
+ { ...options, wqEventOptions: { type: 'prompt' } }
51
+ );
52
+ });
53
+ }
54
+ }
@@ -0,0 +1,245 @@
1
+ import { State } from '@webqit/quantum-js';
2
+ import { _isFunction, _isArray, _isObject } from '@webqit/util/js/index.js';
3
+ import { _from as _arrFrom } from '@webqit/util/arr/index.js';
4
+ import { path as Path } from '../webflo-url/util.js';
5
+ import { isGenerator } from '../webflo-fetch/LiveResponse.js';
6
+
7
+ export class WebfloRouter {
8
+
9
+ #runtime;
10
+ #path;
11
+
12
+ constructor(runtime, path = []) {
13
+ this.#runtime = runtime;
14
+ this.#path = _isArray(path) ? path : (path + '').split('/').filter(a => a);
15
+ }
16
+
17
+ async route(method, event, _default = null, remoteFetch = null) {
18
+ const $this = this;
19
+ // ----------------
20
+ // The loop
21
+ // ----------------
22
+ const next = async function (thisTick) {
23
+ const thisContext = {};
24
+ if (!thisTick.trail || thisTick.trail.length < thisTick.destination.length) {
25
+ thisTick = await $this.readTick(thisTick);
26
+ // -------------
27
+ thisContext.pathname = `/${thisTick.trail.join('/')}`;
28
+ thisContext.stepname = thisTick.trail[thisTick.trail.length - 1];
29
+ $this.finalizeHandlerContext(thisContext, thisTick);
30
+ // -------------
31
+ if (!thisTick.exports) {
32
+ if ((thisTick.currentSegmentOnFile || {}).dirExists) {
33
+ // Exports not found but directory found
34
+ return next(thisTick);
35
+ }
36
+ // Exports not found and directory not found
37
+ if (_default) {
38
+ return await _default.call(thisContext, thisTick.event, remoteFetch);
39
+ }
40
+ return;
41
+ }
42
+ // -------------
43
+ // Broadcast any hints exported by handler
44
+ //@obsolete if (thisTick.exports.hints) { await event.port.post({ ...thisTick.exports.hints, $type: 'handler:hints' }); }
45
+ const methods = _arrFrom(thisTick.method).map(m => m === 'default' ? m : m.toUpperCase());
46
+ const handler = _isFunction(thisTick.exports) && methods.includes('default')
47
+ ? thisTick.exports
48
+ : methods.reduce((_handler, name) => _handler || thisTick.exports[name], null);
49
+ if (!handler) {
50
+ // Handler not found but exports found
51
+ return next(thisTick);
52
+ }
53
+ // -------------
54
+ // Handler found
55
+ // -------------
56
+ const go = async (isFetch, ...args) => {
57
+ const nextTick = { ...thisTick };
58
+ let asResponse = false;
59
+
60
+ if (args.length) {
61
+
62
+ // Obtain URL
63
+ let url = args[0] instanceof Request ? args[0].url : (
64
+ !isFetch && _isObject(args[0]) ? (
65
+ args[0].redirect ?? thisTick.destination.slice(thisTick.trail.length).join('/')
66
+ ) : args[0]
67
+ );
68
+ let urlStr_isRelative = false;
69
+ let urlStr_resolved;
70
+
71
+ // Normalize URL
72
+ if (url instanceof URL) {
73
+ urlStr_resolved = url.href;
74
+ } else if (typeof url === 'string') {
75
+ urlStr_resolved = url.trim();
76
+ if (!/^http(s)?\:/i.test(urlStr_resolved) && !urlStr_resolved.startsWith('/')) {
77
+ urlStr_resolved = $this.pathJoin(`/${thisTick.trail.join('/')}`, urlStr_resolved);
78
+ if (urlStr_resolved.startsWith('../')) {
79
+ throw new Error('Router redirect cannot traverse beyond the routing directory! (' + url + ' >> ' + urlStr_resolved + ')');
80
+ }
81
+ urlStr_isRelative = true;
82
+ }
83
+ url = new URL(urlStr_resolved, thisTick.event.url.origin);
84
+ } else {
85
+ throw new Error('Router redirect url must be instance of URL or a string.');
86
+ }
87
+
88
+ // Handle absolute URLs
89
+ if (url.origin !== thisTick.event.url.origin) {
90
+ if (!isFetch) {
91
+ throw new Error('The next() function cannot make outbound calls!');
92
+ }
93
+ const result = await remoteFetch(...args);
94
+ if (asResponse) {
95
+ }
96
+ return result;
97
+ }
98
+
99
+ // Build request inheritance chain
100
+ const requestInheritanceChain = [url];
101
+ if (!isFetch && thisTick.event.request instanceof Request) {
102
+ const { url: _, ...init } = await Request.copy(thisTick.event.request);
103
+ requestInheritanceChain.push(init);
104
+ }
105
+ const noArg2 = () => {
106
+ throw new Error('Invalid argument #2.');
107
+ };
108
+ if (args[0] instanceof Request) {
109
+ if (args[1]) noArg2();
110
+ const { url: _, ...init } = await Request.copy(args[0]);
111
+ requestInheritanceChain.push(init);
112
+ } else if (!isFetch && _isObject(args[0])) {
113
+ if (args[1]) noArg2();
114
+ if (_isObject(args[0].with)) {
115
+ requestInheritanceChain.push(args[0].with);
116
+ }
117
+ asResponse = args[0].live;
118
+ } else if (args[1] instanceof Request) {
119
+ requestInheritanceChain.push(args[1]);
120
+ } else if (_isObject(args[1])) {
121
+ const { live, ...init } = args[1];
122
+ requestInheritanceChain.push(init);
123
+ asResponse = live;
124
+ }
125
+
126
+ // Compose new event parameters
127
+ const request = requestInheritanceChain.reduce((prev, curr = {}) => new Request(prev, curr), requestInheritanceChain.shift());
128
+
129
+ // Set context parameters
130
+ nextTick.method = request.method;
131
+ nextTick.event = await thisTick.event.extend({ request });
132
+ nextTick.source = thisTick.destination.join('/');
133
+ nextTick.destination = url.pathname.split('/').map((a) => a.trim()).filter((a) => a);
134
+ nextTick.trail = !urlStr_isRelative ? [] : thisTick.trail.reduce((_commonRoot, _seg, i) => _commonRoot.length === i && _seg === nextTick.destination[i] ? _commonRoot.concat(_seg) : _commonRoot, []);
135
+ nextTick.trailOnFile = thisTick.trailOnFile.slice(0, nextTick.trail.length);
136
+ } else {
137
+ if (isFetch) {
138
+ throw new Error('fetch() cannot be called without arguments!');
139
+ }
140
+ nextTick.event = thisTick.event.extend();
141
+ }
142
+ const result = await next(nextTick);
143
+ if (asResponse) {
144
+ }
145
+ return result;
146
+ };
147
+
148
+ // Prepare handler parameters
149
+ const nextPathname = thisTick.destination.slice(thisTick.trail.length);
150
+ const $next = async (...args) => await go(false, ...args);
151
+ $next.pathname = nextPathname.join('/');
152
+ $next.stepname = nextPathname[0];
153
+ const $fetch = async (...args) => await go(true, ...args);
154
+
155
+ // Dispatch to handler
156
+ return new Promise(async (resolve) => {
157
+ let resolved = 0;
158
+
159
+ // Monitor first respondWith()
160
+ thisTick.event.internalLiveResponse.addEventListener('replace', () => {
161
+ if (!resolved) {
162
+ resolved = 1;
163
+ resolve(thisTick.event.internalLiveResponse);
164
+ } else if (resolved === 2) {
165
+ throw new Error(`Unexpected respondWith() after handler return.`);
166
+ }
167
+ });
168
+
169
+ // Call the handler
170
+ const returnValue = await handler.call(thisContext, thisTick.event, $next/*next*/, $fetch/*fetch*/);
171
+
172
+ // Handle cleanup on abort
173
+ if (returnValue instanceof State) {
174
+ thisTick.event.signal.addEventListener('abort', () => {
175
+ returnValue.dispose();
176
+ });
177
+ } else if (isGenerator(returnValue)) {
178
+ thisTick.event.signal.addEventListener('abort', () => {
179
+ if (typeof returnValue.return === 'function') {
180
+ returnValue.return();
181
+ }
182
+ });
183
+ }
184
+
185
+ // Handle return value
186
+ if (!resolved) {
187
+ resolved = 2;
188
+ resolve(returnValue);
189
+ } else if (typeof returnValue !== 'undefined') {
190
+ await thisTick.event.internalLiveResponse.replaceWith(returnValue, { done: true });
191
+ }
192
+ });
193
+ }
194
+ if (_default) {
195
+ return await _default.call(thisContext, thisTick.event, remoteFetch);
196
+ }
197
+ };
198
+
199
+ return next({
200
+ destination: this.#path,
201
+ event,
202
+ method
203
+ });
204
+
205
+ }
206
+
207
+ async readTick(thisTick) {
208
+ thisTick = { ...thisTick };
209
+ var routeTree = this.#runtime.routes;
210
+ var routePaths = Object.keys(this.#runtime.routes);
211
+ if (thisTick.trail) {
212
+ thisTick.currentSegment = thisTick.destination[thisTick.trail.length];
213
+ thisTick.currentSegmentOnFile = [thisTick.currentSegment, '-'].reduce((_segmentOnFile, _seg) => {
214
+ if (_segmentOnFile.handler) return _segmentOnFile;
215
+ var _currentPath = `/${thisTick.trailOnFile.concat(_seg).join('/')}`;
216
+ return routeTree[_currentPath] ? { seg: _seg, handler: _currentPath } : (
217
+ routePaths.filter((p) => p.startsWith(`${_currentPath}/`)).length ? { seg: _seg, dirExists: true } : _segmentOnFile
218
+ );
219
+ }, { seg: null });
220
+ thisTick.trail = thisTick.trail.concat(thisTick.currentSegment);
221
+ thisTick.trailOnFile = thisTick.trailOnFile.concat(thisTick.currentSegmentOnFile.seg);
222
+ thisTick.exports = routeTree[thisTick.currentSegmentOnFile.handler];
223
+ } else {
224
+ thisTick.trail = [];
225
+ thisTick.trailOnFile = [];
226
+ thisTick.currentSegmentOnFile = { handler: '/' };
227
+ thisTick.exports = routeTree['/'];
228
+ if (!routeTree['/'] && routePaths.length) {
229
+ thisTick.currentSegmentOnFile.dirExists = true;
230
+ }
231
+ }
232
+ if (typeof thisTick.exports === 'string') {
233
+ thisTick.exports = await import(thisTick.exports);
234
+ }
235
+ return thisTick;
236
+ }
237
+
238
+ finalizeHandlerContext(context, thisTick) {
239
+ return context.dirname = thisTick.currentSegmentOnFile.handler;
240
+ }
241
+
242
+ pathJoin(...args) {
243
+ return Path.join(...args);
244
+ }
245
+ }
@@ -0,0 +1,19 @@
1
+ import { HttpCookies } from '../webflo-routing/HttpCookies.js';
2
+
3
+ export class ServerSideCookies extends HttpCookies {
4
+ static create({ request }) {
5
+ return new this({
6
+ request,
7
+ entries: request.headers.get('Cookie', true).map((c) => [c.name, c])
8
+ });
9
+ }
10
+
11
+ async commit(response = null) {
12
+ if (response) {
13
+ for (const cookieStr of await this.render()) {
14
+ response.headers.append('Set-Cookie', cookieStr);
15
+ }
16
+ }
17
+ await super.commit();
18
+ }
19
+ }
@@ -0,0 +1,38 @@
1
+ import { HttpSession } from '../webflo-routing/HttpSession.js';
2
+
3
+ export class ServerSideSession extends HttpSession {
4
+
5
+ static create({ store, request, sessionID, ttl }) {
6
+ return new this({
7
+ store,
8
+ request,
9
+ sessionID,
10
+ ttl
11
+ });
12
+ }
13
+
14
+ #sessionID;
15
+ get sessionID() { return this.#sessionID; }
16
+ #ttl;
17
+
18
+ constructor({ store, request, sessionID, ttl }) {
19
+ if (!sessionID) {
20
+ throw new Error(`sessionID is required`);
21
+ }
22
+ super({
23
+ store,
24
+ request,
25
+ session: true
26
+ });
27
+ this.#sessionID = sessionID;
28
+ this.#ttl = ttl;
29
+ }
30
+
31
+ async commit(response = null, devMode = false) {
32
+ if (response && !response.headers.get('Set-Cookie', true).find((c) => c.name === '__sessid')) {
33
+ // expires six months
34
+ response.headers.append('Set-Cookie', `__sessid=${this.#sessionID}; Path=/; ${!devMode ? 'Secure; ' : ''}HttpOnly; SameSite=Lax${this.#ttl ? `; Max-Age=${this.#ttl}` : ''}`);
35
+ }
36
+ await super.commit();
37
+ }
38
+ }