Skip to content

Commit 4af067a

Browse files
authored
Bugfix/JSON5 Parsing (#5201)
use json5 for parsing input data
1 parent e002e61 commit 4af067a

File tree

12 files changed

+32
-43
lines changed

12 files changed

+32
-43
lines changed

packages/components/nodes/agentflow/ExecuteFlow/ExecuteFlow.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
} from '../../../src/Interface'
1010
import axios, { AxiosRequestConfig } from 'axios'
1111
import { getCredentialData, getCredentialParam, processTemplateVariables } from '../../../src/utils'
12+
import JSON5 from 'json5'
1213
import { DataSource } from 'typeorm'
1314
import { BaseMessageLike } from '@langchain/core/messages'
1415
import { updateFlowState } from '../utils'
@@ -167,9 +168,7 @@ class ExecuteFlow_Agentflow implements INode {
167168
let overrideConfig = nodeData.inputs?.executeFlowOverrideConfig
168169
if (typeof overrideConfig === 'string' && overrideConfig.startsWith('{') && overrideConfig.endsWith('}')) {
169170
try {
170-
// Handle escaped square brackets and other common escape sequences
171-
const unescapedConfig = overrideConfig.replace(/\\(\[|\])/g, '$1')
172-
overrideConfig = JSON.parse(unescapedConfig)
171+
overrideConfig = JSON5.parse(overrideConfig)
173172
} catch (parseError) {
174173
throw new Error(`Invalid JSON in executeFlowOverrideConfig: ${parseError.message}`)
175174
}

packages/components/nodes/agentflow/HTTP/HTTP.ts

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import FormData from 'form-data'
44
import * as querystring from 'querystring'
55
import { getCredentialData, getCredentialParam } from '../../../src/utils'
66
import { secureAxiosRequest } from '../../../src/httpSecurity'
7+
import JSON5 from 'json5'
78

89
class HTTP_Agentflow implements INode {
910
label: string
@@ -19,34 +20,13 @@ class HTTP_Agentflow implements INode {
1920
credential: INodeParams
2021
inputs: INodeParams[]
2122

22-
private sanitizeJsonString(jsonString: string): string {
23-
// Remove common problematic escape sequences that are not valid JSON
24-
let sanitized = jsonString
25-
// Remove escaped square brackets (not valid JSON)
26-
.replace(/\\(\[|\])/g, '$1')
27-
// Fix unquoted string values in JSON (simple case)
28-
.replace(/:\s*([a-zA-Z][a-zA-Z0-9]*)\s*([,}])/g, ': "$1"$2')
29-
// Fix trailing commas
30-
.replace(/,(\s*[}\]])/g, '$1')
31-
32-
return sanitized
33-
}
34-
3523
private parseJsonBody(body: string): any {
3624
try {
37-
// First try to parse as-is
38-
return JSON.parse(body)
25+
return JSON5.parse(body)
3926
} catch (error) {
40-
try {
41-
// If that fails, try to sanitize and parse
42-
const sanitized = this.sanitizeJsonString(body)
43-
return JSON.parse(sanitized)
44-
} catch (sanitizeError) {
45-
// If sanitization also fails, throw the original error with helpful message
46-
throw new Error(
47-
`Invalid JSON format in body. Original error: ${error.message}. Please ensure your JSON is properly formatted with quoted strings and valid escape sequences.`
48-
)
49-
}
27+
throw new Error(
28+
`Invalid JSON format in body. Original error: ${error.message}. Please ensure your JSON is properly formatted with quoted strings and valid escape sequences.`
29+
)
5030
}
5131
}
5232

packages/components/nodes/agentflow/Iteration/Iteration.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { ICommonObject, INode, INodeData, INodeParams } from '../../../src/Interface'
2+
import JSON5 from 'json5'
23

34
class Iteration_Agentflow implements INode {
45
label: string
@@ -41,10 +42,10 @@ class Iteration_Agentflow implements INode {
4142
// Helper function to clean JSON strings with redundant backslashes
4243
const safeParseJson = (str: string): string => {
4344
try {
44-
return JSON.parse(str)
45+
return JSON5.parse(str)
4546
} catch {
4647
// Try parsing after cleaning
47-
return JSON.parse(str.replace(/\\(["'[\]{}])/g, '$1'))
48+
return JSON5.parse(str.replace(/\\(["'[\]{}])/g, '$1'))
4849
}
4950
}
5051

packages/components/nodes/tools/MCP/CustomMCP/CustomMCP.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { MCPToolkit } from '../core'
44
import { getVars, prepareSandboxVars } from '../../../../src/utils'
55
import { DataSource } from 'typeorm'
66
import hash from 'object-hash'
7+
import JSON5 from 'json5'
78

89
const mcpServerConfig = `{
910
"command": "npx",
@@ -261,7 +262,7 @@ function substituteVariablesInString(str: string, sandbox: any): string {
261262

262263
function convertToValidJSONString(inputString: string) {
263264
try {
264-
const jsObject = Function('return ' + inputString)()
265+
const jsObject = JSON5.parse(inputString)
265266
return JSON.stringify(jsObject, null, 2)
266267
} catch (error) {
267268
console.error('Error converting to JSON:', error)

packages/components/nodes/tools/RequestsDelete/RequestsDelete.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { INode, INodeData, INodeParams } from '../../../src/Interface'
22
import { getBaseClasses, stripHTMLFromToolInput } from '../../../src/utils'
33
import { desc, RequestParameters, RequestsDeleteTool } from './core'
4+
import JSON5 from 'json5'
45

56
const codeExample = `{
67
"id": {
@@ -130,7 +131,7 @@ class RequestsDelete_Tools implements INode {
130131
if (queryParamsSchema) obj.queryParamsSchema = queryParamsSchema
131132
if (maxOutputLength) obj.maxOutputLength = parseInt(maxOutputLength, 10)
132133
if (headers) {
133-
const parsedHeaders = typeof headers === 'object' ? headers : JSON.parse(stripHTMLFromToolInput(headers))
134+
const parsedHeaders = typeof headers === 'object' ? headers : JSON5.parse(stripHTMLFromToolInput(headers))
134135
obj.headers = parsedHeaders
135136
}
136137

packages/components/nodes/tools/RequestsDelete/core.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { z } from 'zod'
22
import { DynamicStructuredTool } from '../OpenAPIToolkit/core'
33
import { secureFetch } from '../../../src/httpSecurity'
4+
import JSON5 from 'json5'
45

56
export const desc = `Use this when you need to execute a DELETE request to remove data from a website.`
67

@@ -22,7 +23,7 @@ const createRequestsDeleteSchema = (queryParamsSchema?: string) => {
2223
// If queryParamsSchema is provided, parse it and add dynamic query params
2324
if (queryParamsSchema) {
2425
try {
25-
const parsedSchema = JSON.parse(queryParamsSchema)
26+
const parsedSchema = JSON5.parse(queryParamsSchema)
2627
const queryParamsObject: Record<string, z.ZodTypeAny> = {}
2728

2829
Object.entries(parsedSchema).forEach(([key, config]: [string, any]) => {
@@ -108,7 +109,7 @@ export class RequestsDeleteTool extends DynamicStructuredTool {
108109

109110
if (this.queryParamsSchema && params.queryParams && Object.keys(params.queryParams).length > 0) {
110111
try {
111-
const parsedSchema = JSON.parse(this.queryParamsSchema)
112+
const parsedSchema = JSON5.parse(this.queryParamsSchema)
112113
const pathParams: Array<{ key: string; value: string }> = []
113114

114115
Object.entries(params.queryParams).forEach(([key, value]) => {

packages/components/nodes/tools/RequestsGet/RequestsGet.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { INode, INodeData, INodeParams } from '../../../src/Interface'
22
import { getBaseClasses, stripHTMLFromToolInput } from '../../../src/utils'
33
import { desc, RequestParameters, RequestsGetTool } from './core'
4+
import JSON5 from 'json5'
45

56
const codeExample = `{
67
"id": {
@@ -130,7 +131,7 @@ class RequestsGet_Tools implements INode {
130131
if (queryParamsSchema) obj.queryParamsSchema = queryParamsSchema
131132
if (maxOutputLength) obj.maxOutputLength = parseInt(maxOutputLength, 10)
132133
if (headers) {
133-
const parsedHeaders = typeof headers === 'object' ? headers : JSON.parse(stripHTMLFromToolInput(headers))
134+
const parsedHeaders = typeof headers === 'object' ? headers : JSON5.parse(stripHTMLFromToolInput(headers))
134135
obj.headers = parsedHeaders
135136
}
136137

packages/components/nodes/tools/RequestsGet/core.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { z } from 'zod'
22
import { DynamicStructuredTool } from '../OpenAPIToolkit/core'
33
import { secureFetch } from '../../../src/httpSecurity'
4+
import JSON5 from 'json5'
45

56
export const desc = `Use this when you need to execute a GET request to get data from a website.`
67

@@ -22,7 +23,7 @@ const createRequestsGetSchema = (queryParamsSchema?: string) => {
2223
// If queryParamsSchema is provided, parse it and add dynamic query params
2324
if (queryParamsSchema) {
2425
try {
25-
const parsedSchema = JSON.parse(queryParamsSchema)
26+
const parsedSchema = JSON5.parse(queryParamsSchema)
2627
const queryParamsObject: Record<string, z.ZodTypeAny> = {}
2728

2829
Object.entries(parsedSchema).forEach(([key, config]: [string, any]) => {
@@ -108,7 +109,7 @@ export class RequestsGetTool extends DynamicStructuredTool {
108109

109110
if (this.queryParamsSchema && params.queryParams && Object.keys(params.queryParams).length > 0) {
110111
try {
111-
const parsedSchema = JSON.parse(this.queryParamsSchema)
112+
const parsedSchema = JSON5.parse(this.queryParamsSchema)
112113
const pathParams: Array<{ key: string; value: string }> = []
113114

114115
Object.entries(params.queryParams).forEach(([key, value]) => {

packages/components/nodes/tools/RequestsPost/RequestsPost.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { INode, INodeData, INodeParams } from '../../../src/Interface'
22
import { getBaseClasses, stripHTMLFromToolInput } from '../../../src/utils'
33
import { RequestParameters, desc, RequestsPostTool } from './core'
4+
import JSON5 from 'json5'
45

56
const codeExample = `{
67
"name": {
@@ -140,11 +141,11 @@ class RequestsPost_Tools implements INode {
140141
if (bodySchema) obj.bodySchema = stripHTMLFromToolInput(bodySchema)
141142
if (maxOutputLength) obj.maxOutputLength = parseInt(maxOutputLength, 10)
142143
if (headers) {
143-
const parsedHeaders = typeof headers === 'object' ? headers : JSON.parse(stripHTMLFromToolInput(headers))
144+
const parsedHeaders = typeof headers === 'object' ? headers : JSON5.parse(stripHTMLFromToolInput(headers))
144145
obj.headers = parsedHeaders
145146
}
146147
if (body) {
147-
const parsedBody = typeof body === 'object' ? body : JSON.parse(body)
148+
const parsedBody = typeof body === 'object' ? body : JSON5.parse(body)
148149
obj.body = parsedBody
149150
}
150151

packages/components/nodes/tools/RequestsPost/core.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { z } from 'zod'
22
import { DynamicStructuredTool } from '../OpenAPIToolkit/core'
33
import { secureFetch } from '../../../src/httpSecurity'
4+
import JSON5 from 'json5'
45

56
export const desc = `Use this when you want to execute a POST request to create or update a resource.`
67

@@ -27,7 +28,7 @@ const createRequestsPostSchema = (bodySchema?: string) => {
2728
// If bodySchema is provided, parse it and add dynamic body params
2829
if (bodySchema) {
2930
try {
30-
const parsedSchema = JSON.parse(bodySchema)
31+
const parsedSchema = JSON5.parse(bodySchema)
3132
const bodyParamsObject: Record<string, z.ZodTypeAny> = {}
3233

3334
Object.entries(parsedSchema).forEach(([key, config]: [string, any]) => {

0 commit comments

Comments
 (0)