generic-filehandle2 2.1.10 → 2.2.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/dist/filehandle.d.ts +8 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js.map +1 -1
- package/dist/remoteFile.js +6 -1
- package/dist/remoteFile.js.map +1 -1
- package/dist/util.d.ts +13 -0
- package/dist/util.js +47 -0
- package/dist/util.js.map +1 -1
- package/esm/filehandle.d.ts +8 -0
- package/esm/index.d.ts +1 -0
- package/esm/index.js.map +1 -1
- package/esm/remoteFile.js +7 -2
- package/esm/remoteFile.js.map +1 -1
- package/esm/util.d.ts +13 -0
- package/esm/util.js +46 -0
- package/esm/util.js.map +1 -1
- package/package.json +1 -1
- package/src/filehandle.ts +9 -0
- package/src/index.ts +1 -0
- package/src/remoteFile.ts +6 -2
- package/src/util.ts +58 -0
package/dist/filehandle.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ProgressCallback } from './util.ts';
|
|
1
2
|
export type BufferEncoding = 'ascii' | 'utf8' | 'utf-8' | 'utf16le' | 'utf-16le' | 'ucs2' | 'ucs-2' | 'base64' | 'base64url' | 'latin1' | 'binary' | 'hex';
|
|
2
3
|
export type Fetcher = (input: RequestInfo, init?: RequestInit) => Promise<Response>;
|
|
3
4
|
export interface FilehandleOptions {
|
|
@@ -6,6 +7,13 @@ export interface FilehandleOptions {
|
|
|
6
7
|
overrides?: Omit<RequestInit, 'headers'>;
|
|
7
8
|
encoding?: BufferEncoding;
|
|
8
9
|
fetch?: Fetcher;
|
|
10
|
+
/**
|
|
11
|
+
* Opt-in download-progress reporting. When set, the response body is streamed
|
|
12
|
+
* and this is called with the running byte count (and Content-Length total
|
|
13
|
+
* when available) as chunks arrive. Omitting it keeps the faster
|
|
14
|
+
* non-streaming read.
|
|
15
|
+
*/
|
|
16
|
+
onProgress?: ProgressCallback;
|
|
9
17
|
}
|
|
10
18
|
export interface Stats {
|
|
11
19
|
size: number;
|
package/dist/index.d.ts
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA,kDAA+B;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA,kDAA+B;AAG/B,6CAAmD;AAA1C,wHAAA,OAAO,OAAY;AAC5B,iDAAuD;AAA9C,4HAAA,OAAO,OAAc;AAC9B,+CAAqD;AAA5C,0HAAA,OAAO,OAAa"}
|
package/dist/remoteFile.js
CHANGED
|
@@ -92,7 +92,9 @@ class RemoteFile {
|
|
|
92
92
|
size: parseInt(sizeMatch[1], 10),
|
|
93
93
|
};
|
|
94
94
|
}
|
|
95
|
-
const resData =
|
|
95
|
+
const resData = opts.onProgress
|
|
96
|
+
? await (0, util_ts_1.toBytesWithProgress)(res, opts.onProgress)
|
|
97
|
+
: await (0, util_ts_1.toBytes)(res);
|
|
96
98
|
// server didn't honor the range request and returned the full file —
|
|
97
99
|
// the body length is the actual file size
|
|
98
100
|
if (!this._stat && res.status === 200) {
|
|
@@ -119,6 +121,9 @@ class RemoteFile {
|
|
|
119
121
|
else if (encoding) {
|
|
120
122
|
throw new Error(`unsupported encoding: ${encoding}`);
|
|
121
123
|
}
|
|
124
|
+
else if (opts.onProgress) {
|
|
125
|
+
return (0, util_ts_1.toBytesWithProgress)(res, opts.onProgress);
|
|
126
|
+
}
|
|
122
127
|
else {
|
|
123
128
|
return (0, util_ts_1.toBytes)(res);
|
|
124
129
|
}
|
package/dist/remoteFile.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remoteFile.js","sourceRoot":"","sources":["../src/remoteFile.ts"],"names":[],"mappings":";;AAAA,
|
|
1
|
+
{"version":3,"file":"remoteFile.js","sourceRoot":"","sources":["../src/remoteFile.ts"],"names":[],"mappings":"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"}
|
package/dist/util.d.ts
CHANGED
|
@@ -1 +1,14 @@
|
|
|
1
1
|
export declare function toBytes(src: Response | Blob): Promise<Uint8Array<ArrayBuffer>>;
|
|
2
|
+
/** Reports bytes downloaded for a single fetch; `total` from Content-Length. */
|
|
3
|
+
export type ProgressCallback = (bytesReceived: number, total?: number) => void;
|
|
4
|
+
/**
|
|
5
|
+
* Read a Response body to bytes while reporting download progress.
|
|
6
|
+
*
|
|
7
|
+
* When Content-Length is known the body is streamed directly into one pre-sized
|
|
8
|
+
* buffer (no per-chunk array, no second copy), ticking `onProgress` as chunks
|
|
9
|
+
* arrive. Without a known length there is no fraction to show, so it falls back
|
|
10
|
+
* to a single one-shot read and one completion tick rather than buffering
|
|
11
|
+
* chunks. Only used when a caller opts in via `onProgress`; the plain {@link
|
|
12
|
+
* toBytes} fast path is unaffected.
|
|
13
|
+
*/
|
|
14
|
+
export declare function toBytesWithProgress(res: Response, onProgress: ProgressCallback): Promise<Uint8Array<ArrayBuffer>>;
|
package/dist/util.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.toBytes = toBytes;
|
|
4
|
+
exports.toBytesWithProgress = toBytesWithProgress;
|
|
4
5
|
// Response.bytes() / Blob.bytes() is widely available but not yet in all
|
|
5
6
|
// lib.dom.d.ts versions, so the optional-chain check is load-bearing for older
|
|
6
7
|
// runtimes despite TS thinking it's always defined.
|
|
@@ -8,4 +9,50 @@ async function toBytes(src) {
|
|
|
8
9
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
9
10
|
return src.bytes ? src.bytes() : new Uint8Array(await src.arrayBuffer());
|
|
10
11
|
}
|
|
12
|
+
// throttle intermediate progress ticks; a large body can yield thousands of
|
|
13
|
+
// chunks and the final exact count is always emitted regardless
|
|
14
|
+
const PROGRESS_THROTTLE_MS = 50;
|
|
15
|
+
/**
|
|
16
|
+
* Read a Response body to bytes while reporting download progress.
|
|
17
|
+
*
|
|
18
|
+
* When Content-Length is known the body is streamed directly into one pre-sized
|
|
19
|
+
* buffer (no per-chunk array, no second copy), ticking `onProgress` as chunks
|
|
20
|
+
* arrive. Without a known length there is no fraction to show, so it falls back
|
|
21
|
+
* to a single one-shot read and one completion tick rather than buffering
|
|
22
|
+
* chunks. Only used when a caller opts in via `onProgress`; the plain {@link
|
|
23
|
+
* toBytes} fast path is unaffected.
|
|
24
|
+
*/
|
|
25
|
+
async function toBytesWithProgress(res, onProgress) {
|
|
26
|
+
const lengthHeader = res.headers.get('content-length');
|
|
27
|
+
const total = lengthHeader ? parseInt(lengthHeader, 10) : undefined;
|
|
28
|
+
const body = res.body;
|
|
29
|
+
if (!body || total === undefined) {
|
|
30
|
+
const bytes = await toBytes(res);
|
|
31
|
+
onProgress(bytes.byteLength, total ?? bytes.byteLength);
|
|
32
|
+
return bytes;
|
|
33
|
+
}
|
|
34
|
+
const reader = body.getReader();
|
|
35
|
+
let out = new Uint8Array(total);
|
|
36
|
+
let received = 0;
|
|
37
|
+
let lastTick = 0;
|
|
38
|
+
onProgress(0, total);
|
|
39
|
+
for (let chunk = await reader.read(); !chunk.done; chunk = await reader.read()) {
|
|
40
|
+
// a decoded body can exceed Content-Length (e.g. gzip transfer-encoding);
|
|
41
|
+
// grow the buffer rather than throwing on out.set() overflow
|
|
42
|
+
if (received + chunk.value.byteLength > out.length) {
|
|
43
|
+
const grown = new Uint8Array(Math.max(received + chunk.value.byteLength, out.length * 2));
|
|
44
|
+
grown.set(out.subarray(0, received));
|
|
45
|
+
out = grown;
|
|
46
|
+
}
|
|
47
|
+
out.set(chunk.value, received);
|
|
48
|
+
received += chunk.value.byteLength;
|
|
49
|
+
const now = Date.now();
|
|
50
|
+
if (now - lastTick >= PROGRESS_THROTTLE_MS) {
|
|
51
|
+
lastTick = now;
|
|
52
|
+
onProgress(received, total);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
onProgress(received, total);
|
|
56
|
+
return received === out.length ? out : out.subarray(0, received);
|
|
57
|
+
}
|
|
11
58
|
//# sourceMappingURL=util.js.map
|
package/dist/util.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":";;AAGA,0BAKC;
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":";;AAGA,0BAKC;AAmBD,kDAuCC;AAlED,yEAAyE;AACzE,+EAA+E;AAC/E,oDAAoD;AAC7C,KAAK,UAAU,OAAO,CAC3B,GAAoB;IAEpB,uEAAuE;IACvE,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,WAAW,EAAE,CAAC,CAAA;AAC1E,CAAC;AAKD,4EAA4E;AAC5E,gEAAgE;AAChE,MAAM,oBAAoB,GAAG,EAAE,CAAA;AAE/B;;;;;;;;;GASG;AACI,KAAK,UAAU,mBAAmB,CACvC,GAAa,EACb,UAA4B;IAE5B,MAAM,YAAY,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAA;IACtD,MAAM,KAAK,GAAG,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IACnE,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAA;IACrB,IAAI,CAAC,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAA;QAChC,UAAU,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,IAAI,KAAK,CAAC,UAAU,CAAC,CAAA;QACvD,OAAO,KAAK,CAAA;IACd,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,CAAA;IAC/B,IAAI,GAAG,GAAG,IAAI,UAAU,CAAC,KAAK,CAAC,CAAA;IAC/B,IAAI,QAAQ,GAAG,CAAC,CAAA;IAChB,IAAI,QAAQ,GAAG,CAAC,CAAA;IAChB,UAAU,CAAC,CAAC,EAAE,KAAK,CAAC,CAAA;IACpB,KAAK,IAAI,KAAK,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;QAC/E,0EAA0E;QAC1E,6DAA6D;QAC7D,IAAI,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,UAAU,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC;YACnD,MAAM,KAAK,GAAG,IAAI,UAAU,CAC1B,IAAI,CAAC,GAAG,CAAC,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAC5D,CAAA;YACD,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAA;YACpC,GAAG,GAAG,KAAK,CAAA;QACb,CAAC;QACD,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAA;QAC9B,QAAQ,IAAI,KAAK,CAAC,KAAK,CAAC,UAAU,CAAA;QAClC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACtB,IAAI,GAAG,GAAG,QAAQ,IAAI,oBAAoB,EAAE,CAAC;YAC3C,QAAQ,GAAG,GAAG,CAAA;YACd,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAA;QAC7B,CAAC;IACH,CAAC;IAED,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAA;IAC3B,OAAO,QAAQ,KAAK,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAA;AAClE,CAAC"}
|
package/esm/filehandle.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ProgressCallback } from './util.ts';
|
|
1
2
|
export type BufferEncoding = 'ascii' | 'utf8' | 'utf-8' | 'utf16le' | 'utf-16le' | 'ucs2' | 'ucs-2' | 'base64' | 'base64url' | 'latin1' | 'binary' | 'hex';
|
|
2
3
|
export type Fetcher = (input: RequestInfo, init?: RequestInit) => Promise<Response>;
|
|
3
4
|
export interface FilehandleOptions {
|
|
@@ -6,6 +7,13 @@ export interface FilehandleOptions {
|
|
|
6
7
|
overrides?: Omit<RequestInit, 'headers'>;
|
|
7
8
|
encoding?: BufferEncoding;
|
|
8
9
|
fetch?: Fetcher;
|
|
10
|
+
/**
|
|
11
|
+
* Opt-in download-progress reporting. When set, the response body is streamed
|
|
12
|
+
* and this is called with the running byte count (and Content-Length total
|
|
13
|
+
* when available) as chunks arrive. Omitting it keeps the faster
|
|
14
|
+
* non-streaming read.
|
|
15
|
+
*/
|
|
16
|
+
onProgress?: ProgressCallback;
|
|
9
17
|
}
|
|
10
18
|
export interface Stats {
|
|
11
19
|
size: number;
|
package/esm/index.d.ts
CHANGED
package/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAA;AAG/B,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAA;AACnD,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,iBAAiB,CAAA;AACvD,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,gBAAgB,CAAA"}
|
package/esm/remoteFile.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { toBytes } from "./util.js";
|
|
1
|
+
import { toBytes, toBytesWithProgress } from "./util.js";
|
|
2
2
|
function getMessage(e) {
|
|
3
3
|
const r = typeof e === 'object' &&
|
|
4
4
|
e !== null &&
|
|
@@ -90,7 +90,9 @@ export default class RemoteFile {
|
|
|
90
90
|
size: parseInt(sizeMatch[1], 10),
|
|
91
91
|
};
|
|
92
92
|
}
|
|
93
|
-
const resData =
|
|
93
|
+
const resData = opts.onProgress
|
|
94
|
+
? await toBytesWithProgress(res, opts.onProgress)
|
|
95
|
+
: await toBytes(res);
|
|
94
96
|
// server didn't honor the range request and returned the full file —
|
|
95
97
|
// the body length is the actual file size
|
|
96
98
|
if (!this._stat && res.status === 200) {
|
|
@@ -117,6 +119,9 @@ export default class RemoteFile {
|
|
|
117
119
|
else if (encoding) {
|
|
118
120
|
throw new Error(`unsupported encoding: ${encoding}`);
|
|
119
121
|
}
|
|
122
|
+
else if (opts.onProgress) {
|
|
123
|
+
return toBytesWithProgress(res, opts.onProgress);
|
|
124
|
+
}
|
|
120
125
|
else {
|
|
121
126
|
return toBytes(res);
|
|
122
127
|
}
|
package/esm/remoteFile.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remoteFile.js","sourceRoot":"","sources":["../src/remoteFile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"remoteFile.js","sourceRoot":"","sources":["../src/remoteFile.ts"],"names":[],"mappings":"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"}
|
package/esm/util.d.ts
CHANGED
|
@@ -1 +1,14 @@
|
|
|
1
1
|
export declare function toBytes(src: Response | Blob): Promise<Uint8Array<ArrayBuffer>>;
|
|
2
|
+
/** Reports bytes downloaded for a single fetch; `total` from Content-Length. */
|
|
3
|
+
export type ProgressCallback = (bytesReceived: number, total?: number) => void;
|
|
4
|
+
/**
|
|
5
|
+
* Read a Response body to bytes while reporting download progress.
|
|
6
|
+
*
|
|
7
|
+
* When Content-Length is known the body is streamed directly into one pre-sized
|
|
8
|
+
* buffer (no per-chunk array, no second copy), ticking `onProgress` as chunks
|
|
9
|
+
* arrive. Without a known length there is no fraction to show, so it falls back
|
|
10
|
+
* to a single one-shot read and one completion tick rather than buffering
|
|
11
|
+
* chunks. Only used when a caller opts in via `onProgress`; the plain {@link
|
|
12
|
+
* toBytes} fast path is unaffected.
|
|
13
|
+
*/
|
|
14
|
+
export declare function toBytesWithProgress(res: Response, onProgress: ProgressCallback): Promise<Uint8Array<ArrayBuffer>>;
|
package/esm/util.js
CHANGED
|
@@ -5,4 +5,50 @@ export async function toBytes(src) {
|
|
|
5
5
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
6
6
|
return src.bytes ? src.bytes() : new Uint8Array(await src.arrayBuffer());
|
|
7
7
|
}
|
|
8
|
+
// throttle intermediate progress ticks; a large body can yield thousands of
|
|
9
|
+
// chunks and the final exact count is always emitted regardless
|
|
10
|
+
const PROGRESS_THROTTLE_MS = 50;
|
|
11
|
+
/**
|
|
12
|
+
* Read a Response body to bytes while reporting download progress.
|
|
13
|
+
*
|
|
14
|
+
* When Content-Length is known the body is streamed directly into one pre-sized
|
|
15
|
+
* buffer (no per-chunk array, no second copy), ticking `onProgress` as chunks
|
|
16
|
+
* arrive. Without a known length there is no fraction to show, so it falls back
|
|
17
|
+
* to a single one-shot read and one completion tick rather than buffering
|
|
18
|
+
* chunks. Only used when a caller opts in via `onProgress`; the plain {@link
|
|
19
|
+
* toBytes} fast path is unaffected.
|
|
20
|
+
*/
|
|
21
|
+
export async function toBytesWithProgress(res, onProgress) {
|
|
22
|
+
const lengthHeader = res.headers.get('content-length');
|
|
23
|
+
const total = lengthHeader ? parseInt(lengthHeader, 10) : undefined;
|
|
24
|
+
const body = res.body;
|
|
25
|
+
if (!body || total === undefined) {
|
|
26
|
+
const bytes = await toBytes(res);
|
|
27
|
+
onProgress(bytes.byteLength, total ?? bytes.byteLength);
|
|
28
|
+
return bytes;
|
|
29
|
+
}
|
|
30
|
+
const reader = body.getReader();
|
|
31
|
+
let out = new Uint8Array(total);
|
|
32
|
+
let received = 0;
|
|
33
|
+
let lastTick = 0;
|
|
34
|
+
onProgress(0, total);
|
|
35
|
+
for (let chunk = await reader.read(); !chunk.done; chunk = await reader.read()) {
|
|
36
|
+
// a decoded body can exceed Content-Length (e.g. gzip transfer-encoding);
|
|
37
|
+
// grow the buffer rather than throwing on out.set() overflow
|
|
38
|
+
if (received + chunk.value.byteLength > out.length) {
|
|
39
|
+
const grown = new Uint8Array(Math.max(received + chunk.value.byteLength, out.length * 2));
|
|
40
|
+
grown.set(out.subarray(0, received));
|
|
41
|
+
out = grown;
|
|
42
|
+
}
|
|
43
|
+
out.set(chunk.value, received);
|
|
44
|
+
received += chunk.value.byteLength;
|
|
45
|
+
const now = Date.now();
|
|
46
|
+
if (now - lastTick >= PROGRESS_THROTTLE_MS) {
|
|
47
|
+
lastTick = now;
|
|
48
|
+
onProgress(received, total);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
onProgress(received, total);
|
|
52
|
+
return received === out.length ? out : out.subarray(0, received);
|
|
53
|
+
}
|
|
8
54
|
//# sourceMappingURL=util.js.map
|
package/esm/util.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,+EAA+E;AAC/E,oDAAoD;AACpD,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,GAAoB;IAEpB,uEAAuE;IACvE,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,WAAW,EAAE,CAAC,CAAA;AAC1E,CAAC"}
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,+EAA+E;AAC/E,oDAAoD;AACpD,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,GAAoB;IAEpB,uEAAuE;IACvE,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,WAAW,EAAE,CAAC,CAAA;AAC1E,CAAC;AAKD,4EAA4E;AAC5E,gEAAgE;AAChE,MAAM,oBAAoB,GAAG,EAAE,CAAA;AAE/B;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,GAAa,EACb,UAA4B;IAE5B,MAAM,YAAY,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAA;IACtD,MAAM,KAAK,GAAG,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IACnE,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAA;IACrB,IAAI,CAAC,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAA;QAChC,UAAU,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,IAAI,KAAK,CAAC,UAAU,CAAC,CAAA;QACvD,OAAO,KAAK,CAAA;IACd,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,CAAA;IAC/B,IAAI,GAAG,GAAG,IAAI,UAAU,CAAC,KAAK,CAAC,CAAA;IAC/B,IAAI,QAAQ,GAAG,CAAC,CAAA;IAChB,IAAI,QAAQ,GAAG,CAAC,CAAA;IAChB,UAAU,CAAC,CAAC,EAAE,KAAK,CAAC,CAAA;IACpB,KAAK,IAAI,KAAK,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;QAC/E,0EAA0E;QAC1E,6DAA6D;QAC7D,IAAI,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,UAAU,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC;YACnD,MAAM,KAAK,GAAG,IAAI,UAAU,CAC1B,IAAI,CAAC,GAAG,CAAC,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAC5D,CAAA;YACD,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAA;YACpC,GAAG,GAAG,KAAK,CAAA;QACb,CAAC;QACD,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAA;QAC9B,QAAQ,IAAI,KAAK,CAAC,KAAK,CAAC,UAAU,CAAA;QAClC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACtB,IAAI,GAAG,GAAG,QAAQ,IAAI,oBAAoB,EAAE,CAAC;YAC3C,QAAQ,GAAG,GAAG,CAAA;YACd,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAA;QAC7B,CAAC;IACH,CAAC;IAED,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAA;IAC3B,OAAO,QAAQ,KAAK,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAA;AAClE,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "generic-filehandle2",
|
|
3
3
|
"description": "uniform interface for accessing binary data from local files, remote HTTP resources, and browser Blob data",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"exports": {
|
package/src/filehandle.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { ProgressCallback } from './util.ts'
|
|
2
|
+
|
|
1
3
|
// avoids needing to have @types/node as a dependency of the consuming code
|
|
2
4
|
export type BufferEncoding =
|
|
3
5
|
| 'ascii'
|
|
@@ -24,6 +26,13 @@ export interface FilehandleOptions {
|
|
|
24
26
|
overrides?: Omit<RequestInit, 'headers'>
|
|
25
27
|
encoding?: BufferEncoding
|
|
26
28
|
fetch?: Fetcher
|
|
29
|
+
/**
|
|
30
|
+
* Opt-in download-progress reporting. When set, the response body is streamed
|
|
31
|
+
* and this is called with the running byte count (and Content-Length total
|
|
32
|
+
* when available) as chunks arrive. Omitting it keeps the faster
|
|
33
|
+
* non-streaming read.
|
|
34
|
+
*/
|
|
35
|
+
onProgress?: ProgressCallback
|
|
27
36
|
}
|
|
28
37
|
|
|
29
38
|
export interface Stats {
|
package/src/index.ts
CHANGED
package/src/remoteFile.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { toBytes } from './util.ts'
|
|
1
|
+
import { toBytes, toBytesWithProgress } from './util.ts'
|
|
2
2
|
|
|
3
3
|
import type {
|
|
4
4
|
BufferEncoding,
|
|
@@ -125,7 +125,9 @@ export default class RemoteFile implements GenericFilehandle {
|
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
const resData =
|
|
128
|
+
const resData = opts.onProgress
|
|
129
|
+
? await toBytesWithProgress(res, opts.onProgress)
|
|
130
|
+
: await toBytes(res)
|
|
129
131
|
// server didn't honor the range request and returned the full file —
|
|
130
132
|
// the body length is the actual file size
|
|
131
133
|
if (!this._stat && res.status === 200) {
|
|
@@ -164,6 +166,8 @@ export default class RemoteFile implements GenericFilehandle {
|
|
|
164
166
|
return res.text()
|
|
165
167
|
} else if (encoding) {
|
|
166
168
|
throw new Error(`unsupported encoding: ${encoding}`)
|
|
169
|
+
} else if (opts.onProgress) {
|
|
170
|
+
return toBytesWithProgress(res, opts.onProgress)
|
|
167
171
|
} else {
|
|
168
172
|
return toBytes(res)
|
|
169
173
|
}
|
package/src/util.ts
CHANGED
|
@@ -7,3 +7,61 @@ export async function toBytes(
|
|
|
7
7
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
8
8
|
return src.bytes ? src.bytes() : new Uint8Array(await src.arrayBuffer())
|
|
9
9
|
}
|
|
10
|
+
|
|
11
|
+
/** Reports bytes downloaded for a single fetch; `total` from Content-Length. */
|
|
12
|
+
export type ProgressCallback = (bytesReceived: number, total?: number) => void
|
|
13
|
+
|
|
14
|
+
// throttle intermediate progress ticks; a large body can yield thousands of
|
|
15
|
+
// chunks and the final exact count is always emitted regardless
|
|
16
|
+
const PROGRESS_THROTTLE_MS = 50
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Read a Response body to bytes while reporting download progress.
|
|
20
|
+
*
|
|
21
|
+
* When Content-Length is known the body is streamed directly into one pre-sized
|
|
22
|
+
* buffer (no per-chunk array, no second copy), ticking `onProgress` as chunks
|
|
23
|
+
* arrive. Without a known length there is no fraction to show, so it falls back
|
|
24
|
+
* to a single one-shot read and one completion tick rather than buffering
|
|
25
|
+
* chunks. Only used when a caller opts in via `onProgress`; the plain {@link
|
|
26
|
+
* toBytes} fast path is unaffected.
|
|
27
|
+
*/
|
|
28
|
+
export async function toBytesWithProgress(
|
|
29
|
+
res: Response,
|
|
30
|
+
onProgress: ProgressCallback,
|
|
31
|
+
): Promise<Uint8Array<ArrayBuffer>> {
|
|
32
|
+
const lengthHeader = res.headers.get('content-length')
|
|
33
|
+
const total = lengthHeader ? parseInt(lengthHeader, 10) : undefined
|
|
34
|
+
const body = res.body
|
|
35
|
+
if (!body || total === undefined) {
|
|
36
|
+
const bytes = await toBytes(res)
|
|
37
|
+
onProgress(bytes.byteLength, total ?? bytes.byteLength)
|
|
38
|
+
return bytes
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const reader = body.getReader()
|
|
42
|
+
let out = new Uint8Array(total)
|
|
43
|
+
let received = 0
|
|
44
|
+
let lastTick = 0
|
|
45
|
+
onProgress(0, total)
|
|
46
|
+
for (let chunk = await reader.read(); !chunk.done; chunk = await reader.read()) {
|
|
47
|
+
// a decoded body can exceed Content-Length (e.g. gzip transfer-encoding);
|
|
48
|
+
// grow the buffer rather than throwing on out.set() overflow
|
|
49
|
+
if (received + chunk.value.byteLength > out.length) {
|
|
50
|
+
const grown = new Uint8Array(
|
|
51
|
+
Math.max(received + chunk.value.byteLength, out.length * 2),
|
|
52
|
+
)
|
|
53
|
+
grown.set(out.subarray(0, received))
|
|
54
|
+
out = grown
|
|
55
|
+
}
|
|
56
|
+
out.set(chunk.value, received)
|
|
57
|
+
received += chunk.value.byteLength
|
|
58
|
+
const now = Date.now()
|
|
59
|
+
if (now - lastTick >= PROGRESS_THROTTLE_MS) {
|
|
60
|
+
lastTick = now
|
|
61
|
+
onProgress(received, total)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
onProgress(received, total)
|
|
66
|
+
return received === out.length ? out : out.subarray(0, received)
|
|
67
|
+
}
|