npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@isthmus/atom

v1.0.0-rc13

Published

A minimalist atom implementation

Downloads

66

Readme

@isthmus/atom

This is intended to be a minimilistic implementation of Atoms. We will define an Atom as:

A mutable reference to objects which are treated as immutable.

It may be useful to also define 3 specific types of atoms.
root atom:

The basic atom, which has no dependencies on other atoms. Always writable.

lensed atom:

An atom which views a slice of another atom. It is writable if its source is writable. Writes will propagate to the top-most writable source atom.

computed atom:

Computes from other atoms. Always readonly.

Why @isthmus/atom?

This library intends to focus on being a powerful tool, while maintaing a small footprint and staying approachable. If you would like to use streams/atoms but fear about bloat then this may be a good choice for you.

The browser build (as of 9/22/2017) clocks in at 2.5kB gzipped:

> gzip -c dist/atom.js | wc -c
2559

Installation

You can install @isthmus/atom with:

npm install --save @isthmus/atom

Then to use in your JavaScript:

import { Atom, combine, scanMerge } from '@isthmus/atom'

// and then use...

There is a browser version available under @isthmus/atom/dist/atom.js:

<script src="https//unpkg.com/@isthmus/atom/dist/atom.js"></script>
<script>
  const { Atom, scan, HALT } = isthmus.atom
  // ...
</script>

API

Note that all functions without optional arguments are curried with crry.

fantasyland

Atom

Atom(any?) -> atom

Atom is a function which returns a root atom. It optionally can receive an initial value as its argument.

const state = Atom({ foo: { bar: [1, 2, 3] } })

atom

atom(any?) -> atom | any

atoms are functions which can be called with an argument to set its current value.

const atom = Atom(1) // 1
atom(7) // 7

or called with no argument to retrieve its value:

x = atom() + 1 // 8

combine

combine(fn, sources[]) -> computed atom

combine is a function which returns a computed atom from one or more atoms.

const n = Atom(7) 
const m = Atom(2)
const sum = combine(R.add, [n, m]) // 9

m(3) // sum: 10

map

map(fn, source) -> computed atom

map is an aliased version of combine for when mapping from just one atom.

const n = Atom(3)
const sqr = map(x => x * x, n) // 9

for convenience, each atom has a map method bound to it:

const doubled = n.map(R.multiply(2)) // 6

It is also inclued as atom['fantasyland/map'] to support the fantasyland Functor spec.

view

view(lens, atom) --> lensed atom

view returns a lensed atom. It can receive a string, number or array of the two which will be used as a path.

const source = Atom({ foo: [1, 2, 3] })
const lensed = view(['foo', 1], source) // 2
lensed(7) // 7; source: { foo: [1, 7, 3] }

const mapped = map(R.identity, source)
const readonly = view(['foo', 1], mapped) // 2; is readonly so readonly(7) -> Error

undefined or null as a lens, or in a lens path, will be treated like an identity lens. This can be useful for making separate trees that may be ended simultaneously.

const source = Atom({ foo: [1, 2, 3] })
const lensed = view('foo', source)
const proxy = view(null, source)
const proxiedLensed = view('foo', proxy)

proxy.end() // proxiedLensed is ended, lensed is still alive

for convenience, each atom has a view method bound to it:

const atom = Atom(['a', 'b', 'c'])
const head = atom.view(0) // 'a'

get

get(lens, atom) --> value

get allows you to retrieve an atom's value by supplied lens.

const source = Atom({ foo: [7, 9, 11] })
get(['foo', 2], source) // 11

for convenience, each atom has a get method bound to it:

source.get(['foo', 1]) // 9

set

set(lens, value, atom) --> atom

set allows you to change an atom's value by supplied lens. It returns the affected atom.

const source = Atom({ foo: [1, 2, 3] })

set(['foo', 1], 7, source) // { foo: [1, 7, 3] }

for convenience, each atom has a set method bound to it:

source.set(['foo', 2], 11) // { foo: [1, 7, 11] }

over

over(lens, fn, atom) --> atom

over allows you to change an atom's value by supplied lens by applying a function to it. It returns the affected atom.

const source = Atom({ foo: [1, 2, 3] })

over(['foo', 1], x => x - 3, source) // { foo: [1, -1, 3] }

for convenience, each atom has an over method bound to it:

source.over(['foo', 2], x => x * 2) // { over: [1, -1, 6] }

remove

remove(atom) -> atom

remove sets an atom to undefined, and returns the atom. If passed to a lensed atom, this means removing the property/array item from its source.

const atom = Atom({ a: 0, b: 0, c: 7 })
const b = atom.view('b')
remove(b) // undefined, atom: { a: 0, c: 7 }

for convenience, each atom has a remove method bound to it:

const atom = Atom(['a', 'b', 'c'])
const head = atom.view(0) // 'a'
head.remove() // undefined, atom: ['b', 'c']

isAtom

isAtom(any) -> boolean

isAtom is a helper function which returns if the given argument is an atom.

const source = Atom([1, 2, 3]) // [1, 2, 3]
const computed = map(x => ({ x }), source) // { x: [1, 2, 3] }
const lensed = view(['x', 1], computed) // 2

isAtom(source) // true
isAtom(computed) // true
isAtom(lensed) // true

isAtom(123) // false
isAtom('my string') // false
isAtom({ foo: 'bar' }) // false

modify

modify(fn, atom) -> atom

modify accepts a function which will receive the current value of the given atom. Its returned value will be set on the atom.
It returns the atom being modified so it can be chained.

const atom = Atom(7)
modify(R.add(3), atom) // atom: 10

for convenience, each atom has a modify method bound to it:

atom.modify(R.add(10)) // atom: 20

scan

scan(fn, seed, atom) -> computed atom

scan accepts an accumulator function and seed. Note that it fires immediately with any current value of its parent atom.

const nums = Atom(1)
const sum = scan((prev, next) => prev + next, 0, nums) // 1

for convenience, each atom has a scan method bound to it:

const clicks = Atom()
const clickCount = clicks.scan(R.add(1), -1) // 0

document.addEventListener('click', clicks)

merge

merge(atom1, atom2) -> computed atom

merge returns a computed atom from two sources. It has the value of whichever last updated. It will initialize as the value of the second atom passed in.

const atom1 = Atom(1)
const atom2 = Atom('a')
const merged = merge(atom1, atom2) // 'a'

atom1(1) // merged: 1

scanMerge

scanMerge(pairs, seed)

scanMerge accepts an array of arrays which contain an accumulator function and its atom, and a seed.
The resulting atom will update whenever one of its sources does according to the related accumulator function.

const s1 = Atom(0)
const s2 = Atom(0)
const s3 = Atom(0)

const scanmerged = scanMerge([
  [R.add, s1],
  [R.multiply, s2],
  [R.subtract, s2]
], 0) // 0

s1(2) // scanmerged: 2
s2(3) // scanmerged: 6
s3(8) // scanmerged: -2

log

log(values[])

log is a helper function intended for development use which logs values given to it. If they are an atom, it will print its current value.

const atom = Atom('bar')
log('foo', atom) // > foo bar

for convenience, each atom has a log method bound to it:

atom.log() // > 'bar'

atom.toJSON

For serialization, each atom has a toJSON method.

const value = Atom(123)
const serialized = JSON.stringify(value)
console.log(serialized) // > 123

(example borrowed from mithril)

atom.valueOf

For serialization, each atom has a valueOf method.

const value = Atom(123)
console.log('test ' + value) // > "test 123"

(example borrowed from mithril)

HALT

HALT is a string constant which has two purposes.

  1. To create "cold" Atoms, that is: computed atoms will not fire immediately (seeds will be used as intial values in the case of scan, scanMerge):
const atom = Atom(HALT)
const count = scan(R.add(1), 0, atom) // 0

document.addEventListener('click', atom)
// click -> 1
// click -> 2
  1. To be returned in a computed atoms compute function in order to prevent children from updating.
const a = Atom(1)
const b = map(val => val % 2 === 0 ? HALT : val)(a)

const c = Atom(2)

const d = combine(R.add, [b, c]) // 3

a(2) // b: HALT, d: 3
c(4) // d: 3
a(3) // b: 3, d: 7

Notes:

  1. Even one source having a HALT value will stop all updates for descendants.
  2. HALT should not be set via atom(HALT). This will throw an error.
  3. HALT is not intended to be used with lensed atoms.

end

end(atom)

end turns off an atom and its descendants. This will allow its data to be garbage collected.
For convenience, each atom has a bound end method

const atom = Atom(foo)

// later
atom.end()

ap

atom1.ap(atom2)

This is included as atom['fantasyland/ap'] to support the fantasyland Apply spec.
It is aliased as atom.ap for convenience.

const y = Atom(7)
const u = Atom(x => x * x)

y.ap(u) // 49

of

Atom.of(atom2)

This is included as Atom['fantasyland/of'] to support the fantasyland Applicative spec.
It is aliased as Atom.of for convenience.

const x = Atom.of(7) // same as Atom(7)

fantasyland

fantasyland is a set of specifications for algebraic structures to promote interop between libraries.

Acknowledgements

@isthmus/atom draws inspiration from mithril streams, flyd and calmm-js. It utilizes crry and @isthmus/optics as its dependencies.