Why Pulsify
Pulsify is a CRDT framework for TypeScript. A pulse is a typed collaborative document that looks like a plain object, merges edits from any number of peers without conflicts, and persists as rows in PostgreSQL or SQLite.
Fully typed
Yjs and Loro expose untyped maps, arrays and texts. You write the schema in your head, and every reader re-validates. In Pulsify the schema is code: it produces the TypeScript type, the runtime checks and the binary layout.
const Todo = definePulse("Todo", object({
title: "string",
done: "boolean",
tags: array("string"),
})).materialize();
const todo = createPulse(Todo, { title: "Ship", done: false, tags: [] });
todo.done = true; // typed, validated, synced
todo.done = "yes"; // TypeScript error, and rejected at runtime
Built-in migrations
Shipped documents outlive your schema. Pulsify lets you append a migration step and upgrades each document lazily, when it is loaded. There is no batch job, and a client running old code is detected instead of silently corrupting data. Migrations →
Materialization
CRDTs never really delete a field. Materialization gives the rest of your codebase one clean, current type built from the latest version, while the stored shape stays compatible with every peer. Materialization →
PostgreSQL and SQLite
Every change is an immutable row keyed by document, actor and clock. The server answers “what is this client missing?” with one indexed query and never decodes or loads a document. Appends are one round trip. Databases →
Fast
Local writes keep native patches and are encoded once, only when a peer needs them. On a 1,000-item document, Pulsify creates and encodes in about 60% of Yjs's time and 7% of Loro's, and single edits are several times faster. Full benchmark →
Everything else
- Rich text with ProseMirror and Lexical bindings.
- Arrays with move, trees with reparenting, tuples, optionals, unions.
- Offline edits that merge on reconnection.
- Browsers, Bun, Node 22+, Deno 2 and Cloudflare Workers.
- A versioned binary protocol, also implemented in Rust.