@agentica/core 0.41.2 → 0.41.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.mjs +77 -13
- package/lib/index.mjs.map +1 -1
- package/lib/orchestrate/call.js +7 -5
- package/lib/orchestrate/call.js.map +1 -1
- package/lib/utils/JsonUtil.d.ts +2 -2
- package/lib/utils/JsonUtil.js +100 -28
- package/lib/utils/JsonUtil.js.map +1 -1
- package/package.json +1 -1
- package/src/orchestrate/call.ts +16 -11
- package/src/utils/JsonUtil.ts +140 -35
package/src/utils/JsonUtil.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { Escaper } from "typia/lib/utils/Escaper";
|
|
|
6
6
|
|
|
7
7
|
export const JsonUtil = {
|
|
8
8
|
parse,
|
|
9
|
-
|
|
9
|
+
stringifyValidationFailure,
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
const pipe = (...fns: ((str: string) => string)[]) => (str: string) => fns.reduce((acc, fn) => fn(acc), str);
|
|
@@ -16,7 +16,7 @@ function parse(str: string) {
|
|
|
16
16
|
return JSON.parse(str);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
function
|
|
19
|
+
function stringifyValidationFailure(
|
|
20
20
|
failure: IValidation.IFailure,
|
|
21
21
|
): string {
|
|
22
22
|
const usedErrors = new Set<IValidation.IError>();
|
|
@@ -62,7 +62,28 @@ function stringify(props: {
|
|
|
62
62
|
|
|
63
63
|
// Array
|
|
64
64
|
if (Array.isArray(value)) {
|
|
65
|
+
// Check for missing array element errors (path[])
|
|
66
|
+
const missingElementErrors = getMissingArrayElementErrors(
|
|
67
|
+
path,
|
|
68
|
+
errors,
|
|
69
|
+
usedErrors,
|
|
70
|
+
);
|
|
71
|
+
const hasMissingElements = missingElementErrors.length > 0;
|
|
72
|
+
|
|
65
73
|
if (value.length === 0) {
|
|
74
|
+
// Empty array but has missing element errors - show placeholders
|
|
75
|
+
if (hasMissingElements) {
|
|
76
|
+
const innerIndent = " ".repeat(tab + 1);
|
|
77
|
+
const lines: string[] = [];
|
|
78
|
+
lines.push(`${indent}[${errorComment}`);
|
|
79
|
+
missingElementErrors.forEach((e, idx) => {
|
|
80
|
+
const errComment = ` // ❌ ${JSON.stringify([{ path: e.path, expected: e.expected, description: e.description }])}`;
|
|
81
|
+
const comma = idx < missingElementErrors.length - 1 ? "," : "";
|
|
82
|
+
lines.push(`${innerIndent}undefined${comma}${errComment}`);
|
|
83
|
+
});
|
|
84
|
+
lines.push(`${indent}]`);
|
|
85
|
+
return lines.join("\n");
|
|
86
|
+
}
|
|
66
87
|
return `${indent}[]${errorComment}`;
|
|
67
88
|
}
|
|
68
89
|
|
|
@@ -71,6 +92,10 @@ function stringify(props: {
|
|
|
71
92
|
|
|
72
93
|
value.forEach((item: unknown, index: number) => {
|
|
73
94
|
const itemPath: string = `${path}[${index}]`;
|
|
95
|
+
const isLastElement = index === value.length - 1;
|
|
96
|
+
// If there are missing element errors, this is not truly the last line
|
|
97
|
+
const needsComma = !isLastElement || hasMissingElements;
|
|
98
|
+
|
|
74
99
|
let itemStr: string = stringify({
|
|
75
100
|
value: item,
|
|
76
101
|
errors,
|
|
@@ -81,15 +106,15 @@ function stringify(props: {
|
|
|
81
106
|
usedErrors,
|
|
82
107
|
});
|
|
83
108
|
// Add comma before the error comment if not the last element
|
|
84
|
-
if (
|
|
109
|
+
if (needsComma) {
|
|
85
110
|
const itemLines: string[] = itemStr.split("\n");
|
|
86
111
|
const lastLine: string = itemLines[itemLines.length - 1]!;
|
|
87
112
|
const commentIndex: number = lastLine.indexOf(" //");
|
|
88
113
|
if (commentIndex !== -1) {
|
|
89
|
-
itemLines[itemLines.length - 1]
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
114
|
+
itemLines[itemLines.length - 1] = `${lastLine.slice(
|
|
115
|
+
0,
|
|
116
|
+
commentIndex,
|
|
117
|
+
)},${lastLine.slice(commentIndex)}`;
|
|
93
118
|
}
|
|
94
119
|
else {
|
|
95
120
|
itemLines[itemLines.length - 1] += ",";
|
|
@@ -99,6 +124,16 @@ function stringify(props: {
|
|
|
99
124
|
lines.push(itemStr);
|
|
100
125
|
});
|
|
101
126
|
|
|
127
|
+
// Add missing element placeholders at the end for each [] error
|
|
128
|
+
if (hasMissingElements) {
|
|
129
|
+
const innerIndent = " ".repeat(tab + 1);
|
|
130
|
+
missingElementErrors.forEach((e, idx) => {
|
|
131
|
+
const errComment = ` // ❌ ${JSON.stringify([{ path: e.path, expected: e.expected, description: e.description }])}`;
|
|
132
|
+
const comma = idx < missingElementErrors.length - 1 ? "," : "";
|
|
133
|
+
lines.push(`${innerIndent}undefined${comma}${errComment}`);
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
102
137
|
lines.push(`${indent}]`);
|
|
103
138
|
return lines.join("\n");
|
|
104
139
|
}
|
|
@@ -119,17 +154,33 @@ function stringify(props: {
|
|
|
119
154
|
});
|
|
120
155
|
}
|
|
121
156
|
|
|
122
|
-
// Get
|
|
123
|
-
const
|
|
157
|
+
// Get all entries from the object (including undefined values that have errors)
|
|
158
|
+
const allEntries: [string, unknown][] = Object.entries(value);
|
|
159
|
+
|
|
160
|
+
// Split into defined and undefined entries
|
|
161
|
+
const definedEntries: [string, unknown][] = allEntries.filter(
|
|
124
162
|
([_, val]) => val !== undefined,
|
|
125
163
|
);
|
|
164
|
+
const undefinedEntryKeys: Set<string> = new Set(
|
|
165
|
+
allEntries.filter(([_, val]) => val === undefined).map(([key]) => key),
|
|
166
|
+
);
|
|
126
167
|
|
|
127
|
-
// Find missing properties that have validation errors
|
|
168
|
+
// Find missing properties that have validation errors (not in object at all)
|
|
128
169
|
const missingKeys: string[] = getMissingProperties(path, value, errors);
|
|
129
170
|
|
|
130
|
-
// Combine
|
|
171
|
+
// Combine: defined entries + undefined entries with errors + missing properties
|
|
172
|
+
const undefinedKeysWithErrors: string[] = Array.from(
|
|
173
|
+
undefinedEntryKeys,
|
|
174
|
+
).filter((key) => {
|
|
175
|
+
const propPath = Escaper.variable(key)
|
|
176
|
+
? `${path}.${key}`
|
|
177
|
+
: `${path}[${JSON.stringify(key)}]`;
|
|
178
|
+
return errors.some(e => e.path.startsWith(propPath));
|
|
179
|
+
});
|
|
180
|
+
|
|
131
181
|
const allKeys: string[] = [
|
|
132
|
-
...
|
|
182
|
+
...definedEntries.map(([key]) => key),
|
|
183
|
+
...undefinedKeysWithErrors,
|
|
133
184
|
...missingKeys,
|
|
134
185
|
];
|
|
135
186
|
|
|
@@ -146,8 +197,11 @@ function stringify(props: {
|
|
|
146
197
|
: `${path}[${JSON.stringify(key)}]`;
|
|
147
198
|
const propIndent: string = " ".repeat(tab + 1);
|
|
148
199
|
|
|
149
|
-
// Get the value (undefined for missing properties)
|
|
150
|
-
const val: unknown
|
|
200
|
+
// Get the value (undefined for missing properties or undefined entries)
|
|
201
|
+
const val: unknown
|
|
202
|
+
= missingKeys.includes(key) || undefinedKeysWithErrors.includes(key)
|
|
203
|
+
? undefined
|
|
204
|
+
: (value as any)[key];
|
|
151
205
|
|
|
152
206
|
// Primitive property value (including undefined for missing properties)
|
|
153
207
|
if (
|
|
@@ -157,10 +211,16 @@ function stringify(props: {
|
|
|
157
211
|
|| typeof val === "number"
|
|
158
212
|
|| typeof val === "string"
|
|
159
213
|
) {
|
|
160
|
-
const propErrorComment: string = getErrorComment(
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
214
|
+
const propErrorComment: string = getErrorComment(
|
|
215
|
+
propPath,
|
|
216
|
+
errors,
|
|
217
|
+
usedErrors,
|
|
218
|
+
);
|
|
219
|
+
const keyStr: string = JSON.stringify(key);
|
|
220
|
+
const valueStr: string
|
|
221
|
+
= val === undefined
|
|
222
|
+
? `${propIndent}${keyStr}: undefined`
|
|
223
|
+
: `${propIndent}${keyStr}: ${JSON.stringify(val)}`;
|
|
164
224
|
const withComma: string
|
|
165
225
|
= index < array.length - 1 ? `${valueStr},` : valueStr;
|
|
166
226
|
const line: string = withComma + propErrorComment;
|
|
@@ -168,7 +228,7 @@ function stringify(props: {
|
|
|
168
228
|
}
|
|
169
229
|
// Complex property value (object or array)
|
|
170
230
|
else {
|
|
171
|
-
const keyLine: string = `${propIndent}
|
|
231
|
+
const keyLine: string = `${propIndent}${JSON.stringify(key)}: `;
|
|
172
232
|
let valStr: string = stringify({
|
|
173
233
|
value: val,
|
|
174
234
|
errors,
|
|
@@ -185,10 +245,10 @@ function stringify(props: {
|
|
|
185
245
|
const lastLine: string = valLines[valLines.length - 1]!;
|
|
186
246
|
const commentIndex: number = lastLine.indexOf(" //");
|
|
187
247
|
if (commentIndex !== -1) {
|
|
188
|
-
valLines[valLines.length - 1]
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
248
|
+
valLines[valLines.length - 1] = `${lastLine.slice(
|
|
249
|
+
0,
|
|
250
|
+
commentIndex,
|
|
251
|
+
)},${lastLine.slice(commentIndex)}`;
|
|
192
252
|
}
|
|
193
253
|
else {
|
|
194
254
|
valLines[valLines.length - 1] += ",";
|
|
@@ -241,8 +301,26 @@ function getErrorComment(
|
|
|
241
301
|
}
|
|
242
302
|
|
|
243
303
|
/**
|
|
244
|
-
*
|
|
245
|
-
*
|
|
304
|
+
* Check if there are missing array element errors (path ending with []) Returns
|
|
305
|
+
* an array of error objects, one per missing element
|
|
306
|
+
*/
|
|
307
|
+
function getMissingArrayElementErrors(
|
|
308
|
+
path: string,
|
|
309
|
+
errors: IValidation.IError[],
|
|
310
|
+
usedErrors: Set<IValidation.IError>,
|
|
311
|
+
): IValidation.IError[] {
|
|
312
|
+
const wildcardPath = `${path}[]`;
|
|
313
|
+
const missingErrors = errors.filter(e => e.path === wildcardPath);
|
|
314
|
+
|
|
315
|
+
// Mark these errors as used
|
|
316
|
+
missingErrors.forEach(e => usedErrors.add(e));
|
|
317
|
+
|
|
318
|
+
return missingErrors;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Find missing properties that have validation errors but don't exist in the
|
|
323
|
+
* data Returns array of property keys that should be displayed as undefined
|
|
246
324
|
*/
|
|
247
325
|
function getMissingProperties(
|
|
248
326
|
path: string,
|
|
@@ -266,24 +344,51 @@ function getMissingProperties(
|
|
|
266
344
|
}
|
|
267
345
|
|
|
268
346
|
/**
|
|
269
|
-
* Extract direct child property key if errorPath is a direct child of
|
|
270
|
-
* Returns null if not a direct child
|
|
347
|
+
* Extract direct child property key if errorPath is a direct child of
|
|
348
|
+
* parentPath Returns null if not a direct child
|
|
271
349
|
*
|
|
272
350
|
* Examples:
|
|
273
|
-
*
|
|
274
|
-
* -
|
|
275
|
-
* -
|
|
276
|
-
* -
|
|
351
|
+
*
|
|
352
|
+
* - ExtractDirectChildKey("$input", "$input.email") => "email"
|
|
353
|
+
* - ExtractDirectChildKey("$input", "$input.user.email") => null (grandchild)
|
|
354
|
+
* - ExtractDirectChildKey("$input.user", "$input.user.email") => "email"
|
|
355
|
+
* - ExtractDirectChildKey("$input", "$input[0]") => null (array index, not object
|
|
356
|
+
* property)
|
|
357
|
+
* - ExtractDirectChildKey("$input", "$input["foo-bar"]") => "foo-bar"
|
|
358
|
+
* - ExtractDirectChildKey("$input", "$input["foo"]["bar"]") => null (grandchild)
|
|
277
359
|
*/
|
|
278
|
-
function extractDirectChildKey(
|
|
360
|
+
function extractDirectChildKey(
|
|
361
|
+
parentPath: string,
|
|
362
|
+
errorPath: string,
|
|
363
|
+
): string | null {
|
|
279
364
|
if (!errorPath.startsWith(parentPath)) {
|
|
280
365
|
return null;
|
|
281
366
|
}
|
|
282
367
|
|
|
283
368
|
const suffix = errorPath.slice(parentPath.length);
|
|
284
369
|
|
|
285
|
-
// Match ".propertyName" pattern (direct child property)
|
|
370
|
+
// Match ".propertyName" pattern (direct child property with dot notation)
|
|
286
371
|
// Should not contain additional dots or brackets after the property name
|
|
287
|
-
const
|
|
288
|
-
|
|
372
|
+
const dotMatch = suffix.match(/^\.([^.[\]]+)$/);
|
|
373
|
+
if (dotMatch !== null) {
|
|
374
|
+
return dotMatch[1]!;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Match '["key"]' pattern (direct child property with bracket notation)
|
|
378
|
+
// The key is a JSON-encoded string
|
|
379
|
+
const bracketMatch = suffix.match(/^\[("[^"\\]*(?:\\.[^"\\]*)*")\]$/);
|
|
380
|
+
if (bracketMatch !== null) {
|
|
381
|
+
try {
|
|
382
|
+
const parsed = JSON.parse(bracketMatch[1]!);
|
|
383
|
+
// Ensure it's a string key, not a number (array index)
|
|
384
|
+
if (typeof parsed === "string") {
|
|
385
|
+
return parsed;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
catch {
|
|
389
|
+
// Invalid JSON, ignore
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
return null;
|
|
289
394
|
}
|