Setup
The client is the whole setup surface. Declare invalidation once, here — no call site downstream ever names a cache key:
import { QueryClient } from "@tahanabavi/typefetch-query-core";
import { api } from "./api"; // a typefetch ApiClient
const client = new QueryClient({
relations: { "user.updateUser": ["user.getUser"] },
});relations reads "<mutation id>": ["<query id>", …]. The id is the contract's
own stable endpointId (typesocket calls it eventId) — the engine resolves
either, which is exactly why the same client drives both transports.