hoangsonww / hoangsonww/GhostCache-API-Cache
HTTP-Semantics Aware Caching: ETag/304 Revalidation, `stale-while-revalidate`, Cache Tags & Inspector
- Dominant language
- TypeScript
- Stars
- 11
- Forks
- 7
- PR merge metrics
- No merged PRs in 30d
Description
# HTTP-Semantics Aware Caching: ETag/304 Revalidation, `stale-while-revalidate`, Cache Tags & Inspector
**Summary**
Upgrade GhostCache from time-only TTL to full **HTTP-aware** caching: parse and honor `Cache-Control`, support conditional requests (ETag/Last-Modified → 304), add `stale-while-revalidate`/`stale-if-error`, introduce **cache tags** for targeted invalidation, and ship a tiny **DevTools-style inspector** API for visibility.
---
## Motivation
Many APIs expose validators and caching hints (ETag, `Last-Modified`, `Cache-Control`, `Vary`). Today we rely primarily on TTL. Supporting HTTP semantics improves correctness, reduces bandwidth, and enables instant UI while background-refreshing.
---
## Goals
1. **Conditional Revalidation**
* Store ETag / Last-Modified from responses.
* On refresh, send `If-None-Match` / `If-Modified-Since`; treat 304 as a fast cache hit (update age/headers).
2. **`Cache-Control` Parsing**
* Respect `max-age`, `s-maxage`, `no-store`, `no-cache`, `must-revalidate`, `stale-while-revalidate`, `stale-if-error`.
* Normalize with existing `ttl` (explicit `ttl` overrides headers unless `respectHeaders: true`).
3. **Stale While Revalidate (SWR)**
* Serve stale immediately when within `stale-while-revalidate` window, then refresh in background and update store; expose a hook/callback for “refreshed” events.
4. **Stale-If-Error**
* If network/5xx during refresh and stale exists within `stale-if-error`, serve stale and surface a soft warning event/metric.
5. **Cache Key Normalization**
* Option to include/exclude query params, headers, and method; support a custom `keyFn(req)` to unify semantically identical requests.
6. **Cache Tags & Batch Invalidation**
* Allow setting `tags: string[]` per entry; expose `invalidateByTag("user:42")` to drop related keys (useful after POST/PUT/DELETE).
7. **Inspector / DevTools Hooks**
* Optional tiny overlay API:
* `getEntry(key)`, `listEntries({tag})`, `stats()` (hits/misses/revalidations).
* Event hooks: `onHit`, `onMiss`, `onRevalidate`, `onInvalidate`.
8. **Axios & fetch Parity**
* Implement revalidation and header handling for both adapters.
---
## Non-Goals
* Full HTTP proxy behavior or full RFC 9111 edge cases.
* Persisted header normalization across all custom adapters beyond documented subset.
---
## API Proposal
```ts
enableGhostCache({
ttl: 60_000,
respectHeaders: true, // NEW: honor Cache-Control / validators
swr: true, // NEW: enable stale-while-revalidate behavior
keyFn: (req) => string, // NEW: optional custom cache key builder
});
setCache("user/42", data, { tags: ["user:42"] }); // NEW: tags
invalidateByTag("user:42"); // NEW
const info = GhostCacheInspector.getEntry(key); // NEW
GhostCacheInspector.on("revalidate", (e) => { ... }); // NEW
```
---
## Design Notes
* **Storage Layout**: extend stored metadata to `{ body, status, headers, etag, lastModified, cacheControl, tags, createdAt, lastValidatedAt }`.
* **Revalidation Flow**:
1. Lookup entry; if fresh → hit.
2. If stale and `swr` → return stale immediately; kick off background fetch with `If-None-Match` / `If-Modified-Since`.
3. On 304 → update timestamps/headers; on 200 → replace body/headers.
4. On error and `stale-if-error` window → serve stale, emit event.
* **Key Normalization**: default includes method + URL + sorted query; exclude transient headers unless `varyHeaders: ["Accept-Language"]` is set.
* **Security/Privacy**: never cache `Authorization`-bearing responses unless `cacheAuth: true` and `private: true` are set; scrub PII headers from stored metadata by default.
---
## Acceptance Criteria
* [ ] ETag & Last-Modified captured and used for conditional requests with 304 handling.
* [ ] `Cache-Control` directives respected when `respectHeaders: true`.
* [ ] SWR path serves stale immediately and refreshes in background; events emitted.
* [ ] `stale-if-error` returns stale on refresh failure within window.
* [ ] Cache tags supported; `invalidateByTag()` removes affected entries across adapters.
* [ ] Inspector API exposes `getEntry`, `listEntries`, `stats`, and event hooks.
* [ ] Works with both `fetch` and `axios`; 95%+ unit test coverage for new logic.
* [ ] README updated with examples and migration notes.
---
## Testing Plan
* Unit tests covering:
* 200 → store validators; later 304 → metadata refresh only.
* `max-age`/`no-cache`/SWR/`stale-if-error` branches.
* Tag invalidation correctness.
* Key normalization edge cases (query order, header variations).
* Integration tests:
* Axios and fetch parity using a mock HTTP server returning ETag/Last-Modified and varying directives.
* Performance:
* Ensure overhead for header parsing and background revalidation is negligible (<1ms average per call in Node 18+).
Contributor guide
Assessment
This issue has not been assessed yet.