Both bugs were hand-written query keys. lib/api/query-keys.ts exists to
prevent exactly this and neither site used it.
1. /auth/who-am-i was cached under two keys. Four call sites used
['whoAmI'] and seven used ['currentUser'], including the typed
useCurrentUser hook. So the endpoint was fetched twice on any page
using both, and invalidating one never invalidated the other:
edit-profile-form refreshed ['currentUser'] and left the email
verification banner reading stale data under ['whoAmI'], while
use-onboarding had the mirror-image bug.
verify-email/page.tsx additionally cached the whole axios response
where the others cached the unwrapped user, so unifying the key meant
normalising that shape too. One key demands one shape.
2. generate-api-key invalidated ['apiKeys', 'stats'], which no query
uses. react-query matches by prefix, so it matched neither the key
list (['apiKeys', status]) nor the dashboard stats (['stats']).
Generating an API key refreshed nothing at all. It is now three
invalidations against queryKeys, using a new apiKeysAll prefix so
every status filter refreshes rather than just 'active'.
Also added accountDeletionRequestedAt to the User type. It is a real
field on the user schema and the deletion banner already reads it; only
the type did not know about it.
Both bugs are invisible on inspection, which is how they shipped, so the
regression tests were each confirmed to fail against the pre-change code
before being trusted.
Verified: build clean, 0 lint errors (21 warnings, unchanged), 155 unit
tests (from 153), 78 e2e.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Introduce a typed data layer under lib/api (query-key factory, response
types, feature hooks) plus shared lib/format and lib/status helpers, and
migrate the canonical dashboard consumers (subscription-info, overview,
device-list, api-keys) onto it, deleting their duplicated inline queries,
mutations and formatters.
List hooks keep the raw { data: [] } envelope in the cache and unwrap
per-observer with react-query `select`, so shared keys like ['devices']
stay compatible with the not-yet-migrated components that still read the
raw shape (avoids a cache-shape collision surfaced by the dashboard e2e).
Adds unit tests for the formatters and the hooks (against MSW). Build,
19 unit tests, and 2 e2e all green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>