diff options
| author | Adam Malczewski <[email protected]> | 2026-04-10 00:13:45 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-04-10 00:13:45 +0900 |
| commit | 328b962572e4decb5280541c6d01495af440799d (patch) | |
| tree | 9aab35db3b7f7b1820f5a05736badb744afb3939 /src/lib | |
| parent | b650a2ede779c5f2e84412bf30d9c182d92e83f6 (diff) | |
| download | flashair-speedsync-328b962572e4decb5280541c6d01495af440799d.tar.gz flashair-speedsync-328b962572e4decb5280541c6d01495af440799d.zip | |
better debug info
Diffstat (limited to 'src/lib')
| -rw-r--r-- | src/lib/cache/imageCache.ts | 27 | ||||
| -rw-r--r-- | src/lib/components/CacheDebug.svelte | 59 |
2 files changed, 83 insertions, 3 deletions
diff --git a/src/lib/cache/imageCache.ts b/src/lib/cache/imageCache.ts index 2326974..5ff7393 100644 --- a/src/lib/cache/imageCache.ts +++ b/src/lib/cache/imageCache.ts @@ -109,6 +109,10 @@ async function idbGet(key: CacheKey): Promise<StoredRecord | undefined> { }); } +/** Last write error (if any) — exposed via getStats() for diagnostics. */ +let lastWriteError: string | undefined; +let writeErrorCount = 0; + /** Write a record to IndexedDB (best-effort). */ async function idbPut(record: StoredRecord): Promise<void> { const db = await openDb(); @@ -119,9 +123,22 @@ async function idbPut(record: StoredRecord): Promise<void> { const tx = db.transaction(STORE_NAME, 'readwrite'); const store = tx.objectStore(STORE_NAME); store.put(record); - tx.oncomplete = () => resolve(); - tx.onerror = () => resolve(); - } catch { + tx.oncomplete = () => { + lastWriteError = undefined; + resolve(); + }; + tx.onerror = () => { + const msg = tx.error?.message ?? 'unknown write error'; + lastWriteError = msg; + writeErrorCount++; + console.warn(`[imageCache] IDB write failed: ${msg}`); + resolve(); + }; + } catch (e) { + const msg = e instanceof Error ? e.message : String(e); + lastWriteError = msg; + writeErrorCount++; + console.warn(`[imageCache] IDB write exception: ${msg}`); resolve(); } }); @@ -280,6 +297,8 @@ export const imageCache = { idbEntries: number; idbBytes: number; idbError: string | undefined; + idbLastWriteError: string | undefined; + idbWriteErrorCount: number; }> { let fullCount = 0; let thumbCount = 0; @@ -332,6 +351,8 @@ export const imageCache = { idbEntries, idbBytes, idbError, + idbLastWriteError: lastWriteError, + idbWriteErrorCount: writeErrorCount, }; }, diff --git a/src/lib/components/CacheDebug.svelte b/src/lib/components/CacheDebug.svelte index 293ff62..09e859d 100644 --- a/src/lib/components/CacheDebug.svelte +++ b/src/lib/components/CacheDebug.svelte @@ -9,6 +9,8 @@ idbEntries: number; idbBytes: number; idbError: string | undefined; + idbLastWriteError: string | undefined; + idbWriteErrorCount: number; } let stats = $state<CacheStats | undefined>(undefined); @@ -24,11 +26,44 @@ return `${val.toFixed(1)} ${sizes[i]}`; } + let storageEstimate = $state<{ usage: number; quota: number } | undefined>(undefined); + let storageError = $state<string | undefined>(undefined); + async function refresh() { refreshing = true; try { stats = await imageCache.getStats(); } catch { /* ignore */ } + + // Try standard Storage API first, then webkit fallback + storageEstimate = undefined; + storageError = undefined; + try { + if (navigator.storage && navigator.storage.estimate) { + const est = await navigator.storage.estimate(); + storageEstimate = { usage: est.usage ?? 0, quota: est.quota ?? 0 }; + } else if ('webkitTemporaryStorage' in navigator) { + // Chrome HTTP fallback + const wk = (navigator as Record<string, unknown>)['webkitTemporaryStorage'] as + { queryUsageAndQuota: (ok: (u: number, q: number) => void, err: (e: unknown) => void) => void } | undefined; + if (wk !== undefined) { + const result = await new Promise<{ usage: number; quota: number }>((resolve, reject) => { + wk.queryUsageAndQuota( + (usage, quota) => resolve({ usage, quota }), + (err) => reject(err), + ); + }); + storageEstimate = result; + } else { + storageError = 'Storage API not available (HTTP origin)'; + } + } else { + storageError = 'Storage API not available'; + } + } catch (e) { + storageError = e instanceof Error ? e.message : String(e); + } + refreshing = false; } @@ -65,6 +100,12 @@ {:else} <div>Entries: <span class="text-info">{stats.idbEntries}</span></div> <div>Size: <span class="text-info">{formatBytes(stats.idbBytes)}</span></div> + {#if stats.idbWriteErrorCount > 0} + <div class="text-error mt-1">❌ {stats.idbWriteErrorCount} write errors</div> + {#if stats.idbLastWriteError !== undefined} + <div class="text-error text-[10px] break-all">{stats.idbLastWriteError}</div> + {/if} + {/if} <div class="mt-1"> {#if stats.idbEntries === stats.entries} <span class="text-success">✅ In sync with memory</span> @@ -76,6 +117,24 @@ </div> {/if} </div> + <!-- Storage Quota --> + <div class="border-t border-base-300 pt-2"> + <div class="font-semibold text-accent">Storage Quota</div> + {#if storageEstimate !== undefined} + <div>Used: <span class="text-info">{formatBytes(storageEstimate.usage)}</span> / {formatBytes(storageEstimate.quota)}</div> + {#if storageEstimate.quota > 0} + {@const pct = (storageEstimate.usage / storageEstimate.quota * 100)} + <div> + <progress class="progress progress-primary w-full h-2" value={pct} max="100"></progress> + <span class:text-error={pct > 90} class:text-warning={pct > 70 && pct <= 90}>{pct.toFixed(1)}%</span> + </div> + {/if} + {:else if storageError !== undefined} + <div class="text-warning">{storageError}</div> + {:else} + <div class="text-base-content/50">Querying…</div> + {/if} + </div> </div> {:else if !collapsed} <div class="text-base-content/50"> |
