UNPKG

y-op-sqlite

Version:

<p align="center"> <img src="https://cdn.malts.me/6Wu0Fj.svg" /> </p>

1 lines 18.9 kB
{"version":3,"file":"y-op-sqlite.mjs","sources":["../node_modules/.pnpm/lib0@0.2.93/node_modules/lib0/promise.js","../node_modules/.pnpm/lib0@0.2.93/node_modules/lib0/map.js","../node_modules/.pnpm/lib0@0.2.93/node_modules/lib0/set.js","../node_modules/.pnpm/lib0@0.2.93/node_modules/lib0/array.js","../node_modules/.pnpm/lib0@0.2.93/node_modules/lib0/observable.js","../src/index.ts"],"sourcesContent":["/**\n * Utility helpers to work with promises.\n *\n * @module promise\n */\n\nimport * as time from './time.js'\n\n/**\n * @template T\n * @callback PromiseResolve\n * @param {T|PromiseLike<T>} [result]\n */\n\n/**\n * @template T\n * @param {function(PromiseResolve<T>,function(Error):void):any} f\n * @return {Promise<T>}\n */\nexport const create = f => /** @type {Promise<T>} */ (new Promise(f))\n\n/**\n * @param {function(function():void,function(Error):void):void} f\n * @return {Promise<void>}\n */\nexport const createEmpty = f => new Promise(f)\n\n/**\n * `Promise.all` wait for all promises in the array to resolve and return the result\n * @template {unknown[] | []} PS\n *\n * @param {PS} ps\n * @return {Promise<{ -readonly [P in keyof PS]: Awaited<PS[P]> }>}\n */\nexport const all = Promise.all.bind(Promise)\n\n/**\n * @param {Error} [reason]\n * @return {Promise<never>}\n */\nexport const reject = reason => Promise.reject(reason)\n\n/**\n * @template T\n * @param {T|void} res\n * @return {Promise<T|void>}\n */\nexport const resolve = res => Promise.resolve(res)\n\n/**\n * @template T\n * @param {T} res\n * @return {Promise<T>}\n */\nexport const resolveWith = res => Promise.resolve(res)\n\n/**\n * @todo Next version, reorder parameters: check, [timeout, [intervalResolution]]\n *\n * @param {number} timeout\n * @param {function():boolean} check\n * @param {number} [intervalResolution]\n * @return {Promise<void>}\n */\nexport const until = (timeout, check, intervalResolution = 10) => create((resolve, reject) => {\n const startTime = time.getUnixTime()\n const hasTimeout = timeout > 0\n const untilInterval = () => {\n if (check()) {\n clearInterval(intervalHandle)\n resolve()\n } else if (hasTimeout) {\n /* c8 ignore else */\n if (time.getUnixTime() - startTime > timeout) {\n clearInterval(intervalHandle)\n reject(new Error('Timeout'))\n }\n }\n }\n const intervalHandle = setInterval(untilInterval, intervalResolution)\n})\n\n/**\n * @param {number} timeout\n * @return {Promise<undefined>}\n */\nexport const wait = timeout => create((resolve, reject) => setTimeout(resolve, timeout))\n\n/**\n * Checks if an object is a promise using ducktyping.\n *\n * Promises are often polyfilled, so it makes sense to add some additional guarantees if the user of this\n * library has some insane environment where global Promise objects are overwritten.\n *\n * @param {any} p\n * @return {boolean}\n */\nexport const isPromise = p => p instanceof Promise || (p && p.then && p.catch && p.finally)\n","/**\n * Utility module to work with key-value stores.\n *\n * @module map\n */\n\n/**\n * Creates a new Map instance.\n *\n * @function\n * @return {Map<any, any>}\n *\n * @function\n */\nexport const create = () => new Map()\n\n/**\n * Copy a Map object into a fresh Map object.\n *\n * @function\n * @template K,V\n * @param {Map<K,V>} m\n * @return {Map<K,V>}\n */\nexport const copy = m => {\n const r = create()\n m.forEach((v, k) => { r.set(k, v) })\n return r\n}\n\n/**\n * Get map property. Create T if property is undefined and set T on map.\n *\n * ```js\n * const listeners = map.setIfUndefined(events, 'eventName', set.create)\n * listeners.add(listener)\n * ```\n *\n * @function\n * @template {Map<any, any>} MAP\n * @template {MAP extends Map<any,infer V> ? function():V : unknown} CF\n * @param {MAP} map\n * @param {MAP extends Map<infer K,any> ? K : unknown} key\n * @param {CF} createT\n * @return {ReturnType<CF>}\n */\nexport const setIfUndefined = (map, key, createT) => {\n let set = map.get(key)\n if (set === undefined) {\n map.set(key, set = createT())\n }\n return set\n}\n\n/**\n * Creates an Array and populates it with the content of all key-value pairs using the `f(value, key)` function.\n *\n * @function\n * @template K\n * @template V\n * @template R\n * @param {Map<K,V>} m\n * @param {function(V,K):R} f\n * @return {Array<R>}\n */\nexport const map = (m, f) => {\n const res = []\n for (const [key, value] of m) {\n res.push(f(value, key))\n }\n return res\n}\n\n/**\n * Tests whether any key-value pairs pass the test implemented by `f(value, key)`.\n *\n * @todo should rename to some - similarly to Array.some\n *\n * @function\n * @template K\n * @template V\n * @param {Map<K,V>} m\n * @param {function(V,K):boolean} f\n * @return {boolean}\n */\nexport const any = (m, f) => {\n for (const [key, value] of m) {\n if (f(value, key)) {\n return true\n }\n }\n return false\n}\n\n/**\n * Tests whether all key-value pairs pass the test implemented by `f(value, key)`.\n *\n * @function\n * @template K\n * @template V\n * @param {Map<K,V>} m\n * @param {function(V,K):boolean} f\n * @return {boolean}\n */\nexport const all = (m, f) => {\n for (const [key, value] of m) {\n if (!f(value, key)) {\n return false\n }\n }\n return true\n}\n","/**\n * Utility module to work with sets.\n *\n * @module set\n */\n\nexport const create = () => new Set()\n\n/**\n * @template T\n * @param {Set<T>} set\n * @return {Array<T>}\n */\nexport const toArray = set => Array.from(set)\n\n/**\n * @template T\n * @param {Set<T>} set\n * @return {T}\n */\nexport const first = set =>\n set.values().next().value || undefined\n\n/**\n * @template T\n * @param {Iterable<T>} entries\n * @return {Set<T>}\n */\nexport const from = entries => new Set(entries)\n","/**\n * Utility module to work with Arrays.\n *\n * @module array\n */\n\nimport * as set from './set.js'\n\n/**\n * Return the last element of an array. The element must exist\n *\n * @template L\n * @param {ArrayLike<L>} arr\n * @return {L}\n */\nexport const last = arr => arr[arr.length - 1]\n\n/**\n * @template C\n * @return {Array<C>}\n */\nexport const create = () => /** @type {Array<C>} */ ([])\n\n/**\n * @template D\n * @param {Array<D>} a\n * @return {Array<D>}\n */\nexport const copy = a => /** @type {Array<D>} */ (a.slice())\n\n/**\n * Append elements from src to dest\n *\n * @template M\n * @param {Array<M>} dest\n * @param {Array<M>} src\n */\nexport const appendTo = (dest, src) => {\n for (let i = 0; i < src.length; i++) {\n dest.push(src[i])\n }\n}\n\n/**\n * Transforms something array-like to an actual Array.\n *\n * @function\n * @template T\n * @param {ArrayLike<T>|Iterable<T>} arraylike\n * @return {T}\n */\nexport const from = Array.from\n\n/**\n * True iff condition holds on every element in the Array.\n *\n * @function\n * @template ITEM\n * @template {ArrayLike<ITEM>} ARR\n *\n * @param {ARR} arr\n * @param {function(ITEM, number, ARR):boolean} f\n * @return {boolean}\n */\nexport const every = (arr, f) => {\n for (let i = 0; i < arr.length; i++) {\n if (!f(arr[i], i, arr)) {\n return false\n }\n }\n return true\n}\n\n/**\n * True iff condition holds on some element in the Array.\n *\n * @function\n * @template S\n * @template {ArrayLike<S>} ARR\n * @param {ARR} arr\n * @param {function(S, number, ARR):boolean} f\n * @return {boolean}\n */\nexport const some = (arr, f) => {\n for (let i = 0; i < arr.length; i++) {\n if (f(arr[i], i, arr)) {\n return true\n }\n }\n return false\n}\n\n/**\n * @template ELEM\n *\n * @param {ArrayLike<ELEM>} a\n * @param {ArrayLike<ELEM>} b\n * @return {boolean}\n */\nexport const equalFlat = (a, b) => a.length === b.length && every(a, (item, index) => item === b[index])\n\n/**\n * @template ELEM\n * @param {Array<Array<ELEM>>} arr\n * @return {Array<ELEM>}\n */\nexport const flatten = arr => fold(arr, /** @type {Array<ELEM>} */ ([]), (acc, val) => acc.concat(val))\n\n/**\n * @template T\n * @param {number} len\n * @param {function(number, Array<T>):T} f\n * @return {Array<T>}\n */\nexport const unfold = (len, f) => {\n const array = new Array(len)\n for (let i = 0; i < len; i++) {\n array[i] = f(i, array)\n }\n return array\n}\n\n/**\n * @template T\n * @template RESULT\n * @param {Array<T>} arr\n * @param {RESULT} seed\n * @param {function(RESULT, T, number):RESULT} folder\n */\nexport const fold = (arr, seed, folder) => arr.reduce(folder, seed)\n\nexport const isArray = Array.isArray\n\n/**\n * @template T\n * @param {Array<T>} arr\n * @return {Array<T>}\n */\nexport const unique = arr => from(set.from(arr))\n\n/**\n * @template T\n * @template M\n * @param {ArrayLike<T>} arr\n * @param {function(T):M} mapper\n * @return {Array<T>}\n */\nexport const uniqueBy = (arr, mapper) => {\n /**\n * @type {Set<M>}\n */\n const happened = set.create()\n /**\n * @type {Array<T>}\n */\n const result = []\n for (let i = 0; i < arr.length; i++) {\n const el = arr[i]\n const mapped = mapper(el)\n if (!happened.has(mapped)) {\n happened.add(mapped)\n result.push(el)\n }\n }\n return result\n}\n\n/**\n * @template {ArrayLike<any>} ARR\n * @template {function(ARR extends ArrayLike<infer T> ? T : never, number, ARR):any} MAPPER\n * @param {ARR} arr\n * @param {MAPPER} mapper\n * @return {Array<MAPPER extends function(...any): infer M ? M : never>}\n */\nexport const map = (arr, mapper) => {\n /**\n * @type {Array<any>}\n */\n const res = Array(arr.length)\n for (let i = 0; i < arr.length; i++) {\n res[i] = mapper(/** @type {any} */ (arr[i]), i, /** @type {any} */ (arr))\n }\n return /** @type {any} */ (res)\n}\n","/**\n * Observable class prototype.\n *\n * @module observable\n */\n\nimport * as map from './map.js'\nimport * as set from './set.js'\nimport * as array from './array.js'\n\n/**\n * Handles named events.\n * @experimental\n *\n * This is basically a (better typed) duplicate of Observable, which will replace Observable in the\n * next release.\n *\n * @template {{[key in keyof EVENTS]: function(...any):void}} EVENTS\n */\nexport class ObservableV2 {\n constructor () {\n /**\n * Some desc.\n * @type {Map<string, Set<any>>}\n */\n this._observers = map.create()\n }\n\n /**\n * @template {keyof EVENTS & string} NAME\n * @param {NAME} name\n * @param {EVENTS[NAME]} f\n */\n on (name, f) {\n map.setIfUndefined(this._observers, /** @type {string} */ (name), set.create).add(f)\n return f\n }\n\n /**\n * @template {keyof EVENTS & string} NAME\n * @param {NAME} name\n * @param {EVENTS[NAME]} f\n */\n once (name, f) {\n /**\n * @param {...any} args\n */\n const _f = (...args) => {\n this.off(name, /** @type {any} */ (_f))\n f(...args)\n }\n this.on(name, /** @type {any} */ (_f))\n }\n\n /**\n * @template {keyof EVENTS & string} NAME\n * @param {NAME} name\n * @param {EVENTS[NAME]} f\n */\n off (name, f) {\n const observers = this._observers.get(name)\n if (observers !== undefined) {\n observers.delete(f)\n if (observers.size === 0) {\n this._observers.delete(name)\n }\n }\n }\n\n /**\n * Emit a named event. All registered event listeners that listen to the\n * specified name will receive the event.\n *\n * @todo This should catch exceptions\n *\n * @template {keyof EVENTS & string} NAME\n * @param {NAME} name The event name.\n * @param {Parameters<EVENTS[NAME]>} args The arguments that are applied to the event listener.\n */\n emit (name, args) {\n // copy all listeners to an array first to make sure that no event is emitted to listeners that are subscribed while the event handler is called.\n return array.from((this._observers.get(name) || map.create()).values()).forEach(f => f(...args))\n }\n\n destroy () {\n this._observers = map.create()\n }\n}\n\n/* c8 ignore start */\n/**\n * Handles named events.\n *\n * @deprecated\n * @template N\n */\nexport class Observable {\n constructor () {\n /**\n * Some desc.\n * @type {Map<N, any>}\n */\n this._observers = map.create()\n }\n\n /**\n * @param {N} name\n * @param {function} f\n */\n on (name, f) {\n map.setIfUndefined(this._observers, name, set.create).add(f)\n }\n\n /**\n * @param {N} name\n * @param {function} f\n */\n once (name, f) {\n /**\n * @param {...any} args\n */\n const _f = (...args) => {\n this.off(name, _f)\n f(...args)\n }\n this.on(name, _f)\n }\n\n /**\n * @param {N} name\n * @param {function} f\n */\n off (name, f) {\n const observers = this._observers.get(name)\n if (observers !== undefined) {\n observers.delete(f)\n if (observers.size === 0) {\n this._observers.delete(name)\n }\n }\n }\n\n /**\n * Emit a named event. All registered event listeners that listen to the\n * specified name will receive the event.\n *\n * @todo This should catch exceptions\n *\n * @param {N} name The event name.\n * @param {Array<any>} args The arguments that are applied to the event listener.\n */\n emit (name, args) {\n // copy all listeners to an array first to make sure that no event is emitted to listeners that are subscribed while the event handler is called.\n return array.from((this._observers.get(name) || map.create()).values()).forEach(f => f(...args))\n }\n\n destroy () {\n this._observers = map.create()\n }\n}\n/* c8 ignore end */\n","import * as Y from \"yjs\";\nimport * as promise from \"lib0/promise\";\nimport { ObservableV2 } from \"lib0/observable\";\nimport { open, OPSQLiteConnection } from \"@op-engineering/op-sqlite\";\n\nexport const PREFERRED_TRIM_SIZE = 500;\n\nconst DB_PREFIX = \"opssqlite-persistence-\";\n\nexport class OPSQLitePersistence extends ObservableV2<{\n synced: (persistence: OPSQLitePersistence) => void;\n}> {\n doc: Y.Doc;\n name: string;\n whenSynced: Promise<this>;\n db: OPSQLiteConnection;\n synced: boolean = false;\n _dbref = 0;\n _dbsize = 0;\n _destroyed = false;\n\n constructor(name: string, doc: Y.Doc) {\n super();\n this.doc = doc;\n this.name = name;\n\n this.whenSynced = promise.create((resolve) =>\n this.on(\"synced\", () => resolve(this))\n );\n\n this.db = open({ name: DB_PREFIX + name + \".sqlite\" });\n this.db\n .executeAsync(\n \"CREATE TABLE IF NOT EXISTS updates (id INTEGER PRIMARY KEY, content BLOB)\"\n )\n .then(() => {\n const s = Y.encodeStateAsUpdate(doc);\n if (s.length > 0) {\n this._storeUpdate(s, null);\n }\n })\n .then(() =>\n this.db\n .executeAsync(\"SELECT content FROM updates\")\n .catch((e) => console.error(\"error loading updates\", e))\n )\n .then((res) => {\n if (res != undefined && res.rows != undefined) {\n for (const row of res.rows._array) {\n Y.applyUpdate(doc, new Uint8Array(row.content));\n }\n }\n\n this.synced = true;\n this.emit(\"synced\", [this]);\n });\n\n this.destroy = this.destroy.bind(this);\n this._storeUpdate.bind(this);\n doc.on(\"update\", this._storeUpdate.bind(this));\n doc.on(\"destroy\", this.destroy.bind(this));\n }\n _storeUpdate(update: Uint8Array, origin: any) {\n if (!this.db || origin == this) {\n !this.db && console.error(\"trying to store update without db\");\n return;\n }\n\n return this.db\n .executeAsync(`INSERT INTO updates (content) VALUES (?)`, [update])\n .catch((e) => console.error(\"error storing update\", e));\n }\n\n destroy() {\n this.doc.off(\"update\", this._storeUpdate);\n this.doc.off(\"destroy\", this.destroy);\n this._destroyed = true;\n return this.db.close();\n }\n}\n"],"names":["create","f","setIfUndefined","map","key","createT","set","from","ObservableV2","map.create","name","map.setIfUndefined","set.create","_f","args","observers","array.from","PREFERRED_TRIM_SIZE","DB_PREFIX","OPSQLitePersistence","doc","__publicField","promise.create","resolve","open","s","Y","e","res","row","update","origin"],"mappings":"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","x_google_ignoreList":[0,1,2,3,4]}