all files / src/ index.js

100% Statements 22/22
86.36% Branches 19/22
89.66% Functions 26/29
100% Lines 21/21
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65                          10× 68×   10× 10×                                                            
import Rx, {Observable as O} from "rx"
 
const keys = Object.keys
 
const fromKey = signal$ => key =>
  signal$.filter(s => s.key === key).map(s => s.val)
 
 
const mergeObj = (output, local = false) =>
  O.merge(keys(output).map(key => output[key].map(val => ({val, key, local}))))
 
const signals = signal$ => ({
  of: fromKey(signal$),
  ofKeys: (...keys) => keys.reduce((o, k) => ({...o, [k]: fromKey(signal$)(k)}), {})
})
 
// drivers :: {A: () -> {s$, t, e}} -> {s$, t: {A: [t]}, e: {A: [e]}}
export const drivers = spec => {
  const compact = (obj, fn) =>
    keys(obj).reduce((o, k) => { let a = fn(obj[k]); return a ? ({...o, [k]: a}) : o}, {})
 
  const drivers = compact(spec, f => f())
  return {
    signals: mergeObj(compact(drivers, d => d.signals), false),
    transforms: compact(drivers, d => d.transforms),
    executors: compact(drivers, d => d.executor)
  }
}
 
// run :: s$ -> (s$ -> {s$, o$}) -> o$
export const run = (signal$, main) => {
  let loop = null
  const input$ = signal$
    .filter(s => !s.local)
    .merge(O.create(sink => (loop = sink) && (() => loop = null)))
    .share()
 
  const res = main(signals(input$))
  const all$ = mergeObj({loop: mergeObj(res.in || {}, true), out: mergeObj(res.out || {})})
 
  const output$ = O.create(out => {
    return all$.subscribe(
      ({key, val}) => key === "out" ? out.onNext(val) : (loop && loop.onNext(val)),
      error => out.onError(error),
      () => out.onCompleted()
    )
  })
  return signals(output$).ofKeys(...keys(res.out || {}))
}
 
const noopDispose = { dispose() {} }
 
// execute :: {A: o$ -> dispose} -> {A: o$} -> dispose
export const execute = (executors, output) => {
  return new Rx.CompositeDisposable(...keys(output).map(key =>
    executors[key] ? executors[key](output[key]) || noopDispose : noopDispose
  ))
}
 
// for concise
export default (spec, main) => {
  const {signals: s, transforms: t, executors: e} = drivers(spec)
  return execute(e, run(s, main(t)))
}