y-socket.io-provider
Version:
Socket IO Provider for Yjs (Inspired by y-websocket and y-socket.io)
216 lines (215 loc) • 8.81 kB
TypeScript
/**
* SocketIOProvider instance configuration. Here you can configure:
* - autoConnect: (Optional) Will try to connect to the server when the instance is created if true; otherwise you have to call `provider.connect()` manually
* - awareness: (Optional) Give an existing awareness
* - resyncInterval: (Optional) Specify the number of milliseconds to set an interval to synchronize the document,
* if it is greater than 0 enable the synchronization interval (by default is -1)
* - disableBc: (Optional) This boolean disable the broadcast channel functionality, by default is false (broadcast channel enabled)
* - onConnect: (Optional) Set a callback that will triggered immediately when the socket is connected
* - onDisconnect: (Optional) Set a callback that will triggered immediately when the socket is disconnected
* - onConnectError: (Optional) Set a callback that will triggered immediately when the occurs a socket connection error
*/
/**
* The socket io provider class to sync a document
*/
export class SocketIOProvider extends Observable<any> {
/**
* SocketIOProvider constructor
* @constructor
* @param {string} url The connection url from server
* @param {string} roomName The document's room name
* @param {Y.Doc} doc The yjs document
* @param options Configuration options to the SocketIOProvider
*/
constructor(url: string, roomName: string, doc: Y.Doc, { autoConnect, awareness, resyncInterval, disableBc, auth, }: {
autoConnect?: boolean;
awareness?: AwarenessProtocol.Awareness;
resyncInterval?: number;
disableBc?: boolean;
auth?: {};
});
/**
* The connection url to server. Example: `ws://localhost:3001`
* @type {string}
*/
_url: string;
/**
* The name of the document room
* @type {string}
*/
roomName: string;
/**
* The broadcast channel room
* @type {string}
* @private
*/
private _broadcastChannel;
/**
* The socket connection
* @type {Socket}
*/
socket: Socket;
/**
* The yjs document
* @type {Y.Doc}
*/
doc: Y.Doc;
/**
* The awareness
* @type {AwarenessProtocol.Awareness}
*/
awareness: AwarenessProtocol.Awareness;
/**
* Disable broadcast channel, by default is false
* @type {boolean}
*/
disableBc: boolean;
/**
* The broadcast channel connection status indicator
* @type {boolean}
*/
bcconnected: boolean;
/**
* The document's sync status indicator
* @type {boolean}
* @private
*/
private _synced;
/**
* Interval to emit `sync-step-1` to sync changes
* @type {NodeJS.Timer | null}
* @private
*/
private resyncInterval;
/**
* Broadcast channel room getter
* @type {string}
*/
get broadcastChannel(): string;
/**
* URL getter
* @type {string}
*/
get url(): string;
/**
* Synchronized state flag setter
*/
set synced(arg: boolean);
/**
* Synchronized state flag getter
* @type {boolean}
*/
get synced(): boolean;
/**
* This function initializes the socket event listeners to synchronize document changes.
*
* The synchronization protocol is as follows:
* - A server emits the sync step one event (`sync-step-1`) which sends the document as a state vector
* and the sync step two callback as an acknowledgment according to the socket io acknowledgments.
* - When the client receives the `sync-step-1` event, it executes the `syncStep2` acknowledgment callback and sends
* the difference between the received state vector and the local document (this difference is called an update).
* - The second step of the sync is to apply the update sent in the `syncStep2` callback parameters from the client
* to the document on the server side.
* - There is another event (`sync-update`) that is emitted from the server, which sends an update for the document,
* and when the client receives this event, it applies the received update to the local document.
* - When an update is applied to a document, it will fire the document's "update" event, which
* sends the update to the server.
* @type {() => void}
* @private
*/
private initSyncListeners;
/**
* This function initializes socket event listeners to synchronize awareness changes.
*
* The awareness protocol is as follows:
* - The server emits the `awareness-update` event by sending the awareness update.
* - The client receives that event and applies the received update to the local awareness.
* - When an update is applied to awareness, the awareness "update" event will fire, which
* sends the update to the server.
* @type {() => void}
* @private
*/
private initAwarenessListeners;
/**
* This function initialize the window or process events listener. Specifically set ups the
* window `beforeunload` and process `exit` events to remove the client from the awareness.
* @type {() => void}
*/
initSystemListeners: () => void;
connect(): void;
/**
* This function runs when the socket connects and reconnects and emits the `sync-step-1`
* and `awareness-update` socket events to start synchronization.
*
* Also starts the resync interval if is enabled.
* @private
* @param onConnect (Optional) A callback that will be triggered every time that socket is connected or reconnected
* @param resyncInterval (Optional) A number of milliseconds for interval of synchronize
*/
private onSocketConnection;
disconnect(): void;
/**
* This function runs when the socket is disconnected and emits the socket event `awareness-update`
* which removes this client from awareness.
*/
onSocketDisconnection: (event: any) => void;
/**
* This function is executed when the socket connection fails.
*/
onSocketConnectionError: (error: any) => void;
/**
* This function is executed when the document is updated, if the instance that
* emit the change is not this, it emit the changes by socket and broadcast channel.
* @private
* @param {Uint8Array} update Document update
* @param {SocketIOProvider} origin The SocketIOProvider instance that emits the change.
* @type {(update: Uint8Array, origin: SocketIOProvider) => void}
*/
private onUpdateDoc;
/**
* This function is called when the server emits the `sync-update` event and applies the received update to the local document.
* @private
* @param {Uint8Array}update A document update received by the `sync-update` socket event
* @type {(update: Uint8Array) => void}
*/
private onSocketSyncUpdate;
/**
* This function is executed when the local awareness changes and this broadcasts the changes per socket and broadcast channel.
* @private
* @param {{ added: number[], updated: number[], removed: number[] }} awarenessChanges The clients added, updated and removed
* @param {SocketIOProvider | null} origin The SocketIOProvider instance that emits the change.
* @type {({ added, updated, removed }: { added: number[], updated: number[], removed: number[] }, origin: SocketIOProvider | null) => void}
*/
private awarenessUpdate;
/**
* This function is executed when the windows will be unloaded or the process will be closed and this
* will remove the local client from awareness.
* @private
* @type {() => void}
*/
private beforeUnloadHandler;
/**
* This function subscribes the provider to the broadcast channel and initiates synchronization by broadcast channel.
* @type {() => void}
*/
connectBc: () => void;
/**
* This function unsubscribes the provider from the broadcast channel and before unsubscribing, updates the awareness.
* @type {() => void}
*/
disconnectBc: () => void;
/**
* This method handles messages received by the broadcast channel and responds to them.
* @param {{ type: string, data: any }} message The object message received by broadcast channel
* @param {SocketIOProvider} origin The SocketIOProvider instance that emits the change
* @type {(message: { type: string, data: any }, origin: SocketIOProvider) => void}
*/
onBroadcastChannelMessage: (message: {
type: string;
data: any;
}, origin: SocketIOProvider) => void;
}
import { Observable } from "lib0/observable";
import { Socket } from "socket.io-client";
import * as Y from "yjs";
import * as AwarenessProtocol from "y-protocols/awareness";