@webqit/webflo 0.20.25 → 0.20.27
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/package.json +8 -5
- package/src/build-pi/index.js +7 -5
- package/src/init-pi/index.js +0 -1
- package/src/runtime-pi/{WebfloRuntime.js → AppRuntime.js} +57 -113
- package/src/runtime-pi/webflo-client/DeviceCapabilities.js +1 -1
- package/src/runtime-pi/webflo-client/WebfloClient.js +163 -95
- package/src/runtime-pi/webflo-client/{WebfloRootClient1.js → WebfloRootClientA.js} +39 -56
- package/src/runtime-pi/webflo-client/{WebfloRootClient2.js → WebfloRootClientB.js} +3 -3
- package/src/runtime-pi/webflo-client/WebfloSubClient.js +28 -15
- package/src/runtime-pi/webflo-client/index.js +3 -3
- package/src/runtime-pi/webflo-messaging/ClientPortMixin.js +13 -0
- package/src/runtime-pi/{webflo-server/messaging/ClientRequestRealtime.js → webflo-messaging/ClientRequestPort001.js} +13 -9
- package/src/runtime-pi/webflo-messaging/ClientRequestPort010.js +4 -0
- package/src/runtime-pi/webflo-messaging/ClientRequestPort100.js +17 -0
- package/src/runtime-pi/webflo-messaging/WebfloTenancy001.js +27 -0
- package/src/runtime-pi/webflo-messaging/WebfloTenant001.js +27 -0
- package/src/runtime-pi/webflo-routing/HttpCookies101.js +53 -0
- package/src/runtime-pi/webflo-routing/HttpCookies110.js +3 -0
- package/src/runtime-pi/webflo-routing/{HttpEvent.js → HttpEvent111.js} +95 -73
- package/src/runtime-pi/webflo-routing/HttpKeyvalInterface.js +120 -0
- package/src/runtime-pi/webflo-routing/HttpSession001.js +24 -0
- package/src/runtime-pi/webflo-routing/HttpSession110.js +3 -0
- package/src/runtime-pi/webflo-routing/{HttpThread.js → HttpThread111.js} +54 -13
- package/src/runtime-pi/webflo-routing/{HttpUser.js → HttpUser111.js} +10 -23
- package/src/runtime-pi/webflo-routing/KeyvalsFactory001.js +53 -0
- package/src/runtime-pi/webflo-routing/KeyvalsFactory110.js +48 -0
- package/src/runtime-pi/webflo-routing/KeyvalsFactoryInterface.js +56 -0
- package/src/runtime-pi/webflo-routing/{WebfloRouter.js → WebfloRouter111.js} +5 -6
- package/src/runtime-pi/webflo-server/WebfloServer.js +262 -266
- package/src/runtime-pi/webflo-worker/WebfloWorker.js +97 -44
- package/src/util.js +3 -2
- package/src/runtime-pi/apis.js +0 -9
- package/src/runtime-pi/webflo-client/ClientSideCookies.js +0 -18
- package/src/runtime-pi/webflo-fetch/LiveResponse.js +0 -476
- package/src/runtime-pi/webflo-fetch/index.js +0 -419
- package/src/runtime-pi/webflo-fetch/util.js +0 -28
- package/src/runtime-pi/webflo-messaging/WQBroadcastChannel.js +0 -10
- package/src/runtime-pi/webflo-messaging/WQMessageChannel.js +0 -26
- package/src/runtime-pi/webflo-messaging/WQMessageEvent.js +0 -87
- package/src/runtime-pi/webflo-messaging/WQMessagePort.js +0 -38
- package/src/runtime-pi/webflo-messaging/WQRelayPort.js +0 -47
- package/src/runtime-pi/webflo-messaging/WQSockPort.js +0 -111
- package/src/runtime-pi/webflo-messaging/WQStarPort.js +0 -112
- package/src/runtime-pi/webflo-messaging/wq-message-port.js +0 -413
- package/src/runtime-pi/webflo-routing/HttpCookies.js +0 -43
- package/src/runtime-pi/webflo-routing/HttpSession.js +0 -11
- package/src/runtime-pi/webflo-routing/HttpState.js +0 -182
- package/src/runtime-pi/webflo-server/ServerSideCookies.js +0 -22
- package/src/runtime-pi/webflo-server/ServerSideSession.js +0 -40
- package/src/runtime-pi/webflo-server/messaging/Client.js +0 -27
- package/src/runtime-pi/webflo-server/messaging/Clients.js +0 -25
- package/src/runtime-pi/webflo-url/Url.js +0 -156
- package/src/runtime-pi/webflo-url/index.js +0 -1
- package/src/runtime-pi/webflo-url/urlpattern.js +0 -38
- package/src/runtime-pi/webflo-url/util.js +0 -109
- package/src/runtime-pi/webflo-url/xURL.js +0 -94
- package/src/runtime-pi/webflo-worker/WorkerSideCookies.js +0 -21
package/package.json
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"vanila-javascript"
|
|
13
13
|
],
|
|
14
14
|
"homepage": "https://webqit.io/tooling/webflo",
|
|
15
|
-
"version": "0.20.
|
|
15
|
+
"version": "0.20.27",
|
|
16
16
|
"license": "MIT",
|
|
17
17
|
"repository": {
|
|
18
18
|
"type": "git",
|
|
@@ -33,9 +33,6 @@
|
|
|
33
33
|
"site:build": "vitepress build site",
|
|
34
34
|
"site:preview": "vitepress preview site --port 4173"
|
|
35
35
|
},
|
|
36
|
-
"exports": {
|
|
37
|
-
"./apis": "./src/runtime-pi/apis.js"
|
|
38
|
-
},
|
|
39
36
|
"bin": {
|
|
40
37
|
"webflo": "src/webflo-cli.js",
|
|
41
38
|
"webflo-certbot-http-auth-hook": "src/services-pi/cert/http-auth-hook.js",
|
|
@@ -44,13 +41,19 @@
|
|
|
44
41
|
"dependencies": {
|
|
45
42
|
"@octokit/webhooks": "^7.15.1",
|
|
46
43
|
"@webqit/backpack": "^0.1.12",
|
|
44
|
+
"@webqit/fetch-plus": "^0.1.20",
|
|
45
|
+
"@webqit/keyval": "^0.2.17",
|
|
46
|
+
"@webqit/observer": "^3.8.14",
|
|
47
47
|
"@webqit/oohtml-ssr": "^2.2.2",
|
|
48
|
-
"@webqit/
|
|
48
|
+
"@webqit/port-plus": "^0.1.19",
|
|
49
|
+
"@webqit/url-plus": "^0.1.4",
|
|
50
|
+
"@webqit/use-live": "^0.5.49",
|
|
49
51
|
"@webqit/util": "^0.8.11",
|
|
50
52
|
"chokidar": "^4.0.3",
|
|
51
53
|
"dotenv": "^16.4.7",
|
|
52
54
|
"esbuild": "^0.14.38",
|
|
53
55
|
"fast-glob": "^3.3.3",
|
|
56
|
+
"idb": "^8.0.3",
|
|
54
57
|
"mime-types": "^2.1.33",
|
|
55
58
|
"simple-git": "^2.20.1",
|
|
56
59
|
"urlpattern-polyfill": "^4.0.3"
|
package/src/build-pi/index.js
CHANGED
|
@@ -7,12 +7,12 @@ import { gzipSync, brotliCompressSync } from 'zlib';
|
|
|
7
7
|
import { _afterLast, _beforeLast } from '@webqit/util/str/index.js';
|
|
8
8
|
import { _isObject, _isArray } from '@webqit/util/js/index.js';
|
|
9
9
|
import { jsFile } from '@webqit/backpack/src/dotfile/index.js';
|
|
10
|
+
import { URLPatternPlus } from '@webqit/url-plus';
|
|
10
11
|
import { bootstrap as serverBootstrap } from '../runtime-pi/webflo-server/bootstrap.js';
|
|
11
12
|
import { bootstrap as clientBootstrap } from '../runtime-pi/webflo-client/bootstrap.js';
|
|
12
13
|
import { bootstrap as workerBootstrap } from '../runtime-pi/webflo-worker/bootstrap.js';
|
|
13
14
|
import { UseLiveTransform } from './esbuild-plugin-uselive-transform.js';
|
|
14
15
|
import { CLIContext } from '../CLIContext.js';
|
|
15
|
-
import '../runtime-pi/webflo-url/urlpattern.js';
|
|
16
16
|
|
|
17
17
|
function declareConfig({ $source, configExport, indentation = 0 }) {
|
|
18
18
|
const varName = 'config';
|
|
@@ -370,16 +370,18 @@ async function generateWorkerScript({ $context, bootstrap, ...restParams }) {
|
|
|
370
370
|
for (const strategy of ['cache_first_urls', 'cache_only_urls']) {
|
|
371
371
|
if (configExport.WORKER[strategy].length) {
|
|
372
372
|
const [urls, patterns] = configExport.WORKER[strategy].reduce(([urls, patterns], url) => {
|
|
373
|
-
const patternInstance = new
|
|
373
|
+
const patternInstance = new URLPatternPlus(url, 'http://localhost');
|
|
374
374
|
const isPattern = patternInstance.isPattern();
|
|
375
|
-
|
|
375
|
+
|
|
376
|
+
if (isPattern && (patternInstance.hostname !== 'localhost' || patternInstance.port)) {
|
|
376
377
|
throw new Error(`Pattern URLs must have no origin part. Recieved "${url}".`);
|
|
377
378
|
}
|
|
379
|
+
|
|
378
380
|
return isPattern ? [urls, patterns.concat(patternInstance)] : [urls.concat(url), patterns];
|
|
379
381
|
}, [[], []]);
|
|
380
382
|
if (patterns.length) {
|
|
381
383
|
function scanDir(dir) {
|
|
382
|
-
Fs.readdirSync(dir).reduce((result, f) => {
|
|
384
|
+
return Fs.readdirSync(dir).reduce((result, f) => {
|
|
383
385
|
const resource = Path.join(dir, f);
|
|
384
386
|
if (f.startsWith('.')) return result;
|
|
385
387
|
return result.concat(
|
|
@@ -391,7 +393,7 @@ async function generateWorkerScript({ $context, bootstrap, ...restParams }) {
|
|
|
391
393
|
configExport.WORKER[strategy] = patterns.reduce((all, pattern) => {
|
|
392
394
|
const matchedFiles = files.filter((file) => pattern.test(file, 'http://localhost'));
|
|
393
395
|
if (matchedFiles.length) return all.concat(matchedFiles);
|
|
394
|
-
throw new Error(`The pattern "${pattern.
|
|
396
|
+
throw new Error(`The pattern "${pattern.pathname}" didn't match any files.`);
|
|
395
397
|
}, urls);
|
|
396
398
|
}
|
|
397
399
|
}
|
package/src/init-pi/index.js
CHANGED
|
@@ -5,7 +5,6 @@ import { exec } from 'child_process';
|
|
|
5
5
|
import { _toTitle } from '@webqit/util/str/index.js';
|
|
6
6
|
import { readInitConfig } from '../deployment-pi/util.js';
|
|
7
7
|
import { CLIContext } from '../CLIContext.js';
|
|
8
|
-
import * as deployment from '../deployment-pi/index.js';
|
|
9
8
|
|
|
10
9
|
export const desc = {
|
|
11
10
|
init: 'Generate a preset Webflo starter app.',
|
|
@@ -1,33 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { LiveResponse } from './webflo-fetch/LiveResponse.js';
|
|
1
|
+
import { LiveResponse, ResponsePlus } from '@webqit/fetch-plus';
|
|
2
|
+
import { WebfloRouter111 } from './webflo-routing/WebfloRouter111.js';
|
|
4
3
|
import { AppBootstrap } from './AppBootstrap.js';
|
|
5
|
-
import {
|
|
6
|
-
import { HttpThread } from './webflo-routing/HttpThread.js';
|
|
7
|
-
import { HttpSession } from './webflo-routing/HttpSession.js';
|
|
8
|
-
import { HttpUser } from './webflo-routing/HttpUser.js';
|
|
9
|
-
import { _wq } from '../util.js';
|
|
4
|
+
import { _meta } from '../util.js';
|
|
10
5
|
|
|
11
|
-
export class
|
|
6
|
+
export class AppRuntime {
|
|
12
7
|
|
|
13
8
|
#instanceController = new AbortController;
|
|
14
9
|
get $instanceController() { return this.#instanceController; }
|
|
15
10
|
|
|
16
|
-
static get Router() { return WebfloRouter; }
|
|
17
|
-
|
|
18
|
-
static get HttpEvent() { return HttpEvent; }
|
|
19
|
-
|
|
20
|
-
static get HttpThread() { return HttpThread; }
|
|
21
|
-
|
|
22
|
-
static get HttpSession() { return HttpSession; }
|
|
23
|
-
|
|
24
|
-
static get HttpUser() { return HttpUser; }
|
|
25
|
-
|
|
26
11
|
static create(bootstrap) { return new this(bootstrap); }
|
|
27
12
|
|
|
28
13
|
#bootstrap;
|
|
29
|
-
|
|
30
14
|
get bootstrap() { return this.#bootstrap; }
|
|
15
|
+
|
|
31
16
|
get cx() { return this.bootstrap.cx; }
|
|
32
17
|
get config() { return this.bootstrap.config; }
|
|
33
18
|
get routes() { return this.bootstrap.routes; }
|
|
@@ -47,20 +32,6 @@ export class WebfloRuntime {
|
|
|
47
32
|
if (this.bootstrap.init.SETUP) {
|
|
48
33
|
await this.bootstrap.init.SETUP(this);
|
|
49
34
|
}
|
|
50
|
-
await this.initCreateStorage();
|
|
51
|
-
return this.#instanceController;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
async initCreateStorage() {
|
|
55
|
-
if (!this.bootstrap.init.createStorage) {
|
|
56
|
-
const inmemSessionRegistry = new Map;
|
|
57
|
-
this.bootstrap.init.createStorage = (namespace) => {
|
|
58
|
-
if (!inmemSessionRegistry.has(namespace)) {
|
|
59
|
-
inmemSessionRegistry.set(namespace, new Map);
|
|
60
|
-
}
|
|
61
|
-
return inmemSessionRegistry.get(namespace);
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
35
|
return this.#instanceController;
|
|
65
36
|
}
|
|
66
37
|
|
|
@@ -76,48 +47,26 @@ export class WebfloRuntime {
|
|
|
76
47
|
return this.#instanceController;
|
|
77
48
|
}
|
|
78
49
|
|
|
79
|
-
createStorage(namespace, ttl) {
|
|
80
|
-
return this.bootstrap.init.createStorage(namespace, ttl);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
50
|
createRequest(href, init = {}) {
|
|
84
51
|
return new Request(href, init);
|
|
85
52
|
}
|
|
86
53
|
|
|
87
|
-
createHttpThread({ store, threadID, ...rest }) {
|
|
88
|
-
return this.constructor.HttpThread.create({ store, threadID, ...rest });
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
createHttpCookies({ request, thread, ...rest }) {
|
|
92
|
-
return this.constructor.HttpCookies.create({ request, thread, ...rest });
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
createHttpSession({ store, request, thread, ...rest }) {
|
|
96
|
-
return this.constructor.HttpSession.create({ store, request, thread, ...rest });
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
createHttpUser({ store, request, thread, client, ...rest }) {
|
|
100
|
-
return this.constructor.HttpUser.create({ store, request, thread, client, ...rest });
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
createHttpEvent({ request, thread, cookies, session, user, client, detail, signal, state, ...rest }) {
|
|
104
|
-
return this.constructor.HttpEvent.create(null, { request, thread, cookies, session, user, client, detail, signal, state, ...rest });
|
|
105
|
-
}
|
|
106
|
-
|
|
107
54
|
async dispatchNavigationEvent({ httpEvent, crossLayerFetch, clientPortB }) {
|
|
108
55
|
const { flags: FLAGS, logger: LOGGER } = this.cx;
|
|
109
56
|
|
|
110
57
|
// Dispatch event
|
|
111
|
-
const router = new
|
|
58
|
+
const router = new WebfloRouter111(this, httpEvent.url.pathname);
|
|
112
59
|
await router.route(['SETUP'], httpEvent.spawn());
|
|
113
60
|
|
|
114
61
|
// Do proper routing for respone
|
|
115
62
|
const response = await new Promise(async (resolve) => {
|
|
116
63
|
let autoLiveResponse, response;
|
|
117
|
-
|
|
64
|
+
|
|
65
|
+
httpEvent.client.readyStateChange('messaging').then(() => {
|
|
118
66
|
autoLiveResponse = new LiveResponse(null, { status: 202, statusText: 'Accepted', done: false });
|
|
119
67
|
resolve(autoLiveResponse);
|
|
120
68
|
});
|
|
69
|
+
|
|
121
70
|
const route = async () => {
|
|
122
71
|
const routeMethods = [httpEvent.request.method, 'default'];
|
|
123
72
|
const remoteFetch = (...args) => this.remoteFetch(...args);
|
|
@@ -134,53 +83,46 @@ export class WebfloRuntime {
|
|
|
134
83
|
response = new Response(null, { status: 500, statusText: e.message });
|
|
135
84
|
}
|
|
136
85
|
|
|
137
|
-
if (
|
|
138
|
-
|
|
86
|
+
if (autoLiveResponse) {
|
|
87
|
+
await this.handleThreadStatus(httpEvent, response);
|
|
88
|
+
await autoLiveResponse.replaceWith(response, { done: true });
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (response instanceof Response) {
|
|
93
|
+
ResponsePlus.upgradeInPlace(response);
|
|
94
|
+
} else if (!(response instanceof LiveResponse)) {
|
|
95
|
+
const isLifecyleComplete = ['waiting', 'done'].includes(httpEvent.readyState);
|
|
139
96
|
response = LiveResponse.test(response) !== 'Default' || !isLifecyleComplete
|
|
140
|
-
?
|
|
141
|
-
:
|
|
97
|
+
? LiveResponse.from(response, { done: isLifecyleComplete })
|
|
98
|
+
: ResponsePlus.from(response);
|
|
142
99
|
}
|
|
143
100
|
|
|
144
101
|
// Any "status" in thread?
|
|
145
102
|
await this.handleThreadStatus(httpEvent, response);
|
|
146
|
-
|
|
147
|
-
// Resolve now...
|
|
148
|
-
if (autoLiveResponse) {
|
|
149
|
-
await autoLiveResponse.replaceWith(response, { done: true });
|
|
150
|
-
} else {
|
|
151
|
-
resolve(response);
|
|
152
|
-
}
|
|
103
|
+
resolve(response);
|
|
153
104
|
});
|
|
154
105
|
|
|
155
|
-
// Commit data in the exact order. Reason: in how they depend on each other
|
|
156
|
-
for (const storage of [httpEvent.user, httpEvent.session, httpEvent.cookies]) {
|
|
157
|
-
await storage?.commit?.(response, FLAGS['dev']);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
106
|
// End-of-life cleanup
|
|
161
107
|
const cleanup = async () => {
|
|
162
|
-
|
|
163
|
-
storage.cleanup();
|
|
164
|
-
}
|
|
165
|
-
if (!httpEvent.thread.extended) {
|
|
166
|
-
await httpEvent.thread.clear();
|
|
167
|
-
}
|
|
108
|
+
await Promise.all([httpEvent.thread, httpEvent.user, httpEvent.session, httpEvent.cookies].map((x) => x._cleanup()));
|
|
168
109
|
};
|
|
169
110
|
|
|
170
|
-
// Wait for any
|
|
171
|
-
if (
|
|
172
|
-
httpEvent.waitUntil(response.
|
|
111
|
+
// Wait for any "live" response to resolve
|
|
112
|
+
if (response instanceof LiveResponse && response.readyState !== 'done') {
|
|
113
|
+
httpEvent.waitUntil(response.readyStateChange('done'));
|
|
114
|
+
httpEvent.waitUntil(httpEvent.client.readyStateChange('open').then(async () => {
|
|
115
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
116
|
+
}));
|
|
173
117
|
}
|
|
174
118
|
|
|
175
|
-
// Send the X-
|
|
119
|
+
// Send the X-Message-Port header
|
|
176
120
|
// This server's event lifecycle management
|
|
177
|
-
if (!(httpEvent.
|
|
121
|
+
if (!['waiting', 'done'].includes(httpEvent.readyState)) {
|
|
178
122
|
if (this.isClientSide) {
|
|
179
|
-
|
|
180
|
-
responseMeta.set('background_port', clientPortB);
|
|
123
|
+
LiveResponse.attachPort(response, clientPortB);
|
|
181
124
|
} else {
|
|
182
|
-
|
|
183
|
-
response.headers.set('X-Background-Messaging-Port', clientPortB);
|
|
125
|
+
response.headers.set('X-Message-Port', clientPortB);
|
|
184
126
|
}
|
|
185
127
|
|
|
186
128
|
// On navigation:
|
|
@@ -195,42 +137,44 @@ export class WebfloRuntime {
|
|
|
195
137
|
}
|
|
196
138
|
}, 0);
|
|
197
139
|
});
|
|
140
|
+
|
|
198
141
|
// On close:
|
|
199
142
|
// Abort httpEvent itself
|
|
200
|
-
httpEvent.client.
|
|
143
|
+
httpEvent.client.readyStateChange('close').then(() => {
|
|
201
144
|
httpEvent.abort();
|
|
202
145
|
});
|
|
203
146
|
|
|
204
147
|
// On ROOT event complete:
|
|
205
148
|
// Close httpEvent.client
|
|
206
|
-
httpEvent.
|
|
149
|
+
httpEvent.readyStateChange('done').then(async () => {
|
|
207
150
|
httpEvent.client.close();
|
|
208
|
-
cleanup();
|
|
151
|
+
await cleanup();
|
|
209
152
|
});
|
|
210
|
-
} else cleanup();
|
|
153
|
+
} else await cleanup();
|
|
211
154
|
|
|
212
|
-
if (!this.isClientSide
|
|
155
|
+
if (!this.isClientSide
|
|
156
|
+
&& response instanceof LiveResponse) {
|
|
213
157
|
// Must convert to Response on the server-side before returning
|
|
214
|
-
|
|
158
|
+
const outgoingResponse = response.toResponse({ port: httpEvent.client });
|
|
159
|
+
return outgoingResponse;
|
|
215
160
|
}
|
|
216
161
|
|
|
217
162
|
return response;
|
|
218
163
|
}
|
|
219
164
|
|
|
220
165
|
async handleThreadStatus(httpEvent, response) {
|
|
221
|
-
if (
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
}
|
|
166
|
+
if ((response instanceof Response
|
|
167
|
+
|| response instanceof LiveResponse)
|
|
168
|
+
&& response.headers.get('Location')) return;
|
|
169
|
+
|
|
170
|
+
const status = await httpEvent.thread.consume('status', true);
|
|
171
|
+
if (!status.length) return;
|
|
172
|
+
|
|
173
|
+
httpEvent.waitUntil(httpEvent.client.readyStateChange('open').then(async () => {
|
|
174
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
175
|
+
httpEvent.client.postMessage(status, { type: 'alert' });
|
|
176
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
177
|
+
}));
|
|
234
178
|
}
|
|
235
179
|
|
|
236
180
|
streamSlice(stream, { start, end }) {
|
|
@@ -328,13 +272,13 @@ export class WebfloRuntime {
|
|
|
328
272
|
|
|
329
273
|
createStreamingResponse(httpEvent, readStream, stats) {
|
|
330
274
|
let response;
|
|
331
|
-
const requestRange =
|
|
275
|
+
const requestRange = httpEvent.request.headers.get('Range', true); // Parses the Range header
|
|
332
276
|
if (requestRange.length) {
|
|
333
277
|
const streams = requestRange.reduce((streams, range) => {
|
|
334
278
|
if (!streams) return;
|
|
335
|
-
const [start, end] = range.
|
|
279
|
+
const [start, end] = range.resolveAgainst(stats.size); // Resolve offsets
|
|
336
280
|
const currentStart = (streams[streams.length - 1]?.end || -1) + 1;
|
|
337
|
-
if (!range.
|
|
281
|
+
if (!range.canResolveAgainst(currentStart, stats.size)) return; // Only after rendering()
|
|
338
282
|
return streams.concat({ start, end, stream: readStream({ start, end }) });
|
|
339
283
|
}, []);
|
|
340
284
|
if (!streams) {
|