import { z } from "zod"; import { controllerProcedure } from "../trpc.js"; type GraphNodeLike = { id: string; domain: string; }; type GraphLinkLike = { source: string; target: string; }; type ResourceGraphSnapshot = { nodes: GraphNodeLike[]; links: GraphLinkLike[]; meta: { resourceEid: string; resourceName: string; [key: string]: unknown; }; }; type ProjectGraphSnapshot = { nodes: GraphNodeLike[]; links: GraphLinkLike[]; meta: { projectCode: string; projectName: string; [key: string]: unknown; }; }; function filterGraphData< TNode extends GraphNodeLike, TLink extends GraphLinkLike, >(input: { nodes: TNode[]; links: TLink[]; domain?: string; includeLinks?: boolean; }) { const requestedDomain = input.domain?.trim().toUpperCase(); const nodes = requestedDomain ? input.nodes.filter((node) => node.domain === requestedDomain) : input.nodes; const selectedNodeIds = new Set(nodes.map((node) => node.id)); const links = input.includeLinks ? input.links.filter((link) => selectedNodeIds.has(link.source) && selectedNodeIds.has(link.target)) : []; return { requestedDomain: requestedDomain ?? null, includedLinks: input.includeLinks ?? false, selectedNodeCount: nodes.length, selectedLinkCount: links.length, nodes, ...(input.includeLinks ? { links } : {}), }; } function getAvailableDomains(nodes: TNode[]) { return [...new Set(nodes.map((node) => node.domain))]; } function formatResourceGraphDetail(input: { resourceId: string; graph: ResourceGraphSnapshot; domain?: string; includeLinks?: boolean; }) { return { resource: { id: input.resourceId, eid: input.graph.meta.resourceEid, displayName: input.graph.meta.resourceName, }, availableDomains: getAvailableDomains(input.graph.nodes), totalNodeCount: input.graph.nodes.length, totalLinkCount: input.graph.links.length, ...filterGraphData({ nodes: input.graph.nodes, links: input.graph.links, ...(input.domain ? { domain: input.domain } : {}), ...(input.includeLinks !== undefined ? { includeLinks: input.includeLinks } : {}), }), meta: input.graph.meta, }; } function formatProjectGraphDetail(input: { projectId: string; graph: ProjectGraphSnapshot; domain?: string; includeLinks?: boolean; }) { return { project: { id: input.projectId, shortCode: input.graph.meta.projectCode, name: input.graph.meta.projectName, }, availableDomains: getAvailableDomains(input.graph.nodes), totalNodeCount: input.graph.nodes.length, totalLinkCount: input.graph.links.length, ...filterGraphData({ nodes: input.graph.nodes, links: input.graph.links, ...(input.domain ? { domain: input.domain } : {}), ...(input.includeLinks !== undefined ? { includeLinks: input.includeLinks } : {}), }), meta: input.graph.meta, }; } export function createComputationGraphDetailProcedures(input: { resourceGraphInputSchema: z.AnyZodObject; projectGraphInputSchema: z.AnyZodObject; readResourceGraphSnapshot: ( ctx: any, input: any, ) => Promise; readProjectGraphSnapshot: ( ctx: any, input: any, ) => Promise; }) { const resourceGraphDetailInputSchema = input.resourceGraphInputSchema.extend({ domain: z.string().trim().min(1).optional(), includeLinks: z.boolean().optional(), }); const projectGraphDetailInputSchema = input.projectGraphInputSchema.extend({ domain: z.string().trim().min(1).optional(), includeLinks: z.boolean().optional(), }); return { getResourceDataDetail: controllerProcedure .input(resourceGraphDetailInputSchema) .query(async ({ ctx, input: procedureInput }) => { const graph = await input.readResourceGraphSnapshot(ctx, procedureInput); return formatResourceGraphDetail({ resourceId: procedureInput.resourceId, graph, ...(typeof procedureInput.domain === "string" ? { domain: procedureInput.domain } : {}), ...(typeof procedureInput.includeLinks === "boolean" ? { includeLinks: procedureInput.includeLinks } : {}), }); }), getProjectDataDetail: controllerProcedure .input(projectGraphDetailInputSchema) .query(async ({ ctx, input: procedureInput }) => { const graph = await input.readProjectGraphSnapshot(ctx, procedureInput); return formatProjectGraphDetail({ projectId: procedureInput.projectId, graph, ...(typeof procedureInput.domain === "string" ? { domain: procedureInput.domain } : {}), ...(typeof procedureInput.includeLinks === "boolean" ? { includeLinks: procedureInput.includeLinks } : {}), }); }), }; }