fix: closer
This commit is contained in:
parent
f3e0a6095e
commit
ad04aab031
7
index.js.map
Normal file
7
index.js.map
Normal file
File diff suppressed because one or more lines are too long
@ -1,20 +1,54 @@
|
|||||||
import Stream from "stream";
|
import Stream from "stream";
|
||||||
|
|
||||||
|
const DEBUG = process.env['NODEJS_OBJECT_STREAM_TRACE'] !== ''
|
||||||
|
|
||||||
|
/** @type {(s: string) => void} */
|
||||||
|
const log = m => DEBUG ? console.log(m) : undefined;
|
||||||
|
|
||||||
|
let chainCount = 0
|
||||||
|
let composeCount = 0
|
||||||
|
let neverCount = 0
|
||||||
|
let onceCount = 0
|
||||||
|
let bindCount = 0
|
||||||
|
let zipCount = 0
|
||||||
|
let mapCount = 0
|
||||||
|
let constCount = 0
|
||||||
|
let fromPromiseCount = 0
|
||||||
|
|
||||||
export class Never extends Stream.Readable {
|
export class Never extends Stream.Readable {
|
||||||
constructor() {
|
constructor() {
|
||||||
super({read: function() {
|
super({objectMode: true})
|
||||||
|
this.id = neverCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
_read() {
|
||||||
|
log(`Never {id: ${this.id}}#_read()`)
|
||||||
|
log(` this.push(null)`)
|
||||||
this.push(null)
|
this.push(null)
|
||||||
}, objectMode: true})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @template T */
|
/** @template T */
|
||||||
export class Once extends Stream.Readable {
|
export class Once extends Stream.Duplex {
|
||||||
/** @param {T} a */
|
/** @param {T} a */
|
||||||
constructor(a) {
|
constructor(a) {
|
||||||
super({read: function() { }, objectMode: true})
|
super({objectMode: true, allowHalfOpen: false})
|
||||||
this.push(a)
|
this.a = a
|
||||||
|
this.id = onceCount++
|
||||||
|
this.push(this.a)
|
||||||
this.push(null)
|
this.push(null)
|
||||||
|
log(`Once {id: ${this.id}}#new()`)
|
||||||
|
log(` this.push(${a})`)
|
||||||
|
log(` this.push(null)`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {Stream.Duplex['_write']} */
|
||||||
|
_write(_ck, _enc, cb) {
|
||||||
|
cb()
|
||||||
|
}
|
||||||
|
|
||||||
|
_read() {
|
||||||
|
log(`Once {id: ${this.id}}#_read()`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,9 +56,17 @@ export class Once extends Stream.Readable {
|
|||||||
export class Const extends Stream.Transform {
|
export class Const extends Stream.Transform {
|
||||||
/** @param {T} a */
|
/** @param {T} a */
|
||||||
constructor(a) {
|
constructor(a) {
|
||||||
super({transform: function(_c, _enc, cb) {
|
super({objectMode: true})
|
||||||
cb(null, a)
|
this.a = a
|
||||||
}, objectMode: true})
|
this.id = constCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {Stream.Transform['_transform']} */
|
||||||
|
_transform(_c, _enc, cb) {
|
||||||
|
log(`Const {id: ${this.id}}#_transform(${_c}, _, _)`)
|
||||||
|
log(` cb(${this.a})`)
|
||||||
|
this.push(this.a)
|
||||||
|
cb()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,16 +74,27 @@ export class Const extends Stream.Transform {
|
|||||||
export class FromPromise extends Stream.Readable {
|
export class FromPromise extends Stream.Readable {
|
||||||
/** @param {Promise<T>} p */
|
/** @param {Promise<T>} p */
|
||||||
constructor(p) {
|
constructor(p) {
|
||||||
|
super({objectMode: true})
|
||||||
|
this.id = fromPromiseCount++
|
||||||
p
|
p
|
||||||
.then(a => {
|
.then(a => {
|
||||||
|
log(`FromPromise {id: ${this.id}}#new()`)
|
||||||
|
log(` ...p.then(...)`)
|
||||||
|
log(` this.push(${a})`)
|
||||||
|
log(` this.push(null)`)
|
||||||
this.push(a)
|
this.push(a)
|
||||||
this.push(null)
|
this.push(null)
|
||||||
})
|
})
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
|
log(`FromPromise {id: ${this.id}}#new()`)
|
||||||
|
log(` ...p.catch(...)`)
|
||||||
|
log(` this.destroy(${e})`)
|
||||||
this.destroy(e)
|
this.destroy(e)
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
super({read: function() {}, objectMode: true})
|
_read() {
|
||||||
|
log(`FromPromise {id: ${this.id}}#_read()`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,39 +103,56 @@ export class Chain extends Stream.Readable {
|
|||||||
/** @param {...Stream.Readable} streams */
|
/** @param {...Stream.Readable} streams */
|
||||||
constructor(...streams) {
|
constructor(...streams) {
|
||||||
super({objectMode: true})
|
super({objectMode: true})
|
||||||
|
this.id = chainCount++
|
||||||
this.ix = -1
|
this.ix = -1
|
||||||
this.streams = streams
|
this.streams = streams
|
||||||
/** @type {Stream.Readable | undefined} */
|
|
||||||
this.cur = undefined
|
|
||||||
this.next()
|
this.next()
|
||||||
}
|
}
|
||||||
|
|
||||||
next() {
|
next() {
|
||||||
|
log(`Chain {id: ${this.id}}#next()`)
|
||||||
this.ix++
|
this.ix++
|
||||||
if (this.ix === this.streams.length) {
|
if (this.ix === this.streams.length) {
|
||||||
return undefined
|
log(` this.push(null)`)
|
||||||
|
this.push(null)
|
||||||
} else {
|
} else {
|
||||||
this.cur = this.streams[this.ix]
|
const cur = this.streams[this.ix]
|
||||||
|
|
||||||
this.cur.once('end', () => {
|
cur.once('error', e => {
|
||||||
|
log(`Chain {id: ${this.id}}#next()`)
|
||||||
|
log(` cur.once('error', ...)`)
|
||||||
|
log(` this.destroy(${e})`)
|
||||||
|
this.destroy(e)
|
||||||
|
})
|
||||||
|
|
||||||
|
cur.once('end', () => {
|
||||||
|
log(`Chain {id: ${this.id}}#next()`)
|
||||||
|
log(` cur.once('end', ...)`)
|
||||||
|
log(` this.next()`)
|
||||||
this.next()
|
this.next()
|
||||||
})
|
})
|
||||||
|
|
||||||
this.cur.on('data', ck => {
|
cur.on('data', ck => {
|
||||||
|
log(`Chain {id: ${this.id}}#next()`)
|
||||||
|
log(` cur.on('data', ...)`)
|
||||||
|
log(` this.push(${ck})`)
|
||||||
const canPush = this.push(ck)
|
const canPush = this.push(ck)
|
||||||
if (this.cur && !canPush) {
|
if (cur && !canPush) {
|
||||||
this.cur.pause()
|
log(` cur.pause()`)
|
||||||
|
cur.pause()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_read() {
|
_read() {
|
||||||
if (!this.cur) {
|
log(`Chain {id: ${this.id}}#_read()`)
|
||||||
this.push(null)
|
this.streams.forEach(s => {
|
||||||
} else if (this.cur.isPaused()) {
|
if (s.isPaused()) {
|
||||||
this.cur.resume()
|
log(` s.resume()`)
|
||||||
|
s.resume()
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,18 +163,18 @@ export class Chain extends Stream.Readable {
|
|||||||
export class Map extends Stream.Transform {
|
export class Map extends Stream.Transform {
|
||||||
/** @param {(t: T) => R} f */
|
/** @param {(t: T) => R} f */
|
||||||
constructor(f) {
|
constructor(f) {
|
||||||
super({
|
super({objectMode: true})
|
||||||
transform: function (chunk, _, cb) {
|
this.f = f
|
||||||
try {
|
this.id = mapCount++
|
||||||
this.push(f(chunk))
|
|
||||||
cb();
|
|
||||||
} catch (e) {
|
|
||||||
// @ts-ignore
|
|
||||||
cb(e);
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
objectMode: true
|
/** @type {Stream.Transform['_transform']} */
|
||||||
})
|
_transform(ck, _, cb) {
|
||||||
|
log(`Map {id: ${this.id}}#_transform(${ck}, _, _)`)
|
||||||
|
const r = this.f(ck)
|
||||||
|
log(` const r = (${r})`)
|
||||||
|
log(` cb(null, ${r})`)
|
||||||
|
cb(null, r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,25 +188,41 @@ export class Zip extends Stream.Readable {
|
|||||||
/** @param {...Stream.Readable} streams */
|
/** @param {...Stream.Readable} streams */
|
||||||
constructor(...streams) {
|
constructor(...streams) {
|
||||||
super({objectMode: true})
|
super({objectMode: true})
|
||||||
|
this.id = zipCount++
|
||||||
|
log(`Zip {id: ${this.id}}#new()`)
|
||||||
|
log(` this.streams = Array {streams: ${streams.length}}`)
|
||||||
this.streams = streams
|
this.streams = streams
|
||||||
this.streams.forEach((s, ix) => {
|
this.streams.forEach((s, ix) => {
|
||||||
|
log(` this.streams[${ix}].once('error', ...)`)
|
||||||
|
log(` this.streams[${ix}].once('end', ...)`)
|
||||||
|
log(` this.streams[${ix}].once('data', ...)`)
|
||||||
s.once('error', e => this.destroy(e))
|
s.once('error', e => this.destroy(e))
|
||||||
s.once('end', () => this.push(null))
|
s.once('end', () => this.push(null))
|
||||||
s.on('data', ck => {
|
s.on('data', ck => {
|
||||||
const canPush = this.bufput(ix, ck)
|
log(`Zip {id: ${this.id}}#new()`)
|
||||||
if (!canPush) {
|
log(` this.streams[${ix}].once('data', ...)`)
|
||||||
this.streams.forEach(s => s.pause())
|
log(` this.bufput(${ix}, ${ck})`)
|
||||||
}
|
log(` stream.pause()`)
|
||||||
|
this.bufput(ix, ck)
|
||||||
|
s.pause()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @type {(ix: number, val: unknown) => boolean} */
|
/** @type {(ix: number, val: unknown) => boolean} */
|
||||||
bufput(ix, val) {
|
bufput(ix, val) {
|
||||||
|
log(`Zip {id: ${this.id}}#bufput(${ix}, ${val})`)
|
||||||
|
const bufstr = JSON.stringify(this.buf.map(a => a === null ? 'null' : '..'))
|
||||||
|
log(` this.buf = ${bufstr}`)
|
||||||
this.buf[ix] = val
|
this.buf[ix] = val
|
||||||
if (!this.isWaiting()) {
|
if (!this.isWaiting()) {
|
||||||
|
log(` this.push(${bufstr})`)
|
||||||
const canPush = this.push(this.buf)
|
const canPush = this.push(this.buf)
|
||||||
this.bufinit()
|
this.bufinit()
|
||||||
|
if (canPush) {
|
||||||
|
log(` this.streams.forEach(s => s.resume())`)
|
||||||
|
this.streams.forEach(s => s.resume())
|
||||||
|
}
|
||||||
return canPush
|
return canPush
|
||||||
} else {
|
} else {
|
||||||
return true
|
return true
|
||||||
@ -144,7 +230,9 @@ export class Zip extends Stream.Readable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bufinit() {
|
bufinit() {
|
||||||
this.buf = this.streams.map(() => null)
|
const nuls = this.streams.map(() => null)
|
||||||
|
log(` this.buf = ${JSON.stringify(nuls)}`)
|
||||||
|
this.buf = nuls
|
||||||
}
|
}
|
||||||
|
|
||||||
isWaiting() {
|
isWaiting() {
|
||||||
@ -152,6 +240,7 @@ export class Zip extends Stream.Readable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_read() {
|
_read() {
|
||||||
|
log(`Zip {id: ${this.id}}#_read()`)
|
||||||
this.streams.forEach(s => {
|
this.streams.forEach(s => {
|
||||||
if (s.isPaused()) {
|
if (s.isPaused()) {
|
||||||
s.resume()
|
s.resume()
|
||||||
@ -167,51 +256,100 @@ export class Compose extends Stream.Duplex {
|
|||||||
*/
|
*/
|
||||||
constructor(a, b) {
|
constructor(a, b) {
|
||||||
super({objectMode: true})
|
super({objectMode: true})
|
||||||
|
this.id = composeCount++
|
||||||
|
|
||||||
this.a = a
|
this.a = a
|
||||||
this.b = b
|
this.b = b
|
||||||
|
|
||||||
|
log(`Compose {id: ${this.id}}#new()`)
|
||||||
|
log(` a.on('data', ...)`)
|
||||||
|
log(` a.once('end', ...)`)
|
||||||
|
log(` a.once('error', ...)`)
|
||||||
|
log(` a.pause()`)
|
||||||
|
log(` b.on('drain', ...)`)
|
||||||
|
log(` b.on('data', ...)`)
|
||||||
|
log(` b.on('error', ...)`)
|
||||||
|
log(` b.on('finish', ...)`)
|
||||||
|
|
||||||
|
this.a.once('end', () => {
|
||||||
|
log(`Compose {id: ${this.id}}#new()`)
|
||||||
|
log(` a.on('end', ...)`)
|
||||||
|
log(` b.end()`)
|
||||||
|
this.b.end()
|
||||||
|
})
|
||||||
|
|
||||||
this.a.on('data', ck => {
|
this.a.on('data', ck => {
|
||||||
|
log(`Compose {id: ${this.id}}#new()`)
|
||||||
|
log(` a.on('data', ...)`)
|
||||||
|
log(` b.write(${ck})`)
|
||||||
const canWrite = this.b.write(ck)
|
const canWrite = this.b.write(ck)
|
||||||
|
|
||||||
if (!canWrite) {
|
if (!canWrite) {
|
||||||
|
log(` a.pause()`)
|
||||||
this.a.pause()
|
this.a.pause()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.a.once('error', e => this.destroy(e))
|
|
||||||
this.a.once('end', () => this.push(null))
|
this.a.once('error', e => {
|
||||||
|
log(`Compose {id: ${this.id}}#new()`)
|
||||||
|
log(` a.once('error', ...)`)
|
||||||
|
log(` this.destroy(${e})`)
|
||||||
|
this.destroy(e)
|
||||||
|
this.b.destroy(e)
|
||||||
|
})
|
||||||
|
|
||||||
this.b.on('drain', () => {
|
this.b.on('drain', () => {
|
||||||
if (this.a.isPaused()) {
|
log(`Compose {id: ${this.id}}#new()`)
|
||||||
|
log(` b.on('drain', ...)`)
|
||||||
|
log(` this.a.resume()`)
|
||||||
this.a.resume()
|
this.a.resume()
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
this.b.on('data', ck => {
|
this.b.on('data', ck => {
|
||||||
|
log(`Compose {id: ${this.id}}#new()`)
|
||||||
|
log(` b.on('data', ...)`)
|
||||||
|
log(` this.push(${ck})`)
|
||||||
const canPush = this.push(ck)
|
const canPush = this.push(ck)
|
||||||
if (!canPush) {
|
if (!canPush) {
|
||||||
this.a.pause()
|
log(` b.pause()`)
|
||||||
this.b.pause()
|
this.b.pause()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.b.once('error', e => this.destroy(e))
|
|
||||||
this.b.once('end', () => this.emit('end'))
|
this.b.once('error', e => {
|
||||||
this.b.once('finish', () => this.emit('finish'))
|
log(`Compose {id: ${this.id}}#new()`)
|
||||||
|
log(` b.once('error', ...)`)
|
||||||
|
log(` this.destroy(${e})`)
|
||||||
|
this.destroy(e)
|
||||||
|
this.a.destroy(e)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.b.once('finish', () => {
|
||||||
|
log(`Compose {id: ${this.id}}#new()`)
|
||||||
|
log(` b.once('finish', ...)`)
|
||||||
|
log(` this.emit('finish')`)
|
||||||
|
this.push(null)
|
||||||
|
this.end()
|
||||||
|
this.emit('finish')
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
_read() {
|
_read() {
|
||||||
if (this.a.isPaused()) {
|
log(`Compose {id: ${this.id}}#_read()`)
|
||||||
this.a.resume()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.b.isPaused()) {
|
if (this.b.isPaused()) {
|
||||||
|
log(` b.resume()`)
|
||||||
this.b.resume()
|
this.b.resume()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @type {Stream.Duplex['_write']} */
|
/** @type {Stream.Duplex['_write']} */
|
||||||
_write(ck, _enc, cb) {
|
_write(ck, _enc, cb) {
|
||||||
|
log(`Compose {id: ${this.id}}#_write(${ck}, _, _)`)
|
||||||
if (this.a instanceof Stream.Readable) {
|
if (this.a instanceof Stream.Readable) {
|
||||||
throw new Error('Cannot `write` to a Readable stream')
|
throw new Error('Cannot `write` to a Readable stream')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log(` this.a.write(${ck}, _, _)`)
|
||||||
this.a.write(ck, _enc, cb)
|
this.a.write(ck, _enc, cb)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -224,46 +362,78 @@ export class Bind extends Stream.Duplex {
|
|||||||
* @param {(t: T) => () => Stream.Readable} f
|
* @param {(t: T) => () => Stream.Readable} f
|
||||||
*/
|
*/
|
||||||
constructor(f) {
|
constructor(f) {
|
||||||
super({objectMode: true})
|
super({objectMode: true, allowHalfOpen: true})
|
||||||
this.f = f
|
this.f = f
|
||||||
|
this.id = bindCount++
|
||||||
|
|
||||||
/** @type {Stream.Readable | undefined } */
|
this.ix = 0
|
||||||
this.cur = undefined
|
|
||||||
|
|
||||||
/** @type {Array<Stream.Readable>} */
|
/** @type {Array<Stream.Readable>} */
|
||||||
this.streams = []
|
this.streams = []
|
||||||
|
|
||||||
|
/** @type {(() => void) | undefined} */
|
||||||
|
this.doneCb = undefined
|
||||||
|
|
||||||
|
this.paused = true
|
||||||
}
|
}
|
||||||
|
|
||||||
initcur() {
|
/** @type {NonNullable<Stream.Duplex['_final']>} */
|
||||||
if (!this.cur) {
|
_final(cb) {
|
||||||
this.cur = this.streams[0]
|
log(`Bind {id: ${this.id}}#_final(_)`)
|
||||||
|
this.doneCb = cb
|
||||||
}
|
}
|
||||||
|
|
||||||
this.cur.on('data', ck => {
|
init() {
|
||||||
|
log(`Bind {id: ${this.id}}#init()`)
|
||||||
|
|
||||||
|
const s = this.streams[this.ix]
|
||||||
|
if (this.paused || (!s && !this.doneCb)) {
|
||||||
|
this.paused = true
|
||||||
|
return
|
||||||
|
} else if (this.doneCb) {
|
||||||
|
log(` this.doneCb()`)
|
||||||
|
this.doneCb()
|
||||||
|
this.doneCb = undefined
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log(` s.on('data', ...)`)
|
||||||
|
s.on('data', ck => {
|
||||||
|
log(`Bind {id: ${this.id}}#initcur()`)
|
||||||
|
log(` s.on('data', ...)`)
|
||||||
|
log(` this.push(${ck})`)
|
||||||
const canPush = this.push(ck)
|
const canPush = this.push(ck)
|
||||||
if (!canPush && this.cur) {
|
if (!canPush) {
|
||||||
this.cur.pause()
|
s.pause()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
this.cur.on('end', () => {
|
log(` s.once('end', ...)`)
|
||||||
this.streams.shift()
|
s.once('end', () => {
|
||||||
if (this.streams.length > 0) {
|
log(`Bind {id: ${this.id}}#initcur()`)
|
||||||
this.cur = this.streams[0]
|
log(` s.once('end', ...)`)
|
||||||
this.initcur()
|
log(` this.ix++`)
|
||||||
} else {
|
log(` this.init()`)
|
||||||
this.cur = undefined
|
this.ix++
|
||||||
}
|
this.init()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @type {Stream.Duplex['_write']} */
|
/** @type {Stream.Duplex['_write']} */
|
||||||
_write(ck, _, cb) {
|
_write(ck, _, cb) {
|
||||||
|
log(`Bind {id: ${this.id}}#_write(${ck}, _, _)`)
|
||||||
try {
|
try {
|
||||||
|
log(` this.streams = ${JSON.stringify(this.streams.map(_ => 'Readable'))}`)
|
||||||
this.streams.push(this.f(ck)())
|
this.streams.push(this.f(ck)())
|
||||||
this.initcur()
|
if (this.paused) {
|
||||||
|
log(` this.init()`)
|
||||||
|
this.paused = false
|
||||||
|
this.init()
|
||||||
|
}
|
||||||
|
log(` cb()`)
|
||||||
cb()
|
cb()
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
|
log(` cb(${e})`)
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
cb(e)
|
cb(e)
|
||||||
}
|
}
|
||||||
@ -271,9 +441,9 @@ export class Bind extends Stream.Duplex {
|
|||||||
|
|
||||||
/** @type {Stream.Duplex['_read']} */
|
/** @type {Stream.Duplex['_read']} */
|
||||||
_read() {
|
_read() {
|
||||||
if (this.cur && this.cur.isPaused()) {
|
log(`Bind {id: ${this.id}}#_read()`)
|
||||||
this.cur.resume()
|
this.streams.forEach(s =>
|
||||||
}
|
s.isPaused() ? s.resume() : undefined)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ instance Apply (ObjectStream i) where
|
|||||||
apply (ObjectStream iab) (ObjectStream ia) = wrap $ join $ pure applyImpl <*> iab <*> ia
|
apply (ObjectStream iab) (ObjectStream ia) = wrap $ join $ pure applyImpl <*> iab <*> ia
|
||||||
|
|
||||||
instance Applicative (ObjectStream i) where
|
instance Applicative (ObjectStream i) where
|
||||||
pure = wrap <<< constImpl
|
pure = once
|
||||||
|
|
||||||
instance Monad (ObjectStream i)
|
instance Monad (ObjectStream i)
|
||||||
|
|
||||||
@ -67,8 +67,8 @@ instance Profunctor ObjectStream where
|
|||||||
sac <- pipeImpl sab sbc
|
sac <- pipeImpl sab sbc
|
||||||
pipeImpl sac scd
|
pipeImpl sac scd
|
||||||
|
|
||||||
-- | A stream that will emit the value `a` exactly once.
|
-- | A stream that will emit the value `a` exactly once, and ignores any written chunks.
|
||||||
once :: forall a. a -> ObjectStream Unit a
|
once :: forall i a. a -> ObjectStream i a
|
||||||
once = wrap <<< onceImpl
|
once = wrap <<< onceImpl
|
||||||
|
|
||||||
-- | A stream that will immediately emit `close` and `end` events.
|
-- | A stream that will immediately emit `close` and `end` events.
|
||||||
@ -141,9 +141,6 @@ run (ObjectStream s') = do
|
|||||||
cancelError
|
cancelError
|
||||||
cancelEnd
|
cancelEnd
|
||||||
|
|
||||||
-- | Constructs a `Transform` stream that always invokes the callback with the provided value.
|
|
||||||
foreign import constImpl :: forall i a. a -> Effect (Stream i a)
|
|
||||||
|
|
||||||
-- | Constructs a Stream that re-emits the outputs from each stream, in order.
|
-- | Constructs a Stream that re-emits the outputs from each stream, in order.
|
||||||
foreign import chainImpl :: forall a. Array (Stream Unit a) -> Effect (Stream Unit a)
|
foreign import chainImpl :: forall a. Array (Stream Unit a) -> Effect (Stream Unit a)
|
||||||
|
|
||||||
|
@ -45,6 +45,11 @@ spec =
|
|||||||
it "creates a readable that emits each element" do
|
it "creates a readable that emits each element" do
|
||||||
out <- Stream.run (Stream.fromFoldable [ 1, 2, 3 ])
|
out <- Stream.run (Stream.fromFoldable [ 1, 2, 3 ])
|
||||||
out `shouldEqual` [ 1, 2, 3 ]
|
out `shouldEqual` [ 1, 2, 3 ]
|
||||||
|
it "bind maps each number" do
|
||||||
|
out <- Stream.run do
|
||||||
|
a <- Stream.fromFoldable [ 1, 2, 3 ]
|
||||||
|
pure $ a + 1
|
||||||
|
out `shouldEqual` [ 2, 3, 4 ]
|
||||||
it "bind fans out" do
|
it "bind fans out" do
|
||||||
out <- Stream.run do
|
out <- Stream.run do
|
||||||
a <- Stream.fromFoldable [ 1, 2, 3 ]
|
a <- Stream.fromFoldable [ 1, 2, 3 ]
|
||||||
|
Loading…
Reference in New Issue
Block a user