From c013c513aaad0a9a9e6ff0f0b3d4672e97cac02f Mon Sep 17 00:00:00 2001 From: dfahlander Date: Sun, 28 Jul 2024 11:58:31 +0200 Subject: [PATCH] Made it possible to reach the Y.Doc cache from addons or outside dexie: * To lookup whether a certain document is open and find it --- src/public/index.d.ts | 9 ++++++++ src/yjs/DexieYProvider.ts | 14 ++++++------ src/yjs/createYDocProperty.ts | 27 +++++++++++------------ src/yjs/docCache.ts | 40 +++++++++++++++++++++++++++++++---- 4 files changed, 65 insertions(+), 25 deletions(-) diff --git a/src/public/index.d.ts b/src/public/index.d.ts index 1b245d963..e00df1a5d 100644 --- a/src/public/index.d.ts +++ b/src/public/index.d.ts @@ -72,8 +72,17 @@ export function remove(num: number | bigint | any[]): PropModification; declare var DexieYProvider: { (doc: DucktypedYDoc): DexieYProvider; new (doc: DucktypedYDoc): DexieYProvider; + getDocCache: (db: Dexie) => { + cache: { [key: string]: WeakRef }; + readonly size: number; + find: (updatesTable: string, parentId: any) => DucktypedYDoc | undefined; + add: (doc: DucktypedYDoc) => void; + delete: (doc: DucktypedYDoc) => void; + }; } +export { DexieYProvider, RangeSet }; + /** Exporting 'Dexie' as the default export. **/ export default Dexie; diff --git a/src/yjs/DexieYProvider.ts b/src/yjs/DexieYProvider.ts index 88f85935b..90af496d3 100644 --- a/src/yjs/DexieYProvider.ts +++ b/src/yjs/DexieYProvider.ts @@ -4,17 +4,17 @@ import type { DexieYProvider, DucktypedYDoc, } from '../public/types/yjs-related'; -import { throwIfDestroyed } from './docCache'; +import { throwIfDestroyed, getDocCache } from './docCache'; import { getYLibrary } from './getYLibrary'; import { observeYDocUpdates } from './observeYDocUpdates'; export function DexieYProvider (doc: DucktypedYDoc): DexieYProvider { - const { guid, collectionid: updatesTable, meta: { db, table }} = + const { meta: { db, parentTable, parentId, updatesTable }} = (doc as DucktypedYDoc) || {}; - if (!db || !table || !updatesTable) + if (!db || !parentTable || !updatesTable) throw new Error('Y.Doc not generated by Dexie'); - if (!db.table(table) || !db.table(updatesTable)) { - throw new Error(`Table ${table} or ${updatesTable} not found in db`); + if (!db.table(parentTable) || !db.table(updatesTable)) { + throw new Error(`Table ${parentTable} or ${updatesTable} not found in db`); } throwIfDestroyed(doc); const Y = getYLibrary(db); @@ -41,9 +41,11 @@ export function DexieYProvider (doc: DucktypedYDoc): DexieYProvider { on = this.on = createEvents(); // Releases listeners for GC } }; - const stopObserving = observeYDocUpdates(provider, doc, db, table, updatesTable, guid, Y); + const stopObserving = observeYDocUpdates(provider, doc, db, parentTable, updatesTable, parentId, Y); doc.on('destroy', provider.destroy.bind(provider)); db.on.y.fire(provider, Y); // Allow for addons to invoke their sync- and awareness providers here. return provider; } + +DexieYProvider.getDocCache = getDocCache; diff --git a/src/yjs/createYDocProperty.ts b/src/yjs/createYDocProperty.ts index c9079efaf..c18412cd1 100644 --- a/src/yjs/createYDocProperty.ts +++ b/src/yjs/createYDocProperty.ts @@ -2,40 +2,37 @@ import type { Table } from '../public/types/table'; import type { Dexie } from '../public/types/dexie'; import type { DexieYDocMeta, DucktypedY } from '../public/types/yjs-related'; import { getByKeyPath } from '../functions/utils'; -import { docCache, destroyed, registry } from './docCache'; +import { destroyed, getDocCache } from './docCache'; export function createYDocProperty( db: Dexie, Y: DucktypedY, table: Table, - prop: string, updatesTable: string ) { const pkKeyPath = table.schema.primKey.keyPath; + const docCache = getDocCache(db); return { get(this: object) { const id = getByKeyPath(this, pkKeyPath); - const cacheKey = `${table.name}[${id}].${prop}`; - let docRef = docCache[cacheKey]; - if (docRef) return docRef.deref(); - const doc = new Y.Doc({ - collectionid: updatesTable, - guid: ''+id, + let doc = docCache.find(updatesTable, id); + if (doc) return doc; + + doc = new Y.Doc({ meta: { db, - table: table.name, - cacheKey, - } as DexieYDocMeta, + updatesTable, + parentTable: table.name, + parentId: id + } satisfies DexieYDocMeta, }); - docCache[cacheKey] = new WeakRef(doc); - registry.register(doc, cacheKey); + docCache.add(doc); doc.on('destroy', () => { destroyed.add(doc); - registry.unregister(doc); - delete docCache[cacheKey]; + docCache.delete(doc); }); return doc; diff --git a/src/yjs/docCache.ts b/src/yjs/docCache.ts index ca26eb8f2..62f8abeea 100644 --- a/src/yjs/docCache.ts +++ b/src/yjs/docCache.ts @@ -1,10 +1,38 @@ +import { Dexie } from '../public/types/dexie'; import type { DucktypedYDoc } from '../public/types/yjs-related'; -// The cache -export let docCache: { [key: string]: WeakRef; } = {}; +// The Y.Doc cache containing all active documents +export function getDocCache(db: Dexie) { + return db._novip['_docCache'] ??= { + cache: {} as { [key: string]: WeakRef; }, + get size() { + return Object.keys(this.cache).length; + }, + find(updatesTable: string, parentId: any): DucktypedYDoc | undefined { + const cacheKey = getYDocCacheKey(updatesTable, parentId); + const docRef = this.cache[cacheKey]; + return docRef ? docRef.deref() : undefined; + }, + add(doc: DucktypedYDoc): void { + const { updatesTable, parentId } = doc.meta; + if (!updatesTable || parentId == null) + throw new Error(`Missing Dexie-related metadata in Y.Doc`); + const cacheKey = getYDocCacheKey(updatesTable, parentId); + this.cache[cacheKey] = new WeakRef(doc); + docRegistry.register(doc, { cache: this.cache, key: cacheKey }); + }, + delete(doc: DucktypedYDoc): void { + docRegistry.unregister(doc); + delete this.cache[ + getYDocCacheKey(doc.meta.updatesTable, doc.meta.parentId) + ]; + }, + }; +} +//export let docCache: { [key: string]: WeakRef; } = {}; // The finalization registry -export const registry = new FinalizationRegistry((heldValue) => { - delete docCache[heldValue]; +const docRegistry = new FinalizationRegistry<{cache: any, key: string}>(({cache, key}) => { + delete cache[key]; }); // The weak map //export const doc2ProviderWeakMap = new WeakMap>>(); @@ -14,3 +42,7 @@ export function throwIfDestroyed(doc: object) { if (destroyed.has(doc)) throw new Error('Y.Doc has been destroyed'); } + +export function getYDocCacheKey(yTable: string, parentId: any): string { + return `${yTable}[${parentId}]`; +}