summaryrefslogtreecommitdiffhomepage
path: root/packages/desktop/src
diff options
context:
space:
mode:
authorAdam <[email protected]>2026-01-06 21:38:08 -0600
committerAdam <[email protected]>2026-01-06 21:42:03 -0600
commit761863ae355b3ca1e606ea5eb2106772fa763c19 (patch)
treef8fef26687875e75f4279b6695e35a33977b728c /packages/desktop/src
parentdadc08ddc7f3c9b1a34e6f401a99406b6714b965 (diff)
downloadopencode-761863ae355b3ca1e606ea5eb2106772fa763c19.tar.gz
opencode-761863ae355b3ca1e606ea5eb2106772fa763c19.zip
chore(app): rework storage approach
Diffstat (limited to 'packages/desktop/src')
-rw-r--r--packages/desktop/src/index.tsx149
1 files changed, 109 insertions, 40 deletions
diff --git a/packages/desktop/src/index.tsx b/packages/desktop/src/index.tsx
index 1b822e265..aee2c8249 100644
--- a/packages/desktop/src/index.tsx
+++ b/packages/desktop/src/index.tsx
@@ -60,7 +60,7 @@ const platform: Platform = {
void shellOpen(url).catch(() => undefined)
},
- storage: (name = "default.dat") => {
+ storage: (() => {
type StoreLike = {
get(key: string): Promise<string | null | undefined>
set(key: string, value: string): Promise<unknown>
@@ -70,7 +70,13 @@ const platform: Platform = {
length(): Promise<number>
}
- const memory = () => {
+ const WRITE_DEBOUNCE_MS = 250
+
+ const storeCache = new Map<string, Promise<StoreLike>>()
+ const apiCache = new Map<string, AsyncStorage & { flush: () => Promise<void> }>()
+ const memoryCache = new Map<string, StoreLike>()
+
+ const createMemoryStore = () => {
const data = new Map<string, string>()
const store: StoreLike = {
get: async (key) => data.get(key),
@@ -89,45 +95,108 @@ const platform: Platform = {
return store
}
- const api: AsyncStorage & { _store: Promise<StoreLike> | null; _getStore: () => Promise<StoreLike> } = {
- _store: null,
- _getStore: async () => {
- if (api._store) return api._store
- api._store = Store.load(name).catch(() => memory())
- return api._store
- },
- getItem: async (key: string) => {
- const store = await api._getStore()
- const value = await store.get(key).catch(() => null)
- if (value === undefined) return null
- return value
- },
- setItem: async (key: string, value: string) => {
- const store = await api._getStore()
- await store.set(key, value).catch(() => undefined)
- },
- removeItem: async (key: string) => {
- const store = await api._getStore()
- await store.delete(key).catch(() => undefined)
- },
- clear: async () => {
- const store = await api._getStore()
- await store.clear().catch(() => undefined)
- },
- key: async (index: number) => {
- const store = await api._getStore()
- return (await store.keys().catch(() => []))[index]
- },
- getLength: async () => {
- const store = await api._getStore()
- return await store.length().catch(() => 0)
- },
- get length() {
- return api.getLength()
- },
+ const getStore = (name: string) => {
+ const cached = storeCache.get(name)
+ if (cached) return cached
+
+ const store = Store.load(name).catch(() => {
+ const cached = memoryCache.get(name)
+ if (cached) return cached
+
+ const memory = createMemoryStore()
+ memoryCache.set(name, memory)
+ return memory
+ })
+
+ storeCache.set(name, store)
+ return store
}
- return api
- },
+
+ const createStorage = (name: string) => {
+ const pending = new Map<string, string | null>()
+ let timer: ReturnType<typeof setTimeout> | undefined
+ let flushing: Promise<void> | undefined
+
+ const flush = async () => {
+ if (flushing) return flushing
+
+ flushing = (async () => {
+ const store = await getStore(name)
+ while (pending.size > 0) {
+ const batch = Array.from(pending.entries())
+ pending.clear()
+ for (const [key, value] of batch) {
+ if (value === null) {
+ await store.delete(key).catch(() => undefined)
+ } else {
+ await store.set(key, value).catch(() => undefined)
+ }
+ }
+ }
+ })().finally(() => {
+ flushing = undefined
+ })
+
+ return flushing
+ }
+
+ const schedule = () => {
+ if (timer) return
+ timer = setTimeout(() => {
+ timer = undefined
+ void flush()
+ }, WRITE_DEBOUNCE_MS)
+ }
+
+ const api: AsyncStorage & { flush: () => Promise<void> } = {
+ flush,
+ getItem: async (key: string) => {
+ const next = pending.get(key)
+ if (next !== undefined) return next
+
+ const store = await getStore(name)
+ const value = await store.get(key).catch(() => null)
+ if (value === undefined) return null
+ return value
+ },
+ setItem: async (key: string, value: string) => {
+ pending.set(key, value)
+ schedule()
+ },
+ removeItem: async (key: string) => {
+ pending.set(key, null)
+ schedule()
+ },
+ clear: async () => {
+ pending.clear()
+ const store = await getStore(name)
+ await store.clear().catch(() => undefined)
+ },
+ key: async (index: number) => {
+ const store = await getStore(name)
+ return (await store.keys().catch(() => []))[index]
+ },
+ getLength: async () => {
+ const store = await getStore(name)
+ return await store.length().catch(() => 0)
+ },
+ get length() {
+ return api.getLength()
+ },
+ }
+
+ return api
+ }
+
+ return (name = "default.dat") => {
+ const cached = apiCache.get(name)
+ if (cached) return cached
+
+ const api = createStorage(name)
+ apiCache.set(name, api)
+ return api
+ }
+ })(),
checkUpdate: async () => {
if (!UPDATER_ENABLED) return { updateAvailable: false }