230 lines
4.9 KiB
JavaScript
230 lines
4.9 KiB
JavaScript
const { Readable, getStreamError, isStreamx, isDisturbed } = require('streamx')
|
|
|
|
const readableKind = Symbol.for('bare.stream.readable.kind')
|
|
|
|
// https://streams.spec.whatwg.org/#readablestreamdefaultreader
|
|
exports.ReadableStreamDefaultReader = class ReadableStreamDefaultReader {
|
|
constructor(stream) {
|
|
this._stream = stream._stream
|
|
}
|
|
|
|
read() {
|
|
const stream = this._stream
|
|
|
|
return new Promise((resolve, reject) => {
|
|
const err = getStreamError(stream)
|
|
|
|
if (err) return reject(err)
|
|
|
|
if (stream.destroyed) {
|
|
return resolve({ value: undefined, done: true })
|
|
}
|
|
|
|
const value = stream.read()
|
|
|
|
if (value !== null) {
|
|
return resolve({ value, done: false })
|
|
}
|
|
|
|
stream
|
|
.once('readable', onreadable)
|
|
.once('close', onclose)
|
|
.once('error', onerror)
|
|
|
|
function onreadable() {
|
|
const value = stream.read()
|
|
|
|
ondone(
|
|
null,
|
|
value === null
|
|
? { value: undefined, done: true }
|
|
: { value, done: false }
|
|
)
|
|
}
|
|
|
|
function onclose() {
|
|
ondone(null, { value: undefined, done: true })
|
|
}
|
|
|
|
function onerror(err) {
|
|
ondone(err, null)
|
|
}
|
|
|
|
function ondone(err, value) {
|
|
stream
|
|
.off('readable', onreadable)
|
|
.off('close', onclose)
|
|
.off('error', onerror)
|
|
|
|
if (err) reject(err)
|
|
else resolve(value)
|
|
}
|
|
})
|
|
}
|
|
|
|
cancel(reason) {
|
|
if (this._stream.destroyed) return Promise.resolve()
|
|
|
|
return new Promise((resolve) =>
|
|
this._stream.once('close', resolve).destroy(reason)
|
|
)
|
|
}
|
|
}
|
|
|
|
// https://streams.spec.whatwg.org/#readablestreamdefaultcontroller
|
|
exports.ReadableStreamDefaultController = class ReadableStreamDefaultController {
|
|
constructor(stream) {
|
|
this._stream = stream._stream
|
|
}
|
|
|
|
get desiredSize() {
|
|
return (
|
|
this._stream._readableState.highWaterMark -
|
|
this._stream._readableState.buffered
|
|
)
|
|
}
|
|
|
|
enqueue(data) {
|
|
this._stream.push(data)
|
|
}
|
|
|
|
close() {
|
|
this._stream.push(null)
|
|
}
|
|
|
|
error(err) {
|
|
this._stream.destroy(err)
|
|
}
|
|
}
|
|
|
|
// https://streams.spec.whatwg.org/#readablestream
|
|
class ReadableStream {
|
|
static get [readableKind]() {
|
|
return 0 // Compatibility version
|
|
}
|
|
|
|
constructor(underlyingSource = {}, queuingStrategy) {
|
|
if (isStreamx(underlyingSource)) {
|
|
this._stream = underlyingSource
|
|
} else {
|
|
if (queuingStrategy === undefined) {
|
|
queuingStrategy = new exports.CountQueuingStrategy()
|
|
}
|
|
|
|
const { start, pull } = underlyingSource
|
|
const { highWaterMark = 1, size = defaultSize } = queuingStrategy
|
|
|
|
this._stream = new Readable({ highWaterMark, byteLength: size })
|
|
|
|
const controller = new exports.ReadableStreamDefaultController(this)
|
|
|
|
if (start) {
|
|
this._stream._open = open.bind(this, start.call(this, controller))
|
|
}
|
|
|
|
if (pull) {
|
|
this._stream._read = read.bind(this, pull.bind(this, controller))
|
|
}
|
|
}
|
|
}
|
|
|
|
get [readableKind]() {
|
|
return ReadableStream[readableKind]
|
|
}
|
|
|
|
getReader() {
|
|
return new exports.ReadableStreamDefaultReader(this)
|
|
}
|
|
|
|
cancel(reason) {
|
|
if (this._stream.destroyed) return Promise.resolve()
|
|
|
|
return new Promise((resolve) =>
|
|
this._stream.once('close', resolve).destroy(reason)
|
|
)
|
|
}
|
|
|
|
pipeTo(destination) {
|
|
return new Promise((resolve, reject) =>
|
|
this._stream.pipe(destination, (err) => {
|
|
err ? reject(err) : resolve()
|
|
})
|
|
)
|
|
}
|
|
|
|
[Symbol.asyncIterator]() {
|
|
return this._stream[Symbol.asyncIterator]()
|
|
}
|
|
|
|
static from(iterable) {
|
|
return new ReadableStream(Readable.from(iterable))
|
|
}
|
|
}
|
|
|
|
async function open(starting, cb) {
|
|
try {
|
|
await starting
|
|
|
|
cb(null)
|
|
} catch (err) {
|
|
cb(err)
|
|
}
|
|
}
|
|
|
|
async function read(pull, cb) {
|
|
try {
|
|
await pull()
|
|
|
|
cb(null)
|
|
} catch (err) {
|
|
cb(err)
|
|
}
|
|
}
|
|
|
|
function defaultSize() {
|
|
return 1
|
|
}
|
|
|
|
exports.ReadableStream = ReadableStream
|
|
|
|
// https://streams.spec.whatwg.org/#countqueuingstrategy
|
|
exports.CountQueuingStrategy = class CountQueuingStrategy {
|
|
constructor(opts = {}) {
|
|
const { highWaterMark = 1 } = opts
|
|
|
|
this.highWaterMark = highWaterMark
|
|
}
|
|
|
|
size(chunk) {
|
|
return 1
|
|
}
|
|
}
|
|
|
|
// https://streams.spec.whatwg.org/#bytelengthqueuingstrategy
|
|
exports.ByteLengthQueuingStrategy = class ByteLengthQueuingStrategy {
|
|
constructor(opts = {}) {
|
|
const { highWaterMark = 16384 } = opts
|
|
|
|
this.highWaterMark = highWaterMark
|
|
}
|
|
|
|
size(chunk) {
|
|
return chunk.byteLength
|
|
}
|
|
}
|
|
|
|
exports.isReadableStream = function isReadableStream(value) {
|
|
if (value instanceof ReadableStream) return true
|
|
|
|
return (
|
|
typeof value === 'object' &&
|
|
value !== null &&
|
|
value[readableKind] === ReadableStream[readableKind]
|
|
)
|
|
}
|
|
|
|
// https://streams.spec.whatwg.org/#is-readable-stream-disturbed
|
|
exports.isReadableStreamDisturbed = function isReadableStreamDisturbed(stream) {
|
|
return isDisturbed(stream._stream)
|
|
}
|