@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
@@ -1,558 +0,0 @@
1
-
2
- /**
3
- * @imports
4
- */
5
- import Fs from 'fs';
6
- import Path from 'path';
7
- import Http from 'http';
8
- import Https from 'https';
9
- import Sessions from 'client-sessions';
10
- import Observer from '@webqit/observer';
11
- import { _each } from '@webqit/util/obj/index.js';
12
- import { _isEmpty } from '@webqit/util/js/index.js';
13
- import { _from as _arrFrom, _any } from '@webqit/util/arr/index.js';
14
- import { slice as _streamSlice } from 'stream-slice';
15
- import { Readable as _ReadableStream } from 'stream';
16
- import { pattern } from '../util-url.js';
17
- import xRequest from "../xRequest.js";
18
- import xResponse from "../xResponse.js";
19
- import xfetch from '../xfetch.js';
20
- import HttpEvent from '../HttpEvent.js';
21
- import _Runtime from '../Runtime.js';
22
-
23
- export {
24
- //fetch,
25
- HttpEvent,
26
- Observer,
27
- }
28
-
29
- export default class Runtime extends _Runtime {
30
-
31
- /**
32
- * Runtime
33
- *
34
- * @param Object cx
35
- * @param Function applicationInstance
36
- *
37
- * @return void
38
- */
39
- constructor(cx, applicationInstance) {
40
- super(cx, applicationInstance);
41
- // ---------------
42
- this.ready = (async () => {
43
- // ---------------
44
- const resolveContextObj = async (cx, force = false) => {
45
- if (_isEmpty(cx.layout) || force) { cx.layout = await (new cx.config.deployment.Layout(cx)).read(); }
46
- if (_isEmpty(cx.server) || force) { cx.server = await (new cx.config.runtime.Server(cx)).read(); }
47
- if (_isEmpty(cx.env) || force) { cx.env = await (new cx.config.deployment.Env(cx)).read(); }
48
- };
49
- await resolveContextObj(this.cx);
50
- if (this.cx.env.autoload !== false) {
51
- Object.keys(this.cx.env.entries).forEach(key => {
52
- if (!(key in process.env)) {
53
- process.env[key] = this.cx.env.entries[key];
54
- }
55
- });
56
- }
57
- // ---------------
58
- const parseDomains = domains => _arrFrom(domains).reduce((arr, str) => arr.concat(str.split(',')), []).map(str => str.trim()).filter(str => str);
59
- const selectDomains = (serverDefs, matchingPort = null) => serverDefs.reduce((doms, def) => doms.length ? doms : (((!matchingPort || def.port === matchingPort) && parseDomains(def.domains || def.hostnames)) || []), []);
60
- // ---------------
61
- this.proxied = new Map;
62
- if (this.cx.config.deployment.Proxy) {
63
- const proxied = await (new this.cx.config.deployment.Proxy(this.cx)).read();
64
- await Promise.all((proxied.entries || []).map(async vhost => {
65
- let cx, hostnames = parseDomains(vhost.hostnames), port = vhost.port, proto = vhost.proto;
66
- if (vhost.path) {
67
- cx = this.cx.constructor.create(this.cx, Path.join(this.cx.CWD, vhost.path));
68
- await resolveContextObj(cx, true);
69
- cx.dict.key = true;
70
- // From the server that's most likely to be active
71
- port || (port = cx.server.https.port || cx.server.port);
72
- // The domain list that corresponds to the specified resolved port
73
- hostnames.length || (hostnames = selectDomains([cx.server.https, cx.server], port));
74
- // Or anyone available... hoping that the remote configs can eventually be in sync
75
- hostnames.length || (hostnames = selectDomains([cx.server.https, cx.server]));
76
- // The corresponding proto
77
- proto || (proto = port === cx.server.https.port ? 'https' : 'http');
78
- }
79
- hostnames.length || (hostnames = ['*']);
80
- this.proxied.set(hostnames.sort().join('|'), { cx, hostnames, port, proto });
81
- }));
82
- }
83
- // ---------------
84
- this.servers = new Map;
85
- // ---------------
86
- if (!this.cx.flags['test-only'] && !this.cx.flags['https-only'] && this.cx.server.port) {
87
- const httpServer = Http.createServer((request, response) => handleRequest('http', request, response));
88
- httpServer.listen(this.cx.server.port);
89
- // -------
90
- let domains = parseDomains(this.cx.server.domains);
91
- if (!domains.length) { domains = ['*']; }
92
- this.servers.set('http', {
93
- instance: httpServer,
94
- port: this.cx.server.port,
95
- domains,
96
- });
97
- }
98
- // ---------------
99
- if (!this.cx.flags['test-only'] && !this.cx.flags['http-only'] && this.cx.server.https.port) {
100
- const httpsServer = Https.createServer((request, response) => handleRequest('https', request, response));
101
- httpsServer.listen(this.cx.server.https.port);
102
- // -------
103
- const addSSLContext = (serverConfig, domains) => {
104
- if (!Fs.existsSync(serverConfig.https.keyfile)) return;
105
- const cert = {
106
- key: Fs.readFileSync(serverConfig.https.keyfile),
107
- cert: Fs.readFileSync(serverConfig.https.certfile),
108
- };
109
- domains.forEach(domain => { httpsServer.addContext(domain, cert); });
110
- }
111
- // -------
112
- let domains = parseDomains(this.cx.server.https.domains);
113
- if (!domains.length) { domains = ['*']; }
114
- this.servers.set('https', {
115
- instance: httpsServer,
116
- port: this.cx.server.https.port,
117
- domains,
118
- });
119
- // -------
120
- addSSLContext(this.cx.server, domains);
121
- for (const [ /*id*/, vhost ] of this.proxied) {
122
- vhost.cx && addSSLContext(vhost.cx.server, vhost.hostnames);
123
- }
124
- }
125
- // ---------------
126
- const handleRequest = async (proto, request, response) => {
127
- request[Symbol.toStringTag] = 'ReadableStream';
128
- const [ fullUrl, requestInit ] = await this.parseNodeRequest(proto, request);
129
- let clientResponse = await this.go(fullUrl, requestInit, { request, response });
130
- if (response.headersSent) return;
131
- // --------
132
- _each(clientResponse.headers.json(), (name, value) => {
133
- response.setHeader(name, value);
134
- });
135
- // --------
136
- response.statusCode = clientResponse.status;
137
- response.statusMessage = clientResponse.statusText;
138
- if (clientResponse.headers.location) {
139
- return response.end();
140
- }
141
- if ((clientResponse.body instanceof _ReadableStream)) {
142
- return clientResponse.body.pipe(response);
143
- }
144
- if ((clientResponse.body instanceof ReadableStream)) {
145
- return _ReadableStream.from(clientResponse.body).pipe(response);
146
- }
147
- let body = clientResponse.body;
148
- if (clientResponse.headers.contentType === 'application/json') {
149
- body += '';
150
- }
151
- return response.end(body);
152
- };
153
- })();
154
-
155
- // ---------------
156
- Observer.set(this, 'location', {});
157
- Observer.set(this, 'network', {});
158
- // ---------------
159
-
160
- // -------------
161
- // Initialize
162
- (async () => {
163
- await this.ready;
164
- if (this.cx.logger) {
165
- if (this.servers.size) {
166
- this.cx.logger.info(`> Server running! (${this.cx.app.title || ''})`);
167
- for (let [ proto, def ] of this.servers) {
168
- this.cx.logger.info(`> ${ proto.toUpperCase() } / ${ def.domains.concat('').join(`:${ def.port } / `)}`);
169
- }
170
- } else {
171
- this.cx.logger.info(`> Server not running! No port specified.`);
172
- }
173
- if (this.proxied.size) {
174
- this.cx.logger.info(`> Reverse proxy active.`);
175
- for (let [ id, def ] of this.proxied) {
176
- this.cx.logger.info(`> ${ id } >>> ${ def.port }`);
177
- }
178
- }
179
- this.cx.logger.info(``);
180
- }
181
- if (this.app && this.app.init) {
182
- const request = this.generateRequest('http://localhost/');
183
- const httpEvent = new HttpEvent(request, { srcType: 'initialization' }, (id = 'session', options = { duration: 60 * 60 * 24, activeDuration: 60 * 60 * 24 }, callback = null) => {
184
- return this.getSession(httpEvent, id, options, callback);
185
- });
186
- await this.app.init(httpEvent, ( ...args ) => this.remoteFetch( ...args ));
187
- }
188
- })();
189
- // ---------------
190
-
191
- // ---------------
192
- this.mockSessionStore = {};
193
- // ---------------
194
-
195
- }
196
-
197
- /**
198
- * Parse Nodejs's IncomingMessage to WHATWAG request params.
199
- *
200
- * @param String proto
201
- * @param Http.IncomingMessage request
202
- *
203
- * @return Array
204
- */
205
- async parseNodeRequest(proto, request) {
206
- // Detected when using manual proxy setting in a browser
207
- if (request.url.startsWith(`http://${ request.headers.host }`) || request.url.startsWith(`https://${ request.headers.host }`)) {
208
- request.url = request.url.split(request.headers.host)[1];
209
- }
210
- const fullUrl = proto + '://' + request.headers.host + request.url;
211
- const requestInit = { method: request.method, headers: request.headers };
212
- if (!['GET', 'HEAD'].includes(request.method)) {
213
- requestInit.body = request;
214
- requestInit.duplex = 'half'; // See https://github.com/nodejs/node/issues/46221
215
- }
216
- return [ fullUrl, requestInit ];
217
- }
218
-
219
- /**
220
- * Performs a request.
221
- *
222
- * @param object|string url
223
- * @param object|Request init
224
- * @param object detail
225
- *
226
- * @return Response
227
- */
228
- async go(url, init = {}, detail = {}) {
229
- await this.ready;
230
-
231
- // ------------
232
- url = typeof url === 'string' ? new URL(url) : url;
233
- if (!(init instanceof Request) && !init.referrer) {
234
- init = { referrer: this.location.href, ...init };
235
- }
236
- // ------------
237
- const hosts = [];
238
- this.servers.forEach(server => hosts.push(...server.domains));
239
- // ------------
240
- for (const [ /*id*/, vhost ] of this.proxied) {
241
- if (vhost.hostnames.includes(url.hostname) || (vhost.hostnames.includes('*') && !hosts.includes('*'))) {
242
- return this.proxyGo(vhost, url, init);
243
- }
244
- }
245
- // ------------
246
- let exit, exitMessage;
247
- if (!hosts.includes(url.hostname) && !hosts.includes('*')) {
248
- exit = { status: 500 }, exitMessage = 'Unrecognized host';
249
- } else if (url.protocol === 'http:' && this.cx.server.https.force) {
250
- exit = { status: 302, headers: { Location: ( url.protocol = 'https:', url.href ) } };
251
- } else if (url.hostname.startsWith('www.') && this.cx.server.force_www === 'remove') {
252
- exit = { status: 302, headers: { Location: ( url.hostname = url.hostname.substr(4), url.href ) } };
253
- } else if (!url.hostname.startsWith('www.') && this.cx.server.force_www === 'add') {
254
- exit = { status: 302, headers: { Location: ( url.hostname = `www.${ url.hostname }`, url.href ) } };
255
- } else if (this.cx.config.runtime.server.Redirects) {
256
- exit = ((await (new this.cx.config.runtime.server.Redirects(this.cx)).read()).entries || []).reduce((_rdr, entry) => {
257
- return _rdr || ((_rdr = pattern(entry.from, url.origin).exec(url.href)) && { status: entry.code || 302, headers: { Location: _rdr.render(entry.to) } });
258
- }, null);
259
- }
260
- if (exit) { return new xResponse(exitMessage, exit); }
261
- // ------------
262
-
263
- // ------------
264
- Observer.set(this.location, url, { detail: { init, ...detail, } });
265
- Observer.set(this.network, 'error', null, { diff: true });
266
- // ------------
267
-
268
- // ------------
269
- // Automatically-added headers
270
- const autoHeaders = this.cx.config.runtime.server.Headers
271
- ? ((await (new this.cx.config.runtime.server.Headers(this.cx)).read()).entries || []).filter(entry => pattern(entry.url, url.origin).exec(url.href))
272
- : [];
273
- // The request object
274
- const request = this.generateRequest(url.href, init, autoHeaders.filter(header => header.type === 'request'));
275
- // The navigation event
276
- const httpEvent = new HttpEvent(request, detail, (id = 'session', options = { duration: 60 * 60 * 24, activeDuration: 60 * 60 * 24 }, callback = null) => {
277
- return this.getSession(httpEvent, id, options, callback);
278
- });
279
- // Response
280
- let response;
281
- try {
282
- response = await this.app.handle(httpEvent, ( ...args ) => this.remoteFetch( ...args ));
283
- if (!response && response !== 0) { response = new xResponse(null, { status: 404 }); }
284
- else if (!(response instanceof xResponse)) { response = xResponse.compat(response); }
285
- if (!(httpEvent.detail.request && httpEvent.detail.response)) {
286
- for (let cookieName of Object.getOwnPropertyNames(this.mockSessionStore)) {
287
- response.headers.append('Set-Cookie', `${cookieName}=1`); // We just want to know availability... not validity, as this is understood to be for testing purposes only
288
- }
289
- }
290
- response = await this.encodeRedirect(httpEvent, response, async () => {
291
- if (httpEvent.request.headers.accept.match('text/html') && this.app.render && !response.attrs.static) {
292
- let rendering;
293
- if (response.ok && response.meta.type === 'json' && typeof response.meta.body === 'object' && response.meta.body) {
294
- rendering = await this.app.render(httpEvent, response);
295
- } else if (!response.ok) {
296
- if ([404, 500].includes(response.status)) {
297
- Observer.set(this.network, 'error', new Error(response.statusText, { cause: response.status }));
298
- }
299
- rendering = await this.app.render(httpEvent, response);
300
- }
301
- if (typeof rendering !== 'string' && !(typeof rendering === 'object' && rendering && typeof rendering.toString === 'function')) {
302
- throw new Error('render() must return a string response or an object that implements toString()..');
303
- }
304
- rendering = rendering.toString();
305
- response = new httpEvent.Response(rendering, {
306
- headers: { ...response.headers.json(), contentType: 'text/html', contentLength: (new Blob([rendering]).size) },
307
- status: response.status,
308
- });
309
- }
310
- return this.handleResponse2(httpEvent, autoHeaders.filter(header => header.type === 'response'), response);
311
- });
312
- } catch(e) {
313
- response = new xResponse(e.message, { status: 500 });
314
- console.error(e);
315
- }
316
- // Logging
317
- if (this.cx.logger) {
318
- const log = this.generateLog(httpEvent.request, response);
319
- this.cx.logger.log(log);
320
- }
321
- // ------------
322
- // Return value
323
- return response;
324
- }
325
-
326
- // Fetch from proxied host
327
- async proxyGo(vhost, url, init) {
328
- // ---------
329
- const url2 = new URL(url);
330
- url2.port = vhost.port;
331
- if (vhost.proto) { url2.protocol = vhost.proto; }
332
- // ---------
333
- let init2;
334
- if (init instanceof Request) {
335
- init2 = init.clone();
336
- init.headers.set('Host', url2.host);
337
- } else {
338
- init2 = { ...init, decompress: false/* honoured in xfetch() */ };
339
- if (!init2.headers) init2.headers = {};
340
- init2.headers.host = url2.host;
341
- delete init2.headers.connection;
342
- }
343
- // ---------
344
- let response;
345
- try {
346
- response = await this.remoteFetch(url2, init2);
347
- } catch(e) {
348
- response = new xResponse(`Reverse Proxy Error: ${e.message}`, { status: 500 });
349
- console.error(e);
350
- }
351
- if (this.cx.logger) {
352
- const log = this.generateLog({ url: url2.href, ...init2 }, response, true);
353
- this.cx.logger.log(log);
354
- }
355
- return response;
356
-
357
- }
358
-
359
-
360
- // Generates request object
361
- generateRequest(href, init = {}, autoHeaders = []) {
362
- const request = new xRequest(href, init);
363
- this._autoHeaders(request.headers, autoHeaders);
364
- return request;
365
- }
366
-
367
- // Generates session object
368
- getSession(e, id, options = {}, callback = null) {
369
- let baseObject;
370
- if (!(e.detail.request && e.detail.response)) {
371
- baseObject = this.mockSessionStore;
372
- let cookieAvailability = e.request.headers.cookies.get(id); // We just want to know availability... not validity, as this is understood to be for testing purposes only
373
- if (!(this.mockSessionStore[id] && cookieAvailability)) {
374
- let cookieObj = {};
375
- Object.defineProperty(this.mockSessionStore, id, {
376
- get: () => cookieObj,
377
- set: value => (cookieObj = value),
378
- });
379
- }
380
- } else {
381
- Sessions({
382
- duration: 0, // how long the session will stay valid in ms
383
- activeDuration: 0, // if expiresIn < activeDuration, the session will be extended by activeDuration milliseconds
384
- ...options,
385
- cookieName: id, // cookie name dictates the key name added to the request object
386
- secret: this.cx.env.SESSION_KEY, // should be a large unguessable string
387
- })(e.detail.request, e.detail.response, e => {
388
- if (e) {
389
- if (!callback) throw e;
390
- callback(e);
391
- }
392
- });
393
- baseObject = e.detail.request;
394
- }
395
- // Where theres no error, instance is available
396
- let instance = Object.getOwnPropertyDescriptor(baseObject, id);
397
- if (!callback) return instance;
398
- if (instance) callback(null, instance);
399
- }
400
-
401
- // Initiates remote fetch and sets the status
402
- remoteFetch(request, ...args) {
403
- let href = request;
404
- if (request instanceof Request) {
405
- href = request.url;
406
- } else if (request instanceof URL) {
407
- href = request.href;
408
- }
409
- Observer.set(this.network, 'remote', href, {diff: true});
410
- const _response = xfetch(request, ...args);
411
- // Save a reference to this
412
- return _response.then(async response => {
413
- // Stop loading status
414
- Observer.set(this.network, 'remote', false, {diff: true});
415
- return xResponse.compat(response);
416
- });
417
- }
418
-
419
- // Handles response object
420
- encodeRedirect(httpEvent, response, callback) {
421
- // Redirects
422
- if (response.headers.location) {
423
- const xRedirectPolicy = httpEvent.request.headers.get('X-Redirect-Policy');
424
- const xRedirectCode = httpEvent.request.headers.get('X-Redirect-Code') || 300;
425
- const destinationUrl = new URL(response.headers.location, httpEvent.url.origin);
426
- const isSameOriginRedirect = destinationUrl.origin === httpEvent.url.origin;
427
- let isSameSpaRedirect, sparootsFile = Path.join(this.cx.CWD, this.cx.layout.PUBLIC_DIR, 'sparoots.json');
428
- if (isSameOriginRedirect && xRedirectPolicy === 'manual-when-cross-spa' && Fs.existsSync(sparootsFile)) {
429
- // Longest-first sorting
430
- const sparoots = _arrFrom(JSON.parse(Fs.readFileSync(sparootsFile))).sort((a, b) => a.length > b.length ? -1 : 1);
431
- const matchRoot = path => sparoots.reduce((prev, root) => prev || (`${path}/`.startsWith(`${root}/`) && root), null);
432
- isSameSpaRedirect = matchRoot(destinationUrl.pathname) === matchRoot(httpEvent.url.pathname);
433
- }
434
- if (xRedirectPolicy === 'manual' || (!isSameOriginRedirect && xRedirectPolicy === 'manual-when-cross-origin') || (!isSameSpaRedirect && xRedirectPolicy === 'manual-when-cross-spa')) {
435
- response.headers.json({
436
- 'X-Redirect-Code': response.status,
437
- 'Access-Control-Allow-Origin': '*',
438
- 'Cache-Control': 'no-store',
439
- });
440
- response.attrs.status = xRedirectCode;
441
- }
442
- return response;
443
- }
444
- return callback();
445
- }
446
-
447
- // Handles response object
448
- async handleResponse2(httpEvent, autoHeaders, response) {
449
- // Not acceptable
450
- if (response.headers.contentType && httpEvent.request.headers.get('Accept') && !httpEvent.request.headers.accept.match(response.headers.contentType)) {
451
- return new xResponse(response.body, { status: 406, headers: response.headers });
452
- }
453
- // Auto-Headers
454
- this._autoHeaders(response.headers, autoHeaders);
455
- // Important no-caching
456
- // for non-"get" requests
457
- if (httpEvent.request.method !== 'GET' && !response.headers.get('Cache-Control')) {
458
- response.headers.set('Cache-Control', 'no-store');
459
- }
460
- // Body
461
- response.headers.set('Accept-Ranges', 'bytes');
462
- let rangeRequest, body = response.body;
463
- if ((rangeRequest = httpEvent.request.headers.range) && !response.headers.get('Content-Range')
464
- && ((body instanceof ReadableStream) || (ArrayBuffer.isView(body) && (body = _ReadableStream.from(body))))) {
465
- // ...in partials
466
- const totalLength = response.headers.contentLength || 0;
467
- const ranges = await rangeRequest.reduce(async (_ranges, range) => {
468
- _ranges = await _ranges;
469
- if (range[0] < 0 || (totalLength && range[0] > totalLength)
470
- || (range[1] > -1 && (range[1] <= range[0] || (totalLength && range[1] >= totalLength)))) {
471
- // The range is beyond upper/lower limits
472
- _ranges.error = true;
473
- }
474
- if (!totalLength && range[0] === undefined) {
475
- // totalLength is unknown and we cant read the trailing size specified in range[1]
476
- _ranges.error = true;
477
- }
478
- if (_ranges.error) return _ranges;
479
- if (totalLength) { range.clamp(totalLength); }
480
- const partLength = range[1] - range[0] + 1;
481
- _ranges.parts.push({
482
- body: body.pipe(_streamSlice(range[0], range[1] + 1)),
483
- range: range = `bytes ${range[0]}-${range[1]}/${totalLength || '*'}`,
484
- length: partLength,
485
- });
486
- _ranges.totalLength += partLength;
487
- return _ranges;
488
- }, { parts: [], totalLength: 0 });
489
- if (ranges.error) {
490
- response.attrs.status = 416;
491
- response.headers.json({
492
- 'Content-Range': `bytes */${totalLength || '*'}`,
493
- 'Content-Length': 0,
494
- });
495
- } else {
496
- // TODO: of ranges.parts is more than one, return multipart/byteranges
497
- response = new xResponse(ranges.parts[0].body, {
498
- status: 206,
499
- statusText: response.statusText,
500
- headers: response.headers,
501
- });
502
- response.headers.json({
503
- 'Content-Range': ranges.parts[0].range,
504
- 'Content-Length': ranges.totalLength,
505
- });
506
- }
507
- }
508
-
509
- return response;
510
- }
511
-
512
- // Generates log
513
- generateLog(request, response, isproxy = false) {
514
- let log = [];
515
- // ---------------
516
- const style = this.cx.logger.style || { keyword: str => str, comment: str => str, url: str => str, val: str => str, err: str => str, };
517
- const errorCode = [ 404, 500 ].includes(response.status) ? response.status : 0;
518
- const xRedirectCode = response.headers.get('X-Redirect-Code');
519
- const redirectCode = xRedirectCode || ((response.status + '').startsWith('3') ? response.status : 0);
520
- const statusCode = xRedirectCode || response.status;
521
- // ---------------
522
- log.push(`[${style.comment((new Date).toUTCString())}]`);
523
- log.push(style.keyword(request.method));
524
- if (isproxy) log.push(style.keyword('>>'));
525
- log.push(style.url(request.url));
526
- if (response.attrs.hint) log.push(`(${style.comment(response.attrs.hint)})`);
527
- const contentInfo = [response.headers.contentType, response.headers.contentLength].filter(x => x);
528
- if (contentInfo.length) log.push(`(${style.comment(contentInfo.join('; '))})`);
529
- if (response.headers.get('Content-Encoding')) log.push(`(${style.comment(response.headers.get('Content-Encoding'))})`);
530
- if (errorCode) log.push(style.err(`${errorCode} ${response.statusText}`));
531
- else log.push(style.val(`${statusCode} ${response.statusText}`));
532
- if (redirectCode) log.push(`- ${style.url(response.headers.location)}`);
533
-
534
- return log.join(' ');
535
- }
536
-
537
- // Applies auto headers
538
- _autoHeaders(headers, autoHeaders) {
539
- autoHeaders.forEach(header => {
540
- var headerName = header.name.toLowerCase(),
541
- headerValue = header.value,
542
- isAppend = headerName.startsWith('+') ? (headerName = headerName.substr(1), true) : false,
543
- isPrepend = headerName.endsWith('+') ? (headerName = headerName.substr(0, headerName.length - 1), true) : false;
544
- if (isAppend || isPrepend) {
545
- headerValue = [ headers.get(headerName) || '' , headerValue].filter(v => v);
546
- headerValue = isPrepend ? headerValue.reverse().join(',') : headerValue.join(',');
547
- }
548
- headers.set(headerName, headerValue);
549
- });
550
- }
551
-
552
- }
553
-
554
- const _streamRead = stream => new Promise(res => {
555
- let data = '';
556
- stream.on('data', chunk => data += chunk);
557
- stream.on('end', () => res(data));
558
- });
@@ -1,21 +0,0 @@
1
-
2
- /**
3
- * @imports
4
- */
5
- import Context from './Context.js';
6
- import Application from './Application.js';
7
- import Runtime from './Runtime.js';
8
-
9
- /**
10
- * @start
11
- */
12
- export async function start(applicationInstance = null) {
13
- const cx = this || {};
14
- const defaultApplicationInstance = _cx => new Application(_cx);
15
- return new Runtime(Context.create(cx), applicationInstance || defaultApplicationInstance);
16
- }
17
-
18
- /**
19
- * @APIS
20
- */
21
- export * as APIS from './Runtime.js';
@@ -1,86 +0,0 @@
1
-
2
- /**
3
- * @imports
4
- */
5
- import { _isString, _isNumeric, _isObject, _isPlainObject, _isArray, _isPlainArray, _isTypeObject, _isNumber, _isBoolean } from '@webqit/util/js/index.js';
6
- import { _before } from '@webqit/util/str/index.js';
7
- import { params } from './util-url.js';
8
-
9
- export function formatMessage(message) {
10
- const headers = (message.headers instanceof Headers) ? [...message.headers.keys()].reduce((_headers, name) => {
11
- return { ..._headers, [name/* lower-cased */]: message.headers.get(name) };
12
- }, {}) : Object.keys(message.headers || {}).reduce((_headers, name) => {
13
- return { ..._headers, [name.toLowerCase()]: message.headers[name] };
14
- }, {});
15
- let body = message.body, type = dataType(message.body);
16
- if ([ 'Blob', 'File' ].includes(type)) {
17
- !headers['content-type'] && (headers['content-type'] = body.type);
18
- !headers['content-length'] && (headers['content-length'] = body.size);
19
- } else if ([ 'Uint8Array', 'Uint16Array', 'Uint32Array', 'ArrayBuffer' ].includes(type)) {
20
- !headers['content-length'] && (headers['content-length'] = body.byteLength);
21
- } else if (type === 'json' && _isTypeObject(body)) {
22
- if (!headers['content-type']) {
23
- const [ _body, isJsonfiable ] = formDatarizeJson(body);
24
- if (isJsonfiable) {
25
- body = JSON.stringify(body, (k, v) => v instanceof Error ? { ...v, message: v.message } : v);
26
- headers['content-type'] = 'application/json';
27
- headers['content-length'] = (new Blob([ body ])).size;
28
- } else {
29
- body = _body;
30
- type = 'FormData';
31
- }
32
- }
33
- } else if (type === 'json') {
34
- !headers['content-length'] && (headers['content-length'] = (body + '').length);
35
- }
36
- return [ body, headers, type ];
37
- }
38
-
39
- export function formDatarizeJson(data = {}, jsonfy = true) {
40
- const formData = new FormData;
41
- let isJsonfiable = true;
42
- params.reduceValue(data, '', (value, contextPath, suggestedKeys = undefined) => {
43
- if (suggestedKeys) {
44
- const isJson = dataType(value) === 'json';
45
- isJsonfiable = isJsonfiable && isJson;
46
- return isJson && suggestedKeys;
47
- }
48
- if (jsonfy && [true, false, null].includes(value)) {
49
- value = new Blob([value], { type: 'application/json' });
50
- }
51
- formData.append(contextPath, value);
52
- });
53
- return [ formData, isJsonfiable ];
54
- }
55
-
56
- export async function jsonfyFormData(formData, jsonfy = true) {
57
- let isJsonfiable = true;
58
- let json;
59
- for (let [ name, value ] of formData.entries()) {
60
- if (!json) { json = _isNumeric(_before(name, '[')) ? [] : {}; }
61
- let type = dataType(value);
62
- if (jsonfy && ['Blob', 'File'].includes(type) && value.type === 'application/json' && [4, 5].includes(value.size)) {
63
- let _value = await value.text();
64
- if (['true', 'false', 'null'].includes(_value)) {
65
- type = 'json';
66
- value = JSON.parse(_value);
67
- }
68
- }
69
- isJsonfiable = isJsonfiable && type === 'json';
70
- params.set(json, name, value);
71
- }
72
- return [ json, isJsonfiable ];
73
- }
74
-
75
- export function dataType(value) {
76
- if (_isString(value) || _isNumber(value) || _isBoolean(value)) return 'json';
77
- if (!_isTypeObject(value)) return;
78
- const toStringTag = value[Symbol.toStringTag];
79
- const type = [
80
- 'Uint8Array', 'Uint16Array', 'Uint32Array', 'ArrayBuffer', 'Blob', 'File', 'FormData', 'Stream', 'ReadableStream'
81
- ].reduce((_toStringTag, type) => _toStringTag || (toStringTag === type ? type : null), null);
82
- if (type) return type;
83
- if ((_isObject(value) && _isPlainObject(value)) || (_isArray(value) && _isPlainArray(value)) || 'toString' in value) {
84
- return 'json';
85
- }
86
- }