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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@electric-sql/y-electric

v0.1.18

Published

YJS network provider for ElectricSQL

Readme

Y-Electric

A Yjs provider that enables real-time collaborative document editing using YJS, ElectricSQL and Postgres. It supports Awareness and can be used with any Yjs database providers. See a full example here.

How It Works

The typical flow for syncing shared documents using Yjs and Electric is the following:

  1. Developer exposes a shape proxy for authorizing shape requests
  2. Clients define a shape for syncing changes for a Y.Doc
  3. Developer exposes a [write API](#Handling Writes) for handling Yjs updates
  4. Vòila! Y-Electric automatically shares updates across all connected clients

Basic Setup

import * as Y from 'yjs'
import { ElectricProvider } from '@electric-sql/y-electric'
import { Awareness } from 'y-protocols/awareness'
import { parseToDecoder } from '@electric-sql/y-electric/utils'

const ydoc = new Y.Doc()
const awareness = new Awareness(ydoc)

new ElectricProvider({
  doc: ydoc,
  documentUpdates: {
    shape: {
      url: SHAPE_PROXY_URL,
      params: {
        table: `ydoc_update`,
        where: `room = '${room}'`,
      },
      parser: parseToDecoder,
    },
    sendUrl: DOC_UPDATES_SEND_URL,
    getUpdateFromRow: (row) => row.op,
  },
  awarenessUpdates: {
    shape: {
      url: SHAPE_PROXY_URL,
      params: {
        table: `ydoc_awareness`,
        where: `room = '${room}'`,
      },
      parser: parseToDecoder,
    },
    sendUrl: AWARENESS_UPDATES_SEND_URL,
    protocol: awareness,
    getUpdateFromRow: (row) => row.op,
  },
  resumeState: resumeStateProvider.load(),
})

Handling Writes

ElectricSQL is a read-path sync engine. This means that you bring your own API for handling document and awareness updates. See our sample server implementation here. It's very easy!

Document Updates

Y-Electric sends YJS document updates as binary data. You can directly save the body of the request as a bytea column into the database.

-- Schema definition
CREATE TABLE ydoc_updates(
    id uuid DEFAULT uuid_generate_v4() PRIMARY KEY,
    room text NOT NULL,
    op bytea NOT NULL
)
-- Save updates into individual rows
INSERT INTO ydoc_updates (room, op) VALUES ($1, $2)`

Awareness Updates

The awareness protocol implementation saves vector clock for each individual cliendId in separate rows:

Here is an example schema definition for ydoc_awareness:

-- Schema definitions
CREATE TABLE ydoc_awareness(
  client_id TEXT,
  room TEXT,
  op BYTEA NOT NULL,
  updated TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (client_id, room)
);
-- Save
INSERT INTO ydoc_awareness (room, client_id, op, updated) VALUES ($1, $2, $3, now())
         ON CONFLICT (client_id, room) DO UPDATE SET op = $3, updated = now()

It's recommended that you can garbage collect old client rows using a database trigger since the provider can't reliability detect when a client goes away:

CREATE OR REPLACE FUNCTION gc_awareness_timeouts()
RETURNS TRIGGER AS $$
BEGIN
    DELETE FROM ydoc_awareness
    WHERE updated < (CURRENT_TIMESTAMP - INTERVAL '30 seconds') AND room = NEW.room;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER gc_awareness_timeouts_trigger
AFTER INSERT OR UPDATE ON ydoc_awareness
FOR EACH ROW
EXECUTE FUNCTION gc_awareness_timeouts();

Schema mapping in the client

In the client, you need to pass a getUpdateFromRow to extract the column with the update binary. This allows Y-Electric to work with any backend schema.

Storage providers

Y-Electric work with existing database providers to store documents locally. When saving documents locally, we recommend using the ElectricStorageProvider to save a resume point for the shapes, otherwise the entire document will be retransmitted when a new client session starts.

The ElectricStorageProvider also keeps track of the document state vector to handle offline updates.