From c68220166ae1cb3fca865697ecb9cabea6c19c7e Mon Sep 17 00:00:00 2001 From: Alex Yang Date: Sat, 17 Jun 2023 13:58:48 +0800 Subject: [PATCH] feat(y-indexeddb): remove id (#2810) --- packages/workspace/src/local/crud.ts | 2 +- packages/workspace/src/providers/index.ts | 5 +- packages/y-indexeddb/README.md | 16 +++- .../y-indexeddb/src/__tests__/index.spec.ts | 83 +++++++------------ packages/y-indexeddb/src/index.ts | 26 ++++-- packages/y-indexeddb/src/utils.ts | 4 +- 6 files changed, 68 insertions(+), 68 deletions(-) diff --git a/packages/workspace/src/local/crud.ts b/packages/workspace/src/local/crud.ts index 35e1fe99ee..ba03a0c13d 100644 --- a/packages/workspace/src/local/crud.ts +++ b/packages/workspace/src/local/crud.ts @@ -66,7 +66,7 @@ export const CRUD: WorkspaceCRUD = { WorkspaceFlavour.LOCAL ); BlockSuiteWorkspace.Y.applyUpdateV2(blockSuiteWorkspace.doc, binary); - const persistence = createIndexedDBProvider(id, blockSuiteWorkspace.doc); + const persistence = createIndexedDBProvider(blockSuiteWorkspace.doc); persistence.connect(); await persistence.whenSynced.then(() => { persistence.disconnect(); diff --git a/packages/workspace/src/providers/index.ts b/packages/workspace/src/providers/index.ts index baebed7cd1..92447488c7 100644 --- a/packages/workspace/src/providers/index.ts +++ b/packages/workspace/src/providers/index.ts @@ -82,10 +82,7 @@ const createAffineWebSocketProvider = ( const createIndexedDBBackgroundProvider = ( blockSuiteWorkspace: BlockSuiteWorkspace ): LocalIndexedDBBackgroundProvider => { - const indexeddbProvider = create( - blockSuiteWorkspace.id, - blockSuiteWorkspace.doc - ); + const indexeddbProvider = create(blockSuiteWorkspace.doc); const callbacks = new CallbackSet(); return { flavour: 'local-indexeddb-background', diff --git a/packages/y-indexeddb/README.md b/packages/y-indexeddb/README.md index 312172b818..70ee0fa8de 100644 --- a/packages/y-indexeddb/README.md +++ b/packages/y-indexeddb/README.md @@ -1,14 +1,24 @@ # @toeverything/y-indexeddb +## Features + +- persistence data in indexeddb +- sub-documents support +- fully TypeScript + ## Usage ```ts import { createIndexedDBProvider, downloadBinary } from '@toeverything/y-indexeddb'; import * as Y from 'yjs'; -const yDoc = new Y.Doc(); + +const yDoc = new Y.Doc({ + // we use `guid` as unique key + guid: 'my-doc', +}); // sync yDoc with indexedDB -const provider = createIndexedDBProvider('docName', yDoc); +const provider = createIndexedDBProvider(yDoc); provider.connect(); await provider.whenSynced.then(() => { console.log('synced'); @@ -16,7 +26,7 @@ await provider.whenSynced.then(() => { }); // dowload binary data from indexedDB for once -downloadBinary('docName').then(blob => { +downloadBinary(yDoc.guid).then(blob => { if (blob !== false) { Y.applyUpdate(yDoc, blob); } diff --git a/packages/y-indexeddb/src/__tests__/index.spec.ts b/packages/y-indexeddb/src/__tests__/index.spec.ts index 3f4be46077..c4c069d266 100644 --- a/packages/y-indexeddb/src/__tests__/index.spec.ts +++ b/packages/y-indexeddb/src/__tests__/index.spec.ts @@ -70,7 +70,7 @@ afterEach(() => { describe('indexeddb provider', () => { test('connect', async () => { - const provider = createIndexedDBProvider(workspace.id, workspace.doc); + const provider = createIndexedDBProvider(workspace.doc); provider.connect(); await provider.whenSynced; const db = await openDB(rootDBName, dbVersion); @@ -119,11 +119,7 @@ describe('indexeddb provider', () => { }) .register(AffineSchemas) .register(__unstableSchemas); - const provider2 = createIndexedDBProvider( - secondWorkspace.id, - secondWorkspace.doc, - rootDBName - ); + const provider2 = createIndexedDBProvider(secondWorkspace.doc, rootDBName); provider2.connect(); await provider2.whenSynced; expect(Workspace.Y.encodeStateAsUpdate(secondWorkspace.doc)).toEqual( @@ -132,11 +128,7 @@ describe('indexeddb provider', () => { }); test('disconnect suddenly', async () => { - const provider = createIndexedDBProvider( - workspace.id, - workspace.doc, - rootDBName - ); + const provider = createIndexedDBProvider(workspace.doc, rootDBName); const fn = vi.fn(); provider.connect(); provider.disconnect(); @@ -146,11 +138,7 @@ describe('indexeddb provider', () => { }); test('connect and disconnect', async () => { - const provider = createIndexedDBProvider( - workspace.id, - workspace.doc, - rootDBName - ); + const provider = createIndexedDBProvider(workspace.doc, rootDBName); provider.connect(); expect(provider.connected).toBe(true); const p1 = provider.whenSynced; @@ -186,7 +174,7 @@ describe('indexeddb provider', () => { }); test('cleanup', async () => { - const provider = createIndexedDBProvider(workspace.id, workspace.doc); + const provider = createIndexedDBProvider(workspace.doc); provider.connect(); await provider.whenSynced; const db = await openDB(rootDBName, dbVersion); @@ -212,7 +200,7 @@ describe('indexeddb provider', () => { }); test('cleanup when connecting', async () => { - const provider = createIndexedDBProvider(workspace.id, workspace.doc); + const provider = createIndexedDBProvider(workspace.doc); provider.connect(); await expect(() => provider.cleanup()).rejects.toThrowError( CleanupWhenConnectingError @@ -224,11 +212,7 @@ describe('indexeddb provider', () => { test('merge', async () => { setMergeCount(5); - const provider = createIndexedDBProvider( - workspace.id, - workspace.doc, - rootDBName - ); + const provider = createIndexedDBProvider(workspace.doc, rootDBName); provider.connect(); { const page = workspace.createPage({ id: 'page0' }); @@ -247,21 +231,20 @@ describe('indexeddb provider', () => { }); test("data won't be lost", async () => { - const id = uuidv4(); const doc = new Workspace.Y.Doc(); const map = doc.getMap('map'); for (let i = 0; i < 100; i++) { map.set(`${i}`, i); } { - const provider = createIndexedDBProvider(id, doc, rootDBName); + const provider = createIndexedDBProvider(doc, rootDBName); provider.connect(); await provider.whenSynced; provider.disconnect(); } { const newDoc = new Workspace.Y.Doc(); - const provider = createIndexedDBProvider(id, newDoc, rootDBName); + const provider = createIndexedDBProvider(newDoc, rootDBName); provider.connect(); await provider.whenSynced; provider.disconnect(); @@ -280,8 +263,10 @@ describe('indexeddb provider', () => { await persistence.destroy(); } { - const yDoc = new Doc(); - const provider = createIndexedDBProvider('test', yDoc); + const yDoc = new Doc({ + guid: 'test', + }); + const provider = createIndexedDBProvider(yDoc); provider.connect(); await provider.whenSynced; await new Promise(resolve => setTimeout(resolve, 0)); @@ -293,9 +278,11 @@ describe('indexeddb provider', () => { throw new Error('not supported'); }); await expect(indexedDB.databases).rejects.toThrow('not supported'); - const yDoc = new Doc(); + const yDoc = new Doc({ + guid: 'test', + }); expect(indexedDB.databases).toBeCalledTimes(1); - const provider = createIndexedDBProvider('test', yDoc); + const provider = createIndexedDBProvider(yDoc); provider.connect(); await provider.whenSynced; expect(indexedDB.databases).toBeCalledTimes(2); @@ -314,8 +301,10 @@ describe('indexeddb provider', () => { expect(event).toBe('beforeunload'); return oldRemoveEventListener(event, fn, options); }); - const doc = new Doc(); - const provider = createIndexedDBProvider('1', doc); + const doc = new Doc({ + guid: '1', + }); + const provider = createIndexedDBProvider(doc); const map = doc.getMap('map'); map.set('1', 1); provider.connect(); @@ -394,21 +383,25 @@ describe('subDoc', () => { test('basic', async () => { let json1: any, json2: any; { - const doc = new Doc(); + const doc = new Doc({ + guid: 'test', + }); const map = doc.getMap(); const subDoc = new Doc(); subDoc.load(); map.set('1', subDoc); map.set('2', 'test'); - const provider = createIndexedDBProvider('test', doc); + const provider = createIndexedDBProvider(doc); provider.connect(); await provider.whenSynced; provider.disconnect(); json1 = doc.toJSON(); } { - const doc = new Doc(); - const provider = createIndexedDBProvider('test', doc); + const doc = new Doc({ + guid: 'test', + }); + const provider = createIndexedDBProvider(doc); provider.connect(); await provider.whenSynced; const map = doc.getMap(); @@ -427,11 +420,7 @@ describe('subDoc', () => { }); await page0.waitForLoaded(); const { paragraphBlockId: paragraphBlockIdPage1 } = initEmptyPage(page0); - const provider = createIndexedDBProvider( - workspace.id, - workspace.doc, - rootDBName - ); + const provider = createIndexedDBProvider(workspace.doc, rootDBName); provider.connect(); const page1 = workspace.createPage({ id: 'page1', @@ -446,11 +435,7 @@ describe('subDoc', () => { isSSR: true, }); newWorkspace.register(AffineSchemas).register(__unstableSchemas); - const provider = createIndexedDBProvider( - newWorkspace.id, - newWorkspace.doc, - rootDBName - ); + const provider = createIndexedDBProvider(newWorkspace.doc, rootDBName); provider.connect(); await provider.whenSynced; const page0 = newWorkspace.getPage('page0') as Page; @@ -474,11 +459,7 @@ describe('utils', () => { const page = workspace.createPage({ id: 'page0' }); await page.waitForLoaded(); initEmptyPage(page); - const provider = createIndexedDBProvider( - workspace.id, - workspace.doc, - rootDBName - ); + const provider = createIndexedDBProvider(workspace.doc, rootDBName); provider.connect(); await provider.whenSynced; provider.disconnect(); diff --git a/packages/y-indexeddb/src/index.ts b/packages/y-indexeddb/src/index.ts index af7b73e09e..7285bc850e 100644 --- a/packages/y-indexeddb/src/index.ts +++ b/packages/y-indexeddb/src/index.ts @@ -148,10 +148,16 @@ type SubDocsEvent = { loaded: Set; }; +/** + * We use `doc.guid` as the unique key, please make sure it not changes. + */ export const createIndexedDBProvider = ( - id: string, doc: Doc, - dbName: string = DEFAULT_DB_NAME + dbName: string = DEFAULT_DB_NAME, + /** + * In the future, migrate will be removed and there will be a separate function + */ + migrate = true ): IndexedDBProvider => { let resolve: () => void; let reject: (reason?: unknown) => void; @@ -336,15 +342,21 @@ export const createIndexedDBProvider = ( reject = _reject; }); connected = true; - trackDoc(id, doc); + trackDoc(doc.guid, doc); // only the runs `await` below, otherwise the logic is incorrect const db = await dbPromise; - await tryMigrate(db, id, dbName); + if (migrate) { + // Tips: + // this is only backward compatible with the yjs official version of y-indexeddb + await tryMigrate(db, doc.guid, dbName); + } if (!connected) { return; } + + // recursively save all docs into indexeddb const docs: [string, Doc][] = []; - docs.push([id, doc]); + docs.push([doc.guid, doc]); while (docs.length > 0) { const [id, doc] = docs.pop() as [string, Doc]; await saveDocOperation(id, doc); @@ -361,13 +373,13 @@ export const createIndexedDBProvider = ( if (early) { reject(new EarlyDisconnectError()); } - unTrackDoc(id, doc); + unTrackDoc(doc.guid, doc); }, async cleanup() { if (connected) { throw new CleanupWhenConnectingError(); } - await (await dbPromise).delete('workspace', id); + await (await dbPromise).delete('workspace', doc.guid); }, whenSynced: Promise.resolve(), get connected() { diff --git a/packages/y-indexeddb/src/utils.ts b/packages/y-indexeddb/src/utils.ts index be153ba799..516885adb2 100644 --- a/packages/y-indexeddb/src/utils.ts +++ b/packages/y-indexeddb/src/utils.ts @@ -128,7 +128,7 @@ export async function tryMigrate( } export async function downloadBinary( - id: string, + guid: string, dbName = DEFAULT_DB_NAME ): Promise { const dbPromise = openDB(dbName, dbVersion, { @@ -136,7 +136,7 @@ export async function downloadBinary( }); const db = await dbPromise; const t = db.transaction('workspace', 'readonly').objectStore('workspace'); - const doc = await t.get(id); + const doc = await t.get(guid); if (!doc) { return false; } else {