134 lines
4.4 KiB
Markdown
134 lines
4.4 KiB
Markdown
# Cache-First Anti-Pattern Audit Instructions
|
|
|
|
## Goal
|
|
|
|
Scan the codebase for places where `ndkFetchEvents` is called in a way that blocks rendering or user-visible UI when cached data could be used instead. Report each finding so a developer can apply the cache-first fix.
|
|
|
|
---
|
|
|
|
## Background
|
|
|
|
Read `docs/cache-first-page-patterns.md` first. It explains the three data APIs:
|
|
|
|
- `queryCache(filters)` — fast, local IndexedDB, 5s timeout
|
|
- `ndkFetchEvents(filters)` — slow, relay network, 15s timeout
|
|
- `subscribe(filters, opts)` — live streaming, no timeout
|
|
|
|
The rule: **never `await ndkFetchEvents(...)` in the critical render path if the data could exist in cache.** Instead, use `queryCache` first, render immediately, then fire-and-forget `ndkFetchEvents` for background hydration.
|
|
|
|
---
|
|
|
|
## What to Search For
|
|
|
|
### Pattern 1: `await ndkFetchEvents(...)` in page HTML files
|
|
|
|
Search all `www/*.html` files for:
|
|
|
|
```
|
|
await ndkFetchEvents(
|
|
```
|
|
|
|
For each match, determine:
|
|
- Is this in the main initialization flow (inside `main()` or the top-level IIFE)?
|
|
- Is there a `queryCache` call before it for the same or similar filter?
|
|
- Could the data plausibly exist in IndexedDB cache (i.e., it was fetched before on a previous page load)?
|
|
|
|
**Report as a finding if**: `await ndkFetchEvents` is called without a preceding `queryCache` for the same data, and the result is used to render UI that the user sees.
|
|
|
|
**Exclude**: Cases where `ndkFetchEvents` is used with `void` (fire-and-forget) or inside a `.then()` chain — those are already non-blocking.
|
|
|
|
### Pattern 2: `await ndkFetchEvents(...)` in shared modules
|
|
|
|
Search all `www/js/*.mjs` files for:
|
|
|
|
```
|
|
await ndkFetchEvents
|
|
```
|
|
|
|
or
|
|
|
|
```
|
|
await ndkFetchEventsFn
|
|
```
|
|
|
|
For each match, determine:
|
|
- Is this function called during page initialization or rendering?
|
|
- Does the module have access to a `queryCache` / `queryCacheFn` function?
|
|
- If not, is `queryCache` being passed through the dependency chain from the page?
|
|
|
|
**Report as a finding if**: A module-level function awaits `ndkFetchEvents` without first trying cache, AND the module does not have `queryCacheFn` available.
|
|
|
|
### Pattern 3: Missing `queryCache` in dependency threading
|
|
|
|
Search all `www/js/*.mjs` files for functions that accept a deps/options object containing `ndkFetchEvents`:
|
|
|
|
```
|
|
ndkFetchEvents
|
|
```
|
|
|
|
in destructuring patterns like `const { ndkFetchEvents, ... } = deps;`
|
|
|
|
For each match, check:
|
|
- Does the same destructuring also include `queryCache`?
|
|
- If the function forwards deps to another module, does it forward `queryCache` too?
|
|
|
|
**Report as a finding if**: A function destructures `ndkFetchEvents` from a deps object but does NOT also destructure `queryCache`.
|
|
|
|
### Pattern 4: Pages that import `ndkFetchEvents` but not `queryCache`
|
|
|
|
Search all `www/*.html` files for import statements from `init-ndk.mjs`:
|
|
|
|
```
|
|
import {
|
|
```
|
|
|
|
Check whether the import includes both `ndkFetchEvents` and `queryCache`.
|
|
|
|
**Report as a finding if**: A page imports `ndkFetchEvents` but does NOT import `queryCache`.
|
|
|
|
---
|
|
|
|
## Files to Scan
|
|
|
|
### HTML pages (check all):
|
|
- `www/*.html`
|
|
|
|
### JavaScript modules (check all):
|
|
- `www/js/*.mjs`
|
|
|
|
### Specifically check these known integration points:
|
|
- `www/js/post-interactions.mjs` — embed hydration, interaction fetching
|
|
- `www/js/post-interactions2.mjs` — dependency forwarding
|
|
- `www/js/mute-list.mjs` — mute list loading
|
|
- `www/js/profile-cache.mjs` — profile resolution
|
|
- `www/js/blossom-ui.mjs` — blossom server loading
|
|
- `www/js/relay-ui.mjs` — relay status loading
|
|
- `www/js/zaps.mjs` — zap-related fetches
|
|
- `www/js/utilities.mjs` — utility functions that may fetch
|
|
|
|
---
|
|
|
|
## Output Format
|
|
|
|
For each finding, report:
|
|
|
|
```
|
|
FILE: www/example.html (or www/js/example.mjs)
|
|
LINE: 123
|
|
PATTERN: [1|2|3|4] — [brief description]
|
|
CODE: `await ndkFetchEvents({ kinds: [1], ... })`
|
|
CONTEXT: [What this code does — e.g., "fetches user's kind 1 posts for feed render"]
|
|
SEVERITY: [HIGH if it blocks visible UI, MEDIUM if it blocks secondary content, LOW if it's a dep threading gap]
|
|
FIX HINT: [e.g., "Add queryCache call before this, render from cache, make this fire-and-forget"]
|
|
```
|
|
|
|
---
|
|
|
|
## What NOT to Flag
|
|
|
|
- `void ndkFetchEvents(...)` — already fire-and-forget, correct pattern
|
|
- `ndkFetchEvents(...)` inside `.then()` chains — already non-blocking
|
|
- `await ndkFetchEvents(...)` for `publishEvent` confirmation — must await
|
|
- `ndkFetchEvents` used only in background refresh intervals (e.g., `setInterval`)
|
|
- `subscribe(...)` calls — these are already event-driven
|