Socket IO Connector for Yjs (Inspired by y-websocket)

Overview

Welcome to y-socket.io 👋

npm version License: MIT

Socket IO Connector for Yjs (Inspired by y-websocket)

Y-socket.io is a YJS document synchronization implementation over the websockets protocol inspired by y-websocket, but implemented with Socket.io. So for YJS and Socket.io enthusiasts it could be a simple and scalable alternative.

Like y-websocket, this is a solid option if you want to manage authentication and authorization for editing YJS documents at a single point.

Y-socket.io features:

  • Configuration and customization of the server side.
  • Easy implementation of your authentication.
  • Ability to enable document persistence with LevelDB.
  • Ability to add custom callbacks at different points in a document's life cycle as shown below.
  • Cross-tab communication, i.e. when opening the same document in more than one tab, changes are also transmitted via cross-tab communication (broadcast channel and localstorage as an alternative).
  • Awareness information exchange.

IMPORTANT: Y-socket.io does not have HTTP callbacks implemented, because as mentioned above, you can implement custom callbacks (in the document update callback you could implement your HTTP callback logic).

Installation

To install you can run this command

npm i y-socket.io

Usage

Server side

You can run a basic server (you can review the source code here) using:

HOST=localhost PORT=1234 npx y-socket-io

(by default the server listens on localhost:1234)

Although this server is functional, I recommend reviewing the example to extend it, add your own logic (such as authentication, document callbacks, etc.), and adapt it to your use case.

Adding y-socket.io to your project

Y-socket.io is very easy to add an new or existent project with socket.io. You just need to pass the socket.io server instance as shown below:

import { YSocketIO } from 'y-socket.io/dist/server'

// Create the YSocketIO instance
// NOTE: This uses the socket namespaces that match the regular expression /^\/yjs\|.*$/ 
//       (for example: 'ws://localhost:1234/yjs|my-document-room'), make sure that when using namespaces
//       for other logic, these do not match the regular expression, this could cause unwanted problems.
// TIP: You can export a new instance from another file to manage as singleton and access documents from all app.
const ysocketio = new YSocketIO(io)
// Execute initialize method
ysocketio.initialize()

You can also pass an object with the settings you need for your implementation. You can see the available options in the Server API

Client side

You can use SocketIOProvider on your client side like this:

import * as Y from 'yjs'
import { SocketIOProvider } from 'y-socket.io'

const doc = new Y.Doc()
const provider = new SocketIOProvider('ws://localhost:1234', 'room-name', doc);

provider.on('status', ({ status }: { status: string }) => {
  console.log(status) // Logs "connected" or "disconnected"
})

As with the server-side instance, in the SocketIOProvider you can also pass a number of parameters to configure your implementation. You can see the available options in the Client API

(Here you can review an example in ReactJS)

Run examples

This repository has an example that you can run in your machine and play with it. To run the examples, you must clone or download this repository.

Running the server side example

npm run example:server

Running the client side example

npm run example:client

This example is implemented on ReactJS and it was built with Vite.

API

Server API

import { YSocketIO } from 'y-socket.io/dist/server'
ysocketio = new YSocketIO(io: SocketIO.Server [, configuration: YSocketIOConfiguration])
Create an instance to configure the server side of your deployment. The following configuration defaults can be overridden.
configuration = {
  // Optionally, set here the authentication validation callback (by default server accepts all connections)
  // (for example: if client sent a token, you can get token from auth object 
  authenticate: undefined, // Example: (auth) => auth.token === 'valid-token')
  // Optionally, enable LevelDB persistence by setting the directory where you want to store the database (by default the LevelDB persistence is disabled)
  levelPersistenceDir: undefined,
  // Enable/Disable garbage collection (by default the garbage collection is enabled)
  gcEnabled: true,
}
ysocketio.documents: Map< string, Document >
The list of documents
ysocketio.on('document-loaded', function(doc: Document))
Add an event listener for the "document-loaded" event that is fired when a document is created (if persistence is enabled, it is executed after loading the document from the database)loaded.
ysocketio.on('document-update', function(doc: Document, update: Uint8Array))
Add an event listener for the "document-update" event that is fired when a document is updated.
ysocketio.on('awareness-update', function(doc: Document, update: Uint8Array))
Add an event listener for the "awareness update" event that fires when a document's awareness is updated.
ysocketio.on('document-destroy', function(doc: Document))
Add an event listener for the "document-destroy" event that fires before a document is destroyed.
ysocketio.on('all-document-connections-closed', function(doc: Document))
Add an event listener for the "all-document-connections-closed" event that fires when all clients of a document are disconnected.

Client API

import { SocketIOProvider } from 'y-socket.io'
provider = new SocketIOProvider( serverUrl: string, room: string, doc: Y.Doc, [, configuration: ProviderConfiguration]
Create a new instance of SocketIOProvider, which syncs the document through the websockets server from the URL, with all clients connected to the document. You can configure your implementation by modifying the following default configuration.
configuration = {
  // Enable/Disable garbage collection (by default the garbage collection is enabled)
  gcEnabled: true,
  // Enable/Disable auto connect when the instance is created (by default the auto connection is enabled). Set this to "false" if you want to connect manually using provider.connect()
  autoConnect = true,
  // Specify an existing Awareness instance - see https://github.com/yjs/y-protocols
  awareness = new AwarenessProtocol.Awareness(doc),
  // Specify a number of milliseconds greater than 0 to resynchronize the document (by default is -1)
  resyncInterval = -1,
  // Disable broadcast channel synchronization (by default the broadcast channel synchronization is enabled)
  disableBc = false,
  // Specify the authentication data to send to the server on handshake
  auth = {} // Example: { token: 'valid-token' }
}
ysocketio.roomName: string
The name of the document's room.
ysocketio.doc: Y.Doc
The YJS document.
ysocketio.awareness: AwarenessProtocol.Awareness
The YJS document.
ysocketio.bcconnected: boolean
BroadcastChannel connection state.
ysocketio.synced: boolean
Synchronization state.
ysocketio.connect()
Connect to the websockets server.
ysocketio.disconnect()
Disconnect from the websockets server.
ysocketio.on('sync', function(isSynced: boolean))
Add an event listener for the "sync" event that is fired when the client received content from the server.
ysocketio.on('status', function({ status: 'disconnected' | 'connecting' | 'connected' }))
Add an event listener for the "status" event that fires when the client receives connection status updates.
ysocketio.on('connection-close', function(event: Socket.DisconnectReason, provider: SocketIOProvider))
Add an event listener for the "connection-close" event that fires when the websockets connection is closed.
ysocketio.on('connection-error', function(event: Socket.DisconnectReason, provider: SocketIOProvider))
Add an event listener for the "connection-error" event that is fired when an error has occurred while trying to connect to the websockets server (for example, the client is not authenticated).

👦 Author

Iván Topp

🤝 Contributing

Contributions, issues and feature requests are welcome!

Show your support

Give a ⭐️ if this project helped you!

📝 License

Copyright © 2022 Iván Topp.
This project is MIT licensed.


This README was generated with ❤️ by readme-md-generator

You might also like...

A Node.js client & server implementation of the WAMP-like RPC-over-websocket system defined in the OCPP-J protcols.

A Node.js client & server implementation of the WAMP-like RPC-over-websocket system defined in the OCPP-J protcols.

OCPP-RPC A client & server implementation of the WAMP-like RPC-over-websocket system defined in the OCPP-J protcols (e.g. OCPP1.6J and OCPP2.0.1J). Re

Dec 30, 2022

A social network app cloned from Instagram built with Next.Js, Socket.IO and a lots of other new stuff.

A social network app cloned from Instagram built with Next.Js, Socket.IO and a lots of other new stuff.

Instagram Noob ⚡ A social network app cloned from Instagram built with Next.Js, Socket.IO and a lots of other new stuff. Live Demo: https://instagram-

Oct 19, 2022

Node.js implementation of the Socket SDK client

SYNOPSIS A Node.js adapter for the Socket SDK DESCRIPTION Socket SDK uses a simple uri-based protocol for brokering messages between the render proces

Dec 28, 2022

TikTokLive-Widget: A socket client/server program that exposes a widget with alerts (such as gifts, followers ...) for a specific user streaming on Tik Tok Live platform

TikTokLive-Widget: A socket client/server program that exposes a widget with alerts (such as gifts, followers ...) for a specific user streaming on Tik Tok Live platform

TikTokLive-Widget: A socket client/server program that exposes a widget with alerts (such as gifts, followers ...) for a specific user streaming on Tik Tok Live platform

Dec 3, 2022

Simple google docs thing in Remix using socket.io.

This is just a simple google docs thing, me playing around with Remix and sockets. TODO: from socket.io to yjs sockets, support CRDT. Installation Aft

Apr 19, 2022

Performant WebSocket Server & Client

Socketich 📨 Performant WebSocket server and persistent client powered by uWebSockets.js and PersistentWebSocket Install npm i @geut/socketich Usage S

Aug 15, 2022

a chatt app build using node , express and socket IO , it has many rooms

RealTime-chatt-app you can view the app here : https://bit.ly/3zce4ON a chatt app build using node , express and socket IO . used to public chatt room

Aug 31, 2022

A complete application tutorial to show how to implement the Web Socket protocol using only Node.js builtin modules

A complete application tutorial to show how to implement the Web Socket protocol using only Node.js builtin modules

Web Socket application using only Node.js built-in modules About Welcome, this repo is part of my youtube video about Building a complete application

Dec 19, 2022

This application provides the CDK project and a frontend that allows you to build a serverless chat application based on API Gateway's WebSocket-based API feature.

This application provides the CDK project and a frontend that allows you to build a serverless chat application based on API Gateway's WebSocket-based API feature.

Serverless chat application using ApiGateway Websockets This project lets you provision a ready-to-use fully serverless real-time chat application usi

Jan 3, 2023
Comments
  • Feature request: More permissive authentication configuration

    Feature request: More permissive authentication configuration

    A feature request I have would be for the configuration for authenticate to pass in more than the token. I would like to restrict who can access a particular document, but as it is, I am unable to access the URL from the socket.io handshake. Preferably, the entire handshake would be passed instead of just the auth.

    I can see a workaround by manually registering with the dynamic namespace after initialization, but that's inelegant.

    opened by nighoggDatatype 2
  • Cannot sync with multi tabs by built-in broadcast channel

    Cannot sync with multi tabs by built-in broadcast channel

    Thanks for the y-socket.io.

    But when I open 2 Tabs in Chrome and do something, It isn't synced with each other. According to y-websocket, I think when bc connected, y-socket.io missing a step-2 bc.publish.

    https://github.com/yjs/y-websocket/blob/master/src/y-websocket.js#L430 https://github.com/ivan-topp/y-socket.io/blob/main/src/client/provider.ts#L397

    after adding step-2 publish, I works like y-websocket & y-webrtc

    opened by IndexXuan 1
  • How to properly implement debounce

    How to properly implement debounce

    Using your both of your example in for the client and server I'm able to setup a simple debounce to update the yMap.

    Here's the snippet, because the rest of the code is exactly the same as your client example. Server side code is not modified.

    const  handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    	setInput(e.target.value);
    };
    
    // Input Element
    <input
    	value={input}
    	onChange={handleChange}
    	// onChange={(e) =>
    	// doc.getMap("data").set("input", e.target.value ?? "")
    	// }
    />
    

    Debounce on every input change using lodash-debounce

    useEffect(() => {
    	debouncedInput(input);
    	return () => {
    		debouncedInput.cancel();
    	};
    }, [input]);
    
    const  debouncedInput = debounce((input) => {
    	doc?.getMap("data").set("input", input);
    }, 1000);
    

    But I think that cause race condition:

    1. Client A types something
    2. Client B receive the update from client A, but since client B itself receives an update, the debounce function is triggered
    3. If client B debounce happened when client A is still typing something, client A text get reverted to client B version

    I hope I'm clear enough, if not I can provide an example repo.

    Some guidance or insight on this is very helpful

    opened by 0x67 1
  • Is there any way to restrict `update` propagation

    Is there any way to restrict `update` propagation

    In my setup I have read/write clients. Both may access document and its changes, but updates form 'read' clients should not be applied on document and propagated.

    Is there a way to achieve this now? Or may it be a feature request 😉?

    opened by mk45 4
Releases(v1.1.0)
Owner
Iván Topp Sandoval
Iván Topp Sandoval
Grupprojekt för kurserna 'Javascript med Ramverk' och 'Agil Utveckling'

JavaScript-med-Ramverk-Laboration-3 Grupprojektet för kurserna Javascript med Ramverk och Agil Utveckling. Utvecklingsguide För information om hur utv

Svante Jonsson IT-Högskolan 3 May 18, 2022
Hemsida för personer i Sverige som kan och vill erbjuda boende till människor på flykt

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: np

null 4 May 3, 2022
Kurs-repo för kursen Webbserver och Databaser

Webbserver och databaser This repository is meant for CME students to access exercises and codealongs that happen throughout the course. I hope you wi

null 14 Jan 3, 2023
Use Matrix as a backend for local-first applications with the Matrix-CRDT Yjs provider.

Matrix CRDT Matrix-CRDT enables you to use Matrix as a backend for distributed, real-time collaborative web applications that sync automatically. The

Yousef 604 Jan 7, 2023
Syncronize a YJS document to/from a plain old javascript object

y-pojo Syncronize a YJS document to/from a plain old javascript object This library enables multiple users to share state in the form of a Plain ol' J

null 17 Nov 12, 2022
I built a full-stack project using Node, Express, Mongo, webRTC, socket.io, and of course React JS. This project is inspired by the awesome Clubhouse 😊

codershouse-mern - This Project is Under Development. Below are some of the implemented interface and the remaining features will be updated in future

Bishal Das 35 Nov 18, 2022
Cardano DApp Wallet Connector

Cardano DApp Wallet Connector This project was bootstrapped with Create React App. React JS demo In the project directory, you can run: npm start run

null 105 Dec 18, 2022
Nepkit Connector – Build Dashboards & Admin Tools in minutes

Nepkit Connector Use Nepkit Connector to connect Nepkit Dashboard Builder to the database on your server or local machine. Supported databases: Postgr

Nepkit 3 Jun 3, 2022
A wallet connector for the Cosmos ⚛️

cosmos-kit A wallet adapter for react with mobile WalletConnect support for the Cosmos ecosystem. Getting Started @cosmos-kit/react A wallet adapter f

Cosmology 81 Dec 20, 2022
To keep online organized data and be able to add listeners in certain paths by socket.io and http clients

The purpose of this project is to create a state machine server to keep organized data and be able to add listeners with socket.io in specific paths and in addition to providing a possible form of messages between socket.io clients.

Manga 3 Mar 19, 2022