refactoring
This commit is contained in:
142
frontend/src/lib/graph/renderingSignatures.ts
Normal file
142
frontend/src/lib/graph/renderingSignatures.ts
Normal file
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* Pure functions to build "connected node ids" and source signatures for the
|
||||
* rendering pipeline. Used by useRenderingNodeState so the hook stays thin and
|
||||
* this logic is testable in isolation.
|
||||
*/
|
||||
|
||||
import { getConfigContent } from '@/lib/graph/rendering'
|
||||
import { isReachable, resolveExtendsRef, getTemplateRefs } from '@/lib/graph/templateRefs'
|
||||
|
||||
export type NodeLike = { id: string; type?: string; data?: unknown }
|
||||
export type EdgeLike = { source: string; target: string }
|
||||
|
||||
/**
|
||||
* Collects all node ids that affect the render: direct incoming nodes plus
|
||||
* config nodes reached via template refs (extends/include/import) that are
|
||||
* reachable to the render node.
|
||||
*/
|
||||
export function buildConnectedNodeIds(
|
||||
nodes: NodeLike[],
|
||||
edges: EdgeLike[],
|
||||
renderNodeId: string,
|
||||
incomingIds: string[],
|
||||
getContent: (data: Record<string, unknown> | undefined) => string = getConfigContent
|
||||
): Set<string> {
|
||||
const out = new Set<string>()
|
||||
const addConfigRefs = (nodeId: string, visited: Set<string>) => {
|
||||
if (visited.has(nodeId)) return
|
||||
const node = nodes.find((n) => n.id === nodeId && n.type === 'config')
|
||||
if (!node) return
|
||||
visited.add(nodeId)
|
||||
out.add(nodeId)
|
||||
const content = getContent((node.data ?? undefined) as Record<string, unknown> | undefined)
|
||||
for (const ref of getTemplateRefs(content)) {
|
||||
const refId = resolveExtendsRef(nodes, ref)
|
||||
if (
|
||||
refId &&
|
||||
nodes.some((n) => n.id === refId && n.type === 'config') &&
|
||||
isReachable(edges, refId, renderNodeId)
|
||||
) {
|
||||
addConfigRefs(refId, visited)
|
||||
}
|
||||
}
|
||||
}
|
||||
const configVisited = new Set<string>()
|
||||
for (const nid of incomingIds) {
|
||||
const node = nodes.find((n) => n.id === nid)
|
||||
if (node?.type === 'config') addConfigRefs(nid, configVisited)
|
||||
else out.add(nid)
|
||||
}
|
||||
for (const e of edges) {
|
||||
if (out.has(e.target)) out.add(e.source)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
export type SourceSignatures = {
|
||||
connectedNodeIds: Set<string>
|
||||
configSignature: string
|
||||
edgesSignature: string
|
||||
variablesSignature: string
|
||||
functionsSignature: string
|
||||
dataSignature: string
|
||||
sourceSignature: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds connected node set and all signatures used to decide when to re-run
|
||||
* the resolve → render pipeline.
|
||||
*/
|
||||
export function buildSourceSignatures(
|
||||
nodes: NodeLike[],
|
||||
edges: EdgeLike[],
|
||||
renderNodeId: string,
|
||||
incomingIds: string[],
|
||||
getContent: (data: Record<string, unknown> | undefined) => string = getConfigContent
|
||||
): SourceSignatures {
|
||||
const connectedNodeIds = buildConnectedNodeIds(
|
||||
nodes,
|
||||
edges,
|
||||
renderNodeId,
|
||||
incomingIds,
|
||||
getContent
|
||||
)
|
||||
|
||||
const configSignature = nodes
|
||||
.filter((n) => n.type === 'config' && connectedNodeIds.has(n.id))
|
||||
.map(
|
||||
(n) =>
|
||||
`${n.id}:${(n.data as { title?: string })?.title ?? ''}:${getContent(n.data as Record<string, unknown>)}`
|
||||
)
|
||||
.sort()
|
||||
.join('|')
|
||||
|
||||
const edgesSignature = edges
|
||||
.filter(
|
||||
(e) =>
|
||||
connectedNodeIds.has(e.source) &&
|
||||
(connectedNodeIds.has(e.target) || e.target === renderNodeId)
|
||||
)
|
||||
.map((e) => `${e.source}->${e.target}`)
|
||||
.sort()
|
||||
.join('|')
|
||||
|
||||
const variablesSignature = nodes
|
||||
.filter((n) => n.type === 'variable' && connectedNodeIds.has(n.id))
|
||||
.map((n) => `${n.id}:${(n.data as { value?: unknown })?.value}`)
|
||||
.sort()
|
||||
.join('|')
|
||||
|
||||
const functionsSignature = nodes
|
||||
.filter((n) => n.type === 'function' && connectedNodeIds.has(n.id))
|
||||
.map((n) => `${n.id}:${(n.data as { body?: string })?.body ?? ''}`)
|
||||
.sort()
|
||||
.join('|')
|
||||
|
||||
const dataSignature = nodes
|
||||
.filter((n) => n.type === 'data' && connectedNodeIds.has(n.id))
|
||||
.map(
|
||||
(n) =>
|
||||
`${n.id}:${JSON.stringify((n.data as { rows?: unknown[] })?.rows ?? [])}:${JSON.stringify((n.data as { hiddenColumns?: unknown[] })?.hiddenColumns ?? [])}`
|
||||
)
|
||||
.sort()
|
||||
.join('|')
|
||||
|
||||
const sourceSignature = JSON.stringify({
|
||||
configSignature,
|
||||
edgesSignature,
|
||||
variablesSignature,
|
||||
functionsSignature,
|
||||
dataSignature,
|
||||
})
|
||||
|
||||
return {
|
||||
connectedNodeIds,
|
||||
configSignature,
|
||||
edgesSignature,
|
||||
variablesSignature,
|
||||
functionsSignature,
|
||||
dataSignature,
|
||||
sourceSignature,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user