Files
client/plans/cache-first-audit-instructions.md
2026-04-17 16:52:51 -04:00

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 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