diff options
| author | Adam <[email protected]> | 2026-01-06 21:38:08 -0600 |
|---|---|---|
| committer | Adam <[email protected]> | 2026-01-06 21:42:03 -0600 |
| commit | 761863ae355b3ca1e606ea5eb2106772fa763c19 (patch) | |
| tree | f8fef26687875e75f4279b6695e35a33977b728c /packages/desktop | |
| parent | dadc08ddc7f3c9b1a34e6f401a99406b6714b965 (diff) | |
| download | opencode-761863ae355b3ca1e606ea5eb2106772fa763c19.tar.gz opencode-761863ae355b3ca1e606ea5eb2106772fa763c19.zip | |
chore(app): rework storage approach
Diffstat (limited to 'packages/desktop')
| -rw-r--r-- | packages/desktop/src/index.tsx | 149 |
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 } |
