one 2.6.4 → 2.6.5
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/dist/cjs/server/assetRange.cjs +84 -16
- package/dist/cjs/server/assetRange.native.cjs +102 -16
- package/dist/cjs/server/assetRange.native.js +102 -16
- package/dist/cjs/server/assetRange.native.js.map +1 -1
- package/dist/esm/server/assetRange.mjs +84 -16
- package/dist/esm/server/assetRange.mjs.map +1 -1
- package/dist/esm/server/assetRange.native.js +102 -16
- package/dist/esm/server/assetRange.native.js.map +1 -1
- package/nitrogen/generated/ios/One-Swift-Cxx-Bridge.hpp +1 -10
- package/package.json +12 -12
- package/src/server/assetRange.test.ts +50 -10
- package/src/server/assetRange.ts +90 -14
- package/types/server/assetRange.d.ts.map +1 -1
|
@@ -23,35 +23,103 @@ __export(assetRange_exports, { withRange: () => withRange });
|
|
|
23
23
|
module.exports = __toCommonJS(assetRange_exports);
|
|
24
24
|
async function withRange(request, response) {
|
|
25
25
|
const range = request.headers.get("range");
|
|
26
|
-
if (!range || request.method !== "GET" || response.status !== 200 || response.headers.has("content-range") || response.headers.has("content-encoding")) {
|
|
26
|
+
if (!range || request.method !== "GET" || response.status !== 200 || !response.body || response.headers.has("content-range") || response.headers.has("content-encoding") || !ifRangeMatches(request.headers.get("if-range"), response.headers)) {
|
|
27
27
|
return response;
|
|
28
28
|
}
|
|
29
29
|
const match = /^bytes=(\d*)-(\d*)$/.exec(range.trim());
|
|
30
30
|
if (!match || !match[1] && !match[2]) return response;
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
let start;
|
|
34
|
-
let end;
|
|
35
|
-
if (!match[1]) {
|
|
36
|
-
start = Math.max(0, size - Number(match[2]));
|
|
37
|
-
end = size - 1;
|
|
38
|
-
} else {
|
|
39
|
-
start = Number(match[1]);
|
|
40
|
-
end = match[2] ? Math.min(Number(match[2]), size - 1) : size - 1;
|
|
41
|
-
}
|
|
31
|
+
const length = response.headers.get("content-length");
|
|
32
|
+
const known = length && /^\d+$/.test(length) ? Number(length) : void 0;
|
|
42
33
|
const headers = new Headers(response.headers);
|
|
43
34
|
headers.set("accept-ranges", "bytes");
|
|
44
|
-
|
|
35
|
+
headers.delete("content-length");
|
|
36
|
+
const { size, kept, keptStart } = await readRange(response.body, match, known);
|
|
37
|
+
const span = resolve(match, size);
|
|
38
|
+
if (!span) {
|
|
45
39
|
headers.set("content-range", `bytes */${size}`);
|
|
46
40
|
return new Response(null, {
|
|
47
41
|
status: 416,
|
|
48
42
|
headers
|
|
49
43
|
});
|
|
50
44
|
}
|
|
51
|
-
headers.set("content-range", `bytes ${start}-${end}/${size}`);
|
|
52
|
-
headers.set("content-length", String(end - start + 1));
|
|
53
|
-
return new Response(
|
|
45
|
+
headers.set("content-range", `bytes ${span.start}-${span.end}/${size}`);
|
|
46
|
+
headers.set("content-length", String(span.end - span.start + 1));
|
|
47
|
+
return new Response(kept.subarray(span.start - keptStart, span.end - keptStart + 1), {
|
|
54
48
|
status: 206,
|
|
55
49
|
headers
|
|
56
50
|
});
|
|
57
51
|
}
|
|
52
|
+
function ifRangeMatches(ifRange, headers) {
|
|
53
|
+
if (!ifRange) return true;
|
|
54
|
+
const value = ifRange.trim();
|
|
55
|
+
if (value.startsWith("\"")) {
|
|
56
|
+
const etag = headers.get("etag");
|
|
57
|
+
return !!etag && !etag.startsWith("W/") && etag === value;
|
|
58
|
+
}
|
|
59
|
+
if (value.startsWith("W/")) return false;
|
|
60
|
+
return headers.get("last-modified") === value;
|
|
61
|
+
}
|
|
62
|
+
function resolve(match, size) {
|
|
63
|
+
let start;
|
|
64
|
+
let end;
|
|
65
|
+
if (!match[1]) {
|
|
66
|
+
start = Math.max(0, size - Number(match[2]));
|
|
67
|
+
end = size - 1;
|
|
68
|
+
} else {
|
|
69
|
+
start = Number(match[1]);
|
|
70
|
+
end = match[2] ? Math.min(Number(match[2]), size - 1) : size - 1;
|
|
71
|
+
}
|
|
72
|
+
if (start >= size || start > end) return;
|
|
73
|
+
return {
|
|
74
|
+
start,
|
|
75
|
+
end
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
async function readRange(body, match, known) {
|
|
79
|
+
const span = known === void 0 ? void 0 : resolve(match, known) ?? {
|
|
80
|
+
start: known,
|
|
81
|
+
end: -1
|
|
82
|
+
};
|
|
83
|
+
const suffix = !span && !match[1] ? Number(match[2]) : 0;
|
|
84
|
+
const from = span ? span.start : match[1] ? Number(match[1]) : 0;
|
|
85
|
+
const to = span ? span.end : match[2] && match[1] ? Number(match[2]) : Number.POSITIVE_INFINITY;
|
|
86
|
+
const chunks = [];
|
|
87
|
+
let keptBytes = 0;
|
|
88
|
+
let size = 0;
|
|
89
|
+
const reader = body.getReader();
|
|
90
|
+
while (known === void 0 || size <= to) {
|
|
91
|
+
const { done, value } = await reader.read();
|
|
92
|
+
if (done) break;
|
|
93
|
+
const chunkStart = size;
|
|
94
|
+
size += value.byteLength;
|
|
95
|
+
if (suffix) {
|
|
96
|
+
chunks.push(value);
|
|
97
|
+
keptBytes += value.byteLength;
|
|
98
|
+
while (chunks.length > 1 && keptBytes - chunks[0].byteLength >= suffix) {
|
|
99
|
+
keptBytes -= chunks.shift().byteLength;
|
|
100
|
+
}
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
const lo = Math.max(from, chunkStart);
|
|
104
|
+
const hi = Math.min(to + 1, size);
|
|
105
|
+
if (hi > lo) {
|
|
106
|
+
chunks.push(value.subarray(lo - chunkStart, hi - chunkStart));
|
|
107
|
+
keptBytes += hi - lo;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (known !== void 0) {
|
|
111
|
+
await reader.cancel();
|
|
112
|
+
size = known;
|
|
113
|
+
}
|
|
114
|
+
const kept = new Uint8Array(keptBytes);
|
|
115
|
+
let at = 0;
|
|
116
|
+
for (const chunk of chunks) {
|
|
117
|
+
kept.set(chunk, at);
|
|
118
|
+
at += chunk.byteLength;
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
size,
|
|
122
|
+
kept,
|
|
123
|
+
keptStart: suffix ? size - keptBytes : from
|
|
124
|
+
};
|
|
125
|
+
}
|
|
@@ -25,35 +25,121 @@ __export(assetRange_exports, { withRange: () => withRange });
|
|
|
25
25
|
module.exports = __toCommonJS(assetRange_exports);
|
|
26
26
|
async function withRange(request, response) {
|
|
27
27
|
var range = request.headers.get("range");
|
|
28
|
-
if (!range || request.method !== "GET" || response.status !== 200 || response.headers.has("content-range") || response.headers.has("content-encoding")) {
|
|
28
|
+
if (!range || request.method !== "GET" || response.status !== 200 || !response.body || response.headers.has("content-range") || response.headers.has("content-encoding") || !ifRangeMatches(request.headers.get("if-range"), response.headers)) {
|
|
29
29
|
return response;
|
|
30
30
|
}
|
|
31
31
|
var match = /^bytes=(\d*)-(\d*)$/.exec(range.trim());
|
|
32
32
|
if (!match || !match[1] && !match[2]) return response;
|
|
33
|
-
var
|
|
34
|
-
var
|
|
35
|
-
var start;
|
|
36
|
-
var end;
|
|
37
|
-
if (!match[1]) {
|
|
38
|
-
start = Math.max(0, size - Number(match[2]));
|
|
39
|
-
end = size - 1;
|
|
40
|
-
} else {
|
|
41
|
-
start = Number(match[1]);
|
|
42
|
-
end = match[2] ? Math.min(Number(match[2]), size - 1) : size - 1;
|
|
43
|
-
}
|
|
33
|
+
var length = response.headers.get("content-length");
|
|
34
|
+
var known = length && /^\d+$/.test(length) ? Number(length) : void 0;
|
|
44
35
|
var headers = new Headers(response.headers);
|
|
45
36
|
headers.set("accept-ranges", "bytes");
|
|
46
|
-
|
|
37
|
+
headers.delete("content-length");
|
|
38
|
+
var { size, kept, keptStart } = await readRange(response.body, match, known);
|
|
39
|
+
var span = resolve(match, size);
|
|
40
|
+
if (!span) {
|
|
47
41
|
headers.set("content-range", `bytes */${size}`);
|
|
48
42
|
return new Response(null, {
|
|
49
43
|
status: 416,
|
|
50
44
|
headers
|
|
51
45
|
});
|
|
52
46
|
}
|
|
53
|
-
headers.set("content-range", `bytes ${start}-${end}/${size}`);
|
|
54
|
-
headers.set("content-length", String(end - start + 1));
|
|
55
|
-
return new Response(
|
|
47
|
+
headers.set("content-range", `bytes ${span.start}-${span.end}/${size}`);
|
|
48
|
+
headers.set("content-length", String(span.end - span.start + 1));
|
|
49
|
+
return new Response(kept.subarray(span.start - keptStart, span.end - keptStart + 1), {
|
|
56
50
|
status: 206,
|
|
57
51
|
headers
|
|
58
52
|
});
|
|
59
53
|
}
|
|
54
|
+
function ifRangeMatches(ifRange, headers) {
|
|
55
|
+
if (!ifRange) return true;
|
|
56
|
+
var value = ifRange.trim();
|
|
57
|
+
if (value.startsWith("\"")) {
|
|
58
|
+
var etag = headers.get("etag");
|
|
59
|
+
return !!etag && !etag.startsWith("W/") && etag === value;
|
|
60
|
+
}
|
|
61
|
+
if (value.startsWith("W/")) return false;
|
|
62
|
+
return headers.get("last-modified") === value;
|
|
63
|
+
}
|
|
64
|
+
function resolve(match, size) {
|
|
65
|
+
var start;
|
|
66
|
+
var end;
|
|
67
|
+
if (!match[1]) {
|
|
68
|
+
start = Math.max(0, size - Number(match[2]));
|
|
69
|
+
end = size - 1;
|
|
70
|
+
} else {
|
|
71
|
+
start = Number(match[1]);
|
|
72
|
+
end = match[2] ? Math.min(Number(match[2]), size - 1) : size - 1;
|
|
73
|
+
}
|
|
74
|
+
if (start >= size || start > end) return;
|
|
75
|
+
return {
|
|
76
|
+
start,
|
|
77
|
+
end
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
async function readRange(body, match, known) {
|
|
81
|
+
var _resolve;
|
|
82
|
+
var span = known === void 0 ? void 0 : (_resolve = resolve(match, known)) !== null && _resolve !== void 0 ? _resolve : {
|
|
83
|
+
start: known,
|
|
84
|
+
end: -1
|
|
85
|
+
};
|
|
86
|
+
var suffix = !span && !match[1] ? Number(match[2]) : 0;
|
|
87
|
+
var from = span ? span.start : match[1] ? Number(match[1]) : 0;
|
|
88
|
+
var to = span ? span.end : match[2] && match[1] ? Number(match[2]) : Number.POSITIVE_INFINITY;
|
|
89
|
+
var chunks = [];
|
|
90
|
+
var keptBytes = 0;
|
|
91
|
+
var size = 0;
|
|
92
|
+
var reader = body.getReader();
|
|
93
|
+
while (known === void 0 || size <= to) {
|
|
94
|
+
var { done, value } = await reader.read();
|
|
95
|
+
if (done) break;
|
|
96
|
+
var chunkStart = size;
|
|
97
|
+
size += value.byteLength;
|
|
98
|
+
if (suffix) {
|
|
99
|
+
chunks.push(value);
|
|
100
|
+
keptBytes += value.byteLength;
|
|
101
|
+
while (chunks.length > 1 && keptBytes - chunks[0].byteLength >= suffix) {
|
|
102
|
+
keptBytes -= chunks.shift().byteLength;
|
|
103
|
+
}
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
var lo = Math.max(from, chunkStart);
|
|
107
|
+
var hi = Math.min(to + 1, size);
|
|
108
|
+
if (hi > lo) {
|
|
109
|
+
chunks.push(value.subarray(lo - chunkStart, hi - chunkStart));
|
|
110
|
+
keptBytes += hi - lo;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (known !== void 0) {
|
|
114
|
+
await reader.cancel();
|
|
115
|
+
size = known;
|
|
116
|
+
}
|
|
117
|
+
var kept = new Uint8Array(keptBytes);
|
|
118
|
+
var at = 0;
|
|
119
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = void 0;
|
|
120
|
+
try {
|
|
121
|
+
for (var _iterator = chunks[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
|
122
|
+
var chunk = _step.value;
|
|
123
|
+
kept.set(chunk, at);
|
|
124
|
+
at += chunk.byteLength;
|
|
125
|
+
}
|
|
126
|
+
} catch (err) {
|
|
127
|
+
_didIteratorError = true;
|
|
128
|
+
_iteratorError = err;
|
|
129
|
+
} finally {
|
|
130
|
+
try {
|
|
131
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
132
|
+
_iterator.return();
|
|
133
|
+
}
|
|
134
|
+
} finally {
|
|
135
|
+
if (_didIteratorError) {
|
|
136
|
+
throw _iteratorError;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
size,
|
|
142
|
+
kept,
|
|
143
|
+
keptStart: suffix ? size - keptBytes : from
|
|
144
|
+
};
|
|
145
|
+
}
|
|
@@ -25,37 +25,123 @@ __export(assetRange_exports, { withRange: () => withRange });
|
|
|
25
25
|
module.exports = __toCommonJS(assetRange_exports);
|
|
26
26
|
async function withRange(request, response) {
|
|
27
27
|
var range = request.headers.get("range");
|
|
28
|
-
if (!range || request.method !== "GET" || response.status !== 200 || response.headers.has("content-range") || response.headers.has("content-encoding")) {
|
|
28
|
+
if (!range || request.method !== "GET" || response.status !== 200 || !response.body || response.headers.has("content-range") || response.headers.has("content-encoding") || !ifRangeMatches(request.headers.get("if-range"), response.headers)) {
|
|
29
29
|
return response;
|
|
30
30
|
}
|
|
31
31
|
var match = /^bytes=(\d*)-(\d*)$/.exec(range.trim());
|
|
32
32
|
if (!match || !match[1] && !match[2]) return response;
|
|
33
|
-
var
|
|
34
|
-
var
|
|
35
|
-
var start;
|
|
36
|
-
var end;
|
|
37
|
-
if (!match[1]) {
|
|
38
|
-
start = Math.max(0, size - Number(match[2]));
|
|
39
|
-
end = size - 1;
|
|
40
|
-
} else {
|
|
41
|
-
start = Number(match[1]);
|
|
42
|
-
end = match[2] ? Math.min(Number(match[2]), size - 1) : size - 1;
|
|
43
|
-
}
|
|
33
|
+
var length = response.headers.get("content-length");
|
|
34
|
+
var known = length && /^\d+$/.test(length) ? Number(length) : void 0;
|
|
44
35
|
var headers = new Headers(response.headers);
|
|
45
36
|
headers.set("accept-ranges", "bytes");
|
|
46
|
-
|
|
37
|
+
headers.delete("content-length");
|
|
38
|
+
var { size, kept, keptStart } = await readRange(response.body, match, known);
|
|
39
|
+
var span = resolve(match, size);
|
|
40
|
+
if (!span) {
|
|
47
41
|
headers.set("content-range", `bytes */${size}`);
|
|
48
42
|
return new Response(null, {
|
|
49
43
|
status: 416,
|
|
50
44
|
headers
|
|
51
45
|
});
|
|
52
46
|
}
|
|
53
|
-
headers.set("content-range", `bytes ${start}-${end}/${size}`);
|
|
54
|
-
headers.set("content-length", String(end - start + 1));
|
|
55
|
-
return new Response(
|
|
47
|
+
headers.set("content-range", `bytes ${span.start}-${span.end}/${size}`);
|
|
48
|
+
headers.set("content-length", String(span.end - span.start + 1));
|
|
49
|
+
return new Response(kept.subarray(span.start - keptStart, span.end - keptStart + 1), {
|
|
56
50
|
status: 206,
|
|
57
51
|
headers
|
|
58
52
|
});
|
|
59
53
|
}
|
|
54
|
+
function ifRangeMatches(ifRange, headers) {
|
|
55
|
+
if (!ifRange) return true;
|
|
56
|
+
var value = ifRange.trim();
|
|
57
|
+
if (value.startsWith("\"")) {
|
|
58
|
+
var etag = headers.get("etag");
|
|
59
|
+
return !!etag && !etag.startsWith("W/") && etag === value;
|
|
60
|
+
}
|
|
61
|
+
if (value.startsWith("W/")) return false;
|
|
62
|
+
return headers.get("last-modified") === value;
|
|
63
|
+
}
|
|
64
|
+
function resolve(match, size) {
|
|
65
|
+
var start;
|
|
66
|
+
var end;
|
|
67
|
+
if (!match[1]) {
|
|
68
|
+
start = Math.max(0, size - Number(match[2]));
|
|
69
|
+
end = size - 1;
|
|
70
|
+
} else {
|
|
71
|
+
start = Number(match[1]);
|
|
72
|
+
end = match[2] ? Math.min(Number(match[2]), size - 1) : size - 1;
|
|
73
|
+
}
|
|
74
|
+
if (start >= size || start > end) return;
|
|
75
|
+
return {
|
|
76
|
+
start,
|
|
77
|
+
end
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
async function readRange(body, match, known) {
|
|
81
|
+
var _resolve;
|
|
82
|
+
var span = known === void 0 ? void 0 : (_resolve = resolve(match, known)) !== null && _resolve !== void 0 ? _resolve : {
|
|
83
|
+
start: known,
|
|
84
|
+
end: -1
|
|
85
|
+
};
|
|
86
|
+
var suffix = !span && !match[1] ? Number(match[2]) : 0;
|
|
87
|
+
var from = span ? span.start : match[1] ? Number(match[1]) : 0;
|
|
88
|
+
var to = span ? span.end : match[2] && match[1] ? Number(match[2]) : Number.POSITIVE_INFINITY;
|
|
89
|
+
var chunks = [];
|
|
90
|
+
var keptBytes = 0;
|
|
91
|
+
var size = 0;
|
|
92
|
+
var reader = body.getReader();
|
|
93
|
+
while (known === void 0 || size <= to) {
|
|
94
|
+
var { done, value } = await reader.read();
|
|
95
|
+
if (done) break;
|
|
96
|
+
var chunkStart = size;
|
|
97
|
+
size += value.byteLength;
|
|
98
|
+
if (suffix) {
|
|
99
|
+
chunks.push(value);
|
|
100
|
+
keptBytes += value.byteLength;
|
|
101
|
+
while (chunks.length > 1 && keptBytes - chunks[0].byteLength >= suffix) {
|
|
102
|
+
keptBytes -= chunks.shift().byteLength;
|
|
103
|
+
}
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
var lo = Math.max(from, chunkStart);
|
|
107
|
+
var hi = Math.min(to + 1, size);
|
|
108
|
+
if (hi > lo) {
|
|
109
|
+
chunks.push(value.subarray(lo - chunkStart, hi - chunkStart));
|
|
110
|
+
keptBytes += hi - lo;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (known !== void 0) {
|
|
114
|
+
await reader.cancel();
|
|
115
|
+
size = known;
|
|
116
|
+
}
|
|
117
|
+
var kept = new Uint8Array(keptBytes);
|
|
118
|
+
var at = 0;
|
|
119
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = void 0;
|
|
120
|
+
try {
|
|
121
|
+
for (var _iterator = chunks[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
|
122
|
+
var chunk = _step.value;
|
|
123
|
+
kept.set(chunk, at);
|
|
124
|
+
at += chunk.byteLength;
|
|
125
|
+
}
|
|
126
|
+
} catch (err) {
|
|
127
|
+
_didIteratorError = true;
|
|
128
|
+
_iteratorError = err;
|
|
129
|
+
} finally {
|
|
130
|
+
try {
|
|
131
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
132
|
+
_iterator.return();
|
|
133
|
+
}
|
|
134
|
+
} finally {
|
|
135
|
+
if (_didIteratorError) {
|
|
136
|
+
throw _iteratorError;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
size,
|
|
142
|
+
kept,
|
|
143
|
+
keptStart: suffix ? size - keptBytes : from
|
|
144
|
+
};
|
|
145
|
+
}
|
|
60
146
|
|
|
61
147
|
//# sourceMappingURL=assetRange.native.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assetRange.native.js","names":[],"sources":["cjs/server/assetRange.native.js"],"sourcesContent":["\"use strict\";\nvar __defProp = Object.defineProperty;\nvar __getOwnPropDesc = Object.getOwnPropertyDescriptor;\nvar __getOwnPropNames = Object.getOwnPropertyNames;\nvar __hasOwnProp = Object.prototype.hasOwnProperty;\nvar __export = (target, all) => {\n for (var name in all)\n __defProp(target, name, { get: all[name], enumerable: true });\n};\nvar __copyProps = (to, from, except, desc) => {\n if (from && typeof from === \"object\" || typeof from === \"function\") {\n for (let key of __getOwnPropNames(from))\n if (!__hasOwnProp.call(to, key) && key !== except)\n __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });\n }\n return to;\n};\nvar __toCommonJS = (mod) => __copyProps(__defProp({}, \"__esModule\", { value: true }), mod);\nvar assetRange_exports = {};\n__export(assetRange_exports, {\n withRange: () => withRange\n});\nmodule.exports = __toCommonJS(assetRange_exports);\nasync function withRange(request, response) {\n var range = request.headers.get(\"range\");\n if (!range || request.method !== \"GET\" || response.status !== 200 || response.headers.has(\"content-range\") || response.headers.has(\"content-encoding\")) {\n return response;\n }\n var match = /^bytes=(\\d*)-(\\d*)$/.exec(range.trim());\n if (!match || !match[1] && !match[2]) return response;\n var
|
|
1
|
+
{"version":3,"file":"assetRange.native.js","names":[],"sources":["cjs/server/assetRange.native.js"],"sourcesContent":["\"use strict\";\nvar __defProp = Object.defineProperty;\nvar __getOwnPropDesc = Object.getOwnPropertyDescriptor;\nvar __getOwnPropNames = Object.getOwnPropertyNames;\nvar __hasOwnProp = Object.prototype.hasOwnProperty;\nvar __export = (target, all) => {\n for (var name in all)\n __defProp(target, name, { get: all[name], enumerable: true });\n};\nvar __copyProps = (to, from, except, desc) => {\n if (from && typeof from === \"object\" || typeof from === \"function\") {\n for (let key of __getOwnPropNames(from))\n if (!__hasOwnProp.call(to, key) && key !== except)\n __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });\n }\n return to;\n};\nvar __toCommonJS = (mod) => __copyProps(__defProp({}, \"__esModule\", { value: true }), mod);\nvar assetRange_exports = {};\n__export(assetRange_exports, {\n withRange: () => withRange\n});\nmodule.exports = __toCommonJS(assetRange_exports);\nasync function withRange(request, response) {\n var range = request.headers.get(\"range\");\n if (!range || request.method !== \"GET\" || response.status !== 200 || !response.body || response.headers.has(\"content-range\") || response.headers.has(\"content-encoding\") || !ifRangeMatches(request.headers.get(\"if-range\"), response.headers)) {\n return response;\n }\n var match = /^bytes=(\\d*)-(\\d*)$/.exec(range.trim());\n if (!match || !match[1] && !match[2]) return response;\n var length = response.headers.get(\"content-length\");\n var known = length && /^\\d+$/.test(length) ? Number(length) : void 0;\n var headers = new Headers(response.headers);\n headers.set(\"accept-ranges\", \"bytes\");\n headers.delete(\"content-length\");\n var { size, kept, keptStart } = await readRange(response.body, match, known);\n var span = resolve(match, size);\n if (!span) {\n headers.set(\"content-range\", `bytes */${size}`);\n return new Response(null, {\n status: 416,\n headers\n });\n }\n headers.set(\"content-range\", `bytes ${span.start}-${span.end}/${size}`);\n headers.set(\"content-length\", String(span.end - span.start + 1));\n return new Response(kept.subarray(span.start - keptStart, span.end - keptStart + 1), {\n status: 206,\n headers\n });\n}\nfunction ifRangeMatches(ifRange, headers) {\n if (!ifRange) return true;\n var value = ifRange.trim();\n if (value.startsWith('\"')) {\n var etag = headers.get(\"etag\");\n return !!etag && !etag.startsWith(\"W/\") && etag === value;\n }\n if (value.startsWith(\"W/\")) return false;\n return headers.get(\"last-modified\") === value;\n}\nfunction resolve(match, size) {\n var start;\n var end;\n if (!match[1]) {\n start = Math.max(0, size - Number(match[2]));\n end = size - 1;\n } else {\n start = Number(match[1]);\n end = match[2] ? Math.min(Number(match[2]), size - 1) : size - 1;\n }\n if (start >= size || start > end) return;\n return {\n start,\n end\n };\n}\nasync function readRange(body, match, known) {\n var _resolve;\n var span = known === void 0 ? void 0 : (_resolve = resolve(match, known)) !== null && _resolve !== void 0 ? _resolve : {\n start: known,\n end: -1\n };\n var suffix = !span && !match[1] ? Number(match[2]) : 0;\n var from = span ? span.start : match[1] ? Number(match[1]) : 0;\n var to = span ? span.end : match[2] && match[1] ? Number(match[2]) : Number.POSITIVE_INFINITY;\n var chunks = [];\n var keptBytes = 0;\n var size = 0;\n var reader = body.getReader();\n while (known === void 0 || size <= to) {\n var { done, value } = await reader.read();\n if (done) break;\n var chunkStart = size;\n size += value.byteLength;\n if (suffix) {\n chunks.push(value);\n keptBytes += value.byteLength;\n while (chunks.length > 1 && keptBytes - chunks[0].byteLength >= suffix) {\n keptBytes -= chunks.shift().byteLength;\n }\n continue;\n }\n var lo = Math.max(from, chunkStart);\n var hi = Math.min(to + 1, size);\n if (hi > lo) {\n chunks.push(value.subarray(lo - chunkStart, hi - chunkStart));\n keptBytes += hi - lo;\n }\n }\n if (known !== void 0) {\n await reader.cancel();\n size = known;\n }\n var kept = new Uint8Array(keptBytes);\n var at = 0;\n var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = void 0;\n try {\n for (var _iterator = chunks[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {\n var chunk = _step.value;\n kept.set(chunk, at);\n at += chunk.byteLength;\n }\n } catch (err) {\n _didIteratorError = true;\n _iteratorError = err;\n } finally {\n try {\n if (!_iteratorNormalCompletion && _iterator.return != null) {\n _iterator.return();\n }\n } finally {\n if (_didIteratorError) {\n throw _iteratorError;\n }\n }\n }\n return {\n size,\n kept,\n keptStart: suffix ? size - keptBytes : from\n };\n}\n//# sourceMappingURL=assetRange.js.map\n"],"mappings":"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"}
|
|
@@ -1,37 +1,105 @@
|
|
|
1
1
|
async function withRange(request, response) {
|
|
2
2
|
const range = request.headers.get("range");
|
|
3
|
-
if (!range || request.method !== "GET" || response.status !== 200 || response.headers.has("content-range") || response.headers.has("content-encoding")) {
|
|
3
|
+
if (!range || request.method !== "GET" || response.status !== 200 || !response.body || response.headers.has("content-range") || response.headers.has("content-encoding") || !ifRangeMatches(request.headers.get("if-range"), response.headers)) {
|
|
4
4
|
return response;
|
|
5
5
|
}
|
|
6
6
|
const match = /^bytes=(\d*)-(\d*)$/.exec(range.trim());
|
|
7
7
|
if (!match || !match[1] && !match[2]) return response;
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
let start;
|
|
11
|
-
let end;
|
|
12
|
-
if (!match[1]) {
|
|
13
|
-
start = Math.max(0, size - Number(match[2]));
|
|
14
|
-
end = size - 1;
|
|
15
|
-
} else {
|
|
16
|
-
start = Number(match[1]);
|
|
17
|
-
end = match[2] ? Math.min(Number(match[2]), size - 1) : size - 1;
|
|
18
|
-
}
|
|
8
|
+
const length = response.headers.get("content-length");
|
|
9
|
+
const known = length && /^\d+$/.test(length) ? Number(length) : void 0;
|
|
19
10
|
const headers = new Headers(response.headers);
|
|
20
11
|
headers.set("accept-ranges", "bytes");
|
|
21
|
-
|
|
12
|
+
headers.delete("content-length");
|
|
13
|
+
const { size, kept, keptStart } = await readRange(response.body, match, known);
|
|
14
|
+
const span = resolve(match, size);
|
|
15
|
+
if (!span) {
|
|
22
16
|
headers.set("content-range", `bytes */${size}`);
|
|
23
17
|
return new Response(null, {
|
|
24
18
|
status: 416,
|
|
25
19
|
headers
|
|
26
20
|
});
|
|
27
21
|
}
|
|
28
|
-
headers.set("content-range", `bytes ${start}-${end}/${size}`);
|
|
29
|
-
headers.set("content-length", String(end - start + 1));
|
|
30
|
-
return new Response(
|
|
22
|
+
headers.set("content-range", `bytes ${span.start}-${span.end}/${size}`);
|
|
23
|
+
headers.set("content-length", String(span.end - span.start + 1));
|
|
24
|
+
return new Response(kept.subarray(span.start - keptStart, span.end - keptStart + 1), {
|
|
31
25
|
status: 206,
|
|
32
26
|
headers
|
|
33
27
|
});
|
|
34
28
|
}
|
|
29
|
+
function ifRangeMatches(ifRange, headers) {
|
|
30
|
+
if (!ifRange) return true;
|
|
31
|
+
const value = ifRange.trim();
|
|
32
|
+
if (value.startsWith("\"")) {
|
|
33
|
+
const etag = headers.get("etag");
|
|
34
|
+
return !!etag && !etag.startsWith("W/") && etag === value;
|
|
35
|
+
}
|
|
36
|
+
if (value.startsWith("W/")) return false;
|
|
37
|
+
return headers.get("last-modified") === value;
|
|
38
|
+
}
|
|
39
|
+
function resolve(match, size) {
|
|
40
|
+
let start;
|
|
41
|
+
let end;
|
|
42
|
+
if (!match[1]) {
|
|
43
|
+
start = Math.max(0, size - Number(match[2]));
|
|
44
|
+
end = size - 1;
|
|
45
|
+
} else {
|
|
46
|
+
start = Number(match[1]);
|
|
47
|
+
end = match[2] ? Math.min(Number(match[2]), size - 1) : size - 1;
|
|
48
|
+
}
|
|
49
|
+
if (start >= size || start > end) return;
|
|
50
|
+
return {
|
|
51
|
+
start,
|
|
52
|
+
end
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
async function readRange(body, match, known) {
|
|
56
|
+
const span = known === void 0 ? void 0 : resolve(match, known) ?? {
|
|
57
|
+
start: known,
|
|
58
|
+
end: -1
|
|
59
|
+
};
|
|
60
|
+
const suffix = !span && !match[1] ? Number(match[2]) : 0;
|
|
61
|
+
const from = span ? span.start : match[1] ? Number(match[1]) : 0;
|
|
62
|
+
const to = span ? span.end : match[2] && match[1] ? Number(match[2]) : Number.POSITIVE_INFINITY;
|
|
63
|
+
const chunks = [];
|
|
64
|
+
let keptBytes = 0;
|
|
65
|
+
let size = 0;
|
|
66
|
+
const reader = body.getReader();
|
|
67
|
+
while (known === void 0 || size <= to) {
|
|
68
|
+
const { done, value } = await reader.read();
|
|
69
|
+
if (done) break;
|
|
70
|
+
const chunkStart = size;
|
|
71
|
+
size += value.byteLength;
|
|
72
|
+
if (suffix) {
|
|
73
|
+
chunks.push(value);
|
|
74
|
+
keptBytes += value.byteLength;
|
|
75
|
+
while (chunks.length > 1 && keptBytes - chunks[0].byteLength >= suffix) {
|
|
76
|
+
keptBytes -= chunks.shift().byteLength;
|
|
77
|
+
}
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
const lo = Math.max(from, chunkStart);
|
|
81
|
+
const hi = Math.min(to + 1, size);
|
|
82
|
+
if (hi > lo) {
|
|
83
|
+
chunks.push(value.subarray(lo - chunkStart, hi - chunkStart));
|
|
84
|
+
keptBytes += hi - lo;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (known !== void 0) {
|
|
88
|
+
await reader.cancel();
|
|
89
|
+
size = known;
|
|
90
|
+
}
|
|
91
|
+
const kept = new Uint8Array(keptBytes);
|
|
92
|
+
let at = 0;
|
|
93
|
+
for (const chunk of chunks) {
|
|
94
|
+
kept.set(chunk, at);
|
|
95
|
+
at += chunk.byteLength;
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
size,
|
|
99
|
+
kept,
|
|
100
|
+
keptStart: suffix ? size - keptBytes : from
|
|
101
|
+
};
|
|
102
|
+
}
|
|
35
103
|
|
|
36
104
|
export { withRange };
|
|
37
105
|
//# sourceMappingURL=assetRange.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assetRange.js","names":[],"sources":["esm/server/assetRange.js"],"sourcesContent":["async function withRange(request, response) {\n const range = request.headers.get(\"range\");\n if (!range || request.method !== \"GET\" || response.status !== 200 || response.headers.has(\"content-range\") || response.headers.has(\"content-encoding\")) {\n return response;\n }\n const match = /^bytes=(\\d*)-(\\d*)$/.exec(range.trim());\n if (!match || !match[1] && !match[2]) return response;\n const
|
|
1
|
+
{"version":3,"file":"assetRange.js","names":[],"sources":["esm/server/assetRange.js"],"sourcesContent":["async function withRange(request, response) {\n const range = request.headers.get(\"range\");\n if (!range || request.method !== \"GET\" || response.status !== 200 || !response.body || response.headers.has(\"content-range\") || response.headers.has(\"content-encoding\") || !ifRangeMatches(request.headers.get(\"if-range\"), response.headers)) {\n return response;\n }\n const match = /^bytes=(\\d*)-(\\d*)$/.exec(range.trim());\n if (!match || !match[1] && !match[2]) return response;\n const length = response.headers.get(\"content-length\");\n const known = length && /^\\d+$/.test(length) ? Number(length) : void 0;\n const headers = new Headers(response.headers);\n headers.set(\"accept-ranges\", \"bytes\");\n headers.delete(\"content-length\");\n const { size, kept, keptStart } = await readRange(response.body, match, known);\n const span = resolve(match, size);\n if (!span) {\n headers.set(\"content-range\", `bytes */${size}`);\n return new Response(null, { status: 416, headers });\n }\n headers.set(\"content-range\", `bytes ${span.start}-${span.end}/${size}`);\n headers.set(\"content-length\", String(span.end - span.start + 1));\n return new Response(kept.subarray(span.start - keptStart, span.end - keptStart + 1), {\n status: 206,\n headers\n });\n}\nfunction ifRangeMatches(ifRange, headers) {\n if (!ifRange) return true;\n const value = ifRange.trim();\n if (value.startsWith('\"')) {\n const etag = headers.get(\"etag\");\n return !!etag && !etag.startsWith(\"W/\") && etag === value;\n }\n if (value.startsWith(\"W/\")) return false;\n return headers.get(\"last-modified\") === value;\n}\nfunction resolve(match, size) {\n let start;\n let end;\n if (!match[1]) {\n start = Math.max(0, size - Number(match[2]));\n end = size - 1;\n } else {\n start = Number(match[1]);\n end = match[2] ? Math.min(Number(match[2]), size - 1) : size - 1;\n }\n if (start >= size || start > end) return;\n return { start, end };\n}\nasync function readRange(body, match, known) {\n const span = known === void 0 ? void 0 : resolve(match, known) ?? { start: known, end: -1 };\n const suffix = !span && !match[1] ? Number(match[2]) : 0;\n const from = span ? span.start : match[1] ? Number(match[1]) : 0;\n const to = span ? span.end : match[2] && match[1] ? Number(match[2]) : Number.POSITIVE_INFINITY;\n const chunks = [];\n let keptBytes = 0;\n let size = 0;\n const reader = body.getReader();\n while (known === void 0 || size <= to) {\n const { done, value } = await reader.read();\n if (done) break;\n const chunkStart = size;\n size += value.byteLength;\n if (suffix) {\n chunks.push(value);\n keptBytes += value.byteLength;\n while (chunks.length > 1 && keptBytes - chunks[0].byteLength >= suffix) {\n keptBytes -= chunks.shift().byteLength;\n }\n continue;\n }\n const lo = Math.max(from, chunkStart);\n const hi = Math.min(to + 1, size);\n if (hi > lo) {\n chunks.push(value.subarray(lo - chunkStart, hi - chunkStart));\n keptBytes += hi - lo;\n }\n }\n if (known !== void 0) {\n await reader.cancel();\n size = known;\n }\n const kept = new Uint8Array(keptBytes);\n let at = 0;\n for (const chunk of chunks) {\n kept.set(chunk, at);\n at += chunk.byteLength;\n }\n return { size, kept, keptStart: suffix ? size - keptBytes : from };\n}\nexport {\n withRange\n};\n//# sourceMappingURL=assetRange.js.map\n"],"mappings":"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"}
|
|
@@ -1,37 +1,123 @@
|
|
|
1
1
|
async function withRange(request, response) {
|
|
2
2
|
var range = request.headers.get("range");
|
|
3
|
-
if (!range || request.method !== "GET" || response.status !== 200 || response.headers.has("content-range") || response.headers.has("content-encoding")) {
|
|
3
|
+
if (!range || request.method !== "GET" || response.status !== 200 || !response.body || response.headers.has("content-range") || response.headers.has("content-encoding") || !ifRangeMatches(request.headers.get("if-range"), response.headers)) {
|
|
4
4
|
return response;
|
|
5
5
|
}
|
|
6
6
|
var match = /^bytes=(\d*)-(\d*)$/.exec(range.trim());
|
|
7
7
|
if (!match || !match[1] && !match[2]) return response;
|
|
8
|
-
var
|
|
9
|
-
var
|
|
10
|
-
var start;
|
|
11
|
-
var end;
|
|
12
|
-
if (!match[1]) {
|
|
13
|
-
start = Math.max(0, size - Number(match[2]));
|
|
14
|
-
end = size - 1;
|
|
15
|
-
} else {
|
|
16
|
-
start = Number(match[1]);
|
|
17
|
-
end = match[2] ? Math.min(Number(match[2]), size - 1) : size - 1;
|
|
18
|
-
}
|
|
8
|
+
var length = response.headers.get("content-length");
|
|
9
|
+
var known = length && /^\d+$/.test(length) ? Number(length) : void 0;
|
|
19
10
|
var headers = new Headers(response.headers);
|
|
20
11
|
headers.set("accept-ranges", "bytes");
|
|
21
|
-
|
|
12
|
+
headers.delete("content-length");
|
|
13
|
+
var { size, kept, keptStart } = await readRange(response.body, match, known);
|
|
14
|
+
var span = resolve(match, size);
|
|
15
|
+
if (!span) {
|
|
22
16
|
headers.set("content-range", `bytes */${size}`);
|
|
23
17
|
return new Response(null, {
|
|
24
18
|
status: 416,
|
|
25
19
|
headers
|
|
26
20
|
});
|
|
27
21
|
}
|
|
28
|
-
headers.set("content-range", `bytes ${start}-${end}/${size}`);
|
|
29
|
-
headers.set("content-length", String(end - start + 1));
|
|
30
|
-
return new Response(
|
|
22
|
+
headers.set("content-range", `bytes ${span.start}-${span.end}/${size}`);
|
|
23
|
+
headers.set("content-length", String(span.end - span.start + 1));
|
|
24
|
+
return new Response(kept.subarray(span.start - keptStart, span.end - keptStart + 1), {
|
|
31
25
|
status: 206,
|
|
32
26
|
headers
|
|
33
27
|
});
|
|
34
28
|
}
|
|
29
|
+
function ifRangeMatches(ifRange, headers) {
|
|
30
|
+
if (!ifRange) return true;
|
|
31
|
+
var value = ifRange.trim();
|
|
32
|
+
if (value.startsWith("\"")) {
|
|
33
|
+
var etag = headers.get("etag");
|
|
34
|
+
return !!etag && !etag.startsWith("W/") && etag === value;
|
|
35
|
+
}
|
|
36
|
+
if (value.startsWith("W/")) return false;
|
|
37
|
+
return headers.get("last-modified") === value;
|
|
38
|
+
}
|
|
39
|
+
function resolve(match, size) {
|
|
40
|
+
var start;
|
|
41
|
+
var end;
|
|
42
|
+
if (!match[1]) {
|
|
43
|
+
start = Math.max(0, size - Number(match[2]));
|
|
44
|
+
end = size - 1;
|
|
45
|
+
} else {
|
|
46
|
+
start = Number(match[1]);
|
|
47
|
+
end = match[2] ? Math.min(Number(match[2]), size - 1) : size - 1;
|
|
48
|
+
}
|
|
49
|
+
if (start >= size || start > end) return;
|
|
50
|
+
return {
|
|
51
|
+
start,
|
|
52
|
+
end
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
async function readRange(body, match, known) {
|
|
56
|
+
var _resolve;
|
|
57
|
+
var span = known === void 0 ? void 0 : (_resolve = resolve(match, known)) !== null && _resolve !== void 0 ? _resolve : {
|
|
58
|
+
start: known,
|
|
59
|
+
end: -1
|
|
60
|
+
};
|
|
61
|
+
var suffix = !span && !match[1] ? Number(match[2]) : 0;
|
|
62
|
+
var from = span ? span.start : match[1] ? Number(match[1]) : 0;
|
|
63
|
+
var to = span ? span.end : match[2] && match[1] ? Number(match[2]) : Number.POSITIVE_INFINITY;
|
|
64
|
+
var chunks = [];
|
|
65
|
+
var keptBytes = 0;
|
|
66
|
+
var size = 0;
|
|
67
|
+
var reader = body.getReader();
|
|
68
|
+
while (known === void 0 || size <= to) {
|
|
69
|
+
var { done, value } = await reader.read();
|
|
70
|
+
if (done) break;
|
|
71
|
+
var chunkStart = size;
|
|
72
|
+
size += value.byteLength;
|
|
73
|
+
if (suffix) {
|
|
74
|
+
chunks.push(value);
|
|
75
|
+
keptBytes += value.byteLength;
|
|
76
|
+
while (chunks.length > 1 && keptBytes - chunks[0].byteLength >= suffix) {
|
|
77
|
+
keptBytes -= chunks.shift().byteLength;
|
|
78
|
+
}
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
var lo = Math.max(from, chunkStart);
|
|
82
|
+
var hi = Math.min(to + 1, size);
|
|
83
|
+
if (hi > lo) {
|
|
84
|
+
chunks.push(value.subarray(lo - chunkStart, hi - chunkStart));
|
|
85
|
+
keptBytes += hi - lo;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (known !== void 0) {
|
|
89
|
+
await reader.cancel();
|
|
90
|
+
size = known;
|
|
91
|
+
}
|
|
92
|
+
var kept = new Uint8Array(keptBytes);
|
|
93
|
+
var at = 0;
|
|
94
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = void 0;
|
|
95
|
+
try {
|
|
96
|
+
for (var _iterator = chunks[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
|
97
|
+
var chunk = _step.value;
|
|
98
|
+
kept.set(chunk, at);
|
|
99
|
+
at += chunk.byteLength;
|
|
100
|
+
}
|
|
101
|
+
} catch (err) {
|
|
102
|
+
_didIteratorError = true;
|
|
103
|
+
_iteratorError = err;
|
|
104
|
+
} finally {
|
|
105
|
+
try {
|
|
106
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
107
|
+
_iterator.return();
|
|
108
|
+
}
|
|
109
|
+
} finally {
|
|
110
|
+
if (_didIteratorError) {
|
|
111
|
+
throw _iteratorError;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
size,
|
|
117
|
+
kept,
|
|
118
|
+
keptStart: suffix ? size - keptBytes : from
|
|
119
|
+
};
|
|
120
|
+
}
|
|
35
121
|
|
|
36
122
|
export { withRange };
|
|
37
123
|
//# sourceMappingURL=assetRange.native.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assetRange.native.js","names":[],"sources":["esm/server/assetRange.native.js"],"sourcesContent":["async function withRange(request, response) {\n var range = request.headers.get(\"range\");\n if (!range || request.method !== \"GET\" || response.status !== 200 || response.headers.has(\"content-range\") || response.headers.has(\"content-encoding\")) {\n return response;\n }\n var match = /^bytes=(\\d*)-(\\d*)$/.exec(range.trim());\n if (!match || !match[1] && !match[2]) return response;\n var
|
|
1
|
+
{"version":3,"file":"assetRange.native.js","names":[],"sources":["esm/server/assetRange.native.js"],"sourcesContent":["async function withRange(request, response) {\n var range = request.headers.get(\"range\");\n if (!range || request.method !== \"GET\" || response.status !== 200 || !response.body || response.headers.has(\"content-range\") || response.headers.has(\"content-encoding\") || !ifRangeMatches(request.headers.get(\"if-range\"), response.headers)) {\n return response;\n }\n var match = /^bytes=(\\d*)-(\\d*)$/.exec(range.trim());\n if (!match || !match[1] && !match[2]) return response;\n var length = response.headers.get(\"content-length\");\n var known = length && /^\\d+$/.test(length) ? Number(length) : void 0;\n var headers = new Headers(response.headers);\n headers.set(\"accept-ranges\", \"bytes\");\n headers.delete(\"content-length\");\n var { size, kept, keptStart } = await readRange(response.body, match, known);\n var span = resolve(match, size);\n if (!span) {\n headers.set(\"content-range\", `bytes */${size}`);\n return new Response(null, {\n status: 416,\n headers\n });\n }\n headers.set(\"content-range\", `bytes ${span.start}-${span.end}/${size}`);\n headers.set(\"content-length\", String(span.end - span.start + 1));\n return new Response(kept.subarray(span.start - keptStart, span.end - keptStart + 1), {\n status: 206,\n headers\n });\n}\nfunction ifRangeMatches(ifRange, headers) {\n if (!ifRange) return true;\n var value = ifRange.trim();\n if (value.startsWith('\"')) {\n var etag = headers.get(\"etag\");\n return !!etag && !etag.startsWith(\"W/\") && etag === value;\n }\n if (value.startsWith(\"W/\")) return false;\n return headers.get(\"last-modified\") === value;\n}\nfunction resolve(match, size) {\n var start;\n var end;\n if (!match[1]) {\n start = Math.max(0, size - Number(match[2]));\n end = size - 1;\n } else {\n start = Number(match[1]);\n end = match[2] ? Math.min(Number(match[2]), size - 1) : size - 1;\n }\n if (start >= size || start > end) return;\n return {\n start,\n end\n };\n}\nasync function readRange(body, match, known) {\n var _resolve;\n var span = known === void 0 ? void 0 : (_resolve = resolve(match, known)) !== null && _resolve !== void 0 ? _resolve : {\n start: known,\n end: -1\n };\n var suffix = !span && !match[1] ? Number(match[2]) : 0;\n var from = span ? span.start : match[1] ? Number(match[1]) : 0;\n var to = span ? span.end : match[2] && match[1] ? Number(match[2]) : Number.POSITIVE_INFINITY;\n var chunks = [];\n var keptBytes = 0;\n var size = 0;\n var reader = body.getReader();\n while (known === void 0 || size <= to) {\n var { done, value } = await reader.read();\n if (done) break;\n var chunkStart = size;\n size += value.byteLength;\n if (suffix) {\n chunks.push(value);\n keptBytes += value.byteLength;\n while (chunks.length > 1 && keptBytes - chunks[0].byteLength >= suffix) {\n keptBytes -= chunks.shift().byteLength;\n }\n continue;\n }\n var lo = Math.max(from, chunkStart);\n var hi = Math.min(to + 1, size);\n if (hi > lo) {\n chunks.push(value.subarray(lo - chunkStart, hi - chunkStart));\n keptBytes += hi - lo;\n }\n }\n if (known !== void 0) {\n await reader.cancel();\n size = known;\n }\n var kept = new Uint8Array(keptBytes);\n var at = 0;\n var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = void 0;\n try {\n for (var _iterator = chunks[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {\n var chunk = _step.value;\n kept.set(chunk, at);\n at += chunk.byteLength;\n }\n } catch (err) {\n _didIteratorError = true;\n _iteratorError = err;\n } finally {\n try {\n if (!_iteratorNormalCompletion && _iterator.return != null) {\n _iterator.return();\n }\n } finally {\n if (_didIteratorError) {\n throw _iteratorError;\n }\n }\n }\n return {\n size,\n kept,\n keptStart: suffix ? size - keptBytes : from\n };\n}\nexport {\n withRange\n};\n//# sourceMappingURL=assetRange.js.map\n"],"mappings":"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"}
|
|
@@ -305,9 +305,9 @@ namespace margelo::nitro::one { struct LocationPlace; }
|
|
|
305
305
|
// Forward declaration of `LocationPosition` to properly resolve imports.
|
|
306
306
|
namespace margelo::nitro::one { struct LocationPosition; }
|
|
307
307
|
// Forward declaration of `LocationRegion` to properly resolve imports.
|
|
308
|
+
namespace margelo::nitro::one { struct LocationRegion; }
|
|
308
309
|
// Forward declaration of `MailComposeResult` to properly resolve imports.
|
|
309
310
|
namespace margelo::nitro::one { enum class MailComposeResult; }
|
|
310
|
-
namespace margelo::nitro::one { struct LocationRegion; }
|
|
311
311
|
// Forward declaration of `MapCoordinate` to properly resolve imports.
|
|
312
312
|
namespace margelo::nitro::one { struct MapCoordinate; }
|
|
313
313
|
// Forward declaration of `MapPlace` to properly resolve imports.
|
|
@@ -1736,15 +1736,6 @@ namespace margelo::nitro::one::bridge::swift {
|
|
|
1736
1736
|
inline Result_std__shared_ptr_Promise_AudioRecordingResult___ create_Result_std__shared_ptr_Promise_AudioRecordingResult___(const std::exception_ptr& error) noexcept {
|
|
1737
1737
|
return Result<std::shared_ptr<Promise<AudioRecordingResult>>>::withError(error);
|
|
1738
1738
|
}
|
|
1739
|
-
|
|
1740
|
-
// pragma MARK: Result<std::string>
|
|
1741
|
-
using Result_std__string_ = Result<std::string>;
|
|
1742
|
-
inline Result_std__string_ create_Result_std__string_(const std::string& value) noexcept {
|
|
1743
|
-
return Result<std::string>::withValue(value);
|
|
1744
|
-
}
|
|
1745
|
-
inline Result_std__string_ create_Result_std__string_(const std::exception_ptr& error) noexcept {
|
|
1746
|
-
return Result<std::string>::withError(error);
|
|
1747
|
-
}
|
|
1748
1739
|
|
|
1749
1740
|
// pragma MARK: Result<std::string>
|
|
1750
1741
|
using Result_std__string_ = Result<std::string>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "one",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.5",
|
|
4
4
|
"description": "One is a React framework for web, iOS, and Android apps.",
|
|
5
5
|
"license": "BSD-3-Clause",
|
|
6
6
|
"sideEffects": [
|
|
@@ -236,18 +236,18 @@
|
|
|
236
236
|
"@react-navigation/native-stack": "8.0.0-alpha.58",
|
|
237
237
|
"@react-navigation/routers": "8.0.0-alpha.20",
|
|
238
238
|
"@ungap/structured-clone": "^1.2.0",
|
|
239
|
-
"@vxrn/color-scheme": "2.6.
|
|
240
|
-
"@vxrn/compiler": "2.6.
|
|
241
|
-
"@vxrn/resolve": "2.6.
|
|
242
|
-
"@vxrn/tslib-lite": "2.6.
|
|
243
|
-
"@vxrn/use-isomorphic-layout-effect": "2.6.
|
|
244
|
-
"@vxrn/utils": "2.6.
|
|
245
|
-
"@vxrn/vite-plugin-metro": "2.6.
|
|
239
|
+
"@vxrn/color-scheme": "2.6.5",
|
|
240
|
+
"@vxrn/compiler": "2.6.5",
|
|
241
|
+
"@vxrn/resolve": "2.6.5",
|
|
242
|
+
"@vxrn/tslib-lite": "2.6.5",
|
|
243
|
+
"@vxrn/use-isomorphic-layout-effect": "2.6.5",
|
|
244
|
+
"@vxrn/utils": "2.6.5",
|
|
245
|
+
"@vxrn/vite-plugin-metro": "2.6.5",
|
|
246
246
|
"babel-dead-code-elimination": "1.0.10",
|
|
247
247
|
"babel-plugin-module-resolver": "^5.0.2",
|
|
248
248
|
"citty": "^0.1.6",
|
|
249
249
|
"core-js": "^3.38.1",
|
|
250
|
-
"create-vxrn": "2.6.
|
|
250
|
+
"create-vxrn": "2.6.5",
|
|
251
251
|
"escape-string-regexp": "^5.0.0",
|
|
252
252
|
"fast-deep-equal": "^3.1.3",
|
|
253
253
|
"fast-glob": "^3.3.3",
|
|
@@ -269,7 +269,7 @@
|
|
|
269
269
|
"tsconfig-paths": "^4",
|
|
270
270
|
"use-latest-callback": "^0.2.3",
|
|
271
271
|
"vite": "^8.2.2",
|
|
272
|
-
"vxrn": "2.6.
|
|
272
|
+
"vxrn": "2.6.5",
|
|
273
273
|
"web-streams-polyfill": "^4.3.0",
|
|
274
274
|
"ws": "^8.21.0",
|
|
275
275
|
"xxhashjs": "^0.2.2"
|
|
@@ -337,7 +337,7 @@
|
|
|
337
337
|
"@types/react-dom": "^19.2.2",
|
|
338
338
|
"@types/react-test-renderer": "^19.1.0",
|
|
339
339
|
"@types/xxhashjs": "^0.2.4",
|
|
340
|
-
"@vxrn/emitter": "2.6.
|
|
340
|
+
"@vxrn/emitter": "2.6.5",
|
|
341
341
|
"depcheck": "^1.4.7",
|
|
342
342
|
"esbuild": "^0.25.12",
|
|
343
343
|
"immer": "^10.1.1",
|
|
@@ -513,7 +513,7 @@
|
|
|
513
513
|
}
|
|
514
514
|
}
|
|
515
515
|
},
|
|
516
|
-
"releaseSourceCommit": "
|
|
516
|
+
"releaseSourceCommit": "97c5295fca304e7fc443ed228f087c51dc63bc69",
|
|
517
517
|
"repository": {
|
|
518
518
|
"type": "git",
|
|
519
519
|
"url": "git+https://github.com/onejs/one.git",
|
|
@@ -4,12 +4,13 @@ import { withRange } from './assetRange'
|
|
|
4
4
|
const bytes = Uint8Array.from({ length: 100 }, (_, i) => i)
|
|
5
5
|
|
|
6
6
|
// the assets binding streams in chunks; split so ranges cross chunk edges
|
|
7
|
-
function asset(headers: Record<string, string> = {}) {
|
|
7
|
+
function asset(headers: Record<string, string> = {}, pulled = { chunks: 0 }) {
|
|
8
8
|
const chunks = [bytes.slice(0, 30), bytes.slice(30, 70), bytes.slice(70)]
|
|
9
9
|
const body = new ReadableStream<Uint8Array>({
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
controller.
|
|
10
|
+
pull(controller) {
|
|
11
|
+
const chunk = chunks[pulled.chunks++]
|
|
12
|
+
if (chunk) controller.enqueue(chunk)
|
|
13
|
+
else controller.close()
|
|
13
14
|
},
|
|
14
15
|
})
|
|
15
16
|
return new Response(body, {
|
|
@@ -19,8 +20,8 @@ function asset(headers: Record<string, string> = {}) {
|
|
|
19
20
|
})
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
function get(range?: string) {
|
|
23
|
-
return new Request('https://x.test/a.webm',
|
|
23
|
+
function get(range?: string, headers: Record<string, string> = {}) {
|
|
24
|
+
return new Request('https://x.test/a.webm', { headers: range ? { range, ...headers } : headers })
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
async function body(response: Response) {
|
|
@@ -57,10 +58,49 @@ describe('withRange', () => {
|
|
|
57
58
|
expect(await body(clamped)).toEqual([98, 99])
|
|
58
59
|
})
|
|
59
60
|
|
|
60
|
-
it('answers 416 past the end', async () => {
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
it('answers 416 past the end without the full length', async () => {
|
|
62
|
+
for (const headers of [{}, { 'content-length': '100' }] as Record<string, string>[]) {
|
|
63
|
+
const res = await withRange(get('bytes=100-'), asset(headers))
|
|
64
|
+
expect(res.status).toBe(416)
|
|
65
|
+
expect(res.headers.get('content-range')).toBe('bytes */100')
|
|
66
|
+
expect(res.headers.get('content-length')).toBeNull()
|
|
67
|
+
expect(await body(res)).toEqual([])
|
|
68
|
+
}
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('streams only the requested bytes when the length is known', async () => {
|
|
72
|
+
const pulled = { chunks: 0 }
|
|
73
|
+
const res = await withRange(get('bytes=0-1'), asset({ 'content-length': '100' }, pulled))
|
|
74
|
+
expect(res.status).toBe(206)
|
|
75
|
+
expect(res.headers.get('content-range')).toBe('bytes 0-1/100')
|
|
76
|
+
expect(res.headers.get('content-length')).toBe('2')
|
|
77
|
+
expect(await body(res)).toEqual([0, 1])
|
|
78
|
+
expect(pulled.chunks).toBeLessThan(3)
|
|
79
|
+
|
|
80
|
+
const cross = await withRange(get('bytes=25-74'), asset({ 'content-length': '100' }))
|
|
81
|
+
expect(await body(cross)).toEqual(Array.from(bytes.slice(25, 75)))
|
|
82
|
+
|
|
83
|
+
const suffix = await withRange(get('bytes=-5'), asset({ 'content-length': '100' }))
|
|
84
|
+
expect(suffix.headers.get('content-range')).toBe('bytes 95-99/100')
|
|
85
|
+
expect(await body(suffix)).toEqual([95, 96, 97, 98, 99])
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it('honors If-Range only for a matching strong validator', async () => {
|
|
89
|
+
const tagged = { etag: '"new"', 'last-modified': 'Mon, 12 Oct 2026 00:00:00 GMT' }
|
|
90
|
+
const fresh = await withRange(get('bytes=0-1', { 'if-range': '"new"' }), asset(tagged))
|
|
91
|
+
expect(fresh.status).toBe(206)
|
|
92
|
+
const date = await withRange(
|
|
93
|
+
get('bytes=0-1', { 'if-range': 'Mon, 12 Oct 2026 00:00:00 GMT' }),
|
|
94
|
+
asset(tagged)
|
|
95
|
+
)
|
|
96
|
+
expect(date.status).toBe(206)
|
|
97
|
+
|
|
98
|
+
for (const ifRange of ['"old"', 'W/"new"', 'Sun, 11 Oct 2026 00:00:00 GMT']) {
|
|
99
|
+
const response = asset(tagged)
|
|
100
|
+
expect(await withRange(get('bytes=0-1', { 'if-range': ifRange }), response)).toBe(response)
|
|
101
|
+
}
|
|
102
|
+
const weak = asset({ etag: 'W/"new"' })
|
|
103
|
+
expect(await withRange(get('bytes=0-1', { 'if-range': 'W/"new"' }), weak)).toBe(weak)
|
|
64
104
|
})
|
|
65
105
|
|
|
66
106
|
it('keeps the full response without a usable single range', async () => {
|
package/src/server/assetRange.ts
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
// cloudflare's assets binding ignores Range and always answers 200 with the
|
|
2
|
-
// whole file
|
|
3
|
-
//
|
|
4
|
-
//
|
|
2
|
+
// whole file, usually with no content-length, which safari refuses for
|
|
3
|
+
// <video>. this answers the 206 (or 416) the browser asked for. multi-range
|
|
4
|
+
// requests and stale If-Range validators keep the full 200, which http allows.
|
|
5
5
|
export async function withRange(request: Request, response: Response): Promise<Response> {
|
|
6
6
|
const range = request.headers.get('range')
|
|
7
7
|
if (
|
|
8
8
|
!range ||
|
|
9
9
|
request.method !== 'GET' ||
|
|
10
10
|
response.status !== 200 ||
|
|
11
|
+
!response.body ||
|
|
11
12
|
response.headers.has('content-range') ||
|
|
12
|
-
response.headers.has('content-encoding')
|
|
13
|
+
response.headers.has('content-encoding') ||
|
|
14
|
+
!ifRangeMatches(request.headers.get('if-range'), response.headers)
|
|
13
15
|
) {
|
|
14
16
|
return response
|
|
15
17
|
}
|
|
@@ -17,9 +19,43 @@ export async function withRange(request: Request, response: Response): Promise<R
|
|
|
17
19
|
const match = /^bytes=(\d*)-(\d*)$/.exec(range.trim())
|
|
18
20
|
if (!match || (!match[1] && !match[2])) return response
|
|
19
21
|
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
+
const length = response.headers.get('content-length')
|
|
23
|
+
const known = length && /^\d+$/.test(length) ? Number(length) : undefined
|
|
22
24
|
|
|
25
|
+
const headers = new Headers(response.headers)
|
|
26
|
+
headers.set('accept-ranges', 'bytes')
|
|
27
|
+
headers.delete('content-length')
|
|
28
|
+
|
|
29
|
+
// only the requested bytes are kept in memory. with a known size reading
|
|
30
|
+
// stops after them; without one the size only comes from reading to the end
|
|
31
|
+
const { size, kept, keptStart } = await readRange(response.body, match, known)
|
|
32
|
+
const span = resolve(match, size)
|
|
33
|
+
if (!span) {
|
|
34
|
+
headers.set('content-range', `bytes */${size}`)
|
|
35
|
+
return new Response(null, { status: 416, headers })
|
|
36
|
+
}
|
|
37
|
+
headers.set('content-range', `bytes ${span.start}-${span.end}/${size}`)
|
|
38
|
+
headers.set('content-length', String(span.end - span.start + 1))
|
|
39
|
+
return new Response(kept.subarray(span.start - keptStart, span.end - keptStart + 1), {
|
|
40
|
+
status: 206,
|
|
41
|
+
headers,
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// rfc 9110 13.1.5: honor the range only when the validator still matches,
|
|
46
|
+
// using a strong etag comparison or an exact last-modified date
|
|
47
|
+
function ifRangeMatches(ifRange: string | null, headers: Headers) {
|
|
48
|
+
if (!ifRange) return true
|
|
49
|
+
const value = ifRange.trim()
|
|
50
|
+
if (value.startsWith('"')) {
|
|
51
|
+
const etag = headers.get('etag')
|
|
52
|
+
return !!etag && !etag.startsWith('W/') && etag === value
|
|
53
|
+
}
|
|
54
|
+
if (value.startsWith('W/')) return false
|
|
55
|
+
return headers.get('last-modified') === value
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function resolve(match: RegExpExecArray, size: number) {
|
|
23
59
|
let start: number
|
|
24
60
|
let end: number
|
|
25
61
|
if (!match[1]) {
|
|
@@ -30,16 +66,56 @@ export async function withRange(request: Request, response: Response): Promise<R
|
|
|
30
66
|
start = Number(match[1])
|
|
31
67
|
end = match[2] ? Math.min(Number(match[2]), size - 1) : size - 1
|
|
32
68
|
}
|
|
69
|
+
if (start >= size || start > end) return
|
|
70
|
+
return { start, end }
|
|
71
|
+
}
|
|
33
72
|
|
|
34
|
-
|
|
35
|
-
|
|
73
|
+
async function readRange(
|
|
74
|
+
body: ReadableStream<Uint8Array>,
|
|
75
|
+
match: RegExpExecArray,
|
|
76
|
+
known: number | undefined
|
|
77
|
+
) {
|
|
78
|
+
const span = known === undefined ? undefined : (resolve(match, known) ?? { start: known, end: -1 })
|
|
79
|
+
// an unknown-size suffix range keeps a rolling tail of the requested length
|
|
80
|
+
const suffix = !span && !match[1] ? Number(match[2]) : 0
|
|
81
|
+
const from = span ? span.start : match[1] ? Number(match[1]) : 0
|
|
82
|
+
const to = span ? span.end : match[2] && match[1] ? Number(match[2]) : Number.POSITIVE_INFINITY
|
|
36
83
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
84
|
+
const chunks: Uint8Array[] = []
|
|
85
|
+
let keptBytes = 0
|
|
86
|
+
let size = 0
|
|
87
|
+
const reader = body.getReader()
|
|
88
|
+
while (known === undefined || size <= to) {
|
|
89
|
+
const { done, value } = await reader.read()
|
|
90
|
+
if (done) break
|
|
91
|
+
const chunkStart = size
|
|
92
|
+
size += value.byteLength
|
|
93
|
+
if (suffix) {
|
|
94
|
+
chunks.push(value)
|
|
95
|
+
keptBytes += value.byteLength
|
|
96
|
+
// drop whole chunks that fall before the rolling tail
|
|
97
|
+
while (chunks.length > 1 && keptBytes - chunks[0].byteLength >= suffix) {
|
|
98
|
+
keptBytes -= chunks.shift()!.byteLength
|
|
99
|
+
}
|
|
100
|
+
continue
|
|
101
|
+
}
|
|
102
|
+
const lo = Math.max(from, chunkStart)
|
|
103
|
+
const hi = Math.min(to + 1, size)
|
|
104
|
+
if (hi > lo) {
|
|
105
|
+
chunks.push(value.subarray(lo - chunkStart, hi - chunkStart))
|
|
106
|
+
keptBytes += hi - lo
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (known !== undefined) {
|
|
110
|
+
await reader.cancel()
|
|
111
|
+
size = known
|
|
40
112
|
}
|
|
41
113
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
114
|
+
const kept = new Uint8Array(keptBytes)
|
|
115
|
+
let at = 0
|
|
116
|
+
for (const chunk of chunks) {
|
|
117
|
+
kept.set(chunk, at)
|
|
118
|
+
at += chunk.byteLength
|
|
119
|
+
}
|
|
120
|
+
return { size, kept, keptStart: suffix ? size - keptBytes : from }
|
|
45
121
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assetRange.d.ts","sourceRoot":"","sources":["../../src/server/assetRange.ts"],"names":[],"mappings":"AAIA,wBAAsB,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"file":"assetRange.d.ts","sourceRoot":"","sources":["../../src/server/assetRange.ts"],"names":[],"mappings":"AAIA,wBAAsB,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAsCvF"}
|