import type { GitStackedAction } from "@t3tools/contracts";import { mutationOptions, queryOptions, type QueryClient } from "@tanstack/react-query";import { ensureNativeApi } from "../nativeApi"; const GIT_STATUS_STALE_TIME_MS = 5_000;const GIT_STATUS_REFETCH_INTERVAL_MS = 15_000;const GIT_BRANCHES_STALE_TIME_MS = 15_000;const GIT_BRANCHES_REFETCH_INTERVAL_MS = 60_000; export const gitQueryKeys = { all: ["git"] as const, status: (cwd: string | null) => ["git", "status", cwd] as const, branches: (cwd: string | null) => ["git", "branches", cwd] as const,}; export const gitMutationKeys = { init: (cwd: string | null) => ["git", "mutation", "init", cwd] as const, checkout: (cwd: string | null) => ["git", "mutation", "checkout", cwd] as const, runStackedAction: (cwd: string | null) => ["git", "mutation", "run-stacked-action", cwd] as const, pull: (cwd: string | null) => ["git", "mutation", "pull", cwd] as const,}; export function invalidateGitQueries(queryClient: QueryClient) { return queryClient.invalidateQueries({ queryKey: gitQueryKeys.all });} export function gitStatusQueryOptions(cwd: string | null) { return queryOptions({ queryKey: gitQueryKeys.status(cwd), queryFn: async () => { const api = ensureNativeApi(); if (!cwd) throw new Error("Git status is unavailable."); return api.git.status({ cwd }); }, enabled: cwd !== null, staleTime: GIT_STATUS_STALE_TIME_MS, refetchOnWindowFocus: "always", refetchOnReconnect: "always", refetchInterval: GIT_STATUS_REFETCH_INTERVAL_MS, });} export function gitBranchesQueryOptions(cwd: string | null) { return queryOptions({ queryKey: gitQueryKeys.branches(cwd), queryFn: async () => { const api = ensureNativeApi(); if (!cwd) throw new Error("Git branches are unavailable."); return api.git.listBranches({ cwd }); }, enabled: cwd !== null, staleTime: GIT_BRANCHES_STALE_TIME_MS, refetchOnWindowFocus: true, refetchOnReconnect: true, refetchInterval: GIT_BRANCHES_REFETCH_INTERVAL_MS, });} export function gitInitMutationOptions(input: { cwd: string | null; queryClient: QueryClient }) { return mutationOptions({ mutationKey: gitMutationKeys.init(input.cwd), mutationFn: async () => { const api = ensureNativeApi(); if (!input.cwd) throw new Error("Git init is unavailable."); return api.git.init({ cwd: input.cwd }); }, onSuccess: async () => { await invalidateGitQueries(input.queryClient); }, });} export function gitCheckoutMutationOptions(input: { cwd: string | null; queryClient: QueryClient;}) { return mutationOptions({ mutationKey: gitMutationKeys.checkout(input.cwd), mutationFn: async (branch: string) => { const api = ensureNativeApi(); if (!input.cwd) throw new Error("Git checkout is unavailable."); return api.git.checkout({ cwd: input.cwd, branch }); }, onSuccess: async () => { await invalidateGitQueries(input.queryClient); }, });} export function gitRunStackedActionMutationOptions(input: { cwd: string | null; queryClient: QueryClient;}) { return mutationOptions({ mutationKey: gitMutationKeys.runStackedAction(input.cwd), mutationFn: async ({ action, commitMessage, featureBranch, }: { action: GitStackedAction; commitMessage?: string; featureBranch?: boolean; }) => { const api = ensureNativeApi(); if (!input.cwd) throw new Error("Git action is unavailable."); return api.git.runStackedAction({ cwd: input.cwd, action, ...(commitMessage ? { commitMessage } : {}), ...(featureBranch ? { featureBranch } : {}), }); }, onSettled: async () => { await invalidateGitQueries(input.queryClient); }, });} export function gitPullMutationOptions(input: { cwd: string | null; queryClient: QueryClient }) { return mutationOptions({ mutationKey: gitMutationKeys.pull(input.cwd), mutationFn: async () => { const api = ensureNativeApi(); if (!input.cwd) throw new Error("Git pull is unavailable."); return api.git.pull({ cwd: input.cwd }); }, onSettled: async () => { await invalidateGitQueries(input.queryClient); }, });} export function gitCreateWorktreeMutationOptions(input: { queryClient: QueryClient }) { return mutationOptions({ mutationFn: async ({ cwd, branch, newBranch, path, }: { cwd: string; branch: string; newBranch: string; path?: string | null; }) => { const api = ensureNativeApi(); if (!cwd) throw new Error("Git worktree creation is unavailable."); return api.git.createWorktree({ cwd, branch, newBranch, path: path ?? null }); }, mutationKey: ["git", "mutation", "create-worktree"] as const, onSettled: async () => { await invalidateGitQueries(input.queryClient); }, });} export function gitRemoveWorktreeMutationOptions(input: { queryClient: QueryClient }) { return mutationOptions({ mutationFn: async ({ cwd, path, force }: { cwd: string; path: string; force?: boolean }) => { const api = ensureNativeApi(); if (!cwd) throw new Error("Git worktree removal is unavailable."); return api.git.removeWorktree({ cwd, path, force }); }, mutationKey: ["git", "mutation", "remove-worktree"] as const, onSettled: async () => { await invalidateGitQueries(input.queryClient); }, });}
import type { GitStackedAction } from "@t3tools/contracts";
import { mutationOptions, queryOptions, type QueryClient } from "@tanstack/react-query";
import { ensureNativeApi } from "../nativeApi";
const GIT_STATUS_STALE_TIME_MS = 5_000;
const GIT_STATUS_REFETCH_INTERVAL_MS = 15_000;
const GIT_BRANCHES_STALE_TIME_MS = 15_000;
const GIT_BRANCHES_REFETCH_INTERVAL_MS = 60_000;
export const gitQueryKeys = {
all: ["git"] as const,
status: (cwd: string | null) => ["git", "status", cwd] as const,
branches: (cwd: string | null) => ["git", "branches", cwd] as const,
};
export const gitMutationKeys = {
init: (cwd: string | null) => ["git", "mutation", "init", cwd] as const,
checkout: (cwd: string | null) => ["git", "mutation", "checkout", cwd] as const,
runStackedAction: (cwd: string | null) => ["git", "mutation", "run-stacked-action", cwd] as const,
pull: (cwd: string | null) => ["git", "mutation", "pull", cwd] as const,
};
export function invalidateGitQueries(queryClient: QueryClient) {
return queryClient.invalidateQueries({ queryKey: gitQueryKeys.all });
}
export function gitStatusQueryOptions(cwd: string | null) {
return queryOptions({
queryKey: gitQueryKeys.status(cwd),
queryFn: async () => {
const api = ensureNativeApi();
if (!cwd) throw new Error("Git status is unavailable.");
return api.git.status({ cwd });
},
enabled: cwd !== null,
staleTime: GIT_STATUS_STALE_TIME_MS,
refetchOnWindowFocus: "always",
refetchOnReconnect: "always",
refetchInterval: GIT_STATUS_REFETCH_INTERVAL_MS,
});
}
export function gitBranchesQueryOptions(cwd: string | null) {
return queryOptions({
queryKey: gitQueryKeys.branches(cwd),
queryFn: async () => {
const api = ensureNativeApi();
if (!cwd) throw new Error("Git branches are unavailable.");
return api.git.listBranches({ cwd });
},
enabled: cwd !== null,
staleTime: GIT_BRANCHES_STALE_TIME_MS,
refetchOnWindowFocus: true,
refetchOnReconnect: true,
refetchInterval: GIT_BRANCHES_REFETCH_INTERVAL_MS,
});
}
export function gitInitMutationOptions(input: { cwd: string | null; queryClient: QueryClient }) {
return mutationOptions({
mutationKey: gitMutationKeys.init(input.cwd),
mutationFn: async () => {
const api = ensureNativeApi();
if (!input.cwd) throw new Error("Git init is unavailable.");
return api.git.init({ cwd: input.cwd });
},
onSuccess: async () => {
await invalidateGitQueries(input.queryClient);
},
});
}
export function gitCheckoutMutationOptions(input: {
cwd: string | null;
queryClient: QueryClient;
}) {
return mutationOptions({
mutationKey: gitMutationKeys.checkout(input.cwd),
mutationFn: async (branch: string) => {
const api = ensureNativeApi();
if (!input.cwd) throw new Error("Git checkout is unavailable.");
return api.git.checkout({ cwd: input.cwd, branch });
},
onSuccess: async () => {
await invalidateGitQueries(input.queryClient);
},
});
}
export function gitRunStackedActionMutationOptions(input: {
cwd: string | null;
queryClient: QueryClient;
}) {
return mutationOptions({
mutationKey: gitMutationKeys.runStackedAction(input.cwd),
mutationFn: async ({
action,
commitMessage,
featureBranch,
}: {
action: GitStackedAction;
commitMessage?: string;
featureBranch?: boolean;
}) => {
const api = ensureNativeApi();
if (!input.cwd) throw new Error("Git action is unavailable.");
return api.git.runStackedAction({
cwd: input.cwd,
action,
...(commitMessage ? { commitMessage } : {}),
...(featureBranch ? { featureBranch } : {}),
});
},
onSettled: async () => {
await invalidateGitQueries(input.queryClient);
},
});
}
export function gitPullMutationOptions(input: { cwd: string | null; queryClient: QueryClient }) {
return mutationOptions({
mutationKey: gitMutationKeys.pull(input.cwd),
mutationFn: async () => {
const api = ensureNativeApi();
if (!input.cwd) throw new Error("Git pull is unavailable.");
return api.git.pull({ cwd: input.cwd });
},
onSettled: async () => {
await invalidateGitQueries(input.queryClient);
},
});
}
export function gitCreateWorktreeMutationOptions(input: { queryClient: QueryClient }) {
return mutationOptions({
mutationFn: async ({
cwd,
branch,
newBranch,
path,
}: {
cwd: string;
branch: string;
newBranch: string;
path?: string | null;
}) => {
const api = ensureNativeApi();
if (!cwd) throw new Error("Git worktree creation is unavailable.");
return api.git.createWorktree({ cwd, branch, newBranch, path: path ?? null });
},
mutationKey: ["git", "mutation", "create-worktree"] as const,
onSettled: async () => {
await invalidateGitQueries(input.queryClient);
},
});
}
export function gitRemoveWorktreeMutationOptions(input: { queryClient: QueryClient }) {
return mutationOptions({
mutationFn: async ({ cwd, path, force }: { cwd: string; path: string; force?: boolean }) => {
const api = ensureNativeApi();
if (!cwd) throw new Error("Git worktree removal is unavailable.");
return api.git.removeWorktree({ cwd, path, force });
},
mutationKey: ["git", "mutation", "remove-worktree"] as const,
onSettled: async () => {
await invalidateGitQueries(input.queryClient);
},
});
}