feat(ssr-server): enhance lookup and aggregate handling for SSR cache invalidation

- Improved parsing of `lookup` and `aggregate` options to support JSON strings and arrays.
- Added support for object format in `lookup` and `aggregate` to specify collections.
- Simplified dependency tracking for SSR cache invalidation based on new formats.
This commit is contained in:
2026-05-17 15:16:32 +00:00
parent bd8d413850
commit 349fb9b2da
4 changed files with 2136 additions and 1278 deletions
+10 -4
View File
@@ -78,14 +78,17 @@ When content changes, `clear_cache.js` only invalidates SSR entries that depend
- `col:id` OR `col:*` on `PUT`/`DELETE`
- everything on manual clear (`POST /ssr?clear=1` with no collection context)
## Limit: 1 ensures precise dependencies
By default, an API query for a collection (like `/api/v1/_/content?filter=...`) sets a list dependency `collection:*`. This means *any* change to ANY entry in that collection will clear the SSR cache for this page.
By default, an API query for a collection (like `/api/v1/_/content?filter=...`) sets a list dependency `collection:*`. This means _any_ change to ANY entry in that collection will clear the SSR cache for this page.
If you are querying a single document (like a page or article based on its path or slug), you should ALWAYS append `limit: 1` to your API call (or pass `limit=1` to `getDBEntries`).
If you are querying a single document (like a page or article based on its path or slug), you should ALWAYS append `limit: 1` to your API call (or pass `{ filter: {...}, limit: 1 }` to `getDBEntries`).
When `api/hooks/lib/ssr-server.js` intercepts a request with `limit === 1` and exactly one result is returned, it will register a precise `collection:id` dependency instead of a wildcard `collection:*`. This optimizes the cache drastically, because edits to *other* pages won't invalidate this page.
When `api/hooks/lib/ssr-server.js` intercepts a request with `limit === 1` and exactly one result is returned, it will register a precise `collection:id` dependency instead of a wildcard `collection:*`. This optimizes the cache drastically, because edits to _other_ pages won't invalidate this page.
### Automatic dependency tracking via `lookup` and `aggregate`
When options like `lookup` (e.g. `lookup: "image:medialib"`) or `aggregate` (e.g. `aggregate: "comments:contentId:count"`) are provided to an API call, `ssr-server.js` automatically parses these values and adds wildcard cache dependencies (`medialib:*` or `comments:*`) to the page. This guarantees that if a referenced image or child comment changes, the parent's SSR HTML is correctly flushed.
## How SSR data loading is supposed to work
@@ -181,6 +184,7 @@ if (typeof window === "undefined") {
```
**Why this works:**
- The `tibi-types` package declares `var context: HookContext` as a global (available because goja provides it during SSR).
- During SSR, `loadContent()` runs synchronously (goja transforms `async`/`await`).
- By the time `render(App)` returns in `ssr.ts`, `context.is404` is already `true`.
@@ -254,6 +258,7 @@ $effect(() => { loadData() })
```
**Warum das funktioniert:**
- `loadData()` läuft während der Component-Initialisierung (vor Template-Auswertung)
- `getCachedEntries``apiRequest` → SSR-Pfad → `context.ssrRequest()` → blockierender HTTP-Fetch in goja
- goja's `await` auf einem bereits aufgelösten Promise läuft synchron weiter (kein Microtask-Hickhack)
@@ -275,6 +280,7 @@ pathRewrite: function (path, req) {
Ohne `noCache` wird die erste SSR-Antwort gecached und bei Code-Änderungen nicht invalidiert. Der Entwickler sieht immer den alten Stand. **Immer zuerst den Cache-Bypass prüfen, bevor SSR-Fehler gesucht werden.**
**Erkennungsmerkmale für veralteten SSR-Cache:**
- `X-SSR-Cache: true` im Response-Header
- `<!--COMMENT--><!--SSR.ERROR-->` im HTML
- `__SSR_CACHE__` enthält nicht die erwarteten Daten