diff --git a/src/persist-plugins/indexeddb.ts b/src/persist-plugins/indexeddb.ts index eefe841d4..578b3c38d 100644 --- a/src/persist-plugins/indexeddb.ts +++ b/src/persist-plugins/indexeddb.ts @@ -6,7 +6,7 @@ import type { PersistOptions, ObservablePersistIndexedDBPluginOptions, } from '@legendapp/state/sync'; -import { isPrimitive, isPromise, observable, setAtPath, when } from '@legendapp/state'; +import { isPrimitive, isPromise, observable, setAtPath, symbolDelete, when } from '@legendapp/state'; const MetadataSuffix = '__legend_metadata'; const PrimitiveName = '__legend_primitive'; @@ -397,7 +397,7 @@ export class ObservablePersistIndexedDB implements ObservablePersistPlugin { return transaction.objectStore(table); } private _setItem(table: string, key: string, value: any, store: IDBObjectStore, config: PersistOptions) { - if (!value) { + if (!value || value === symbolDelete) { if (this.tableData[table]) { delete this.tableData[table][key]; } diff --git a/tests/persist-indexeddb.test.ts b/tests/persist-indexeddb.test.ts index 1b1b261c6..da77ea607 100644 --- a/tests/persist-indexeddb.test.ts +++ b/tests/persist-indexeddb.test.ts @@ -1,5 +1,6 @@ import { IDBFactory } from 'fake-indexeddb'; import 'fake-indexeddb/auto'; +import { symbolDelete } from '../src/globals'; import { observable } from '../src/observable'; import { observablePersistIndexedDB } from '../src/persist-plugins/indexeddb'; import { configureSynced } from '../src/sync/configureSynced'; @@ -371,6 +372,97 @@ describe('Persist IDB', () => { await expectIDB(persistName, []); }); + test('Persist IDB deletes an item when valueAtPath is symbolDelete', async () => { + const persistName = getLocalName(); + const persist = myIndexedDBPlugin as ObservablePersistPlugin; + await persist.initialize!(persistOptions); + + await persist.set( + persistName, + [ + { + path: ['id-1'], + pathTypes: ['object'], + prevAtPath: undefined, + valueAtPath: { id: 'id-1', name: 'Alice' }, + }, + { + path: ['id-2'], + pathTypes: ['object'], + prevAtPath: undefined, + valueAtPath: { id: 'id-2', name: 'Bob' }, + }, + ], + {}, + ); + + await expectIDB(persistName, [ + { id: 'id-1', name: 'Alice' }, + { id: 'id-2', name: 'Bob' }, + ]); + + await persist.set( + persistName, + [ + { + path: ['id-1'], + pathTypes: ['object'], + prevAtPath: { id: 'id-1', name: 'Alice' }, + valueAtPath: symbolDelete, + }, + ], + {}, + ); + + await expectIDB(persistName, [{ id: 'id-2', name: 'Bob' }]); + }); + test('Persist IDB deletes keys marked symbolDelete on full-table set', async () => { + const persistName = getLocalName(); + const persist = myIndexedDBPlugin as ObservablePersistPlugin; + await persist.initialize!(persistOptions); + + await persist.set( + persistName, + [ + { + path: [], + pathTypes: [], + prevAtPath: undefined, + valueAtPath: { + id1: { id: 'id1', name: 'Alice' }, + id2: { id: 'id2', name: 'Bob' }, + }, + }, + ], + {}, + ); + + await expectIDB(persistName, [ + { id: 'id1', name: 'Alice' }, + { id: 'id2', name: 'Bob' }, + ]); + + await persist.set( + persistName, + [ + { + path: [], + pathTypes: [], + prevAtPath: { + id1: { id: 'id1', name: 'Alice' }, + id2: { id: 'id2', name: 'Bob' }, + }, + valueAtPath: { + id1: { id: 'id1', name: 'Alice' }, + id2: symbolDelete, + }, + }, + ], + {}, + ); + + await expectIDB(persistName, [{ id: 'id1', name: 'Alice' }]); + }); test('Persist without a name errors', async () => { const obs = observable>({});