Capture exact filesystem identity and nanosecond timestamps, validate descriptor/path state before and after streaming, and enforce the captured byte count. Bound growing files to their captured size plus one byte using precise positional reads; keep unrepresentable display sizes explicitly unknown. Reuse the captured-state comparison around Apple assetutil and retain existing reader cleanup ownership. Validation: focused reader and affected consumer suites, full static/architecture checks, and a compiled directory-read probe on Bun 1.3.14 passed. Includes same-size mutation, growth, truncation, cancellation, FIFO replacement, cleanup retry, ZIP identity forgery, ASAR and Mach-O coverage.
56 lines
1.8 KiB
TypeScript
56 lines
1.8 KiB
TypeScript
import type { FileHandle } from "node:fs/promises";
|
|
import { Readable } from "node:stream";
|
|
|
|
/** Stream positional reads while leaving descriptor cleanup to its owner. */
|
|
export const readFileHandleChunks = (
|
|
handle: Pick<FileHandle, "read">,
|
|
options: {
|
|
readonly start?: number | bigint;
|
|
readonly end?: number | bigint;
|
|
readonly signal?: AbortSignal | undefined;
|
|
} = {},
|
|
): Readable => {
|
|
const start = fileOffset(options.start ?? 0);
|
|
const unlimited = options.end === undefined || options.end === Infinity;
|
|
const end = unlimited ? undefined : fileOffset(options.end);
|
|
if (
|
|
start === undefined ||
|
|
(!unlimited && end === undefined) ||
|
|
(end !== undefined && end < start)
|
|
)
|
|
throw new RangeError("Invalid file read range");
|
|
return Readable.from(
|
|
(async function* () {
|
|
let position = start;
|
|
while (end === undefined || position <= end) {
|
|
options.signal?.throwIfAborted();
|
|
const remaining = end === undefined ? undefined : end - position + 1n;
|
|
const chunk = Buffer.allocUnsafe(
|
|
remaining === undefined || remaining > 64n * 1024n
|
|
? 64 * 1024
|
|
: Number(remaining),
|
|
);
|
|
const { bytesRead } = await handle.read(
|
|
chunk,
|
|
0,
|
|
chunk.length,
|
|
position,
|
|
);
|
|
options.signal?.throwIfAborted();
|
|
if (bytesRead === 0) return;
|
|
position += BigInt(bytesRead);
|
|
yield chunk.subarray(0, bytesRead);
|
|
}
|
|
})(),
|
|
{
|
|
objectMode: false,
|
|
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
},
|
|
);
|
|
};
|
|
|
|
const fileOffset = (value: number | bigint): bigint | undefined => {
|
|
if (typeof value === "bigint") return value >= 0n ? value : undefined;
|
|
return Number.isSafeInteger(value) && value >= 0 ? BigInt(value) : undefined;
|
|
};
|