diff --git a/.changeset/review-indexeddb.md b/.changeset/review-indexeddb.md new file mode 100644 index 00000000..6f2afa90 --- /dev/null +++ b/.changeset/review-indexeddb.md @@ -0,0 +1,6 @@ +--- +"@react-native-async-storage/async-storage": patch +--- + +Propagate open/transaction failures through caller promises, wait for deletion commit, close blocking connections and discard terminated connections. + diff --git a/packages/async-storage/src/web-module/IndexedDBConnectionRegistry.ts b/packages/async-storage/src/web-module/IndexedDBConnectionRegistry.ts index 529d75b6..8016496c 100644 --- a/packages/async-storage/src/web-module/IndexedDBConnectionRegistry.ts +++ b/packages/async-storage/src/web-module/IndexedDBConnectionRegistry.ts @@ -30,36 +30,37 @@ class IndexedDBConnectionRegistry { db.createObjectStore(WebStorageTableName); } }, - blocked: ( - currentVersion: number, - blockedVersion: number | null, - _: IDBVersionChangeEvent - ) => { + blocked: (currentVersion: number, blockedVersion: number | null) => { throw AsyncStorageError.jsError( `New version (${blockedVersion}) is blocked by current one (${currentVersion})`, AsyncStorageError.Type.WebStorageError ); }, - blocking: ( - currentVersion: number, - blockedVersion: number | null, - _: IDBVersionChangeEvent - ) => { + blocking: (currentVersion: number, blockedVersion: number | null) => { + void db.then((connection) => connection.close()); + if (this.registry.get(dbName) === db) { + this.registry.delete(dbName); + } throw AsyncStorageError.jsError( `Current db version (${currentVersion}) is blocking upgrade to next version (${blockedVersion})`, AsyncStorageError.Type.WebStorageError ); }, + terminated: () => { + if (this.registry.get(dbName) === db) { + this.registry.delete(dbName); + } + }, + }).catch((err) => { + // Return the rejection to callers while allowing a later open to retry. + if (this.registry.get(dbName) === db) { + this.registry.delete(dbName); + } + throw err; }); this.registry.set(dbName, db); - // in case of error while opening, clear the storage to retry - db.catch((err) => { - this.registry.delete(dbName); - throw err; - }); - return db; } } diff --git a/packages/async-storage/src/web-module/IndexedDBStorage.ts b/packages/async-storage/src/web-module/IndexedDBStorage.ts index 1bd16bbc..d90d7df2 100644 --- a/packages/async-storage/src/web-module/IndexedDBStorage.ts +++ b/packages/async-storage/src/web-module/IndexedDBStorage.ts @@ -12,14 +12,15 @@ class IndexedDBStorage { const tx = db.transaction(registry.TableName, "readonly"); const store = tx.objectStore(registry.TableName); - const result = await Promise.all( - keys.map(async (key) => { - const entry = await store.get(key); - return { key, value: entry ?? null }; - }) - ); - - await tx.done; + const [result] = await Promise.all([ + Promise.all( + keys.map(async (key) => { + const entry = await store.get(key); + return { key, value: entry ?? null }; + }) + ), + tx.done, + ]); return result; } catch (e: any) { throw this.createError(e); @@ -34,14 +35,15 @@ class IndexedDBStorage { const tx = db.transaction(registry.TableName, "readwrite"); const store = tx.objectStore(registry.TableName); - const result = await Promise.all( - values.map(async (entry) => { - await store.put(entry.value, entry.key); - return { key: entry.key, value: entry.value }; - }) - ); - - await tx.done; + const [result] = await Promise.all([ + Promise.all( + values.map(async (entry) => { + await store.put(entry.value, entry.key); + return { key: entry.key, value: entry.value }; + }) + ), + tx.done, + ]); return result; } catch (e: any) { throw this.createError(e); @@ -54,11 +56,7 @@ class IndexedDBStorage { const tx = db.transaction(registry.TableName, "readwrite"); const store = tx.objectStore(registry.TableName); - await Promise.all( - keys.map(async (key) => { - await store.delete(key); - }) - ); + await Promise.all([tx.done, ...keys.map((key) => store.delete(key))]); } catch (e: any) { throw this.createError(e); }