forked from public/fvtt-cthulhu-eternal
135 lines
3.2 KiB
JavaScript
135 lines
3.2 KiB
JavaScript
'use strict';
|
|
|
|
var Transform = require('streamx').Transform;
|
|
|
|
// Based on help from @mafintosh via https://gist.github.com/mafintosh/92836a8d03df0ef41356e233e0f06382
|
|
|
|
function toThrough(readable) {
|
|
var highWaterMark = readable._readableState.highWaterMark;
|
|
|
|
// Streamx uses 16384 as the default highWaterMark for everything and then
|
|
// divides it by 1024 for objects
|
|
// However, node's objectMode streams the number of objects as highWaterMark, so we need to
|
|
// multiply the objectMode highWaterMark by 1024 to make it streamx compatible
|
|
if (readable._readableState.objectMode) {
|
|
highWaterMark = readable._readableState.highWaterMark * 1024;
|
|
}
|
|
|
|
var destroyedByError = false;
|
|
var readableClosed = false;
|
|
var readableEnded = false;
|
|
|
|
function flush(cb) {
|
|
var self = this;
|
|
|
|
// Afer all writes have drained, we change the `_read` implementation
|
|
self._read = function (cb) {
|
|
readable.resume();
|
|
cb();
|
|
};
|
|
|
|
readable.on('data', onData);
|
|
readable.once('error', onError);
|
|
readable.once('end', onEnd);
|
|
|
|
function cleanup() {
|
|
readable.off('data', onData);
|
|
readable.off('error', onError);
|
|
readable.off('end', onEnd);
|
|
}
|
|
|
|
function onData(data) {
|
|
var drained = self.push(data);
|
|
// When the stream is not drained, we pause it because `_read` will be called later
|
|
if (!drained) {
|
|
readable.pause();
|
|
}
|
|
}
|
|
|
|
function onError(err) {
|
|
cleanup();
|
|
cb(err);
|
|
}
|
|
|
|
function onEnd() {
|
|
cleanup();
|
|
cb();
|
|
}
|
|
}
|
|
|
|
// Handle the case where a user destroyed the returned stream
|
|
function predestroy() {
|
|
// Only call destroy on the readable if this `predestroy` wasn't
|
|
// caused via the readable having an `error` or `close` event
|
|
if (destroyedByError) {
|
|
return;
|
|
}
|
|
if (readableClosed) {
|
|
return;
|
|
}
|
|
readable.destroy(new Error('Wrapper destroyed'));
|
|
}
|
|
|
|
var wrapper = new Transform({
|
|
highWaterMark: highWaterMark,
|
|
flush: flush,
|
|
predestroy: predestroy,
|
|
});
|
|
|
|
// Forward errors from the underlying stream
|
|
readable.once('error', onError);
|
|
readable.once('end', onEnd);
|
|
readable.once('close', onClose);
|
|
|
|
function onError(err) {
|
|
destroyedByError = true;
|
|
wrapper.destroy(err);
|
|
}
|
|
|
|
function onEnd() {
|
|
readableEnded = true;
|
|
}
|
|
|
|
function onClose() {
|
|
readableClosed = true;
|
|
// Only destroy the wrapper if the readable hasn't ended successfully
|
|
if (!readableEnded) {
|
|
wrapper.destroy();
|
|
}
|
|
}
|
|
|
|
var shouldFlow = true;
|
|
wrapper.once('pipe', onPipe);
|
|
wrapper.on('piping', onPiping);
|
|
wrapper.on('newListener', onListener);
|
|
|
|
function onPiping() {
|
|
maybeFlow();
|
|
wrapper.off('piping', onPiping);
|
|
wrapper.off('newListener', onListener);
|
|
}
|
|
|
|
function onListener(event) {
|
|
// Once we've seen the data or readable event, check if we need to flow
|
|
if (event === 'data' || event === 'readable') {
|
|
onPiping();
|
|
}
|
|
}
|
|
|
|
function onPipe() {
|
|
// If the wrapper is piped, disable flow
|
|
shouldFlow = false;
|
|
}
|
|
|
|
function maybeFlow() {
|
|
// If we need to flow, end the stream which triggers flush
|
|
if (shouldFlow) {
|
|
wrapper.end();
|
|
}
|
|
}
|
|
|
|
return wrapper;
|
|
}
|
|
|
|
module.exports = toThrough;
|