@juspay/neurolink 9.70.6 → 9.70.7
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/CHANGELOG.md +6 -0
- package/dist/browser/neurolink.min.js +287 -287
- package/dist/lib/utils/json/coerce.js +85 -0
- package/dist/utils/json/coerce.js +85 -0
- package/package.json +2 -2
|
@@ -49,6 +49,73 @@ function parseOrRepair(candidate) {
|
|
|
49
49
|
return undefined;
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
|
+
/** Bounds the recursive nested-string unwrap against pathological inputs. */
|
|
53
|
+
const MAX_NESTED_UNWRAP_DEPTH = 6;
|
|
54
|
+
/**
|
|
55
|
+
* Recursively replace any string-valued field whose content is itself a JSON
|
|
56
|
+
* object/array with the parsed value. Models sometimes double-encode a NESTED
|
|
57
|
+
* field — e.g. `{ "attachment": "{\"k\":1}" }` instead of
|
|
58
|
+
* `{ "attachment": { "k": 1 } }` — which fails schema validation even though the
|
|
59
|
+
* intended object is right there. (`coerceJsonToSchema` already unwraps a
|
|
60
|
+
* stringified TOP-LEVEL object; this handles the nested case.)
|
|
61
|
+
*
|
|
62
|
+
* A parsed string is NOT re-descended into: its own string fields (e.g. an
|
|
63
|
+
* attachment's `content`) are the model's intended values and must be left
|
|
64
|
+
* alone. Recursion only walks already-structural objects/arrays to find
|
|
65
|
+
* stringified fields anywhere in the tree. Returns a NEW value (never mutates
|
|
66
|
+
* the input) plus whether anything changed, so the caller can skip a redundant
|
|
67
|
+
* re-validation when nothing was unwrapped. Callers MUST re-validate the result
|
|
68
|
+
* against the schema — that gate is what keeps an over-eager unwrap (a field
|
|
69
|
+
* that should stay a string) from being accepted.
|
|
70
|
+
*/
|
|
71
|
+
function deepUnwrapJsonStrings(value, depth = 0) {
|
|
72
|
+
if (depth > MAX_NESTED_UNWRAP_DEPTH) {
|
|
73
|
+
return { value, changed: false };
|
|
74
|
+
}
|
|
75
|
+
if (typeof value === "string") {
|
|
76
|
+
const s = value.trim();
|
|
77
|
+
const looksJson = (s.startsWith("{") && s.endsWith("}")) ||
|
|
78
|
+
(s.startsWith("[") && s.endsWith("]"));
|
|
79
|
+
if (looksJson) {
|
|
80
|
+
try {
|
|
81
|
+
const parsed = JSON.parse(s);
|
|
82
|
+
if (parsed !== null && typeof parsed === "object") {
|
|
83
|
+
// Parsed one stringified layer. Do NOT descend into `parsed` — its
|
|
84
|
+
// own string fields are intended values, not double-encodings.
|
|
85
|
+
return { value: parsed, changed: true };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
// not JSON — leave the string as-is
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return { value, changed: false };
|
|
93
|
+
}
|
|
94
|
+
if (Array.isArray(value)) {
|
|
95
|
+
let changed = false;
|
|
96
|
+
const out = value.map((item) => {
|
|
97
|
+
const r = deepUnwrapJsonStrings(item, depth + 1);
|
|
98
|
+
if (r.changed) {
|
|
99
|
+
changed = true;
|
|
100
|
+
}
|
|
101
|
+
return r.value;
|
|
102
|
+
});
|
|
103
|
+
return { value: changed ? out : value, changed };
|
|
104
|
+
}
|
|
105
|
+
if (value !== null && typeof value === "object") {
|
|
106
|
+
let changed = false;
|
|
107
|
+
const out = {};
|
|
108
|
+
for (const [k, v] of Object.entries(value)) {
|
|
109
|
+
const r = deepUnwrapJsonStrings(v, depth + 1);
|
|
110
|
+
if (r.changed) {
|
|
111
|
+
changed = true;
|
|
112
|
+
}
|
|
113
|
+
out[k] = r.value;
|
|
114
|
+
}
|
|
115
|
+
return { value: changed ? out : value, changed };
|
|
116
|
+
}
|
|
117
|
+
return { value, changed: false };
|
|
118
|
+
}
|
|
52
119
|
/**
|
|
53
120
|
* Try to produce canonical JSON from `text`. Returns null when no JSON object
|
|
54
121
|
* could be recovered (caller should then keep the raw text).
|
|
@@ -147,6 +214,24 @@ export function coerceJsonToSchema(text, schema) {
|
|
|
147
214
|
if (safeParseable.safeParse(outcome.value).success) {
|
|
148
215
|
schemaValid.push(record);
|
|
149
216
|
}
|
|
217
|
+
else {
|
|
218
|
+
// The model may have double-encoded a NESTED field as a JSON string
|
|
219
|
+
// (e.g. `{"attachment":"{...}"}` instead of `{"attachment":{...}}`),
|
|
220
|
+
// which fails validation even though the intended object is present.
|
|
221
|
+
// Unwrap stringified object/array fields and re-validate before giving
|
|
222
|
+
// up — the safeParse gate rejects any over-eager unwrap.
|
|
223
|
+
const unwrapped = deepUnwrapJsonStrings(outcome.value);
|
|
224
|
+
if (unwrapped.changed &&
|
|
225
|
+
unwrapped.value !== null &&
|
|
226
|
+
typeof unwrapped.value === "object" &&
|
|
227
|
+
safeParseable.safeParse(unwrapped.value).success) {
|
|
228
|
+
schemaValid.push({
|
|
229
|
+
value: unwrapped.value,
|
|
230
|
+
repaired: true,
|
|
231
|
+
truncated: candidate.truncated,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
}
|
|
150
235
|
}
|
|
151
236
|
// Among schema-valid candidates prefer the MOST COMPLETE one. With nullable
|
|
152
237
|
// fields a lean object (e.g. `{summary, attachment: null}`) validates
|
|
@@ -49,6 +49,73 @@ function parseOrRepair(candidate) {
|
|
|
49
49
|
return undefined;
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
|
+
/** Bounds the recursive nested-string unwrap against pathological inputs. */
|
|
53
|
+
const MAX_NESTED_UNWRAP_DEPTH = 6;
|
|
54
|
+
/**
|
|
55
|
+
* Recursively replace any string-valued field whose content is itself a JSON
|
|
56
|
+
* object/array with the parsed value. Models sometimes double-encode a NESTED
|
|
57
|
+
* field — e.g. `{ "attachment": "{\"k\":1}" }` instead of
|
|
58
|
+
* `{ "attachment": { "k": 1 } }` — which fails schema validation even though the
|
|
59
|
+
* intended object is right there. (`coerceJsonToSchema` already unwraps a
|
|
60
|
+
* stringified TOP-LEVEL object; this handles the nested case.)
|
|
61
|
+
*
|
|
62
|
+
* A parsed string is NOT re-descended into: its own string fields (e.g. an
|
|
63
|
+
* attachment's `content`) are the model's intended values and must be left
|
|
64
|
+
* alone. Recursion only walks already-structural objects/arrays to find
|
|
65
|
+
* stringified fields anywhere in the tree. Returns a NEW value (never mutates
|
|
66
|
+
* the input) plus whether anything changed, so the caller can skip a redundant
|
|
67
|
+
* re-validation when nothing was unwrapped. Callers MUST re-validate the result
|
|
68
|
+
* against the schema — that gate is what keeps an over-eager unwrap (a field
|
|
69
|
+
* that should stay a string) from being accepted.
|
|
70
|
+
*/
|
|
71
|
+
function deepUnwrapJsonStrings(value, depth = 0) {
|
|
72
|
+
if (depth > MAX_NESTED_UNWRAP_DEPTH) {
|
|
73
|
+
return { value, changed: false };
|
|
74
|
+
}
|
|
75
|
+
if (typeof value === "string") {
|
|
76
|
+
const s = value.trim();
|
|
77
|
+
const looksJson = (s.startsWith("{") && s.endsWith("}")) ||
|
|
78
|
+
(s.startsWith("[") && s.endsWith("]"));
|
|
79
|
+
if (looksJson) {
|
|
80
|
+
try {
|
|
81
|
+
const parsed = JSON.parse(s);
|
|
82
|
+
if (parsed !== null && typeof parsed === "object") {
|
|
83
|
+
// Parsed one stringified layer. Do NOT descend into `parsed` — its
|
|
84
|
+
// own string fields are intended values, not double-encodings.
|
|
85
|
+
return { value: parsed, changed: true };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
// not JSON — leave the string as-is
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return { value, changed: false };
|
|
93
|
+
}
|
|
94
|
+
if (Array.isArray(value)) {
|
|
95
|
+
let changed = false;
|
|
96
|
+
const out = value.map((item) => {
|
|
97
|
+
const r = deepUnwrapJsonStrings(item, depth + 1);
|
|
98
|
+
if (r.changed) {
|
|
99
|
+
changed = true;
|
|
100
|
+
}
|
|
101
|
+
return r.value;
|
|
102
|
+
});
|
|
103
|
+
return { value: changed ? out : value, changed };
|
|
104
|
+
}
|
|
105
|
+
if (value !== null && typeof value === "object") {
|
|
106
|
+
let changed = false;
|
|
107
|
+
const out = {};
|
|
108
|
+
for (const [k, v] of Object.entries(value)) {
|
|
109
|
+
const r = deepUnwrapJsonStrings(v, depth + 1);
|
|
110
|
+
if (r.changed) {
|
|
111
|
+
changed = true;
|
|
112
|
+
}
|
|
113
|
+
out[k] = r.value;
|
|
114
|
+
}
|
|
115
|
+
return { value: changed ? out : value, changed };
|
|
116
|
+
}
|
|
117
|
+
return { value, changed: false };
|
|
118
|
+
}
|
|
52
119
|
/**
|
|
53
120
|
* Try to produce canonical JSON from `text`. Returns null when no JSON object
|
|
54
121
|
* could be recovered (caller should then keep the raw text).
|
|
@@ -147,6 +214,24 @@ export function coerceJsonToSchema(text, schema) {
|
|
|
147
214
|
if (safeParseable.safeParse(outcome.value).success) {
|
|
148
215
|
schemaValid.push(record);
|
|
149
216
|
}
|
|
217
|
+
else {
|
|
218
|
+
// The model may have double-encoded a NESTED field as a JSON string
|
|
219
|
+
// (e.g. `{"attachment":"{...}"}` instead of `{"attachment":{...}}`),
|
|
220
|
+
// which fails validation even though the intended object is present.
|
|
221
|
+
// Unwrap stringified object/array fields and re-validate before giving
|
|
222
|
+
// up — the safeParse gate rejects any over-eager unwrap.
|
|
223
|
+
const unwrapped = deepUnwrapJsonStrings(outcome.value);
|
|
224
|
+
if (unwrapped.changed &&
|
|
225
|
+
unwrapped.value !== null &&
|
|
226
|
+
typeof unwrapped.value === "object" &&
|
|
227
|
+
safeParseable.safeParse(unwrapped.value).success) {
|
|
228
|
+
schemaValid.push({
|
|
229
|
+
value: unwrapped.value,
|
|
230
|
+
repaired: true,
|
|
231
|
+
truncated: candidate.truncated,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
}
|
|
150
235
|
}
|
|
151
236
|
// Among schema-valid candidates prefer the MOST COMPLETE one. With nullable
|
|
152
237
|
// fields a lean object (e.g. `{summary, attachment: null}`) validates
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@juspay/neurolink",
|
|
3
|
-
"version": "9.70.
|
|
3
|
+
"version": "9.70.7",
|
|
4
4
|
"packageManager": "pnpm@10.15.1",
|
|
5
5
|
"description": "Universal AI Development Platform with working MCP integration, multi-provider support, voice (TTS/STT/realtime), and professional CLI. 58+ external MCP servers discoverable, multimodal file processing, RAG pipelines. Build, test, and deploy AI applications with 21+ providers: OpenAI, Anthropic, Google AI Studio, Google Vertex, AWS Bedrock, Azure OpenAI, Mistral, LiteLLM, SageMaker, Hugging Face, Ollama, OpenAI-compatible, OpenRouter, DeepSeek, NVIDIA NIM, LM Studio, llama.cpp, plus voice (OpenAI TTS, ElevenLabs, Deepgram, Azure Speech).",
|
|
6
6
|
"author": {
|
|
@@ -418,7 +418,7 @@
|
|
|
418
418
|
"@changesets/changelog-github": "^0.6.0",
|
|
419
419
|
"@changesets/cli": "^2.29.8",
|
|
420
420
|
"@eslint/js": "^10.0.1",
|
|
421
|
-
"@juspay/hippocampus": "
|
|
421
|
+
"@juspay/hippocampus": ">=0.1.7",
|
|
422
422
|
"@opentelemetry/api": "^1.9.0",
|
|
423
423
|
"@opentelemetry/sdk-trace-base": "^2.6.0",
|
|
424
424
|
"@opentelemetry/sdk-trace-node": "^2.6.0",
|