refactor!: simplify query and session ownership
Use Query cancellation and native mutation results instead of HTTP registries, access epochs and copied scope state. Keep local auth ordering and mutation identity fences. Allow staged generated files to remain excluded from ESLint. Backend contract: WealthLedger/backend@d698ed9 BREAKING CHANGE: drop investment stateVersion/progress/resources from the client contract and use data/metadata readiness instead of rewritten query status. Cross-tab auth may require re-login.
This commit is contained in:
@@ -23,14 +23,6 @@ type AuthHandlers = {
|
||||
refresh: () => Promise<boolean>;
|
||||
};
|
||||
|
||||
const inFlight = new Set<{ invalidated: boolean; pathname: string }>();
|
||||
const latestReads = new Map<string, { pending: number; sequence: number }>();
|
||||
|
||||
function invalidateApiRequests(isMatches: (pathname: string) => boolean) {
|
||||
for (const request of inFlight)
|
||||
if (isMatches(request.pathname)) request.invalidated = true;
|
||||
}
|
||||
|
||||
const sessionState: { handlers?: AuthHandlers; hasSession: boolean } = {
|
||||
hasSession: false,
|
||||
};
|
||||
@@ -38,48 +30,6 @@ const sessionState: { handlers?: AuthHandlers; hasSession: boolean } = {
|
||||
const authSessionListeners = new Set<() => void>();
|
||||
|
||||
async function authenticatedFetch(request: Request): Promise<Response> {
|
||||
const url = new URL(request.url);
|
||||
url.searchParams.delete("sync");
|
||||
url.searchParams.sort();
|
||||
const key = `${request.method} ${url}`;
|
||||
const ticket = { invalidated: false, pathname: url.pathname };
|
||||
|
||||
const read
|
||||
= request.method === "GET"
|
||||
? (latestReads.get(key) ?? { pending: 0, sequence: 0 })
|
||||
: undefined;
|
||||
|
||||
if (read) {
|
||||
read.sequence += 1;
|
||||
read.pending += 1;
|
||||
latestReads.set(key, read);
|
||||
}
|
||||
|
||||
const sequence = read?.sequence;
|
||||
inFlight.add(ticket);
|
||||
|
||||
try {
|
||||
return await fetchCurrent(
|
||||
request,
|
||||
() => !ticket.invalidated && (!read || sequence === read.sequence),
|
||||
);
|
||||
}
|
||||
finally {
|
||||
inFlight.delete(ticket);
|
||||
if (read && --read.pending === 0) latestReads.delete(key);
|
||||
}
|
||||
}
|
||||
|
||||
function configureAuthClient(handlers?: AuthHandlers) {
|
||||
if (!handlers) invalidateApiRequests(() => true);
|
||||
sessionState.handlers = handlers;
|
||||
setAuthenticatedSession(Boolean(sessionState.handlers?.accessToken()));
|
||||
}
|
||||
|
||||
async function fetchCurrent(
|
||||
request: Request,
|
||||
isLatest: () => boolean,
|
||||
): Promise<Response> {
|
||||
const isAuthRequest = new URL(request.url).pathname.startsWith(
|
||||
"/api/v1/auth/",
|
||||
);
|
||||
@@ -91,7 +41,7 @@ async function fetchCurrent(
|
||||
(session?.generation
|
||||
? generation === session.generation()
|
||||
&& generation === sessionState.handlers?.generation?.()
|
||||
: session === sessionState.handlers) && isLatest();
|
||||
: session === sessionState.handlers);
|
||||
|
||||
const token = session?.accessToken();
|
||||
|
||||
@@ -107,7 +57,6 @@ async function fetchCurrent(
|
||||
|
||||
const retryRequest = authorized.clone();
|
||||
const response = await fetch(authorized);
|
||||
await response.clone().arrayBuffer();
|
||||
if (!isAuthRequest && !isCurrent())
|
||||
throw new DOMException("Session changed", "AbortError");
|
||||
if (
|
||||
@@ -139,12 +88,16 @@ async function fetchCurrent(
|
||||
const refreshedToken = sessionState.handlers.accessToken();
|
||||
if (!refreshedToken) return response;
|
||||
const retried = await fetch(withToken(retryRequest, refreshedToken));
|
||||
await retried.clone().arrayBuffer();
|
||||
if (!isCurrent()) throw new DOMException("Session changed", "AbortError");
|
||||
|
||||
return retried;
|
||||
}
|
||||
|
||||
function configureAuthClient(handlers?: AuthHandlers) {
|
||||
sessionState.handlers = handlers;
|
||||
setAuthenticatedSession(Boolean(sessionState.handlers?.accessToken()));
|
||||
}
|
||||
|
||||
function hasAuthenticatedSession() {
|
||||
return sessionState.hasSession;
|
||||
}
|
||||
@@ -174,6 +127,5 @@ export {
|
||||
apiQueryClient,
|
||||
configureAuthClient,
|
||||
hasAuthenticatedSession as getAuthenticatedSessionSnapshot,
|
||||
invalidateApiRequests,
|
||||
subscribeAuthenticatedSession,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user