UNPKG

@wordpress/sync

Version:
129 lines (114 loc) 3.41 kB
/** * External dependencies */ // @ts-ignore import * as Y from 'yjs'; /** @typedef {import('./types').ObjectType} ObjectType */ /** @typedef {import('./types').ObjectID} ObjectID */ /** @typedef {import('./types').ObjectConfig} ObjectConfig */ /** @typedef {import('./types').CRDTDoc} CRDTDoc */ /** @typedef {import('./types').ConnectDoc} ConnectDoc */ /** @typedef {import('./types').SyncProvider} SyncProvider */ /** * Create a sync provider. * * @param {ConnectDoc} connectLocal Connect the document to a local database. * @param {ConnectDoc} connectRemote Connect the document to a remote sync connection. * @return {SyncProvider} Sync provider. */ export const createSyncProvider = ( connectLocal, connectRemote ) => { /** * @type {Record<string,ObjectConfig>} */ const config = {}; /** * @type {Record<string,Record<string,()=>void>>} */ const listeners = {}; /** * @type {Record<string,Record<string,CRDTDoc>>} */ const docs = {}; /** * Registers an object type. * * @param {ObjectType} objectType Object type to register. * @param {ObjectConfig} objectConfig Object config. */ function register( objectType, objectConfig ) { config[ objectType ] = objectConfig; } /** * Fetch data from local database or remote source. * * @param {ObjectType} objectType Object type to load. * @param {ObjectID} objectId Object ID to load. * @param {Function} handleChanges Callback to call when data changes. */ async function bootstrap( objectType, objectId, handleChanges ) { const doc = new Y.Doc(); docs[ objectType ] = docs[ objectType ] || {}; docs[ objectType ][ objectId ] = doc; const updateHandler = () => { const data = config[ objectType ].fromCRDTDoc( doc ); handleChanges( data ); }; doc.on( 'update', updateHandler ); // connect to locally saved database. const destroyLocalConnection = await connectLocal( objectId, objectType, doc ); // Once the database syncing is done, start the remote syncing if ( connectRemote ) { await connectRemote( objectId, objectType, doc ); } const loadRemotely = config[ objectType ].fetch; if ( loadRemotely ) { loadRemotely( objectId ).then( ( data ) => { doc.transact( () => { config[ objectType ].applyChangesToDoc( doc, data ); } ); } ); } listeners[ objectType ] = listeners[ objectType ] || {}; listeners[ objectType ][ objectId ] = () => { destroyLocalConnection(); doc.off( 'update', updateHandler ); }; } /** * Fetch data from local database or remote source. * * @param {ObjectType} objectType Object type to load. * @param {ObjectID} objectId Object ID to load. * @param {any} data Updates to make. */ async function update( objectType, objectId, data ) { const doc = docs[ objectType ][ objectId ]; if ( ! doc ) { throw 'Error doc ' + objectType + ' ' + objectId + ' not found'; } doc.transact( () => { config[ objectType ].applyChangesToDoc( doc, data ); } ); } /** * Stop updating a document and discard it. * * @param {ObjectType} objectType Object type to load. * @param {ObjectID} objectId Object ID to load. */ async function discard( objectType, objectId ) { if ( listeners?.[ objectType ]?.[ objectId ] ) { listeners[ objectType ][ objectId ](); } } return { register, bootstrap, update, discard, }; };