UNPKG

y-socket.io

Version:

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

1 lines 20.4 kB
{"version":3,"sources":["../src/server/document.ts","../src/server/y-socket-io.ts"],"sourcesContent":["import * as Y from 'yjs'\r\nimport { Namespace, Socket } from 'socket.io'\r\nimport * as AwarenessProtocol from 'y-protocols/awareness'\r\nimport { AwarenessChange } from '../types'\r\n\r\nconst gcEnabled = process.env.GC !== 'false' && process.env.GC !== '0'\r\n\r\n/**\r\n * Document callbacks. Here you can set:\r\n * - onUpdate: Set a callback that will be triggered when the document is updated\r\n * - onChangeAwareness: Set a callback that will be triggered when the awareness is updated\r\n * - onDestroy: Set a callback that will be triggered when the document is destroyed\r\n */\r\nexport interface Callbacks {\r\n /**\r\n * Set a callback that will be triggered when the document is updated\r\n */\r\n onUpdate?: (doc: Document, docUpdate: Uint8Array) => void\r\n /**\r\n * Set a callback that will be triggered when the awareness is updated\r\n */\r\n onChangeAwareness?: (doc: Document, awarenessUpdate: Uint8Array) => void\r\n /**\r\n * Set a callback that will be triggered when the document is destroyed\r\n */\r\n onDestroy?: (doc: Document) => Promise<void>\r\n}\r\n\r\n/**\r\n * YSocketIO document\r\n */\r\nexport class Document extends Y.Doc {\r\n /**\r\n * The document name\r\n * @type {string}\r\n */\r\n public name: string\r\n /**\r\n * The socket connection\r\n * @type {Namespace}\r\n * @private\r\n */\r\n private readonly namespace: Namespace\r\n /**\r\n * The document awareness\r\n * @type {Awareness}\r\n */\r\n public awareness: AwarenessProtocol.Awareness\r\n /**\r\n * The document callbacks\r\n * @type {Callbacks}\r\n * @private\r\n */\r\n private readonly callbacks?: Callbacks\r\n\r\n /**\r\n * Document constructor.\r\n * @constructor\r\n * @param {string} name Name for the document\r\n * @param {Namespace} namespace The namespace connection\r\n * @param {Callbacks} callbacks The document callbacks\r\n */\r\n constructor (name: string, namespace: Namespace, callbacks?: Callbacks) {\r\n super({ gc: gcEnabled })\r\n this.name = name\r\n this.namespace = namespace\r\n this.awareness = new AwarenessProtocol.Awareness(this)\r\n this.awareness.setLocalState(null)\r\n this.callbacks = callbacks\r\n\r\n this.awareness.on('update', this.onUpdateAwareness)\r\n\r\n this.on('update', this.onUpdateDoc)\r\n }\r\n\r\n /**\r\n * Handles the document's update and emit eht changes to clients.\r\n * @type {(update: Uint8Array) => void}\r\n * @param {Uint8Array} update\r\n * @private\r\n */\r\n private readonly onUpdateDoc = (update: Uint8Array): void => {\r\n if ((this.callbacks?.onUpdate) != null) {\r\n try {\r\n this.callbacks.onUpdate(this, update)\r\n } catch (error) {\r\n console.warn(error)\r\n }\r\n }\r\n this.namespace.emit('sync-update', update)\r\n }\r\n\r\n /**\r\n * Handles the awareness update and emit the changes to clients.\r\n * @type {({ added, updated, removed }: { added: number[], updated: number[], removed: number[] }, _socket: Socket | null) => void}\r\n * @param {AwarenessChange} awarenessChange\r\n * @param {Socket | null} _socket\r\n * @private\r\n */\r\n private readonly onUpdateAwareness = ({ added, updated, removed }: AwarenessChange, _socket: Socket | null): void => {\r\n const changedClients = added.concat(updated, removed)\r\n const update = AwarenessProtocol.encodeAwarenessUpdate(this.awareness, changedClients)\r\n if ((this.callbacks?.onChangeAwareness) != null) {\r\n try {\r\n this.callbacks.onChangeAwareness(this, update)\r\n } catch (error) {\r\n console.warn(error)\r\n }\r\n }\r\n this.namespace.emit('awareness-update', update)\r\n }\r\n\r\n /**\r\n * Destroy the document and remove the listeners.\r\n * @type {() => Promise<void>}\r\n */\r\n public async destroy (): Promise<void> {\r\n if ((this.callbacks?.onDestroy) != null) {\r\n try {\r\n await this.callbacks.onDestroy(this)\r\n } catch (error) {\r\n console.warn(error)\r\n }\r\n }\r\n this.awareness.off('update', this.onUpdateAwareness)\r\n this.off('update', this.onUpdateDoc)\r\n this.namespace.disconnectSockets()\r\n super.destroy()\r\n }\r\n}\r\n","import * as Y from 'yjs'\r\nimport { Namespace, Server, Socket } from 'socket.io'\r\nimport * as AwarenessProtocol from 'y-protocols/awareness'\r\nimport { LeveldbPersistence } from 'y-leveldb'\r\nimport { Document } from './document'\r\nimport { Observable } from 'lib0/observable'\r\n\r\n/**\r\n * Level db persistence object\r\n */\r\nexport interface Persistence {\r\n bindState: (docName: string, ydoc: Document) => void\r\n writeState: (docName: string, ydoc: Document) => Promise<any>\r\n provider: any\r\n}\r\n\r\n/**\r\n * YSocketIO instance cofiguration. Here you can configure:\r\n * - gcEnabled: Enable/Disable garbage collection (default: gc=true)\r\n * - levelPersistenceDir: The directory path where the persistent Level database will be stored\r\n * - authenticate: The callback to authenticate the client connection\r\n */\r\nexport interface YSocketIOConfiguration {\r\n /**\r\n * Enable/Disable garbage collection (default: gc=true)\r\n */\r\n gcEnabled?: boolean\r\n /**\r\n * The directory path where the persistent Level database will be stored\r\n */\r\n levelPersistenceDir?: string\r\n /**\r\n * Callback to authenticate the client connection.\r\n *\r\n * It can be a promise and if it returns true, the connection is allowed; otherwise, if it returns false, the connection is rejected.\r\n * @param handshake Provided from the handshake attribute of the socket io\r\n */\r\n authenticate?: (handshake: { [key: string]: any }) => Promise<boolean> | boolean\r\n}\r\n\r\n/**\r\n * YSocketIO class. This handles document synchronization.\r\n */\r\nexport class YSocketIO extends Observable<string> {\r\n /**\r\n * @type {Map<string, Document>}\r\n */\r\n private readonly _documents: Map<string, Document> = new Map<string, Document>()\r\n /**\r\n * @type {Server}\r\n */\r\n private readonly io: Server\r\n /**\r\n * @type {string | undefined | null}\r\n */\r\n private readonly _levelPersistenceDir: string | undefined | null = null\r\n /**\r\n * @type {Persistence | null}\r\n */\r\n private persistence: Persistence | null = null\r\n /**\r\n * @type {YSocketIOConfiguration}\r\n */\r\n private readonly configuration?: YSocketIOConfiguration\r\n /**\r\n * @type {Namespace | null}\r\n */\r\n public nsp: Namespace | null = null\r\n /**\r\n * YSocketIO constructor.\r\n * @constructor\r\n * @param {Server} io Server instance from Socket IO\r\n * @param {YSocketIOConfiguration} configuration (Optional) The YSocketIO configuration\r\n */\r\n constructor (io: Server, configuration?: YSocketIOConfiguration) {\r\n super()\r\n\r\n this.io = io\r\n\r\n this._levelPersistenceDir = configuration?.levelPersistenceDir ?? process.env.YPERSISTENCE\r\n if (this._levelPersistenceDir != null) this.initLevelDB(this._levelPersistenceDir)\r\n\r\n this.configuration = configuration\r\n }\r\n\r\n /**\r\n * YSocketIO initialization.\r\n *\r\n * This method set ups a dynamic namespace manager for namespaces that match with the regular expression `/^\\/yjs\\|.*$/`\r\n * and adds the connection authentication middleware to the dynamics namespaces.\r\n *\r\n * It also starts socket connection listeners.\r\n * @type {() => void}\r\n */\r\n public initialize (): void {\r\n this.nsp = this.io.of(/^\\/yjs\\|.*$/)\r\n\r\n this.nsp.use(async (socket, next) => {\r\n if ((this.configuration?.authenticate) == null) return next()\r\n if (await this.configuration.authenticate(socket.handshake)) return next()\r\n else return next(new Error('Unauthorized'))\r\n })\r\n\r\n this.nsp.on('connection', async (socket) => {\r\n const namespace = socket.nsp.name.replace(/\\/yjs\\|/, '')\r\n\r\n const doc = await this.initDocument(namespace, socket.nsp, this.configuration?.gcEnabled)\r\n\r\n this.initSyncListeners(socket, doc)\r\n this.initAwarenessListeners(socket, doc)\r\n\r\n this.initSocketListeners(socket, doc)\r\n\r\n this.startSynchronization(socket, doc)\r\n })\r\n }\r\n\r\n /**\r\n * The document map's getter. If you want to delete a document externally, make sure you don't delete\r\n * the document directly from the map, instead use the \"destroy\" method of the document you want to delete,\r\n * this way when you destroy the document you are also closing any existing connection on the document.\r\n * @type {Map<string, Document>}\r\n */\r\n public get documents (): Map<string, Document> {\r\n return this._documents\r\n }\r\n\r\n /**\r\n * This method creates a yjs document if it doesn't exist in the document map. If the document exists, get the map document.\r\n *\r\n * - If document is created:\r\n * - Binds the document to LevelDB if LevelDB persistence is enabled.\r\n * - Adds the new document to the documents map.\r\n * - Emit the `document-loaded` event\r\n * @private\r\n * @param {string} name The name for the document\r\n * @param {Namespace} namespace The namespace of the document\r\n * @param {boolean} gc Enable/Disable garbage collection (default: gc=true)\r\n * @returns {Promise<Document>} The document\r\n */\r\n private async initDocument (name: string, namespace: Namespace, gc: boolean = true): Promise<Document> {\r\n const doc = this._documents.get(name) ?? (new Document(name, namespace, {\r\n onUpdate: (doc, update) => this.emit('document-update', [doc, update]),\r\n onChangeAwareness: (doc, update) => this.emit('awareness-update', [doc, update]),\r\n onDestroy: async (doc) => {\r\n this._documents.delete(doc.name)\r\n this.emit('document-destroy', [doc])\r\n }\r\n }))\r\n doc.gc = gc\r\n if (!this._documents.has(name)) {\r\n if (this.persistence != null) await this.persistence.bindState(name, doc)\r\n this._documents.set(name, doc)\r\n this.emit('document-loaded', [doc])\r\n }\r\n return doc\r\n }\r\n\r\n /**\r\n * This method sets persistence if enabled.\r\n * @private\r\n * @param {string} levelPersistenceDir The directory path where the persistent Level database is stored\r\n */\r\n private initLevelDB (levelPersistenceDir: string): void {\r\n const ldb = new LeveldbPersistence(levelPersistenceDir)\r\n this.persistence = {\r\n provider: ldb,\r\n bindState: async (docName: string, ydoc: Document) => {\r\n const persistedYdoc = await ldb.getYDoc(docName)\r\n const newUpdates = Y.encodeStateAsUpdate(ydoc)\r\n await ldb.storeUpdate(docName, newUpdates)\r\n Y.applyUpdate(ydoc, Y.encodeStateAsUpdate(persistedYdoc))\r\n ydoc.on('update', async (update: Uint8Array) => await ldb.storeUpdate(docName, update))\r\n },\r\n writeState: async (_docName: string, _ydoc: Document) => { }\r\n }\r\n }\r\n\r\n /**\r\n * This function initializes the socket event listeners to synchronize document changes.\r\n *\r\n * The synchronization protocol is as follows:\r\n * - A client emits the sync step one event (`sync-step-1`) which sends the document as a state vector\r\n * and the sync step two callback as an acknowledgment according to the socket io acknowledgments.\r\n * - When the server receives the `sync-step-1` event, it executes the `syncStep2` acknowledgment callback and sends\r\n * the difference between the received state vector and the local document (this difference is called an update).\r\n * - The second step of the sync is to apply the update sent in the `syncStep2` callback parameters from the server\r\n * to the document on the client side.\r\n * - There is another event (`sync-update`) that is emitted from the client, which sends an update for the document,\r\n * and when the server receives this event, it applies the received update to the local document.\r\n * - When an update is applied to a document, it will fire the document's \"update\" event, which\r\n * sends the update to clients connected to the document's namespace.\r\n * @private\r\n * @type {(socket: Socket, doc: Document) => void}\r\n * @param {Socket} socket The socket connection\r\n * @param {Document} doc The document\r\n */\r\n private readonly initSyncListeners = (socket: Socket, doc: Document): void => {\r\n socket.on('sync-step-1', (stateVector: Uint8Array, syncStep2: (update: Uint8Array) => void) => {\r\n syncStep2(Y.encodeStateAsUpdate(doc, new Uint8Array(stateVector)))\r\n })\r\n\r\n socket.on('sync-update', (update: Uint8Array) => {\r\n Y.applyUpdate(doc, update, null)\r\n })\r\n }\r\n\r\n /**\r\n * This function initializes socket event listeners to synchronize awareness changes.\r\n *\r\n * The awareness protocol is as follows:\r\n * - A client emits the `awareness-update` event by sending the awareness update.\r\n * - The server receives that event and applies the received update to the local awareness.\r\n * - When an update is applied to awareness, the awareness \"update\" event will fire, which\r\n * sends the update to clients connected to the document namespace.\r\n * @private\r\n * @type {(socket: Socket, doc: Document) => void}\r\n * @param {Socket} socket The socket connection\r\n * @param {Document} doc The document\r\n */\r\n private readonly initAwarenessListeners = (socket: Socket, doc: Document): void => {\r\n socket.on('awareness-update', (update: ArrayBuffer) => {\r\n AwarenessProtocol.applyAwarenessUpdate(doc.awareness, new Uint8Array(update), socket)\r\n })\r\n }\r\n\r\n /**\r\n * This function initializes socket event listeners for general purposes.\r\n *\r\n * When a client has been disconnected, check the clients connected to the document namespace,\r\n * if no connection remains, emit the `all-document-connections-closed` event\r\n * parameters and if LevelDB persistence is enabled, persist the document in LevelDB and destroys it.\r\n * @private\r\n * @type {(socket: Socket, doc: Document) => void}\r\n * @param {Socket} socket The socket connection\r\n * @param {Document} doc The document\r\n */\r\n private readonly initSocketListeners = (socket: Socket, doc: Document): void => {\r\n socket.on('disconnect', async () => {\r\n if ((await socket.nsp.allSockets()).size === 0) {\r\n this.emit('all-document-connections-closed', [doc])\r\n if (this.persistence != null) {\r\n await this.persistence.writeState(doc.name, doc)\r\n await doc.destroy()\r\n }\r\n }\r\n })\r\n }\r\n\r\n /**\r\n * This function is called when a client connects and it emit the `sync-step-1` and `awareness-update`\r\n * events to the client to start the sync.\r\n * @private\r\n * @type {(socket: Socket, doc: Document) => void}\r\n * @param {Socket} socket The socket connection\r\n * @param {Document} doc The document\r\n */\r\n private readonly startSynchronization = (socket: Socket, doc: Document): void => {\r\n socket.emit('sync-step-1', Y.encodeStateVector(doc), (update: Uint8Array) => {\r\n Y.applyUpdate(doc, new Uint8Array(update), this)\r\n })\r\n socket.emit('awareness-update', AwarenessProtocol.encodeAwarenessUpdate(doc.awareness, Array.from(doc.awareness.getStates().keys())))\r\n }\r\n}\r\n"],"mappings":"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","names":["Y","AwarenessProtocol","gcEnabled","Document","name","namespace","callbacks","update","_a","error","added","updated","removed","_socket","changedClients","Y","AwarenessProtocol","LeveldbPersistence","Observable","YSocketIO","io","configuration","_a","socket","doc","stateVector","syncStep2","update","next","namespace","name","gc","Document","levelPersistenceDir","ldb","LeveldbPersistence","docName","ydoc","persistedYdoc","newUpdates","_docName","_ydoc"]}