feat(02-03): purge every cached resource on logout
Demo images / Build & push demo images (push) Successful in 3m34s
Demo images / Build & push demo images (push) Successful in 3m34s
- resources.ts: clearAll() drops memory entries, in-flight/revalidator/ invalidate-timer bookkeeping, and every resource:-prefixed sessionStorage key; a generation counter stops an in-flight fetch that resolves after clearAll from repopulating memory or sessionStorage (T-02-02) - auth.ts: logout() calls clearAll() in the finally path so a failed server-side logout still leaves no cached payload behind locally - resourcesClear.test.ts: covers all five required behaviors plus the generation-guard fix Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
a9a20039eb
commit
f44b8ac78c
@@ -56,6 +56,11 @@ export const useResourcesStore = defineStore('resources', () => {
|
||||
const inflight = new Map<string, Promise<void>>()
|
||||
const revalidators = new Map<string, Set<() => void>>()
|
||||
const invalidateTimers = new Map<string, ReturnType<typeof setTimeout>>()
|
||||
// Bumped by clearAll() so an in-flight fetch from a just-ended session that
|
||||
// resolves afterward can detect it and skip writing its result — without
|
||||
// this guard the resolving promise would still call writeSnapshot() and
|
||||
// repopulate sessionStorage even though the cache was just purged.
|
||||
let generation = 0
|
||||
|
||||
/** Get (or create) the reactive entry for a key, hydrating from the
|
||||
* sessionStorage snapshot on first sight so revisits after a reload paint
|
||||
@@ -84,17 +89,22 @@ export const useResourcesStore = defineStore('resources', () => {
|
||||
): Promise<void> {
|
||||
const existing = inflight.get(key)
|
||||
if (existing) return existing
|
||||
const startGeneration = generation
|
||||
const e = entry<T>(key, opts.persist ?? true)
|
||||
e.loadState = e.loadState === 'ready' || e.loadState === 'refreshing' ? 'refreshing' : 'loading'
|
||||
const p = (async () => {
|
||||
try {
|
||||
const data = await fetcher()
|
||||
// A clearAll() (logout) ran while this fetch was in flight — drop
|
||||
// the result rather than repopulate a cache that was just purged.
|
||||
if (generation !== startGeneration) return
|
||||
e.data = data
|
||||
e.error = null
|
||||
e.fetchedAt = Date.now()
|
||||
e.loadState = 'ready'
|
||||
if (opts.persist ?? true) writeSnapshot(key, data, e.fetchedAt)
|
||||
} catch (err) {
|
||||
if (generation !== startGeneration) return
|
||||
e.error = err instanceof Error ? err.message : String(err)
|
||||
// Keep last-known data visible; only 'error' when we have nothing.
|
||||
e.loadState = e.data !== null ? 'ready' : 'error'
|
||||
@@ -162,5 +172,29 @@ export const useResourcesStore = defineStore('resources', () => {
|
||||
}
|
||||
}
|
||||
|
||||
return { entries, entry, refresh, invalidate, subscribe, optimistic, evict }
|
||||
/** Purge every cached resource — memory and sessionStorage — so no payload
|
||||
* from this session outlives a logout or identity switch (T-02-02). Cancels
|
||||
* pending invalidate timers and drops in-flight/revalidator bookkeeping
|
||||
* first, so a fetch that resolves after this call cannot repopulate the
|
||||
* cache with data from the ending session. */
|
||||
function clearAll(): void {
|
||||
generation++
|
||||
for (const timer of invalidateTimers.values()) clearTimeout(timer)
|
||||
invalidateTimers.clear()
|
||||
inflight.clear()
|
||||
revalidators.clear()
|
||||
entries.clear()
|
||||
try {
|
||||
const keys: string[] = []
|
||||
for (let i = 0; i < sessionStorage.length; i++) {
|
||||
const k = sessionStorage.key(i)
|
||||
if (k && k.startsWith(SNAPSHOT_PREFIX)) keys.push(k)
|
||||
}
|
||||
for (const k of keys) sessionStorage.removeItem(k)
|
||||
} catch {
|
||||
/* sessionStorage unavailable or inaccessible — memory is already clear */
|
||||
}
|
||||
}
|
||||
|
||||
return { entries, entry, refresh, invalidate, subscribe, optimistic, evict, clearAll }
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user