11import type { UseStore } from 'idb-keyval' ;
2- import { set , keys , getMany , setMany , get , clear , del , delMany , promisifyRequest } from 'idb-keyval' ;
2+ import * as IDB from 'idb-keyval' ;
33import utils from '../../../utils' ;
44import type StorageProvider from '../types' ;
55import type { OnyxKey , OnyxValue } from '../../../types' ;
66import createStore from './createStore' ;
77
8- // We don't want to initialize the store while the JS bundle loads as idb-keyval will try to use global.indexedDB
9- // which might not be available in certain environments that load the bundle (e.g. electron main process).
10- let idbKeyValStore : UseStore ;
118const DB_NAME = 'OnyxDB' ;
129const STORE_NAME = 'keyvaluepairs' ;
1310
14- const provider : StorageProvider = {
11+ const provider : StorageProvider < UseStore | undefined > = {
12+ // We don't want to initialize the store while the JS bundle loads as idb-keyval will try to use global.indexedDB
13+ // which might not be available in certain environments that load the bundle (e.g. electron main process).
14+ store : undefined ,
1515 /**
1616 * The name of the provider that can be printed to the logs
1717 */
@@ -25,71 +25,120 @@ const provider: StorageProvider = {
2525 if ( newIdbKeyValStore == null ) {
2626 throw Error ( 'IDBKeyVal store could not be created' ) ;
2727 }
28-
29- idbKeyValStore = newIdbKeyValStore ;
28+ provider . store = newIdbKeyValStore ;
3029 } ,
3130
32- setItem : ( key , value ) => {
31+ setItem ( key , value ) {
32+ if ( ! provider . store ) {
33+ throw new Error ( 'Store not initialized!' ) ;
34+ }
35+
3336 if ( value === null ) {
34- provider . removeItem ( key ) ;
37+ return provider . removeItem ( key ) ;
38+ }
39+
40+ return IDB . set ( key , value , provider . store ) ;
41+ } ,
42+ multiGet ( keysParam ) {
43+ if ( ! provider . store ) {
44+ throw new Error ( 'Store not initialized!' ) ;
3545 }
3646
37- return set ( key , value , idbKeyValStore ) ;
47+ return IDB . getMany ( keysParam , provider . store ) . then ( ( values ) => values . map ( ( value , index ) => [ keysParam [ index ] , value ] ) ) ;
3848 } ,
39- multiGet : ( keysParam ) => getMany ( keysParam , idbKeyValStore ) . then ( ( values ) => values . map ( ( value , index ) => [ keysParam [ index ] , value ] ) ) ,
40- multiMerge : ( pairs ) =>
41- idbKeyValStore ( 'readwrite' , ( store ) => {
49+ multiMerge ( pairs ) {
50+ if ( ! provider . store ) {
51+ throw new Error ( 'Store not initialized!' ) ;
52+ }
53+
54+ return provider . store ( 'readwrite' , ( store ) => {
4255 // Note: we are using the manual store transaction here, to fit the read and update
4356 // of the items in one transaction to achieve best performance.
44- const getValues = Promise . all ( pairs . map ( ( [ key ] ) => promisifyRequest < OnyxValue < OnyxKey > > ( store . get ( key ) ) ) ) ;
57+ const getValues = Promise . all ( pairs . map ( ( [ key ] ) => IDB . promisifyRequest < OnyxValue < OnyxKey > > ( store . get ( key ) ) ) ) ;
4558
4659 return getValues . then ( ( values ) => {
47- const pairsWithoutNull = pairs . filter ( ( [ key , value ] ) => {
60+ for ( const [ index , [ key , value ] ] of pairs . entries ( ) ) {
4861 if ( value === null ) {
49- provider . removeItem ( key ) ;
50- return false ;
51- }
52-
53- return true ;
54- } ) ;
62+ store . delete ( key ) ;
63+ } else {
64+ const newValue = utils . fastMerge ( values [ index ] as Record < string , unknown > , value as Record < string , unknown > , {
65+ shouldRemoveNestedNulls : true ,
66+ objectRemovalMode : 'replace' ,
67+ } ) . result ;
5568
56- const upsertMany = pairsWithoutNull . map ( ( [ key , value ] , index ) => {
57- const prev = values [ index ] ;
58- const newValue = utils . fastMerge ( prev as Record < string , unknown > , value as Record < string , unknown > , {
59- shouldRemoveNestedNulls : true ,
60- objectRemovalMode : 'replace' ,
61- } ) . result ;
69+ store . put ( newValue , key ) ;
70+ }
71+ }
6272
63- return promisifyRequest ( store . put ( newValue , key ) ) ;
64- } ) ;
65- return Promise . all ( upsertMany ) ;
73+ return IDB . promisifyRequest ( store . transaction ) ;
6674 } ) ;
67- } ) . then ( ( ) => undefined ) ,
75+ } ) ;
76+ } ,
6877 mergeItem ( key , change ) {
6978 // Since Onyx already merged the existing value with the changes, we can just set the value directly.
7079 return provider . multiMerge ( [ [ key , change ] ] ) ;
7180 } ,
72- multiSet : ( pairs ) => {
73- const pairsWithoutNull = pairs . filter ( ( [ key , value ] ) => {
74- if ( value === null ) {
75- provider . removeItem ( key ) ;
76- return false ;
81+ multiSet ( pairs ) {
82+ if ( ! provider . store ) {
83+ throw new Error ( 'Store not initialized!' ) ;
84+ }
85+
86+ return provider . store ( 'readwrite' , ( store ) => {
87+ for ( const [ key , value ] of pairs ) {
88+ if ( value === null ) {
89+ store . delete ( key ) ;
90+ } else {
91+ store . put ( value , key ) ;
92+ }
7793 }
7894
79- return true ;
80- } ) as Array < [ IDBValidKey , unknown ] > ;
95+ return IDB . promisifyRequest ( store . transaction ) ;
96+ } ) ;
97+ } ,
98+ clear ( ) {
99+ if ( ! provider . store ) {
100+ throw new Error ( 'Store not initialized!' ) ;
101+ }
102+
103+ return IDB . clear ( provider . store ) ;
104+ } ,
105+ getAllKeys ( ) {
106+ if ( ! provider . store ) {
107+ throw new Error ( 'Store not initialized!' ) ;
108+ }
109+
110+ return IDB . keys ( provider . store ) ;
111+ } ,
112+ getItem ( key ) {
113+ if ( ! provider . store ) {
114+ throw new Error ( 'Store not initialized!' ) ;
115+ }
81116
82- return setMany ( pairsWithoutNull , idbKeyValStore ) ;
117+ return (
118+ IDB . get ( key , provider . store )
119+ // idb-keyval returns undefined for missing items, but this needs to return null so that idb-keyval does the same thing as SQLiteStorage.
120+ . then ( ( val ) => ( val === undefined ? null : val ) )
121+ ) ;
122+ } ,
123+ removeItem ( key ) {
124+ if ( ! provider . store ) {
125+ throw new Error ( 'Store not initialized!' ) ;
126+ }
127+
128+ return IDB . del ( key , provider . store ) ;
129+ } ,
130+ removeItems ( keysParam ) {
131+ if ( ! provider . store ) {
132+ throw new Error ( 'Store not initialized!' ) ;
133+ }
134+
135+ return IDB . delMany ( keysParam , provider . store ) ;
83136 } ,
84- clear : ( ) => clear ( idbKeyValStore ) ,
85- getAllKeys : ( ) => keys ( idbKeyValStore ) ,
86- getItem : ( key ) =>
87- get ( key , idbKeyValStore )
88- // idb-keyval returns undefined for missing items, but this needs to return null so that idb-keyval does the same thing as SQLiteStorage.
89- . then ( ( val ) => ( val === undefined ? null : val ) ) ,
90- removeItem : ( key ) => del ( key , idbKeyValStore ) ,
91- removeItems : ( keysParam ) => delMany ( keysParam , idbKeyValStore ) ,
92137 getDatabaseSize ( ) {
138+ if ( ! provider . store ) {
139+ throw new Error ( 'Store is not initialized!' ) ;
140+ }
141+
93142 if ( ! window . navigator || ! window . navigator . storage ) {
94143 throw new Error ( 'StorageManager browser API unavailable' ) ;
95144 }
0 commit comments