y-socket.io
Version:
Socket IO Connector for Yjs (Inspired by y-websocket)
262 lines (259 loc) • 11.4 kB
TypeScript
import * as Y from 'yjs';
import * as AwarenessProtocol from 'y-protocols/awareness';
import { Observable } from 'lib0/observable';
import { Socket, ManagerOptions, SocketOptions } from 'socket.io-client';
/**
* 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
*/
interface ProviderConfiguration {
/**
* (Optional) This boolean specify if the provider should connect when the instance is created, by default is true
*/
autoConnect?: boolean;
/**
* (Optional) An existent awareness, by default is a new AwarenessProtocol.Awareness instance
*/
awareness?: AwarenessProtocol.Awareness;
/**
* (optional) Specify the number of milliseconds to synchronize, by default is -1 (this disable resync interval)
*/
resyncInterval?: number;
/**
* (Optional) This boolean disable the broadcast channel functionality, by default is false (broadcast channel enabled)
*/
disableBc?: boolean;
/**
* (Optional) Add the authentication data
*/
auth?: {
[key: string]: any;
};
}
/**
* The socket io provider class to sync a document
*/
declare class SocketIOProvider extends Observable<string> {
/**
* The connection url to server. Example: `ws://localhost:3001`
* @type {string}
*/
private readonly _url;
/**
* The name of the document room
* @type {string}
*/
roomName: string;
/**
* The broadcast channel room
* @type {string}
* @private
*/
private readonly _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 {ReturnType<typeof setTimeout> | null}
* @private
*/
private resyncInterval;
/**
* Optional overrides for socket.io
* @type {Partial<ManagerOptions & SocketOptions> | undefined}
* @private
*/
private readonly _socketIoOptions;
/**
* 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 {ProviderConfiguration} options Configuration options to the SocketIOProvider
* @param {Partial<ManagerOptions & SocketOptions> | undefined} socketIoOptions optional overrides for socket.io
*/
constructor(url: string, roomName: string, doc: Y.Doc | undefined, { autoConnect, awareness, resyncInterval, disableBc, auth }: ProviderConfiguration, socketIoOptions?: Partial<ManagerOptions & SocketOptions> | undefined);
/**
* Broadcast channel room getter
* @type {string}
*/
get broadcastChannel(): string;
/**
* URL getter
* @type {string}
*/
get url(): string;
/**
* Synchronized state flag getter
* @type {boolean}
*/
get synced(): boolean;
/**
* Synchronized state flag setter
*/
set synced(state: 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 readonly 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 readonly 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}
*/
private readonly initSystemListeners;
/**
* Connect provider's socket
* @type {() => 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 {() => void | Promise<void>} onConnect (Optional) A callback that will be triggered every time that socket is connected or reconnected
* @param {number} resyncInterval (Optional) A number of milliseconds for interval of synchronize
* @type {(onConnect: () => void | Promise<void>, resyncInterval: number = -1) => void}
*/
private readonly onSocketConnection;
/**
* Disconnect provider's socket
* @type {() => void}
*/
disconnect(): void;
/**
* This function runs when the socket is disconnected and emits the socket event `awareness-update`
* which removes this client from awareness.
* @private
* @param {Socket.DisconnectReason} event The reason of the socket disconnection
* @param {() => void | Promise<void>} onDisconnect (Optional) A callback that will be triggered every time that socket is disconnected
* @type {(event: Socket.DisconnectReason, onDisconnect: () => void | Promise<void>) => void}
*/
private readonly onSocketDisconnection;
/**
* This function is executed when the socket connection fails.
* @param {Error} error The error in the connection
* @param {(error: Error) => void | Promise<void>} onConnectError (Optional) A callback that will be triggered every time that socket has a connection error
* @type {(error: Error, onConnectError: (error: Error) => void | Promise<void>) => void}
*/
private readonly onSocketConnectionError;
/**
* Destroy the provider. This method clears the document, awareness, and window/process listeners and disconnects the socket.
* @type {() => void}
*/
destroy(): 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 readonly 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 readonly 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 readonly 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 readonly beforeUnloadHandler;
/**
* This function subscribes the provider to the broadcast channel and initiates synchronization by broadcast channel.
* @type {() => void}
*/
private readonly connectBc;
/**
* This function unsubscribes the provider from the broadcast channel and before unsubscribing, updates the awareness.
* @type {() => void}
*/
private readonly disconnectBc;
/**
* 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}
*/
private readonly onBroadcastChannelMessage;
}
export { ProviderConfiguration, SocketIOProvider };