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 🙏

© 2025 – Pkg Stats / Ryan Hefner

aims-js

v1.3.4

Published

AIMS Is Managing State

Readme

AIMS

A tiny, stream-free* riff on @foxdonut's brilliant and elegant Meiosis pattern.

* That's right, no streams were harmed in the making of this package. But of course you can bring some of your own if you want.

What (and more importantly WHY) is it?

AIMS Is Managing State

I love Meiosis. I also love a nice godref. So here we are: AIMS uses the kernel of the Meiosis pattern, shallowly, to create both infrastructure and methodology for managing application state, without requiring users to be self-loathing or good at wrestling*. Oh and it's also just over 750 bytes with zero dependencies.

* Meiosis doesn't have these requirements either, but many other state management approaches do. You know who you are.

Installation

npm i aims-js

Properties

These are passed at instantiation to aims:

| | type | description | default | |------|---------------------|-----------------------------------------------------------------------------|----------| | a | function | Accumulator: (x, y) => ({}) | merge* | | i | object | Initial state object | {} | | m | function or array | Mutators/Measurements: (state, patch?) => ({}) (or an array of these) | [] | | s | boolean | Safemode | false |

* merge is a slightly modified port of mergerino by @fuzetsu.

Methods

These are attached to the returned aims instance:

| | usage | description | |-------------|---------------------------------------------------|-----------------------------------------------------------------------------------------------------| | get | const foo = state.get() | returns the current state | | patch* ** | state.patch({ bar: 'baz' }) | uses the a function to apply the passed-in patch,which in turn generates a whole new state |

* In AIMS parlance, the word "patch" has dual meanings: as a verb, it's the method we use to "patch" our state with new values; as a noun, it's the object which provides those values. Try not to use both in the same sentence :) "Patrick, please patch our state with this patch."

** In safemode, patch is not a property of state, and instead is passed as the second argument to m.

Usage

Accumulator, Initialization, Mutators/Measurements, Safemode

Begin here:

import aims from 'aims-js' 
const state = aims()

Now state is ready to use. Give it some properties:

state.patch({ 
    name: 'Jack', 
    height: 'Short' 
})

Ok, now let's access our state:

const { name, height } = state.get()
console.log(name, height) // Jack Short

Accumulator function: a

Any function with the signature (previous_state, incoming_patch) => ({}) (e.g. Object.assign) will do:

// low-rent, shallow immutability
const state = aims({ 
  a: (prev, incoming) => Object.assign({}, prev, incoming) 
})

Initialization: i

Of course, in our first example, we could've set name, height at initialization:

const i = {
    name: 'Mike',
    height: 'Average'
}

const state = aims({ i })
const { name, height } = state.get()
console.log(name, height) // Mike Average

Mutators: m

Mutators are easier to illustrate than to explain:

const m = state => ({
    //  MUTATION FUNCTIONS: 
    //  apply patches to state
    
    setFirstName: firstName => {
        state.patch({ firstName })
    },
    setLastName: lastName => {
        state.patch({ lastName })
    },
  
    // MEASUREMENT FUNCTIONS: 
    // side effects, computations, and whatever 
    // else you want to be able to access via 
    // `state.myMeasurement(...)`
    fullName: () => {
      const { firstName, lastName } = state.get()
      return `${firstName} ${{lastName}}`
    },
    
})

const state = aims({ m: mutators })

/* ...somwhere in your code... */

onclick: e => { state.setFoo(e.target.textContent) }

Each mutator is a closure which accepts state as its parameter, and returns an object with state in scope. aims attaches the properties of each returned object to state, so calls can be made via state.myMethod(...).

You may have multiple, discrete sets of mutators, e.g. SocketMutators and RESTMutators.

const state = aims({ m: [SocketMutators, RESTMutators] })

In this case, it may be advisable to set namespaces, since aims is determinedly tiny and won't detect collisions for you.

// create the "Socket" namespace
const SocketMutators = state => ({
    Socket: {
        setFoo: foo => {
            state.patch({ foo })
        }
    }
})
// ...and the "REST" namespace 
const RESTMutators = state => ({
    REST: {...}
})

const state = aims({m: [SocketMutators, RESTMutators]})

// destructure state — NOT state.get() 
const { Socket } = state
Socket.setFoo('jack')

console.log(state.get()) // { foo: 'jack' }

Safemode: s

In larger codebases, it may be desirable to restrict mutations to actions only, eliminating occurences of state.patch({...}) within application views and elsewhere. Safemode achieves this by omitting state.patch and instead passing the patching function as a second parameter to Mutators, e.g.

const state = aims({
  m: (state, patch) => ({
    setFoo: foo => {
      patch({ foo })
    }
  })
  s: true,
})

Patch inspection

Sometimes there are imperatives associated with particular state changes. TodoMVC is a great example — every data change must be persisted, as must every filter change, which must change the URL for routing purposes. Rather than having several mutators each kicking off persistence and routing, we can use a custom accumulator to inspect incoming patches and respond accordingly, all in one place. A Mithril implementation might look like this:

import aims from 'aims-js'
const a = (prev, incoming) => {
    // update the route on filter changes  
    if (incoming.filter) m.route.set('/' + incoming.filter)
    
    // update localStorage with new state
    const new_state = Object.assign(prev, incoming)
    localStorage.setItem('todoapp-aims-m', JSON.stringify(new_state))
    
    return new_state
}

const state = aims({ a })

Integrating with view libraries*

* Unless you're using an auto-redrawing library or framework like Mithril.js, in which case you can skip this step.

To render your view, pass a function to the second argument of aims, which in turn takes state as its own argument, and render your App within the function. So for e.g. React:

import { createRoot } from 'ReactDOM'

const root = createRoot(document.getElementById('app'))

// Here the reference returned from `aims` is 
// unneeded, since we pass `state` to the function 
// provided, so we can just call `aims` directly
aims({}, state => {
  root.render(<App state={state} />)
})

Examples