diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/chat-context-kind-registry/chat-context-kind-registry.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/chat-context-kind-registry/chat-context-kind-registry.tsx index c599b5e197a..233b0e8d202 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/chat-context-kind-registry/chat-context-kind-registry.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/chat-context-kind-registry/chat-context-kind-registry.tsx @@ -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 @@ -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 diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/user-input/hooks/use-mention-data.ts b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/user-input/hooks/use-mention-data.ts index d0d0a1a43a9..2736c964f75 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/user-input/hooks/use-mention-data.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/user-input/hooks/use-mention-data.ts @@ -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 { diff --git a/apps/sim/blocks/registry.ts b/apps/sim/blocks/registry.ts index d87b685347a..6d1170e5198 100644 --- a/apps/sim/blocks/registry.ts +++ b/apps/sim/blocks/registry.ts @@ -24,8 +24,24 @@ 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 @@ -33,7 +49,7 @@ const HAS_PREVIEW_BLOCKS = Object.values(BLOCK_REGISTRY).some((block) => block.p * 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 } @@ -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 = BLOCK_REGISTRY +export function getBlockRegistry(): Record { + return BLOCK_REGISTRY +} export type { BlockCategory } diff --git a/apps/sim/lib/copilot/chat/process-contents.ts b/apps/sim/lib/copilot/chat/process-contents.ts index 54fce5d321c..1d5096fa84d 100644 --- a/apps/sim/lib/copilot/chat/process-contents.ts +++ b/apps/sim/lib/copilot/chat/process-contents.ts @@ -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 }