feat(ui): B5 — /ws/db pushes revalidate cached resources
Some checks failed
Demo images / Build & push demo images (push) Failing after 2m5s

Bridge WebSocket patches into the resource layer: a /peer-health/<onion>
patch invalidates that peer's cloud.peer-browse entry and the federation
node list; /package-data patches invalidate the tor-services list.
invalidate() debounces 800ms and refetches only keys with mounted
subscribers, so patch storms cost one revalidation per key; the 30s
staleness reconciliation remains the backstop for unmapped data.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
archipelago 2026-07-28 06:49:36 -04:00
parent 8907cc47d9
commit 73228114b9

View File

@ -2,9 +2,38 @@
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import type { DataModel } from '../types/api'
import type { DataModel, PatchOperation } from '../types/api'
import { wsClient, applyDataPatch } from '../api/websocket'
import { rpcClient } from '../api/rpc-client'
import { useResourcesStore } from './resources'
/** Unescape one JSON-pointer segment (RFC 6901: ~1 → '/', ~0 → '~'). */
function pointerSegment(path: string, prefix: string): string {
const seg = path.slice(prefix.length).split('/')[0] ?? ''
return seg.replace(/~1/g, '/').replace(/~0/g, '~')
}
/** B5: bridge /ws/db pushes into the cached-resource layer. Each patch op
* maps to the resource keys whose backing data it changes; invalidate()
* debounces (800ms) and only refetches keys with mounted subscribers, so a
* patch storm costs one revalidation per key. The 30s staleness
* reconciliation stays as the backstop for anything unmapped. */
function invalidateResourcesForPatch(patch: PatchOperation[]): void {
const resources = useResourcesStore()
for (const op of patch) {
const path = op.path ?? ''
if (path.startsWith('/peer-health/')) {
// A peer flipping reachability changes both its browse result and the
// federation node list's online state.
const onion = pointerSegment(path, '/peer-health/')
if (onion) resources.invalidate(`cloud.peer-browse:${onion}`)
resources.invalidate('federation.nodes')
} else if (path.startsWith('/package-data/')) {
// App installs/uninstalls add or remove their tor services.
resources.invalidate('server.tor-services')
}
}
}
export const useSyncStore = defineStore('sync', () => {
// State
@ -108,6 +137,7 @@ export const useSyncStore = defineStore('sync', () => {
try {
if (import.meta.env.DEV) console.log('[Store] Applying patch at revision', update.rev || 'unknown')
data.value = applyDataPatch(data.value, update.patch)
invalidateResourcesForPatch(update.patch)
// Mark as connected once we receive any valid patch
if (!isConnected.value) {
isConnected.value = true