UNPKG

y-cap-sqlite

Version:

y-cap-sqlite is a persistence provider for [Yjs](https://github.com/yjs/yjs) that uses [@capacitor-community/sqlite](https://github.com/capacitor-community/sqlite) to store document changes in a SQLite database. This is useful for Capacitor projects that

99 lines (87 loc) 3.2 kB
import * as Y from "yjs"; import * as promise from "lib0/promise"; import { ObservableV2 } from "lib0/observable"; import { capConnectionOptions, SQLiteConnection, SQLiteDBConnection } from "@capacitor-community/sqlite"; import { Buffer } from "buffer"; const DB_PREFIX = "cap-sqlite-persistence-"; export class CapSQLitePersistence extends ObservableV2<{ synced: (persistence: CapSQLitePersistence) => void; }> { doc: Y.Doc; config: Omit<capConnectionOptions, "database">; whenSynced: Promise<this>; name: string; db: SQLiteDBConnection | undefined; con: SQLiteConnection synced: boolean = false; _dbref = 0; _dbsize = 0; _destroyed = false; constructor(config: Omit<capConnectionOptions, "database">, doc: Y.Doc, name: string, sqlite: SQLiteConnection) { super(); this.doc = doc; this.config = config; this.name = name; this.con = sqlite this.whenSynced = promise.create((resolve) => this.on("synced", () => resolve(this)) ); this.destroy = this.destroy.bind(this); this._storeUpdate.bind(this); doc.on("update", this._storeUpdate.bind(this)); doc.on("destroy", this.destroy.bind(this)); } async initDb() { try { const dbName = DB_PREFIX + this.name console.log((await this.con.isConnection(dbName, this.config.readonly ?? false)).result) if ((await this.con.isConnection(dbName, this.config.readonly ?? false)).result) { this.db = await this.con.retrieveConnection(dbName, this.config.readonly ?? false) } else { this.db = await this.con.createConnection(dbName, this.config.encrypted ?? false, this.config.mode ?? "", this.config.version ?? 1, this.config.readonly ?? false) } await this.db.open() await this.db.execute("CREATE TABLE IF NOT EXISTS updates (id INTEGER PRIMARY KEY, content BLOB)") const s = Y.encodeStateAsUpdate(this.doc); if (s.length > 0) { this._storeUpdate(s, null); } const { values } = await this.db.query("SELECT content FROM updates") if (values != undefined) { for (const row of values) { Y.applyUpdate(this.doc, new Uint8Array(row.content)); } } this.synced = true; this.emit("synced", [this]); console.log("initialisation finished") } catch (e) { console.log("error loading update", e) throw e; } } static async create(config: Omit<capConnectionOptions, "database">, doc: Y.Doc, name: string, sqlite: SQLiteConnection) { const c = new CapSQLitePersistence(config, doc, name, sqlite) await c.initDb() return c; } _storeUpdate(update: Uint8Array, origin: any) { if (!this.db || origin == this) { !this.db && console.error("trying to store update without db"); return; } return this.db .run(`INSERT INTO updates (content) VALUES (?)`, [Buffer.from(update)]) .catch((e) => console.error("error storing update", e)); } destroy() { this.doc.off("update", this._storeUpdate); this.doc.off("destroy", this.destroy); this._destroyed = true; return this.db?.close().catch((e) => { console.log("can't close", e) }); } }