funda-ui 4.7.599 → 4.7.601

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.
@@ -1,48 +1,389 @@
1
1
  /**
2
- * Check if a string is a valid number
3
- * @param str - The string to check
4
- * @returns boolean indicating if the string is a valid number
2
+ * Fix And Parse JSON (Support for handling complex escape JSON strings)
3
+ * @private
5
4
  */
6
- function isValidNumeric(str: unknown): boolean {
7
- if (typeof str !== "string") return false; // we only process strings!
8
- if (
9
- !isNaN(Number(str)) && // use type coercion to parse the _entirety_ of the string
10
- !isNaN(parseFloat(str)) // ensure strings of whitespace fail
11
- ) {
12
- return true;
5
+ /*
6
+ - Always try JSON.parse first;
7
+ - If parsing fails, unescape \" → ";
8
+ - Then process the outermost object or array key-by-key, value-by-value;
9
+ - If a top-level value is an unquoted object or array (e.g. messages: [ {...} ]),
10
+ recursively treat that value as a new root to repair;
11
+ - For values wrapped in quotes ('...' or "..."), extract the inner text and
12
+ re-encode it using JSON.stringify (ensures internal single/double quotes
13
+ are not corrupted);
14
+ - Set MAX_DEPTH to prevent infinite recursion.
15
+ */
16
+ // fixAndParseJSON - recursively repairs top-level key/value
17
+ // (when encountering outermost values that are objects/arrays, it recurses)
18
+
19
+ interface ParseResult {
20
+ success: boolean;
21
+ data?: any;
22
+ error?: string;
23
+ details?: string;
24
+ }
25
+
26
+ type JSONValue = string | number | boolean | null | JSONObject | JSONArray;
27
+ interface JSONObject {
28
+ [key: string]: JSONValue;
29
+ }
30
+ interface JSONArray extends Array<JSONValue> {}
31
+
32
+ /*
33
+ DEMO:
34
+
35
+ // ✅ Valid JSON (contains svg and single-quote content)
36
+ const okJson = `{
37
+ "label":"<svg width='16' height='16'><path fill='currentColor' d='M19 13h-6'/></svg> New Session",
38
+ "value":"new",
39
+ "onClick":"method.setVal(''); method.clearData();"
40
+ }`;
41
+
42
+ // ❌ Single-quote JSON
43
+ const badJson = "{'model':'{model}','messages':[{'role':'user','content':'{message}'}],'stream': true}";
44
+
45
+ // ❌ Escaped JSON
46
+ const badJson2 = "{\\\"label\\\":\\\"<svg width='16' height='16' viewBox='0 0 24 24'><path fill='currentColor' d='M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z'/></svg> New Session\\\",\\\"value\\\":\\\"new\\\",\\\"onClick\\\":\\\"method.setVal(''); method.clearData();\\\"}";
47
+
48
+ const badJson3 = "{\"label\":\"<svg width='16' height='16' viewBox='0 0 24 24'><path fill='currentColor' d='M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z'/></svg> New Session\",\"value\":\"new\",\"onClick\":\"method.setVal(''); method.clearData();\"}";
49
+
50
+ const badJson4 = "[{\"label\":\"<svg fill='currentColor' width='12' height='12' viewBox='0 0 24 24'><path d='M20.5 9a3.49 3.49 0 0 0-3.45 3h-1.1a2.49 2.49 0 0 0-4.396-1.052L8.878 9.731l3.143-4.225a2.458 2.458 0 0 0 2.98-.019L17.339 8H16v1h3V6h-1v1.243l-2.336-2.512A2.473 2.473 0 0 0 16 3.5a2.5 2.5 0 0 0-5 0 2.474 2.474 0 0 0 .343 1.243L7.947 9.308 4.955 7.947a2.404 2.404 0 0 0-.161-1.438l3.704-1.385-.44 1.371.942.333L10 4 7.172 3l-.334.943 1.01.357-3.659 1.368a2.498 2.498 0 1 0-.682 4.117l2.085 2.688-2.053 2.76a2.5 2.5 0 1 0 .87 3.864l3.484 1.587-1.055.373.334.943L10 21l-1-2.828-.943.333.435 1.354-3.608-1.645A2.471 2.471 0 0 0 5 17.5a2.5 2.5 0 0 0-.058-.527l3.053-1.405 3.476 4.48a2.498 2.498 0 1 0 4.113.075L18 17.707V19h1v-3h-3v1h1.293l-2.416 2.416a2.466 2.466 0 0 0-2.667-.047l-3.283-4.23 2.554-1.176A2.494 2.494 0 0 0 15.95 13h1.1a3.493 3.493 0 1 0 3.45-4zm-7-7A1.5 1.5 0 1 1 12 3.5 1.502 1.502 0 0 1 13.5 2zm0 18a1.5 1.5 0 1 1-1.5 1.5 1.502 1.502 0 0 1 1.5-1.5zM1 7.5a1.5 1.5 0 1 1 2.457 1.145l-.144.112A1.496 1.496 0 0 1 1 7.5zm3.32 1.703a2.507 2.507 0 0 0 .264-.326l2.752 1.251-1.124 1.512zM2.5 19A1.5 1.5 0 1 1 4 17.5 1.502 1.502 0 0 1 2.5 19zm2.037-2.941a2.518 2.518 0 0 0-.193-.234l1.885-2.532 1.136 1.464zm3.76-1.731L6.849 12.46l1.42-1.908L11.1 11.84a2.29 2.29 0 0 0-.033 1.213zM13.5 14a1.5 1.5 0 1 1 1.5-1.5 1.502 1.502 0 0 1-1.5 1.5zm7 1a2.5 2.5 0 1 1 2.5-2.5 2.502 2.502 0 0 1-2.5 2.5zm1.5-2.5a1.5 1.5 0 1 1-1.5-1.5 1.502 1.502 0 0 1 1.5 1.5z'/><path fill='none' d='M0 0h24v24H0z'/></svg> Deep Thought","value":"brief","onClick":"if(isActive){method.executeCustomMethod('changeModel', true)}else{method.executeCustomMethod('changeModel', false)}"},{"label":"<svg fill='currentColor' width='12' height='12' viewBox='0 0 24 24'><path d='M19 2H5c-1.103 0-2 .897-2 2v12c0 1.103.897 2 2 2h3.586L12 21.414 15.414 18H19c1.103 0 2-.897 2-2V4c0-1.103-.897-2-2-2zm0 14h-4.414L12 18.586 9.414 16H5V4h14v12z'/></svg> Concise Answer","value":"brief","onClick":"if(isActive){method.setContextData({systemPrompt:'Please answer concisely, around 150 words, keep reasoning brief',mergedText:method.getContextData().mergedText,analyzeMetrics:method.getContextData().analyzeMetrics});}else{method.setContextData({mergedText:method.getContextData().mergedText,analyzeMetrics:method.getContextData().analyzeMetrics});}"},{"label":"<svg fill='none' width='12' height='12' viewBox='0 0 16 16'><path d='M7 0.0618896V9H15.9381C15.446 12.9463 12.0796 16 8 16C3.58172 16 0 12.4183 0 8C0 3.92038 3.05369 0.553988 7 0.0618896Z' fill='currentColor'/><path d='M9 0.0618897V7H15.9381C15.4869 3.38128 12.6187 0.513137 9 0.0618897Z' fill='currentColor'/></svg> Metrics Analysis","value":"lab","onClick":"return method.executeCustomMethod('getLibList')","isSelect":true,"dynamicOptions":true}]";
51
+
52
+
53
+ console.log('okJson =>', fixAndParseJSON(okJson)); // parses correctly
54
+ console.log('badJson =>', fixAndParseJSON(badJson)); // repaired and parsed
55
+ console.log('badJson2 =>', fixAndParseJSON(badJson2)); // repaired and parsed
56
+ console.log('badJson3 =>', fixAndParseJSON(badJson3)); // repaired and parsed
57
+ console.log('badJson4 =>', fixAndParseJSON(badJson4)); // repaired and parsed
58
+ */
59
+ export function fixAndParseJSON(input: string): ParseResult {
60
+ const MAX_DEPTH = 6;
61
+
62
+ // 1. Fast attempt
63
+ try {
64
+ return { success: true, data: JSON.parse(input) };
65
+ } catch (e) {
66
+ // continue to repair
67
+ }
68
+
69
+ // 2. Simple unescape of \" (common when copied from JS literals)
70
+ let s: string = input;
71
+ if (s.includes('\\"')) s = s.replace(/\\"/g, '"');
72
+ s = s.trim();
73
+
74
+ try {
75
+ if (s.startsWith('{')) {
76
+ s = processTopObject(s, 0, MAX_DEPTH);
77
+ } else if (s.startsWith('[')) {
78
+ s = processTopArray(s, 0, MAX_DEPTH);
79
+ } else {
80
+ throw new Error('Input is not an object or array');
81
+ }
82
+
83
+ return { success: true, data: JSON.parse(s) };
84
+ } catch (err) {
85
+ return {
86
+ success: false,
87
+ error: 'Invalid JSON format',
88
+ details: err instanceof Error ? err.message : String(err)
89
+ };
13
90
  }
14
- return false;
91
+ }
92
+
93
+ /* ---------- Helper (recursive) functions ---------- */
94
+
95
+ function processTopObject(str: string, depth: number, MAX_DEPTH: number): string {
96
+ if (depth > MAX_DEPTH) return str;
97
+ str = str.trim();
98
+ // Ensure it is wrapped in { ... }
99
+ if (!(str.startsWith('{') && str.endsWith('}'))) {
100
+ const f: number = str.indexOf('{');
101
+ const l: number = str.lastIndexOf('}');
102
+ if (f === -1 || l === -1 || l <= f) return str;
103
+ str = str.slice(f, l + 1);
104
+ }
105
+ const inner: string = str.slice(1, -1);
106
+ const pairs: string[] = splitTopLevel(inner);
107
+
108
+ const repairedPairs: string[] = pairs.map(pair => {
109
+ if (!pair || pair.trim() === '') return '';
110
+ const idx: number = findTopLevelColon(pair);
111
+ if (idx === -1) {
112
+ return pair; // Non key:value fragment, keep as is (rare case)
113
+ }
114
+ const rawKey: string = pair.slice(0, idx).trim();
115
+ const rawVal: string = pair.slice(idx + 1);
116
+
117
+ const keyContent: string = extractKeyContent(rawKey);
118
+ const keyJson: string = JSON.stringify(keyContent);
119
+
120
+ const repairedValue: string = repairPossiblyQuotedValue(rawVal, depth + 1, MAX_DEPTH);
121
+
122
+ return keyJson + ':' + repairedValue;
123
+ });
124
+
125
+ return '{' + repairedPairs.join(',') + '}';
126
+ }
127
+
128
+ function processTopArray(str: string, depth: number, MAX_DEPTH: number): string {
129
+ if (depth > MAX_DEPTH) return str;
130
+ str = str.trim();
131
+ if (!(str.startsWith('[') && str.endsWith(']'))) {
132
+ const f: number = str.indexOf('[');
133
+ const l: number = str.lastIndexOf(']');
134
+ if (f === -1 || l === -1 || l <= f) return str;
135
+ str = str.slice(f, l + 1);
136
+ }
137
+ const inner: string = str.slice(1, -1);
138
+ const elements: string[] = splitTopLevel(inner);
139
+
140
+ const processed: string[] = elements.map(el => {
141
+ const t: string = el.trim();
142
+ if (t === '') return '';
143
+ if (t.startsWith('{')) return processTopObject(t, depth + 1, MAX_DEPTH);
144
+ if (t.startsWith('[')) return processTopArray(t, depth + 1, MAX_DEPTH);
145
+ return repairPossiblyQuotedValue(t, depth + 1, MAX_DEPTH);
146
+ });
147
+
148
+ return '[' + processed.join(',') + ']';
149
+ }
150
+
151
+ // If value is quoted, extract inside and JSON.stringify again (safe escaping)
152
+ // If value is unquoted object/array literal, recurse treating it as new root
153
+ // Otherwise return as is (numbers, booleans, null, or raw expressions)
154
+ function repairPossiblyQuotedValue(rawVal: string, depth: number, MAX_DEPTH: number): string {
155
+ const v: string = rawVal.trim();
156
+ if (v === '') return v;
157
+
158
+ if (v[0] === '"' || v[0] === "'") {
159
+ const quote: string = v[0];
160
+ // Find the last unescaped matching quote
161
+ let lastPos: number = -1;
162
+ for (let i: number = v.length - 1; i >= 0; i--) {
163
+ if (v[i] === quote) {
164
+ // check if escaped
165
+ let bs: number = 0;
166
+ let k: number = i - 1;
167
+ while (k >= 0 && v[k] === '\\') {
168
+ bs++;
169
+ k--;
170
+ }
171
+ if (bs % 2 === 0) {
172
+ lastPos = i;
173
+ break;
174
+ }
175
+ }
176
+ }
177
+ const inner: string = lastPos > 0 ? v.slice(1, lastPos) : v.slice(1);
178
+ return JSON.stringify(inner); // Generate valid JSON string (auto escape)
179
+ }
180
+
181
+ // If unquoted object/array literal -> recurse
182
+ if (v.startsWith('{')) {
183
+ return processTopObject(v, depth, MAX_DEPTH);
184
+ }
185
+ if (v.startsWith('[')) {
186
+ return processTopArray(v, depth, MAX_DEPTH);
187
+ }
188
+
189
+ // Other (number, boolean, null, raw expression): return as is
190
+ return v;
191
+ }
192
+
193
+ /* --------- Utils: split by top-level commas, find colon, extract key --------- */
194
+
195
+ // Split string by top-level commas (ignores commas inside strings/objects/arrays/parentheses)
196
+ function splitTopLevel(str: string): string[] {
197
+ const parts: string[] = [];
198
+ let buf: string = '';
199
+ let depthCurly: number = 0;
200
+ let depthSquare: number = 0;
201
+ let depthParen: number = 0;
202
+ let inSingle: boolean = false;
203
+ let inDouble: boolean = false;
204
+ let esc: boolean = false;
205
+
206
+ for (let i: number = 0; i < str.length; i++) {
207
+ const ch: string = str[i];
208
+
209
+ if (esc) {
210
+ buf += ch;
211
+ esc = false;
212
+ continue;
213
+ }
214
+ if (ch === '\\') {
215
+ buf += ch;
216
+ esc = true;
217
+ continue;
218
+ }
219
+
220
+ if (ch === "'" && !inDouble) {
221
+ inSingle = !inSingle;
222
+ buf += ch;
223
+ continue;
224
+ }
225
+ if (ch === '"' && !inSingle) {
226
+ inDouble = !inDouble;
227
+ buf += ch;
228
+ continue;
229
+ }
230
+
231
+ if (!inSingle && !inDouble) {
232
+ if (ch === '{') {
233
+ depthCurly++;
234
+ buf += ch;
235
+ continue;
236
+ }
237
+ if (ch === '}') {
238
+ depthCurly--;
239
+ buf += ch;
240
+ continue;
241
+ }
242
+ if (ch === '[') {
243
+ depthSquare++;
244
+ buf += ch;
245
+ continue;
246
+ }
247
+ if (ch === ']') {
248
+ depthSquare--;
249
+ buf += ch;
250
+ continue;
251
+ }
252
+ if (ch === '(') {
253
+ depthParen++;
254
+ buf += ch;
255
+ continue;
256
+ }
257
+ if (ch === ')') {
258
+ depthParen--;
259
+ buf += ch;
260
+ continue;
261
+ }
262
+
263
+ if (ch === ',' && depthCurly === 0 && depthSquare === 0 && depthParen === 0) {
264
+ parts.push(buf);
265
+ buf = '';
266
+ continue;
267
+ }
268
+ }
269
+
270
+ buf += ch;
271
+ }
272
+ if (buf.trim() !== '') parts.push(buf);
273
+ return parts;
274
+ }
275
+
276
+ // Find the first top-level colon (ignores strings and nested structures)
277
+ function findTopLevelColon(str: string): number {
278
+ let inSingle: boolean = false;
279
+ let inDouble: boolean = false;
280
+ let esc: boolean = false;
281
+ let depthCurly: number = 0;
282
+ let depthSquare: number = 0;
283
+ let depthParen: number = 0;
284
+
285
+ for (let i: number = 0; i < str.length; i++) {
286
+ const ch: string = str[i];
287
+ if (esc) {
288
+ esc = false;
289
+ continue;
290
+ }
291
+ if (ch === '\\') {
292
+ esc = true;
293
+ continue;
294
+ }
295
+ if (ch === "'" && !inDouble) {
296
+ inSingle = !inSingle;
297
+ continue;
298
+ }
299
+ if (ch === '"' && !inSingle) {
300
+ inDouble = !inDouble;
301
+ continue;
302
+ }
303
+
304
+ if (!inSingle && !inDouble) {
305
+ if (ch === '{') {
306
+ depthCurly++;
307
+ continue;
308
+ }
309
+ if (ch === '}') {
310
+ depthCurly--;
311
+ continue;
312
+ }
313
+ if (ch === '[') {
314
+ depthSquare++;
315
+ continue;
316
+ }
317
+ if (ch === ']') {
318
+ depthSquare--;
319
+ continue;
320
+ }
321
+ if (ch === '(') {
322
+ depthParen++;
323
+ continue;
324
+ }
325
+ if (ch === ')') {
326
+ depthParen--;
327
+ continue;
328
+ }
329
+
330
+ if (ch === ':' && depthCurly === 0 && depthSquare === 0 && depthParen === 0) {
331
+ return i;
332
+ }
333
+ }
334
+ }
335
+ return -1;
336
+ }
337
+
338
+ // Extract key content (supports "key", 'key', key) → returns pure string key
339
+ function extractKeyContent(rawKey: string): string {
340
+ const r: string = rawKey.trim();
341
+ if ((r.startsWith('"') && r.endsWith('"')) || (r.startsWith("'") && r.endsWith("'"))) {
342
+ const inner: string = r.slice(1, -1).replace(/\\"/g, '"').replace(/\\'/g, "'");
343
+ return inner;
344
+ }
345
+ return r;
15
346
  }
16
347
 
17
348
  /**
18
349
  * Determine whether it is in JSON format
19
- * @param str - The value to check
20
- * @returns boolean indicating if the value is valid JSON
350
+ * @private
21
351
  */
22
- function isJSON(str: unknown): boolean {
23
- if (typeof str === 'string' && str.length > 0) {
24
- if (str.replace(/\"\"/g, '').replace(/\,/g, '') === '[{}]') {
25
- return false;
26
- }
27
-
28
- if (/^[\],:{}\s]*$/.test(str.replace(/\\["\\\/bfnrtu]/g, '@')
29
- .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
30
- .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
352
+ function isJSON(input: any): boolean {
353
+ if (typeof (input) === 'string' && input.length > 0) {
354
+ return fixAndParseJSON(input).success;
355
+ } else {
356
+ if (
357
+ typeof (input) === 'object' &&
358
+ Object.prototype.toString.call(input) === '[object Object]' &&
359
+ !(input as any).length
360
+ ) {
31
361
  return true;
362
+ } else {
363
+ return false;
32
364
  }
33
- return false;
34
365
  }
366
+ }
35
367
 
368
+
369
+
370
+ /**
371
+ * Check if a string is a valid number
372
+ * @param str - The string to check
373
+ * @returns boolean indicating if the string is a valid number
374
+ */
375
+ function isValidNumeric(str: unknown): boolean {
376
+ if (typeof str !== "string") return false; // we only process strings!
36
377
  if (
37
- typeof str === 'object' &&
38
- Object.prototype.toString.call(str) === '[object Object]' &&
39
- !(str as any).length
378
+ !isNaN(Number(str)) && // use type coercion to parse the _entirety_ of the string
379
+ !isNaN(parseFloat(str)) // ensure strings of whitespace fail
40
380
  ) {
41
381
  return true;
42
382
  }
43
383
  return false;
44
384
  }
45
385
 
386
+
46
387
  /**
47
388
  * Check if input is empty
48
389
  * @param input - The input to check (string or array of strings)
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "author": "UIUX Lab",
3
3
  "email": "uiuxlab@gmail.com",
4
4
  "name": "funda-ui",
5
- "version": "4.7.599",
5
+ "version": "4.7.601",
6
6
  "description": "React components using pure Bootstrap 5+ which does not contain any external style and script libraries.",
7
7
  "repository": {
8
8
  "type": "git",