UNPKG

@wordpress/sync

Version:
132 lines (131 loc) 4.17 kB
// packages/sync/src/manager.ts import * as Y from "yjs"; import { CRDT_RECORD_MAP_KEY as RECORD_KEY, LOCAL_SYNC_MANAGER_ORIGIN } from "./config"; import { createPersistedCRDTDoc, getPersistedCrdtDoc } from "./persistence"; import { getProviderCreators } from "./providers"; import { createUndoManager } from "./undo-manager"; import { createYjsDoc } from "./utils"; function createSyncManager() { const entityStates = /* @__PURE__ */ new Map(); const undoManager = createUndoManager(); async function loadEntity(syncConfig, objectType, objectId, record, handlers) { const providerCreators = getProviderCreators(); if (0 === providerCreators.length) { return; } const entityId = getEntityId(objectType, objectId); if (entityStates.has(entityId)) { return; } const ydoc = createYjsDoc({ objectType }); const recordMap = ydoc.getMap(RECORD_KEY); const unload = () => { providerResults.forEach((result) => result.destroy()); recordMap.unobserveDeep(onRecordUpdate); ydoc.destroy(); entityStates.delete(entityId); }; const onRecordUpdate = (_events, transaction) => { if (transaction.local && !(transaction.origin instanceof Y.UndoManager)) { return; } void updateEntityRecord(objectType, objectId); }; undoManager.addToScope(recordMap); const entityState = { handlers, objectId, objectType, syncConfig, unload, ydoc }; entityStates.set(entityId, entityState); const providerResults = await Promise.all( providerCreators.map( (create) => create(objectType, objectId, ydoc) ) ); recordMap.observeDeep(onRecordUpdate); const isInvalid = applyPersistedCrdtDoc(syncConfig, ydoc, record); if (isInvalid) { ydoc.transact(() => { syncConfig.applyChangesToCRDTDoc(ydoc, record); }, LOCAL_SYNC_MANAGER_ORIGIN); const meta = createEntityMeta(objectType, objectId); handlers.editRecord({ meta }); handlers.saveRecord(); } } function unloadEntity(objectType, objectId) { entityStates.get(getEntityId(objectType, objectId))?.unload(); } function getEntityId(objectType, objectId) { return `${objectType}_${objectId}`; } function applyPersistedCrdtDoc(syncConfig, targetDoc, record) { if (!syncConfig.supports?.crdtPersistence) { return true; } const tempDoc = getPersistedCrdtDoc(record); if (!tempDoc) { return true; } const update = Y.encodeStateAsUpdateV2(tempDoc); targetDoc.transact(() => { Y.applyUpdateV2(targetDoc, update); }, LOCAL_SYNC_MANAGER_ORIGIN); const changes = syncConfig.getChangesFromCRDTDoc(tempDoc, record); tempDoc.destroy(); return Object.keys(changes).length > 0; } function updateCRDTDoc(objectType, objectId, changes, origin) { const entityId = getEntityId(objectType, objectId); const entityState = entityStates.get(entityId); if (!entityState) { return; } const { syncConfig, ydoc } = entityState; ydoc.transact(() => { syncConfig.applyChangesToCRDTDoc(ydoc, changes); }, origin); } async function updateEntityRecord(objectType, objectId) { const entityId = getEntityId(objectType, objectId); const entityState = entityStates.get(entityId); if (!entityState) { return; } const { handlers, syncConfig, ydoc } = entityState; const changes = syncConfig.getChangesFromCRDTDoc( ydoc, await handlers.getEditedRecord() ); if (0 === Object.keys(changes).length) { return; } handlers.editRecord(changes); } function createEntityMeta(objectType, objectId) { const entityId = getEntityId(objectType, objectId); const entityState = entityStates.get(entityId); if (!entityState?.syncConfig.supports?.crdtPersistence) { return {}; } return createPersistedCRDTDoc(entityState.ydoc); } return { createMeta: createEntityMeta, load: loadEntity, undoManager, unload: unloadEntity, update: updateCRDTDoc }; } export { createSyncManager }; //# sourceMappingURL=manager.js.map