UNPKG

igir

Version:

🕹 A zero-setup ROM collection manager that sorts, filters, extracts or archives, patches, and reports on collections of any size on any OS.

145 lines (144 loc) • 4.28 kB
import stream from "node:stream"; import Defaults from "../globals/defaults.js"; var streamUtil_default = { /** * Concatenate multiple readable streams into a single readable stream. */ concat(...readables) { if (readables.length === 1) { return readables[0]; } const out = new stream.PassThrough({ highWaterMark: Defaults.FILE_READING_CHUNK_SIZE }); let current = 0; let activeStream = void 0; let isDestroyed = false; function pipeNext() { if (isDestroyed) { return; } if (current >= readables.length) { out.end(); return; } activeStream = readables[current++]; activeStream.pipe(out, { end: false }); activeStream.once("error", (err) => { out.emit("error", err); }); activeStream.once("end", pipeNext); } out._destroy = (err, callback) => { isDestroyed = true; if (typeof activeStream?.destroy === "function") { activeStream.destroy(err ?? void 0); } for (let i = current; i < readables.length; i++) { const readable = readables[i]; if (typeof readable.destroy === "function") { readable.destroy(); } } callback(err); }; pipeNext(); return out; }, /** * Pad a readable stream to a specified length by appending a fill string. */ padEnd(readable, maxLength, fillString) { const output = new stream.PassThrough({ highWaterMark: Defaults.FILE_READING_CHUNK_SIZE }); let readableBytesRead = 0; readable.on("data", (chunk) => { readableBytesRead += chunk.length; if (!output.write(chunk)) { readable.pause(); output.once("drain", () => readable.resume()); } }); readable.on("end", () => { const remainingBytes = maxLength - readableBytesRead; if (remainingBytes > 0) { this.staticReadable(remainingBytes, fillString).pipe(output, { end: true }); } else { output.end(); } }); readable.on("error", (err) => output.destroy(err)); return output; }, /** * Split a readable stream into multiple readable streams, such that the original stream can be * read concurrently by multiple consumers. */ split(readable, count) { if (count === 0) { return []; } if (count === 1) { return [readable]; } const outputs = []; for (let i = 0; i < count; i++) { const output = new stream.PassThrough({ highWaterMark: Defaults.FILE_READING_CHUNK_SIZE }); const onData = output.write.bind(output); const onEnd = output.end.bind(output); const onError = output.destroy.bind(output); readable.on("data", onData); readable.on("end", onEnd); readable.on("error", onError); output._destroy = (err, callback) => { readable.off("data", onData); readable.off("end", onEnd); readable.off("error", onError); callback(err); }; outputs.push(output); } return outputs; }, /** * Generate a static readable stream that emits a fixed number of bytes filled with a specified * string or number. */ staticReadable(length, fillString) { let bytesRemaining = length; return new stream.Readable({ read(size) { const chunkSize = Math.min(size, bytesRemaining); const chunk = Buffer.alloc(chunkSize, fillString); this.push(chunk); bytesRemaining -= chunkSize; if (bytesRemaining <= 0) { this.push(null); } } }); }, /** * Return a new readable stream that has had the specified transforms applied to it. * This differs from {@link stream.pipeline} in that it returns a readable stream, NOT a writable * stream. */ withTransforms(readable, ...transforms) { if (transforms.length === 0) { return readable; } const output = new stream.PassThrough({ highWaterMark: Defaults.FILE_READING_CHUNK_SIZE }); Reflect.apply(stream.pipeline, void 0, [ readable, ...transforms, output, (err) => { if (err) { output.destroy(err); } } ]); return output; } }; export { streamUtil_default as default }; //# sourceMappingURL=streamUtil.js.map