Phase 7c+7d: cache invalidation + offline data layer

7c: Client data cache management via element attributes
(sx-cache-invalidate) and response headers (SX-Cache-Invalidate,
SX-Cache-Update). Programmatic API: invalidate-page-cache,
invalidate-all-page-cache, update-page-cache.

7d: Service Worker (sx-sw.js) with IndexedDB for offline-capable
data caching. Network-first for /sx/data/ and /sx/io/, stale-while-
revalidate for /static/. Cache invalidation propagates from
in-memory cache to SW via postMessage.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-08 00:45:33 +00:00
parent 9a707dbe56
commit 0ce3f95d6c
7 changed files with 519 additions and 49 deletions

View File

@@ -195,6 +195,10 @@
;; Triggers (before swap)
(dispatch-trigger-events el (get resp-headers "trigger"))
;; Cache directives — process before navigation so cache is
;; ready when the target page loads.
(process-cache-directives el resp-headers text)
(cond
;; Redirect
(get resp-headers "redirect")
@@ -589,6 +593,76 @@
{"data" data "ts" (now-ms)})))
;; --------------------------------------------------------------------------
;; Client-side routing — cache management
;; --------------------------------------------------------------------------
(define invalidate-page-cache
(fn (page-name)
;; Clear cached data for a page. Removes all cache entries whose key
;; matches page-name (exact) or starts with "page-name:" (with params).
;; Also notifies the service worker to clear its IndexedDB entries.
(for-each
(fn (k)
(when (or (= k page-name) (starts-with? k (str page-name ":")))
(dict-set! _page-data-cache k nil)))
(keys _page-data-cache))
(sw-post-message {"type" "invalidate" "page" page-name})
(log-info (str "sx:cache invalidate " page-name))))
(define invalidate-all-page-cache
(fn ()
;; Clear all cached page data and notify service worker.
(set! _page-data-cache (dict))
(sw-post-message {"type" "invalidate" "page" "*"})
(log-info "sx:cache invalidate *")))
(define update-page-cache
(fn (page-name data)
;; Replace cached data for a page with server-provided data.
;; Uses a bare page-name key (no params) — the server knows the
;; canonical data shape for the page.
(let ((cache-key (page-data-cache-key page-name (dict))))
(page-data-cache-set cache-key data)
(log-info (str "sx:cache update " page-name)))))
(define process-cache-directives
(fn (el resp-headers response-text)
;; Process cache invalidation and update directives from both
;; element attributes and response headers.
;;
;; Element attributes (set by component author):
;; sx-cache-invalidate="page-name" — clear page cache on success
;; sx-cache-invalidate="*" — clear all page caches
;;
;; Response headers (set by server):
;; SX-Cache-Invalidate: page-name — clear page cache
;; SX-Cache-Update: page-name — replace cache with response data
;; 1. Element-level invalidation
(let ((el-invalidate (dom-get-attr el "sx-cache-invalidate")))
(when el-invalidate
(if (= el-invalidate "*")
(invalidate-all-page-cache)
(invalidate-page-cache el-invalidate))))
;; 2. Response header invalidation
(let ((hdr-invalidate (get resp-headers "cache-invalidate")))
(when hdr-invalidate
(if (= hdr-invalidate "*")
(invalidate-all-page-cache)
(invalidate-page-cache hdr-invalidate))))
;; 3. Response header cache update (server pushes fresh data)
;; parse-sx-data is a platform-provided function that parses SX text
;; into a data value (returns nil on parse error).
(let ((hdr-update (get resp-headers "cache-update")))
(when hdr-update
(let ((data (parse-sx-data response-text)))
(when data
(update-page-cache hdr-update data)))))))
;; --------------------------------------------------------------------------
;; Client-side routing
;; --------------------------------------------------------------------------
@@ -1061,6 +1135,8 @@
;; (resolve-page-data name params cb) → void; resolves data for a named page.
;; Platform decides transport (HTTP, cache, IPC, etc). Calls (cb data-dict)
;; when data is available. params is a dict of URL/route parameters.
;; (parse-sx-data text) → parsed SX data value, or nil on error.
;; Used by cache update to parse server-provided data in SX format.
;;
;; From boot.sx:
;; _page-routes → list of route entries
@@ -1080,4 +1156,8 @@
;; (csrf-token) → string
;; (cross-origin? url) → boolean
;; (now-ms) → timestamp ms
;;
;; === Cache management ===
;; (parse-sx-data text) → parsed SX data value, or nil on error
;; (sw-post-message msg) → void; post message to active service worker
;; --------------------------------------------------------------------------