UNPKG

@whatwg-node/node-fetch

Version:

Fetch API implementation for Node

175 lines (174 loc) • 5.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PonyfillReadableStream = void 0; const stream_1 = require("stream"); function createController(desiredSize, readable) { let chunks = []; let _closed = false; let flushed = false; return { desiredSize, enqueue(chunk) { const buf = typeof chunk === 'string' ? Buffer.from(chunk) : chunk; if (!flushed) { chunks.push(buf); } else { readable.push(buf); } }, close() { if (chunks.length > 0) { this._flush(); } readable.push(null); _closed = true; }, error(error) { if (chunks.length > 0) { this._flush(); } readable.destroy(error); }, get _closed() { return _closed; }, _flush() { flushed = true; if (chunks.length > 0) { const concatenated = chunks.length > 1 ? Buffer.concat(chunks) : chunks[0]; readable.push(concatenated); chunks = []; } }, }; } function isNodeReadable(obj) { return obj?.read != null; } function isReadableStream(obj) { return obj?.getReader != null; } class PonyfillReadableStream { constructor(underlyingSource) { this.locked = false; if (underlyingSource instanceof PonyfillReadableStream && underlyingSource.readable != null) { this.readable = underlyingSource.readable; } else if (isNodeReadable(underlyingSource)) { this.readable = underlyingSource; } else if (isReadableStream(underlyingSource)) { let reader; let started = false; this.readable = new stream_1.Readable({ read() { if (!started) { started = true; reader = underlyingSource.getReader(); } reader .read() .then(({ value, done }) => { if (done) { this.push(null); } else { this.push(value); } }) .catch(err => { this.destroy(err); }); }, destroy(err, callback) { reader.cancel(err).then(() => callback(err), callback); }, }); } else { let started = false; let ongoing = false; this.readable = new stream_1.Readable({ read(desiredSize) { if (ongoing) { return; } ongoing = true; return Promise.resolve().then(async () => { if (!started) { const controller = createController(desiredSize, this); started = true; await underlyingSource?.start?.(controller); controller._flush(); if (controller._closed) { return; } } const controller = createController(desiredSize, this); await underlyingSource?.pull?.(controller); controller._flush(); ongoing = false; }); }, async destroy(err, callback) { try { await underlyingSource?.cancel?.(err); callback(null); } catch (err) { callback(err); } }, }); } } cancel(reason) { this.readable.destroy(reason); return Promise.resolve(); } getReader(_options) { const iterator = this.readable[Symbol.asyncIterator](); this.locked = true; return { read() { return iterator.next(); }, releaseLock: () => { iterator.return?.(); this.locked = false; }, cancel: async (reason) => { await iterator.return?.(reason); this.locked = false; }, closed: new Promise((resolve, reject) => { this.readable.once('end', resolve); this.readable.once('error', reject); }), }; } [Symbol.asyncIterator]() { return this.readable[Symbol.asyncIterator](); } tee() { throw new Error('Not implemented'); } async pipeTo(destination) { const writer = destination.getWriter(); await writer.ready; for await (const chunk of this.readable) { await writer.write(chunk); } await writer.ready; return writer.close(); } pipeThrough({ writable, readable, }) { this.pipeTo(writable); return readable; } static [Symbol.hasInstance](instance) { return isReadableStream(instance); } } exports.PonyfillReadableStream = PonyfillReadableStream;