UNPKG

ix

Version:

The Interactive Extensions for JavaScript

160 lines (147 loc) 5.27 kB
import { AsyncIterableX } from './asynciterablex.js'; /** @ignore */ const SharedArrayBuf = typeof SharedArrayBuffer !== 'undefined' ? SharedArrayBuffer : ArrayBuffer; /** @ignore */ /** @ignore */ export class AsyncIterableReadableStream<T> extends AsyncIterableX<T | undefined> { constructor(protected _stream: ReadableStream<T>) { super(); } [Symbol.asyncIterator]() { const stream = this._stream; const reader = stream['getReader'](); return _consumeReader(stream, reader, defaultReaderToAsyncIterator(reader)); } } /** @ignore */ /** @ignore */ export class AsyncIterableReadableByteStream extends AsyncIterableReadableStream<Uint8Array> { [Symbol.asyncIterator]() { const stream = this._stream; let reader: ReadableStreamReader<Uint8Array>; try { reader = (stream as any)['getReader']({ mode: 'byob' }); } catch (e) { return super[Symbol.asyncIterator](); } const iterator = _consumeReader(stream, reader, byobReaderToAsyncIterator(reader)); // "pump" the iterator once so it initializes and is ready to accept a buffer or bytesToRead iterator.next(); return iterator; } } async function* _consumeReader<T>( stream: ReadableStream<T>, reader: ReadableStreamReader<Uint8Array> | ReadableStreamDefaultReader, iterator: AsyncGenerator<T> ): AsyncIterator<T, any, undefined> { let threw = false; try { yield* iterator; } catch (e) { if ((threw = true) && reader) { await reader['cancel'](e); } } finally { if (reader) { if (!threw) { await reader['cancel'](); } if (stream.locked) { try { reader.closed.catch(() => { /* */ }); reader.releaseLock(); } catch (e) { /* */ } } } } } /** @ignore */ async function* defaultReaderToAsyncIterator<T = any>(reader: ReadableStreamDefaultReader<T>) { let r: ReadableStreamReadResult<T>; while (!(r = await reader.read()).done) { yield r.value; } } /** @ignore */ async function* byobReaderToAsyncIterator(reader: ReadableStreamReader<Uint8Array>) { let r: ReadableStreamReadResult<Uint8Array>; let value: number | ArrayBufferLike = yield null!; while (!(r = await readNext(reader, value, 0)).done) { value = yield r.value; } } /** @ignore */ async function readNext( reader: ReadableStreamReader<Uint8Array>, bufferOrLen: ArrayBufferLike | number, offset: number ): Promise<ReadableStreamReadResult<Uint8Array>> { let size: number; let buffer: ArrayBufferLike; if (typeof bufferOrLen === 'number') { buffer = new ArrayBuffer((size = bufferOrLen)); } else if (bufferOrLen instanceof ArrayBuffer) { size = (buffer = bufferOrLen).byteLength; } else if (bufferOrLen instanceof SharedArrayBuf) { size = (buffer = bufferOrLen).byteLength; } else { return { done: true, value: undefined! }; } return await readInto(reader, buffer, offset, size); } /** @ignore */ async function readInto( reader: ReadableStreamReader<Uint8Array>, buffer: ArrayBufferLike, offset: number, size: number ): Promise<ReadableStreamReadResult<Uint8Array>> { let innerOffset = offset; if (innerOffset >= size) { return { done: false, value: new Uint8Array(buffer, 0, size) }; } const { done, value } = await (reader as any).read( new Uint8Array(buffer, innerOffset, size - innerOffset) ); if ((innerOffset += value!.byteLength) < size && !done) { return await readInto(reader, value!.buffer, innerOffset, size); } return { done, value: new Uint8Array(value!.buffer, 0, innerOffset) }; } /** * Creates an async-iterable from an existing DOM stream. * * @template TSource The type of elements in the source DOM stream. * @param {ReadableStream<TSource>} stream The DOM Readable stream to convert to an async-iterable. * @returns {AsyncIterableX<TSource>} An async-iterable containing the elements from the ReadableStream. */ export function fromDOMStream<TSource>(stream: ReadableStream<TSource>): AsyncIterableX<TSource>; /** * Creates an async-iterable from an existing DOM stream and options. * * @template TSource * @template TSource The type of elements in the source DOM stream. * @param {ReadableStream<TSource>} stream The readable stream to convert to an async-iterable. * @param {{ mode: 'byob' }} options The options to set the mode for the DOM stream. * @returns {AsyncIterableX<TSource>} An async-iterable created from the incoming async-iterable. */ export function fromDOMStream<TSource extends ArrayBufferView>( stream: ReadableStream<TSource>, options: { mode: 'byob' } ): AsyncIterableX<TSource>; /** * Creates an async-iterable from an existing DOM stream and optional options. * * @param {ReadableStream} stream The readable stream to convert to an async-iterable. * @param {{ mode: 'byob' }} [options] The optional options to set the mode for the DOM stream. * @returns {AsyncIterableX<any>} An async-iterable created from the incoming async-iterable. */ export function fromDOMStream(stream: ReadableStream, options?: { mode: 'byob' }) { return !options || options['mode'] !== 'byob' ? new AsyncIterableReadableStream(stream) : new AsyncIterableReadableByteStream(stream); }