import { __DEVBUILD__, assert, GENERIC_ERROR_MSG } from "./_assert" import { checkObservable } from "./_check" import { invoke, InvokeableWithoutParam } from "./_core" import { AnyEvent, Dispose } from "./_interfaces" import { mutable, MutableSource } from "./_mutable" import { curry2, is, isFunction, noop } from "./_util" import { EventStream, EventStreamDispatcher } from "./EventStream" import { Observable } from "./Observable" import { subscribe } from "./operators/subscribe" import { scheduleActivationTask } from "./scheduler/index" export interface PushOp { (bus: Bus, event: T | AnyEvent): void (bus: Bus): (event: T | AnyEvent) => void } export interface PushNextOp { (bus: Bus, val: T): void (bus: Bus): (val: T) => void } export interface PushErrorOp { (bus: Bus, err: Error): void (bus: Bus): (err: Error) => void } export interface PlugOp { (bus: Bus, obs: Observable): Dispose (bus: Bus): (bs: Observable) => Dispose } export const push: PushOp = curry2(_push) export const pushNext: PushNextOp = curry2(_pushNext) export const pushError: PushErrorOp = curry2(_pushError) export const pushEnd = _pushEnd export const plug: PlugOp = curry2(_plug) /** * Creates a new `Bus` instance. * * @public */ export function bus(): Bus { return new Bus() } // tslint:disable:no-shadowed-variable function _push(bus: Bus, event: T | AnyEvent): void { if (__DEVBUILD__) { checkBus(bus) } mutable(bus).send(event) } function _pushNext(bus: Bus, val: T): void { if (__DEVBUILD__) { checkBus(bus) } mutable(bus).sendNext(val) } function _pushError(bus: Bus, err: Error): void { if (__DEVBUILD__) { checkBus(bus) } mutable(bus).sendError(err) } function _pushEnd(bus: Bus): void { if (__DEVBUILD__) { checkBus(bus) } mutable(bus).sendEnd() } function _plug(bus: Bus, obs: Observable): Dispose { if (__DEVBUILD__) { checkBus(bus) checkObservable(obs) } return (mutable(bus) as BusSource).plug(obs) } export class Bus extends EventStream { constructor() { super(new EventStreamDispatcher(new BusSource())) } } class BusSource extends MutableSource implements InvokeableWithoutParam { private plugged: Array> = [] public activate(initialNeeded: boolean): void { super.activate(initialNeeded) if (this.plugged.length > 0) { scheduleActivationTask(invoke(this)) } } public invoke(): void { if (this.active && !this.ended) { this.plugged.forEach(entry => this.follow(entry)) } } public dispose(): void { super.dispose() this.plugged.forEach(entry => this.unfollow(entry)) } public plug(obs: Observable): Dispose { if (this.ended) { return noop } else { const bus = this const entry: PluggedEntry = { obs } bus.plugged.push(entry) if (this.active) { this.follow(entry) } return function unplug() { const idx = bus.plugged.indexOf(entry) idx >= 0 && bus.plugged.splice(idx, 1) bus.unfollow(entry) } } } private follow(entry: PluggedEntry): void { if (!isFunction(entry.dispose)) { entry.dispose = subscribe(event => { if (!event.isEnd) { // TODO: should we ignore errors as well? check from bacon sources this.send(event) } }, entry.obs) } } private unfollow(entry: PluggedEntry): void { const { dispose } = entry entry.dispose = void 0 if (isFunction(dispose)) { dispose() } } } interface PluggedEntry { obs: Observable dispose?: Dispose } function checkBus(x: any): void { const ok = is(x, Bus) assert(ok, __DEVBUILD__ ? (!ok ? `Expected a Bus but got ${x}` : "") : GENERIC_ERROR_MSG) }