@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,437 @@
1
+ import { State, Observer } from '@webqit/quantum-js';
2
+ import { _isObject, _isTypeObject } from '@webqit/util/js/index.js';
3
+ import { publishMutations, applyMutations } from '../webflo-messaging/wq-message-port.js';
4
+ import { responseRealtime } from './response.js';
5
+ import { _wq, _await } from '../../util.js';
6
+ import { isTypeStream } from './util.js';
7
+
8
+ export class LiveResponse extends EventTarget {
9
+
10
+ /* STATIC methods for Input sources */
11
+
12
+ static test(data) {
13
+ if (data instanceof LiveResponse) {
14
+ return 'LiveResponse';
15
+ }
16
+ if (data instanceof Response) {
17
+ return 'Response';
18
+ }
19
+ if (isGenerator(data)) {
20
+ return 'Generator';
21
+ }
22
+ if (data instanceof State) {
23
+ return 'Quantum';
24
+ }
25
+ return 'Default';
26
+ }
27
+
28
+ static from(data, ...args) {
29
+ if (data instanceof LiveResponse) {
30
+ return data.clone(...args);
31
+ }
32
+ if (data instanceof Response) {
33
+ return this.fromResponse(data, ...args);
34
+ }
35
+ if (isGenerator(data)) {
36
+ return this.fromGenerator(data, ...args);
37
+ }
38
+ if (data instanceof State) {
39
+ return this.fromQuantum(data, ...args);
40
+ }
41
+ return new this(data, ...args);
42
+ }
43
+
44
+ static fromResponse(response, options = {}) {
45
+ if (!(response instanceof Response)) {
46
+ throw new Error('Argument must be a Response instance.');
47
+ }
48
+ return response.parse().then((body) => {
49
+ // Instance
50
+ const instance = new this(body, {
51
+ status: response.status,
52
+ statusText: response.statusText,
53
+ headers: response.headers,
54
+ done: false,
55
+ ...options,
56
+ });
57
+ const responseMeta = _wq(response, 'meta');
58
+ _wq(instance).set('meta', responseMeta);
59
+ // Generator binding
60
+ if (response.isLive() === 2) {
61
+ if (_isTypeObject(body) && !isTypeStream(body)) {
62
+ applyMutations.call(response.wqRealtime,
63
+ body,
64
+ response.headers.get('X-Live-Response-Message-ID').trim(),
65
+ { signal: instance.#abortController.signal }
66
+ );
67
+ }
68
+ // Capture subsequent frames?
69
+ response.wqRealtime.addEventListener('response.replace', (e) => {
70
+ const { body, ...options } = e.data;
71
+ instance.#replaceWith(body, options);
72
+ }, { signal: instance.#abortController.signal });
73
+ response.wqRealtime.addEventListener('close', () => {
74
+ instance.#extendLifecycle(Promise.resolve());
75
+ }, { once: true, signal: instance.#abortController.signal });
76
+ }
77
+ // Data props
78
+ instance.#type = response.type;
79
+ instance.#redirected = response.redirected;
80
+ instance.#url = response.url;
81
+ // Lifecycle props
82
+ instance.#generator = response;
83
+ instance.#generatorType = 'Response';
84
+ return instance;
85
+ });
86
+ }
87
+
88
+ static fromGenerator(gen, options = {}) {
89
+ if (!isGenerator(gen)) {
90
+ throw new Error('Argument must be a generator or async generator.');
91
+ }
92
+ const $firstFrame = gen.next();
93
+ const instance = _await($firstFrame, (frame) => {
94
+ return _await(frame.value, (value) => {
95
+ const $options = { done: frame.done, ...options };
96
+ let instance, $$await;
97
+ if (value instanceof Response && frame.done && options.done !== false/* && value.responsesOK*/) {
98
+ return value;
99
+ }
100
+ if (value instanceof LiveResponse) {
101
+ instance = new this;
102
+ const responseMeta = _wq(value, 'meta');
103
+ _wq(instance).set('meta', responseMeta);
104
+ $$await = instance.#replaceWith(value, $options);
105
+ } else {
106
+ instance = this.from/*important*/(value, $options);
107
+ }
108
+ return _await(instance, (instance) => {
109
+ (async function () {
110
+ await $$await;
111
+ instance.#generator = gen;
112
+ instance.#generatorType = 'Generator';
113
+ while (!frame.done && !options.done && !instance.#abortController.signal.aborted) {
114
+ frame = await gen.next();
115
+ value = await frame.value;
116
+ if (!instance.#abortController.signal.aborted) {
117
+ await instance.#replaceWith(value, { done: frame.done });
118
+ }
119
+ }
120
+ })();
121
+ return instance;
122
+ });
123
+ });
124
+ });
125
+ return instance;
126
+ }
127
+
128
+ static fromQuantum(qState, options = {}) {
129
+ if (!(qState instanceof State)) {
130
+ throw new Error('Argument must be a Quantum State instance.');
131
+ }
132
+ const instance = new this(qState.value, { done: false, ...options });
133
+ instance.#generator = qState;
134
+ instance.#generatorType = 'Quantum';
135
+ Observer.observe(
136
+ qState,
137
+ 'value',
138
+ (e) => instance.#replaceWith(e.value),
139
+ { signal: instance.#abortController.signal }
140
+ );
141
+ return instance;
142
+ }
143
+
144
+ #generator = null;
145
+ #generatorType = 'Default';
146
+ get generatorType() { return this.#generatorType; }
147
+
148
+ #abortController = new AbortController;
149
+
150
+ disconnect() {
151
+ // Disconnection from existing generator
152
+ this.#abortController.abort();
153
+ this.#generator = null;
154
+ this.#generatorType = 'Default';
155
+ // and a new signal system
156
+ this.#abortController = new AbortController;
157
+ }
158
+
159
+ /* INSTANCE */
160
+
161
+ constructor(body, ...args) {
162
+ super();
163
+ this.#replaceWith(body, ...args);
164
+ }
165
+
166
+ /* Level 1 props */
167
+
168
+ body = null; // Managed at #replaceWith()
169
+
170
+ #headers = new Headers;
171
+ get headers() { return this.#headers; }
172
+
173
+ #status = 200;
174
+ get status() { return this.#status; }
175
+
176
+ #statusText = '';
177
+ get statusText() { return this.#statusText; }
178
+
179
+ get ok() { return this.#status >= 200 && this.#status < 299; }
180
+
181
+ get bodyUsed() { return false; }
182
+
183
+ /* Level 2 props */
184
+
185
+ #type = 'basic';
186
+ get type() { return this.#type; }
187
+
188
+ #redirected = false;
189
+ get redirected() { return this.#redirected; }
190
+
191
+ #url = null;
192
+ get url() { return this.#url; }
193
+
194
+ /* Level 3 props */
195
+
196
+ get wqRealtime() {
197
+ return responseRealtime.call(this);
198
+ }
199
+
200
+ /* Lifecycle methods */
201
+
202
+ isLive() {
203
+ let liveLevel = (this.headers.get('X-Background-Messaging-Port')?.trim() || _wq(this, 'meta').has('wqRealtime')) && 1 || 0;
204
+ liveLevel += this.headers.get('X-Live-Response-Message-ID')?.trim() && 1 || 0;
205
+ return liveLevel;
206
+ }
207
+
208
+ whileLive(returningThePromise = false) {
209
+ if (returningThePromise) {
210
+ return this.#lifeCycleResolutionPromise;
211
+ }
212
+ return this.#currentFramePromise !== null;
213
+ }
214
+
215
+ #currentFramePromise;
216
+ #lifeCycleResolve;
217
+ #lifeCycleReject;
218
+ #lifeCycleResolutionPromise = new Promise((resolve, reject) => {
219
+ this.#lifeCycleResolve = resolve;
220
+ this.#lifeCycleReject = reject;
221
+ });
222
+
223
+ #extendLifecycle(promise) {
224
+ if (this.#currentFramePromise === null) {
225
+ throw new Error('Event lifecycle already complete.');
226
+ }
227
+ this.#currentFramePromise = promise;
228
+ promise.then((value) => {
229
+ if (this.#currentFramePromise === promise) {
230
+ this.#currentFramePromise = null;
231
+ this.#lifeCycleResolve(value);
232
+ }
233
+ }).catch((e) => {
234
+ if (this.#currentFramePromise === promise) {
235
+ this.#currentFramePromise = null;
236
+ this.#lifeCycleReject(e);
237
+ }
238
+ });
239
+ }
240
+
241
+ async #replaceWith(body, ...args) {
242
+
243
+ if (body instanceof Promise) {
244
+ this.#extendLifecycle(body);
245
+ return await new Promise((resolve, reject) => {
246
+ let aborted = false;
247
+ this.#abortController.signal.addEventListener('abort', () => {
248
+ aborted = true
249
+ resolve();
250
+ });
251
+ body.then(async (resolveData) => {
252
+ resolve();
253
+ if (aborted) return;
254
+ await this.#replaceWith(resolveData, ...args);
255
+ });
256
+ body.catch((e) => reject(e));
257
+ });
258
+ }
259
+
260
+ const options = _isObject(args[0]/* !ORDER 1 */) ? { ...args.shift() } : {};
261
+ const frameClosure = typeof args[0]/* !ORDER 2 */ === 'function' ? args.shift() : null;
262
+
263
+ if ('status' in options) {
264
+ options.status = parseInt(options.status);
265
+ if (options.status < 200 || options.status > 599) {
266
+ throw new Error(`The status provided (${options.status}) is outside the range [200, 599].`);
267
+ }
268
+ }
269
+ if ('statusText' in options) {
270
+ options.statusText = String(options.statusText);
271
+ }
272
+ if (options.headers && !(options.headers instanceof Headers)) {
273
+ options.headers = new Headers(options.headers);
274
+ }
275
+
276
+ const execReplaceWith = (responseLike) => {
277
+ const $body = responseLike.body;
278
+ this.#status = responseLike.status;
279
+ this.#statusText = responseLike.statusText;
280
+ for (const [name] of [/*IMPORTANT*/...this.#headers.entries()]) { // for some reason, some entries not produced when not spread
281
+ this.#headers.delete(name);
282
+ }
283
+ for (const [name, value] of responseLike.headers.entries()) {
284
+ this.#headers.append(name, value);
285
+ }
286
+ this.#type = responseLike.type;
287
+ this.#redirected = responseLike.redirected;
288
+ this.#url = responseLike.url;
289
+ // Must come after all property assignments above because it fires events
290
+ Observer.defineProperty(this, 'body', { get: () => $body, enumerable: true, configurable: true });
291
+ this.dispatchEvent(new Event('replace'));
292
+ };
293
+
294
+ const execReplaceWithResponse = async (response, options) => {
295
+ this.#generator = response;
296
+ this.#generatorType = response instanceof Response ? 'Response' : 'LiveResponse';
297
+ execReplaceWith({
298
+ body: response instanceof Response ? await response.parse() : response.body,
299
+ status: response.status,
300
+ statusText: response.statusText,
301
+ headers: response.headers,
302
+ ...options,
303
+ type: response.type,
304
+ redirected: response.redirected,
305
+ url: response.url,
306
+ });
307
+ if (response instanceof LiveResponse) {
308
+ response.addEventListener('replace', () => execReplaceWith(response), { signal: this.#abortController.signal });
309
+ return await response.whileLive(true);
310
+ }
311
+ return Promise.resolve();
312
+ };
313
+
314
+ const execReplaceWithBody = async (body, options) => {
315
+ execReplaceWith({
316
+ body,
317
+ status: 200,
318
+ statusText: '',
319
+ headers: new Headers,
320
+ ...options,
321
+ type: 'basic',
322
+ redirected: false,
323
+ url: null
324
+ });
325
+ if (frameClosure) {
326
+ const reactiveProxy = _isTypeObject(body) && !isTypeStream(body)
327
+ ? Observer.proxy(body, { chainable: true, membrane: body })
328
+ : body;
329
+ return await frameClosure.call(this, reactiveProxy);
330
+ }
331
+ return Promise.resolve();
332
+ };
333
+
334
+ let donePromise;
335
+ if (body instanceof Response || body instanceof LiveResponse) {
336
+ if (frameClosure) {
337
+ throw new Error('frameClosure unsupported for inputs of type response.');
338
+ }
339
+ donePromise = execReplaceWithResponse(body, options);
340
+ } else {
341
+ donePromise = execReplaceWithBody(body, options);
342
+ }
343
+
344
+ if (options.done === false) {
345
+ this.#extendLifecycle(new Promise(() => { }));
346
+ } else {
347
+ this.#extendLifecycle(donePromise);
348
+ }
349
+
350
+ return await new Promise((resolve, reject) => {
351
+ this.#abortController.signal.addEventListener('abort', resolve);
352
+ donePromise.then(() => resolve());
353
+ donePromise.catch((e) => reject(e));
354
+ });
355
+ }
356
+
357
+ async replaceWith(body, ...args) {
358
+ if (!this.whileLive()) {
359
+ throw new Error(`Instance already "complete".`);
360
+ }
361
+ this.disconnect(); // Disconnect from existing source if any
362
+ await this.#replaceWith(body, ...args);
363
+ }
364
+
365
+ toResponse({ clientRequestRealtime, signal: abortSignal } = {}) {
366
+ const response = Response.from(this.body, {
367
+ status: this.status,
368
+ statusText: this.statusText,
369
+ headers: this.headers,
370
+ });
371
+ const responseMeta = _wq(this, 'meta');
372
+ _wq(response).set('meta', responseMeta);
373
+ if (clientRequestRealtime && this.whileLive()) {
374
+ const liveResponseMessageID = Date.now().toString();
375
+ response.headers.set('X-Live-Response-Message-ID', liveResponseMessageID);
376
+ // Publish mutations
377
+ if (_isTypeObject(this.body) && !isTypeStream(this.body)) {
378
+ publishMutations.call(clientRequestRealtime, this.body, liveResponseMessageID, { signal: abortSignal/* stop observing mutations on body when we abort */ });
379
+ }
380
+ // Publish replacements?
381
+ const replaceHandler = () => {
382
+ const headers = Object.fromEntries([...this.headers.entries()]);
383
+ if (headers?.['set-cookie']) {
384
+ delete headers['set-cookie'];
385
+ console.warn('Warning: The "set-cookie" header is not supported for security reasons and has been removed from the response.');
386
+ }
387
+ clientRequestRealtime.postMessage({
388
+ body: this.body,
389
+ status: this.status,
390
+ statusText: this.statusText,
391
+ headers,
392
+ done: !this.whileLive(),
393
+ }, { wqEventOptions: { type: 'response.replace', live: true/*gracefully ignored if not an object*/ }, observerOptions: { signal: abortSignal/* stop observing mutations on body when we abort */ } });
394
+ };
395
+ this.addEventListener('replace', replaceHandler, { signal: abortSignal/* stop listening when we abort */ });
396
+ }
397
+ return response;
398
+ }
399
+
400
+ async * toGenerator({ signal: abortSignal } = {}) {
401
+ do {
402
+ yield $this.body;
403
+ } while (await new Promise((resolve) => {
404
+ this.addEventListener('replace', () => resolve(true), { once: true, signal: abortSignal });
405
+ this.whileLive(true).then(() => resolve(false));
406
+ }));
407
+ }
408
+
409
+ toQuantum({ signal: abortSignal } = {}) {
410
+ const state = new StateX;
411
+ const replaceHandler = () => Observer.defineProperty(state, 'value', { value: this.body, enumerable: true, configurable: true });
412
+ this.addEventListener('replace', replaceHandler, { signal: abortSignal });
413
+ replaceHandler();
414
+ return state;
415
+ }
416
+
417
+ clone(init = {}) {
418
+ const clone = new this.constructor();
419
+ const responseMeta = _wq(this, 'meta');
420
+ _wq(clone).set('meta', responseMeta);
421
+ clone.replaceWith(this, init);
422
+ return clone;
423
+ }
424
+ }
425
+
426
+ export const isGenerator = (obj) => {
427
+ return typeof obj?.next === 'function' &&
428
+ typeof obj?.throw === 'function' &&
429
+ typeof obj?.return === 'function';
430
+ };
431
+
432
+ class StateX extends State {
433
+ constructor() { }
434
+ dispose() { }
435
+ }
436
+
437
+ globalThis.LiveResponse = LiveResponse;
@@ -0,0 +1,10 @@
1
+ export function renderCookieObjToString(cookieObj) {
2
+ const attrsArr = [`${cookieObj.name}=${/*encodeURIComponent*/(cookieObj.value)}`];
3
+ for (const attrName in cookieObj) {
4
+ if (['name', 'value'].includes(attrName)) continue;
5
+ let _attrName = attrName[0].toUpperCase() + attrName.substring(1);
6
+ if (_attrName === 'MaxAge') { _attrName = 'Max-Age' };
7
+ attrsArr.push(cookieObj[attrName] === true ? _attrName : `${_attrName}=${cookieObj[attrName]}`);
8
+ }
9
+ return attrsArr.join(';');
10
+ }
@@ -0,0 +1,16 @@
1
+ import { renderHttpMessageInit } from './message.js';
2
+
3
+ const nativeFetch = fetch;
4
+ export async function fetch(url, init = {}) {
5
+ return await nativeFetch(url);
6
+ if (init.body) {
7
+ const { body, headers } = renderHttpMessageInit(init);
8
+ init = { ...init, body, headers, };
9
+ }
10
+ let response = await nativeFetch(url, init), encoding;
11
+ if (init.decompress === false && (encoding = response.headers.get('Content-Encoding'))) {
12
+ const recompressedBody = response.body.pipeThrough(new CompressionStream(encoding));
13
+ response = new Response(recompressedBody, response);
14
+ }
15
+ return response;
16
+ }
@@ -0,0 +1,54 @@
1
+
2
+ import { _isNumeric } from '@webqit/util/js/index.js';
3
+ import { _before } from '@webqit/util/str/index.js';
4
+ import { DeepURLSearchParams } from '../webflo-url/util.js';
5
+ import { dataType } from './util.js';
6
+
7
+ export function createFormDataFromJson(data = {}, jsonfy = true, getIsJsonfiable = false) {
8
+ const formData = new FormData;
9
+ let isJsonfiable = true;
10
+ DeepURLSearchParams.reduceValue(data, '', (value, contextPath, suggestedKeys = undefined) => {
11
+ if (suggestedKeys) {
12
+ const isJson = dataType(value) === 'json';
13
+ isJsonfiable = isJsonfiable && isJson;
14
+ return isJson && suggestedKeys;
15
+ }
16
+ if (jsonfy && [true, false, null].includes(value)) {
17
+ value = new Blob([value], { type: 'application/json' });
18
+ }
19
+ formData.append(contextPath, value);
20
+ });
21
+ if (getIsJsonfiable) return [formData, isJsonfiable];
22
+ return formData;
23
+ }
24
+
25
+ export async function renderFormDataToJson(formData, jsonfy = true, getIsJsonfiable = false) {
26
+ let isJsonfiable = true;
27
+ let json;
28
+ for (let [name, value] of formData.entries()) {
29
+ if (!json) { json = _isNumeric(_before(name, '[')) ? [] : {}; }
30
+ let type = dataType(value);
31
+ if (jsonfy && ['Blob', 'File'].includes(type) && value.type === 'application/json') {
32
+ let _value = await value.text();
33
+ value = JSON.parse(_value);
34
+ type = 'json';
35
+ }
36
+ isJsonfiable = isJsonfiable && type === 'json';
37
+ DeepURLSearchParams.set(json, name, value);
38
+ }
39
+ if (getIsJsonfiable) return [json, isJsonfiable];
40
+ return json;
41
+ }
42
+
43
+ Object.defineProperties(FormData, {
44
+ json: { value: createFormDataFromJson }
45
+ });
46
+
47
+ Object.defineProperties(FormData.prototype, {
48
+ json: {
49
+ value: async function (data = {}) {
50
+ const result = await renderFormDataToJson(this, ...arguments);
51
+ return result;
52
+ }
53
+ }
54
+ });
@@ -0,0 +1,151 @@
1
+ import { _after } from '@webqit/util/str/index.js';
2
+ import { _isObject, _isTypeObject } from '@webqit/util/js/index.js';
3
+ import { _from as _arrFrom } from '@webqit/util/arr/index.js';
4
+ import { renderCookieObjToString } from './cookies.js';
5
+
6
+ const prototypeOriginals = {
7
+ set: Headers.prototype.set,
8
+ get: Headers.prototype.get,
9
+ append: Headers.prototype.append,
10
+ };
11
+ const prototypeExtensions = {
12
+ set: {
13
+ value: function (name, value) {
14
+ // -------------------------
15
+ // Format "Set-Cookie" response header
16
+ if (/^Set-Cookie$/i.test(name) && _isObject(value)) {
17
+ value = renderCookieObjToString(value);
18
+ }
19
+ // -------------------------
20
+ // Format "Cookie" request header
21
+ if (/Cookie/i.test(name) && _isTypeObject(value)) {
22
+ value = [].concat(value).map(renderCookieObjToString).join(';');
23
+ }
24
+ // -------------------------
25
+ // Format "Content-Range" response header?
26
+ if (/^Content-Range$/i.test(name) && Array.isArray(value)) {
27
+ if (value.length < 2 || !value[0].includes('-')) {
28
+ throw new Error(`A Content-Range array must be in the format: [ 'start-end', 'total' ]`);
29
+ }
30
+ value = `bytes ${value.join('/')}`;
31
+ }
32
+ // -------------------------
33
+ // Format "Range" request header?
34
+ if (/^Range$/i.test(name)) {
35
+ let rangeArr = [];
36
+ _arrFrom(value).forEach((range, i) => {
37
+ let rangeStr = Array.isArray(range) ? range.join('-') : range + '';
38
+ if (i === 0 && !rangeStr.includes('bytes=')) {
39
+ rangeStr = `bytes=${rangeStr}`;
40
+ }
41
+ rangeArr.push(rangeStr);
42
+ });
43
+ value = rangeArr.join(', ');
44
+ }
45
+ // -------------------------
46
+ // Format "Accept" request header?
47
+ if (/^Accept$/i.test(name) && Array.isArray(value)) {
48
+ value = value.join(',');
49
+ }
50
+ // -------------------------
51
+ return prototypeOriginals.set.call(this, name, value);
52
+ }
53
+ },
54
+ append: {
55
+ value: function (name, value) {
56
+ // -------------------------
57
+ // Format "Set-Cookie" response header
58
+ if (/^Set-Cookie$/i.test(name) && _isObject(value)) {
59
+ value = renderCookieObjToString(value);
60
+ }
61
+ // -------------------------
62
+ return prototypeOriginals.append.call(this, name, value);
63
+ }
64
+ },
65
+ get: {
66
+ value: function (name, parsed = false) {
67
+ let value = prototypeOriginals.get.call(this, name);
68
+ // -------------------------
69
+ // Parse "Set-Cookie" response header
70
+ if (/^Set-Cookie$/i.test(name) && parsed) {
71
+ value = this.getSetCookie()/*IMPORTANT*/.map((str) => {
72
+ const [cookieDefinition, attrsStr] = str.split(';');
73
+ const [name, value] = cookieDefinition.split('=').map((s) => s.trim());
74
+ const cookieObj = { name, value: /*decodeURIComponent*/(value), };
75
+ attrsStr && attrsStr.split(/\;/g).map(attrStr => attrStr.trim().split('=')).forEach(attrsArr => {
76
+ cookieObj[attrsArr[0][0].toLowerCase() + attrsArr[0].substring(1).replace('-', '')] = attrsArr.length === 1 ? true : attrsArr[1];
77
+ });
78
+ return cookieObj;
79
+ });
80
+ }
81
+ // -------------------------
82
+ // Parse "Cookie" request header
83
+ if (/^Cookie$/i.test(name) && parsed) {
84
+ value = value?.split(';').map((str) => {
85
+ const [name, value] = str.split('=').map((s) => s.trim());
86
+ return { name, value: /*decodeURIComponent*/(value), };
87
+ }) || [];
88
+ }
89
+ // -------------------------
90
+ // Parse "Content-Range" response header?
91
+ if (/^Content-Range$/i.test(name) && value && parsed) {
92
+ value = _after(value, 'bytes ').split('/');
93
+ }
94
+ // -------------------------
95
+ // Parse "Range" request header?
96
+ if (/^Range$/i.test(name) && parsed) {
97
+ value = !value ? [] : _after(value, 'bytes=').split(',').map((rangeStr) => {
98
+ const range = rangeStr.trim().split('-').map((s) => s ? parseInt(s, 10) : null);
99
+ range.render = (totalLength) => {
100
+ if (range[1] === null) {
101
+ range[1] = totalLength - 1;
102
+ }
103
+ if (range[0] === null) {
104
+ range[0] = range[1] ? totalLength - range[1] - 1 : 0;
105
+ }
106
+ return range
107
+ };
108
+ range.isValid = (currentStart, totalLength) => {
109
+ // Start higher than end or vice versa?
110
+ if (range[0] > range[1] || range[1] < range[0]) return false;
111
+ // Stretching beyond valid start/end?
112
+ if (range[0] < currentStart || range[1] > totalLength) return false;
113
+ return true;
114
+ };
115
+ return range;
116
+ });
117
+ }
118
+ // -------------------------
119
+ // Parse "Accept" request header?
120
+ if (/^Accept$/i.test(name) && value && parsed) {
121
+ const parseSpec = (spec) => {
122
+ const [mime, q] = spec.trim().split(';').map((s) => s.trim());
123
+ return [mime, parseFloat((q || 'q=1').replace('q=', ''))];
124
+ };
125
+ const list = value.split(',')
126
+ .map((spec) => parseSpec(spec))
127
+ .sort((a, b) => a[1] > b[1] ? -1 : 1) || [];
128
+ const $value = value;
129
+ value = {
130
+ match(mime) {
131
+ if (!mime) return 0;
132
+ const splitMime = (mime) => mime.split('/').map((s) => s.trim());
133
+ const $mime = splitMime(mime + '');
134
+ return list.reduce((prev, [entry, q]) => {
135
+ if (prev) return prev;
136
+ const $entry = splitMime(entry);
137
+ return [0, 1].every((i) => (($mime[i] === $entry[i]) || $mime[i] === '*' || $entry[i] === '*')) ? q : 0;
138
+ }, 0);
139
+ },
140
+ toString() {
141
+ return $value;
142
+ }
143
+ };
144
+ }
145
+ // -------------------------
146
+ return value;
147
+ }
148
+ }
149
+ };
150
+
151
+ Object.defineProperties(Headers.prototype, prototypeExtensions);
@@ -0,0 +1,5 @@
1
+ import './formdata.js';
2
+ import './request.js';
3
+ import './response.js';
4
+ import './LiveResponse.js';
5
+ import './headers.js';