4.4 KiB
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 timeoutndkFetchEvents(filters)— slow, relay network, 15s timeoutsubscribe(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
queryCachecall 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/queryCacheFnfunction? - If not, is
queryCachebeing 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
queryCachetoo?
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 fetchingwww/js/post-interactions2.mjs— dependency forwardingwww/js/mute-list.mjs— mute list loadingwww/js/profile-cache.mjs— profile resolutionwww/js/blossom-ui.mjs— blossom server loadingwww/js/relay-ui.mjs— relay status loadingwww/js/zaps.mjs— zap-related fetcheswww/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 patternndkFetchEvents(...)inside.then()chains — already non-blockingawait ndkFetchEvents(...)forpublishEventconfirmation — must awaitndkFetchEventsused only in background refresh intervals (e.g.,setInterval)subscribe(...)calls — these are already event-driven