@wordpress/sync
Version:
132 lines (131 loc) • 4.17 kB
JavaScript
// 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