Compare commits

...

1 Commits

Author SHA1 Message Date
Waleed Latif
858ce16c1e fix(selectors): resolve env var references at design time for selector context
Environment variable references ({{VAR}}) are static values available
at design time via the environment store. Instead of skipping them like
block references, resolve them to their actual values so selectors can
use env-var-based credentials and context fields (e.g. {{SLACK_BOT_TOKEN}}).

Block references (<Block.output>) remain skipped since they can only
be resolved at execution time.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 15:23:08 -08:00
2 changed files with 42 additions and 9 deletions

View File

@@ -3,8 +3,9 @@
import { useMemo } from 'react'
import { useParams } from 'next/navigation'
import type { SubBlockConfig } from '@/blocks/types'
import { isEnvVarReference, isReference } from '@/executor/constants'
import { extractEnvVarName, isEnvVarReference, isReference } from '@/executor/constants'
import type { SelectorContext, SelectorKey } from '@/hooks/selectors/types'
import { useEnvironmentStore } from '@/stores/settings/environment'
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
import { useDependsOnGate } from './use-depends-on-gate'
@@ -30,23 +31,43 @@ export function useSelectorSetup(
const activeWorkflowId = useWorkflowRegistry((s) => s.activeWorkflowId)
const workflowId = (params?.workflowId as string) || activeWorkflowId || ''
const envVariables = useEnvironmentStore((s) => s.variables)
const { finalDisabled, dependencyValues, canonicalIndex } = useDependsOnGate(
blockId,
subBlock,
opts
)
const resolvedDependencyValues = useMemo(() => {
const resolved: Record<string, unknown> = {}
for (const [key, value] of Object.entries(dependencyValues)) {
if (value === null || value === undefined) {
resolved[key] = value
continue
}
const str = String(value)
if (isEnvVarReference(str)) {
const varName = extractEnvVarName(str)
resolved[key] = envVariables[varName]?.value ?? value
} else {
resolved[key] = value
}
}
return resolved
}, [dependencyValues, envVariables])
const selectorContext = useMemo<SelectorContext>(() => {
const context: SelectorContext = {
workflowId,
mimeType: subBlock.mimeType,
}
for (const [depKey, value] of Object.entries(dependencyValues)) {
for (const [depKey, value] of Object.entries(resolvedDependencyValues)) {
if (value === null || value === undefined) continue
const strValue = String(value)
if (!strValue) continue
if (isReference(strValue) || isEnvVarReference(strValue)) continue
if (isReference(strValue)) continue
const canonicalParamId = canonicalIndex.canonicalIdBySubBlockId[depKey] ?? depKey
@@ -58,14 +79,14 @@ export function useSelectorSetup(
}
return context
}, [dependencyValues, canonicalIndex, workflowId, subBlock.mimeType])
}, [resolvedDependencyValues, canonicalIndex, workflowId, subBlock.mimeType])
return {
selectorKey: (subBlock.selectorKey ?? null) as SelectorKey | null,
selectorContext,
allowSearch: subBlock.selectorAllowSearch ?? true,
disabled: finalDisabled || !subBlock.selectorKey,
dependencyValues,
dependencyValues: resolvedDependencyValues,
}
}

View File

@@ -1,8 +1,9 @@
import { useMemo } from 'react'
import { useQuery } from '@tanstack/react-query'
import { isEnvVarReference, isReference } from '@/executor/constants'
import { extractEnvVarName, isEnvVarReference, isReference } from '@/executor/constants'
import { getSelectorDefinition, mergeOption } from '@/hooks/selectors/registry'
import type { SelectorKey, SelectorOption, SelectorQueryArgs } from '@/hooks/selectors/types'
import { useEnvironmentStore } from '@/stores/settings/environment'
interface SelectorHookArgs extends Omit<SelectorQueryArgs, 'key'> {
search?: string
@@ -30,14 +31,25 @@ export function useSelectorOptionDetail(
key: SelectorKey,
args: SelectorHookArgs & { detailId?: string }
) {
const envVariables = useEnvironmentStore((s) => s.variables)
const definition = getSelectorDefinition(key)
const resolvedDetailId = useMemo(() => {
if (!args.detailId) return undefined
if (isReference(args.detailId)) return undefined
if (isEnvVarReference(args.detailId)) {
const varName = extractEnvVarName(args.detailId)
return envVariables[varName]?.value || undefined
}
return args.detailId
}, [args.detailId, envVariables])
const queryArgs: SelectorQueryArgs = {
key,
context: args.context,
detailId: args.detailId,
detailId: resolvedDetailId,
}
const hasRealDetailId =
Boolean(args.detailId) && !isReference(args.detailId!) && !isEnvVarReference(args.detailId!)
const hasRealDetailId = Boolean(resolvedDetailId)
const baseEnabled =
hasRealDetailId && definition.fetchById !== undefined
? definition.enabled