@@ -21,6 +21,14 @@ const filterObject = (object: Record<string, unknown>, regex: string | RegExp) =
21
21
const reverseFilterObject = ( object : Record < string , unknown > , regex : string | RegExp ) =>
22
22
Object . fromEntries ( Object . entries ( object ) . filter ( ( [ key ] ) => ! ( typeof regex === 'string' ? new RegExp ( regex ) : regex ) . test ( key ) ) ) ;
23
23
24
+ /**
25
+ * This function is used to get the total size of the local storage.
26
+ * @param storage The storage area to get the size of.
27
+ */
28
+ const getLocalStorageSize = ( storage = window . localStorage ) => {
29
+ return Object . entries ( storage ) . reduce ( ( acc , [ key , value ] ) => acc + key . length + value . length , 0 ) ;
30
+ } ;
31
+
24
32
/**
25
33
* This function is used to wrap the storage areas to provide type inference and a more convenient interface.
26
34
* @param area The storage area to wrap.
@@ -53,6 +61,7 @@ export const storageWrapper = (area: chrome.storage.StorageArea, name: string) =
53
61
54
62
window . trakt = { ...window . trakt , [ name ] : storage } ;
55
63
return {
64
+ getBytesInUse : async ( ) : Promise < number > => getLocalStorageSize ( window . localStorage ) ,
56
65
getAll : async < T > ( regex ?: string | RegExp ) : Promise < T > => ( regex ? filterObject ( storage . values , regex ) : storage . values ) as T ,
57
66
get : async < T > ( key : string ) : Promise < T > => storage . values [ key ] as T ,
58
67
set : async < T > ( key : string , value : T ) : Promise < void > => storage . setItem ( key , value ) ,
@@ -64,6 +73,7 @@ export const storageWrapper = (area: chrome.storage.StorageArea, name: string) =
64
73
} ;
65
74
}
66
75
return {
76
+ getBytesInUse : ( ) : Promise < number > => area . getBytesInUse ( ) ,
67
77
getAll : < T > ( regex ?: string | RegExp ) : Promise < T > => area . get ( ) . then ( data => ( regex ? filterObject ( data , regex ) : data ) as T ) ,
68
78
get : < T > ( key : string ) : Promise < T > => area . get ( key ) . then ( ( { [ key ] : value } ) => value ) ,
69
79
set : < T > ( key : string , value : T ) : Promise < void > => area . set ( { [ key ] : value } ) ,
@@ -92,3 +102,30 @@ export const storage = {
92
102
local : storageWrapper ( localStorage , 'local' ) ,
93
103
session : storageWrapper ( sessionStorage , 'session' ) ,
94
104
} ;
105
+
106
+ export const defaultMaxLocalStorageSize = 10485760 ;
107
+
108
+ export const localCache : < T > ( key : string , value : T , regex ?: string | RegExp ) => Promise < void > = async ( key , value , regex ) => {
109
+ let inUse = await storage . local . getBytesInUse ( ) ;
110
+ const max = globalThis ?. chrome ?. storage ?. local . QUOTA_BYTES ?? defaultMaxLocalStorageSize ;
111
+ const payload = JSON . stringify ( value ) . length ;
112
+
113
+ if ( payload > max ) {
114
+ console . warn ( 'Payload is too large to store in local storage.' , { payload, max, inUse } ) ;
115
+ return Promise . resolve ( ) ;
116
+ }
117
+
118
+ if ( inUse + payload >= max ) {
119
+ console . warn ( 'Local storage is full, clearing cache.' , { payload, max, inUse } ) ;
120
+ if ( regex ) await storage . local . removeAll ( regex ) ;
121
+ else await storage . local . clear ( ) ;
122
+ }
123
+
124
+ inUse = await storage . local . getBytesInUse ( ) ;
125
+ if ( inUse + payload >= max ) {
126
+ console . warn ( 'Local storage is still full, skipping cache.' , { payload, max, inUse } ) ;
127
+ return Promise . resolve ( ) ;
128
+ }
129
+
130
+ return storage . local . set ( key , value ) ;
131
+ } ;
0 commit comments