From 498fc64b600b675b8ac50f87c8a536418776807e Mon Sep 17 00:00:00 2001 From: Siddharth Ganesan Date: Sat, 15 Nov 2025 14:59:23 -0800 Subject: [PATCH 1/3] Fix variable <> --- .../sub-block/components/code/code.tsx | 9 +- .../condition-input/condition-input.tsx | 9 +- .../sub-block/components/formatted-text.tsx | 8 +- .../editor/hooks/use-subflow-editor.ts | 9 +- apps/sim/executor/orchestrators/loop.ts | 7 +- .../executor/utils/reference-validation.ts | 85 +++++++++++++++++++ apps/sim/executor/variables/resolver.ts | 17 ++-- 7 files changed, 120 insertions(+), 24 deletions(-) create mode 100644 apps/sim/executor/utils/reference-validation.ts diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/code/code.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/code/code.tsx index 8fec01358a..8fdc4fb1b6 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/code/code.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/code/code.tsx @@ -13,6 +13,10 @@ import { languages, } from '@/components/emcn/components/code/code' import { Button } from '@/components/ui/button' +import { + createEnvVarPattern, + createReferencePattern, +} from '@/executor/utils/reference-validation' import { CodeLanguage } from '@/lib/execution/languages' import { createLogger } from '@/lib/logs/console/logger' import { cn } from '@/lib/utils' @@ -99,14 +103,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 " should match separately) + processedCode = processedCode.replace(createReferencePattern(), (match) => { if (shouldHighlightReference(match)) { const placeholder = `__VAR_REF_${placeholders.length}__` placeholders.push({ placeholder, original: match, type: 'var' }) diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/condition-input/condition-input.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/condition-input/condition-input.tsx index d3972f1ebb..c74457a24d 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/condition-input/condition-input.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/condition-input/condition-input.tsx @@ -14,6 +14,10 @@ import { languages, } from '@/components/emcn/components/code/code' import { Trash } from '@/components/emcn/icons/trash' +import { + createEnvVarPattern, + createReferencePattern, +} from '@/executor/utils/reference-validation' import { createLogger } from '@/lib/logs/console/logger' import { cn } from '@/lib/utils' import { @@ -869,7 +873,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,7 +885,8 @@ export function ConditionInput({ }) // Replace variable references with placeholders - processedCode = processedCode.replace(/<([^>]+)>/g, (match) => { + // Use [^<>]+ to prevent matching across nested brackets (e.g., "<3 " should match separately) + processedCode = processedCode.replace(createReferencePattern(), (match) => { const shouldHighlight = shouldHighlightReference(match) if (shouldHighlight) { const placeholder = `__VAR_REF_${placeholders.length}__` diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/formatted-text.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/formatted-text.tsx index ff97cf4715..342196d042 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/formatted-text.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/formatted-text.tsx @@ -1,6 +1,8 @@ 'use client' import type { ReactNode } from 'react' +import { REFERENCE } from '@/executor/consts' +import { createCombinedPattern } from '@/executor/utils/reference-validation' import { splitReferenceSegment } from '@/lib/workflows/references' import { normalizeBlockName } from '@/stores/workflows/utils' @@ -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 " should match "<3" and "", 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( {matchText} diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/hooks/use-subflow-editor.ts b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/hooks/use-subflow-editor.ts index c238c28010..3aa93fd38f 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/hooks/use-subflow-editor.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/hooks/use-subflow-editor.ts @@ -1,5 +1,9 @@ import { useCallback, useRef, useState } from 'react' import { highlight, languages } from '@/components/emcn' +import { + createEnvVarPattern, + createReferencePattern, +} from '@/executor/utils/reference-validation' import { isLikelyReferenceSegment, SYSTEM_REFERENCE_PREFIXES, @@ -133,13 +137,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 " should match separately) + processedCode = processedCode.replace(createReferencePattern(), (match) => { if (shouldHighlightReference(match)) { const placeholder = `__VAR_REF_${placeholders.length}__` placeholders.push({ placeholder, original: match, type: 'var' }) diff --git a/apps/sim/executor/orchestrators/loop.ts b/apps/sim/executor/orchestrators/loop.ts index dcc2f82f0b..aee8f4cc4c 100644 --- a/apps/sim/executor/orchestrators/loop.ts +++ b/apps/sim/executor/orchestrators/loop.ts @@ -12,6 +12,7 @@ import { } from '@/executor/utils/subflow-utils' import type { VariableResolver } from '@/executor/variables/resolver' import type { SerializedLoop } from '@/serializer/types' +import { replaceValidReferences } from '@/executor/utils/reference-validation' const logger = createLogger('LoopOrchestrator') @@ -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, diff --git a/apps/sim/executor/utils/reference-validation.ts b/apps/sim/executor/utils/reference-validation.ts new file mode 100644 index 0000000000..342f318cac --- /dev/null +++ b/apps/sim/executor/utils/reference-validation.ts @@ -0,0 +1,85 @@ +import { REFERENCE } from '@/executor/consts' +import { isLikelyReferenceSegment } from '@/lib/workflows/references' + +/** + * Creates a regex pattern for matching variable references in the format + * This pattern matches content between < and > brackets without allowing nested brackets. + * Uses [^<>]+ to prevent matching across multiple reference attempts. + */ +export function createReferencePattern(): RegExp { + // Match < followed by content that doesn't contain < or >, followed by > + // This prevents matching "<3. text " as one big match + return new RegExp(`${REFERENCE.START}([^${REFERENCE.START}${REFERENCE.END}]+)${REFERENCE.END}`, 'g') +} + +/** + * Creates a regex pattern for matching environment variable references in the format {{variable}} + */ +export function createEnvVarPattern(): RegExp { + return new RegExp(`\\${REFERENCE.ENV_VAR_START}([^}]+)\\${REFERENCE.ENV_VAR_END}`, 'g') +} + +/** + * Creates a combined regex pattern for matching both variable references and environment variables + * Returns a pattern that matches: or {{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' + ) +} + +/** + * Validates if a matched string is a likely variable reference using smart detection. + * This prevents treating operators like "<5" or "< 10" as variable references. + * + * Rules: + * - Must contain a valid block/variable name before the first dot (if dot exists) + * - Cannot start with spaces + * - Cannot contain invalid characters like +, *, /, =, <, >, ! in the wrong positions + * - Must follow variable reference syntax: or + * + * @param match - The matched string (e.g., "") + * @returns true if this is likely a variable reference, false otherwise + */ +export function isValidVariableReference(match: string): boolean { + return isLikelyReferenceSegment(match) +} + +/** + * Replaces variable references in a template string with a callback function. + * Only processes matches that pass smart validation to avoid treating operators + * like "<" as variable reference opening brackets. + * + * @param template - The template string containing potential variable references + * @param replacer - Callback function that receives the match and returns the replacement + * @returns The template with valid variable references replaced + * + * @example + * ```ts + * const result = replaceValidReferences( + * "Value is and condition < 10", + * (match) => resolveReference(match) + * ) + * // Only and are replaced, "< 10" is left as-is + * ``` + */ +export function replaceValidReferences( + template: string, + replacer: (match: string) => string +): string { + const pattern = createReferencePattern() + + return template.replace(pattern, (match) => { + // Smart validation: only process if this looks like a valid variable reference + // This prevents treating things like "<5" or "< 10" as variable references + if (!isValidVariableReference(match)) { + return match + } + + return replacer(match) + }) +} + diff --git a/apps/sim/executor/variables/resolver.ts b/apps/sim/executor/variables/resolver.ts index c8641e0bbe..60106b219e 100644 --- a/apps/sim/executor/variables/resolver.ts +++ b/apps/sim/executor/variables/resolver.ts @@ -9,6 +9,7 @@ import { ParallelResolver } from '@/executor/variables/resolvers/parallel' import type { ResolutionContext, Resolver } from '@/executor/variables/resolvers/reference' import { WorkflowResolver } from '@/executor/variables/resolvers/workflow' import type { SerializedBlock, SerializedWorkflow } from '@/serializer/types' +import { replaceValidReferences } from '@/executor/utils/reference-validation' const logger = createLogger('VariableResolver') @@ -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 { From d51979e9d2fb1f965b7ab59092552e5f37cce4f9 Mon Sep 17 00:00:00 2001 From: Siddharth Ganesan Date: Sat, 15 Nov 2025 14:59:36 -0800 Subject: [PATCH 2/3] Ling --- .../sub-block/components/code/code.tsx | 5 +-- .../condition-input/condition-input.tsx | 34 +++++++++---------- .../sub-block/components/formatted-text.tsx | 2 +- .../editor/hooks/use-subflow-editor.ts | 5 +-- apps/sim/executor/orchestrators/loop.ts | 2 +- .../executor/utils/reference-validation.ts | 22 ++++++------ apps/sim/executor/variables/resolver.ts | 2 +- 7 files changed, 34 insertions(+), 38 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/code/code.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/code/code.tsx index 8fdc4fb1b6..786de33f2f 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/code/code.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/code/code.tsx @@ -13,10 +13,6 @@ import { languages, } from '@/components/emcn/components/code/code' import { Button } from '@/components/ui/button' -import { - createEnvVarPattern, - createReferencePattern, -} from '@/executor/utils/reference-validation' import { CodeLanguage } from '@/lib/execution/languages' import { createLogger } from '@/lib/logs/console/logger' import { cn } from '@/lib/utils' @@ -39,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' diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/condition-input/condition-input.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/condition-input/condition-input.tsx index c74457a24d..96014130c2 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/condition-input/condition-input.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/condition-input/condition-input.tsx @@ -14,10 +14,6 @@ import { languages, } from '@/components/emcn/components/code/code' import { Trash } from '@/components/emcn/icons/trash' -import { - createEnvVarPattern, - createReferencePattern, -} from '@/executor/utils/reference-validation' import { createLogger } from '@/lib/logs/console/logger' import { cn } from '@/lib/utils' import { @@ -35,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' @@ -886,20 +883,23 @@ export function ConditionInput({ // Replace variable references with placeholders // Use [^<>]+ to prevent matching across nested brackets (e.g., "<3 " 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 + 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( diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/formatted-text.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/formatted-text.tsx index 342196d042..6ce3443f39 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/formatted-text.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/components/sub-block/components/formatted-text.tsx @@ -1,9 +1,9 @@ 'use client' import type { ReactNode } from 'react' +import { splitReferenceSegment } from '@/lib/workflows/references' import { REFERENCE } from '@/executor/consts' import { createCombinedPattern } from '@/executor/utils/reference-validation' -import { splitReferenceSegment } from '@/lib/workflows/references' import { normalizeBlockName } from '@/stores/workflows/utils' export interface HighlightContext { diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/hooks/use-subflow-editor.ts b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/hooks/use-subflow-editor.ts index 3aa93fd38f..96516bcc7f 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/hooks/use-subflow-editor.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel-new/components/editor/hooks/use-subflow-editor.ts @@ -1,9 +1,5 @@ import { useCallback, useRef, useState } from 'react' import { highlight, languages } from '@/components/emcn' -import { - createEnvVarPattern, - createReferencePattern, -} from '@/executor/utils/reference-validation' import { isLikelyReferenceSegment, SYSTEM_REFERENCE_PREFIXES, @@ -11,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' diff --git a/apps/sim/executor/orchestrators/loop.ts b/apps/sim/executor/orchestrators/loop.ts index aee8f4cc4c..93dde42b0a 100644 --- a/apps/sim/executor/orchestrators/loop.ts +++ b/apps/sim/executor/orchestrators/loop.ts @@ -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, @@ -12,7 +13,6 @@ import { } from '@/executor/utils/subflow-utils' import type { VariableResolver } from '@/executor/variables/resolver' import type { SerializedLoop } from '@/serializer/types' -import { replaceValidReferences } from '@/executor/utils/reference-validation' const logger = createLogger('LoopOrchestrator') diff --git a/apps/sim/executor/utils/reference-validation.ts b/apps/sim/executor/utils/reference-validation.ts index 342f318cac..d346da2691 100644 --- a/apps/sim/executor/utils/reference-validation.ts +++ b/apps/sim/executor/utils/reference-validation.ts @@ -1,5 +1,5 @@ -import { REFERENCE } from '@/executor/consts' import { isLikelyReferenceSegment } from '@/lib/workflows/references' +import { REFERENCE } from '@/executor/consts' /** * Creates a regex pattern for matching variable references in the format @@ -9,7 +9,10 @@ import { isLikelyReferenceSegment } from '@/lib/workflows/references' export function createReferencePattern(): RegExp { // Match < followed by content that doesn't contain < or >, followed by > // This prevents matching "<3. text " as one big match - return new RegExp(`${REFERENCE.START}([^${REFERENCE.START}${REFERENCE.END}]+)${REFERENCE.END}`, 'g') + return new RegExp( + `${REFERENCE.START}([^${REFERENCE.START}${REFERENCE.END}]+)${REFERENCE.END}`, + 'g' + ) } /** @@ -26,7 +29,7 @@ export function createEnvVarPattern(): RegExp { export function createCombinedPattern(): RegExp { return new RegExp( `${REFERENCE.START}[^${REFERENCE.START}${REFERENCE.END}]+${REFERENCE.END}|` + - `\\${REFERENCE.ENV_VAR_START}[^}]+\\${REFERENCE.ENV_VAR_END}`, + `\\${REFERENCE.ENV_VAR_START}[^}]+\\${REFERENCE.ENV_VAR_END}`, 'g' ) } @@ -34,13 +37,13 @@ export function createCombinedPattern(): RegExp { /** * Validates if a matched string is a likely variable reference using smart detection. * This prevents treating operators like "<5" or "< 10" as variable references. - * + * * Rules: * - Must contain a valid block/variable name before the first dot (if dot exists) * - Cannot start with spaces * - Cannot contain invalid characters like +, *, /, =, <, >, ! in the wrong positions * - Must follow variable reference syntax: or - * + * * @param match - The matched string (e.g., "") * @returns true if this is likely a variable reference, false otherwise */ @@ -52,11 +55,11 @@ export function isValidVariableReference(match: string): boolean { * Replaces variable references in a template string with a callback function. * Only processes matches that pass smart validation to avoid treating operators * like "<" as variable reference opening brackets. - * + * * @param template - The template string containing potential variable references * @param replacer - Callback function that receives the match and returns the replacement * @returns The template with valid variable references replaced - * + * * @example * ```ts * const result = replaceValidReferences( @@ -71,15 +74,14 @@ export function replaceValidReferences( replacer: (match: string) => string ): string { const pattern = createReferencePattern() - + return template.replace(pattern, (match) => { // Smart validation: only process if this looks like a valid variable reference // This prevents treating things like "<5" or "< 10" as variable references if (!isValidVariableReference(match)) { return match } - + return replacer(match) }) } - diff --git a/apps/sim/executor/variables/resolver.ts b/apps/sim/executor/variables/resolver.ts index 60106b219e..d5aa1dfbab 100644 --- a/apps/sim/executor/variables/resolver.ts +++ b/apps/sim/executor/variables/resolver.ts @@ -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' @@ -9,7 +10,6 @@ import { ParallelResolver } from '@/executor/variables/resolvers/parallel' import type { ResolutionContext, Resolver } from '@/executor/variables/resolvers/reference' import { WorkflowResolver } from '@/executor/variables/resolvers/workflow' import type { SerializedBlock, SerializedWorkflow } from '@/serializer/types' -import { replaceValidReferences } from '@/executor/utils/reference-validation' const logger = createLogger('VariableResolver') From 04f202de6704f7756422763a2a6613913e5dd51f Mon Sep 17 00:00:00 2001 From: Siddharth Ganesan Date: Sat, 15 Nov 2025 15:03:22 -0800 Subject: [PATCH 3/3] Clean --- .../executor/utils/reference-validation.ts | 52 +++---------------- 1 file changed, 7 insertions(+), 45 deletions(-) diff --git a/apps/sim/executor/utils/reference-validation.ts b/apps/sim/executor/utils/reference-validation.ts index d346da2691..906af32876 100644 --- a/apps/sim/executor/utils/reference-validation.ts +++ b/apps/sim/executor/utils/reference-validation.ts @@ -2,13 +2,10 @@ import { isLikelyReferenceSegment } from '@/lib/workflows/references' import { REFERENCE } from '@/executor/consts' /** - * Creates a regex pattern for matching variable references in the format - * This pattern matches content between < and > brackets without allowing nested brackets. - * Uses [^<>]+ to prevent matching across multiple reference attempts. + * Creates a regex pattern for matching variable references. + * Uses [^<>]+ to prevent matching across nested brackets (e.g., "<3 " matches separately). */ export function createReferencePattern(): RegExp { - // Match < followed by content that doesn't contain < or >, followed by > - // This prevents matching "<3. text " as one big match return new RegExp( `${REFERENCE.START}([^${REFERENCE.START}${REFERENCE.END}]+)${REFERENCE.END}`, 'g' @@ -16,15 +13,14 @@ export function createReferencePattern(): RegExp { } /** - * Creates a regex pattern for matching environment variable references in the format {{variable}} + * 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') } /** - * Creates a combined regex pattern for matching both variable references and environment variables - * Returns a pattern that matches: or {{env_var}} + * Combined pattern matching both and {{env_var}} */ export function createCombinedPattern(): RegExp { return new RegExp( @@ -35,39 +31,8 @@ export function createCombinedPattern(): RegExp { } /** - * Validates if a matched string is a likely variable reference using smart detection. - * This prevents treating operators like "<5" or "< 10" as variable references. - * - * Rules: - * - Must contain a valid block/variable name before the first dot (if dot exists) - * - Cannot start with spaces - * - Cannot contain invalid characters like +, *, /, =, <, >, ! in the wrong positions - * - Must follow variable reference syntax: or - * - * @param match - The matched string (e.g., "") - * @returns true if this is likely a variable reference, false otherwise - */ -export function isValidVariableReference(match: string): boolean { - return isLikelyReferenceSegment(match) -} - -/** - * Replaces variable references in a template string with a callback function. - * Only processes matches that pass smart validation to avoid treating operators - * like "<" as variable reference opening brackets. - * - * @param template - The template string containing potential variable references - * @param replacer - Callback function that receives the match and returns the replacement - * @returns The template with valid variable references replaced - * - * @example - * ```ts - * const result = replaceValidReferences( - * "Value is and condition < 10", - * (match) => resolveReference(match) - * ) - * // Only and are replaced, "< 10" is left as-is - * ``` + * Replaces variable references with smart validation. + * Distinguishes < operator from < bracket using isLikelyReferenceSegment. */ export function replaceValidReferences( template: string, @@ -76,12 +41,9 @@ export function replaceValidReferences( const pattern = createReferencePattern() return template.replace(pattern, (match) => { - // Smart validation: only process if this looks like a valid variable reference - // This prevents treating things like "<5" or "< 10" as variable references - if (!isValidVariableReference(match)) { + if (!isLikelyReferenceSegment(match)) { return match } - return replacer(match) }) }