Refactor proxy codebase

This commit is contained in:
Gregory Schier
2026-03-12 08:31:05 -07:00
parent 4968237ece
commit 5e3ef70d93
21 changed files with 437 additions and 408 deletions

View File

@@ -0,0 +1,30 @@
import type { ActionInvocation, ActionMetadata } from '@yaakapp-internal/proxy-lib';
import { useEffect, useState } from 'react';
import { rpc } from '../lib/rpc';
/** Look up metadata for a specific action invocation. */
export function useActionMetadata(action: ActionInvocation): ActionMetadata | null {
const [meta, setMeta] = useState<ActionMetadata | null>(null);
useEffect(() => {
getActions().then((actions) => {
const match = actions.find(
([inv]) => inv.scope === action.scope && inv.action === action.action,
);
setMeta(match?.[1] ?? null);
});
}, [action]);
return meta;
}
let cachedActions: [ActionInvocation, ActionMetadata][] | null = null;
/** Fetch and cache all action metadata. */
async function getActions(): Promise<[ActionInvocation, ActionMetadata][]> {
if (!cachedActions) {
const { actions } = await rpc('list_actions', {});
cachedActions = actions;
}
return cachedActions;
}

View File

@@ -0,0 +1,15 @@
import type { RpcEventSchema } from '@yaakapp-internal/proxy-lib';
import { useEffect } from 'react';
import { listen } from '../lib/rpc';
/**
* Subscribe to an RPC event. Cleans up automatically on unmount.
*/
export function useRpcEvent<K extends keyof RpcEventSchema>(
event: K & string,
callback: (payload: RpcEventSchema[K]) => void,
) {
useEffect(() => {
return listen(event, callback);
}, [event, callback]);
}

View File

@@ -0,0 +1,18 @@
import { type UseMutationOptions, useMutation } from '@tanstack/react-query';
import type { RpcSchema } from '@yaakapp-internal/proxy-lib';
import { minPromiseMillis } from '@yaakapp-internal/ui';
import type { Req, Res } from '../lib/rpc';
import { rpc } from '../lib/rpc';
/**
* React Query mutation wrapper for RPC commands.
*/
export function useRpcMutation<K extends keyof RpcSchema>(
cmd: K,
opts?: Omit<UseMutationOptions<Res<K>, Error, Req<K>>, 'mutationFn'>,
) {
return useMutation<Res<K>, Error, Req<K>>({
mutationFn: (payload) => minPromiseMillis(rpc(cmd, payload)),
...opts,
});
}

View File

@@ -0,0 +1,20 @@
import { type UseQueryOptions, useQuery } from '@tanstack/react-query';
import type { RpcSchema } from '@yaakapp-internal/proxy-lib';
import type { Req, Res } from '../lib/rpc';
import { rpc } from '../lib/rpc';
/**
* React Query wrapper for RPC commands.
* Automatically caches by [cmd, payload] and supports all useQuery options.
*/
export function useRpcQuery<K extends keyof RpcSchema>(
cmd: K,
payload: Req<K>,
opts?: Omit<UseQueryOptions<Res<K>>, 'queryKey' | 'queryFn'>,
) {
return useQuery<Res<K>>({
queryKey: [cmd, payload],
queryFn: () => rpc(cmd, payload),
...opts,
});
}

View File

@@ -0,0 +1,23 @@
import { type UseQueryOptions, useQueryClient } from '@tanstack/react-query';
import type { RpcEventSchema, RpcSchema } from '@yaakapp-internal/proxy-lib';
import type { Req, Res } from '../lib/rpc';
import { useRpcEvent } from './useRpcEvent';
import { useRpcQuery } from './useRpcQuery';
/**
* Combines useRpcQuery with an event listener that invalidates the query
* whenever the specified event fires, keeping data fresh automatically.
*/
export function useRpcQueryWithEvent<
K extends keyof RpcSchema,
E extends keyof RpcEventSchema & string,
>(cmd: K, payload: Req<K>, event: E, opts?: Omit<UseQueryOptions<Res<K>>, 'queryKey' | 'queryFn'>) {
const queryClient = useQueryClient();
const query = useRpcQuery(cmd, payload, opts);
useRpcEvent(event, () => {
queryClient.invalidateQueries({ queryKey: [cmd, payload] });
});
return query;
}