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

transactor-ts

v1.0.0

Published

Track client-side transactional changes to data — undo/redo, edge dedup, superimpose, and batched saves. Typed and immutable.

Downloads

128

Readme

transactor-ts

CI npm license types

Track client-side transactional changes to data — with undo/redo, edge dedup, superimpose, and batched or per-item saves. Fully typed, immutable, zero runtime dependencies.

Install

npm i transactor-ts

What & why

transactor-ts records an ordered sequence of local changes ("transactions") against sets of data, so you can operate on them individually or as a whole. Build up a batch of edits in the UI, undo/redo through them, superimpose them onto data you already have, then flush them to your backend in one or many calls. Each instance is isolated, so you can track several independent sets of changes at once.

It was purpose-built for the case where a server mutates data on save and the client needs to mimic that state locally before saving — hence the distinction between saveable and non-saveable transactions.

Quick start

import { create } from 'transactor-ts';

interface User {
  id: number;
  name: string;
}

const t = create<User>();

t.add(1, { id: 1, name: 'Ada' }); // update (the default)
t.add(1, { id: 1, name: 'Ada L.' }); // another change to the same record
t.add(2, { id: 2, name: 'Alan' }, { add: true });

// All transactions, in order:
t.get();

// Latest transaction per id (edge dedup):
t.getLatestEdge();

// Undo up to and including the last saveable transaction:
t.back();
// Redo it:
t.forward();

// Flush to a backend — one call per operation type:
await t.save(
  (updates) => api.put(updates), // put   — updates
  (creates) => api.post(creates), // post  — adds
  (deletes) => api.del(deletes), // del   — deletes
);

Grouping by id

The first argument to add(id, data) groups transactions. These three are seen as one record with three transactions:

t.add(1, { id: 1, value: 'a' });
t.add(1, { id: 2, value: 'b' });
t.add(1, { id: 3, value: 'c' });

while these are seen as three records with one transaction each:

t.add(1, { id: 1, value: 'a' });
t.add(2, { id: 1, value: 'b' });
t.add(3, { id: 1, value: 'c' });

superimpose

Apply the latest-edge transactions onto a copy of data you already hold. The input is never mutated.

const clientData = [{ id: 1, val: 'test' }];
t.add(1, { id: 1, val: 'updated' });

t.superimpose(clientData.map((cd) => ({ id: cd.id, data: cd })));
// => [{ id: 1, data: { id: 1, val: 'updated' } }]

A transaction added with { delete: true } removes the matching record from the result.

API reference

| Method | Description | | ----------------------------------- | ------------------------------------------------------------------------------------------------ | | create(options?) | Factory returning a Transactor instance. | | new Transactor(options?) | Same as create; the class is exported too. | | init(get?, set?) | Inject custom storage get/set. No args resets to a fresh in-memory store. | | add(id, data, options?) | Add a transaction. Clears the redo stack. | | asyncAdd(id, data, options?) | Add asynchronously, preserving submission order across concurrent calls. Returns a Promise. | | get() | All transactions { id, data, options } in order. | | getLatestEdge() | Latest transaction per unique id (edge dedup). | | back() | Undo up to and including the last saveable transaction. | | forward() | Redo up to and including the last undone saveable transaction. | | superimpose(clientData) | Apply latest-edge transactions onto a copy of clientData. Never mutates the input. | | save(put?, post?, del?) | Batch: sort saveable transactions into add/update/delete arrays; call each handler once, if any. | | saveLatestEdge(put?, post?, del?) | As save, over the latest-edge transactions. | | saveEach(put?, post?, del?) | Call the matching handler once per saveable transaction, in order. | | saveEachEdge(put?, post?, del?) | As saveEach, over the latest-edge transactions. | | clear() | Remove all transactions for this instance and clear the redo stack. | | destroy() | Remove this instance's data from the backing store. |

Options

add(id, data, options) accepts:

| Option | Default | Meaning | | -------- | ------------------------------ | --------------------------------------------------------------------------- | | add | — | Treat as a create; routed to the post handler on save. | | update | true (when no type is given) | Treat as an update; routed to the put handler. | | delete | — | Treat as a delete; routed to del, and drives superimpose. | | save | true (unless set to false) | Whether the transaction is sent to handlers and forms undo/redo boundaries. |

All save handlers may return a promise; every save method returns a Promise that resolves once all handler promises resolve. If a transaction needs a handler that was not supplied, a clear error is thrown.

Undo/redo & edge semantics

  • back() / forward() move up to and including the last saveable transaction, so a saveable change plus any trailing non-saveable changes are undone/redone as a unit.
  • Adding a new transaction clears the redo stack.
  • Edge dedup (getLatestEdge, saveLatestEdge, saveEachEdge, superimpose) keeps the latest transaction per id, with two nuances: add-then-update stays an add, and add-then-delete cancels out entirely.

Lineage & migration (from sequence-transactor)

transactor-ts is the first-class TypeScript successor to the original sequence-transactor (last published as 2.1.2). The public API is unchanged — the same create/init/Transactor, the same methods and option semantics — now with generics over your data type, bundled type declarations, ESM output, strict typing, and full test coverage. To migrate, install transactor-ts and update the import specifier; no code changes are required.

Behavioural fix in this rewrite: superimpose no longer corrupts data when a delete transaction targets an id that is not present in the client data (the original removed the wrong element). Internals are now immutable — stored arrays and caller-provided arrays/objects are never mutated.

License

BSD-3-Clause © 2018–2026 Daniel Cassil