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

@drifting-ink/ledger

v0.2.0

Published

WebRTC + SQL + CRDT replication library (powered by cr-sqlite)

Readme

Ledger

WebRTC + SQLite + CRDT replication. Peer-to-peer syncing SQL database for the browser. Built on cr-sqlite for conflict-free replication.

Install

npm install @drifting-ink/ledger

Usage

import { Ledger } from '@drifting-ink/ledger';

// Create instance
const db = new Ledger({ dbName: 'my-app' });

// Initialize database
await db.init();

// Create tables and enable sync
await db.exec(`
  CREATE TABLE IF NOT EXISTS notes (
    id TEXT PRIMARY KEY NOT NULL,
    content TEXT,
    created_at TEXT DEFAULT (datetime('now'))
  )
`);
await db.enableSync('notes');

// Connect to peers (same token = same room)
await db.connect('wss://your-signaling-server.com', 'room-token');

// Use SQL normally - changes sync automatically
await db.exec('INSERT INTO notes (id, content) VALUES (?, ?)', [crypto.randomUUID(), 'Hello']);
await db.exec('UPDATE notes SET content = ? WHERE id = ?', ['Updated', id]);
await db.exec('DELETE FROM notes WHERE id = ?', [id]);

// Query
const result = await db.exec('SELECT * FROM notes');
console.log(result.rows);

// Events
db.on('sync', (count, peerId) => console.log(`Synced ${count} changes from ${peerId}`));
db.on('peer-join', (peerId) => console.log(`Peer joined: ${peerId}`));
db.on('peer-leave', (peerId) => console.log(`Peer left: ${peerId}`));
db.on('connected', () => console.log('Connected to signaling'));
db.on('disconnected', () => console.log('Disconnected'));

// Cleanup
await db.close();

API

new Ledger(config?)

| Option | Type | Description | |--------|------|-------------| | dbName | string | Database name (default: 'ledger-default') | | signalingUrl | string | WebSocket signaling server URL | | token | string | Room token (peers with same token sync together) | | iceServers | RTCIceServer[] | Custom ICE servers |

Methods

| Method | Description | |--------|-------------| | init() | Initialize the database | | connect(url?, token?) | Connect to signaling server and start P2P sync | | disconnect() | Disconnect from peers | | close() | Close database and cleanup | | exec(sql, params?) | Execute SQL query, returns { columns, rows } | | enableSync(tableName) | Enable CRDT sync on a table (call after CREATE TABLE) | | getNodeId() | Get this peer's unique ID | | getVersion() | Get current database version | | getPeers() | Get list of connected peer IDs | | isConnected() | Check if connected to signaling |

Events

| Event | Callback | Description | |-------|----------|-------------| | sync | (count, peerId) | Received changes from peer | | peer-join | (peerId) | Peer discovered | | peer-ready | (peerId) | Peer connection established | | peer-leave | (peerId) | Peer disconnected | | connected | () | Connected to signaling | | disconnected | () | Disconnected from signaling | | reconnecting | (attempt) | Attempting to reconnect | | reconnected | () | Successfully reconnected |

Running the Demo

# Install dependencies
npm install

# Start signaling server
npm run server

# Build and serve demo
npm run build:demo
npx serve demo

Open two browser tabs to http://localhost:3000/#room-token (same hash = same room).

Signaling Server

The included signaling server handles WebRTC connection setup:

npm run server  # Runs on ws://localhost:8081

For production, deploy server/signaling.js or use any WebSocket server that implements the signaling protocol.

How It Works

  • cr-sqlite: SQLite compiled to WASM with built-in CRDT support
  • Automatic change tracking: All mutations to synced tables are captured
  • Cell-level conflict resolution: Last-write-wins based on logical clocks
  • WebRTC DataChannels: Direct peer-to-peer data transfer
  • Signaling server: Only used for initial peer discovery (no data passes through it)

Schema Requirements

  • Tables must have PRIMARY KEY NOT NULL
  • Call enableSync(tableName) after creating each table you want to sync
  • Schema is not synced - all peers must have identical table definitions