import { Readable as NodeReadableNative } from 'stream'; import { isStrictlyObject } from "./type.js"; import { ReadableStream } from 'web-streams-polyfill'; const NodeReadable = NodeReadableNative || class {}; /** * Validates if passed object is either browser's ReadableStream * or Node's Readable. * * @param entry */ export function isReadable(entry) { return isReadableStream(entry) || isNodeReadable(entry); } export function isReadableStream(entry) { if (!isStrictlyObject(entry)) { return false; } const browserReadable = entry; if (typeof browserReadable.getReader === 'function' && browserReadable.locked !== undefined && typeof browserReadable.cancel === 'function' && typeof browserReadable.pipeTo === 'function' && typeof browserReadable.pipeThrough === 'function') { return true; } return false; } export function isNodeReadable(entry) { if (!isStrictlyObject(entry)) { return false; } const nodeReadable = entry; if (typeof nodeReadable.pipe === 'function' && nodeReadable.readable && typeof nodeReadable._read === 'function') { return true; } return false; } /** * Function that converts Node's Readable into WHATWG ReadableStream * * Taken over from https://github.com/gwicke/node-web-streams/blob/master/lib/conversions.js * Because it uses forked web-streams-polyfill that are outdated. * * @author https://github.com/gwicke * @licence Apache License 2.0 https://github.com/gwicke/node-web-streams/blob/master/LICENSE * @param nodeStream */ export function readableNodeToWeb(nodeStream) { return new ReadableStream({ start(controller) { nodeStream.pause(); nodeStream.on('data', chunk => { if (Buffer.isBuffer(chunk)) { controller.enqueue(new Uint8Array(chunk.buffer)); } else { controller.enqueue(chunk); } nodeStream.pause(); }); nodeStream.on('end', () => controller.close()); nodeStream.on('error', e => controller.error(e)); }, pull() { nodeStream.resume(); }, cancel() { nodeStream.pause(); } }); } /** * Taken over from https://github.com/gwicke/node-web-streams/blob/master/lib/conversions.js * Because it uses forked web-streams-polyfill that are outdated. * * @author https://github.com/gwicke * @licence Apache License 2.0 https://github.com/gwicke/node-web-streams/blob/master/LICENSE */ class NodeReadableWrapper extends NodeReadable { constructor(webStream, options) { super(options); this._webStream = webStream; this._reader = webStream.getReader(); this._reading = false; } _read() { if (this._reading) { return; } this._reading = true; const doRead = () => { this._reader.read().then(res => { if (res.done) { this.push(null); return; } if (this.push(res.value)) { return doRead(); } else { this._reading = false; } }); }; doRead(); } } /** * Function that converts WHATWG ReadableStream into Node's Readable * * Taken over from https://github.com/gwicke/node-web-streams/blob/master/lib/conversions.js * Because it uses forked web-streams-polyfill that is outdated. * * **Warning!** * If you want to use this function in browser you have to polyfill `stream` package with your bundler. * * @author https://github.com/gwicke * @licence Apache License 2.0 https://github.com/gwicke/node-web-streams/blob/master/LICENSE * @param webStream * @param options */ export function readableWebToNode(webStream, options) { if (!NodeReadableNative) { throw new Error("The Node's Readable is not available! If you are running this in browser you have to polyfill 'stream' package!"); } return new NodeReadableWrapper(webStream, options); } export function normalizeToReadableStream(stream) { if (isNodeReadable(stream)) { return readableNodeToWeb(stream); } else if (isReadableStream(stream)) { return stream; } throw new TypeError('Passed stream is not Node Readable nor ReadableStream!'); }