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

@monkin/history

v0.1.4

Published

An immutable library for document history management with first-class undo/redo support

Downloads

448

Readme

@monkin/history

CI

An immutable library for document history management with first-class undo/redo support.

While any document state can be reconstructed by replaying operations from the beginning, @monkin/history optimizes this process by combining an operation timeline with state snapshots.

Core Concept

Current State = Latest Snapshot + Subsequent Operations

[!TIP] Built for Synchronization

Since history is immutable, you can save a reference at any moment and later diff it with the newest version. This makes it easy to implement background persistence or synchronization logic.


Features

  • Immutable by Design: Every operation returns a new instance of OperationList or SnapshotList.
  • Linked-List Timeline: Operations are modeled as a sequence where each entry points to its predecessor, allowing efficient undo/redo traversal.
  • Flexible ID Generation: Use numeric IDs, UUIDs, bigints, or any custom identifier.
  • Decoupled Snapshots: Manage state snapshots independently for maximum architectural flexibility.

Installation

npm install @monkin/history

Core Components

OperationList

Tracks document operations and manages the undo/redo pointer.

[!IMPORTANT] The IdGenerator must always return an identifier strictly greater than the provided maxId. This is required for correct history traversal and ordering.

Basic Usage

import { OperationList } from "@monkin/history";

// Initialize with an ID generator
const generateId: OperationList.IdGenerator<number | bigint> = (maxId) => (maxId ?? 0n) + 1n;
let history = OperationList.empty<number | bigint, string>(generateId);

// Record operations
history = history.add("Add Header");
history = history.add("Change Background");

// Undo and Redo
if (history.canUndo) history = history.undo(); // Moves pointer back
if (history.canRedo) history = history.redo(); // Moves pointer forward

// Reconstruct state from the pointer branch
for (const entry of history) {
    console.log(entry.operation);
}

API Highlights

  • history.pointer: The ID of the current operation.
  • history.setPointer(id): Moves the history pointer to a specific operation id.
  • history.isUndone(id): Checks if an operation exists but is currently undone.
  • history.ageOf(id): Distance between current state and a given entry.
  • history.get(id): Retrieves an entry reachable from the current state.
  • history.entry(id): Retrieves an entry by ID, including undone ones.
  • history.entries(): Generator yielding all recorded entries.
  • [Symbol.iterator]: Iterates over the pointer branch (skipping undone operations).
  • history.upload(items): Bulk-upload entries, useful for partial history loading.
  • OperationList.diff(before, after): Static method comparing two history lists to find added, removed, or changed operations.

SnapshotList

An immutable collection for managing state snapshots indexed by identifiers.

Basic Usage

import { SnapshotList } from "@monkin/history";

let snapshots = SnapshotList.empty<number | bigint, string>();

// Add and retrieve snapshots
snapshots = snapshots.add(1, "Initial Content");
const item = snapshots.get(1);
console.log(item?.snapshot); // "Initial Content"

// Cleanup
snapshots = snapshots.remove(1);

API Highlights

  • snapshots.add(id, snapshot): Adds a new snapshot.
  • snapshots.addAll(items): Bulk-add snapshots from an iterable.
  • snapshots.remove(id): Removes a snapshot by its ID.
  • snapshots.get(id): Retrieves a snapshot item by ID.
  • snapshots.filter(predicate): Filter snapshots into a new list.
  • [Symbol.iterator]: Iterates over all items in sorted order.
  • SnapshotList.diff(before, after): Static method comparing two snapshot lists to find added, removed, or changed items.

Design Philosophy

Why Decoupled?

OperationList and SnapshotList are intentionally separate primitives. This approach avoids imposing a specific state structure and enables complex compositions:

  • Modular Architecture: Most apps use one list of each, but some require more. An animation editor might use one OperationList for global actions but 30 separate SnapshotList instances—one per frame.
  • Lightweight: The library provides the mathematical primitives; you can easily wrap them in a higher-level "History Manager" tailored to your application's needs.

Historical Note

OperationList was originally named History. It was renamed to keep the History name available for consumers who want to implement their own domain-specific manager by combining these primitives.


Performance

The library is optimized for the latest history items. While accessing very deep history might be slower, it typically remains efficient enough for standard use cases. Use snapshots strategically to keep the number of operations to replay minimal.


Development

  • npm run dev: Start Vite development server.
  • npm run build: Build the project (TypeScript & Vite).
  • npm run test: Run tests using Vitest.
  • npm run lint: Lint the project.
  • npm run format: Format the project.
  • npm run prepublishOnly: Build, lint, and test before publish.

License

MIT