@samepage/internal
Version:
Utilities used across modules - not meant for use by users directly
95 lines • 4.91 kB
JavaScript
;
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