diff --git a/packages/twenty-shared/src/utils/__tests__/rich-text-variable-resolver.test.ts b/packages/twenty-shared/src/utils/__tests__/rich-text-variable-resolver.test.ts index 4e2dc815e0..241fc90695 100644 --- a/packages/twenty-shared/src/utils/__tests__/rich-text-variable-resolver.test.ts +++ b/packages/twenty-shared/src/utils/__tests__/rich-text-variable-resolver.test.ts @@ -126,6 +126,23 @@ describe('resolveRichTextVariables', () => { ); }); + it('should serialize object values as JSON instead of [object Object]', () => { + const contextWithObject = { + step1: { + message: { foo: 'bar', count: 1 }, + }, + }; + + const input = + '[{"type":"paragraph","content":[{"type":"variableTag","attrs":{"variable":"{{step1.message}}"}}]}]'; + + const result = resolveRichTextVariables(input, contextWithObject); + + expect(result).toBe( + '[{"type":"paragraph","content":[{"type":"text","text":"{\\"foo\\":\\"bar\\",\\"count\\":1}"}]}]', + ); + }); + it('should preserve regular {{variable}} patterns in non-variableTag contexts', () => { const input = '[{"type":"paragraph","content":[{"type":"text","text":"Regular {{step1.message}} pattern"}]}]'; diff --git a/packages/twenty-shared/src/utils/__tests__/variable-resolver.test.ts b/packages/twenty-shared/src/utils/__tests__/variable-resolver.test.ts index 4912493359..f8013e86a8 100644 --- a/packages/twenty-shared/src/utils/__tests__/variable-resolver.test.ts +++ b/packages/twenty-shared/src/utils/__tests__/variable-resolver.test.ts @@ -94,6 +94,27 @@ describe('resolveInput', () => { expect(resolveInput(input, context)).toEqual(expected); }); + it('should serialize an object variable embedded in a string', () => { + expect(resolveInput('Log this message: {{user}}', context)).toBe( + 'Log this message: {"name":"John Doe","age":30}', + ); + }); + + it('should serialize an array variable embedded in a string', () => { + expect( + resolveInput('Themes: {{preferences}}', { + preferences: ['dark', 'light'], + }), + ).toBe('Themes: ["dark","light"]'); + }); + + it('should return the raw object when the whole string is a single variable', () => { + expect(resolveInput('{{user}}', context)).toEqual({ + name: 'John Doe', + age: 30, + }); + }); + it('does not wrap string variables with double quotes', () => { expect( resolveInput('{ {{test}}: 2 }', { diff --git a/packages/twenty-shared/src/utils/rich-text-variable-resolver.ts b/packages/twenty-shared/src/utils/rich-text-variable-resolver.ts index 1223ab2ed3..dc62c3ba0c 100644 --- a/packages/twenty-shared/src/utils/rich-text-variable-resolver.ts +++ b/packages/twenty-shared/src/utils/rich-text-variable-resolver.ts @@ -41,7 +41,11 @@ export const resolveRichTextVariables = ( (_, variableTypeFirst: string, variableAttrsFirst: string) => { const variable = variableTypeFirst ?? variableAttrsFirst; const resolvedValue = evalFromContext(variable, context); - const textValue = isDefined(resolvedValue) ? String(resolvedValue) : ''; + const textValue = !isDefined(resolvedValue) + ? '' + : typeof resolvedValue === 'object' + ? JSON.stringify(resolvedValue) + : String(resolvedValue); return buildTextNodesWithLineBreaks(textValue); }, diff --git a/packages/twenty-shared/src/utils/variable-resolver.ts b/packages/twenty-shared/src/utils/variable-resolver.ts index b4dec1aa62..bcd0ac2d3e 100644 --- a/packages/twenty-shared/src/utils/variable-resolver.ts +++ b/packages/twenty-shared/src/utils/variable-resolver.ts @@ -78,6 +78,10 @@ const resolveString = ( return input.replace(VARIABLE_PATTERN, (matchedToken, _) => { const processedToken = evalFromContext(matchedToken, context); + if (typeof processedToken === 'object' && processedToken !== null) { + return JSON.stringify(processedToken); + } + return processedToken; }); };