Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/idb-storage-memory-fallback.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'accounts': patch
---

Fell back to in-memory storage in `Storage.idb()` when IndexedDB is unavailable (e.g. React Native/Expo, some SSR), instead of throwing `ReferenceError: indexedDB is not defined` on the first read or write.
24 changes: 24 additions & 0 deletions src/core/Storage.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { expect, test } from 'vp/test'

import * as Storage from './Storage.js'

// In runtimes that expose `window` but not IndexedDB (React Native/Expo, some
// SSR), `idb()` is still selected as the default storage. It must degrade to a
// working in-memory adapter instead of throwing: idb-keyval treats an
// `undefined` store as "use the default store", which opens IndexedDB and
// throws `ReferenceError: indexedDB is not defined`.
test('idb: degrades to in-memory storage when IndexedDB is unavailable', async () => {
expect(typeof indexedDB).toBe('undefined')

const storage = Storage.idb()

await storage.setItem('accounts', ['0xabc'])
expect(await storage.getItem('accounts')).toMatchInlineSnapshot(`
[
"0xabc",
]
`)

await storage.removeItem('accounts')
expect(await storage.getItem('accounts')).toMatchInlineSnapshot(`null`)
})
8 changes: 6 additions & 2 deletions src/core/Storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,13 @@ export declare namespace cookie {
type Options = from.Options
}

/** Creates an IndexedDB-backed storage adapter. Stores raw values (no JSON serialization). */
/** Creates an IndexedDB-backed storage adapter. Stores raw values (no JSON serialization). Falls back to in-memory storage in runtimes without IndexedDB (e.g. React Native, some SSR). */
export function idb(options: idb.Options = {}): Storage {
const store = typeof indexedDB !== 'undefined' ? createStore('tempo', 'store') : undefined
// idb-keyval treats an `undefined` store as "use the default store", which
// opens IndexedDB and throws where it is unavailable — so degrade to memory
// rather than passing `undefined` through.
if (typeof indexedDB === 'undefined') return memory(options)
const store = createStore('tempo', 'store')
const storage = from(
{
async getItem(name) {
Expand Down