stream-ack
Version:
ACK data chunks send over a Node.js stream
82 lines (60 loc) • 1.7 kB
JavaScript
const {Writable} = require('stream')
class Sender extends Writable
{
constructor(writable, inFlight, {idField = 'id', ...options} = {})
{
super({...options, objectMode: true})
this
.once('close', () =>
{
const {_src} = this
if(!inFlight.size) return
if(!_src) return this.emit('error',
new ReferenceError("`src` stream not found, can't unshift chunks"))
_src.unpipe(this)
})
.once('finish', writable.end.bind(writable))
.on('pipe', src =>
{
if(this._src) return this.emit('error',
new ReferenceError("Can't pipe from more than one `src` stream"))
this._src = src
})
.on('unpipe', () =>
{
const {_src} = this
delete this._src
if(!inFlight.size) return
for(const [id, value] of Array.from(inFlight.entries()).reverse())
{
this.emit('landed', id, value)
_src.unshift(value)
}
inFlight.clear()
this.emit('allLanded')
})
writable
.once('close', this.emit.bind(this, 'close'))
.on('drain', process.nextTick.bind(process, this.uncork.bind(this)))
.on('error', this.emit.bind(this, 'error'))
this._writable = writable
this._idField = idField
this._inFlight = inFlight
}
_write(chunk, _, callback)
{
const {_writable, _idField, _inFlight} = this
let id = chunk[_idField]
if(id != null)
{
id = id.toString()
if(_inFlight.has(id))
return callback(new ReferenceError('Duplicated chunk ID'))
_inFlight.set(id, chunk)
this.emit('inFlight', id, chunk)
}
if(!_writable.write(chunk)) this.cork()
callback()
}
}
module.exports = Sender