/* import { Y } from '../utils/Y.mjs' import { createMutex } from '../lib/mutex.mjs' import { decodePersisted, encodeStructsDS, encodeUpdate, PERSIST_STRUCTS_DS, PERSIST_UPDATE } from './decodePersisted.mjs' function rtop (request) { return new Promise(function (resolve, reject) { request.onerror = function (event) { reject(new Error(event.target.error)) } request.onblocked = function () { location.reload() } request.onsuccess = function (event) { resolve(event.target.result) } }) } function openDB (room) { return new Promise(function (resolve, reject) { let request = indexedDB.open(room) request.onupgradeneeded = function (event) { const db = event.target.result if (db.objectStoreNames.contains('updates')) { db.deleteObjectStore('updates') } db.createObjectStore('updates', {autoIncrement: true}) } request.onerror = function (event) { reject(new Error(event.target.error)) } request.onblocked = function () { location.reload() } request.onsuccess = function (event) { const db = event.target.result db.onversionchange = function () { db.close() } resolve(db) } }) } function persist (room) { let t = room.db.transaction(['updates'], 'readwrite') let updatesStore = t.objectStore('updates') return rtop(updatesStore.getAll()) .then(updates => { // apply all previous updates before deleting them room.mutex(() => { updates.forEach(update => { decodePersisted(y, new BinaryDecoder(update)) }) }) const encoder = new BinaryEncoder() encodeStructsDS(y, encoder) // delete all pending updates rtop(updatesStore.clear()).then(() => { // write current model updatesStore.put(encoder.createBuffer()) }) }) } function saveUpdate (room, updateBuffer) { const db = room.db if (db !== null) { const t = db.transaction(['updates'], 'readwrite') const updatesStore = t.objectStore('updates') const updatePut = rtop(updatesStore.put(updateBuffer)) rtop(updatesStore.count()).then(cnt => { if (cnt >= PREFERRED_TRIM_SIZE) { persist(room) } }) return updatePut } } function registerRoomInPersistence (documentsDB, roomName) { return documentsDB.then( db => Promise.all([ db, rtop(db.transaction(['documents'], 'readonly').objectStore('documents').get(roomName)) ]) ).then( ([db, doc]) => { if (doc === undefined) { return rtop(db.transaction(['documents'], 'readwrite').objectStore('documents').add({ roomName, serverUpdateCounter: 0 })) } } ) } const PREFERRED_TRIM_SIZE = 400 export class IndexedDBPersistence { constructor () { this._rooms = new Map() this._documentsDB = new Promise(function (resolve, reject) { let request = indexedDB.open('_yjs_documents') request.onupgradeneeded = function (event) { const db = event.target.result if (db.objectStoreNames.contains('documents')) { db.deleteObjectStore('documents') } db.createObjectStore('documents', { keyPath: "roomName" }) } request.onerror = function (event) { reject(new Error(event.target.error)) } request.onblocked = function () { location.reload()EventListener' is not defined. /home/dmonad/go/src/github.com/y-js/yjs/persistences/IndexedDBPersistence.js:160:36: 'BinaryDecoder' is not defined. /home/dmonad/go/src/github.com/y-js/yjs/persistences/IndexedDBPersistence.js:167:25: 'BinaryEncoder' is not defined. /home/dmonad/go/src/github.com/y-js/yjs/persistences/IndexedDBPersistence.js:178:25: 'BinaryEncoder' is not defined. /home/dmonad/go/src/github.com/y-js/yjs/persistences/IndexedDBPersistence.js:203:34: 'BinaryDecoder' is not defined. /home/dmonad/go/src/github.com/y-js/yjs/persistences/IndexedDBPersistence.js:213:17: 'encoder' is assigned a value but never used. /home/dmonad/go/src/github.com/y-js/yjs/persistences/IndexedDBPersistence.js:213:31: 'BinaryEncoder' is not defined. /home/dmonad/go/src/github.com/y-js/yjs/persistences/IndexedDBPersistence.js:214:30: 'BinaryEncoder' is not defined. /home/dmonad/go/src/github.com/y-js/yjs/persistences/IndexedDBPersistence.js:230:12: Trailing spaces not allowed. /home/dmonad/go/src/github.com/y-js/yjs/persistences/IndexedDBPersistence.js:237:5: Return statement should not contain assignment. /home/dmonad/go/src/github.com/y-js/yjs/persistences/IndexedDBPersistence.js:243:29: 'BinaryEncoder' i } request.onsuccess = function (event) { const db = event.target.result db.onversionchange = function () { db.close() } resolve(db) } }) addEventListener('unload', () => { // close everything when page unloads this._rooms.forEach(room => { if (room.db !== null) { room.db.close() } else { room.dbPromise.then(db => db.close()) } }) this._documentsDB.then(db => db.close()) }) } getAllDocuments () { return this._documentsDB.then( db => rtop(db.transaction(['documents'], 'readonly').objectStore('documents').getAll()) ) } setRemoteUpdateCounter (roomName, remoteUpdateCounter) { this._documentsDB.then( db => rtop(db.transaction(['documents'], 'readwrite').objectStore('documents').put({ roomName, remoteUpdateCounter })) ) } _createYInstance (roomName) { const room = this._rooms.get(roomName) if (room !== undefined) { return room.y } const y = new Y() return openDB(roomName).then( db => rtop(db.transaction(['updates'], 'readonly').objectStore('updates').getAll()) ).then( updates => y.transact(() => { updates.forEach(update => { decodePersisted(y, new BinaryDecoder(update)) }) }, true) ).then(() => Promise.resolve(y)) } _persistStructsDS (roomName, structsDS) { const encoder = new BinaryEncoder() encoder.writeVarUint(PERSIST_STRUCTS_DS) encoder.writeArrayBuffer(structsDS) return openDB(roomName).then(db => { const t = db.transaction(['updates'], 'readwrite') const updatesStore = t.objectStore('updates') return rtop(updatesStore.put(encoder.createBuffer())) }) } _persistStructs (roomName, structs) { const encoder = new BinaryEncoder() encoder.writeVarUint(PERSIST_UPDATE) encoder.writeArrayBuffer(structs) return openDB(roomName).then(db => { const t = db.transaction(['updates'], 'readwrite') const updatesStore = t.objectStore('updates') return rtop(updatesStore.put(encoder.createBuffer())) }) } connectY (roomName, y) { if (this._rooms.has(roomName)) { throw new Error('A Y instance is already bound to this room!') } let room = { db: null, dbPromise: null, channel: null, mutex: createMutex(), y } if (typeof BroadcastChannel !== 'undefined') { room.channel = new BroadcastChannel('__yjs__' + roomName) room.channel.addEventListener('message', e => { room.mutex(function () { decodePersisted(y, new BinaryDecoder(e.data)) }) }) } y.on('destroyed', () => { this.disconnectY(roomName, y) }) y.on('afterTransaction', (y, transaction) => { room.mutex(() => { if (transaction.encodedStructsLen > 0) { const encoder = new BinaryEncoder() const update = new BinaryEncoder() encodeUpdate(y, transaction.encodedStructs, update) const updateBuffer = update.createBuffer() if (room.channel !== null) { room.channel.postMessage(updateBuffer) } if (transaction.encodedStructsLen > 0) { if (room.db !== null) { saveUpdate(room, updateBuffer) } } } }) }) // register document in documentsDB this._documentsDB.then( db => rtop(db.transaction(['documents'], 'readonly').objectStore('documents').get(roomName)) .then( doc => doc === undefined && rtop(db.transaction(['documents'], 'readwrite').objectStore('documents').add({ roomName, serverUpdateCounter: -1 })) ) ) // open room db and read existing data return room.dbPromise = openDB(roomName) .then(db => { room.db = db const t = room.db.transaction(['updates'], 'readwrite') const updatesStore = t.objectStore('updates') // write current state as update const encoder = new BinaryEncoder() encodeStructsDS(y, encoder) return rtop(updatesStore.put(encoder.createBuffer())).then(() => { // read persisted state return rtop(updatesStore.getAll()).then(updates => { room.mutex(() => { y.transact(() => { updates.forEach(update => { decodePersisted(y, new BinaryDecoder(update)) }) }, true) }) }) }) }) } disconnectY (roomName) { const { db, channel } = this._rooms.get(roomName) db.close() if (channel !== null) { channel.close() } this._rooms.delete(roomName) } /** * Remove all persisted data that belongs to a room. * Automatically destroys all Yjs all Yjs instances that persist to * the room. If `destroyYjsInstances = false` the persistence functionality * will be removed from the Yjs instances. * removePersistedData (roomName, destroyYjsInstances = true) { this.disconnectY(roomName) return rtop(indexedDB.deleteDatabase(roomName)) } } */