Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { AgentSkillsIcon, McpIcon } from '@/components/icons'
import { getDocumentIcon } from '@/components/icons/document-icons'
import type { ChatContextKind, ChatMessageContext } from '@/app/workspace/[workspaceId]/home/types'
import { getBareIconStyle } from '@/blocks/icon-color'
import { registry as blockRegistry } from '@/blocks/registry'
import { getBlockRegistry } from '@/blocks/registry'

interface RenderIconArgs {
context: ChatMessageContext
Expand All @@ -41,7 +41,7 @@ function renderWorkflowIcon({ className }: RenderIconArgs): ReactNode | null {
function renderIntegrationTile({ context, className }: RenderIconArgs): ReactNode | null {
if (context.kind !== 'integration') return null
if (!context.blockType) return null
const block = blockRegistry[context.blockType]
const block = getBlockRegistry()[context.blockType]
if (!block) return null
const Icon = block.icon
return <Icon className={className} style={getBareIconStyle(Icon)} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,8 @@ export function useMentionData(props: UseMentionDataProps): MentionDataReturn {
// Fetch current blocks from store
const workflowStoreBlocks = useWorkflowStore.getState().blocks

const { registry: blockRegistry } = await import('@/blocks/registry')
const { getBlockRegistry } = await import('@/blocks/registry')
const blockRegistry = getBlockRegistry()
const mapped = Object.values(workflowStoreBlocks).map((b: any) => {
const reg = (blockRegistry as any)[b.type]
return {
Expand Down
35 changes: 29 additions & 6 deletions apps/sim/blocks/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,32 @@ export function getBlock(type: string): BlockConfig | undefined {
return BLOCK_REGISTRY[type] ?? BLOCK_REGISTRY[normalizeType(type)] ?? resolveOverlayBlock(type)
}

/** Whether any registered block is an unreleased `preview` block. Static — computed once. */
const HAS_PREVIEW_BLOCKS = Object.values(BLOCK_REGISTRY).some((block) => block.preview)
/**
* Whether any registered block is an unreleased `preview` block. Computed once,
* on first use rather than at module scope.
*
* `blocks/registry-maps` and this module sit in an import cycle (a block config
* reaches back here through `providers/utils` → `tools/params` → `blocks/index`),
* so whichever module the cycle is entered through evaluates first. Reading
* `BLOCK_REGISTRY` at module scope therefore threw
* `ReferenceError: Cannot access 'BLOCK_REGISTRY' before initialization` for
* anything that imported `blocks/registry-maps` directly — scripts and tests
* under Bun. It only worked under Turbopack because that bundler happened to
* order the modules favourably, which is not a guarantee to build on.
*/
let hasPreviewBlocks: boolean | undefined
function anyPreviewBlocks(): boolean {
hasPreviewBlocks ??= Object.values(BLOCK_REGISTRY).some((block) => block.preview)
return hasPreviewBlocks
}

/**
* True when the visibility projection cannot change any block, so accessors can
* return raw arrays untouched: no `preview` blocks exist (they must be hidden
* even with a null state) and no kill-switch entries apply.
*/
function visibilityInert(vis: BlockVisibilityState | null): boolean {
if (HAS_PREVIEW_BLOCKS) return false
if (anyPreviewBlocks()) return false
return vis === null || vis.disabled.size === 0
}

Expand Down Expand Up @@ -232,9 +248,16 @@ export function getSuggestedSkillsForBlock(type: string): readonly SuggestedSkil

/**
* Raw block registry map keyed by block type. Prefer the typed accessors
* (`getBlock`, `getAllBlocks`, `getCanonicalBlocksByCategory`); this alias is
* retained for callers that need the underlying record directly.
* (`getBlock`, `getAllBlocks`, `getCanonicalBlocksByCategory`); this is retained
* for callers that need the underlying record directly.
*
* A function rather than an eager `const` alias: binding `BLOCK_REGISTRY` at
* module scope re-introduces the initialization-order failure that
* {@link anyPreviewBlocks} documents, since this module can evaluate before
* `blocks/registry-maps` has finished.
*/
export const registry: Record<string, BlockConfig> = BLOCK_REGISTRY
export function getBlockRegistry(): Record<string, BlockConfig> {
return BLOCK_REGISTRY
}

export type { BlockCategory }
3 changes: 2 additions & 1 deletion apps/sim/lib/copilot/chat/process-contents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,8 @@ async function processBlockMetadata(
return null
}

const { registry: blockRegistry } = await import('@/blocks/registry')
const { getBlockRegistry } = await import('@/blocks/registry')
const blockRegistry = getBlockRegistry()
if (!(blockRegistry as any)[blockId]) {
return null
}
Expand Down
Loading