fix: Improve json repair logic (#22088)

This commit is contained in:
yehorkardash
2025-11-24 14:02:05 +02:00
committed by GitHub
parent 214a7e9af3
commit 60a9cbf133
10 changed files with 237 additions and 52 deletions
@@ -712,22 +712,12 @@ export const configureToolFunction = (
if (value) {
let parsedValue;
try {
parsedValue = jsonParse<IDataObject>(value);
parsedValue = jsonParse<IDataObject>(value, { repairJSON: true });
} catch (error) {
let recoveredData = '';
try {
recoveredData = value
.replace(/'/g, '"') // Replace single quotes with double quotes
.replace(/(['"])?([a-zA-Z0-9_]+)(['"])?:/g, '"$2":') // Wrap keys in double quotes
.replace(/,\s*([\]}])/g, '$1') // Remove trailing commas from objects
.replace(/,+$/, ''); // Remove trailing comma
parsedValue = jsonParse<IDataObject>(recoveredData);
} catch (err) {
throw new NodeOperationError(
ctx.getNode(),
`Could not replace placeholders in ${key}: ${error.message}`,
);
}
throw new NodeOperationError(
ctx.getNode(),
`Could not replace placeholders in ${key}: ${error.message}`,
);
}
options[key as 'qs' | 'headers' | 'body'] = parsedValue;
}
@@ -127,6 +127,7 @@ watch(
setLanguage(newLocale);
axios.defaults.headers.common['Accept-Language'] = newLocale;
void locale.use(newLocale);
},
{ immediate: true },
@@ -129,7 +129,7 @@ describe('test Set2, rawMode/json Mode', () => {
const output = await execute.call(fakeExecuteFunction, item, 0, options, {}, node);
expect(output).toEqual({
json: { error: "The 'JSON Output' in item 0 contains invalid JSON" },
json: { error: "The 'JSON Output' in item 0 does not contain a valid JSON object" },
pairedItem: { item: 0 },
});
});
@@ -9,9 +9,9 @@ import type {
} from 'n8n-workflow';
import { NodeOperationError } from 'n8n-workflow';
import { prepareReturnItem } from '../utils';
import type { SetNodeOptions } from '../interfaces';
import { INCLUDE } from '../interfaces';
import { parseJsonParameter, prepareReturnItem } from '../utils';
describe('prepareReturnItem', () => {
const mockNode = mock<INode>({
@@ -448,3 +448,44 @@ describe('prepareReturnItem', () => {
});
});
});
describe('parseJsonParameter', () => {
const mockNode = mock<INode>({
name: 'Set Node',
typeVersion: 3.4,
});
describe('Valid JSON input', () => {
it('should parse valid JSON string', () => {
const result = parseJsonParameter('{"key": "value"}', mockNode, 0);
expect(result).toEqual({ key: 'value' });
});
it('should parse valid JSON object directly', () => {
const input = { key: 'value' };
const result = parseJsonParameter(input, mockNode, 0);
expect(result).toEqual(input);
});
it('should parse complex valid JSON', () => {
const json = '{"nested": {"key": "value"}, "array": [1, 2, 3]}';
const result = parseJsonParameter(json, mockNode, 0);
expect(result).toEqual({ nested: { key: 'value' }, array: [1, 2, 3] });
});
});
describe('JSON repair', () => {
it('should repair JSON string', () => {
const result = parseJsonParameter('{key: "value"}', mockNode, 0);
expect(result).toEqual({ key: 'value' });
});
});
describe('Error cases', () => {
it('should throw error for invalid JSON that cannot be recovered', () => {
expect(() => parseJsonParameter('{key1: "value",, key2: "value2"}', mockNode, 0)).toThrow(
"The 'JSON Output' in item 0 contains invalid JSON",
);
});
});
});
@@ -137,23 +137,11 @@ export const parseJsonParameter = (
if (typeof jsonData === 'string') {
try {
returnData = jsonParse<IDataObject>(jsonData);
returnData = jsonParse<IDataObject>(jsonData, { repairJSON: true });
} catch (error) {
let recoveredData = '';
try {
recoveredData = jsonData
.replace(/'/g, '"') // Replace single quotes with double quotes
.replace(/(['"])?([a-zA-Z0-9_]+)(['"])?:/g, '"$2":') // Wrap keys in double quotes
.replace(/,\s*([\]}])/g, '$1') // Remove trailing commas from objects
.replace(/,+$/, ''); // Remove trailing comma
returnData = jsonParse<IDataObject>(recoveredData);
} catch (err) {
const description =
recoveredData === jsonData ? jsonData : `${recoveredData};\n Original input: ${jsonData}`;
throw new NodeOperationError(node, `The ${location} in item ${i} contains invalid JSON`, {
description,
});
}
throw new NodeOperationError(node, `The ${location} in item ${i} contains invalid JSON`, {
description: jsonData,
});
}
} else {
returnData = jsonData;
+2 -1
View File
@@ -67,6 +67,7 @@
"title-case": "3.0.3",
"transliteration": "2.3.5",
"xml2js": "catalog:",
"zod": "catalog:"
"zod": "catalog:",
"jsonrepair": "catalog:"
}
}
+11 -1
View File
@@ -2,6 +2,7 @@ import { ApplicationError } from '@n8n/errors';
import { parse as esprimaParse, Syntax } from 'esprima-next';
import type { Node as SyntaxNode, ExpressionStatement } from 'esprima-next';
import FormData from 'form-data';
import { jsonrepair } from 'jsonrepair';
import merge from 'lodash/merge';
import { ALPHABET } from './constants';
@@ -109,7 +110,7 @@ type MutuallyExclusive<T, U> =
| (T & { [k in Exclude<keyof U, keyof T>]?: never })
| (U & { [k in Exclude<keyof T, keyof U>]?: never });
type JSONParseOptions<T> = { acceptJSObject?: boolean } & MutuallyExclusive<
type JSONParseOptions<T> = { acceptJSObject?: boolean; repairJSON?: boolean } & MutuallyExclusive<
{ errorMessage?: string },
{ fallbackValue?: T }
>;
@@ -120,6 +121,7 @@ type JSONParseOptions<T> = { acceptJSObject?: boolean } & MutuallyExclusive<
* @param {string} jsonString - The JSON string to parse.
* @param {Object} [options] - Optional settings for parsing the JSON string. Either `fallbackValue` or `errorMessage` can be set, but not both.
* @param {boolean} [options.acceptJSObject=false] - If true, attempts to recover from common JSON format errors by parsing the JSON string as a JavaScript Object.
* @param {boolean} [options.repairJSON=false] - If true, attempts to repair common JSON format errors by repairing the JSON string.
* @param {string} [options.errorMessage] - A custom error message to throw if the JSON string cannot be parsed.
* @param {*} [options.fallbackValue] - A fallback value to return if the JSON string cannot be parsed.
* @returns {Object} - The parsed object, or the fallback value if parsing fails and `fallbackValue` is set.
@@ -136,6 +138,14 @@ export const jsonParse = <T>(jsonString: string, options?: JSONParseOptions<T>):
// Ignore this error and return the original error or the fallback value
}
}
if (options?.repairJSON) {
try {
const jsonStringCleaned = jsonrepair(jsonString);
return JSON.parse(jsonStringCleaned) as T;
} catch (e) {
// Ignore this error and return the original error or the fallback value
}
}
if (options?.fallbackValue !== undefined) {
if (options.fallbackValue instanceof Function) {
return options.fallbackValue();
+130
View File
@@ -109,6 +109,136 @@ describe('jsonParse', () => {
it('optionally returns a `fallbackValue`', () => {
expect(jsonParse('', { fallbackValue: { foo: 'bar' } })).toEqual({ foo: 'bar' });
});
describe('JSON repair', () => {
describe('Recovery edge cases', () => {
it('should handle simple object with single quotes', () => {
const result = jsonParse("{name: 'John', age: 30}", { repairJSON: true });
expect(result).toEqual({ name: 'John', age: 30 });
});
it('should handle nested objects with single quotes', () => {
const result = jsonParse("{user: {name: 'John', active: true},}", { repairJSON: true });
expect(result).toEqual({ user: { name: 'John', active: true } });
});
it('should handle empty string values', () => {
const result = jsonParse("{key: ''}", { repairJSON: true });
expect(result).toEqual({ key: '' });
});
it('should handle numeric string values', () => {
const result = jsonParse("{key: '123'}", { repairJSON: true });
expect(result).toEqual({ key: '123' });
});
it('should handle multiple keys with trailing comma', () => {
const result = jsonParse("{a: '1', b: '2', c: '3',}", { repairJSON: true });
expect(result).toEqual({ a: '1', b: '2', c: '3' });
});
it('should recover single quotes around strings', () => {
const result = jsonParse("{key: 'value'}", { repairJSON: true });
expect(result).toEqual({ key: 'value' });
});
it('should recover unquoted keys', () => {
const result = jsonParse("{myKey: 'value'}", { repairJSON: true });
expect(result).toEqual({ myKey: 'value' });
});
it('should recover trailing commas in objects', () => {
const result = jsonParse("{key: 'value',}", { repairJSON: true });
expect(result).toEqual({ key: 'value' });
});
it('should recover trailing commas in nested objects', () => {
const result = jsonParse("{outer: {inner: 'value',},}", { repairJSON: true });
expect(result).toEqual({ outer: { inner: 'value' } });
});
it('should recover multiple issues at once', () => {
const result = jsonParse("{key1: 'value1', key2: 'value2',}", { repairJSON: true });
expect(result).toEqual({ key1: 'value1', key2: 'value2' });
});
it('should recover numeric values with single quotes', () => {
const result = jsonParse("{key: '123'}", { repairJSON: true });
expect(result).toEqual({ key: '123' });
});
it('should recover boolean values with single quotes', () => {
const result = jsonParse("{key: 'true'}", { repairJSON: true });
expect(result).toEqual({ key: 'true' });
});
it('should handle urls', () => {
const result = jsonParse('{"key": "https://example.com",}', { repairJSON: true });
expect(result).toEqual({ key: 'https://example.com' });
});
it('should handle ipv6 addresses', () => {
const result = jsonParse('{"key": "2a01:c50e:3544:bd00:4df0:7609:251a:f6d0",}', {
repairJSON: true,
});
expect(result).toEqual({ key: '2a01:c50e:3544:bd00:4df0:7609:251a:f6d0' });
});
it('should handle single quotes containing double quotes', () => {
const result = jsonParse('{key: \'value with "quotes" inside\'}', { repairJSON: true });
expect(result).toEqual({ key: 'value with "quotes" inside' });
});
it('should handle escaped single quotes', () => {
const result = jsonParse("{key: 'it\\'s escaped'}", { repairJSON: true });
expect(result).toEqual({ key: "it's escaped" });
});
it('should handle keys containing hyphens', () => {
const result = jsonParse("{key-with-dash: 'value'}", { repairJSON: true });
expect(result).toEqual({ 'key-with-dash': 'value' });
});
it('should handle keys containing dots', () => {
const result = jsonParse("{key.name: 'value'}", { repairJSON: true });
expect(result).toEqual({ 'key.name': 'value' });
});
it('should handle unquoted string values', () => {
const result = jsonParse('{key: value}', { repairJSON: true });
expect(result).toEqual({ key: 'value' });
});
it('should handle unquoted multi-word values', () => {
const result = jsonParse('{key: some text}', { repairJSON: true });
expect(result).toEqual({ key: 'some text' });
});
it('should handle input with double quotes mixed with single quotes', () => {
const result = jsonParse('{key: "value with \'single\' quotes"}', { repairJSON: true });
expect(result).toEqual({ key: "value with 'single' quotes" });
});
it('should handle keys starting with numbers', () => {
const result = jsonParse("{123key: 'value'}", { repairJSON: true });
expect(result).toEqual({ '123key': 'value' });
});
it('should handle nested objects containing quotes', () => {
const result = jsonParse("{outer: {inner: 'value with \"quotes\"', other: 'test'},}", {
repairJSON: true,
});
expect(result).toEqual({ outer: { inner: 'value with "quotes"', other: 'test' } });
});
it('should handle complex nested quote conflicts', () => {
const result = jsonParse("{key: 'value with \"quotes\" inside', nested: {inner: 'test'}}", {
repairJSON: true,
});
expect(result).toEqual({ key: 'value with "quotes" inside', nested: { inner: 'test' } });
});
});
});
});
describe('jsonStringify', () => {
+40 -16
View File
@@ -81,6 +81,9 @@ catalogs:
js-base64:
specifier: 3.7.2
version: 3.7.2
jsonrepair:
specifier: 3.13.1
version: 3.13.1
jsonwebtoken:
specifier: 9.0.2
version: 9.0.2
@@ -1063,7 +1066,7 @@ importers:
version: 12.1.0
'@getzep/zep-cloud':
specifier: 1.0.12
version: 1.0.12(@langchain/core@0.3.68(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.67)))(encoding@0.1.13)(langchain@0.3.33(ca377405f102dc2905a39d54ecb4d621))
version: 1.0.12(@langchain/core@0.3.68(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.67)))(encoding@0.1.13)(langchain@0.3.33(5cc28a029307bb3da1dcaf370c8a2b8d))
'@getzep/zep-js':
specifier: 0.9.0
version: 0.9.0
@@ -1090,7 +1093,7 @@ importers:
version: 0.3.4(@langchain/core@0.3.68(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.67)))(encoding@0.1.13)
'@langchain/community':
specifier: 'catalog:'
version: 0.3.50(ba725e6da5f3cda58245f84e6fe2ecaf)
version: 0.3.50(071f2de44c6bccc3a8cd2fadf2db3e78)
'@langchain/core':
specifier: 'catalog:'
version: 0.3.68(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.67))
@@ -1213,7 +1216,7 @@ importers:
version: 23.0.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)
langchain:
specifier: 0.3.33
version: 0.3.33(ca377405f102dc2905a39d54ecb4d621)
version: 0.3.33(5cc28a029307bb3da1dcaf370c8a2b8d)
lodash:
specifier: 'catalog:'
version: 4.17.21
@@ -3304,6 +3307,9 @@ importers:
js-base64:
specifier: 'catalog:'
version: 3.7.2(patch_hash=bb02fdf69495c7b0768791b60ab6e1a002053b8decd19a174f5755691e5c9500)
jsonrepair:
specifier: 'catalog:'
version: 3.13.1
jssha:
specifier: 3.3.1
version: 3.3.1
@@ -13081,6 +13087,10 @@ packages:
resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==}
engines: {node: '>=0.10.0'}
jsonrepair@3.13.1:
resolution: {integrity: sha512-WJeiE0jGfxYmtLwBTEk8+y/mYcaleyLXWaqp5bJu0/ZTSeG0KQq/wWQ8pmnkKenEdN6pdnn6QtcoSUkbqDHWNw==}
hasBin: true
jsonschema@1.4.1:
resolution: {integrity: sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==}
@@ -17575,8 +17585,8 @@ packages:
vue-component-type-helpers@2.2.12:
resolution: {integrity: sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==}
vue-component-type-helpers@3.1.4:
resolution: {integrity: sha512-Uws7Ew1OzTTqHW8ZVl/qLl/HB+jf08M0NdFONbVWAx0N4gMLK8yfZDgeB77hDnBmaigWWEn5qP8T9BG59jIeyQ==}
vue-component-type-helpers@3.1.5:
resolution: {integrity: sha512-7V3yJuNWW7/1jxCcI1CswnpDsvs02Qcx/N43LkV+ZqhLj2PKj50slUflHAroNkN4UWiYfzMUUUXiNuv9khmSpQ==}
vue-demi@0.14.10:
resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==}
@@ -20944,7 +20954,7 @@ snapshots:
'@gar/promisify@1.1.3':
optional: true
'@getzep/zep-cloud@1.0.12(@langchain/core@0.3.68(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.67)))(encoding@0.1.13)(langchain@0.3.33(ca377405f102dc2905a39d54ecb4d621))':
'@getzep/zep-cloud@1.0.12(@langchain/core@0.3.68(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.67)))(encoding@0.1.13)(langchain@0.3.33(5cc28a029307bb3da1dcaf370c8a2b8d))':
dependencies:
form-data: 4.0.4
node-fetch: 2.7.0(encoding@0.1.13)
@@ -20953,7 +20963,7 @@ snapshots:
zod: 3.25.67
optionalDependencies:
'@langchain/core': 0.3.68(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.67))
langchain: 0.3.33(ca377405f102dc2905a39d54ecb4d621)
langchain: 0.3.33(5cc28a029307bb3da1dcaf370c8a2b8d)
transitivePeerDependencies:
- encoding
@@ -21639,7 +21649,7 @@ snapshots:
- aws-crt
- encoding
'@langchain/community@0.3.50(ba725e6da5f3cda58245f84e6fe2ecaf)':
'@langchain/community@0.3.50(071f2de44c6bccc3a8cd2fadf2db3e78)':
dependencies:
'@browserbasehq/stagehand': 1.9.0(@playwright/test@1.56.0)(bufferutil@4.0.9)(deepmerge@4.3.1)(dotenv@16.6.1)(encoding@0.1.13)(openai@5.12.2(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.67))(utf-8-validate@5.0.10)(zod@3.25.67)
'@ibm-cloud/watsonx-ai': 1.1.2
@@ -21651,7 +21661,7 @@ snapshots:
flat: 5.0.2
ibm-cloud-sdk-core: 5.3.2
js-yaml: 4.1.0
langchain: 0.3.33(ca377405f102dc2905a39d54ecb4d621)
langchain: 0.3.33(5cc28a029307bb3da1dcaf370c8a2b8d)
langsmith: 0.3.55(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.67))
openai: 5.12.2(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.67)
uuid: 10.0.0
@@ -21666,7 +21676,7 @@ snapshots:
'@azure/search-documents': 12.1.0
'@azure/storage-blob': 12.26.0
'@browserbasehq/sdk': 2.6.0(encoding@0.1.13)
'@getzep/zep-cloud': 1.0.12(@langchain/core@0.3.68(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.67)))(encoding@0.1.13)(langchain@0.3.33(ca377405f102dc2905a39d54ecb4d621))
'@getzep/zep-cloud': 1.0.12(@langchain/core@0.3.68(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.67)))(encoding@0.1.13)(langchain@0.3.33(5cc28a029307bb3da1dcaf370c8a2b8d))
'@getzep/zep-js': 0.9.0
'@google-ai/generativelanguage': 3.4.0(encoding@0.1.13)
'@google-cloud/storage': 7.12.1(encoding@0.1.13)
@@ -23990,7 +24000,7 @@ snapshots:
storybook: 9.1.7(@testing-library/dom@10.4.0)(bufferutil@4.0.9)(prettier@3.6.2)(utf-8-validate@5.0.10)(vite@7.0.0(@types/node@20.19.21)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.89.2)(terser@5.16.1)(tsx@4.19.3))
type-fest: 2.19.0
vue: 3.5.13(typescript@5.9.2)
vue-component-type-helpers: 3.1.4
vue-component-type-helpers: 3.1.5
'@stylistic/eslint-plugin@5.0.0(eslint@9.29.0(jiti@2.6.1))':
dependencies:
@@ -25798,6 +25808,14 @@ snapshots:
transitivePeerDependencies:
- debug
axios@1.12.0(debug@4.4.3):
dependencies:
follow-redirects: 1.15.11(debug@4.4.3)
form-data: 4.0.4
proxy-from-env: 1.1.0
transitivePeerDependencies:
- debug
b4a@1.6.7: {}
babel-jest@29.6.2(@babel/core@7.28.4):
@@ -28574,6 +28592,10 @@ snapshots:
optionalDependencies:
debug: 4.4.1(supports-color@8.1.1)
follow-redirects@1.15.11(debug@4.4.3):
optionalDependencies:
debug: 4.4.3
for-each@0.3.5:
dependencies:
is-callable: 1.2.7
@@ -29276,7 +29298,7 @@ snapshots:
'@types/debug': 4.1.12
'@types/node': 20.19.21
'@types/tough-cookie': 4.0.5
axios: 1.12.0(debug@4.4.1)
axios: 1.12.0(debug@4.4.3)
camelcase: 6.3.0
debug: 4.4.3
dotenv: 16.6.1
@@ -29286,7 +29308,7 @@ snapshots:
isstream: 0.1.2
jsonwebtoken: 9.0.2
mime-types: 2.1.35
retry-axios: 2.6.0(axios@1.12.0(debug@4.4.3))
retry-axios: 2.6.0(axios@1.12.0)
tough-cookie: 4.1.4
transitivePeerDependencies:
- supports-color
@@ -30658,6 +30680,8 @@ snapshots:
jsonpointer@5.0.1: {}
jsonrepair@3.13.1: {}
jsonschema@1.4.1: {}
jsonwebtoken@9.0.2:
@@ -30743,7 +30767,7 @@ snapshots:
kuler@2.0.0: {}
langchain@0.3.33(ca377405f102dc2905a39d54ecb4d621):
langchain@0.3.33(5cc28a029307bb3da1dcaf370c8a2b8d):
dependencies:
'@langchain/core': 0.3.68(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.67))
'@langchain/openai': 0.6.16(@langchain/core@0.3.68(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.67)))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))
@@ -33636,7 +33660,7 @@ snapshots:
onetime: 5.1.2
signal-exit: 3.0.7
retry-axios@2.6.0(axios@1.12.0(debug@4.4.3)):
retry-axios@2.6.0(axios@1.12.0):
dependencies:
axios: 1.12.0(debug@4.4.1)
@@ -35971,7 +35995,7 @@ snapshots:
vue-component-type-helpers@2.2.12: {}
vue-component-type-helpers@3.1.4: {}
vue-component-type-helpers@3.1.5: {}
vue-demi@0.14.10(vue@3.5.13(typescript@5.9.2)):
dependencies:
+1 -1
View File
@@ -34,6 +34,7 @@ catalog:
https-proxy-agent: 7.0.6
iconv-lite: 0.6.3
js-base64: 3.7.2
jsonrepair: 3.13.1
jsonwebtoken: 9.0.2
lodash: 4.17.21
luxon: 3.4.4
@@ -78,4 +79,3 @@ catalogs:
vue-markdown-render: ^2.2.1
vue-router: ^4.5.0
vue-tsc: ^2.2.8