mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-06 03:00:16 -04:00
fix(variables): Fix resolution on double < (#2016)
* Fix variable <> * Ling * Clean
This commit is contained in:
committed by
GitHub
parent
fca92a7499
commit
949f9287cf
@@ -35,6 +35,7 @@ import { WandPromptBar } from '@/app/workspace/[workspaceId]/w/[workflowId]/comp
|
||||
import { useAccessibleReferencePrefixes } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-accessible-reference-prefixes'
|
||||
import { useWand } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-wand'
|
||||
import type { GenerationType } from '@/blocks/types'
|
||||
import { createEnvVarPattern, createReferencePattern } from '@/executor/utils/reference-validation'
|
||||
import { useTagSelection } from '@/hooks/use-tag-selection'
|
||||
import { normalizeBlockName } from '@/stores/workflows/utils'
|
||||
|
||||
@@ -99,14 +100,15 @@ const createHighlightFunction = (
|
||||
let processedCode = codeToHighlight
|
||||
|
||||
// Replace environment variables with placeholders
|
||||
processedCode = processedCode.replace(/\{\{([^}]+)\}\}/g, (match) => {
|
||||
processedCode = processedCode.replace(createEnvVarPattern(), (match) => {
|
||||
const placeholder = `__ENV_VAR_${placeholders.length}__`
|
||||
placeholders.push({ placeholder, original: match, type: 'env' })
|
||||
return placeholder
|
||||
})
|
||||
|
||||
// Replace variable references with placeholders
|
||||
processedCode = processedCode.replace(/<([^>]+)>/g, (match) => {
|
||||
// Use [^<>]+ to prevent matching across nested brackets (e.g., "<3 <real.ref>" should match separately)
|
||||
processedCode = processedCode.replace(createReferencePattern(), (match) => {
|
||||
if (shouldHighlightReference(match)) {
|
||||
const placeholder = `__VAR_REF_${placeholders.length}__`
|
||||
placeholders.push({ placeholder, original: match, type: 'var' })
|
||||
|
||||
@@ -31,6 +31,7 @@ import {
|
||||
} from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown'
|
||||
import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/hooks/use-sub-block-value'
|
||||
import { useAccessibleReferencePrefixes } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-accessible-reference-prefixes'
|
||||
import { createEnvVarPattern, createReferencePattern } from '@/executor/utils/reference-validation'
|
||||
import { useTagSelection } from '@/hooks/use-tag-selection'
|
||||
import { normalizeBlockName } from '@/stores/workflows/utils'
|
||||
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
|
||||
@@ -869,7 +870,7 @@ export function ConditionInput({
|
||||
let processedCode = codeToHighlight
|
||||
|
||||
// Replace environment variables with placeholders
|
||||
processedCode = processedCode.replace(/\{\{([^}]+)\}\}/g, (match) => {
|
||||
processedCode = processedCode.replace(createEnvVarPattern(), (match) => {
|
||||
const placeholder = `__ENV_VAR_${placeholders.length}__`
|
||||
placeholders.push({
|
||||
placeholder,
|
||||
@@ -881,20 +882,24 @@ export function ConditionInput({
|
||||
})
|
||||
|
||||
// Replace variable references with placeholders
|
||||
processedCode = processedCode.replace(/<([^>]+)>/g, (match) => {
|
||||
const shouldHighlight = shouldHighlightReference(match)
|
||||
if (shouldHighlight) {
|
||||
const placeholder = `__VAR_REF_${placeholders.length}__`
|
||||
placeholders.push({
|
||||
placeholder,
|
||||
original: match,
|
||||
type: 'var',
|
||||
shouldHighlight: true,
|
||||
})
|
||||
return placeholder
|
||||
// Use [^<>]+ to prevent matching across nested brackets (e.g., "<3 <real.ref>" should match separately)
|
||||
processedCode = processedCode.replace(
|
||||
createReferencePattern(),
|
||||
(match) => {
|
||||
const shouldHighlight = shouldHighlightReference(match)
|
||||
if (shouldHighlight) {
|
||||
const placeholder = `__VAR_REF_${placeholders.length}__`
|
||||
placeholders.push({
|
||||
placeholder,
|
||||
original: match,
|
||||
type: 'var',
|
||||
shouldHighlight: true,
|
||||
})
|
||||
return placeholder
|
||||
}
|
||||
return match
|
||||
}
|
||||
return match
|
||||
})
|
||||
)
|
||||
|
||||
// Apply Prism syntax highlighting
|
||||
let highlightedCode = highlight(
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
import type { ReactNode } from 'react'
|
||||
import { splitReferenceSegment } from '@/lib/workflows/references'
|
||||
import { REFERENCE } from '@/executor/consts'
|
||||
import { createCombinedPattern } from '@/executor/utils/reference-validation'
|
||||
import { normalizeBlockName } from '@/stores/workflows/utils'
|
||||
|
||||
export interface HighlightContext {
|
||||
@@ -43,7 +45,9 @@ export function formatDisplayText(text: string, context?: HighlightContext): Rea
|
||||
}
|
||||
|
||||
const nodes: ReactNode[] = []
|
||||
const regex = /<[^>]+>|\{\{[^}]+\}\}/g
|
||||
// Match variable references without allowing nested brackets to prevent matching across references
|
||||
// e.g., "<3. text <real.ref>" should match "<3" and "<real.ref>", not the whole string
|
||||
const regex = createCombinedPattern()
|
||||
let lastIndex = 0
|
||||
let key = 0
|
||||
|
||||
@@ -61,7 +65,7 @@ export function formatDisplayText(text: string, context?: HighlightContext): Rea
|
||||
pushPlainText(text.slice(lastIndex, index))
|
||||
}
|
||||
|
||||
if (matchText.startsWith('{{')) {
|
||||
if (matchText.startsWith(REFERENCE.ENV_VAR_START)) {
|
||||
nodes.push(
|
||||
<span key={key++} className='text-[#34B5FF] dark:text-[#34B5FF]'>
|
||||
{matchText}
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
} from '@/lib/workflows/references'
|
||||
import { checkTagTrigger } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown'
|
||||
import { useAccessibleReferencePrefixes } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-accessible-reference-prefixes'
|
||||
import { createEnvVarPattern, createReferencePattern } from '@/executor/utils/reference-validation'
|
||||
import { useCollaborativeWorkflow } from '@/hooks/use-collaborative-workflow'
|
||||
import { normalizeBlockName } from '@/stores/workflows/utils'
|
||||
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
|
||||
@@ -133,13 +134,14 @@ export function useSubflowEditor(currentBlock: BlockState | null, currentBlockId
|
||||
|
||||
let processedCode = code
|
||||
|
||||
processedCode = processedCode.replace(/\{\{([^}]+)\}\}/g, (match) => {
|
||||
processedCode = processedCode.replace(createEnvVarPattern(), (match) => {
|
||||
const placeholder = `__ENV_VAR_${placeholders.length}__`
|
||||
placeholders.push({ placeholder, original: match, type: 'env' })
|
||||
return placeholder
|
||||
})
|
||||
|
||||
processedCode = processedCode.replace(/<[^>]+>/g, (match) => {
|
||||
// Use [^<>]+ to prevent matching across nested brackets (e.g., "<3 <real.ref>" should match separately)
|
||||
processedCode = processedCode.replace(createReferencePattern(), (match) => {
|
||||
if (shouldHighlightReference(match)) {
|
||||
const placeholder = `__VAR_REF_${placeholders.length}__`
|
||||
placeholders.push({ placeholder, original: match, type: 'var' })
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { LoopScope } from '@/executor/execution/state'
|
||||
import type { BlockStateController } from '@/executor/execution/types'
|
||||
import type { ExecutionContext, NormalizedBlockOutput } from '@/executor/types'
|
||||
import type { LoopConfigWithNodes } from '@/executor/types/loop'
|
||||
import { replaceValidReferences } from '@/executor/utils/reference-validation'
|
||||
import {
|
||||
buildSentinelEndId,
|
||||
buildSentinelStartId,
|
||||
@@ -271,16 +272,14 @@ export class LoopOrchestrator {
|
||||
}
|
||||
|
||||
try {
|
||||
const referencePattern = /<([^>]+)>/g
|
||||
let evaluatedCondition = condition
|
||||
|
||||
logger.info('Evaluating loop condition', {
|
||||
originalCondition: condition,
|
||||
iteration: scope.iteration,
|
||||
workflowVariables: ctx.workflowVariables,
|
||||
})
|
||||
|
||||
evaluatedCondition = evaluatedCondition.replace(referencePattern, (match) => {
|
||||
// Use generic utility for smart variable reference replacement
|
||||
const evaluatedCondition = replaceValidReferences(condition, (match) => {
|
||||
const resolved = this.resolver.resolveSingleReference(ctx, '', match, scope)
|
||||
logger.info('Resolved variable reference in loop condition', {
|
||||
reference: match,
|
||||
|
||||
49
apps/sim/executor/utils/reference-validation.ts
Normal file
49
apps/sim/executor/utils/reference-validation.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { isLikelyReferenceSegment } from '@/lib/workflows/references'
|
||||
import { REFERENCE } from '@/executor/consts'
|
||||
|
||||
/**
|
||||
* Creates a regex pattern for matching variable references.
|
||||
* Uses [^<>]+ to prevent matching across nested brackets (e.g., "<3 <real.ref>" matches separately).
|
||||
*/
|
||||
export function createReferencePattern(): RegExp {
|
||||
return new RegExp(
|
||||
`${REFERENCE.START}([^${REFERENCE.START}${REFERENCE.END}]+)${REFERENCE.END}`,
|
||||
'g'
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a regex pattern for matching environment variables {{variable}}
|
||||
*/
|
||||
export function createEnvVarPattern(): RegExp {
|
||||
return new RegExp(`\\${REFERENCE.ENV_VAR_START}([^}]+)\\${REFERENCE.ENV_VAR_END}`, 'g')
|
||||
}
|
||||
|
||||
/**
|
||||
* Combined pattern matching both <reference> and {{env_var}}
|
||||
*/
|
||||
export function createCombinedPattern(): RegExp {
|
||||
return new RegExp(
|
||||
`${REFERENCE.START}[^${REFERENCE.START}${REFERENCE.END}]+${REFERENCE.END}|` +
|
||||
`\\${REFERENCE.ENV_VAR_START}[^}]+\\${REFERENCE.ENV_VAR_END}`,
|
||||
'g'
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces variable references with smart validation.
|
||||
* Distinguishes < operator from < bracket using isLikelyReferenceSegment.
|
||||
*/
|
||||
export function replaceValidReferences(
|
||||
template: string,
|
||||
replacer: (match: string) => string
|
||||
): string {
|
||||
const pattern = createReferencePattern()
|
||||
|
||||
return template.replace(pattern, (match) => {
|
||||
if (!isLikelyReferenceSegment(match)) {
|
||||
return match
|
||||
}
|
||||
return replacer(match)
|
||||
})
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { BlockType, REFERENCE } from '@/executor/consts'
|
||||
import type { ExecutionState, LoopScope } from '@/executor/execution/state'
|
||||
import type { ExecutionContext } from '@/executor/types'
|
||||
import { replaceValidReferences } from '@/executor/utils/reference-validation'
|
||||
import { BlockResolver } from '@/executor/variables/resolvers/block'
|
||||
import { EnvResolver } from '@/executor/variables/resolvers/env'
|
||||
import { LoopResolver } from '@/executor/variables/resolvers/loop'
|
||||
@@ -147,21 +148,17 @@ export class VariableResolver {
|
||||
loopScope?: LoopScope,
|
||||
block?: SerializedBlock
|
||||
): string {
|
||||
let result = template
|
||||
const resolutionContext: ResolutionContext = {
|
||||
executionContext: ctx,
|
||||
executionState: this.state,
|
||||
currentNodeId,
|
||||
loopScope,
|
||||
}
|
||||
const referenceRegex = new RegExp(
|
||||
`${REFERENCE.START}([^${REFERENCE.END}]+)${REFERENCE.END}`,
|
||||
'g'
|
||||
)
|
||||
|
||||
let replacementError: Error | null = null
|
||||
|
||||
result = result.replace(referenceRegex, (match) => {
|
||||
// Use generic utility for smart variable reference replacement
|
||||
let result = replaceValidReferences(template, (match) => {
|
||||
if (replacementError) return match
|
||||
|
||||
try {
|
||||
@@ -202,21 +199,17 @@ export class VariableResolver {
|
||||
template: string,
|
||||
loopScope?: LoopScope
|
||||
): string {
|
||||
let result = template
|
||||
const resolutionContext: ResolutionContext = {
|
||||
executionContext: ctx,
|
||||
executionState: this.state,
|
||||
currentNodeId,
|
||||
loopScope,
|
||||
}
|
||||
const referenceRegex = new RegExp(
|
||||
`${REFERENCE.START}([^${REFERENCE.END}]+)${REFERENCE.END}`,
|
||||
'g'
|
||||
)
|
||||
|
||||
let replacementError: Error | null = null
|
||||
|
||||
result = result.replace(referenceRegex, (match) => {
|
||||
// Use generic utility for smart variable reference replacement
|
||||
let result = replaceValidReferences(template, (match) => {
|
||||
if (replacementError) return match
|
||||
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user