-
-
Notifications
You must be signed in to change notification settings - Fork 246
fix:add indexdb storage #1339
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix:add indexdb storage #1339
Changes from all commits
d0fff94
d0b35c4
1528c5c
1550b4d
87e09e8
d0a7fd2
313a861
9c04083
c52a996
57ee31c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,10 @@ | ||
| /** biome-ignore-all lint/suspicious/noExplicitAny: Tests require it */ | ||
| /** biome-ignore-all lint/suspicious/noAsyncPromiseExecutor: ok for tests */ | ||
| /** biome-ignore-all lint/style/noNonNullAssertion: Tests require it */ | ||
| /** | ||
| * Test fixtures and utilities for the Multichain SDK tests | ||
| * This file is excluded from test discovery via vitest.config.ts | ||
| */ | ||
|
|
||
| // Additional imports for standardized setup functions | ||
| import AsyncStorage from '@react-native-async-storage/async-storage'; | ||
| import { JSDOM as Page } from 'jsdom'; | ||
|
|
@@ -19,6 +19,7 @@ import { createMetamaskSDK as createMetamaskSDKWeb } from './index.browser'; | |
| import { createMetamaskSDK as createMetamaskSDKRN } from './index.native'; | ||
| import { createMetamaskSDK as createMetamaskSDKNode } from './index.node'; | ||
| import * as nodeStorage from './store/adapters/node'; | ||
| import * as webStorage from './store/adapters/web'; | ||
| import type { DappClient } from '@metamask/mobile-wallet-protocol-dapp-client'; | ||
|
|
||
| // Mock logger at the top level | ||
|
|
@@ -341,7 +342,6 @@ export const setupWebMocks = (nativeStorageStub: NativeStorageStub, dappUrl = 'h | |
| ...dom.window, | ||
| addEventListener: t.vi.fn(), | ||
| removeEventListener: t.vi.fn(), | ||
| localStorage: nativeStorageStub, | ||
| ethereum: { | ||
| isMetaMask: true, | ||
| }, | ||
|
|
@@ -356,6 +356,24 @@ export const setupWebMocks = (nativeStorageStub: NativeStorageStub, dappUrl = 'h | |
| t.vi.stubGlobal('document', dom.window.document); | ||
| t.vi.stubGlobal('HTMLElement', dom.window.HTMLElement); | ||
| t.vi.stubGlobal('requestAnimationFrame', t.vi.fn()); | ||
| t.vi.spyOn(webStorage, 'StoreAdapterWeb').mockImplementation(() => { | ||
| const __storage = { | ||
| get: t.vi.fn((key: string) => { | ||
| return nativeStorageStub.getItem(key); | ||
| }), | ||
| set: t.vi.fn((key: string, value: string) => { | ||
| return nativeStorageStub.setItem(key, value); | ||
| }), | ||
| delete: t.vi.fn((key: string) => { | ||
| return nativeStorageStub.removeItem(key); | ||
| }), | ||
| platform: 'web' as const, | ||
| get storage() { | ||
| return __storage; | ||
| }, | ||
| } as any; | ||
| return __storage; | ||
| }); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i'm a little confused. Why do we need
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thats true, fake-indexeddb/auto is no longer needed. This mock came after |
||
| }; | ||
|
|
||
| // Helper functions to create standardized test configurations | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,23 +1,92 @@ | ||
| import { StoreAdapter } from '../../domain'; | ||
|
|
||
| type kvStores = 'sdk-kv-store' | 'key-value-pairs'; | ||
|
|
||
| export class StoreAdapterWeb extends StoreAdapter { | ||
| static readonly stores: kvStores[] = ['sdk-kv-store', 'key-value-pairs']; | ||
| static readonly DB_NAME = 'mmsdk'; | ||
|
|
||
| readonly platform = 'web'; | ||
| readonly dbPromise: Promise<IDBDatabase>; | ||
|
|
||
| private get internal() { | ||
| if (typeof window === 'undefined' || !window.localStorage) { | ||
| throw new Error('localStorage is not available in this environment'); | ||
| if (typeof window === 'undefined' || !window.indexedDB) { | ||
| throw new Error('indexedDB is not available in this environment'); | ||
| } | ||
| return window.localStorage; | ||
| return window.indexedDB; | ||
| } | ||
|
|
||
| constructor( | ||
| dbNameSuffix: `-${string}` = '-kv-store', | ||
| private storeName: kvStores = StoreAdapterWeb.stores[0], | ||
| ) { | ||
| super(); | ||
|
|
||
| const dbName = `${StoreAdapterWeb.DB_NAME}${dbNameSuffix}`; | ||
| this.dbPromise = new Promise((resolve, reject) => { | ||
| try { | ||
| const request = this.internal.open(dbName, 1); | ||
| request.onerror = () => reject(new Error('Failed to open IndexedDB.')); | ||
| request.onsuccess = () => resolve(request.result); | ||
| request.onupgradeneeded = () => { | ||
| const db = request.result; | ||
| for (const name of StoreAdapterWeb.stores) { | ||
| if (!db.objectStoreNames.contains(name)) { | ||
| db.createObjectStore(name); | ||
| } | ||
| } | ||
| }; | ||
| } catch (error) { | ||
| reject(error); | ||
| } | ||
| }); | ||
|
cursor[bot] marked this conversation as resolved.
|
||
| } | ||
|
cursor[bot] marked this conversation as resolved.
|
||
|
|
||
| async get(key: string): Promise<string | null> { | ||
| return this.internal.getItem(key); | ||
| const { storeName } = this; | ||
| const db = await this.dbPromise; | ||
| return new Promise((resolve, reject) => { | ||
| try { | ||
| const tx = db.transaction(storeName, 'readonly'); | ||
| const store = tx.objectStore(storeName); | ||
| const request = store.get(key); | ||
| request.onerror = () => reject(new Error('Failed to get value from IndexedDB.')); | ||
| request.onsuccess = () => resolve((request.result as string) ?? null); | ||
| } catch (error) { | ||
| reject(error); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| async set(key: string, value: string): Promise<void> { | ||
| return this.internal.setItem(key, value); | ||
| const { storeName } = this; | ||
| const db = await this.dbPromise; | ||
| return new Promise((resolve, reject) => { | ||
| try { | ||
| const tx = db.transaction(storeName, 'readwrite'); | ||
| const store = tx.objectStore(storeName); | ||
| const request = store.put(value, key); | ||
| request.onerror = () => reject(new Error('Failed to set value in IndexedDB.')); | ||
| request.onsuccess = () => resolve(); | ||
| } catch (error) { | ||
| reject(error); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| async delete(key: string): Promise<void> { | ||
| return this.internal.removeItem(key); | ||
| const { storeName } = this; | ||
| const db = await this.dbPromise; | ||
| return new Promise((resolve, reject) => { | ||
| try { | ||
| const tx = db.transaction(storeName, 'readwrite'); | ||
| const store = tx.objectStore(storeName); | ||
| const request = store.delete(key); | ||
| request.onerror = () => reject(new Error('Failed to delete value from IndexedDB.')); | ||
| request.onsuccess = () => resolve(); | ||
| } catch (error) { | ||
| reject(error); | ||
| } | ||
| }); | ||
|
elribonazo marked this conversation as resolved.
elribonazo marked this conversation as resolved.
|
||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.