@domonda/plumb
Advanced tools
Comparing version 1.0.0 to 2.0.0
@@ -8,12 +8,15 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const equality_1 = require("./equality"); | ||
function createPlumb(initialState, props = {}) { | ||
const { handler } = props; | ||
const { transformer, skipInitialTransform } = props; | ||
let disposeHandlers = []; | ||
let disposed = false; | ||
let internalState = initialState; | ||
// when the piped plumb triggers next it internally handles | ||
if (transformer && !skipInitialTransform) { | ||
internalState = transformer(internalState); | ||
} | ||
// when the chained plumb triggers next it internally transforms | ||
// the value before sending it to next, this flag is used to | ||
// skip re-handling the value | ||
let skipHandler = null; | ||
const handlers = []; | ||
// skip transforming the value twice | ||
let skipTransformer = null; | ||
const transformers = []; | ||
const subscribers = []; | ||
@@ -27,3 +30,3 @@ function subscribe(subscriber) { | ||
dispose: () => { | ||
subscribers.splice(subscribers.indexOf(subscriber)); | ||
subscribers.splice(subscribers.indexOf(subscriber), 1); | ||
}, | ||
@@ -37,11 +40,11 @@ }; | ||
internalState = state; | ||
// first do all extra handlers | ||
for (const handler of handlers) { | ||
if (handler !== skipHandler) { | ||
internalState = handler(internalState); | ||
// first do all extra transformers | ||
for (const transformer of transformers) { | ||
if (transformer !== skipTransformer) { | ||
internalState = transformer(internalState); | ||
} | ||
} | ||
// then do main handler | ||
if (handler) { | ||
internalState = handler(internalState); | ||
// then do main transformer | ||
if (transformer) { | ||
internalState = transformer(internalState); | ||
} | ||
@@ -57,29 +60,49 @@ for (const subscriber of subscribers) { | ||
} | ||
function pipe(props) { | ||
function chain(props) { | ||
if (disposed) { | ||
throw new Error('cannot pipe a disposed plumb'); | ||
throw new Error('cannot chain a disposed plumb'); | ||
} | ||
const { selector, updater, filter = () => true } = props; | ||
function pipeHandler(state) { | ||
return updater(state, selector(state)); | ||
const { selector, updater, filter = () => true, transformer: chainTransformer, skipInitialTransform: chainSkipInitialTransform, } = props; | ||
const memoData = { | ||
state: internalState, | ||
selectedState: selector(internalState), | ||
}; | ||
function memoSelector(state) { | ||
if (memoData.state !== state) { | ||
memoData.state = state; | ||
memoData.selectedState = selector(memoData.state); | ||
} | ||
return memoData.selectedState; | ||
} | ||
const nextState = pipeHandler(internalState); | ||
if (!equality_1.shallowEqual(internalState, nextState)) { | ||
next(nextState); | ||
function parentTransformer(state) { | ||
let selectedState = memoSelector(state); | ||
if (chainTransformer) { | ||
selectedState = chainTransformer(selectedState); | ||
} | ||
return updater(state, selectedState); | ||
} | ||
handlers.push(pipeHandler); | ||
let selectedState = selector(internalState); | ||
let outside = false; // next is triggered by the parent plumb | ||
let inside = false; // next is triggered on the piped plump | ||
const piped = createPlumb(selectedState, { | ||
handler: (selectedState) => { | ||
if (!outside) { | ||
transformers.push(parentTransformer); | ||
let isInitialTransform = true; | ||
let parentNext = false; // next is triggered by the parent plumb | ||
let chainedNext = false; // next is triggered on the chained/child plump | ||
const chained = createPlumb(memoSelector(internalState), { | ||
transformer: (selectedState) => { | ||
if (chainTransformer) { | ||
if (chainSkipInitialTransform && isInitialTransform) { | ||
// skip initial transform | ||
} | ||
else { | ||
selectedState = chainTransformer(selectedState); | ||
} | ||
} | ||
isInitialTransform = false; | ||
if (!parentNext) { | ||
const nextState = updater(internalState, selectedState); | ||
if (!equality_1.shallowEqual(internalState, nextState)) { | ||
inside = true; | ||
skipHandler = pipeHandler; | ||
if (internalState !== nextState) { | ||
chainedNext = true; | ||
skipTransformer = parentTransformer; | ||
next(nextState); | ||
skipHandler = null; | ||
inside = false; | ||
return selector(internalState); | ||
skipTransformer = null; | ||
chainedNext = false; | ||
return memoSelector(internalState); | ||
} | ||
@@ -91,18 +114,29 @@ } | ||
const subscription = subscribe((state) => { | ||
if (!inside) { | ||
selectedState = selector(state); | ||
if (!chainedNext) { | ||
const selectedState = memoSelector(state); | ||
if (filter(selectedState)) { | ||
outside = true; | ||
piped.next(selectedState); | ||
outside = false; | ||
parentNext = true; | ||
chained.next(selectedState); | ||
parentNext = false; | ||
} | ||
} | ||
}); | ||
piped.subscribe({ | ||
// indicates that dispose was called from the parent | ||
let parentDispose = false; | ||
function chainedDispose() { | ||
parentDispose = true; | ||
chained.dispose(); | ||
} | ||
disposeHandlers.push(chainedDispose); | ||
chained.subscribe({ | ||
dispose: () => { | ||
subscription.dispose(); | ||
handlers.splice(handlers.indexOf(pipeHandler)); | ||
// when parent disposals happen, the transformers will get deleted by the parent | ||
if (!parentDispose) { | ||
transformers.splice(transformers.indexOf(parentTransformer), 1); | ||
disposeHandlers.splice(disposeHandlers.indexOf(chainedDispose), 1); | ||
} | ||
}, | ||
}); | ||
return piped; | ||
return chained; | ||
} | ||
@@ -119,2 +153,7 @@ function dispose() { | ||
subscribers.splice(0, subscribers.length); | ||
transformers.splice(0, transformers.length); | ||
for (const disposeHandler of disposeHandlers) { | ||
disposeHandler(); | ||
} | ||
disposeHandlers.splice(0, disposeHandlers.length); | ||
internalState = undefined; | ||
@@ -136,5 +175,8 @@ disposed = true; | ||
}, | ||
get disposed() { | ||
return disposed; | ||
}, | ||
next, | ||
subscribe, | ||
pipe, | ||
chain, | ||
dispose, | ||
@@ -141,0 +183,0 @@ }; |
@@ -0,2 +1,3 @@ | ||
export * from './Plumb'; | ||
export * from './createPlumb'; | ||
export * from './equality'; |
{ | ||
"name": "@domonda/plumb", | ||
"version": "1.0.0", | ||
"version": "2.0.0", | ||
"description": "Simple state management inspired by Observables.", | ||
@@ -23,5 +23,4 @@ "keywords": [ | ||
}, | ||
"dependencies": {}, | ||
"main": "index.js", | ||
"typings": "index.d.ts" | ||
} |
@@ -14,8 +14,7 @@ /** | ||
export declare type Subscription = Disposable; | ||
export declare type Handler<T> = (state: Readonly<T>) => T; | ||
export declare type Transformer<T> = (state: Readonly<T>) => T; | ||
export declare type Filter<T> = (state: T) => boolean; | ||
export declare type Next<T> = (state: T) => void; | ||
export declare type Selector<T, K> = (state: Readonly<T>) => K; | ||
export declare type Updater<T, K> = (state: Readonly<T>, selectedState: Readonly<K>) => T; | ||
export interface PipeProps<T, K> { | ||
export interface ChainProps<T, K> extends PlumbProps<K> { | ||
selector: Selector<T, K>; | ||
@@ -26,4 +25,4 @@ updater: Updater<T, K>; | ||
export interface PlumbProps<T> { | ||
handler?: Handler<T>; | ||
next?: Next<T>; | ||
transformer?: Transformer<T>; | ||
skipInitialTransform?: boolean; | ||
} | ||
@@ -33,5 +32,6 @@ export interface Plumb<T> extends Disposable { | ||
readonly subscribers: Subscriber<T>[]; | ||
pipe: <K>(props: PipeProps<T, K>) => Plumb<K>; | ||
next: Next<T>; | ||
readonly disposed: boolean; | ||
chain: <K>(props: ChainProps<T, K>) => Plumb<K>; | ||
next: (state: T) => void; | ||
subscribe: (subscriber: Subscriber<T>) => Subscription; | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Deprecated
MaintenanceThe maintainer of the package marked it as deprecated. This could indicate that a single version should not be used, or that the package is no longer maintained and any new vulnerabilities will not be fixed.
Found 1 instance in 1 package
13011
306
0