apeiron v1.0.5
###APEIRON.JS - reactive bindings for IO-actions and more. Apeiron is a tiny library written in ES6 with RxJS to provide concise and robust infrastructure for driving data layer abstractions: HTTP, SSE, WS, other IO-actions as multidirectional reactive streams (ie. binding IO and Observable/Observer with Rx.Subject and vice versa).
####Motivation What is the difference between Apeiron and RxJS-DOM?
Apeiron is not just a syntactic sugar to avoid boilerplate code. It is flexible and semantically clean abstraction for M(model) layer.
- Apeiron provides unified and simple API for IO of any origin.
- Apeiron is accurate in terms of app architecture semantics: any IO is implied to be treated with Apeiron, which encourages best FP practices (side effect denotation with data type).
- Hence Apeiron implies good architecture: Model layer isolation, Model immutability, flattened dataStream and errorStream, and on the other hand allows high composability and decomposition.
- The barrier for entry into Apeiron is low: you may have no reactive or Haskell experience to operate IO a binding to Stream a quasi-impertively as usual chained methods.
- Apeiron has richer API both on HTTP and WS/SSE written in more functional style.
- Apeiron plays great with RxJS - dataStream and errorStream are merely RxJS Observables.
HTTP features:
- Apeiron HTTP relies on Axios library, which has richer API than RxJS-DOM.
- Apeiron HTTP endpoints config is declarative - you may think of it as dual to View framework router.
- On the other hand Apeiron HTTP is dynamic - you can create new Apeiron HTTP instances base on subsets of config. Check GroupBy API.
WS/SSE features:
- Apeiron WS automatically tries to reconnect.
- Apeiron WS handles WS life cycle for open, close, and error consistently - bound to dataStream/errorStream Observables and send/sendMany API.
- Apeiron WS send close command to the server when completed is called.
- Apeiron WS enforces a single instance of a socket regardless of the number of subscriptions.
- Apeiron WS gracefully buffers messages until underlying socket isn't open and then sends them asynchronously when it does open.
ALSO:
- Apeiron's end mission is isomorphic development: similar API for client and node.js (currently in development).
####Install
NPM:
npm i apeiron -Sthen hook up APEIRON.js into project:
ES6:
import { StreamAPI } from 'apeiron'Node / Browserify:
const StreamAPI = require('apeiron').StreamAPIUMD:
<script src="apeiron/dist/apeiron.min.js"></script>####Class API Import Apeiron:
import { StreamAPI } from 'apeiron'Create streamer instance with following data structures:
StreamAPI :: TYPE -> OPTIONS -> Streamer
-- JS: const Streamer = new StreamAPI(TYPE, OPTIONS);
-- notice that args are not curried.
data TYPE = "HTTP" | "WS" | "SSE"
data OPTIONS = 
	HTTPOptions {
		config :: Maybe AxiosConfig, 
		endpoints :: ApeironEndopints
	} | 
	WSOptions {
		endpoint :: Url,
		protocol :: Protocol | [Protocol]
	} |
	SSEOptions {
		endpoint :: Url,
		withCredentials: Bool
	}####Streams API
-- common interface
-- JS: Streamer.dataStream
dataStream :: Observable a
-- JS: Streamer.errorStream
errorStream :: Observable a
-- for SSE and WS only
-- JS: Streamer.metaStream
metaStream :: Observable a####Send API
Send API is for HTTP and WS only. It tunnels data to IO () and returns dataStream reference.
-- Notice, that Data type differs for HTTP and WS (see examples below)
-- JS: Streamer.send(Data)
send :: Data -> Streamer.dataStream
-- JS: Streamer.send([Data1, Data2.. DataN], 1000) or Streamer.send([Data1, Data2.. DataN])
sendMany :: [Data] -> Maybe Delay -> Streamer.dataStream
-- for SSE and WS
-- JS: Streamer.close()
close :: IO ()As send / sendMany returns Streamer.dataStream, it is easy to nest operations like:
DL.send('getPosts')
	.filter(x => x.isActive)
	.subscribe(x => console.log('Data Stream:', x));####Group API - HTTP only
Creates new Streamer instance with the endpoints matched by name (multiple args) / url (single arg) / method (single arg)
-- JS: Streamer.groupByName('ep1','ep2',...'epN')
groupByName :: Args EP -> Streamer
-- JS: Streamer.groupByUrl('posts')
groupByUrl :: Url -> Streamer
-- JS: Streamer.groupByMethod('put')
groupByMethod :: Method -> Streamer####Headers API - HTTP only Add and remove headers for all HTTP requests
-- JS: Streamer.setHeader('common', 'AUTH', token)
setHeader :: HMethod -> Header -> Value -> ()
-- JS: Streamer.removeHeader('common', 'AUTH')
removeHeader :: HMethod -> Header -> ()####Type reference
data Method = "post" | "put" | "patch" | "get" | "delete" | "head"
data HMethod = Method | "common"
type Url = String
type EP = String
type Header = String
type Value = String
type Delay = Int
data Data = HTTPData {
	endpoint :: EP,
	payload :: {
		data :: Maybe a,
		config :: Maybe AxiosConfig
	}} | 
	WSData {
		endpoint: EP,
		data: a
	}####Examples HTTP Prepare config (for config details check AXIOS API):
const config = {
  baseURL: 'http://localhost:3000'
};Add endpoints declaratively:
const endpoints = {
  removePost: {
    url: '/posts/:id',
    method: 'delete'
  },
  addPost: {
    url: '/posts',
    method: 'post'
  },
  getPosts: {
    url: '/posts',
    method: 'get'
  },
  getPost: {
    url: '/posts/:id',
    method: 'get'
  },
  editPost: {
    url: '/posts/:id',
    method: 'put'
  }
}Create Apeiron instance:
const StreamAPI = require('apeiron').StreamAPI;
const DL = new StreamAPI('HTTP', { endpoints, config });Run REST API server and add subscription:
DL.dataStream.subscribe(res => {
  console.log('Data Stream:');
  console.log(res.data);
});
DL.dataStream.subscribe(insertDataInDOM);
DL.errorStream.subscribe(err => {
  console.log('Error Stream:');
  console.error(err);
});
DL.errorStream.subscribe(err => {
  if (err.statusText) {
    $('#notifications').text(err.statusText);
  } else {
    $('#notifications').text(err);
  }
});
DL.errorStream.subscribe(err => {
  console.log('SSE Error Stream:');
  console.error(err);
});Senders example:
DL.send('getPosts'); // possible no config for GET 
DL.send('getPost', {
    config: { id : 1 } // GET id denoted in config for url/:id	
});
DL.send('getPost', {
	config: { // GET url/:id with query params ?ID=123
		id : 1,
		params : { ID : 123 } 
	}
});
DL.send('addPost', {
	data: { a: '12345' }, // data is required for POST, PUT, PATCH
	config: {
		params: { ID: 123 }, // optional
		withCredentials: true, // optional
		headers: {'X-Requested-With': 'XMLHttpRequest'} // optional
	}
})
DL.send('removePost', {
	// data - ignored for GET, DELETE, HEAD
	config: {
		id: 12345, // required for DELETE, HEAD, optional for GET
		params: { ID: 12345 }, // optional
		withCredentials: true, // optional
		headers: {'X-Requested-With': 'XMLHttpRequest'} // optional
	}
})
// sendMany argument is the list of tuples :: [[endpoint, payload]]
// i.e. [[endpoint1, {data, config}], [endpoint2, {data, config}]]
DL.sendMany([
	['getPost', {config: {id: 1} }],
	['getPost', {config: {id: 9774} }]
]);For config details check AXIOS API
####Examples WS/SSE Create Apeiron instance:
const StreamAPI = require('apeiron').StreamAPI;
const DLWS = new StreamAPI('WS', 'ws://localhost:3001');Run server and add subscription:
DLWS.dataStream
	.take(10)
	.do(() => void 0, () => void 0, () => {
		DLWS.close();
		console.log('DLWS stopped');
	})
	.subscribe((res) => {
		console.log('WS Data Stream:');
		res.type === 'message' ?
		  console.log(res.data) :
		  console.log(res.type);
	});
DLWS.errorStream
	.subscribe(err => {
	  console.log('SSE Error Stream:');
	  console.error(err);
	});Senders example:
DLWS.sendMany([{data:'1'},{data:'2'},{data:'3'}]);
setTimeout(() => {
  DLWS.send({data:'x'});
}, 3000)Same way works for SSE.
Check more examples in /examples folder
####License ISC
####TODO (Beta-3 release)
CLIENT (ClientAPI)
1. StreamAPI -> ClientAPI
2. bower package
3. browser support
4. WS reconnect
5. HTTP.poll
6. HTTP.pollUntil
7. Notification API
8. unit tests####TODO (Beta-4 release)
SERVER (ServerAPI)
1. SSE custom
2. WS ws
3. Regis redis
4. Mongo mongodb
5. unit tests####Future examples 1. DL -> Controller -> Stateless Components (React) 2. DL -> Stateless Services -> VM (Angular 1.5) 3. Architecture guide: async MVC (DL -> Controller -> View)