Security News
The Risks of Misguided Research in Supply Chain Security
Snyk's use of malicious npm packages for research raises ethical concerns, highlighting risks in public deployment, data exfiltration, and unauthorized testing.
derive-state
Advanced tools
State library that implements the Observer pattern, built with reactivity and composition in mind.
State library that implements the Observer pattern, built with reactivity and composition in mind.
Heavily inspired from RxJS, but while RxJS focuses on asynchronous values, DeriveState focuses on stateful boxes, (aka cells in a spreadsheet) - As if everything were a BehaviourSubject
, and you create new states deriving them from other states.
Experimental
import { State } from 'derive-state';
const apples = new State(0);
apples.subscribe(apples => console.log(`We now have ${apples} apple(s)`));
// Logs "We now have 0 apple(s)"
apples.setValue(2);
// Logs "We now have 2 apple(s)"
console.log('apples: ' + apples.getValue());
// Logs "apples: 2"
The initial value is optional: if omitted the state will be empty
import { State } from 'derive-state';
const apples = new State();
apples.subscribe(apples => console.log(`We now have ${apples} apple(s)`));
// Doesn't log anything
// We can't read the value synchronously in this case
expect(() => apples.getValue()).toThrow();
// We have a function that tell us whether it holds a value or not
expect(apples.hasValue()).toBe(false);
// Or we can get a promise for when it finally has a value
apples.value.then(v => console.log(`Promise resolved with ${v}`));
apples.setValue(2);
// Logs "We now have 2 apple(s)" followed by "Promise resolved with 2"
expect(apples.hasValue()).toBe(true);
Subscriptions and state can both get cleaned up:
import { State } from 'derive-state';
const apples = new State(0);
const unsub1 = apples.subscribe(
apples => console.log(`We now have ${apples} apple(s)`)
() => console.log("complete")
);
// Logs "We now have 0 apple(s)"
unsub1();
apples.setValue(2);
// Doesn't log anything
apples.subscribe(
apples => console.log(`We now have ${apples} apple(s)`)
() => console.log("complete")
);
// Logs "We now have 2 apple(s)"
apples.close();
// Logs "complete"
// We can't set the value of a closed state.
// As semantically it means this value won't ever change again.
expect(() => apples.setValue(5)).toThrow();
// But we can still read the value
console.log('apples: ' + apples.getValue());
// Logs "apples: 2"
// And subscribe (it will just call back and close the subscription immediately)
apples.subscribe(
apples => console.log(`We now have ${apples} apple(s)`)
() => console.log("complete")
);
// Logs "We now have 2 apple(s)" followed by "complete"
An stateless observable doesn't hold any value. It has two main ways it can be used:
import { Stateless } from 'derive-state';
const clicks = new Stateless();
clicks.emit('click');
clicks.subscribe(() => console.log('received a click'));
// Logs nothing
clicks.emit('click');
// Logs "received a click"
import { Stateless } from 'derive-state';
const apples = new State(2);
const squaredApples = new Stateless(obs => {});
const squaredApples = new Stateless(obs =>
apples.subscribe(apples => obs.next(apples * apples), obs.complete)
);
squaredApples.subscribe(apples =>
console.log(`We now have ${apples} squared apple(s)`)
);
// Logs "We now have 4 squared apple(s)"
// Equivalent when using `pipe`:
const pipedSquaredApples = apples.pipe(map(apples => apples * apples));
A stateless is lazy: Meaning it won't run the derive function until someone subscribes to it.
A stateless is multicast: It will share the derive function along all the subscribers in the chain.
Stateless are designed to make composition easily: This way we can write operators that define their behaviour, and we can compose them easily with pipe
.
Note that this has an apparent issue:
const squaredApples = apples.pipe(map(apples => apples * apples));
squaredApples.subscribe(apples =>
console.log(`We now have ${apples} squared apple(s)`)
);
// Logs "We now have 4 squared apple(s)"
squaredApples.subscribe(apples =>
console.log(`We now have ${apples} squared apple(s)`)
);
// Doesn't log anything
This is due to the nature of squaredApples
being stateless and multicast: By the time the second subscriber comes in, apples
has already emitted its value, so it won't receive it. When apple
changes, both subscribers will receive the new value.
That's why usually you would capture
these stateless observables into stateful observables. The trade-off is that while stateless observables get cleaned up when they don't have subscriptions, stateful observables as they hold a value they need cleaning up, that's why you'd call capture
only after you've applied the composed operator chain
const pears = new State(10);
// eq. combineLatest in rxjs
const totalFruits = combine([apples, pears])
.pipe(map(([apples, pears]) => apples + pears))
.capture();
totalFruits.subscribe(fruits => console.log(`We now have ${fruits} fruits`));
// Logs "We now have 12 fruits"
totalFruits.subscribe(fruits => console.log(`We now have ${fruits} fruits`));
// Logs "We now have 12 fruits"
Creation operators
just(value)
: Emits value
on the first subscription.combine(obj)
: subscribes to every observable in obj
, and emits the value of all of them in the same structure as obj
(works with arrays too)merge(array)
: subscribes to all the observables in array and emits every value from them.Pipeable operators
map(fn)
: maps the values from the source stream by using the map function.filter(fn)
: filters changes based on the filter function.distinctUntilChanged()
: prevents emitting the same value twice in a row.switchMap(fn)
: flattens out the observable returned by the map function, unsubscribing from the previous ones.take(n)
: updates at most N times.skip(n)
: skips the first N values.withDefault(value)
: adds in a value if none is present.scan(accumulator, initialValue?)
: accumulates and emits values using the accumulator functionFAQs
State library that implements the Observer pattern, built with reactivity and composition in mind.
The npm package derive-state receives a total of 234 weekly downloads. As such, derive-state popularity was classified as not popular.
We found that derive-state demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Snyk's use of malicious npm packages for research raises ethical concerns, highlighting risks in public deployment, data exfiltration, and unauthorized testing.
Research
Security News
Socket researchers found several malicious npm packages typosquatting Chalk and Chokidar, targeting Node.js developers with kill switches and data theft.
Security News
pnpm 10 blocks lifecycle scripts by default to improve security, addressing supply chain attack risks but sparking debate over compatibility and workflow changes.