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

@coaction/history

v4.0.0

Published

A undo/redo middleware for Coaction

Readme

@coaction/history

Node CI npm license

English documentation · 中文文档

Patch-based undo/redo middleware for Coaction, powered by Travels.

Whole-store commits hand the core-generated patch pair directly to the controlled journal in Travels 2.1 and 2.2; partialized histories derive one patch pair over the tracked projection. Retained history scales with recorded changes instead of whole-store snapshots. Legacy snapshots are reserved for values such as cyclic graphs, Date, sparse arrays, symbol keys, and custom prototypes.

Cycle support covers complete value replacement and snapshot restoration. History does not extend the native recipe contract to creating cycles from draft references or editing inside cyclic drafts. Construct a new graph from ordinary objects and replace its field or root. Replay preserves the topology of each recorded state, including intentional splits of acyclic shared nodes.

Installation

Install it with pnpm:

pnpm add coaction @coaction/history

Usage

import { create } from 'coaction';
import { history, type HistoryApi } from '@coaction/history';

type Counter = {
  count: number;
  increment: () => void;
};

const store = create<Counter>(
  (set) => ({
    count: 0,
    increment() {
      set((draft) => {
        draft.count += 1;
      });
    }
  }),
  {
    middlewares: [history()]
  }
);

store.getState().increment();
const timeline = (store as typeof store & { history: HistoryApi<Counter> })
  .history;

timeline.undo();
timeline.redo();

API

| Method | Description | | -------------- | -------------------------------------------------------------------------------------------------- | | undo() | Moves back one entry and returns whether the cursor moved. | | redo() | Moves forward one entry and returns whether the cursor moved. | | canUndo() | Reports whether an undo entry is available. | | canRedo() | Reports whether a redo entry is available. | | clear() | Rebases the timeline at the current state. | | getPast() | Reconstructs and returns detached past snapshots for compatibility. | | getFuture() | Reconstructs and returns detached future snapshots for compatibility. | | getPatches() | Returns the Travels-backed patch groups and cursor, or undefined in snapshot compatibility mode. |

getPast() and getFuture() materialize snapshots lazily. Keep them out of render and update hot paths when only canUndo() or canRedo() is needed.

getPatches() exposes the compact durable shape without reconstructing every state:

const patches = timeline.getPatches();

if (patches) {
  const payload = {
    state: store.getPureState(),
    ...patches
  };
  localStorage.setItem('counter-history', JSON.stringify(payload));
}

The returned patch arrays are detached from the internal timeline. The shape is { patches, inversePatches, position }, where both patch fields contain one group per history entry.

Partial history

Use partialize to track a JSON-compatible projection while leaving other state untouched:

history<Counter>({
  limit: 50,
  partialize: (state) => ({ count: state.count })
});

Travels stores patches over the projected state. Undo and redo apply only those projected fields through Coaction's normal middleware and subscription pipeline. Untracked state can contain runtime-only or cyclic values without forcing the tracked projection into snapshot mode.

Compatibility behavior

  • JSON-compatible whole-store and partialized histories use Travels patches.
  • travels@^2.1.0 is required. Compatibility is tested against the minimum supported 2.1.0 release and the current 2.2.0 release. Every patch timeline uses its controlled journal and direct recordPatches() handoff; no Travels 2.0 replay fallback is bundled.
  • If tracked state becomes non-JSON-compatible, the existing timeline is materialized once and recording continues with legacy snapshots.
  • In snapshot compatibility mode, getPatches() returns undefined while the other history methods keep their previous behavior.
  • Install history on the authoritative main store. Client mirror stores reject local history because it would diverge from their authority.
  • limit defaults to 100 and must be a non-negative integer.

Documentation

You can find the documentation here.