Repository navigation
Expand file tree
/
Copy pathcolada.options.ts
More file actions
70 lines (63 loc) · 2.95 KB
/
Copy pathcolada.options.ts
File metadata and controls
70 lines (63 loc) · 2.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import type { PiniaColadaOptions } from '@pinia/colada'
import type { CachePersisterOptions, PersistedQueryCache, PiniaColadaStorage } from '@pinia/colada-plugin-cache-persister'
import { PiniaColadaCachePersister } from '@pinia/colada-plugin-cache-persister'
import { createDebouncedStorage } from './app/utils/debounced-storage'
import { PERSISTED_QUERY_KEY_ROOTS } from './app/utils/query-key-roots'
// Bump to discard persisted caches whose shape is no longer compatible.
const PERSISTED_CACHE_VERSION = 'v2'
const PERSISTED_CACHE_PREFIX = `${PERSISTED_CACHE_VERSION}|`
// The persisted set grows with every book ever browsed (entries are
// deliberately never evicted in-session), so cap what gets written: an
// unbounded blob would eventually hit the localStorage quota — silently
// ending all persistence — and every flush stringifies the whole blob.
const MAX_PERSISTED_ENTRIES_PER_ROOT = 300
function capPersistedQueryCache(cache: PersistedQueryCache): PersistedQueryCache {
const hashesByRoot = new Map<string, string[]>()
for (const keyHash of Object.keys(cache)) {
const root = String(JSON.parse(keyHash)[0])
let hashes = hashesByRoot.get(root)
if (!hashes) hashesByRoot.set(root, hashes = [])
hashes.push(keyHash)
}
const capped: PersistedQueryCache = {}
for (const hashes of hashesByRoot.values()) {
if (hashes.length > MAX_PERSISTED_ENTRIES_PER_ROOT) {
// Serialized entries carry their age (Date.now() - when) at index 2;
// keep the most recently resolved ones.
hashes.sort((a, b) => (cache[a]?.[2] ?? Infinity) - (cache[b]?.[2] ?? Infinity))
hashes.length = MAX_PERSISTED_ENTRIES_PER_ROOT
}
for (const keyHash of hashes) {
capped[keyHash] = cache[keyHash]
}
}
return capped
}
export function createCachePersisterOptions(storage: PiniaColadaStorage): CachePersisterOptions {
return {
key: '3ook-query-cache',
// The plugin serializes the whole allowlisted set before each setItem, so
// coalescing must happen here — the debounced storage below only coalesces
// the final string write. It stays for quota-swallowing and for flushing
// the last produced string on pagehide/visibilitychange.
debounce: 300,
storage,
filter: {
predicate: entry => PERSISTED_QUERY_KEY_ROOTS.includes(String(entry.key[0])),
},
stringify: cache => PERSISTED_CACHE_PREFIX + JSON.stringify(capPersistedQueryCache(cache)),
parse: (stored) => {
// Throwing discards the stored cache (the plugin starts empty and the
// next write overwrites the key with the current version).
if (!stored.startsWith(PERSISTED_CACHE_PREFIX)) {
throw new Error('Persisted query cache version mismatch')
}
return JSON.parse(stored.slice(PERSISTED_CACHE_PREFIX.length))
},
}
}
export default {
plugins: import.meta.client
? [PiniaColadaCachePersister(createCachePersisterOptions(createDebouncedStorage(localStorage)))]
: [],
} satisfies PiniaColadaOptions