@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.
- package/.github/FUNDING.yml +12 -0
- package/.github/workflows/publish.yml +48 -0
- package/.gitignore +2 -0
- package/LICENSE +2 -2
- package/README.md +71 -2050
- package/package.json +28 -13
- package/site/-/_.md +139 -0
- package/site/-/docs.old.md +2010 -0
- package/site/.vitepress/cache/deps/@braintree_sanitize-url 2.js +93 -0
- package/site/.vitepress/cache/deps/@braintree_sanitize-url.js +93 -0
- package/site/.vitepress/cache/deps/@braintree_sanitize-url.js 2.map +7 -0
- package/site/.vitepress/cache/deps/@braintree_sanitize-url.js.map +7 -0
- package/site/.vitepress/cache/deps/_metadata 2.json +85 -0
- package/site/.vitepress/cache/deps/_metadata.json +85 -0
- package/site/.vitepress/cache/deps/chunk-BUSYA2B4 2.js +9 -0
- package/site/.vitepress/cache/deps/chunk-BUSYA2B4.js +9 -0
- package/site/.vitepress/cache/deps/chunk-BUSYA2B4.js 2.map +7 -0
- package/site/.vitepress/cache/deps/chunk-BUSYA2B4.js.map +7 -0
- package/site/.vitepress/cache/deps/chunk-Q2AYPHVK 2.js +9719 -0
- package/site/.vitepress/cache/deps/chunk-Q2AYPHVK.js +9719 -0
- package/site/.vitepress/cache/deps/chunk-Q2AYPHVK.js 2.map +7 -0
- package/site/.vitepress/cache/deps/chunk-Q2AYPHVK.js.map +7 -0
- package/site/.vitepress/cache/deps/chunk-QAXAIFA7 2.js +12705 -0
- package/site/.vitepress/cache/deps/chunk-QAXAIFA7.js +12705 -0
- package/site/.vitepress/cache/deps/chunk-QAXAIFA7.js 2.map +7 -0
- package/site/.vitepress/cache/deps/chunk-QAXAIFA7.js.map +7 -0
- package/site/.vitepress/cache/deps/cytoscape 2.js +30278 -0
- package/site/.vitepress/cache/deps/cytoscape-cose-bilkent 2.js +4710 -0
- package/site/.vitepress/cache/deps/cytoscape-cose-bilkent.js +4710 -0
- package/site/.vitepress/cache/deps/cytoscape-cose-bilkent.js 2.map +7 -0
- package/site/.vitepress/cache/deps/cytoscape-cose-bilkent.js.map +7 -0
- package/site/.vitepress/cache/deps/cytoscape.js +30278 -0
- package/site/.vitepress/cache/deps/cytoscape.js 2.map +7 -0
- package/site/.vitepress/cache/deps/cytoscape.js.map +7 -0
- package/site/.vitepress/cache/deps/dayjs 2.js +285 -0
- package/site/.vitepress/cache/deps/dayjs.js +285 -0
- package/site/.vitepress/cache/deps/dayjs.js 2.map +7 -0
- package/site/.vitepress/cache/deps/dayjs.js.map +7 -0
- package/site/.vitepress/cache/deps/debug 2.js +453 -0
- package/site/.vitepress/cache/deps/debug.js +453 -0
- package/site/.vitepress/cache/deps/debug.js 2.map +7 -0
- package/site/.vitepress/cache/deps/debug.js.map +7 -0
- package/site/.vitepress/cache/deps/package 2.json +3 -0
- package/site/.vitepress/cache/deps/package.json +3 -0
- package/site/.vitepress/cache/deps/vitepress___@vue_devtools-api 2.js +4507 -0
- package/site/.vitepress/cache/deps/vitepress___@vue_devtools-api.js +4507 -0
- package/site/.vitepress/cache/deps/vitepress___@vue_devtools-api.js 2.map +7 -0
- package/site/.vitepress/cache/deps/vitepress___@vue_devtools-api.js.map +7 -0
- package/site/.vitepress/cache/deps/vitepress___@vueuse_core 2.js +584 -0
- package/site/.vitepress/cache/deps/vitepress___@vueuse_core.js +584 -0
- package/site/.vitepress/cache/deps/vitepress___@vueuse_core.js 2.map +7 -0
- package/site/.vitepress/cache/deps/vitepress___@vueuse_core.js.map +7 -0
- package/site/.vitepress/cache/deps/vitepress___@vueuse_integrations_useFocusTrap 2.js +1166 -0
- package/site/.vitepress/cache/deps/vitepress___@vueuse_integrations_useFocusTrap.js +1166 -0
- package/site/.vitepress/cache/deps/vitepress___@vueuse_integrations_useFocusTrap.js 2.map +7 -0
- package/site/.vitepress/cache/deps/vitepress___@vueuse_integrations_useFocusTrap.js.map +7 -0
- package/site/.vitepress/cache/deps/vitepress___mark__js_src_vanilla__js 2.js +1667 -0
- package/site/.vitepress/cache/deps/vitepress___mark__js_src_vanilla__js.js +1667 -0
- package/site/.vitepress/cache/deps/vitepress___mark__js_src_vanilla__js.js 2.map +7 -0
- package/site/.vitepress/cache/deps/vitepress___mark__js_src_vanilla__js.js.map +7 -0
- package/site/.vitepress/cache/deps/vitepress___minisearch 2.js +1815 -0
- package/site/.vitepress/cache/deps/vitepress___minisearch.js +1815 -0
- package/site/.vitepress/cache/deps/vitepress___minisearch.js 2.map +7 -0
- package/site/.vitepress/cache/deps/vitepress___minisearch.js.map +7 -0
- package/site/.vitepress/cache/deps/vue 2.js +344 -0
- package/site/.vitepress/cache/deps/vue.js +344 -0
- package/site/.vitepress/cache/deps/vue.js 2.map +7 -0
- package/site/.vitepress/cache/deps/vue.js.map +7 -0
- package/site/.vitepress/config.ts +147 -0
- package/site/.vitepress/theme/custom.css +50 -0
- package/site/.vitepress/theme/index.ts +6 -0
- package/site/api/webflo-fetch/FormData.md +0 -0
- package/site/api/webflo-fetch/Headers.md +0 -0
- package/site/api/webflo-fetch/LiveResponse.md +0 -0
- package/site/api/webflo-fetch/Request.md +0 -0
- package/site/api/webflo-fetch/Response.md +0 -0
- package/site/api/webflo-fetch/fetch.md +0 -0
- package/site/api/webflo-routing/HttpCookies.md +0 -0
- package/site/api/webflo-routing/HttpEvent/respondWith.md +1 -0
- package/site/api/webflo-routing/HttpEvent/waitUntil.md +1 -0
- package/site/api/webflo-routing/HttpEvent/waitUntilNavigate.md +1 -0
- package/site/api/webflo-routing/HttpEvent.md +30 -0
- package/site/api/webflo-routing/HttpSession.md +0 -0
- package/site/api/webflo-routing/HttpState.md +0 -0
- package/site/api/webflo-routing/HttpUser.md +0 -0
- package/site/api/webflo-routing/handler/fetch.md +42 -0
- package/site/api/webflo-routing/handler/next.md +54 -0
- package/site/api/webflo-routing/handler.md +119 -0
- package/site/api.md +26 -0
- package/site/contributing.md +16 -0
- package/site/docs/advanced/lifecycles.md +20 -0
- package/site/docs/advanced/redirects.md +0 -0
- package/site/docs/advanced/routing.md +1 -0
- package/site/docs/advanced.md +9 -0
- package/site/docs/concepts/realtime.md +637 -0
- package/site/docs/concepts/rendering.md +60 -0
- package/site/docs/concepts/request-response.md +47 -0
- package/site/docs/concepts/routing.md +656 -0
- package/site/docs/concepts/state.md +44 -0
- package/site/docs/concepts/templates.md +48 -0
- package/site/docs/concepts.md +97 -0
- package/site/docs/getting-started.md +378 -0
- package/site/docs/tech-stack.md +56 -0
- package/site/docs.md +100 -0
- package/site/examples/pwa.md +10 -0
- package/site/examples/web.md +11 -0
- package/site/examples.md +10 -0
- package/site/faq.md +13 -0
- package/site/guides/guide-auth.md +13 -0
- package/site/guides/guide-file-upload.md +11 -0
- package/site/guides/guide-service-worker.md +10 -0
- package/site/guides/tutorial-1-todo.md +24 -0
- package/site/guides.md +15 -0
- package/site/index.md +39 -0
- package/site/public/img/brand/logo-670x670.png +0 -0
- package/site/recipes/realtime.md +11 -0
- package/site/recipes/streaming.md +15 -0
- package/site/reference/cli.md +11 -0
- package/site/reference/config.md +13 -0
- package/site/reference/tools.md +9 -0
- package/src/Context.js +3 -11
- package/src/config-pi/deployment/Env.js +6 -19
- package/src/config-pi/deployment/Layout.js +11 -3
- package/src/config-pi/runtime/Client.js +40 -48
- package/src/config-pi/runtime/Server.js +52 -20
- package/src/config-pi/runtime/client/Worker.js +22 -20
- package/src/config-pi/static/Init.js +57 -0
- package/src/config-pi/static/index.js +2 -0
- package/src/deployment-pi/origins/index.js +1 -1
- package/src/deployment-pi/util.js +161 -0
- package/src/index.js +3 -9
- package/src/init-pi/index.js +117 -0
- package/src/init-pi/templates/pwa/app/handler.server.js +8 -0
- package/src/init-pi/templates/pwa/app/page.html +7 -0
- package/src/init-pi/templates/pwa/package.json +19 -0
- package/src/init-pi/templates/pwa/public/assets/app.css +16 -0
- package/src/init-pi/templates/pwa/public/index.html +39 -0
- package/src/init-pi/templates/pwa/public/manifest.json +29 -0
- package/src/init-pi/templates/web/app/handler.server.js +8 -0
- package/src/init-pi/templates/web/app/page.html +7 -0
- package/src/init-pi/templates/web/package.json +19 -0
- package/src/init-pi/templates/web/public/assets/app.css +16 -0
- package/src/init-pi/templates/web/public/index.html +39 -0
- package/src/runtime-pi/WebfloRuntime.js +350 -0
- package/src/runtime-pi/index.js +3 -10
- package/src/runtime-pi/webflo-client/ClientSideCookies.js +17 -0
- package/src/runtime-pi/webflo-client/ClientSideWorkport.js +63 -0
- package/src/runtime-pi/webflo-client/DeviceCapabilities.js +213 -0
- package/src/runtime-pi/webflo-client/WebfloClient.js +500 -0
- package/src/runtime-pi/webflo-client/WebfloRootClient1.js +206 -0
- package/src/runtime-pi/webflo-client/WebfloRootClient2.js +113 -0
- package/src/runtime-pi/webflo-client/WebfloSubClient.js +118 -0
- package/src/runtime-pi/webflo-client/index.js +17 -0
- package/src/runtime-pi/webflo-client/webflo-codegen.js +469 -0
- package/src/runtime-pi/webflo-client/webflo-devmode.js +243 -0
- package/src/runtime-pi/webflo-client/webflo-embedded.js +50 -0
- package/src/runtime-pi/webflo-fetch/LiveResponse.js +437 -0
- package/src/runtime-pi/webflo-fetch/cookies.js +10 -0
- package/src/runtime-pi/webflo-fetch/fetch.js +16 -0
- package/src/runtime-pi/webflo-fetch/formdata.js +54 -0
- package/src/runtime-pi/webflo-fetch/headers.js +151 -0
- package/src/runtime-pi/webflo-fetch/index.js +5 -0
- package/src/runtime-pi/webflo-fetch/message.js +49 -0
- package/src/runtime-pi/webflo-fetch/request.js +62 -0
- package/src/runtime-pi/webflo-fetch/response.js +110 -0
- package/src/runtime-pi/webflo-fetch/util.js +28 -0
- package/src/runtime-pi/webflo-messaging/WQBroadcastChannel.js +10 -0
- package/src/runtime-pi/webflo-messaging/WQMessageChannel.js +26 -0
- package/src/runtime-pi/webflo-messaging/WQMessageEvent.js +87 -0
- package/src/runtime-pi/webflo-messaging/WQMessagePort.js +38 -0
- package/src/runtime-pi/webflo-messaging/WQRelayPort.js +47 -0
- package/src/runtime-pi/webflo-messaging/WQSockPort.js +113 -0
- package/src/runtime-pi/webflo-messaging/WQStarPort.js +104 -0
- package/src/runtime-pi/webflo-messaging/wq-message-port.js +404 -0
- package/src/runtime-pi/webflo-routing/HttpCookies.js +42 -0
- package/src/runtime-pi/webflo-routing/HttpEvent.js +112 -0
- package/src/runtime-pi/webflo-routing/HttpSession.js +11 -0
- package/src/runtime-pi/webflo-routing/HttpState.js +153 -0
- package/src/runtime-pi/webflo-routing/HttpUser.js +54 -0
- package/src/runtime-pi/webflo-routing/WebfloRouter.js +245 -0
- package/src/runtime-pi/webflo-server/ServerSideCookies.js +19 -0
- package/src/runtime-pi/webflo-server/ServerSideSession.js +38 -0
- package/src/runtime-pi/webflo-server/WebfloServer.js +937 -0
- package/src/runtime-pi/webflo-server/index.js +11 -0
- package/src/runtime-pi/webflo-server/messaging/Client.js +27 -0
- package/src/runtime-pi/webflo-server/messaging/ClientRequestRealtime.js +50 -0
- package/src/runtime-pi/webflo-server/messaging/Clients.js +25 -0
- package/src/runtime-pi/webflo-server/webflo-devmode.js +326 -0
- package/src/runtime-pi/{client → webflo-url}/Url.js +27 -76
- package/src/runtime-pi/webflo-url/index.js +1 -0
- package/src/runtime-pi/webflo-url/urlpattern.js +38 -0
- package/src/runtime-pi/{util-url.js → webflo-url/util.js} +5 -43
- package/src/runtime-pi/webflo-url/xURL.js +94 -0
- package/src/runtime-pi/webflo-worker/WebfloWorker.js +234 -0
- package/src/runtime-pi/webflo-worker/WorkerSideCookies.js +19 -0
- package/src/runtime-pi/webflo-worker/WorkerSideWorkport.js +18 -0
- package/src/runtime-pi/webflo-worker/index.js +11 -0
- package/src/services-pi/index.js +2 -0
- package/src/services-pi/push/index.js +23 -0
- package/src/util.js +10 -0
- package/src/{webflo.js → webflo-cli.js} +4 -4
- package/src/runtime-pi/Application.js +0 -29
- package/src/runtime-pi/Cookies.js +0 -82
- package/src/runtime-pi/HttpEvent.js +0 -107
- package/src/runtime-pi/Router.js +0 -130
- package/src/runtime-pi/Runtime.js +0 -21
- package/src/runtime-pi/client/Application.js +0 -76
- package/src/runtime-pi/client/Context.js +0 -7
- package/src/runtime-pi/client/Router.js +0 -48
- package/src/runtime-pi/client/Runtime.js +0 -525
- package/src/runtime-pi/client/Workport.js +0 -190
- package/src/runtime-pi/client/createStorage.js +0 -58
- package/src/runtime-pi/client/generate.js +0 -481
- package/src/runtime-pi/client/index.js +0 -21
- package/src/runtime-pi/client/worker/Application.js +0 -44
- package/src/runtime-pi/client/worker/Context.js +0 -7
- package/src/runtime-pi/client/worker/Runtime.js +0 -275
- package/src/runtime-pi/client/worker/Workport.js +0 -78
- package/src/runtime-pi/client/worker/index.js +0 -21
- package/src/runtime-pi/server/Application.js +0 -101
- package/src/runtime-pi/server/Context.js +0 -16
- package/src/runtime-pi/server/Router.js +0 -159
- package/src/runtime-pi/server/Runtime.js +0 -558
- package/src/runtime-pi/server/index.js +0 -21
- package/src/runtime-pi/util-http.js +0 -86
- package/src/runtime-pi/xFormData.js +0 -24
- package/src/runtime-pi/xHeaders.js +0 -146
- package/src/runtime-pi/xRequest.js +0 -46
- package/src/runtime-pi/xRequestHeaders.js +0 -109
- package/src/runtime-pi/xResponse.js +0 -33
- package/src/runtime-pi/xResponseHeaders.js +0 -117
- package/src/runtime-pi/xURL.js +0 -105
- package/src/runtime-pi/xfetch.js +0 -23
- package/src/runtime-pi/xxHttpMessage.js +0 -102
- 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);
|