hoangsonww / hoangsonww/GhostCache-API-Cache

HTTP-Semantics Aware Caching: ETag/304 Revalidation, `stale-while-revalidate`, Cache Tags & Inspector

Open
#3 0 comments 0 reactions 1 assignee Claimed by @hoangsonww View on GitHub
bug documentation enhancement good first issue help wanted question
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.