UNPKG

@samepage/internal

Version:

Utilities used across modules - not meant for use by users directly

95 lines 4.91 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); const base64ToBinary_1 = tslib_1.__importDefault(require("./base64ToBinary")); const automerge_1 = tslib_1.__importDefault(require("automerge")); const dispatchAppEvent_1 = tslib_1.__importDefault(require("./dispatchAppEvent")); const binaryToBase64_1 = tslib_1.__importDefault(require("./binaryToBase64")); const localAutomergeDb_1 = require("../utils/localAutomergeDb"); const apiClient_1 = tslib_1.__importDefault(require("./apiClient")); const saveAndApply_1 = tslib_1.__importDefault(require("./saveAndApply")); const ExtensionError_1 = tslib_1.__importDefault(require("./ExtensionError")); // TODO - have this map to raw data instead of a function for serializability const pendingUpdates = {}; const handleSharePageUpdateOperation = async ({ changes, notebookPageId, dependencies = {}, }, decodeState) => { const isShared = await (0, localAutomergeDb_1.has)(notebookPageId); if (!isShared) return; const executeUpdate = () => (0, localAutomergeDb_1.load)(notebookPageId) .then(async (oldDoc) => { const binaryChanges = changes.map((c) => (0, base64ToBinary_1.default)(c)); const [newDoc, patch] = automerge_1.default.applyChanges(oldDoc, binaryChanges); (0, localAutomergeDb_1.set)(notebookPageId, newDoc); if (patch.pendingChanges) { const storedChanges = automerge_1.default.getAllChanges(newDoc).map((c) => automerge_1.default.decodeChange(c)); const existingDependencies = Object.fromEntries(storedChanges.map((c) => [`${c.actor}~${c.seq}`, c.hash])); const me = automerge_1.default.getActorId(newDoc); if (Object.entries(dependencies).some(([actor, { seq, hash }]) => actor !== me && existingDependencies[`${actor}~${seq}`] && existingDependencies[`${actor}~${seq}`] !== hash)) { (0, dispatchAppEvent_1.default)({ type: "log", id: "share-page-corrupted", content: `It looks like your version of the shared page ${notebookPageId} is corrupted and will cease to apply updates from other notebooks in the future. To resolve this issue, ask one of the other connected notebooks to manually sync the page.`, intent: "error", }); } else { const storedHashes = new Set(storedChanges.map((c) => c.hash || "")); const actorsToRequest = Object.entries(patch.clock).filter(([actor, seq]) => { if (me === actor) { return false; } const dependentHashFromActor = existingDependencies[`${actor}~${seq}`]; return !(dependentHashFromActor && storedHashes.has(dependentHashFromActor)); }); if (!actorsToRequest.length && !automerge_1.default.isFrozen(newDoc)) { const missingDependencies = binaryChanges .map((c) => automerge_1.default.decodeChange(c)) .flatMap((c) => c.deps) .filter((c) => !storedHashes.has(c)); throw new ExtensionError_1.default("No actors to request and still waiting for changes", { missingDependencies, binaryDocument: (0, binaryToBase64_1.default)(automerge_1.default.save(newDoc)), notebookPageId, }); } else { await Promise.all(actorsToRequest.map(([actor]) => (0, apiClient_1.default)({ method: "request-page-update", notebookPageId, seq: patch.clock[actor], actor, }))); } } } if (Object.keys(patch.diffs.props).length) { (0, saveAndApply_1.default)({ notebookPageId, doc: newDoc, applyState: (id, state) => decodeState(id, { $body: state }), }); } }) .finally(() => { var _a; if (pendingUpdates[notebookPageId].length === 0) { delete pendingUpdates[notebookPageId]; return Promise.resolve(); } else { return (_a = pendingUpdates[notebookPageId].shift()) === null || _a === void 0 ? void 0 : _a(); } }); if (!pendingUpdates[notebookPageId]) { pendingUpdates[notebookPageId] = []; return executeUpdate(); } else { pendingUpdates[notebookPageId].push(executeUpdate); } }; exports.default = handleSharePageUpdateOperation; //# sourceMappingURL=handleSharePageUpdateOperation.js.map