Persist remaining client state in disk cache files
Release rolling / release (push) Has been cancelled

Move templates, notifications, backups, table columns, peer envs, and
sidebar/first-connect prefs onto ~/.config/peardock/cache (settings.json
or dedicated JSON files) with legacy localStorage migration.
This commit is contained in:
Raven Scott
2026-07-17 17:41:11 -04:00
parent 9da9345e32
commit ec96356855
11 changed files with 990 additions and 185 deletions
+102
View File
@@ -0,0 +1,102 @@
/**
* File-backed JSON cache under ~/.config/peardock/cache/
*/
import test from 'brittle'
import fs from 'fs'
import os from 'os'
import path from 'path'
import {
loadJsonCache,
saveJsonCache,
clearJsonCache,
getCacheFilePath,
parseCachePayload,
buildCachePayload,
JSON_CACHE_VERSION,
} from '../client/jsonCache.js'
function installLocalStorage() {
/** @type {Map<string, string>} */
const map = new Map()
globalThis.localStorage = {
get length() {
return map.size
},
key(i) {
return [...map.keys()][i] ?? null
},
getItem(k) {
return map.has(k) ? map.get(k) : null
},
setItem(k, v) {
map.set(String(k), String(v))
},
removeItem(k) {
map.delete(k)
},
clear() {
map.clear()
},
}
return map
}
function withTempHome(t) {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'peardock-json-cache-'))
const prev = process.env.PEARDOCK_HOME
process.env.PEARDOCK_HOME = tmp
t.teardown(() => {
if (prev === undefined) delete process.env.PEARDOCK_HOME
else process.env.PEARDOCK_HOME = prev
try {
fs.rmSync(tmp, { recursive: true, force: true })
} catch {
// ignore
}
})
return tmp
}
test('build/parse cache envelope', (t) => {
const env = buildCachePayload({ a: 1 })
t.is(env.version, JSON_CACHE_VERSION)
t.ok(env.updatedAt)
t.alike(env.data, { a: 1 })
t.alike(parseCachePayload(JSON.stringify(env)), { a: 1 })
t.alike(parseCachePayload({ flat: true }), { flat: true })
t.alike(parseCachePayload([1, 2]), [1, 2])
})
test('save and load json cache file', (t) => {
withTempHome(t)
installLocalStorage()
const r = saveJsonCache('templates', { web: { image: 'nginx' } })
t.ok(r.ok)
t.ok(fs.existsSync(getCacheFilePath('templates')))
const loaded = loadJsonCache('templates', { fallback: {} })
t.alike(loaded, { web: { image: 'nginx' } })
})
test('migrates legacy localStorage key into cache file', (t) => {
withTempHome(t)
const ls = installLocalStorage()
ls.set('peardock_templates', JSON.stringify({ old: { image: 'busybox' } }))
const loaded = loadJsonCache('templates', {
fallback: {},
legacyLocalStorageKey: 'peardock_templates',
})
t.alike(loaded, { old: { image: 'busybox' } })
t.ok(fs.existsSync(getCacheFilePath('templates')))
})
test('clearJsonCache removes file', (t) => {
withTempHome(t)
installLocalStorage()
saveJsonCache('notifications', [{ id: '1' }])
t.ok(fs.existsSync(getCacheFilePath('notifications')))
clearJsonCache('notifications', { legacyLocalStorageKey: 'peardock_notifications' })
t.absent(fs.existsSync(getCacheFilePath('notifications')))
})