chore: finish frontend ESLint migration #18

Enable every plugin recommended preset and replace Biome with ESLint formatting.

Preserve API contracts and JSX whitespace; verify source, tests, builds and tooling.
This commit is contained in:
2026-09-28 16:36:40 +03:00
committed by ksv741
parent 57630102e4
commit 72d7835227
253 changed files with 10059 additions and 8864 deletions
+63 -61
View File
@@ -1,15 +1,15 @@
import type { QueryClient } from "@tanstack/react-query";
import {
import { type QueryClient,
type QueryFunctionContext,
type QueryKey,
type UseQueryOptions,
type UseQueryResult,
useQueryClient,
} from "@tanstack/react-query";
type UseQueryOptions,
type UseQueryResult } from "@tanstack/react-query";
import { useEffect } from "react";
import type { components } from "./generated/schema";
import { useAuthenticatedQuery } from "./authenticated-query";
import { getAuthenticatedSessionSnapshot } from "./client";
import type { components } from "./generated/schema";
import { apiErrorCode } from "./problem-details";
const accessEpochs = new WeakMap<QueryClient, Map<string, number>>();
@@ -18,31 +18,6 @@ type Resource = {
data: unknown;
meta: components["schemas"]["InvestmentResourceMetaDto"];
};
function unwrapResourceResult<T extends Resource, E>(
result: UseQueryResult<T, E>,
) {
const missing = result.isSuccess && result.data.data === null;
const data: T["data"] | undefined = result.data?.data;
const meta: T["meta"] | undefined = result.data?.meta;
const status: UseQueryResult<T, E>["status"] = missing
? "pending"
: result.status;
// Project resource fields; refetch still returns the transport envelope.
return {
...result,
transport: {
status: result.status,
fetchStatus: result.fetchStatus,
error: result.error,
},
data: data ?? undefined,
meta,
isPending: result.isPending || missing,
isSuccess: result.isSuccess && !missing,
status,
};
}
function invalidateResourceAccess(
client: QueryClient,
group: string,
@@ -50,10 +25,10 @@ function invalidateResourceAccess(
) {
if (
![
"NOT_FOUND",
"FORBIDDEN",
"TBANK_NOT_CONNECTED",
"INVALID_TBANK_TOKEN",
"NOT_FOUND",
"TBANK_NOT_CONNECTED",
].includes(apiErrorCode(error) ?? "")
)
return;
@@ -69,19 +44,44 @@ function invalidateResourceAccess(
cached.setState({
data: undefined,
dataUpdatedAt: 0,
status: "error",
error: error as Error,
fetchStatus: "idle",
status: "error",
});
}
function unwrapResourceResult<T extends Resource, E>(
result: UseQueryResult<T, E>,
) {
const isMissing = result.isSuccess && result.data.data === null;
const data: T["data"] | undefined = result.data?.data;
const meta: T["meta"] | undefined = result.data?.meta;
const status: UseQueryResult<T, E>["status"] = isMissing
? "pending"
: result.status;
// Project resource fields; refetch still returns the transport envelope.
return {
...result,
data: data ?? undefined,
isPending: result.isPending || isMissing,
isSuccess: result.isSuccess && !isMissing,
meta,
status,
transport: {
error: result.error,
fetchStatus: result.fetchStatus,
status: result.status,
},
};
}
// Observation changes transport mode, never the generated cache identity.
function useResourceQuery<T extends Resource, E, K extends QueryKey>(
options: UseQueryOptions<T, E, T, K>,
) {
const client = useQueryClient();
const group =
typeof options.meta?.accessGroup === "string"
const group
= typeof options.meta?.accessGroup === "string"
? options.meta.accessGroup
: undefined;
let epochs = accessEpochs.get(client);
@@ -90,40 +90,38 @@ function useResourceQuery<T extends Resource, E, K extends QueryKey>(
accessEpochs.set(client, epochs);
}
const groupEpochs = epochs;
const queryFn = options.queryFn;
const guarded =
typeof queryFn === "function"
const queryFunction = options.queryFn;
const guarded
= typeof queryFunction === "function"
? async (context: QueryFunctionContext<K>) => {
const epoch = group ? (groupEpochs.get(group) ?? 0) : 0;
const result = await queryFn(context);
if (group && epoch !== (groupEpochs.get(group) ?? 0))
throw new DOMException("Access changed", "AbortError");
return result;
}
: queryFn;
const epoch = group ? (groupEpochs.get(group) ?? 0) : 0;
const result = await queryFunction(context);
if (group && epoch !== (groupEpochs.get(group) ?? 0))
throw new DOMException("Access changed", "AbortError");
return result;
}
: queryFunction;
const query = useAuthenticatedQuery({ ...options, queryFn: guarded });
useEffect(() => {
if (group) invalidateResourceAccess(client, group, query.error);
}, [client, group, query.error]);
const observe = async (expectedStateVersion?: string) => {
const queryFn = guarded;
const queryFunction = guarded;
if (
!query.isEnabled ||
!getAuthenticatedSessionSnapshot() ||
typeof queryFn !== "function"
typeof queryFunction !== "function"
|| !query.isEnabled
|| !getAuthenticatedSessionSnapshot()
)
return;
// Cancellation is synchronous; start the replacement in the same boundary.
void client.cancelQueries({ queryKey: options.queryKey, exact: true });
return client.fetchQuery({
void client.cancelQueries({ exact: true, queryKey: options.queryKey });
return client.query({
...options,
staleTime: 0,
retry: false,
queryFn: async (context: QueryFunctionContext<K>) => {
const [method, path, input] = context.queryKey;
const init = input as
| { params?: { query?: Record<string, unknown> } }
| undefined;
| undefined
| { params?: { query?: Record<string, unknown> } };
const queryKey = [
method,
path,
@@ -135,25 +133,29 @@ function useResourceQuery<T extends Resource, E, K extends QueryKey>(
},
},
] as unknown as K;
const response = await queryFn({ ...context, queryKey });
const response = await queryFunction({ ...context, queryKey });
if (
expectedStateVersion &&
response.meta.stateVersion !== expectedStateVersion
expectedStateVersion
&& response.meta.stateVersion !== expectedStateVersion
) {
// One more observational read; never turn a stale notification into an import.
const current = await queryFn({ ...context, queryKey });
const current = await queryFunction({ ...context, queryKey });
if (current.meta.stateVersion !== expectedStateVersion)
throw new DOMException("Resource changed", "AbortError");
return current;
}
return response;
},
retry: false,
// Observation validates raw resource metadata, before any observer projection.
select: undefined,
staleTime: 0,
});
};
return {
...unwrapResourceResult(query),
queryKey: options.queryKey,
observe,
queryKey: options.queryKey,
};
}