Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: yjs/y-websocket
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v1.5.4
Choose a base ref
...
head repository: yjs/y-websocket
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v2.0.0
Choose a head ref
  • 3 commits
  • 7 files changed
  • 1 contributor

Commits on Mar 15, 2024

  1. Add y-redis backend

    dmonad committed Mar 15, 2024
    Copy the full SHA
    89fb1e3 View commit details

Commits on Mar 20, 2024

  1. make this a proper esm module

    dmonad committed Mar 20, 2024
    Copy the full SHA
    c3d14cf View commit details
  2. 2.0.0

    dmonad committed Mar 20, 2024
    Copy the full SHA
    244889f View commit details
Showing with 80 additions and 44 deletions.
  1. +22 −4 README.md
  2. +5 −4 bin/{callback.js → callback.cjs}
  3. +4 −3 bin/{server.js → server.cjs}
  4. +16 −12 bin/{utils.js → utils.cjs}
  5. +23 −13 package-lock.json
  6. +9 −7 package.json
  7. +1 −1 tsconfig.json
26 changes: 22 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -2,12 +2,30 @@
# y-websocket :tophat:
> WebSocket Provider for Yjs
The Websocket Provider implements a classical client server model. Clients connect to a single endpoint over Websocket. The server distributes awareness information and document updates among clients.
The Websocket Provider implements a classical client server model. Clients
connect to a single endpoint over Websocket. The server distributes awareness
information and document updates among clients.

This repository contains a simple in-memory backend that can persist to
databases, but it can't be scaled easily. The
[y-redis](https://github.com/yjs/y-redis/) repository contains an alternative
backend that is scalable, provides auth*, and can persist to different backends.

The Websocket Provider is a solid choice if you want a central source that
handles authentication and authorization. Websockets also send header
information and cookies, so you can use existing authentication mechanisms with
this server.

* Supports cross-tab communication. When you open the same document in the same
browser, changes on the document are exchanged via cross-tab communication
([Broadcast
Channel](https://developer.mozilla.org/en-US/docs/Web/API/Broadcast_Channel_API)
and
[localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage)
as fallback).
* Supports exchange of awareness information (e.g. cursors).

The Websocket Provider is a solid choice if you want a central source that handles authentication and authorization. Websockets also send header information and cookies, so you can use existing authentication mechanisms with this server.

* Supports cross-tab communication. When you open the same document in the same browser, changes on the document are exchanged via cross-tab communication ([Broadcast Channel](https://developer.mozilla.org/en-US/docs/Web/API/Broadcast_Channel_API) and [localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) as fallback).
* Supports exchange of awareness information (e.g. cursors).

## Quick Start

9 changes: 5 additions & 4 deletions bin/callback.js → bin/callback.cjs
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
const http = require('http')
const number = require('lib0/number')

const CALLBACK_URL = process.env.CALLBACK_URL ? new URL(process.env.CALLBACK_URL) : null
const CALLBACK_TIMEOUT = process.env.CALLBACK_TIMEOUT || 5000
const CALLBACK_TIMEOUT = number.parseInt(process.env.CALLBACK_TIMEOUT || '5000')
const CALLBACK_OBJECTS = process.env.CALLBACK_OBJECTS ? JSON.parse(process.env.CALLBACK_OBJECTS) : {}

exports.isCallbackSet = !!CALLBACK_URL

/**
* @param {Uint8Array} update
* @param {any} origin
* @param {WSSharedDoc} doc
* @param {import('./utils.cjs').WSSharedDoc} doc
*/
exports.callbackHandler = (update, origin, doc) => {
const room = doc.name
@@ -25,7 +26,7 @@ exports.callbackHandler = (update, origin, doc) => {
content: getContent(sharedObjectName, sharedObjectType, doc).toJSON()
}
})
callbackRequest(CALLBACK_URL, CALLBACK_TIMEOUT, dataToSend)
CALLBACK_URL && callbackRequest(CALLBACK_URL, CALLBACK_TIMEOUT, dataToSend)
}

/**
@@ -62,7 +63,7 @@ const callbackRequest = (url, timeout, data) => {
/**
* @param {string} objName
* @param {string} objType
* @param {WSSharedDoc} doc
* @param {import('./utils.cjs').WSSharedDoc} doc
*/
const getContent = (objName, objType, doc) => {
switch (objType) {
7 changes: 4 additions & 3 deletions bin/server.js → bin/server.cjs
Original file line number Diff line number Diff line change
@@ -5,13 +5,14 @@
*/
const WebSocket = require('ws')
const http = require('http')
const number = require('lib0/number')
const wss = new WebSocket.Server({ noServer: true })
const setupWSConnection = require('./utils.js').setupWSConnection
const setupWSConnection = require('./utils.cjs').setupWSConnection

const host = process.env.HOST || 'localhost'
const port = process.env.PORT || 1234
const port = number.parseInt(process.env.PORT || '1234')

const server = http.createServer((request, response) => {
const server = http.createServer((_request, response) => {
response.writeHead(200, { 'Content-Type': 'text/plain' })
response.end('okay')
})
28 changes: 16 additions & 12 deletions bin/utils.js → bin/utils.cjs
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
const Y = require('yjs')
const syncProtocol = require('y-protocols/dist/sync.cjs')
const awarenessProtocol = require('y-protocols/dist/awareness.cjs')
const syncProtocol = require('y-protocols/sync')
const awarenessProtocol = require('y-protocols/awareness')

const encoding = require('lib0/dist/encoding.cjs')
const decoding = require('lib0/dist/decoding.cjs')
const map = require('lib0/dist/map.cjs')
const encoding = require('lib0/encoding')
const decoding = require('lib0/decoding')
const map = require('lib0/map')

const debounce = require('lodash.debounce')

const callbackHandler = require('./callback.js').callbackHandler
const isCallbackSet = require('./callback.js').isCallbackSet
const callbackHandler = require('./callback.cjs').callbackHandler
const isCallbackSet = require('./callback.cjs').isCallbackSet

const CALLBACK_DEBOUNCE_WAIT = parseInt(process.env.CALLBACK_DEBOUNCE_WAIT) || 2000
const CALLBACK_DEBOUNCE_MAXWAIT = parseInt(process.env.CALLBACK_DEBOUNCE_MAXWAIT) || 10000
const CALLBACK_DEBOUNCE_WAIT = parseInt(process.env.CALLBACK_DEBOUNCE_WAIT || '2000')
const CALLBACK_DEBOUNCE_MAXWAIT = parseInt(process.env.CALLBACK_DEBOUNCE_MAXWAIT || '10000')

const wsReadyStateConnecting = 0
const wsReadyStateOpen = 1
@@ -73,10 +73,11 @@ const messageAwareness = 1

/**
* @param {Uint8Array} update
* @param {any} origin
* @param {any} _origin
* @param {WSSharedDoc} doc
* @param {any} _tr
*/
const updateHandler = (update, origin, doc) => {
const updateHandler = (update, _origin, doc, _tr) => {
const encoder = encoding.createEncoder()
encoding.writeVarUint(encoder, messageSync)
syncProtocol.writeUpdate(encoder, update)
@@ -124,7 +125,7 @@ class WSSharedDoc extends Y.Doc {
})
}
this.awareness.on('update', awarenessChangeHandler)
this.on('update', updateHandler)
this.on('update', /** @type {any} */ (updateHandler))
if (isCallbackSet) {
this.on('update', debounce(
callbackHandler,
@@ -135,6 +136,8 @@ class WSSharedDoc extends Y.Doc {
}
}

exports.WSSharedDoc = WSSharedDoc

/**
* Gets a Y.Doc by name, whether in memory or on disk
*
@@ -183,6 +186,7 @@ const messageListener = (conn, doc, message) => {
}
} catch (err) {
console.error(err)
// @ts-ignore
doc.emit('error', [err])
}
}
36 changes: 23 additions & 13 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 9 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
{
"name": "y-websocket",
"version": "1.5.4",
"version": "2.0.0",
"description": "Websockets provider for Yjs",
"main": "./dist/y-websocket.cjs",
"module": "./src/y-websocket.js",
"types": "./dist/src/y-websocket.d.ts",
"type": "module",
"sideEffects": false,
"funding": {
"type": "GitHub Sponsors ❤",
"url": "https://github.com/sponsors/dmonad"
},
"scripts": {
"start": "node ./bin/server.js",
"start": "node ./bin/server.cjs",
"dist": "rm -rf dist && rollup -c && tsc",
"lint": "standard && tsc",
"test": "npm run lint",
"preversion": "npm run lint && npm run dist && test -e dist/src/y-websocket.d.ts && test -e dist/y-websocket.cjs"
},
"bin": {
"y-websocket-server": "./bin/server.js",
"y-websocket": "./bin/server.js"
"y-websocket-server": "./bin/server.cjs",
"y-websocket": "./bin/server.cjs"
},
"files": [
"dist/*",
@@ -28,13 +29,14 @@
],
"exports": {
"./package.json": "./package.json",
"./bin/utils": "./bin/utils.js",
"./bin/callback": "./bin/callback.js",
"./bin/utils": "./bin/utils.cjs",
"./bin/callback": "./bin/callback.cjs",
".": {
"module": "./src/y-websocket.js",
"import": "./src/y-websocket.js",
"require": "./dist/y-websocket.cjs",
"types": "./dist/src/y-websocket.d.ts"
"types": "./dist/src/y-websocket.d.ts",
"default": "./dist/y-websocket.js"
}
},
"repository": {
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
@@ -58,5 +58,5 @@
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
// "maxNodeModuleJsDepth": 5
},
"include": ["./src/y-websocket.js"]
"include": ["./src/y-websocket.js", "./bin/**/*"]
}