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

@@ -884,6 +884,9 @@ def auto_mount_pages(app: Any, service_name: str) -> None:
# Mount IO proxy endpoint for Phase 5: client-side IO primitives
mount_io_endpoint(app, service_name)
# Mount service worker at root scope for offline data layer
mount_service_worker(app)
def mount_pages(bp: Any, service_name: str,
names: set[str] | list[str] | None = None) -> None:
@@ -1186,3 +1189,39 @@ def mount_io_endpoint(app: Any, service_name: str) -> None:
methods=["GET", "POST"],
)
logger.info("Mounted IO proxy for %s: %s", service_name, sorted(_ALLOWED_IO))
# ---------------------------------------------------------------------------
# Service Worker mount
# ---------------------------------------------------------------------------
_SW_MOUNTED = False
def mount_service_worker(app: Any) -> None:
"""Mount the SX service worker at /sx-sw.js (root scope).
The service worker provides offline data caching:
- /sx/data/* and /sx/io/* responses cached in IndexedDB
- /static/* assets cached via Cache API (stale-while-revalidate)
- Everything else passes through to network
"""
global _SW_MOUNTED
if _SW_MOUNTED:
return
_SW_MOUNTED = True
sw_path = os.path.join(
os.path.dirname(__file__), "..", "static", "scripts", "sx-sw.js"
)
sw_path = os.path.abspath(sw_path)
async def serve_sw():
from quart import send_file
return await send_file(
sw_path,
mimetype="application/javascript",
cache_timeout=0, # no caching — SW updates checked by browser
)
app.add_url_rule("/sx-sw.js", endpoint="sx_service_worker", view_func=serve_sw)
logger.info("Mounted service worker at /sx-sw.js")