UNPKG

y-mongodb-provider

Version:
175 lines (172 loc) 7.04 kB
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