Files
paseo/packages/app/src/data/push-router.ts
T
Mohamed Boudra fcde2072ab feat(diff): stream Changes revisions without blocking the socket (#8907)
* feat(protocol): define streamed diff revisions and resolve tag bases

* feat(server): stream shared diff revisions with paced file bodies

* feat(client): observe streamed diffs with atomic revision updates

* feat(app): show the Changes tree while diff bodies stream

* fix(server): retain the legacy structured diff payload budget

* fix(diff): preserve revision ordering and readable files

* fix(diff): select renamed patches and read dangling symlinks

* fix(diff): preserve nested checkouts and ambiguous patch paths

Only regular files and symlinks are readable untracked entries. Prefer patch metadata to ambiguous unquoted headers so paths containing a prefix delimiter keep their hunks.

* fix(diff): preserve BOM text and complete file replacements

Decode each string without consuming its leading BOM. File-type replacements emit multiple patch sections for one listed path, so retain all matching hunks. Assert the optional rename metadata in the existing commit-file journey.

* fix(diff): highlight symlinks from their stored target text

Working tree source context must match the symlink blob emitted by Git rather than the contents reached by following the link. Otherwise highlight runs do not cover the patch line and encoding fails the revision.

* fix(diff): tolerate disappearing highlight sources

Source context is optional after Git has produced the patch. Missing worktree link metadata must preserve the existing hunk-based highlighting fallback instead of failing the revision.

* fix(diff): batch cold reads and refresh logical comparison inputs

Acquire bounded pages of tracked patches lazily after Begin while retaining per-file highlighting and awaited frame delivery. Keep legacy preferred-base selection live across revisions and include submodule Git state in worktree cache identity.

* fix(diff): distinguish gitlink directories from replacements

A raw old gitlink mode also occurs when a submodule becomes a regular file. Query nested Git state only for a current directory, preserving normal file identity for replacements.

* fix(diff): invalidate timestamp-preserving file replacements

Size and modification time do not identify live file contents after a preserving copy. Include filesystem change time and file identity for tracked and untracked reuse, and verify live revisions agree with fresh reads.

* fix(diff): align highlight tokens with normalized patch lines

Git can normalize CRLF worktrees while highlighting reads their original line endings. Match tokens to the emitted patch text and trim only a normalization CR before encoding runs; mismatched source tokens are not attached.

* fix(diff): refresh equivalent worktree paths across platforms

Explicit refreshes must match filesystem-equivalent roots, including Windows path aliases. Use the existing realpath-aware matcher, budget real Git completion waits consistently, and assert optional status metadata in the filesystem commit-file journey.

* fix(diff): validate Git inputs and resolve refresh roots

Reuse now accounts for effective attributes, configuration, and canonical worktree blobs as well as filesystem state. Explicit refresh resolves repository identity like subscription creation, and callers await that resolution before acknowledging the command.

* fix(diff): keep non-Git workspace refresh successful

Diff invalidation has no target repository for an ordinary folder. Treat that expected absence as a no-op while preserving other root-resolution errors.

* fix(diff): observe scoped Git interpretation settings

Classify each metadata root with its worktree and common scopes, retaining both when the directories coincide. Shared attributes affect all checkouts, while config.worktree belongs to the owning checkout. Real subscriptions verify automatic binary and hunk-context refreshes in main and linked worktrees.

* fix(diff): observe interpretation inputs for pinned comparisons

Immutable commit objects still depend on mutable Git configuration and attributes. Declare those metadata inputs independently of ref, index, and worktree mutability, while keeping unrelated endpoint events out of pinned comparisons.

* fix(diff): observe interpretation independently of comparison endpoints

Commit comparisons still read worktree attributes. Watch those inputs independently, checking effective Git interpretation before invalidating fixed contents. Retain paths hidden by whitespace filtering so attribute changes can expose them. Forward binary frames through the branch-loading test interceptor.
2026-11-08 11:00:21 +07:00

736 lines
22 KiB
TypeScript

import { checkoutDiffSnapshot, type CheckoutDiffSnapshot } from "@/git/diff-snapshot";
import type { DiffState } from "@getpaseo/client";
import type { OwnedSubscription } from "@getpaseo/client";
import type { ProviderSnapshotEntry } from "@getpaseo/protocol/agent-types";
import type { Query, QueryCacheNotifyEvent, QueryClient, QueryKey } from "@tanstack/react-query";
import type {
ListTerminalsResponse,
MutableDaemonConfig,
SessionOutboundMessage,
} from "@getpaseo/protocol/messages";
import { invalidateCommandsForProviderChange } from "@/agent-commands/cache";
import { shareCheckoutDiff } from "@/git/diff-sharing";
import { orderCheckoutDiffFiles } from "@/git/diff-order";
import { daemonConfigQueryKey } from "@/data/daemon-config";
import { daemonPairingOfferQueryKey } from "@/data/daemon-pairing";
import { type ProviderSnapshotCache } from "@/data/provider-snapshot-cache";
import {
normalizeProvidersSnapshotCwd,
fetchProvidersSnapshot,
providersSnapshotQueryKey,
providersSnapshotQueryRoot,
} from "@/data/providers-snapshot";
type ProvidersSnapshotUpdateMessage = Extract<
SessionOutboundMessage,
{ type: "providers_snapshot_update" }
>;
type StatusMessage = Extract<SessionOutboundMessage, { type: "status" }>;
type TerminalsChangedMessage = Extract<SessionOutboundMessage, { type: "terminals_changed" }>;
type CheckoutDiffCachePayload = CheckoutDiffSnapshot;
type ListTerminalsPayload = ListTerminalsResponse["payload"];
interface CheckoutDiffCompare {
mode: "uncommitted" | "base" | "all";
baseRef?: string;
ignoreWhitespace?: boolean;
}
interface CheckoutDiffRoute {
domain: "checkoutDiff";
enabled: boolean;
serverId: string;
subscriptionId: string;
cwd: string;
compare: CheckoutDiffCompare;
}
interface CheckoutDiffRegistration extends CheckoutDiffRoute {
subscription: OwnedSubscription<DiffState>;
}
interface WorkspaceTerminalsRoute {
domain: "workspaceTerminals";
enabled: boolean;
serverId: string;
cwd: string;
workspaceId?: string;
}
interface WorkspaceTerminalsRegistration extends WorkspaceTerminalsRoute {
subscription: OwnedSubscription<TerminalsChangedMessage["payload"]>;
}
type ServerDataRoute = CheckoutDiffRoute | WorkspaceTerminalsRoute;
export interface ServerDataQueryMeta extends Record<string, unknown> {
serverData: ServerDataRoute;
}
export type ProvidersSnapshotUpdate = ProvidersSnapshotUpdateMessage;
interface ServerDataPushClient {
getProvidersSnapshot: import("@getpaseo/client/internal/daemon-client").DaemonClient["getProvidersSnapshot"];
observeEvents: import("@getpaseo/client/internal/daemon-client").DaemonClient["observeEvents"];
observeDiff: import("@getpaseo/client/internal/daemon-client").DaemonClient["observeDiff"];
observeTerminals: import("@getpaseo/client/internal/daemon-client").DaemonClient["observeTerminals"];
}
interface PushRouterInput {
client: ServerDataPushClient;
queryClient: QueryClient;
serverId: string;
}
interface ActiveServerDataSubscriptions {
checkoutDiff: Map<string, CheckoutDiffRoute>;
workspaceTerminals: Map<string, WorkspaceTerminalsRoute>;
}
interface ReconnectRepairPolicy {
domain: string;
invalidate(input: { queryClient: QueryClient; serverId: string }): void;
}
const RECONNECT_REPAIR_POLICIES: ReconnectRepairPolicy[] = [
{
domain: "providersSnapshot",
invalidate: ({ queryClient, serverId }) => {
void queryClient.invalidateQueries({ queryKey: providersSnapshotQueryRoot(serverId) });
},
},
{
domain: "daemonConfig",
invalidate: ({ queryClient, serverId }) => {
void queryClient.invalidateQueries({ queryKey: daemonConfigQueryKey(serverId) });
},
},
{
domain: "daemonPairingOffer",
invalidate: ({ queryClient, serverId }) => {
void queryClient.invalidateQueries({ queryKey: daemonPairingOfferQueryKey(serverId) });
},
},
{
domain: "checkoutDiff",
invalidate: ({ queryClient, serverId }) => {
void queryClient.invalidateQueries({
predicate: (query) => isQueryForServer(query.queryKey, "checkoutDiff", serverId),
});
},
},
{
domain: "workspaceTerminals",
invalidate: ({ queryClient, serverId }) => {
void queryClient.invalidateQueries({
predicate: (query) => isQueryForServer(query.queryKey, "terminals", serverId),
});
},
},
];
export function checkoutDiffPushRoute(input: {
enabled: boolean;
serverId: string;
subscriptionId: string;
cwd: string;
compare: CheckoutDiffCompare;
}): ServerDataQueryMeta {
return {
serverData: {
domain: "checkoutDiff",
enabled: input.enabled,
serverId: input.serverId,
subscriptionId: input.subscriptionId,
cwd: input.cwd,
compare: input.compare,
},
};
}
export function workspaceTerminalsPushRoute(input: {
enabled: boolean;
serverId: string;
cwd: string;
workspaceId?: string;
}): ServerDataQueryMeta {
return {
serverData: {
domain: "workspaceTerminals",
enabled: input.enabled,
serverId: input.serverId,
cwd: input.cwd,
...(input.workspaceId ? { workspaceId: input.workspaceId } : {}),
},
};
}
export function invalidateServerDataQueriesAfterReconnect(input: {
queryClient: QueryClient;
serverId: string;
}): void {
for (const policy of RECONNECT_REPAIR_POLICIES) {
policy.invalidate(input);
}
}
export async function applyProvidersSnapshotUpdate(input: {
serverId: string;
queryClient: QueryClient;
message: ProvidersSnapshotUpdate;
client: Pick<ServerDataPushClient, "getProvidersSnapshot">;
cache?: ProviderSnapshotCache;
}): Promise<void> {
const snapshot = { ...input.message.payload, requestId: "providers_snapshot_update" };
const cwd = normalizeProvidersSnapshotCwd(snapshot.cwd);
const queryKey = providersSnapshotQueryKey(input.serverId, cwd);
const previous = input.queryClient.getQueryData<{ entries: ProviderSnapshotEntry[] }>(queryKey);
let announcement: typeof snapshot | undefined = snapshot;
void input.queryClient.cancelQueries({ queryKey, exact: true });
const current = await input.queryClient.fetchQuery({
queryKey,
staleTime: 0,
structuralSharing: false,
retry: false,
queryFn: ({ signal }) => {
const incoming = announcement;
announcement = undefined;
return fetchProvidersSnapshot({
...input,
cwd,
snapshot: incoming,
signal,
});
},
});
void invalidateCommandsForProviderChange({
queryClient: input.queryClient,
serverId: input.serverId,
cwd,
previous: previous?.entries,
current: current.entries,
});
}
export function mountServerDataPushRouter(input: PushRouterInput): () => void {
const activeCheckoutDiffSubscriptions = new Map<string, CheckoutDiffRegistration>();
const activeTerminalSubscriptions = new Map<string, WorkspaceTerminalsRegistration>();
let disposed = false;
function reconcileSubscriptions(
fallbackActive: ActiveServerDataSubscriptions = {
checkoutDiff: activeCheckoutDiffSubscriptions,
workspaceTerminals: activeTerminalSubscriptions,
},
): void {
if (disposed) {
return;
}
const desiredCheckoutDiffSubscriptions = new Map<string, CheckoutDiffRoute>();
const desiredTerminalSubscriptions = new Map<string, WorkspaceTerminalsRoute>();
for (const query of input.queryClient.getQueryCache().getAll()) {
const route = getActiveServerDataRoute(query, input.serverId, {
checkoutDiff: fallbackActive.checkoutDiff,
workspaceTerminals: fallbackActive.workspaceTerminals,
});
if (!route) {
continue;
}
if (route.domain === "checkoutDiff") {
desiredCheckoutDiffSubscriptions.set(route.subscriptionId, route);
continue;
}
desiredTerminalSubscriptions.set(workspaceTerminalSubscriptionKey(route), route);
}
reconcileCheckoutDiffSubscriptions({
active: activeCheckoutDiffSubscriptions,
client: input.client,
desired: desiredCheckoutDiffSubscriptions,
serverId: input.serverId,
queryClient: input.queryClient,
});
reconcileTerminalSubscriptions({
active: activeTerminalSubscriptions,
client: input.client,
desired: desiredTerminalSubscriptions,
apply: (route, payload) =>
applyTerminalsChanged({
activeCheckoutDiffSubscriptions,
activeTerminalSubscriptions,
queryClient: input.queryClient,
serverId: input.serverId,
route,
message: { type: "terminals_changed", payload },
}),
});
}
const unsubscribeQueryCache = input.queryClient.getQueryCache().subscribe((event) => {
if (
!shouldReconcileSubscriptionsForCacheEvent(event, input.serverId, {
checkoutDiff: activeCheckoutDiffSubscriptions,
workspaceTerminals: activeTerminalSubscriptions,
})
) {
return;
}
reconcileSubscriptions();
});
const events = input.client.observeEvents([
"providers_snapshot_update",
"status.daemon_config_changed",
]);
events.subscribe({
snapshot: () => {},
update: (message) => {
if (message.type === "providers_snapshot_update") {
void applyProvidersSnapshotUpdate({
client: input.client,
queryClient: input.queryClient,
serverId: input.serverId,
message,
}).catch(() => {
/* Query state owns fetch failures; reconnect/refetch repairs them. */
});
}
if (message.type === "status")
applyDaemonConfigStatus({
queryClient: input.queryClient,
serverId: input.serverId,
message,
});
},
});
reconcileSubscriptions();
return () => {
disposed = true;
unsubscribeQueryCache();
void events.release().catch(console.error);
for (const current of activeCheckoutDiffSubscriptions.values()) {
void current.subscription.release().catch(console.error);
}
activeCheckoutDiffSubscriptions.clear();
for (const route of activeTerminalSubscriptions.values()) {
void route.subscription.release().catch(console.error);
}
activeTerminalSubscriptions.clear();
};
}
function reconcileCheckoutDiffSubscriptions(input: {
active: Map<string, CheckoutDiffRegistration>;
client: ServerDataPushClient;
desired: Map<string, CheckoutDiffRoute>;
serverId: string;
queryClient: QueryClient;
}): void {
for (const [key, current] of input.active) {
const desired = input.desired.get(key);
if (desired && areCheckoutDiffRoutesEqual(current, desired)) continue;
input.active.delete(key);
void current.subscription.release().catch(console.error);
}
for (const [key, desired] of input.desired) {
if (input.active.has(key)) continue;
const { mode, baseRef, ignoreWhitespace } = desired.compare;
const revision = mode === "uncommitted" ? "HEAD" : baseRef;
if (!revision) continue;
const subscription = input.client.observeDiff({
cwd: desired.cwd,
base: { kind: "rev", rev: revision },
head: mode === "base" ? { kind: "rev", rev: "HEAD" } : { kind: "worktree" },
mergeBase: mode !== "uncommitted",
ignoreWhitespace,
});
const registration = { ...desired, subscription };
input.active.set(key, registration);
subscription.subscribe({
snapshot: (state) => {
if (input.active.get(key) !== registration) return;
const snapshot = checkoutDiffSnapshot(desired.cwd, state);
setCheckoutDiffPayload({
activeCheckoutDiffSubscriptions: input.active,
queryClient: input.queryClient,
serverId: input.serverId,
subscriptionId: key,
payload: { ...snapshot, files: orderCheckoutDiffFiles(snapshot.files) },
});
},
update: () => {},
error: (error) => {
if (input.active.get(key) === registration) input.active.delete(key);
console.error("[server-data] observeDiff failed", {
serverId: input.serverId,
cwd: desired.cwd,
error,
});
},
});
}
}
function reconcileTerminalSubscriptions(input: {
active: Map<string, WorkspaceTerminalsRegistration>;
client: ServerDataPushClient;
desired: Map<string, WorkspaceTerminalsRoute>;
apply(route: WorkspaceTerminalsRoute, payload: TerminalsChangedMessage["payload"]): void;
}): void {
for (const [key, current] of input.active) {
const desired = input.desired.get(key);
if (desired && areWorkspaceTerminalsRoutesEqual(current, desired)) continue;
input.active.delete(key);
void current.subscription.release().catch(console.error);
}
for (const [key, desired] of input.desired) {
if (input.active.has(key)) continue;
const subscription = input.client.observeTerminals(workspaceTerminalSubscriptionInput(desired));
const registration = { ...desired, subscription };
input.active.set(key, registration);
const apply = (payload: TerminalsChangedMessage["payload"]) => {
if (input.active.get(key) === registration) input.apply(desired, payload);
};
subscription.subscribe({
snapshot: apply,
update: (message) => {
if (message.type === "terminals_changed") apply(message.payload);
},
error: (error) => {
if (input.active.get(key) === registration) input.active.delete(key);
console.error("[server-data] observeTerminals failed", { cwd: desired.cwd, error });
},
});
}
}
function applyDaemonConfigStatus(input: {
queryClient: QueryClient;
serverId: string;
message: StatusMessage;
}): void {
const payload = input.message.payload;
if (!isDaemonConfigChangedPayload(payload)) {
return;
}
input.queryClient.setQueryData<MutableDaemonConfig>(
daemonConfigQueryKey(input.serverId),
payload.config,
);
void input.queryClient.invalidateQueries({
queryKey: daemonPairingOfferQueryKey(input.serverId),
});
}
function setCheckoutDiffPayload(input: {
activeCheckoutDiffSubscriptions: Map<string, CheckoutDiffRoute>;
queryClient: QueryClient;
serverId: string;
subscriptionId: string;
payload: CheckoutDiffCachePayload;
}): void {
for (const query of input.queryClient.getQueryCache().getAll()) {
const route =
getServerDataRoute(query) ??
getActiveCheckoutDiffRouteForQueryKey({
active: input.activeCheckoutDiffSubscriptions,
queryKey: query.queryKey,
serverId: input.serverId,
});
if (
!route ||
route.domain !== "checkoutDiff" ||
route.serverId !== input.serverId ||
route.subscriptionId !== input.subscriptionId
) {
continue;
}
query.setOptions({ ...query.options, structuralSharing: shareCheckoutDiff });
input.queryClient.setQueryData<CheckoutDiffCachePayload>(query.queryKey, input.payload);
}
}
function applyTerminalsChanged(input: {
activeCheckoutDiffSubscriptions: Map<string, CheckoutDiffRoute>;
activeTerminalSubscriptions: Map<string, WorkspaceTerminalsRoute>;
queryClient: QueryClient;
serverId: string;
message: TerminalsChangedMessage;
route: WorkspaceTerminalsRoute;
}): void {
for (const query of input.queryClient.getQueryCache().getAll()) {
const route = getActiveServerDataRoute(query, input.serverId, {
checkoutDiff: input.activeCheckoutDiffSubscriptions,
workspaceTerminals: input.activeTerminalSubscriptions,
});
if (
!route ||
route.domain !== "workspaceTerminals" ||
!areWorkspaceTerminalsRoutesEqual(route, input.route)
) {
continue;
}
const matchingTerminals = input.message.payload.terminals.filter(
(terminal) => terminal.workspaceId === route.workspaceId,
);
input.queryClient.setQueryData<ListTerminalsPayload>(query.queryKey, (current) => ({
cwd: input.message.payload.cwd,
terminals: matchingTerminals,
requestId: current?.requestId ?? `terminals-changed-${Date.now()}`,
}));
}
}
function getActiveServerDataRoute(
query: Query,
serverId: string,
active: ActiveServerDataSubscriptions,
): ServerDataRoute | null {
if (query.getObserversCount() === 0) {
return null;
}
// Query metadata belongs to the last observer that updated its options. A
// retained hidden panel must not cancel demand from another visible panel.
const observerRoutes = query.observers
.map((observer) => getServerDataRoute({ meta: observer.options.meta }))
.filter((route): route is ServerDataRoute => route !== null);
if (observerRoutes.length > 0) {
return observerRoutes.find((route) => route.enabled && route.serverId === serverId) ?? null;
}
const route = getServerDataRoute(query);
if (route) {
return route.enabled && route.serverId === serverId ? route : null;
}
return getActiveRouteForQueryKey({
active,
queryKey: query.queryKey,
serverId,
});
}
function getActiveRouteForQueryKey(input: {
active: ActiveServerDataSubscriptions;
queryKey: QueryKey;
serverId: string;
}): ServerDataRoute | null {
return (
getActiveTerminalRouteForQueryKey({
active: input.active.workspaceTerminals,
queryKey: input.queryKey,
serverId: input.serverId,
}) ??
getActiveCheckoutDiffRouteForQueryKey({
active: input.active.checkoutDiff,
queryKey: input.queryKey,
serverId: input.serverId,
})
);
}
function getActiveTerminalRouteForQueryKey(input: {
active: Map<string, WorkspaceTerminalsRoute>;
queryKey: QueryKey;
serverId: string;
}): WorkspaceTerminalsRoute | null {
if (!isQueryForServer(input.queryKey, "terminals", input.serverId)) {
return null;
}
const cwd = input.queryKey[2];
const workspaceId = input.queryKey[3];
if (
typeof cwd !== "string" ||
(workspaceId !== undefined && workspaceId !== null && typeof workspaceId !== "string")
) {
return null;
}
return input.active.get(`${cwd}\u0000${workspaceId ?? ""}`) ?? null;
}
function getActiveCheckoutDiffRouteForQueryKey(input: {
active: Map<string, CheckoutDiffRoute>;
queryKey: QueryKey;
serverId: string;
}): CheckoutDiffRoute | null {
if (!isQueryForServer(input.queryKey, "checkoutDiff", input.serverId)) {
return null;
}
for (const route of input.active.values()) {
if (isCheckoutDiffQueryKeyForRoute(input.queryKey, route)) {
return route;
}
}
return null;
}
function shouldReconcileSubscriptionsForCacheEvent(
event: QueryCacheNotifyEvent,
serverId: string,
active: ActiveServerDataSubscriptions,
): boolean {
if (!canEventChangeDesiredSubscriptions(event.type)) {
return false;
}
const route = getServerDataRoute(event.query);
if (route?.serverId === serverId) {
return true;
}
return (
getActiveRouteForQueryKey({
active,
queryKey: event.query.queryKey,
serverId,
}) !== null
);
}
function canEventChangeDesiredSubscriptions(type: QueryCacheNotifyEvent["type"]): boolean {
return (
type === "added" ||
type === "removed" ||
type === "observerAdded" ||
type === "observerRemoved" ||
type === "observerOptionsUpdated"
);
}
function getServerDataRoute(query: Pick<Query, "meta">): ServerDataRoute | null {
const meta = query.meta;
if (!isRecord(meta) || !isRecord(meta.serverData)) {
return null;
}
return readServerDataRoute(meta.serverData);
}
function readServerDataRoute(value: Record<string, unknown>): ServerDataRoute | null {
const domain = value.domain;
const enabled = value.enabled;
const serverId = value.serverId;
const cwd = value.cwd;
if (typeof enabled !== "boolean" || typeof serverId !== "string" || typeof cwd !== "string") {
return null;
}
if (domain === "checkoutDiff") {
const subscriptionId = value.subscriptionId;
const compare = readCheckoutDiffCompare(value.compare);
if (typeof subscriptionId !== "string" || !compare) {
return null;
}
return {
domain,
enabled,
serverId,
subscriptionId,
cwd,
compare,
};
}
if (domain === "workspaceTerminals") {
const workspaceId = value.workspaceId;
if (workspaceId !== undefined && typeof workspaceId !== "string") {
return null;
}
return {
domain,
enabled,
serverId,
cwd,
...(workspaceId ? { workspaceId } : {}),
};
}
return null;
}
function readCheckoutDiffCompare(value: unknown): CheckoutDiffCompare | null {
if (!isRecord(value)) {
return null;
}
const mode = value.mode;
const baseRef = value.baseRef;
const ignoreWhitespace = value.ignoreWhitespace;
if (mode !== "uncommitted" && mode !== "base" && mode !== "all") {
return null;
}
if (baseRef !== undefined && typeof baseRef !== "string") {
return null;
}
if (ignoreWhitespace !== undefined && typeof ignoreWhitespace !== "boolean") {
return null;
}
return {
mode,
...(baseRef ? { baseRef } : {}),
...(ignoreWhitespace !== undefined ? { ignoreWhitespace } : {}),
};
}
function areCheckoutDiffRoutesEqual(
left: CheckoutDiffRoute | undefined,
right: CheckoutDiffRoute,
): boolean {
return (
left?.serverId === right.serverId &&
left.subscriptionId === right.subscriptionId &&
left.cwd === right.cwd &&
left.compare.mode === right.compare.mode &&
left.compare.baseRef === right.compare.baseRef &&
left.compare.ignoreWhitespace === right.compare.ignoreWhitespace
);
}
function isCheckoutDiffQueryKeyForRoute(queryKey: QueryKey, route: CheckoutDiffRoute): boolean {
return (
queryKey[0] === "checkoutDiff" &&
queryKey[1] === route.serverId &&
queryKey[2] === route.cwd &&
queryKey[3] === route.compare.mode &&
queryKey[4] === (route.compare.baseRef ?? "") &&
queryKey[5] === (route.compare.ignoreWhitespace === true)
);
}
function areWorkspaceTerminalsRoutesEqual(
left: WorkspaceTerminalsRoute,
right: WorkspaceTerminalsRoute,
): boolean {
return (
left.serverId === right.serverId &&
left.cwd === right.cwd &&
left.workspaceId === right.workspaceId
);
}
function workspaceTerminalSubscriptionKey(route: WorkspaceTerminalsRoute): string {
return `${route.cwd}\u0000${route.workspaceId ?? ""}`;
}
function workspaceTerminalSubscriptionInput(route: WorkspaceTerminalsRoute): {
cwd: string;
workspaceId?: string;
} {
return {
cwd: route.cwd,
...(route.workspaceId ? { workspaceId: route.workspaceId } : {}),
};
}
function isQueryForServer(queryKey: QueryKey, kind: string, serverId: string): boolean {
return queryKey.length >= 2 && queryKey[0] === kind && queryKey[1] === serverId;
}
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null;
}
function isDaemonConfigChangedPayload(
payload: StatusMessage["payload"],
): payload is { status: "daemon_config_changed"; config: MutableDaemonConfig } {
return payload.status === "daemon_config_changed" && isRecord(payload.config);
}