0.1.2 • Published 7 years ago

observable-diff-operator v0.1.2

Weekly downloads
2
License
MIT
Repository
github
Last release
7 years ago

observable-diff-operator

Greenkeeper badge NPM version Build Status Coverage Status Standard Version

this is fromDiff / toDiff logic implementation for observables.

those operators are meant for sending observables over network: Example diagram

Wrapping observers for Examples

to use the logic, you will need to simply wrap observables using your observables implementation, for example, RxJs:

import { Observable } from 'rxjs';
import { fromDiffObserver, IObservableDiff, toDiffObserver } from 'observable-diff-operator';

export function fromDiffWrap<T>(obs: Observable<IObservableDiff>): Observable<T> {
  return new Observable((observer) => {
    return obs.subscribe(fromDiffObserver(observer));
  });
}

export function toDiffWrap<T>(obs: Observable<T>): Observable<IObservableDiff> {
  return new Observable((observer) => {
    return obs.subscribe(toDiffObserver(observer));
  });
}

NOTE: for rxjs, rxjs-diff-operator will give the same result but as embeded operator instead of converting observables.

Operators:

toDiff

signature: toDiffObserver<T>(Observer<IObservableDiff>): Observer<T>

Description

toDiff operator is used to convert output of an obsersvable stream, into a stream that contains diff information. this operator is inteded to be used on the server.

Example
//emit (1,2,3,4,5)
const source = Rx.Observable.from([1,2,3,4,5]);
//add 10 to each value
const example = toDiffWrap(source);
//output: { type: "init", payload: 1, isObject: false }, { type: "update", payload: 2 }, ...
const subscribe = example.subscribe(val => console.log(val));

fromDiff

signature: fromDiffObserver<T>(Observer<T>): Observable<IObservableDiff>

Description

fromDiff operator is used to convert output of an diff obsersvable stream (see toDiff above), into a stream that contains diff information. this operator is inteded to be used on the client.

Example
//emit diff information
const source = Rx.Observable.from([{ type: "init", payload: 1, isObject: false }, { type: "update", payload: 2 }, { type: "complete" }]);
//add 10 to each value
const example = fromDiffWrap(source);
//output: 1, 2
const subscribe = example.subscribe(val => console.log(val));

Deep-Dive:

Protocol

the protocol contains 4 message types:

  • init
    • must be the first message on the line.
    • contains isObject property which represents the payload type on the stream.
    • contains intial payload (not a diff)
  • update
    • sent for each next on the original observable
    • contains payload property as raw value (simple values) or diff object (objects/arrays)
  • error
    • sent for errors on the original observable
    • contains payload of error message
  • complete
    • sent when original observable is complete.

Handling Objects:

diffing simple values is not efficient, the real power of this operator comes when dealing with array or objects. for that, deep-diff is being used.

Object example:

given the input of:

{
  "value": 1,
},
{
  "value": 2,
},
{
  "value": 3,
},
{
  "value": 4,
},
{
  "value": 5,
},

this output will be emitted:

{
    "isObject": true,
    "payload": {
        "value": 1,
    },
    "type": "init",
}, {
    "payload": [
        {
            "kind": "E",
            "lhs": 1,
            "path": [
                "value",
            ],
            "rhs": 2,
        },
    ],
    "type": "update",
}, {
    "payload": [
        {
            "kind": "E",
            "lhs": 2,
            "path": [
                "value",
            ],
            "rhs": 3,
        },
    ],
    "type": "update",
}, {
    "payload": [
        {
            "kind": "E",
            "lhs": 3,
            "path": [
                "value",
            ],
            "rhs": 4,
        },
    ],
    "type": "update",
}, {
    "payload": [
        {
            "kind": "E",
            "lhs": 4,
            "path": [
                "value",
            ],
            "rhs": 5,
        },
    ],
    "type": "update",
}, {
    "type": "complete",
}

Observable with an error example:

given the input of:

let obs: Observable<{ value: number }> = Observable.of(1, 2, 3, 4, 5)
.map((v: number, i: number) => {
    if ( i === 2 ) {
        throw new Error('testing errors');
    }

    return { value: v };
});

this output will be emitted:

{
    "isObject": true,
    "payload": {
        "value": 1,
    },
    "type": "init",
}, {
    "payload": [
        {
            "kind": "E",
            "lhs": 1,
            "path": [
                "value",
            ],
            "rhs": 2,
        },
    ],
    "type": "update",
}, {
    "payload": "testing errors",
    "type": "error",
}

Contributions

Contributions, issues and feature requests are very welcome. If you are using this package and fixed a bug for yourself, please consider submitting a PR!