npm i @connectv/core

/*!*/import { wrap, map, filter } from '@connectv/core'; import { fromEvent } from 'rxjs'; let a = document.getElementById('a') as HTMLInputElement; let p = document.getElementById('p'); // // Will say hello to everyone but 'Donald'. // For obvious reasons. // /*!*/wrap(fromEvent(a, 'input')) // --> wrap the `Observable` in a `Pin` /*!*/.to(map(() => a.value)) // --> map the event to value of the input /*!*/.to(filter(name => name != 'Donald')) // --> filter 'Donald' out /*!*/.to(map(name => 'hellow ' + name)) // --> add 'hellow' to the name /*!*/.subscribe(msg => p.innerHTML = msg); // --> write it to the <p> element

/*!*/import { wrap, pipe, map, filter, sink } from '@connectv/core'; import { fromEvent, timer } from 'rxjs'; import { delay, debounceTime } from 'rxjs/operators'; let a = document.getElementById('a'); let p = document.getElementById('p'); // // Will calculate fibonacci sequence up to given index, displaying every number in the // sequence along the way. // // --> calculate next iteration step on fibonacci sequence /*!*/let m = map(([next, prev, l]) => [next + prev, next, l - 1]); /*!*/wrap(fromEvent(a, 'input')) // --> wrap the `Observable` in a `Pin` /*!*/.to(pipe(debounceTime(1000))) // --> wait for people to type in the number /*!*/.to(map(() => parseInt((a as any).value))) // --> map the input event to value of the input /*!*/.to(map(n => [1, 0, n])) // --> map the number to start iteration /*!*/.to(filter(([_, __, l]) => l >= 0)) // --> check if we should do any iteration /*!*/.to(m) // --> calculate next step /*!*/.to(pipe(delay(300))) // --> take a breath /*!*/.to(filter(([_, __, l]) => l > 0)) // --> check if we should continue /*!*/.to(m) // --> back to the loop /*!*/.to(map(([_, f, __]) => f)) // --> btw, lets take each number in the sequence /*!*/.to(sink(v => p.innerHTML = v)) // --> set the text of <p> to the fib number /*!*/.subscribe(); // --> bind the whole thing.

How To Install

npm i @connectv/core

<!-- Click on each line to copy it --> <!-- Dependencies --> <script src="https://unpkg.com/rxjs/bundles/rxjs.umd.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.14/lodash.min.js"></script> <script src="https://unpkg.com/@connectv/core/dist/bundles/connective.es5.min.js"></script>

How To Use

Quick Dive

/*!*/import { source, sink, pin } from '@connectv/core'; let a = source(); let b = pin(); let c = sink(value => console.log(value)); /*!*/a.to(b).to(c); // --> data/events go from a to b then to c. c.bind(); a.send('hellow!'); a.send('world!');

/*!*/import { source, sink, pin } from '@connectv/core'; let a = source(); let b = pin(); let c = sink(v => console.log(v)); /*!*/c.from(b).from(a); // --> data/events that c receives come from b which in turn come from a. c.bind(); a.send('hellow!'); a.send('world!');

.from()

.to()

.from()

import { source, sink } from '@connectv/core'; let a = source(); let b = sink(value => console.log('B::' + value)); let c = sink(value => console.log('C::' + value)); /*!*/a.to(b, c); // --> stuff from a goes to both b and c b.bind(); c.bind(); a.send('hellow!'); a.send('world!');

import { source, sink } from '@connectv/core'; let a = source(); let b = source(); let c = sink(value => console.log(value)); /*!*/c.from(a, b); c.bind(); // --> c's stuff come from both a and b a.send('hellow!'); b.send('world!');

Serial connections

.serialTo()

.serialFrom()

/*!*/import { source, sink, group } from '@connectv/core'; let a = source(); let b = source(); let c = sink(value => console.log('C::' + value)); let d = sink(value => console.log('D::' + value)); /*!*/group(a, b).serialTo(c, d); /*!*/group(c, d).bind(); a.send('hellow!'); b.send('world!');

a

c

b

d

.bind()

c

d

group()

sink()

import { source, pin, group } from '@connectv/core'; let a = source(); let b = source(); /*!*/group(a, b).to(pin()).subscribe(v => console.log(v)); a.send('hellow!'); b.send('world!');

the .bind() method is not present on all pin types.

method is not present on all pin types. when you call .subscribe() or .bind() methods of a pin, the pin becomes locked . You cannot connect any new pin to a locked pin.

or methods of a pin, the pin becomes . You cannot connect any new pin to a locked pin. if a pin is connected to another locked pin, it will become locked as well.

you can check if a pin is locked via its .locked property.

import { pin } from '@connectv/core'; let a = pin(); let b = pin(); a.to(b); console.log('A:: ' + a.locked + ' B:: ' + b.locked); // > 'A:: false B:: false' b.subscribe(); console.log('A:: ' + a.locked + ' B:: ' + b.locked); // > 'A:: true B:: true'

Sources

/*!*/import { wrap, value, pin, group } from '@connectv/core'; import { interval } from 'rxjs'; /*!*/let a = value('hellow world!'); // --> a emits 'hellow world!' once /*!*/let b = wrap(interval(1000)); // --> b emits a number every 1000ms group(a, b).to(pin()).subscribe(v => console.log(v));

Spread

/*!*/import { spread, value } from '@connectv/core'; value([1, 2, 3, 4, 5, 6]) /*!*/.to(spread()) .subscribe(v => console.log(v));

Filter & Map

/*!*/import { spread, value, filter, map } from '@connectv/core'; value([1, 2, 3, 4, 5, 6]) .to(spread()) /*!*/.to(filter(v => v % 2 == 0)) // --> only allow even ones through /*!*/.to(map(v => v * 10)) // --> multiply each value by 10 .subscribe(v => console.log(v));

Control

/*!*/import { spread, value, map, control } from '@connectv/core'; value([1, 2, 3, 4, 5, 6]) .to(spread()) .to( map(v => v * 10), map(v => v * 100), ) /*!*/.to(control()) // --> will wait for all incoming emissions and join them pair-wise .subscribe(v => console.log(v));

Loops

/*!*/import { source, map, filter } from '@connectv/core'; let a = source(); /*!*/let m = map(x => x + 1); // --> lets give this pin a name so we can loop back to it a.to(m) /*!*/.to(filter(x => x < 10)) // --> keep looping until we reach 10 /*!*/.to(m) // --> looping back to m .subscribe(v => console.log(v)); a.send(0);

Pipe

delay()

/*!*/import { source, map, filter, pipe } from '@connectv/core'; import { delay } from 'rxjs'; let a = source(); let m = map(x => x + 1); // --> lets give this pin a name so we can loop back to it a.to(m) .to(filter(x => x < 10)) // --> keep looping until we reach 10 /*!*/.to(pipe(delay(1))) // --> wait a bit .to(m) // --> looping back to m .subscribe(v => console.log(v)); a.send(0);

Composition

/*!*/import { composition, source, pin, filter, sink } from '@connectv/core'; /*!*/const evenOdd = composition(() => { //--> define the composition /*!*/ let input = pin(); //--> it has one input pin /*!*/ let odd = input.to(filter(x => x % 2 == 1)); /*!*/ let even = input.to(filter(x => x % 2 == 0)); /*!*/ /*!*/ return [{ input }, { odd, even }]; //--> return the inputs and outputs in the end /*!*/}); // // now using the composition: // let a = source(); /*!*/a.to(evenOdd()).serialTo( //--> `serialTo()` here connects ... sink(v => console.log('ODD:: ' + v)), //... the first output of `evenOdd()` to this sink ... sink(v => console.log('EVEN:: ' + v)) //... and the second output to this sink. ).subscribe(); a.send(2); a.send(3); a.send(4);

return [{ input }, { odd, even }];

input

odd

even

a.to(evenOdd())

a

evenOdd()

input

serialTo()

odd

even

let e = evenOdd(); // --> so basically these are all equivalent: /*!*/a.to(e.in('input')); /*!*/e.out('even').to(sink(v => console.log('ODD:: ' + v))).subscribe(); /*!*/e.out('odd').to(sink(v => console.log('EVEN:: ' + v))).subscribe(); // ... using group: /*!*/a.to(e.in('input')); /*!*/group(e.out('odd'), e.out('even')) /*!*/.serialTo( /*!*/ sink(v => console.log('ODD:: ' + v)), /*!*/ sink(v => console.log('EVEN:: ' + v)) /*!*/).subscribe(); // ... implicit connections: /*!*/a.to(e).serialTo( /*!*/ sink(v => console.log('ODD:: ' + v)), /*!*/ sink(v => console.log('EVEN:: ' + v)) /*!*/).subscribe();

Agent

inputs

outputs

Expr

/*!*/import { source, expr, group } from '@connectv/core'; let a = source(); let b = source(); group(a, b) /*!*/.serialTo(expr((a, b) => `${a} + ${b} = ${a + b}`)) //--> a is connected to the first input of the expr, //... b to the second .subscribe(console.log); //... and the output is subscribed to. a.send(2); b.send(3); a.send(42); b.send(30);

.serialTo()

expr()

let e = expr((a, b) => a + b); group(a, b).serialTo(e.in(0), e.in(1)); e.result.subscribe(console.log); // --> this is short-hand for `e.out('result')`

Node

"result"

node()

/*!*/import { source, node } from '@connectv/core'; /*!*/let N = node({ inputs: ['a'], outputs: ['even', 'odd']}, // --> set the signature /*!*/ (input, output) => { /*!*/ if (input.a % 2 == 0) output('even', input.a); /*!*/ else output('odd', input.a); /*!*/ }); let a = source(); /*!*/let n = N(); // --> create a new instance of the agent we defined above /*!*/ /*!*/a.to(n.in('a')); /*!*/n.out('even').subscribe(v => console.log('EVEN:: ' + v)); /*!*/n.out('odd').subscribe(v => console.log('ODD:: ' + v)); a.send(3); a.send(4); a.send(5);

n

.serialTo()

import { source, node, sink } from '@connectv/core'; let N = node({ inputs: ['a'], outputs: ['even', 'odd']}, // --> set the signature (input, output) => { if (input.a % 2 == 0) output('even', input.a); else output('odd', input.a); }); let a = source(); /*!*/a.to(N()).serialTo( /*!*/ sink(v => console.log('EVEN:: ' + v)), /*!*/ sink(v => console.log('ODD:: ' + v)) /*!*/).subscribe(); a.send(3); a.send(4); a.send(5);

Gate

/*!*/import { source, gate, map, spread, pipe, group, control } from '@connectv/core'; import { delay } from 'rxjs/operators'; let a = source(); /*!*/let g = gate(); /*!*/group(g.output, control()) // --> when the gate outputs something, // ... `control()` here ensures one opening of // ... the gate before the first output .to(pipe(delay(1000))) // --> wait one second, /*!*/.to(g.control); // --> open the gate a.to(map(v => v.split(' '))) // --> get all the words .to(spread()) // --> spread them /*!*/.to(g) // --> pass it through the gate .subscribe(console.log); // --> log it when it comes out a.send("Hellow darkness my old friend I've come to talk with you again");

g.control

node()

expr()

.control

State

state()

/*!*/import { wrap, map, state, sink, composition, pin } from '@connectv/core'; import { fromEvent } from 'rxjs'; function inputState(el: HTMLInputElement) { return composition(track => { let input = pin(), output = pin(); /*!*/ let model = state(); track(model); //--> track the state for later cleaning it up ... /*!*/ input.to(model) //--> the input sets the state /*!*/ .to(sink(v => el.value = v)) //--> the HTML element value is also set based on that /*!*/ .to(output); //--> and it all goes to the output wrap(fromEvent(el, 'input')) //--> when the user changes the input as well .to(map(() => el.value)) //--> we will fetch the new value of the input .to(model); //--> and update the state using it return [{input}, {output}]; })(); } let a = inputState(document.getElementById('a') as HTMLInputElement); let b = inputState(document.getElementById('b') as HTMLInputElement); /*!*/a.to(b); // --> literal two-way binding /*!*/b.to(a); a.bind(); // --> .bind() on composition will call .bind() on all child nodes having that method b.bind();

Sequence

/*!*/import { wrap, map, sequence, group } from '@connectv/core'; import { fromEvent } from 'rxjs'; group( wrap(fromEvent(document, 'mousedown')), wrap(fromEvent(document, 'mousemove')), wrap(fromEvent(document, 'mouseup')) ) .serialTo( /*!*/ sequence(1, 0, 1), //--> click is 1 mouse down, 0 mousemove, 1 mouse up /*!*/ sequence(1, '+', 1) //--> drag is 1 mouse down, +1 mousemoves, 1 mouse up ) .serialTo( map(() => 'CLICK'), map(() => 'DRAG') ).subscribe(console.log);

Memory Management

.clear()

.clear()

.clear()

let myPin = ...; let myAgent = ...; // // do your stuff // myPin.clear(); myAgent.clear();

.subscribe()

.unsubscribe()

.unsubscribe()

let myPin = ...; // // do whatever // let sub = myPin.subscribe(...); // // some other stuff // sub.unsubscribe();

mySource .to(...) .to(...) .subscribe( value => do_something_with_value(value), undefined, () => clear_everything_up() );

Under the Hood

.subscribe()

.observable

CONNECTIVE v RxJS

It allows description of a reactive flow in any order,

It provides a different model of reactive flows,

It provides standard abstractions for flow re-use.

For bugs, issues and suggestions, the best way is to create issues or pull requests to the repository.

For questions, feedback, etc. join the conversation on Gitter.

You can also drop me an email anytime.