@@ -16,6 +16,9 @@ type PersistTarget = {
1616
1717const LEGACY_STORAGE = "default.dat"
1818const GLOBAL_STORAGE = "opencode.global.dat"
19+ const LOCAL_PREFIX = "opencode."
20+ const fallback = { disabled : false }
21+ const cache = new Map < string , string > ( )
1922
2023function quota ( error : unknown ) {
2124 if ( error instanceof DOMException ) {
@@ -40,20 +43,55 @@ function quota(error: unknown) {
4043 return false
4144}
4245
46+ type Evict = { key : string ; size : number }
47+
48+ function evict ( storage : Storage , keep : string , value : string ) {
49+ const total = storage . length
50+ const indexes = Array . from ( { length : total } , ( _ , index ) => index )
51+ const items : Evict [ ] = [ ]
52+
53+ for ( const index of indexes ) {
54+ const name = storage . key ( index )
55+ if ( ! name ) continue
56+ if ( ! name . startsWith ( LOCAL_PREFIX ) ) continue
57+ if ( name === keep ) continue
58+ const stored = storage . getItem ( name )
59+ items . push ( { key : name , size : stored ?. length ?? 0 } )
60+ }
61+
62+ items . sort ( ( a , b ) => b . size - a . size )
63+
64+ for ( const item of items ) {
65+ storage . removeItem ( item . key )
66+
67+ try {
68+ storage . setItem ( keep , value )
69+ return true
70+ } catch ( error ) {
71+ if ( ! quota ( error ) ) throw error
72+ }
73+ }
74+
75+ return false
76+ }
77+
4378function write ( storage : Storage , key : string , value : string ) {
4479 try {
4580 storage . setItem ( key , value )
46- return
81+ return true
4782 } catch ( error ) {
4883 if ( ! quota ( error ) ) throw error
4984 }
5085
5186 try {
5287 storage . removeItem ( key )
5388 storage . setItem ( key , value )
89+ return true
5490 } catch ( error ) {
5591 if ( ! quota ( error ) ) throw error
5692 }
93+
94+ return evict ( storage , key , value )
5795}
5896
5997function snapshot ( value : unknown ) {
@@ -108,17 +146,64 @@ function localStorageWithPrefix(prefix: string): SyncStorage {
108146 const base = `${ prefix } :`
109147 const item = ( key : string ) => base + key
110148 return {
111- getItem : ( key ) => localStorage . getItem ( item ( key ) ) ,
112- setItem : ( key , value ) => write ( localStorage , item ( key ) , value ) ,
113- removeItem : ( key ) => localStorage . removeItem ( item ( key ) ) ,
149+ getItem : ( key ) => {
150+ const name = item ( key )
151+ const cached = cache . get ( name )
152+ if ( fallback . disabled && cached !== undefined ) return cached
153+
154+ const stored = localStorage . getItem ( name )
155+ if ( stored === null ) return cached ?? null
156+ cache . set ( name , stored )
157+ return stored
158+ } ,
159+ setItem : ( key , value ) => {
160+ const name = item ( key )
161+ cache . set ( name , value )
162+ if ( fallback . disabled ) return
163+ try {
164+ if ( write ( localStorage , name , value ) ) return
165+ } catch {
166+ fallback . disabled = true
167+ return
168+ }
169+ fallback . disabled = true
170+ } ,
171+ removeItem : ( key ) => {
172+ const name = item ( key )
173+ cache . delete ( name )
174+ if ( fallback . disabled ) return
175+ localStorage . removeItem ( name )
176+ } ,
114177 }
115178}
116179
117180function localStorageDirect ( ) : SyncStorage {
118181 return {
119- getItem : ( key ) => localStorage . getItem ( key ) ,
120- setItem : ( key , value ) => write ( localStorage , key , value ) ,
121- removeItem : ( key ) => localStorage . removeItem ( key ) ,
182+ getItem : ( key ) => {
183+ const cached = cache . get ( key )
184+ if ( fallback . disabled && cached !== undefined ) return cached
185+
186+ const stored = localStorage . getItem ( key )
187+ if ( stored === null ) return cached ?? null
188+ cache . set ( key , stored )
189+ return stored
190+ } ,
191+ setItem : ( key , value ) => {
192+ cache . set ( key , value )
193+ if ( fallback . disabled ) return
194+ try {
195+ if ( write ( localStorage , key , value ) ) return
196+ } catch {
197+ fallback . disabled = true
198+ return
199+ }
200+ fallback . disabled = true
201+ } ,
202+ removeItem : ( key ) => {
203+ cache . delete ( key )
204+ if ( fallback . disabled ) return
205+ localStorage . removeItem ( key )
206+ } ,
122207 }
123208}
124209
0 commit comments