Node

node() allows you to create custom agents by wrapping traditional functions:
  /*!*/import { node } from '@connectv/core';

const minLength = 6;
const symbols = ['$', '#', '@', '!', '%', '^', '*'];

/*!*/export const passwordValidator = node({ //--> this is the node's signature
/*!*/  inputs: ['pass', 'confirm'],          //--> taking in password and its confirmation as inputs
/*!*/  outputs: [                            //--> possible outputs ...
/*!*/    'valid',                            //... password is valid
/*!*/    'not match',                        //... passwords do not match
/*!*/    'too short',                        //... password is too short
/*!*/    'no symbol'                         //... password does not have a symbol character
/*!*/  ]
/*!*/}, (inputs, output) => {                //--> `inputs` is an object with all of the inputs,
/*!*/                                        //... `output` is a callback to to output the result
/*!*/  if (inputs.pass != inputs.confirm) output('not match');
/*!*/  if (inputs.pass.length < minLength) output('too short', minLength);
/*!*/  if (!inputs.pass.match(new RegExp(symbols.map(s => `\\${s}`).join('|'))))
/*!*/    output('no symbol', symbols);
/*!*/  output('valid');
/*!*/});
node() takes two arguments, first is the signature of your custom agent, and second the function outlining its functionality. This function is is provided with an object of all inputs, and a callback that will send given data to given output pin.

node() returns a factory function for your custom agent, so you can re-use your custom agent like this:
  import { wrap, map } from '@connectv/core';
import { fromEvent } from 'rxjs';

/*!*/import { passwordValidator } from './password-validator';

let pass = document.getElementById('pass') as HTMLInputElement;
let confirm = document.getElementById('confirm') as HTMLInputElement;
let hint = document.getElementById('hint');

/*!*/let validator = passwordValidator();

/*!*/wrap(fromEvent(pass, 'input')).to(map(() => pass.value)).to(validator.in('pass'));
/*!*/wrap(fromEvent(confirm, 'input')).to(map(() => confirm.value)).to(validator.in('confirm'));

/*!*/validator.out('valid').subscribe(() => hint.textContent = 'valid password');
/*!*/validator.out('not match').subscribe(() => hint.textContent = 'passowrds do not match');
/*!*/validator.out('too short').subscribe(l => hint.textContent = `must be at least ${l} characters`);
/*!*/validator.out('no symbol').subscribe(symbols => hint.textContent = `must include one of ${symbols}`);
Note that:

node()s also support implicit connection. For example you can simplify the previous example like this:
import { group, wrap, map } from '@connectv/core';
import { fromEvent } from 'rxjs';

import { passwordValidator } from './password-validator';

let pass = document.getElementById('pass') as HTMLInputElement;
let confirm = document.getElementById('confirm') as HTMLInputElement;
let hint = document.getElementById('hint');

group(
  wrap(fromEvent(pass, 'input')).to(map(() => pass.value)),
  wrap(fromEvent(confirm, 'input')).to(map(() => confirm.value)),
)
/*!*/.serialTo(passwordValidator())
/*!*/.serialTo(
/*!*/  map(() => 'valid password'),
/*!*/  map(() => 'passwords do not match'),
/*!*/  map(l => `must be at least ${l} characters`),
/*!*/  map(symbols => `must include one of ${symbols}`)
/*!*/)
.subscribe(v => hint.textContent = v);

Control

Each node() comes with a .control property which allows you to control on its execution:
  import { wrap, map, pipe, sink, group } from '@connectv/core';
import { fromEvent } from 'rxjs';
import { debounceTime } from 'rxjs/operators';

import { passwordValidator } from './password-validator';

let pass = document.getElementById('pass') as HTMLInputElement;
let confirm = document.getElementById('confirm') as HTMLInputElement;
let hint = document.getElementById('hint');

let validator = passwordValidator();
let $pass = wrap(fromEvent(pass, 'input')).to(map(() => pass.value));
let $confirm = wrap(fromEvent(confirm, 'input')).to(map(() => confirm.value));

group($pass, $confirm).serialTo(validator);

//
// so lets be a bit less annoying on feedback:
//
/*!*/group($pass, $confirm)                   //--> when the user types
/*!*/  .to(sink(() => hint.textContent = '')) //--> clear the hint
/*!*/  .to(pipe(debounceTime(1000)))          //--> wait a bit
/*!*/  .to(validator.control);                //--> THEN validate

validator.serialTo(
  map(() => 'valid password'),
  map(() => 'passwords do not match'),
  map(l => `must be at least ${l} characters`),
  map(symbols => `must include one of ${symbols}`)
).subscribe(v => hint.textContent = v);

Optional parameters

By default, a node() waits on values from inputs that are connected, i.e. if you do not connect anything from the outside to an input pin of a node(), it will be treated as an optional parameter and it will not appear in inputs object.

If your node() needs some specific inputs for execution, you can mark them in the required field of the signature object:
  node({
  inputs: ['a', 'b', 'c'],  //--> three inputs
  required: ['a', 'b'],     //--> a and b are required, which leaves c as an optional input
  outputs: [ /* ... */ ]
}, (inputs, output) => {
  //...
})


Error handling

A third argument is always provided to your node's function, which can be used for error handling:
node({
  //...
}, (inputs, output, error) => {
  //...
  if (errorCase)
    error('some error');
  //....
})
You MUST capture all errors and pass them to this callback for them to be handled properly.

Further reading




Copied to Clipboard!