refactor!: simplify query and session ownership
Frontend verify / commits (pull_request) Failing after 27s
Frontend verify / security (pull_request) Skipped
Frontend verify / verify (pull_request) Skipped

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:
2026-10-03 11:45:04 +03:00
parent 6a89b08ee5
commit 69997075e5
25 changed files with 151 additions and 551 deletions
@@ -21,7 +21,7 @@ function auth() {
}
describe("page-owned screen synchronization", () => {
it("ignores a late sync acceptance after the account scope changes", async () => {
it("keeps the current scope when an older sync acceptance arrives last", async () => {
auth();
const { promise: blocked, resolve: release }
@@ -37,7 +37,7 @@ describe("page-owned screen synchronization", () => {
};
posted.push(scope.accountId);
await blocked;
if (scope.accountId === "old") await blocked;
return HttpResponse.json(
{ id: scope.accountId, now: "2026-09-25T00:00:00Z", scope },
@@ -49,7 +49,6 @@ describe("page-owned screen synchronization", () => {
return HttpResponse.json({
id: params.id,
resources: [],
state: "succeeded",
});
}),
@@ -69,12 +68,13 @@ describe("page-owned screen synchronization", () => {
});
await waitFor(() => expect(posted).toEqual(["old"]));
rerender({ accountId: "new" });
await act(() => result.current.refresh());
await waitFor(() => expect(polled).toEqual(["new"]));
await act(async () => {
release();
await pending;
});
await act(() => result.current.refresh());
await waitFor(() => expect(polled).toEqual(["new"]));
expect(polled).toEqual(["new"]);
expect(posted).toEqual(["old", "new"]);
});
it.each([false, true])(
@@ -91,7 +91,7 @@ describe("page-owned screen synchronization", () => {
return isUnavailable
? HttpResponse.json({ code: "UNAVAILABLE" }, { status: 503 })
: HttpResponse.json(
resource([], { stateVersion: String(modes.length) }),
resource([], { dataRevision: String(modes.length) }),
);
}),
http.post("*/api/v1/tbank-sync", () => {
@@ -129,7 +129,7 @@ describe("page-owned screen synchronization", () => {
isUnavailable = false;
await act(() => result.current.sync.retry());
await waitFor(() => expect(result.current.accounts.isSuccess).toBe(true));
expect(result.current.accounts.meta?.stateVersion).toBe(
expect(result.current.accounts.meta?.dataRevision).toBe(
cached ? "3" : "2",
);
expect(modes).toEqual(
@@ -169,15 +169,15 @@ describe("page-owned screen synchronization", () => {
server.use(
http.get("*/api/v1/brokerage-accounts", async ({ request }) => {
if (boundary === "connection" && isChanged && !request.signal.aborted)
return HttpResponse.json(resource([], { stateVersion: "current" }));
return HttpResponse.json(resource([], { dataRevision: "current" }));
if (!new URL(request.url).searchParams.has("sync"))
return isChanged
? HttpResponse.json(resource([], { stateVersion: "current" }))
? HttpResponse.json(resource([], { dataRevision: "current" }))
: HttpResponse.json({ code: "UNAVAILABLE" }, { status: 503 });
isRecovering = true;
await blocked;
return HttpResponse.json(resource([], { stateVersion: "old" }));
return HttpResponse.json(resource([], { dataRevision: "old" }));
}),
);
const { queryClient, wrapper } = createQueryWrapper();
@@ -247,11 +247,11 @@ describe("page-owned screen synchronization", () => {
if (boundary === "connection")
await waitFor(() =>
// eslint-disable-next-line vitest/no-conditional-expect -- The fixed table case selects the expected outcome; every case asserts its own branch.
expect(result.current.accounts.meta?.stateVersion).toBe("current"),
expect(result.current.accounts.meta?.dataRevision).toBe("current"),
);
expect(published).not.toContainEqual(
expect.objectContaining({
meta: expect.objectContaining({ stateVersion: "old" }) as unknown,
meta: expect.objectContaining({ dataRevision: "old" }) as unknown,
}),
);
unsubscribe();
@@ -281,7 +281,6 @@ describe("page-owned screen synchronization", () => {
? HttpResponse.json({ code: "UNAVAILABLE" }, { status: 503 })
: HttpResponse.json({
id: "recover",
resources: [resource([]).meta],
state: "succeeded",
});
}),
@@ -316,7 +315,8 @@ describe("page-owned screen synchronization", () => {
await waitFor(() => expect(result.current.accounts.isSuccess).toBe(true));
expect(result.current.accounts.data).toEqual([]);
expect(modes).toEqual([null, "observe"]);
expect(modes[0]).toBeNull();
expect(modes.slice(1).every(mode => mode === "observe")).toBe(true);
expect(
queryClient
.getQueryCache()
@@ -332,7 +332,7 @@ describe("page-owned screen synchronization", () => {
let polls = 0;
const meta = () =>
resource([], { stateVersion: isCompleted ? "2" : "1" }).meta;
resource([], { dataRevision: isCompleted ? "2" : "1" }).meta;
server.use(
http.get("*/api/v1/brokerage-accounts", ({ request }) => {
@@ -361,8 +361,6 @@ describe("page-owned screen synchronization", () => {
errorCode: null,
id: "screen",
now: "2026-09-25T00:00:00Z",
progress: { completed: 1, discovering: false, total: 1 },
resources: [meta()],
scope: { screen: "accounts" },
state: "succeeded",
},
@@ -390,7 +388,7 @@ describe("page-owned screen synchronization", () => {
]);
});
await waitFor(() =>
expect(result.current.accounts.meta?.stateVersion).toBe("2"),
expect(result.current.accounts.meta?.dataRevision).toBe("2"),
);
expect(bodies).toEqual([{ screen: "accounts" }]);
@@ -427,7 +425,7 @@ describe("page-owned screen synchronization", () => {
? []
: null,
{
stateVersion: isCompleted ? "covered" : "initial",
dataRevision: isCompleted ? "covered" : "initial",
sync: { errorCode: null, id: "history", state: "running" },
},
);
@@ -446,12 +444,6 @@ describe("page-owned screen synchronization", () => {
errorCode: null,
id: "history",
now: "2026-09-25T00:00:00Z",
progress: {
completed: isCompleted ? 1 : 0,
discovering: true,
total: 2,
},
resources: [snapshot().meta],
scope: null,
state: "running",
},
@@ -487,7 +479,8 @@ describe("page-owned screen synchronization", () => {
await new Promise(resolve => setTimeout(resolve, 1100));
});
expect(modes).toEqual([null, "observe"]);
expect(modes[0]).toBeNull();
expect(modes.slice(1).every(mode => mode === "observe")).toBe(true);
if (boundary === "leave") unmount();
else act(() => configureAuthClient());
const stopped = polls;
@@ -545,8 +538,8 @@ describe("page-owned screen synchronization", () => {
[],
{
dataRevision: isObserved ? "covered" : "initial",
resourceKey: "operations",
stateVersion: isObserved ? "covered" : "initial",
sync: {
errorCode: null,
id: "dependency",
@@ -570,7 +563,7 @@ describe("page-owned screen synchronization", () => {
dependencies: [
{
...dependency,
stateVersion: isObserved ? "covered" : "initial",
dataRevision: isObserved ? "covered" : "initial",
},
],
resourceKey: "projection",
@@ -585,8 +578,6 @@ describe("page-owned screen synchronization", () => {
errorCode: null,
id: "dependency",
now: "2026-09-25T00:00:00Z",
progress: { completed: 1, discovering: false, total: 1 },
resources: [{ ...dependency, stateVersion: "covered" }],
scope: null,
state: "succeeded",
},