New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

horizonstate

Package Overview
Dependencies
Maintainers
1
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

horizonstate

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

latest
npmnpm
Version
0.0.11
Version published
Weekly downloads
11
1000%
Maintainers
1
Weekly downloads
 
Created
Source

horizonstate

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.

FAQs

Package last updated on 14 Aug 2025

Did you know?

Socket

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.

Install

Related posts