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 🙏

© 2024 – Pkg Stats / Ryan Hefner

mobus

v1.4.2

Published

Turn state into an RxJS state machine

Downloads

45

Readme

Mo'Bus

Supercharge your state management with events, and predictable pure function state machines.

Adapt any existing class, MobX store, or Javascript Object to be event driven. Great for 3D projects in ThreeJS.

Installation

npm i mobus

Why?

You are looking for:

  • pure function state machines, to avoid the pain from side effects
  • event driven architecture, streaming solutions, or functional reactive programming
  • how to leverage the power of RxJS (EGs in React with MobX)

Example

https://github.com/jtmckay/mobus/tree/main/examples/preact

Control state with commands

import { definedEntity, stateMachineFactory } from 'mobus';

export const counterStore = { count: 0 }

export const { commandFactory, subscribe } = stateMachineFactory('counter', {
  storeSingle: counterStore
});
subscribe();

export const increment = commandFactory<void>({
  eventHandler: (entity) => {
    const counter = definedEntity(entity)
    counter.count++;
    return counter
  }
})

If you need it in a render cycle (EG: React with MobX)

import { definedEntity, stateMachineFactory } from 'mobus';
import { observable, runInAction } from 'mobx';

export const counterStore = observable({ count: 0 })

export const { commandFactory, subscribe } = stateMachineFactory('counter', {
  wrapper: runInAction,
  storeSingle: counterStore
});

Then render it in React, Preact etc.

import { increment, counterStore } from '../../domain/counter/counter.bus';
import { observer } from 'mobx-react-lite';

const Counter = observer(() => (
  <div onClick={() => increment()}>
    Counter: {counterStore.count}
	</div>
));

Optimistic updates

This example uses a more advanced store. EG: Map<string, Pedometer>.

import { stateMachineFactory } from 'mobus';
import { pedometerStore } from './pedometer.store';

export const { commandFactory, subscribe } = stateMachineFactory(ENTITY, { wrapper: runInAction, store: pedometerStore });
subscribe();

const syncHeartRate = commandFactory<WithID & { rate: number }>({
  eventHandler: (entity, event) => {
    // EG: optional eventHandler immediately updates the state
    const pedometer = definedEntity(entity);
    pedometer.heartRate = event.payload.rate;
    return pedometer;
  },
  asyncEventHandler: async (entity, event) => {
    // EG: optional asyncEventHandler will update the state again (after hitting server etc.)
    const pedometer = definedEntity(entity);
    await new Promise((resolve) => setTimeout(resolve, 2000));
    runInAction(() => {
      pedometer.heartRate = 100;
    });
    return pedometer;
  },
});

Parallel handlers

By default, all stores will only handle one event at a time, and will queue any events that are triggered in the meantime.

import { stateMachineFactory } from 'mobus';
import { pedometerStore } from './pedometer.store';

export const { commandFactory, subscribe } = stateMachineFactory(ENTITY, {
  wrapper: runInAction,
  store: pedometerStore,
  parallel: true, // Setting parallel to true will allow multiple async handlers to fire simultaneously
});

Testing

Incredibly simple testing when compared to most RxJS implementations, because it exposes a promise that can be awaited.

describe('when incrementing the counter with a delay', () => {
  beforeEach(async () => {
    await delayedIncrement()
  });

  it('increases the count to 1', () => {
    expect(counterStore.count).toBe(1)
  });
});

Future examples

With events driving the system, it is trivial to develop advanced features for your product such as:

  • stream interactions over websockets for live collaboration
  • analytics for user engagement or business changelog

Flow

graph TD
    subgraph RxJS Events
        Event1["Command 1"]
        Event2["Command 2"]
        Event3["Command 3"]
        Event4["Command 4"]
    end
    
    subgraph Bus
        Event4 --> Commands
        Event3 --> Commands
        Event2 --> Commands
        Event1 --> Commands
        Commands --> Get
        Database -->Get
        Get --> Function["Pure Function Bussiness Logic Handlers"]
        Function --> Set
        Set --> Database["MobX Store"]
    end

    Database --> React["React (any UI)"]

    Function --> EntityStream["RxJS Stream"]

Developing & Publishing

yarn build

cd package

npm login --auth-type=legacy

npm publish