@agentica/core
Version:
Agentic AI Library specialized in LLM Function Calling
226 lines • 9.08 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.JsonUtil = void 0;
const es_jsonkit_1 = require("es-jsonkit");
const jsonrepair_1 = require("jsonrepair");
const Escaper_1 = require("typia/lib/utils/Escaper");
exports.JsonUtil = {
parse,
stringifyValidateFailure,
};
const pipe = (...fns) => (str) => fns.reduce((acc, fn) => fn(acc), str);
function parse(str) {
str = pipe(es_jsonkit_1.removeEmptyObjectPrefix, es_jsonkit_1.addMissingBraces, es_jsonkit_1.removeTrailingCommas, jsonrepair_1.jsonrepair)(str);
return JSON.parse(str);
}
function stringifyValidateFailure(failure) {
const usedErrors = new Set();
const jsonOutput = stringify({
value: failure.data,
errors: failure.errors,
path: "$input",
tab: 0,
inArray: false,
inToJson: false,
usedErrors,
});
// Find errors that couldn't be embedded
const unmappableErrors = failure.errors.filter(e => !usedErrors.has(e));
// If there are unmappable errors, append them as a separate block
if (unmappableErrors.length > 0) {
return `\`\`\`json\n${jsonOutput}\n\`\`\`\n\n**Unmappable validation errors:**\n\n\`\`\`json\n${JSON.stringify(unmappableErrors, null, 2)}\n\`\`\``;
}
return `\`\`\`json\n${jsonOutput}\n\`\`\``;
}
function stringify(props) {
var _a;
const { value, errors, path, tab, inArray, inToJson, usedErrors } = props;
const indent = " ".repeat(tab);
const errorComment = getErrorComment(path, errors, usedErrors);
// Handle undefined in arrays
if (inArray && value === undefined) {
return `${indent}undefined${errorComment}`;
}
// Array
if (Array.isArray(value)) {
if (value.length === 0) {
return `${indent}[]${errorComment}`;
}
const lines = [];
lines.push(`${indent}[${errorComment}`);
value.forEach((item, index) => {
const itemPath = `${path}[${index}]`;
let itemStr = stringify({
value: item,
errors,
path: itemPath,
tab: tab + 1,
inArray: true,
inToJson: false,
usedErrors,
});
// Add comma before the error comment if not the last element
if (index < value.length - 1) {
const itemLines = itemStr.split("\n");
const lastLine = itemLines[itemLines.length - 1];
const commentIndex = lastLine.indexOf(" //");
if (commentIndex !== -1) {
itemLines[itemLines.length - 1]
= `${lastLine.slice(0, commentIndex)},${lastLine.slice(commentIndex)}`;
}
else {
itemLines[itemLines.length - 1] += ",";
}
itemStr = itemLines.join("\n");
}
lines.push(itemStr);
});
lines.push(`${indent}]`);
return lines.join("\n");
}
// Object
if (typeof value === "object" && value !== null) {
// Check for toJSON method
if (!inToJson && typeof value.toJSON === "function") {
const jsonValue = value.toJSON();
return stringify({
value: jsonValue,
errors,
path,
tab,
inArray,
inToJson: true,
usedErrors,
});
}
// Get existing entries (filter out undefined values from actual data)
const existingEntries = Object.entries(value).filter(([_, val]) => val !== undefined);
// Find missing properties that have validation errors
const missingKeys = getMissingProperties(path, value, errors);
// Combine existing and missing properties
const allKeys = [
...existingEntries.map(([key]) => key),
...missingKeys,
];
if (allKeys.length === 0) {
return `${indent}{}${errorComment}`;
}
const lines = [];
lines.push(`${indent}{${errorComment}`);
allKeys.forEach((key, index, array) => {
const propPath = Escaper_1.Escaper.variable(key)
? `${path}.${key}`
: `${path}[${JSON.stringify(key)}]`;
const propIndent = " ".repeat(tab + 1);
// Get the value (undefined for missing properties)
const val = missingKeys.includes(key) ? undefined : value[key];
// Primitive property value (including undefined for missing properties)
if (val === undefined
|| val === null
|| typeof val === "boolean"
|| typeof val === "number"
|| typeof val === "string") {
const propErrorComment = getErrorComment(propPath, errors, usedErrors);
const valueStr = val === undefined
? `${propIndent}"${key}": undefined`
: `${propIndent}"${key}": ${JSON.stringify(val)}`;
const withComma = index < array.length - 1 ? `${valueStr},` : valueStr;
const line = withComma + propErrorComment;
lines.push(line);
}
// Complex property value (object or array)
else {
const keyLine = `${propIndent}"${key}": `;
let valStr = stringify({
value: val,
errors,
path: propPath,
tab: tab + 1,
inArray: false,
inToJson: false,
usedErrors,
});
const valStrWithoutIndent = valStr.trimStart();
// Add comma before the error comment if not the last property
if (index < array.length - 1) {
const valLines = valStrWithoutIndent.split("\n");
const lastLine = valLines[valLines.length - 1];
const commentIndex = lastLine.indexOf(" //");
if (commentIndex !== -1) {
valLines[valLines.length - 1]
= `${lastLine.slice(0, commentIndex)},${lastLine.slice(commentIndex)}`;
}
else {
valLines[valLines.length - 1] += ",";
}
valStr = valLines.join("\n");
}
else {
valStr = valStrWithoutIndent;
}
const combined = keyLine + valStr;
lines.push(combined);
}
});
lines.push(`${indent}}`);
return lines.join("\n");
}
// Primitive types (null, boolean, number, string, undefined, etc.)
const valStr = value === undefined
? "undefined"
: ((_a = JSON.stringify(value)) !== null && _a !== void 0 ? _a : String(value));
return `${indent}${valStr}${errorComment}`;
}
/** Get error comment for a given path */
function getErrorComment(path, errors, usedErrors) {
const pathErrors = errors.filter((e) => e.path === path);
if (pathErrors.length === 0) {
return "";
}
// Mark these errors as used
pathErrors.forEach(e => usedErrors.add(e));
return ` // ❌ ${JSON.stringify(pathErrors.map(e => ({
path: e.path,
expected: e.expected,
description: e.description,
})))}`;
}
/**
* Find missing properties that have validation errors but don't exist in the data
* Returns array of property keys that should be displayed as undefined
*/
function getMissingProperties(path, value, errors) {
const missingKeys = new Set();
for (const e of errors) {
// Check if error.path is a direct child of current path
const childKey = extractDirectChildKey(path, e.path);
if (childKey !== null) {
// Check if this property actually exists in the value
if (!(childKey in value)) {
missingKeys.add(childKey);
}
}
}
return Array.from(missingKeys);
}
/**
* Extract direct child property key if errorPath is a direct child of parentPath
* Returns null if not a direct child
*
* Examples:
* - extractDirectChildKey("$input", "$input.email") => "email"
* - extractDirectChildKey("$input", "$input.user.email") => null (grandchild)
* - extractDirectChildKey("$input.user", "$input.user.email") => "email"
* - extractDirectChildKey("$input", "$input[0]") => null (array index, not object property)
*/
function extractDirectChildKey(parentPath, errorPath) {
if (!errorPath.startsWith(parentPath)) {
return null;
}
const suffix = errorPath.slice(parentPath.length);
// Match ".propertyName" pattern (direct child property)
// Should not contain additional dots or brackets after the property name
const match = suffix.match(/^\.([^.[\]]+)$/);
return match !== null ? match[1] : null;
}
//# sourceMappingURL=JsonUtil.js.map