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

horizonstate

v0.0.11

Published

A transactional state management library for JS with temporal ordering and automatic rollback

Readme

horizonstate

transactional state management library

horizonstate is a transactional state management library for JS with temporal ordering and automatic rollback

🚧👷 Warning Proceed at your own risk. This is an in-development library, which is another way of saying that it will change quite a lot.

Overview

Horizonstate is a transactional state management library for JS that is designed to provide simple primitives for managing state without concern for response ordering or manual rollback of optimistic updates. It does this by saving a snapshot of the state before each transaction and recording mutations to the state via updator functions. These mutations can be replayed or dropped when prior transactions complete, ensuring that each update to the state is applied in the correct order. Transactions run concurrently and non-blocking by default, but temporal ordering results in eventual consistency and automatic conflict resolution.

Usage

Define a model:

import { Model } from "horizonstate";

let model = new Model(
  {
    foo: "bar",
    pending: false,
  },
  // optional config:
  {
    timeout: 10000 // the minimum amount of time a transaction has to complete. Default: 10000ms
  }
);

Define transactions:

let setFoo = model.addTransaction(async (args: string, { optimisticUpdate, update, applyOptimisticUpdates }) => {
  // set state optimistically, which will be rolled back once this transaction completes
  optimisticUpdate(draft => {
    draft.foo = args;
    draft.pending = true;
  });
  let result = await someApiCall(args);
  // apply updates to the model
  update(draft => draft.foo = result.foo);
  // Or call applyOptimisticUpdates() if you're sure the optimistic updates will match the server state
});

Run the transaction:

setFoo.run("baz");

Subscribe to updates:

model.subscribe((state) => {
  console.log(state.foo);
});
// with a selector
model.select(state => state.foo, console.log);

Transactions can await prior pending transactions:

model.addTransaction(async (args: any, { optimisticUpdate, update, pendingTransactions }) => {
  await pendingTransactions;
  // run transaction...
});

Transactions can read state from the model. This state is the result of previous transactions (pending and resolved), but does not reflect state updates from later transactions (temporal ordering is preserved). Keep in mind if prior transactions complete or update the model after state() is called, this state could be stale. It's generally advisable to await previous transactions if you need to send state from the model to the server.

model.addTransaction(async (args: any, { optimisticUpdate, update, state }) => {
  let s = state();
  // continue with transaction...
});

You can also drop optimistic updates during your transaction, which will automatically roll them back:

model.addTransaction(async (args: any, { optimisticUpdate, update, dropOptimisticUpdates }) => {
  optimisticUpdate(draft => {
    draft.foo = args;
    draft.pending = true;
  });
  let result = await someApiCall(args);
  if(result.someCond) {
    // drop all prior optimistic updates
    dropOptimisticUpdates();
  }
  optimisticUpdate(draft => draft.error = true);
  let newResult = await someOtherApiCall(args);
  update(draft => draft.foo = newResult.foo);
});

Since optimistic updates are automatically rolled back, handling errors is simply a matter of applying a different update if applicable.

model.addTransaction(async (args: any, { optimisticUpdate, update }) => {
  optimisticUpdate(draft => draft.foo = args)
  let result = await someApiCall(args);
  if(result.success) {
    update(draft => draft.foo = result.foo);
  } else {
    update(draft => draft.error = true);
  }
});

React

Horizonstate exports a React hook for managing models inside of components.

import { model } from "./model.ts"
import { useModel } from 'horizonstate/react';

let Component = () => {
  let state = useModel(model);
  return <div>{state.foo}</div>;
};

useModel takes an additional argument for the model to use as a state selector to reduce rerenders.

let Component = () => {
  let bar_baz = useModel(model, (state) => state.bar.baz);
  return <div>{bar_baz}</div>;
};

License

Made with 💛

Published under MIT License.