UNPKG

open-collaboration-yjs

Version:

Open Collaboration Yjs integration, part of the Open Collaboration Tools project

98 lines 4.54 kB
// ****************************************************************************** // Copyright 2024 TypeFox GmbH // This program and the accompanying materials are made available under the // terms of the MIT License, which is available in the project root. // ****************************************************************************** import * as syncProtocol from 'y-protocols/sync'; import * as awarenessProtocol from 'y-protocols/awareness'; import * as encoding from 'lib0/encoding'; import * as decoding from 'lib0/decoding'; import { ObservableV2 } from 'lib0/observable'; export const LOCAL_ORIGIN = 'local'; export class OpenCollaborationYjsProvider extends ObservableV2 { connection; doc; awareness; constructor(connection, doc, awareness, options) { super(); this.connection = connection; this.doc = doc; this.awareness = awareness; this.doc.on('update', this.yjsUpdateHandler.bind(this)); this.awareness.on('update', this.yjsAwarenessUpdateHandler.bind(this)); connection.sync.onDataUpdate(this.ocpDataUpdateHandler.bind(this)); connection.sync.onAwarenessUpdate(this.ocpAwarenessUpdateHandler.bind(this)); connection.sync.onAwarenessQuery(this.ocpAwarenessQueryHandler.bind(this)); if (options?.resyncTimer && options.resyncTimer > 0) { this.setResyncInterval(options.resyncTimer); } } setResyncInterval(timeout) { const interval = setInterval(() => { const encoder = encoding.createEncoder(); syncProtocol.writeSyncStep1(encoder, this.doc); this.connection.sync.dataUpdate(this.encode(encoder)); }, timeout); this.doc.on('destroy', () => { clearInterval(interval); }); } ocpDataUpdateHandler(origin, update) { const decoder = this.decode(update); const encoder = encoding.createEncoder(); const syncMessageType = syncProtocol.readSyncMessage(decoder, encoder, this.doc, origin); if (syncMessageType === syncProtocol.messageYjsSyncStep1) { this.connection.sync.dataUpdate(origin, this.encode(encoder)); } } ocpAwarenessUpdateHandler(origin, update) { const decoder = this.decode(update); awarenessProtocol.applyAwarenessUpdate(this.awareness, decoding.readVarUint8Array(decoder), origin); } ocpAwarenessQueryHandler(origin) { const encoder = encoding.createEncoder(); encoding.writeVarUint8Array(encoder, awarenessProtocol.encodeAwarenessUpdate(this.awareness, Array.from(this.awareness.getStates().keys()))); this.connection.sync.awarenessUpdate(origin, this.encode(encoder)); } yjsUpdateHandler(update, origin) { if (origin !== this) { const encoder = encoding.createEncoder(); syncProtocol.writeUpdate(encoder, update); this.connection.sync.dataUpdate(this.encode(encoder)); } } yjsAwarenessUpdateHandler(changed) { const changedClients = changed.added.concat(changed.updated).concat(changed.removed); const encoder = encoding.createEncoder(); encoding.writeVarUint8Array(encoder, awarenessProtocol.encodeAwarenessUpdate(this.awareness, changedClients)); this.connection.sync.awarenessUpdate(this.encode(encoder)); } connect() { // write sync step 1 const encoderSync = encoding.createEncoder(); syncProtocol.writeSyncStep1(encoderSync, this.doc); this.connection.sync.dataUpdate(this.encode(encoderSync)); // broadcast local state const encoderState = encoding.createEncoder(); syncProtocol.writeSyncStep2(encoderState, this.doc); this.connection.sync.dataUpdate(this.encode(encoderState)); // query awareness info this.connection.sync.awarenessQuery(); // broadcast local awareness info const encoderAwareness = encoding.createEncoder(); encoding.writeVarUint8Array(encoderAwareness, awarenessProtocol.encodeAwarenessUpdate(this.awareness, [ this.doc.clientID ])); this.connection.sync.awarenessUpdate(this.encode(encoderAwareness)); } dispose() { awarenessProtocol.removeAwarenessStates(this.awareness, [this.doc.clientID], 'client disconnected'); } encode(encoder) { return encoding.toUint8Array(encoder); } decode(data) { return decoding.createDecoder(data); } } //# sourceMappingURL=yjs-provider.js.map