funda-ui 4.7.604 → 4.7.608

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,73 +1,85 @@
1
1
  /**
2
2
  * Fix And Parse JSON (Support for handling complex escape JSON strings)
3
+ * @desc recursively fix top-level key/value (recursively handles when encountering top-level values that are objects/arrays)
3
4
  * @private
4
5
  */
5
6
  /*
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);
7
+ - Still prioritize JSON.parse first;
8
+ - After parse fails, do unescaping (\\" → ");
9
+ - Then process the outermost layer (object or array) key by key, value by value;
10
+ - If a top-level value is an unquoted object or array (e.g., messages: [ {...} ]), recursively treat that value as a new outermost layer for fixing;
11
+ - For strings wrapped in quotes ('...' or "..."), only extract the outer layer and re-encode with JSON.stringify (ensuring internal single/double quotes won't be mistakenly changed);
14
12
  - Set MAX_DEPTH to prevent infinite recursion.
15
13
  */
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
14
  /*
33
- DEMO:
15
+ @Examples:
34
16
 
35
- // ✅ Valid JSON (contains svg and single-quote content)
17
+ // ✅ Valid JSON (contains svg and single quote content)
36
18
  const okJson = `{
37
- "label":"<svg width='16' height='16'><path fill='currentColor' d='M19 13h-6'/></svg> New Session",
19
+ "label":"<svg width='16' height='16'><path fill='currentColor' d='M19 13h-6'/></svg> 新建会话",
38
20
  "value":"new",
39
21
  "onClick":"method.setVal(''); method.clearData();"
40
22
  }`;
41
23
 
42
- // Single-quote JSON
24
+ const okJson2 = `{
25
+ label:"<svg width='16' height='16'><path fill='currentColor' d='M19 13h-6'/></svg> 新建会话",
26
+ value:"new",
27
+ onClick:"method.setVal(''); method.clearData();"
28
+ }`;
29
+
30
+ // ⚠️ Single quote JSON
43
31
  const badJson = "{'model':'{model}','messages':[{'role':'user','content':'{message}'}],'stream': true}";
44
32
 
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();\\\"}";
33
+ // ⚠️ Escaped JSON
34
+ const badJson2 = "{\\\"label\\\":\\\"<svg width='16' height='16' viewBox='0 0 24 24'><path fill='currentColor' d='M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z'/></svg> 新建会话\\\",\\\"value\\\":\\\"new\\\",\\\"onClick\\\":\\\"method.setVal(''); method.clearData();\\\"}";
35
+
36
+ const badJson3 = "{\"label\":\"<svg width='16' height='16' viewBox='0 0 24 24'><path fill='currentColor' d='M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z'/></svg> 新建会话\",\"value\":\"new\",\"onClick\":\"method.setVal(''); method.clearData();\"}";
37
+
38
+ 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> 深度思考\",\"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> 精简回答\",\"value\":\"brief\",\"onClick\":\"if(isActive){method.setContextData({systemPrompt:'请精简回答,字数控制在150个字左右, 思考过程请简洁简短',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> 指标分析\",\"value\":\"lab\",\"onClick\":\"return method.executeCustomMethod('getLibList')\",\"isSelect\":true,\"dynamicOptions\":true}]";
39
+
40
+
41
+ // ❌ Invalid JSON with missing } or ]
42
+ const errorJson001 = "{'model':'qwen-plus','base_url':'https://dashscope.aliyuncs.com/compatible-mode/v1/','api_key':'sk-0989fb9baab8450682af4d000f5b7cba','message':'{message}','stream':'true','chatId': '{chatId}', 'token': '{token}'";
43
+
44
+ const errorJson002 = "[{'model':'qwen-plus','base_url':'https://dashscope.aliyuncs.com/compatible-mode/v1/','api_key':'sk-0989fb9baab8450682af4d000f5b7cba','message':'{message}','stream':'true','chatId': '{chatId}', 'token': '{token}'}";
47
45
 
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();\"}";
46
+ // Invalid JSON with missing quotes
47
+ const errorJson003 = "{'model':'qwen-plus','base_url':'https://dashscope.aliyuncs.com/compatible-mode/v1/','api_key':'sk-0989fb9baab8450682af4d000f5b7cba','message':'{message}','stream':'true','chatId': {chatId}', 'token': '{token}'}";
49
48
 
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
49
 
52
50
 
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
51
+ console.log('okJson =>', fixAndParseJSON(okJson)); // Can parse normally success = true
52
+ console.log('okJson =>', fixAndParseJSON(okJson2)); // Can parse normally success = true
53
+ console.log('badJson =>', fixAndParseJSON(badJson)); // Can parse after fixing success = true
54
+ console.log('badJson =>', fixAndParseJSON(badJson2)); // Can parse after fixing success = true
55
+ console.log('badJson =>', fixAndParseJSON(badJson3)); // Can parse after fixing success = true
56
+ console.log('badJson =>', fixAndParseJSON(badJson4)); // Can parse after fixing success = true
57
+ console.log('errorJson =>', fixAndParseJSON(errorJson001)); // {success: false, error: 'Invalid JSON format', details: 'Invalid object: mismatched braces'}
58
+ console.log('errorJson =>', fixAndParseJSON(errorJson002)); // {success: false, error: 'Invalid JSON format', details: "Expected property name or '}' in JSON at position 2 (line 1 column 3)"}
59
+ console.log('errorJson =>', fixAndParseJSON(errorJson003)); // {success: false, error: 'Invalid JSON format', details: 'Invalid object: mismatched braces'}
60
+
58
61
  */
59
- export function fixAndParseJSON(input: string): ParseResult {
62
+
63
+ // Type definitions
64
+ interface ParseResult {
65
+ success: boolean;
66
+ data?: any;
67
+ error?: string;
68
+ details?: string;
69
+ }
70
+
71
+ function fixAndParseJSON(input: string): ParseResult {
60
72
  const MAX_DEPTH = 6;
61
73
 
62
- // 1. Fast attempt
74
+ // 1. Quick attempt
63
75
  try {
64
76
  return { success: true, data: JSON.parse(input) };
65
77
  } catch (e) {
66
- // continue to repair
78
+ // Continue with fixing
67
79
  }
68
80
 
69
- // 2. Simple unescape of \" (common when copied from JS literals)
70
- let s: string = input;
81
+ // 2. Simple unescaping of escaped \" (common case from pasted JS literals)
82
+ let s = input;
71
83
  if (s.includes('\\"')) s = s.replace(/\\"/g, '"');
72
84
  s = s.trim();
73
85
 
@@ -82,42 +94,75 @@ export function fixAndParseJSON(input: string): ParseResult {
82
94
 
83
95
  return { success: true, data: JSON.parse(s) };
84
96
  } catch (err) {
85
- return {
86
- success: false,
87
- error: 'Invalid JSON format',
88
- details: err instanceof Error ? err.message : String(err)
89
- };
97
+ const error = err instanceof Error ? err : new Error('Unknown error');
98
+ return { success: false, error: 'Invalid JSON format', details: error.message };
90
99
  }
91
100
  }
92
101
 
93
- /* ---------- Helper (recursive) functions ---------- */
102
+ /* ---------- Helper (Recursive) Functions ---------- */
94
103
 
95
104
  function processTopObject(str: string, depth: number, MAX_DEPTH: number): string {
96
105
  if (depth > MAX_DEPTH) return str;
97
106
  str = str.trim();
98
- // Ensure it is wrapped in { ... }
107
+
108
+ // First check if braces match
109
+ let braceCount = 0;
110
+ let inString = false;
111
+ let escapeNext = false;
112
+
113
+ for (let i = 0; i < str.length; i++) {
114
+ const ch = str[i];
115
+
116
+ if (escapeNext) {
117
+ escapeNext = false;
118
+ continue;
119
+ }
120
+
121
+ if (ch === '\\') {
122
+ escapeNext = true;
123
+ continue;
124
+ }
125
+
126
+ if ((ch === '"' || ch === "'") && !escapeNext) {
127
+ inString = !inString;
128
+ continue;
129
+ }
130
+
131
+ if (!inString) {
132
+ if (ch === '{') braceCount++;
133
+ else if (ch === '}') braceCount--;
134
+ }
135
+ }
136
+
137
+ if (braceCount !== 0) {
138
+ throw new Error('Invalid object: mismatched braces');
139
+ }
140
+
141
+ // Ensure both ends are { ... }
99
142
  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;
143
+ const f = str.indexOf('{'), l = str.lastIndexOf('}');
144
+ if (f === -1 || l === -1 || l <= f) {
145
+ throw new Error('Invalid object format: missing or mismatched braces');
146
+ }
103
147
  str = str.slice(f, l + 1);
104
148
  }
105
- const inner: string = str.slice(1, -1);
106
- const pairs: string[] = splitTopLevel(inner);
149
+
150
+ const inner = str.slice(1, -1);
151
+ const pairs = splitTopLevel(inner);
107
152
 
108
- const repairedPairs: string[] = pairs.map(pair => {
153
+ const repairedPairs = pairs.map(pair => {
109
154
  if (!pair || pair.trim() === '') return '';
110
- const idx: number = findTopLevelColon(pair);
155
+ const idx = findTopLevelColon(pair);
111
156
  if (idx === -1) {
112
157
  return pair; // Non key:value fragment, keep as is (rare case)
113
158
  }
114
- const rawKey: string = pair.slice(0, idx).trim();
115
- const rawVal: string = pair.slice(idx + 1);
159
+ const rawKey = pair.slice(0, idx).trim();
160
+ const rawVal = pair.slice(idx + 1);
116
161
 
117
- const keyContent: string = extractKeyContent(rawKey);
118
- const keyJson: string = JSON.stringify(keyContent);
162
+ const keyContent = extractKeyContent(rawKey);
163
+ const keyJson = JSON.stringify(keyContent);
119
164
 
120
- const repairedValue: string = repairPossiblyQuotedValue(rawVal, depth + 1, MAX_DEPTH);
165
+ const repairedValue = repairPossiblyQuotedValue(rawVal, depth + 1, MAX_DEPTH);
121
166
 
122
167
  return keyJson + ':' + repairedValue;
123
168
  });
@@ -129,16 +174,15 @@ function processTopArray(str: string, depth: number, MAX_DEPTH: number): string
129
174
  if (depth > MAX_DEPTH) return str;
130
175
  str = str.trim();
131
176
  if (!(str.startsWith('[') && str.endsWith(']'))) {
132
- const f: number = str.indexOf('[');
133
- const l: number = str.lastIndexOf(']');
177
+ const f = str.indexOf('['), l = str.lastIndexOf(']');
134
178
  if (f === -1 || l === -1 || l <= f) return str;
135
179
  str = str.slice(f, l + 1);
136
180
  }
137
- const inner: string = str.slice(1, -1);
138
- const elements: string[] = splitTopLevel(inner);
181
+ const inner = str.slice(1, -1);
182
+ const elements = splitTopLevel(inner);
139
183
 
140
- const processed: string[] = elements.map(el => {
141
- const t: string = el.trim();
184
+ const processed = elements.map(el => {
185
+ const t = el.trim();
142
186
  if (t === '') return '';
143
187
  if (t.startsWith('{')) return processTopObject(t, depth + 1, MAX_DEPTH);
144
188
  if (t.startsWith('[')) return processTopArray(t, depth + 1, MAX_DEPTH);
@@ -148,37 +192,30 @@ function processTopArray(str: string, depth: number, MAX_DEPTH: number): string
148
192
  return '[' + processed.join(',') + ']';
149
193
  }
150
194
 
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)
195
+ // If it's a string wrapped in quotes, extract the inner content and JSON.stringify (safe escaping)
196
+ // If it's an object/array literal (not wrapped in quotes), recursively process (treat as new outermost layer)
197
+ // Otherwise return the original fragment directly (numbers/true/false/null or JS expressions)
154
198
  function repairPossiblyQuotedValue(rawVal: string, depth: number, MAX_DEPTH: number): string {
155
- const v: string = rawVal.trim();
199
+ const v = rawVal.trim();
156
200
  if (v === '') return v;
157
201
 
158
202
  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--) {
203
+ const quote = v[0];
204
+ // Find the last unescaped same quote
205
+ let lastPos = -1;
206
+ for (let i = v.length - 1; i >= 0; i--) {
163
207
  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
- }
208
+ // check escaped
209
+ let bs = 0, k = i - 1;
210
+ while (k >= 0 && v[k] === '\\') { bs++; k--; }
211
+ if (bs % 2 === 0) { lastPos = i; break; }
175
212
  }
176
213
  }
177
- const inner: string = lastPos > 0 ? v.slice(1, lastPos) : v.slice(1);
178
- return JSON.stringify(inner); // Generate valid JSON string (auto escape)
214
+ const inner = lastPos > 0 ? v.slice(1, lastPos) : v.slice(1);
215
+ return JSON.stringify(inner); // Use JSON.stringify to generate valid JSON string (automatically escape internal quotes, etc.)
179
216
  }
180
217
 
181
- // If unquoted object/array literal -> recurse
218
+ // If it's an object or array literal (not wrapped in quotes) -> recursively treat as new outermost layer
182
219
  if (v.startsWith('{')) {
183
220
  return processTopObject(v, depth, MAX_DEPTH);
184
221
  }
@@ -186,25 +223,21 @@ function repairPossiblyQuotedValue(rawVal: string, depth: number, MAX_DEPTH: num
186
223
  return processTopArray(v, depth, MAX_DEPTH);
187
224
  }
188
225
 
189
- // Other (number, boolean, null, raw expression): return as is
226
+ // Others (numbers, boolean, null, or JS expressions): return as-is
190
227
  return v;
191
228
  }
192
229
 
193
- /* --------- Utils: split by top-level commas, find colon, extract key --------- */
230
+ /* --------- Utilities: Split by top-level commas, find top-level colon, extract key --------- */
194
231
 
195
- // Split string by top-level commas (ignores commas inside strings/objects/arrays/parentheses)
232
+ // Split by top-level commas (ignore strings, sub-objects, sub-arrays, commas inside parentheses)
196
233
  function splitTopLevel(str: string): string[] {
197
234
  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;
235
+ let buf = '';
236
+ let depthCurly = 0, depthSquare = 0, depthParen = 0;
237
+ let inSingle = false, inDouble = false, esc = false;
205
238
 
206
- for (let i: number = 0; i < str.length; i++) {
207
- const ch: string = str[i];
239
+ for (let i = 0; i < str.length; i++) {
240
+ const ch = str[i];
208
241
 
209
242
  if (esc) {
210
243
  buf += ch;
@@ -217,48 +250,16 @@ function splitTopLevel(str: string): string[] {
217
250
  continue;
218
251
  }
219
252
 
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
- }
253
+ if (ch === "'" && !inDouble) { inSingle = !inSingle; buf += ch; continue; }
254
+ if (ch === '"' && !inSingle) { inDouble = !inDouble; buf += ch; continue; }
230
255
 
231
256
  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
- }
257
+ if (ch === '{') { depthCurly++; buf += ch; continue; }
258
+ if (ch === '}') { depthCurly--; buf += ch; continue; }
259
+ if (ch === '[') { depthSquare++; buf += ch; continue; }
260
+ if (ch === ']') { depthSquare--; buf += ch; continue; }
261
+ if (ch === '(') { depthParen++; buf += ch; continue; }
262
+ if (ch === ')') { depthParen--; buf += ch; continue; }
262
263
 
263
264
  if (ch === ',' && depthCurly === 0 && depthSquare === 0 && depthParen === 0) {
264
265
  parts.push(buf);
@@ -269,63 +270,34 @@ function splitTopLevel(str: string): string[] {
269
270
 
270
271
  buf += ch;
271
272
  }
273
+
274
+ // Check for unclosed brackets or quotes
275
+ if (depthCurly !== 0 || depthSquare !== 0 || depthParen !== 0 || inSingle || inDouble) {
276
+ throw new Error('Invalid JSON: unclosed brackets or quotes');
277
+ }
278
+
272
279
  if (buf.trim() !== '') parts.push(buf);
273
280
  return parts;
274
281
  }
275
282
 
276
- // Find the first top-level colon (ignores strings and nested structures)
283
+ // Find the first "top-level" colon index (ignore inside strings & sub-levels)
277
284
  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
- }
285
+ let inSingle = false, inDouble = false, esc = false;
286
+ let depthCurly = 0, depthSquare = 0, depthParen = 0;
287
+ for (let i = 0; i < str.length; i++) {
288
+ const ch = str[i];
289
+ if (esc) { esc = false; continue; }
290
+ if (ch === '\\') { esc = true; continue; }
291
+ if (ch === "'" && !inDouble) { inSingle = !inSingle; continue; }
292
+ if (ch === '"' && !inSingle) { inDouble = !inDouble; continue; }
303
293
 
304
294
  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
- }
295
+ if (ch === '{') { depthCurly++; continue; }
296
+ if (ch === '}') { depthCurly--; continue; }
297
+ if (ch === '[') { depthSquare++; continue; }
298
+ if (ch === ']') { depthSquare--; continue; }
299
+ if (ch === '(') { depthParen++; continue; }
300
+ if (ch === ')') { depthParen--; continue; }
329
301
 
330
302
  if (ch === ':' && depthCurly === 0 && depthSquare === 0 && depthParen === 0) {
331
303
  return i;
@@ -335,16 +307,17 @@ function findTopLevelColon(str: string): number {
335
307
  return -1;
336
308
  }
337
309
 
338
- // Extract key content (supports "key", 'key', key) returns pure string key
310
+ // Extract key content (supports "key", 'key', key), returns pure key string
339
311
  function extractKeyContent(rawKey: string): string {
340
- const r: string = rawKey.trim();
312
+ const r = rawKey.trim();
341
313
  if ((r.startsWith('"') && r.endsWith('"')) || (r.startsWith("'") && r.endsWith("'"))) {
342
- const inner: string = r.slice(1, -1).replace(/\\"/g, '"').replace(/\\'/g, "'");
314
+ const inner = r.slice(1, -1).replace(/\\"/g, '"').replace(/\\'/g, "'");
343
315
  return inner;
344
316
  }
345
317
  return r;
346
318
  }
347
319
 
320
+
348
321
  /**
349
322
  * Determine whether it is in JSON format
350
323
  * @private
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.604",
5
+ "version": "4.7.608",
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",