y-mongodb-provider
Version:
MongoDB database adapter for Yjs
175 lines (172 loc) • 7.04 kB
JavaScript
import * as Y from 'yjs';
import * as binary from 'lib0/binary';
import { MongoAdapter } from './mongo-adapter.mjs';
import { PREFERRED_TRIM_SIZE, getYDocFromDb, storeUpdate, readStateVector, getCurrentUpdateClock, flushDocument, createDocumentStateVectorKey, clearUpdatesRange, createDocumentMetaKey, getAllSVDocs, decodeMongodbStateVector, flushDB } from './utils.mjs';
class MongodbPersistence {
constructor(location, opts = {}) {
Object.defineProperty(this, "flushSize", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "multipleCollections", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "tr", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "_transact", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
const { collectionName = 'yjs-writings', multipleCollections = false, flushSize = 400 } = opts;
if (typeof collectionName !== 'string' || !collectionName) {
throw new Error('Constructor option "collectionName" is not a valid string. Either dont use this option (default is "yjs-writings") or use a valid string! Take a look into the Readme for more information: https://github.com/MaxNoetzold/y-mongodb-provider#persistence--mongodbpersistenceconnectionlink-string-options-object');
}
if (typeof multipleCollections !== 'boolean') {
throw new Error('Constructor option "multipleCollections" is not a boolean. Either dont use this option (default is "false") or use a valid boolean! Take a look into the Readme for more information: https://github.com/MaxNoetzold/y-mongodb-provider#persistence--mongodbpersistenceconnectionlink-string-options-object');
}
if (typeof flushSize !== 'number' || flushSize <= 0) {
throw new Error('Constructor option "flushSize" is not a valid number. Either dont use this option (default is "400") or use a valid number larger than 0! Take a look into the Readme for more information: https://github.com/MaxNoetzold/y-mongodb-provider#persistence--mongodbpersistenceconnectionlink-string-options-object');
}
const db = new MongoAdapter(location, {
collection: collectionName,
multipleCollections,
});
this.flushSize = flushSize ?? PREFERRED_TRIM_SIZE;
this.multipleCollections = multipleCollections;
this.tr = {};
this._transact = (docName, f) => {
if (!this.tr[docName]) {
this.tr[docName] = Promise.resolve();
}
const currTr = this.tr[docName];
let nextTr = null;
nextTr = (async () => {
await currTr;
let res = null;
try {
res = await f(db);
}
catch (err) {
console.warn('Error during saving transaction', err);
}
if (this.tr[docName] === nextTr) {
delete this.tr[docName];
}
return res;
})();
this.tr[docName] = nextTr;
return this.tr[docName];
};
}
getYDoc(docName) {
return this._transact(docName, async (db) => getYDocFromDb(db, docName, this.flushSize));
}
storeUpdate(docName, update) {
return this._transact(docName, (db) => storeUpdate(db, docName, update));
}
getStateVector(docName) {
return this._transact(docName, async (db) => {
const { clock, sv } = await readStateVector(db, docName);
let curClock = -1;
if (sv !== null) {
curClock = await getCurrentUpdateClock(db, docName);
}
if (sv !== null && clock === curClock) {
return sv;
}
else {
const ydoc = await getYDocFromDb(db, docName, this.flushSize);
const newSv = Y.encodeStateVector(ydoc);
await flushDocument(db, docName, Y.encodeStateAsUpdate(ydoc), newSv);
return newSv;
}
});
}
async getDiff(docName, stateVector) {
const ydoc = await this.getYDoc(docName);
return Y.encodeStateAsUpdate(ydoc, stateVector);
}
clearDocument(docName) {
return this._transact(docName, async (db) => {
if (!this.multipleCollections) {
await db.del(createDocumentStateVectorKey(docName));
await clearUpdatesRange(db, docName, 0, binary.BITS32);
}
else {
await db.dropCollection(docName);
}
});
}
setMeta(docName, metaKey, value) {
return this._transact(docName, async (db) => {
await db.put(createDocumentMetaKey(docName, metaKey), { value });
});
}
getMeta(docName, metaKey) {
return this._transact(docName, async (db) => {
const res = await db.get({
...createDocumentMetaKey(docName, metaKey),
});
if (!res?.value) {
return undefined;
}
return res.value;
});
}
delMeta(docName, metaKey) {
return this._transact(docName, (db) => db.del({
...createDocumentMetaKey(docName, metaKey),
}));
}
getAllDocNames() {
return this._transact('global', async (db) => {
if (this.multipleCollections) {
return db.getCollectionNames();
}
else {
const docs = await getAllSVDocs(db);
return docs.map((doc) => doc.docName);
}
});
}
getAllDocStateVectors() {
return this._transact('global', async (db) => {
const docs = await getAllSVDocs(db);
return docs.map((doc) => {
const { sv, clock } = decodeMongodbStateVector(doc.value);
return { name: doc.docName, sv, clock };
});
});
}
flushDocument(docName) {
return this._transact(docName, async (db) => {
const ydoc = await getYDocFromDb(db, docName, this.flushSize);
const newSv = Y.encodeStateVector(ydoc);
await flushDocument(db, docName, Y.encodeStateAsUpdate(ydoc), newSv);
return newSv;
});
}
flushDB() {
return this._transact('global', async (db) => {
await flushDB(db);
});
}
destroy() {
return this._transact('global', async (db) => {
await db.close();
});
}
}
export { MongodbPersistence };
//# sourceMappingURL=y-mongodb.mjs.map