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

simple-crdts

Package Overview
Dependencies
Maintainers
1
Versions
3
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install
Package was removed
Sorry, it seems this package was removed from the registry

simple-crdts

Tiny CRDT helpers: Last-Writer-Wins register, PN-Counter, and TextRGA you can merge across offline/online nodes.

latest
npmnpm
Version
1.2.0
Version published
Maintainers
1
Created
Source

simple-crdts

Lightweight, zero-dependency CRDT primitives you can drop into offline-first or multi-node apps. Ships with a Last-Writer-Wins register, a PN-Counter, and a Text RGA (text CRDT) plus ready-to-use JSON serialization and TypeScript typings.

Features

  • Minimal API: LWW, PNCounter, and TextRGA with deterministic merges
  • Offline-friendly: serialize to JSON, rehydrate with fromJSON
  • Text-aware: TextRGA supports insert/delete with stable callbacks
  • ESM first: tree-shakeable and side-effect free
  • Typed: bundled .d.ts for painless TS/JS IntelliSense

Install

npm install simple-crdts

Quick start

PN-Counter

import { PNCounter } from "simple-crdts";

const alice = new PNCounter("alice");
alice.increment().increment(); // +2 on alice

const bob = new PNCounter("bob");
bob.increment().decrement(); // +1 then -1 on bob

// Merge state from both replicas
const merged = PNCounter.fromJSON(alice.toJSON());
merged.merge(bob);

merged.getCount(); // -> 1

Last-Writer-Wins register

import { LWW } from "simple-crdts";

// value, nodeId, counter (for near-simultaneous writes)
const draft = new LWW("draft", "node-a", 1);
const published = new LWW("published", "node-b", 2);

// Resolves in place; timestamp, counter, then nodeId break ties
draft.competition(published);

draft.value; // -> "published"

TextRGA (text CRDT)

import { TextRGA } from "simple-crdts";

const a = new TextRGA("node-a");
a.insertAt(0, "H");
a.insertAt(1, "i");

// Another replica
const b = TextRGA.fromJSON(a.toJSON());
b.insertAt(2, "!");

// Merge and materialize
a.merge(b);
a.getText(); // -> "Hi!"

// Handle remote op streams
const idx = a.applyRemoteInsert({ id: "peer:42", char: "?" });
// Later...
a.applyRemoteDelete({ id: "peer:42" });

Persist and rehydrate

import { PNCounter, LWW, TextRGA } from "simple-crdts";

const counter = new PNCounter("cache-node").increment();
localStorage.setItem("counter", JSON.stringify(counter.toJSON()));

const restoredCounter = PNCounter.fromJSON(
  JSON.parse(localStorage.getItem("counter") || "{}")
);

const title = new LWW("Hello", "node-1", 4);
const payload = JSON.stringify(title.toJSON()); // send over the wire
const mergedTitle = LWW.fromJSON(JSON.parse(payload));

const doc = new TextRGA("writer");
doc.insertAt(0, "A");
localStorage.setItem("doc", JSON.stringify(doc.toJSON()));
const restoredDoc = TextRGA.fromJSON(
  JSON.parse(localStorage.getItem("doc") || "{}")
);

API in 30 seconds

  • PNCounter(localNodeId, increments?, decrements?) - create a replica.

  • increment() / decrement() - mutate the local register.

  • merge(other) - element-wise max merge; returns this.

  • getCount() - returns sum(increments) - sum(decrements).

  • toJSON() / PNCounter.fromJSON(json) - serialize/rehydrate.

  • LWW(value, nodeId?, counter?, timestamp?) - create a register.

  • competition(other) - merge winner into this using timestamp, then counter, then nodeId.

  • toJSON() / LWW.fromJSON(json) - serialize/rehydrate.

  • Constants: LWW.STALE_THRESHOLD_MS (30 min) and LWW.COUNTER_WINDOW_MS (30 s) tune the merge windows.

  • TextRGA(nodeId, localCounter?, entries?, order?, onInsert?, onDelete?) - create a text replica; callbacks fire on local inserts/deletes.

  • insertAt(index, char) / deleteAt(index) - mutate the visible text.

  • applyRemoteInsert({id, char}) / applyRemoteDelete({id}) - apply explicit remote ops idempotently (useful when you stream operations instead of state).

  • merge(other) - union entries and deterministic order; returns this.

  • getText() - materialize current visible string.

  • toJSON() / TextRGA.fromJSON(json) - serialize/rehydrate (callbacks are not serialized).

Notes

  • Published as an ES module; use dynamic import() for CommonJS if needed.
  • Pure data classes; safe to store in IndexedDB, localStorage, or send over the network.
  • Deterministic merges mean replicas converge as long as everyone exchanges state.

License

MIT

Keywords

crdt

FAQs

Package last updated on 09 Dec 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