Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@thi.ng/atom

Package Overview
Dependencies
Maintainers
1
Versions
241
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@thi.ng/atom - npm Package Compare versions

Comparing version 0.9.0 to 0.9.1

9

api.d.ts

@@ -6,7 +6,8 @@ import * as api from "@thi.ng/api/api";

export declare type ViewTransform<T> = (x: any) => T;
export declare type InterceptorFn = (state: any, e: Event) => any;
export declare type InterceptorPredicate = (state: any, e: Event) => boolean;
export declare type InterceptorFn = (state: any, e: Event, fx?: any) => any;
export declare type InterceptorPredicate = (state: any, e: Event, fx?: any) => boolean;
export declare type SideEffect = (x: any) => void;
export declare type EventDef = Interceptor | Interceptor[] | InterceptorFn | InterceptorFn[];
export declare type EffectDef = [SideEffect, number];
export declare type EventDef = Interceptor | InterceptorFn | (Interceptor | InterceptorFn)[];
export declare type EffectDef = SideEffect | [SideEffect, number];
export declare type EffectPriority = [string, number];
export interface ReadonlyAtom<T> extends api.IDeref<T>, api.IWatch<T> {

@@ -13,0 +14,0 @@ }

@@ -6,2 +6,10 @@ # Change Log

<a name="0.9.1"></a>
## [0.9.1](https://github.com/thi-ng/umbrella/compare/@thi.ng/atom@0.9.0...@thi.ng/atom@0.9.1) (2018-03-07)
**Note:** Version bump only for package @thi.ng/atom
<a name="0.9.0"></a>

@@ -8,0 +16,0 @@ # [0.9.0](https://github.com/thi-ng/umbrella/compare/@thi.ng/atom@0.8.0...@thi.ng/atom@0.9.0) (2018-03-07)

import { IObjectOf } from "@thi.ng/api/api";
import { DCons } from "@thi.ng/dcons";
import * as api from "./api";
/**
* Batched event processor for using composable interceptors for event handling
* and side effects to execute the result of handled events.
*
* In this model an event handler is an array of objects with `pre` and/or `post`
* keys and functions attached to each key. These functions are called interceptors,
* since each intercepts the processing of an event and can contribute their
* own side effects. The outcome of this setup is a more aspect-oriented, composable
* approach to event handling and allows to inject common, re-usable behaviors
* for multiple event types (tracing, validation, undo/redo triggers etc.)
*
* The overall approach of this type of event processing is heavily based on the
* pattern initially pioneered by @Day8/re-frame, with the following differences:
*
* - standalone implementation (no assumptions about surrounding context/framework)
* - manual trigger of event queue processing
* - supports event cancellation
* - side effect collection (multiple side effects for same effect type per frame)
* - side effect priorities (to better control execution order)
* - dynamic addition/removal of handlers & effects
*
*/
export declare class EventBus {

@@ -10,3 +31,3 @@ state: api.IAtom<any>;

effects: IObjectOf<api.SideEffect>;
priorites: DCons<[number, string]>;
priorities: api.EffectPriority[];
constructor(state: api.IAtom<any>, handlers?: IObjectOf<api.EventDef>, effects?: IObjectOf<api.EffectDef>);

@@ -17,8 +38,71 @@ addHandler(id: string, spec: api.EventDef): void;

addEffects(specs: IObjectOf<api.EffectDef>): void;
removeHandler(id: string): void;
removeHandlers(ids: string[]): void;
removeEffect(id: string): void;
removeEffects(ids: string[]): void;
/**
* Adds given event to event queue to be processed
* by `processQueue()` later on.
*
* @param e
*/
dispatch(e: api.Event): void;
/**
* Adds given event to whatever is the current
* event queue. If triggered via the `FX_DISPATCH_NOW`
* side effect the event will still be executed
* in the currently active batch. If called from
* elsewhere, the result is the same as calling
* `dispatch()`.
*
* @param e
*/
dispatchNow(e: api.Event): void;
/**
* Triggers processing of current event queue and
* returns `true` if the any of the processed events
* caused a state change.
*
* If an event handler triggers the `FX_DISPATCH_NOW`
* side effect, the new event will be added to the
* currently processed batch and therefore executed
* in the same frame. Also see `dispatchNow()`.
*/
processQueue(): boolean;
/**
* Processes a single event using the configured handler/interceptor chain.
* Logs warning message and skips processing if no handler
* is available for the event.
*
* This function processes the array of interceptors in bi-directional
* order. First any `pre` interceptors are processed in
* forward order. Then `post` interceptors are processed in reverse.
*
* Each interceptor can return a result object of side effects,
* which are being merged and collected for `processEffects()`.
*
* Any interceptor can trigger zero or more known side effects,
* each (side effect) will be collected in an array to support
* multiple invocations of the same effect type per frame. If no
* side effects are requested, an interceptor can return `undefined`.
*
* Processing of the current event stops immediatedly, if an
* interceptor includes the `FX_CANCEL` side effect. However, the
* results interceptors (incl. the one which cancelled) are kept and
* processed further as usual.
*
* @param fx
* @param e
*/
protected processEvent(fx: any, e: api.Event): void;
/**
* Takes a collection of side effects generated during
* event processing and applies them in order of configured
* priorities.
*
* @param fx
*/
protected processEffects(fx: any): void;
protected mergeEffects(fx: any, ret: any): void;
protected insertPriority(p: api.EffectPriority): void;
}

@@ -5,4 +5,25 @@ "use strict";

const is_function_1 = require("@thi.ng/checks/is-function");
const dcons_1 = require("@thi.ng/dcons");
const api = require("./api");
/**
* Batched event processor for using composable interceptors for event handling
* and side effects to execute the result of handled events.
*
* In this model an event handler is an array of objects with `pre` and/or `post`
* keys and functions attached to each key. These functions are called interceptors,
* since each intercepts the processing of an event and can contribute their
* own side effects. The outcome of this setup is a more aspect-oriented, composable
* approach to event handling and allows to inject common, re-usable behaviors
* for multiple event types (tracing, validation, undo/redo triggers etc.)
*
* The overall approach of this type of event processing is heavily based on the
* pattern initially pioneered by @Day8/re-frame, with the following differences:
*
* - standalone implementation (no assumptions about surrounding context/framework)
* - manual trigger of event queue processing
* - supports event cancellation
* - side effect collection (multiple side effects for same effect type per frame)
* - side effect priorities (to better control execution order)
* - dynamic addition/removal of handlers & effects
*
*/
class EventBus {

@@ -14,3 +35,3 @@ constructor(state, handlers, effects) {

this.eventQueue = [];
this.priorites = new dcons_1.DCons();
this.priorities = [];
this.addEffect(api.FX_STATE, (x) => this.state.reset(x), -1000);

@@ -43,15 +64,78 @@ this.addEffect(api.FX_DISPATCH, (e) => this.dispatch(e), -999);

this.effects[id] = fx;
this.priorites.insertSorted([priority, id], (a, b) => a[0] - b[0]);
const p = [id, priority];
const priors = this.priorities;
for (let i = 0; i < priors.length; i++) {
if (p[1] < priors[i][1]) {
priors.splice(i, 0, p);
return;
}
}
priors.push(p);
}
addEffects(specs) {
for (let id in specs) {
this.addEffect(id, specs[id][0], specs[id][1]);
const fx = specs[id];
if (is_array_1.isArray(fx)) {
this.addEffect(id, fx[0], fx[1]);
}
else {
this.addEffect(id, fx);
}
}
}
removeHandler(id) {
delete this.handlers[id];
}
removeHandlers(ids) {
for (let id of ids) {
this.removeHandler(id);
}
}
removeEffect(id) {
delete this.effects[id];
const p = this.priorities;
for (let i = p.length - 1; i >= 0; i--) {
if (id === p[i][0]) {
p.splice(i, 1);
return;
}
}
}
removeEffects(ids) {
for (let id of ids) {
this.removeEffect(id);
}
}
/**
* Adds given event to event queue to be processed
* by `processQueue()` later on.
*
* @param e
*/
dispatch(e) {
this.eventQueue.push(e);
}
/**
* Adds given event to whatever is the current
* event queue. If triggered via the `FX_DISPATCH_NOW`
* side effect the event will still be executed
* in the currently active batch. If called from
* elsewhere, the result is the same as calling
* `dispatch()`.
*
* @param e
*/
dispatchNow(e) {
(this.currQueue || this.eventQueue).push(e);
}
/**
* Triggers processing of current event queue and
* returns `true` if the any of the processed events
* caused a state change.
*
* If an event handler triggers the `FX_DISPATCH_NOW`
* side effect, the new event will be added to the
* currently processed batch and therefore executed
* in the same frame. Also see `dispatchNow()`.
*/
processQueue() {

@@ -72,4 +156,33 @@ if (this.eventQueue.length > 0) {

}
/**
* Processes a single event using the configured handler/interceptor chain.
* Logs warning message and skips processing if no handler
* is available for the event.
*
* This function processes the array of interceptors in bi-directional
* order. First any `pre` interceptors are processed in
* forward order. Then `post` interceptors are processed in reverse.
*
* Each interceptor can return a result object of side effects,
* which are being merged and collected for `processEffects()`.
*
* Any interceptor can trigger zero or more known side effects,
* each (side effect) will be collected in an array to support
* multiple invocations of the same effect type per frame. If no
* side effects are requested, an interceptor can return `undefined`.
*
* Processing of the current event stops immediatedly, if an
* interceptor includes the `FX_CANCEL` side effect. However, the
* results interceptors (incl. the one which cancelled) are kept and
* processed further as usual.
*
* @param fx
* @param e
*/
processEvent(fx, e) {
const iceps = this.handlers[e[0]];
if (!iceps) {
console.warn(`missing handler for event type: ${e[0]}`);
return;
}
const n = iceps.length - 1;

@@ -80,3 +193,3 @@ let hasPost = false;

if (icep.pre) {
this.mergeEffects(fx, icep.pre(fx.state, e));
this.mergeEffects(fx, icep.pre(fx.state, e, fx));
}

@@ -91,10 +204,17 @@ hasPost = hasPost || !!icep.post;

if (icep.post) {
this.mergeEffects(fx, icep.post(fx.state, e));
this.mergeEffects(fx, icep.post(fx.state, e, fx));
}
}
}
/**
* Takes a collection of side effects generated during
* event processing and applies them in order of configured
* priorities.
*
* @param fx
*/
processEffects(fx) {
const effects = this.effects;
for (let p of this.priorites) {
const id = p[1];
for (let p of this.priorities) {
const id = p[0];
const val = fx[id];

@@ -130,3 +250,5 @@ if (val !== undefined) {

}
insertPriority(p) {
}
}
exports.EventBus = EventBus;

@@ -1,5 +0,52 @@

import { InterceptorFn, InterceptorPredicate } from "./api";
export declare const trace: (_: any, e: any) => void;
export declare const ensurePred: (pred: InterceptorPredicate, err?: InterceptorFn) => (state: any, e: any) => any;
export declare const ensureLessThan: (max: number, err: InterceptorFn) => (state: any, e: any) => any;
export declare const ensureGreaterThan: (min: number, err: InterceptorFn) => (state: any, e: any) => any;
import { Event, InterceptorFn, InterceptorPredicate, Path } from "./api";
/**
* Debug interceptor to log the current event to the console.
*/
export declare function trace(_: any, e: any): void;
/**
* Higher-order interceptor for validation purposes.
* Takes a predicate function and an optional interceptor function,
* which will only be called if the predicate fails for a given event.
* By default the `FX_CANCEL` side effect is triggered if the predicate
* failed, thus ensuring the actual event handler for the failed event
* will not be executed anymore. However, this can be overridden using
* the error interceptor's result, which is merged into the result of
* this interceptor.
*
* Note: For this interceptor to work as expected, it needs to be provided
* BEFORE the main handler in the interceptor list for a given event, i.e.
*
* ```
* [
* ensurePred((state, e) => false),
* (state, e) => console.log("no one never calls me")
* ]
* ```
*
* @param pred predicate applied to given state & event
* @param err interceptor triggered on predicate failure
*/
export declare function ensurePred(pred: InterceptorPredicate, err?: InterceptorFn): (state: any, e: any, fx: any) => any;
/**
* Specialization of `ensurePred()` to ensure a state value is less than given max.
* The optional `path` fn is used to extract or produce the path for the state
* value to be validated. If omitted, the event's payload item is interpreted as
* the value path.
*
* For example, without a provided `path` function and for an event
* of this form: `["event-id", "foo.bar"]`, the term `"foo.bar"` would be
* interpreted as path.
*
* If the event has this shape: `["event-id", ["foo.bar", 23]]`, we must provide
* `(e) => e[1][0]` as path function to extract `"foo.bar"` from the event.
*
* @param path path extractor
*/
export declare function ensureLessThan(max: number, path?: (e: Event) => Path, err?: InterceptorFn): (state: any, e: any, fx: any) => any;
/**
* Specialization of `ensurePred()` to ensure a state value is greater than given min.
* See `ensureLessThan()` for further details.
*
* @param path path extractor
*/
export declare function ensureGreaterThan(min: number, path?: (e: Event) => Path, err?: InterceptorFn): (state: any, e: any, fx: any) => any;

@@ -5,20 +5,68 @@ "use strict";

const path_1 = require("./path");
///////////////////////////////////////////////////////////////////////
// re-usable interceptors
// this one simply logs the current event
exports.trace = (_, e) => console.log("event:", e);
// higher-order interceptor for validation purposes
// takes a predicate function and an optional interceptor function
// which will be called if the predicate fails for a given event
// by default the FX_CANCEL side effect is triggered if there is a failure
// ensuring the actual event handler for the failed event will not be
// executed anymore
exports.ensurePred = (pred, err) => (state, e) => {
if (!pred(state, e)) {
return Object.assign({ [api_1.FX_CANCEL]: true }, (err ? err(state, e) : null));
}
};
// specialization of `ensurePred()` to ensure a value less than given max
exports.ensureLessThan = (max, err) => exports.ensurePred((state, e) => path_1.getIn(state, e[1]) < max, err);
// specialization of `ensurePred()` to ensure a value greater than given min
exports.ensureGreaterThan = (min, err) => exports.ensurePred((state, e) => path_1.getIn(state, e[1]) > min, err);
/**
* Debug interceptor to log the current event to the console.
*/
function trace(_, e) {
console.log("event:", e);
}
exports.trace = trace;
/**
* Higher-order interceptor for validation purposes.
* Takes a predicate function and an optional interceptor function,
* which will only be called if the predicate fails for a given event.
* By default the `FX_CANCEL` side effect is triggered if the predicate
* failed, thus ensuring the actual event handler for the failed event
* will not be executed anymore. However, this can be overridden using
* the error interceptor's result, which is merged into the result of
* this interceptor.
*
* Note: For this interceptor to work as expected, it needs to be provided
* BEFORE the main handler in the interceptor list for a given event, i.e.
*
* ```
* [
* ensurePred((state, e) => false),
* (state, e) => console.log("no one never calls me")
* ]
* ```
*
* @param pred predicate applied to given state & event
* @param err interceptor triggered on predicate failure
*/
function ensurePred(pred, err) {
return (state, e, fx) => {
if (!pred(state, e, fx)) {
return Object.assign({ [api_1.FX_CANCEL]: true }, (err ? err(state, e, fx) : null));
}
};
}
exports.ensurePred = ensurePred;
/**
* Specialization of `ensurePred()` to ensure a state value is less than given max.
* The optional `path` fn is used to extract or produce the path for the state
* value to be validated. If omitted, the event's payload item is interpreted as
* the value path.
*
* For example, without a provided `path` function and for an event
* of this form: `["event-id", "foo.bar"]`, the term `"foo.bar"` would be
* interpreted as path.
*
* If the event has this shape: `["event-id", ["foo.bar", 23]]`, we must provide
* `(e) => e[1][0]` as path function to extract `"foo.bar"` from the event.
*
* @param path path extractor
*/
function ensureLessThan(max, path, err) {
return ensurePred((state, e) => path_1.getIn(state, path ? path(e) : e[1]) < max, err);
}
exports.ensureLessThan = ensureLessThan;
/**
* Specialization of `ensurePred()` to ensure a state value is greater than given min.
* See `ensureLessThan()` for further details.
*
* @param path path extractor
*/
function ensureGreaterThan(min, path, err) {
return ensurePred((state, e) => path_1.getIn(state, path ? path(e) : e[1]) > min, err);
}
exports.ensureGreaterThan = ensureGreaterThan;
{
"name": "@thi.ng/atom",
"version": "0.9.0",
"version": "0.9.1",
"description": "Mutable wrapper for a immutable values",

@@ -29,4 +29,3 @@ "main": "./index.js",

"dependencies": {
"@thi.ng/api": "^2.0.3",
"@thi.ng/dcons": "0.1.13"
"@thi.ng/api": "^2.0.3"
},

@@ -33,0 +32,0 @@ "keywords": [

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc