send-stream
Version:
Streaming file serving library with Range and conditional-GET support from file system or any streaming sources.
129 lines • 3.88 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.MultiStream = exports.BufferStream = void 0;
const node_stream_1 = require("node:stream");
/**
* Single buffer stream
*/
class BufferStream extends node_stream_1.Readable {
/**
* Create a single buffer stream
* @param buffer - content buffer or undefined if stream need to be empty
*/
constructor(buffer) {
super({ autoDestroy: true });
Object.defineProperty(this, "buffer", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
this.buffer = buffer;
}
/**
* Read implementation
*/
_read() {
const { buffer } = this;
if (buffer) {
this.buffer = undefined;
this.push(buffer);
}
this.push(null);
}
/**
* Destroy implementation
* @param error - error or null
* @param callback - callback to be called after destroy
*/
_destroy(error, callback) {
this.buffer = undefined;
// eslint-disable-next-line no-underscore-dangle
super._destroy(error, callback);
}
}
exports.BufferStream = BufferStream;
/**
* Multi stream
*/
class MultiStream extends node_stream_1.PassThrough {
/**
* Create a multi streams stream
* @param ranges - array of Buffer or StreamRange
* @param onNextStream - function creating the Readable when needed
* @param onDestroy - function called on close to release resources
*/
constructor(ranges, onNextStream, onDestroy) {
super({ allowHalfOpen: false });
Object.defineProperty(this, "ranges", {
enumerable: true,
configurable: true,
writable: true,
value: ranges
});
Object.defineProperty(this, "onNextStream", {
enumerable: true,
configurable: true,
writable: true,
value: onNextStream
});
Object.defineProperty(this, "onDestroy", {
enumerable: true,
configurable: true,
writable: true,
value: onDestroy
});
this.sendNextRange();
}
/**
* Destroy implementation
* @param error - error or null
* @param callback - callback to be called after destroy
*/
_destroy(error, callback) {
this.onDestroy()
.then(() => {
// eslint-disable-next-line no-underscore-dangle
super._destroy(error, callback);
})
.catch((closeError) => {
// eslint-disable-next-line no-underscore-dangle
super._destroy(error
? new Error(`${String(error)}\nthen\n${String(closeError)}`)
: new Error(String(closeError)), callback);
});
}
sendNextRange() {
const currentRange = this.ranges.shift();
if (!currentRange) {
this.end(() => {
this.destroy();
});
return;
}
const stream = this.onNextStream(currentRange);
const listenerMap = new Map();
const onClose = () => {
for (const [eventType, listener] of listenerMap.entries()) {
stream.off(eventType, listener);
}
};
const onError = (error) => {
onClose();
this.end(() => {
this.destroy(error);
});
};
const onEnd = () => {
onClose();
this.sendNextRange();
};
listenerMap.set('error', onError);
stream.on('error', onError);
listenerMap.set('end', onEnd);
stream.on('end', onEnd);
stream.pipe(this, { end: false });
}
}
exports.MultiStream = MultiStream;
//# sourceMappingURL=streams.js.map