Skip to content

Commit 1c7fc31

Browse files
committed
refactor(workflow): type the node-utilities block map
`useNodeUtilities` took `Record<string, any>`, so the test fixtures had to be cast to reach it and nothing in the hook was checked against a real block. Typing it as `Record<string, BlockState>` surfaced an unsafe read straight away: the cycle walk re-read `blocks[currentId].data.parentId` after the `while` condition had tested the same optional chain, on a map where both links are optional. It now reads the value once and breaks on absence, which is what the condition was trying to express. The fixtures follow the hook's own parameter type, so they stay honest without a cast on either side.
1 parent 5e1aa23 commit 1c7fc31

2 files changed

Lines changed: 9 additions & 7 deletions

File tree

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-node-utilities.test.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ vi.mock('reactflow', () => ({
1818
import { useNodeUtilities } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-node-utilities'
1919

2020
/** Renders the hook and hands back what it returned, without a test library. */
21-
function renderNodeUtilities(blockMap: Record<string, unknown>) {
21+
function renderNodeUtilities(blockMap: Parameters<typeof useNodeUtilities>[0]) {
2222
let api: ReturnType<typeof useNodeUtilities> | null = null
2323
function Probe() {
24-
api = useNodeUtilities(blockMap as Record<string, any>)
24+
api = useNodeUtilities(blockMap)
2525
return null
2626
}
2727
const host = document.createElement('div')
@@ -43,7 +43,7 @@ const CHILD_POSITION = {
4343
y: CONTAINER_DIMENSIONS.HEADER_HEIGHT + CONTAINER_DIMENSIONS.TOP_PADDING,
4444
}
4545

46-
const blocks = {
46+
const blocks: Parameters<typeof useNodeUtilities>[0] = {
4747
loop: { id: 'loop', type: 'loop', position: CONTAINER_POSITION, data: {} },
4848
child: { id: 'child', type: 'gmail_v2', position: CHILD_POSITION, data: { parentId: 'loop' } },
4949
root: { id: 'root', type: 'gmail_v2', position: { x: 10, y: 20 }, data: {} },

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-node-utilities.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useCallback } from 'react'
22
import { createLogger } from '@sim/logger'
33
import { BLOCK_DIMENSIONS, CONTAINER_DIMENSIONS, getNoteBlockHeight } from '@sim/workflow-renderer'
4+
import type { BlockState } from '@sim/workflow-types/workflow'
45
import { useReactFlow } from 'reactflow'
56
import { getBlockMetrics } from '@/lib/workflows/autolayout/utils'
67
import {
@@ -14,7 +15,7 @@ const logger = createLogger('NodeUtilities')
1415
/**
1516
* Hook providing utilities for node position, hierarchy, and dimension calculations
1617
*/
17-
export function useNodeUtilities(blocks: Record<string, any>) {
18+
export function useNodeUtilities(blocks: Record<string, BlockState>) {
1819
const { getNodes } = useReactFlow()
1920

2021
/**
@@ -180,9 +181,10 @@ export function useNodeUtilities(blocks: Record<string, any>) {
180181
}
181182

182183
const visited = new Set<string>()
183-
let currentId = nodeId
184-
while (currentId && blocks?.[currentId]?.data?.parentId) {
185-
const currentParentId = blocks[currentId].data.parentId
184+
let currentId: string | undefined = nodeId
185+
while (currentId) {
186+
const currentParentId: string | undefined = blocks[currentId]?.data?.parentId
187+
if (!currentParentId) break
186188
if (visited.has(currentParentId)) {
187189
logger.error('Circular parent reference detected', {
188190
nodeId,

0 commit comments

Comments
 (0)