@nocobase/plugin-ai 2.1.0-beta.17 → 2.1.0-beta.19
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/dist/ai/ai-employees/atlas/prompt.md +28 -22
- package/dist/ai/ai-employees/viz.js +14 -88
- package/dist/ai/skills/business-analysis-report/SKILLS.md +8 -3
- package/dist/ai/skills/business-analysis-report/tools/businessReportGenerator.js +49 -8
- package/dist/client/280.003b9e9788599244.js +10 -0
- package/dist/client/{646.51313bb0d85d6366.js → 646.cba98d80e9e6ea74.js} +1 -1
- package/dist/client/index.js +4 -4
- package/dist/common/recommended-models.js +1 -25
- package/dist/externalVersion.js +14 -14
- package/dist/locale/en-US.json +9 -0
- package/dist/locale/zh-CN.json +9 -0
- package/dist/node_modules/fast-glob/package.json +1 -1
- package/dist/node_modules/flexsearch/package.json +1 -1
- package/dist/node_modules/fs-extra/package.json +1 -1
- package/dist/node_modules/jsonrepair/bin/cli.js +179 -0
- package/dist/node_modules/jsonrepair/lib/cjs/index.js +1 -0
- package/dist/node_modules/jsonrepair/lib/cjs/package.json +3 -0
- package/dist/node_modules/jsonrepair/lib/cjs/regular/jsonrepair.js +746 -0
- package/dist/node_modules/jsonrepair/lib/cjs/stream.js +13 -0
- package/dist/node_modules/jsonrepair/lib/cjs/streaming/buffer/InputBuffer.js +75 -0
- package/dist/node_modules/jsonrepair/lib/cjs/streaming/buffer/OutputBuffer.js +117 -0
- package/dist/node_modules/jsonrepair/lib/cjs/streaming/core.js +824 -0
- package/dist/node_modules/jsonrepair/lib/cjs/streaming/stack.js +51 -0
- package/dist/node_modules/jsonrepair/lib/cjs/streaming/stream.js +37 -0
- package/dist/node_modules/jsonrepair/lib/cjs/utils/JSONRepairError.js +14 -0
- package/dist/node_modules/jsonrepair/lib/cjs/utils/stringUtils.js +174 -0
- package/dist/node_modules/jsonrepair/lib/esm/index.js +4 -0
- package/dist/node_modules/jsonrepair/lib/esm/regular/jsonrepair.js +740 -0
- package/dist/node_modules/jsonrepair/lib/esm/stream.js +3 -0
- package/dist/node_modules/jsonrepair/lib/esm/streaming/buffer/InputBuffer.js +69 -0
- package/dist/node_modules/jsonrepair/lib/esm/streaming/buffer/OutputBuffer.js +111 -0
- package/dist/node_modules/jsonrepair/lib/esm/streaming/core.js +818 -0
- package/dist/node_modules/jsonrepair/lib/esm/streaming/stack.js +44 -0
- package/dist/node_modules/jsonrepair/lib/esm/streaming/stream.js +31 -0
- package/dist/node_modules/jsonrepair/lib/esm/utils/JSONRepairError.js +7 -0
- package/dist/node_modules/jsonrepair/lib/esm/utils/stringUtils.js +147 -0
- package/dist/node_modules/jsonrepair/lib/types/index.d.ts +3 -0
- package/dist/node_modules/jsonrepair/lib/types/regular/jsonrepair.d.ts +18 -0
- package/dist/node_modules/jsonrepair/lib/types/stream.d.ts +2 -0
- package/dist/node_modules/jsonrepair/lib/types/streaming/buffer/InputBuffer.d.ts +14 -0
- package/dist/node_modules/jsonrepair/lib/types/streaming/buffer/OutputBuffer.d.ts +18 -0
- package/dist/node_modules/jsonrepair/lib/types/streaming/core.d.ts +11 -0
- package/dist/node_modules/jsonrepair/lib/types/streaming/stack.d.ts +20 -0
- package/dist/node_modules/jsonrepair/lib/types/streaming/stream.d.ts +7 -0
- package/dist/node_modules/jsonrepair/lib/types/utils/JSONRepairError.d.ts +5 -0
- package/dist/node_modules/jsonrepair/lib/types/utils/stringUtils.d.ts +65 -0
- package/dist/node_modules/jsonrepair/lib/umd/jsonrepair.js +903 -0
- package/dist/node_modules/jsonrepair/lib/umd/jsonrepair.min.js +3 -0
- package/dist/node_modules/jsonrepair/lib/umd/package.json +3 -0
- package/dist/node_modules/jsonrepair/package.json +1 -0
- package/dist/node_modules/nodejs-snowflake/package.json +1 -1
- package/dist/node_modules/openai/package.json +1 -1
- package/dist/node_modules/zod/package.json +1 -1
- package/dist/server/resource/aiEmployees.js +15 -1
- package/dist/server/resource/aiSkills.js +1 -1
- package/package.json +2 -2
- package/dist/client/280.029428b83d62155f.js +0 -10
|
@@ -0,0 +1,903 @@
|
|
|
1
|
+
(function (global, factory) {
|
|
2
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
|
3
|
+
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
|
4
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.JSONRepair = {}));
|
|
5
|
+
})(this, (function (exports) { 'use strict';
|
|
6
|
+
|
|
7
|
+
class JSONRepairError extends Error {
|
|
8
|
+
constructor(message, position) {
|
|
9
|
+
super(`${message} at position ${position}`);
|
|
10
|
+
this.position = position;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const codeSpace = 0x20; // " "
|
|
15
|
+
const codeNewline = 0xa; // "\n"
|
|
16
|
+
const codeTab = 0x9; // "\t"
|
|
17
|
+
const codeReturn = 0xd; // "\r"
|
|
18
|
+
const codeNonBreakingSpace = 0xa0;
|
|
19
|
+
const codeEnQuad = 0x2000;
|
|
20
|
+
const codeHairSpace = 0x200a;
|
|
21
|
+
const codeNarrowNoBreakSpace = 0x202f;
|
|
22
|
+
const codeMediumMathematicalSpace = 0x205f;
|
|
23
|
+
const codeIdeographicSpace = 0x3000;
|
|
24
|
+
function isHex(char) {
|
|
25
|
+
return /^[0-9A-Fa-f]$/.test(char);
|
|
26
|
+
}
|
|
27
|
+
function isDigit(char) {
|
|
28
|
+
return char >= '0' && char <= '9';
|
|
29
|
+
}
|
|
30
|
+
function isValidStringCharacter(char) {
|
|
31
|
+
// note that the valid range is between \u{0020} and \u{10ffff},
|
|
32
|
+
// but in JavaScript it is not possible to create a code point larger than
|
|
33
|
+
// \u{10ffff}, so there is no need to test for that here.
|
|
34
|
+
return char >= '\u0020';
|
|
35
|
+
}
|
|
36
|
+
function isDelimiter(char) {
|
|
37
|
+
return ',:[]/{}()\n+'.includes(char);
|
|
38
|
+
}
|
|
39
|
+
function isFunctionNameCharStart(char) {
|
|
40
|
+
return char >= 'a' && char <= 'z' || char >= 'A' && char <= 'Z' || char === '_' || char === '$';
|
|
41
|
+
}
|
|
42
|
+
function isFunctionNameChar(char) {
|
|
43
|
+
return char >= 'a' && char <= 'z' || char >= 'A' && char <= 'Z' || char === '_' || char === '$' || char >= '0' && char <= '9';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// matches "https://" and other schemas
|
|
47
|
+
const regexUrlStart = /^(http|https|ftp|mailto|file|data|irc):\/\/$/;
|
|
48
|
+
|
|
49
|
+
// matches all valid URL characters EXCEPT "[", "]", and ",", since that are important JSON delimiters
|
|
50
|
+
const regexUrlChar = /^[A-Za-z0-9-._~:/?#@!$&'()*+;=]$/;
|
|
51
|
+
function isUnquotedStringDelimiter(char) {
|
|
52
|
+
return ',[]/{}\n+'.includes(char);
|
|
53
|
+
}
|
|
54
|
+
function isStartOfValue(char) {
|
|
55
|
+
return isQuote(char) || regexStartOfValue.test(char);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// alpha, number, minus, or opening bracket or brace
|
|
59
|
+
const regexStartOfValue = /^[[{\w-]$/;
|
|
60
|
+
function isControlCharacter(char) {
|
|
61
|
+
return char === '\n' || char === '\r' || char === '\t' || char === '\b' || char === '\f';
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Check if the given character is a whitespace character like space, tab, or
|
|
65
|
+
* newline
|
|
66
|
+
*/
|
|
67
|
+
function isWhitespace(text, index) {
|
|
68
|
+
const code = text.charCodeAt(index);
|
|
69
|
+
return code === codeSpace || code === codeNewline || code === codeTab || code === codeReturn;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Check if the given character is a whitespace character like space or tab,
|
|
74
|
+
* but NOT a newline
|
|
75
|
+
*/
|
|
76
|
+
function isWhitespaceExceptNewline(text, index) {
|
|
77
|
+
const code = text.charCodeAt(index);
|
|
78
|
+
return code === codeSpace || code === codeTab || code === codeReturn;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Check if the given character is a special whitespace character, some
|
|
83
|
+
* unicode variant
|
|
84
|
+
*/
|
|
85
|
+
function isSpecialWhitespace(text, index) {
|
|
86
|
+
const code = text.charCodeAt(index);
|
|
87
|
+
return code === codeNonBreakingSpace || code >= codeEnQuad && code <= codeHairSpace || code === codeNarrowNoBreakSpace || code === codeMediumMathematicalSpace || code === codeIdeographicSpace;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Test whether the given character is a quote or double quote character.
|
|
92
|
+
* Also tests for special variants of quotes.
|
|
93
|
+
*/
|
|
94
|
+
function isQuote(char) {
|
|
95
|
+
// the first check double quotes, since that occurs most often
|
|
96
|
+
return isDoubleQuoteLike(char) || isSingleQuoteLike(char);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Test whether the given character is a double quote character.
|
|
101
|
+
* Also tests for special variants of double quotes.
|
|
102
|
+
*/
|
|
103
|
+
function isDoubleQuoteLike(char) {
|
|
104
|
+
return char === '"' || char === '\u201c' || char === '\u201d';
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Test whether the given character is a double quote character.
|
|
109
|
+
* Does NOT test for special variants of double quotes.
|
|
110
|
+
*/
|
|
111
|
+
function isDoubleQuote(char) {
|
|
112
|
+
return char === '"';
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Test whether the given character is a single quote character.
|
|
117
|
+
* Also tests for special variants of single quotes.
|
|
118
|
+
*/
|
|
119
|
+
function isSingleQuoteLike(char) {
|
|
120
|
+
return char === "'" || char === '\u2018' || char === '\u2019' || char === '\u0060' || char === '\u00b4';
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Test whether the given character is a single quote character.
|
|
125
|
+
* Does NOT test for special variants of single quotes.
|
|
126
|
+
*/
|
|
127
|
+
function isSingleQuote(char) {
|
|
128
|
+
return char === "'";
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Strip last occurrence of textToStrip from text
|
|
133
|
+
*/
|
|
134
|
+
function stripLastOccurrence(text, textToStrip) {
|
|
135
|
+
let stripRemainingText = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
|
136
|
+
const index = text.lastIndexOf(textToStrip);
|
|
137
|
+
return index !== -1 ? text.substring(0, index) + (stripRemainingText ? '' : text.substring(index + 1)) : text;
|
|
138
|
+
}
|
|
139
|
+
function insertBeforeLastWhitespace(text, textToInsert) {
|
|
140
|
+
let index = text.length;
|
|
141
|
+
if (!isWhitespace(text, index - 1)) {
|
|
142
|
+
// no trailing whitespaces
|
|
143
|
+
return text + textToInsert;
|
|
144
|
+
}
|
|
145
|
+
while (isWhitespace(text, index - 1)) {
|
|
146
|
+
index--;
|
|
147
|
+
}
|
|
148
|
+
return text.substring(0, index) + textToInsert + text.substring(index);
|
|
149
|
+
}
|
|
150
|
+
function removeAtIndex(text, start, count) {
|
|
151
|
+
return text.substring(0, start) + text.substring(start + count);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Test whether a string ends with a newline or comma character and optional whitespace
|
|
156
|
+
*/
|
|
157
|
+
function endsWithCommaOrNewline(text) {
|
|
158
|
+
return /[,\n][ \t\r]*$/.test(text);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const controlCharacters = {
|
|
162
|
+
'\b': '\\b',
|
|
163
|
+
'\f': '\\f',
|
|
164
|
+
'\n': '\\n',
|
|
165
|
+
'\r': '\\r',
|
|
166
|
+
'\t': '\\t'
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
// map with all escape characters
|
|
170
|
+
const escapeCharacters = {
|
|
171
|
+
'"': '"',
|
|
172
|
+
'\\': '\\',
|
|
173
|
+
'/': '/',
|
|
174
|
+
b: '\b',
|
|
175
|
+
f: '\f',
|
|
176
|
+
n: '\n',
|
|
177
|
+
r: '\r',
|
|
178
|
+
t: '\t'
|
|
179
|
+
// note that \u is handled separately in parseString()
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Repair a string containing an invalid JSON document.
|
|
184
|
+
* For example changes JavaScript notation into JSON notation.
|
|
185
|
+
*
|
|
186
|
+
* Example:
|
|
187
|
+
*
|
|
188
|
+
* try {
|
|
189
|
+
* const json = "{name: 'John'}"
|
|
190
|
+
* const repaired = jsonrepair(json)
|
|
191
|
+
* console.log(repaired)
|
|
192
|
+
* // '{"name": "John"}'
|
|
193
|
+
* } catch (err) {
|
|
194
|
+
* console.error(err)
|
|
195
|
+
* }
|
|
196
|
+
*
|
|
197
|
+
*/
|
|
198
|
+
function jsonrepair(text) {
|
|
199
|
+
let i = 0; // current index in text
|
|
200
|
+
let output = ''; // generated output
|
|
201
|
+
|
|
202
|
+
parseMarkdownCodeBlock(['```', '[```', '{```']);
|
|
203
|
+
const processed = parseValue();
|
|
204
|
+
if (!processed) {
|
|
205
|
+
throwUnexpectedEnd();
|
|
206
|
+
}
|
|
207
|
+
parseMarkdownCodeBlock(['```', '```]', '```}']);
|
|
208
|
+
const processedComma = parseCharacter(',');
|
|
209
|
+
if (processedComma) {
|
|
210
|
+
parseWhitespaceAndSkipComments();
|
|
211
|
+
}
|
|
212
|
+
if (isStartOfValue(text[i]) && endsWithCommaOrNewline(output)) {
|
|
213
|
+
// start of a new value after end of the root level object: looks like
|
|
214
|
+
// newline delimited JSON -> turn into a root level array
|
|
215
|
+
if (!processedComma) {
|
|
216
|
+
// repair missing comma
|
|
217
|
+
output = insertBeforeLastWhitespace(output, ',');
|
|
218
|
+
}
|
|
219
|
+
parseNewlineDelimitedJSON();
|
|
220
|
+
} else if (processedComma) {
|
|
221
|
+
// repair: remove trailing comma
|
|
222
|
+
output = stripLastOccurrence(output, ',');
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// repair redundant end quotes
|
|
226
|
+
while (text[i] === '}' || text[i] === ']') {
|
|
227
|
+
i++;
|
|
228
|
+
parseWhitespaceAndSkipComments();
|
|
229
|
+
}
|
|
230
|
+
if (i >= text.length) {
|
|
231
|
+
// reached the end of the document properly
|
|
232
|
+
return output;
|
|
233
|
+
}
|
|
234
|
+
throwUnexpectedCharacter();
|
|
235
|
+
function parseValue() {
|
|
236
|
+
parseWhitespaceAndSkipComments();
|
|
237
|
+
const processed = parseObject() || parseArray() || parseString() || parseNumber() || parseKeywords() || parseUnquotedString(false) || parseRegex();
|
|
238
|
+
parseWhitespaceAndSkipComments();
|
|
239
|
+
return processed;
|
|
240
|
+
}
|
|
241
|
+
function parseWhitespaceAndSkipComments() {
|
|
242
|
+
let skipNewline = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
|
|
243
|
+
const start = i;
|
|
244
|
+
let changed = parseWhitespace(skipNewline);
|
|
245
|
+
do {
|
|
246
|
+
changed = parseComment();
|
|
247
|
+
if (changed) {
|
|
248
|
+
changed = parseWhitespace(skipNewline);
|
|
249
|
+
}
|
|
250
|
+
} while (changed);
|
|
251
|
+
return i > start;
|
|
252
|
+
}
|
|
253
|
+
function parseWhitespace(skipNewline) {
|
|
254
|
+
const _isWhiteSpace = skipNewline ? isWhitespace : isWhitespaceExceptNewline;
|
|
255
|
+
let whitespace = '';
|
|
256
|
+
while (true) {
|
|
257
|
+
if (_isWhiteSpace(text, i)) {
|
|
258
|
+
whitespace += text[i];
|
|
259
|
+
i++;
|
|
260
|
+
} else if (isSpecialWhitespace(text, i)) {
|
|
261
|
+
// repair special whitespace
|
|
262
|
+
whitespace += ' ';
|
|
263
|
+
i++;
|
|
264
|
+
} else {
|
|
265
|
+
break;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (whitespace.length > 0) {
|
|
269
|
+
output += whitespace;
|
|
270
|
+
return true;
|
|
271
|
+
}
|
|
272
|
+
return false;
|
|
273
|
+
}
|
|
274
|
+
function parseComment() {
|
|
275
|
+
// find a block comment '/* ... */'
|
|
276
|
+
if (text[i] === '/' && text[i + 1] === '*') {
|
|
277
|
+
// repair block comment by skipping it
|
|
278
|
+
while (i < text.length && !atEndOfBlockComment(text, i)) {
|
|
279
|
+
i++;
|
|
280
|
+
}
|
|
281
|
+
i += 2;
|
|
282
|
+
return true;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// find a line comment '// ...'
|
|
286
|
+
if (text[i] === '/' && text[i + 1] === '/') {
|
|
287
|
+
// repair line comment by skipping it
|
|
288
|
+
while (i < text.length && text[i] !== '\n') {
|
|
289
|
+
i++;
|
|
290
|
+
}
|
|
291
|
+
return true;
|
|
292
|
+
}
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
295
|
+
function parseMarkdownCodeBlock(blocks) {
|
|
296
|
+
// find and skip over a Markdown fenced code block:
|
|
297
|
+
// ``` ... ```
|
|
298
|
+
// or
|
|
299
|
+
// ```json ... ```
|
|
300
|
+
if (skipMarkdownCodeBlock(blocks)) {
|
|
301
|
+
if (isFunctionNameCharStart(text[i])) {
|
|
302
|
+
// strip the optional language specifier like "json"
|
|
303
|
+
while (i < text.length && isFunctionNameChar(text[i])) {
|
|
304
|
+
i++;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
parseWhitespaceAndSkipComments();
|
|
308
|
+
return true;
|
|
309
|
+
}
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
function skipMarkdownCodeBlock(blocks) {
|
|
313
|
+
parseWhitespace(true);
|
|
314
|
+
for (const block of blocks) {
|
|
315
|
+
const end = i + block.length;
|
|
316
|
+
if (text.slice(i, end) === block) {
|
|
317
|
+
i = end;
|
|
318
|
+
return true;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
function parseCharacter(char) {
|
|
324
|
+
if (text[i] === char) {
|
|
325
|
+
output += text[i];
|
|
326
|
+
i++;
|
|
327
|
+
return true;
|
|
328
|
+
}
|
|
329
|
+
return false;
|
|
330
|
+
}
|
|
331
|
+
function skipCharacter(char) {
|
|
332
|
+
if (text[i] === char) {
|
|
333
|
+
i++;
|
|
334
|
+
return true;
|
|
335
|
+
}
|
|
336
|
+
return false;
|
|
337
|
+
}
|
|
338
|
+
function skipEscapeCharacter() {
|
|
339
|
+
return skipCharacter('\\');
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Skip ellipsis like "[1,2,3,...]" or "[1,2,3,...,9]" or "[...,7,8,9]"
|
|
344
|
+
* or a similar construct in objects.
|
|
345
|
+
*/
|
|
346
|
+
function skipEllipsis() {
|
|
347
|
+
parseWhitespaceAndSkipComments();
|
|
348
|
+
if (text[i] === '.' && text[i + 1] === '.' && text[i + 2] === '.') {
|
|
349
|
+
// repair: remove the ellipsis (three dots) and optionally a comma
|
|
350
|
+
i += 3;
|
|
351
|
+
parseWhitespaceAndSkipComments();
|
|
352
|
+
skipCharacter(',');
|
|
353
|
+
return true;
|
|
354
|
+
}
|
|
355
|
+
return false;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Parse an object like '{"key": "value"}'
|
|
360
|
+
*/
|
|
361
|
+
function parseObject() {
|
|
362
|
+
if (text[i] === '{') {
|
|
363
|
+
output += '{';
|
|
364
|
+
i++;
|
|
365
|
+
parseWhitespaceAndSkipComments();
|
|
366
|
+
|
|
367
|
+
// repair: skip leading comma like in {, message: "hi"}
|
|
368
|
+
if (skipCharacter(',')) {
|
|
369
|
+
parseWhitespaceAndSkipComments();
|
|
370
|
+
}
|
|
371
|
+
let initial = true;
|
|
372
|
+
while (i < text.length && text[i] !== '}') {
|
|
373
|
+
let processedComma;
|
|
374
|
+
if (!initial) {
|
|
375
|
+
processedComma = parseCharacter(',');
|
|
376
|
+
if (!processedComma) {
|
|
377
|
+
// repair missing comma
|
|
378
|
+
output = insertBeforeLastWhitespace(output, ',');
|
|
379
|
+
}
|
|
380
|
+
parseWhitespaceAndSkipComments();
|
|
381
|
+
} else {
|
|
382
|
+
processedComma = true;
|
|
383
|
+
initial = false;
|
|
384
|
+
}
|
|
385
|
+
skipEllipsis();
|
|
386
|
+
const processedKey = parseString() || parseUnquotedString(true);
|
|
387
|
+
if (!processedKey) {
|
|
388
|
+
if (text[i] === '}' || text[i] === '{' || text[i] === ']' || text[i] === '[' || text[i] === undefined) {
|
|
389
|
+
// repair trailing comma
|
|
390
|
+
output = stripLastOccurrence(output, ',');
|
|
391
|
+
} else {
|
|
392
|
+
throwObjectKeyExpected();
|
|
393
|
+
}
|
|
394
|
+
break;
|
|
395
|
+
}
|
|
396
|
+
parseWhitespaceAndSkipComments();
|
|
397
|
+
const processedColon = parseCharacter(':');
|
|
398
|
+
const truncatedText = i >= text.length;
|
|
399
|
+
if (!processedColon) {
|
|
400
|
+
if (isStartOfValue(text[i]) || truncatedText) {
|
|
401
|
+
// repair missing colon
|
|
402
|
+
output = insertBeforeLastWhitespace(output, ':');
|
|
403
|
+
} else {
|
|
404
|
+
throwColonExpected();
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
const processedValue = parseValue();
|
|
408
|
+
if (!processedValue) {
|
|
409
|
+
if (processedColon || truncatedText) {
|
|
410
|
+
// repair missing object value
|
|
411
|
+
output += 'null';
|
|
412
|
+
} else {
|
|
413
|
+
throwColonExpected();
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
if (text[i] === '}') {
|
|
418
|
+
output += '}';
|
|
419
|
+
i++;
|
|
420
|
+
} else {
|
|
421
|
+
// repair missing end bracket
|
|
422
|
+
output = insertBeforeLastWhitespace(output, '}');
|
|
423
|
+
}
|
|
424
|
+
return true;
|
|
425
|
+
}
|
|
426
|
+
return false;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Parse an array like '["item1", "item2", ...]'
|
|
431
|
+
*/
|
|
432
|
+
function parseArray() {
|
|
433
|
+
if (text[i] === '[') {
|
|
434
|
+
output += '[';
|
|
435
|
+
i++;
|
|
436
|
+
parseWhitespaceAndSkipComments();
|
|
437
|
+
|
|
438
|
+
// repair: skip leading comma like in [,1,2,3]
|
|
439
|
+
if (skipCharacter(',')) {
|
|
440
|
+
parseWhitespaceAndSkipComments();
|
|
441
|
+
}
|
|
442
|
+
let initial = true;
|
|
443
|
+
while (i < text.length && text[i] !== ']') {
|
|
444
|
+
if (!initial) {
|
|
445
|
+
const processedComma = parseCharacter(',');
|
|
446
|
+
if (!processedComma) {
|
|
447
|
+
// repair missing comma
|
|
448
|
+
output = insertBeforeLastWhitespace(output, ',');
|
|
449
|
+
}
|
|
450
|
+
} else {
|
|
451
|
+
initial = false;
|
|
452
|
+
}
|
|
453
|
+
skipEllipsis();
|
|
454
|
+
const processedValue = parseValue();
|
|
455
|
+
if (!processedValue) {
|
|
456
|
+
// repair trailing comma
|
|
457
|
+
output = stripLastOccurrence(output, ',');
|
|
458
|
+
break;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
if (text[i] === ']') {
|
|
462
|
+
output += ']';
|
|
463
|
+
i++;
|
|
464
|
+
} else {
|
|
465
|
+
// repair missing closing array bracket
|
|
466
|
+
output = insertBeforeLastWhitespace(output, ']');
|
|
467
|
+
}
|
|
468
|
+
return true;
|
|
469
|
+
}
|
|
470
|
+
return false;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Parse and repair Newline Delimited JSON (NDJSON):
|
|
475
|
+
* multiple JSON objects separated by a newline character
|
|
476
|
+
*/
|
|
477
|
+
function parseNewlineDelimitedJSON() {
|
|
478
|
+
// repair NDJSON
|
|
479
|
+
let initial = true;
|
|
480
|
+
let processedValue = true;
|
|
481
|
+
while (processedValue) {
|
|
482
|
+
if (!initial) {
|
|
483
|
+
// parse optional comma, insert when missing
|
|
484
|
+
const processedComma = parseCharacter(',');
|
|
485
|
+
if (!processedComma) {
|
|
486
|
+
// repair: add missing comma
|
|
487
|
+
output = insertBeforeLastWhitespace(output, ',');
|
|
488
|
+
}
|
|
489
|
+
} else {
|
|
490
|
+
initial = false;
|
|
491
|
+
}
|
|
492
|
+
processedValue = parseValue();
|
|
493
|
+
}
|
|
494
|
+
if (!processedValue) {
|
|
495
|
+
// repair: remove trailing comma
|
|
496
|
+
output = stripLastOccurrence(output, ',');
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// repair: wrap the output inside array brackets
|
|
500
|
+
output = `[\n${output}\n]`;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* Parse a string enclosed by double quotes "...". Can contain escaped quotes
|
|
505
|
+
* Repair strings enclosed in single quotes or special quotes
|
|
506
|
+
* Repair an escaped string
|
|
507
|
+
*
|
|
508
|
+
* The function can run in two stages:
|
|
509
|
+
* - First, it assumes the string has a valid end quote
|
|
510
|
+
* - If it turns out that the string does not have a valid end quote followed
|
|
511
|
+
* by a delimiter (which should be the case), the function runs again in a
|
|
512
|
+
* more conservative way, stopping the string at the first next delimiter
|
|
513
|
+
* and fixing the string by inserting a quote there, or stopping at a
|
|
514
|
+
* stop index detected in the first iteration.
|
|
515
|
+
*/
|
|
516
|
+
function parseString() {
|
|
517
|
+
let stopAtDelimiter = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
|
|
518
|
+
let stopAtIndex = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : -1;
|
|
519
|
+
let skipEscapeChars = text[i] === '\\';
|
|
520
|
+
if (skipEscapeChars) {
|
|
521
|
+
// repair: remove the first escape character
|
|
522
|
+
i++;
|
|
523
|
+
skipEscapeChars = true;
|
|
524
|
+
}
|
|
525
|
+
if (isQuote(text[i])) {
|
|
526
|
+
// double quotes are correct JSON,
|
|
527
|
+
// single quotes come from JavaScript for example, we assume it will have a correct single end quote too
|
|
528
|
+
// otherwise, we will match any double-quote-like start with a double-quote-like end,
|
|
529
|
+
// or any single-quote-like start with a single-quote-like end
|
|
530
|
+
const isEndQuote = isDoubleQuote(text[i]) ? isDoubleQuote : isSingleQuote(text[i]) ? isSingleQuote : isSingleQuoteLike(text[i]) ? isSingleQuoteLike : isDoubleQuoteLike;
|
|
531
|
+
const iBefore = i;
|
|
532
|
+
const oBefore = output.length;
|
|
533
|
+
let str = '"';
|
|
534
|
+
i++;
|
|
535
|
+
while (true) {
|
|
536
|
+
if (i >= text.length) {
|
|
537
|
+
// end of text, we are missing an end quote
|
|
538
|
+
|
|
539
|
+
const iPrev = prevNonWhitespaceIndex(i - 1);
|
|
540
|
+
if (!stopAtDelimiter && isDelimiter(text.charAt(iPrev))) {
|
|
541
|
+
// if the text ends with a delimiter, like ["hello],
|
|
542
|
+
// so the missing end quote should be inserted before this delimiter
|
|
543
|
+
// retry parsing the string, stopping at the first next delimiter
|
|
544
|
+
i = iBefore;
|
|
545
|
+
output = output.substring(0, oBefore);
|
|
546
|
+
return parseString(true);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// repair missing quote
|
|
550
|
+
str = insertBeforeLastWhitespace(str, '"');
|
|
551
|
+
output += str;
|
|
552
|
+
return true;
|
|
553
|
+
}
|
|
554
|
+
if (i === stopAtIndex) {
|
|
555
|
+
// use the stop index detected in the first iteration, and repair end quote
|
|
556
|
+
str = insertBeforeLastWhitespace(str, '"');
|
|
557
|
+
output += str;
|
|
558
|
+
return true;
|
|
559
|
+
}
|
|
560
|
+
if (isEndQuote(text[i])) {
|
|
561
|
+
// end quote
|
|
562
|
+
// let us check what is before and after the quote to verify whether this is a legit end quote
|
|
563
|
+
const iQuote = i;
|
|
564
|
+
const oQuote = str.length;
|
|
565
|
+
str += '"';
|
|
566
|
+
i++;
|
|
567
|
+
output += str;
|
|
568
|
+
parseWhitespaceAndSkipComments(false);
|
|
569
|
+
if (stopAtDelimiter || i >= text.length || isDelimiter(text[i]) || isQuote(text[i]) || isDigit(text[i])) {
|
|
570
|
+
// The quote is followed by the end of the text, a delimiter,
|
|
571
|
+
// or a next value. So the quote is indeed the end of the string.
|
|
572
|
+
parseConcatenatedString();
|
|
573
|
+
return true;
|
|
574
|
+
}
|
|
575
|
+
const iPrevChar = prevNonWhitespaceIndex(iQuote - 1);
|
|
576
|
+
const prevChar = text.charAt(iPrevChar);
|
|
577
|
+
if (prevChar === ',') {
|
|
578
|
+
// A comma followed by a quote, like '{"a":"b,c,"d":"e"}'.
|
|
579
|
+
// We assume that the quote is a start quote, and that the end quote
|
|
580
|
+
// should have been located right before the comma but is missing.
|
|
581
|
+
i = iBefore;
|
|
582
|
+
output = output.substring(0, oBefore);
|
|
583
|
+
return parseString(false, iPrevChar);
|
|
584
|
+
}
|
|
585
|
+
if (isDelimiter(prevChar)) {
|
|
586
|
+
// This is not the right end quote: it is preceded by a delimiter,
|
|
587
|
+
// and NOT followed by a delimiter. So, there is an end quote missing
|
|
588
|
+
// parse the string again and then stop at the first next delimiter
|
|
589
|
+
i = iBefore;
|
|
590
|
+
output = output.substring(0, oBefore);
|
|
591
|
+
return parseString(true);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// revert to right after the quote but before any whitespace, and continue parsing the string
|
|
595
|
+
output = output.substring(0, oBefore);
|
|
596
|
+
i = iQuote + 1;
|
|
597
|
+
|
|
598
|
+
// repair unescaped quote
|
|
599
|
+
str = `${str.substring(0, oQuote)}\\${str.substring(oQuote)}`;
|
|
600
|
+
} else if (stopAtDelimiter && isUnquotedStringDelimiter(text[i])) {
|
|
601
|
+
// we're in the mode to stop the string at the first delimiter
|
|
602
|
+
// because there is an end quote missing
|
|
603
|
+
|
|
604
|
+
// test start of an url like "https://..." (this would be parsed as a comment)
|
|
605
|
+
if (text[i - 1] === ':' && regexUrlStart.test(text.substring(iBefore + 1, i + 2))) {
|
|
606
|
+
while (i < text.length && regexUrlChar.test(text[i])) {
|
|
607
|
+
str += text[i];
|
|
608
|
+
i++;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// repair missing quote
|
|
613
|
+
str = insertBeforeLastWhitespace(str, '"');
|
|
614
|
+
output += str;
|
|
615
|
+
parseConcatenatedString();
|
|
616
|
+
return true;
|
|
617
|
+
} else if (text[i] === '\\') {
|
|
618
|
+
// handle escaped content like \n or \u2605
|
|
619
|
+
const char = text.charAt(i + 1);
|
|
620
|
+
const escapeChar = escapeCharacters[char];
|
|
621
|
+
if (escapeChar !== undefined) {
|
|
622
|
+
str += text.slice(i, i + 2);
|
|
623
|
+
i += 2;
|
|
624
|
+
} else if (char === 'u') {
|
|
625
|
+
let j = 2;
|
|
626
|
+
while (j < 6 && isHex(text[i + j])) {
|
|
627
|
+
j++;
|
|
628
|
+
}
|
|
629
|
+
if (j === 6) {
|
|
630
|
+
str += text.slice(i, i + 6);
|
|
631
|
+
i += 6;
|
|
632
|
+
} else if (i + j >= text.length) {
|
|
633
|
+
// repair invalid or truncated unicode char at the end of the text
|
|
634
|
+
// by removing the unicode char and ending the string here
|
|
635
|
+
i = text.length;
|
|
636
|
+
} else {
|
|
637
|
+
throwInvalidUnicodeCharacter();
|
|
638
|
+
}
|
|
639
|
+
} else {
|
|
640
|
+
// repair invalid escape character: remove it
|
|
641
|
+
str += char;
|
|
642
|
+
i += 2;
|
|
643
|
+
}
|
|
644
|
+
} else {
|
|
645
|
+
// handle regular characters
|
|
646
|
+
const char = text.charAt(i);
|
|
647
|
+
if (char === '"' && text[i - 1] !== '\\') {
|
|
648
|
+
// repair unescaped double quote
|
|
649
|
+
str += `\\${char}`;
|
|
650
|
+
i++;
|
|
651
|
+
} else if (isControlCharacter(char)) {
|
|
652
|
+
// unescaped control character
|
|
653
|
+
str += controlCharacters[char];
|
|
654
|
+
i++;
|
|
655
|
+
} else {
|
|
656
|
+
if (!isValidStringCharacter(char)) {
|
|
657
|
+
throwInvalidCharacter(char);
|
|
658
|
+
}
|
|
659
|
+
str += char;
|
|
660
|
+
i++;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
if (skipEscapeChars) {
|
|
664
|
+
// repair: skipped escape character (nothing to do)
|
|
665
|
+
skipEscapeCharacter();
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
return false;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
/**
|
|
673
|
+
* Repair concatenated strings like "hello" + "world", change this into "helloworld"
|
|
674
|
+
*/
|
|
675
|
+
function parseConcatenatedString() {
|
|
676
|
+
let processed = false;
|
|
677
|
+
parseWhitespaceAndSkipComments();
|
|
678
|
+
while (text[i] === '+') {
|
|
679
|
+
processed = true;
|
|
680
|
+
i++;
|
|
681
|
+
parseWhitespaceAndSkipComments();
|
|
682
|
+
|
|
683
|
+
// repair: remove the end quote of the first string
|
|
684
|
+
output = stripLastOccurrence(output, '"', true);
|
|
685
|
+
const start = output.length;
|
|
686
|
+
const parsedStr = parseString();
|
|
687
|
+
if (parsedStr) {
|
|
688
|
+
// repair: remove the start quote of the second string
|
|
689
|
+
output = removeAtIndex(output, start, 1);
|
|
690
|
+
} else {
|
|
691
|
+
// repair: remove the + because it is not followed by a string
|
|
692
|
+
output = insertBeforeLastWhitespace(output, '"');
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
return processed;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
/**
|
|
699
|
+
* Parse a number like 2.4 or 2.4e6
|
|
700
|
+
*/
|
|
701
|
+
function parseNumber() {
|
|
702
|
+
const start = i;
|
|
703
|
+
if (text[i] === '-') {
|
|
704
|
+
i++;
|
|
705
|
+
if (atEndOfNumber()) {
|
|
706
|
+
repairNumberEndingWithNumericSymbol(start);
|
|
707
|
+
return true;
|
|
708
|
+
}
|
|
709
|
+
if (!isDigit(text[i])) {
|
|
710
|
+
i = start;
|
|
711
|
+
return false;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
// Note that in JSON leading zeros like "00789" are not allowed.
|
|
716
|
+
// We will allow all leading zeros here though and at the end of parseNumber
|
|
717
|
+
// check against trailing zeros and repair that if needed.
|
|
718
|
+
// Leading zeros can have meaning, so we should not clear them.
|
|
719
|
+
while (isDigit(text[i])) {
|
|
720
|
+
i++;
|
|
721
|
+
}
|
|
722
|
+
if (text[i] === '.') {
|
|
723
|
+
i++;
|
|
724
|
+
if (atEndOfNumber()) {
|
|
725
|
+
repairNumberEndingWithNumericSymbol(start);
|
|
726
|
+
return true;
|
|
727
|
+
}
|
|
728
|
+
if (!isDigit(text[i])) {
|
|
729
|
+
i = start;
|
|
730
|
+
return false;
|
|
731
|
+
}
|
|
732
|
+
while (isDigit(text[i])) {
|
|
733
|
+
i++;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
if (text[i] === 'e' || text[i] === 'E') {
|
|
737
|
+
i++;
|
|
738
|
+
if (text[i] === '-' || text[i] === '+') {
|
|
739
|
+
i++;
|
|
740
|
+
}
|
|
741
|
+
if (atEndOfNumber()) {
|
|
742
|
+
repairNumberEndingWithNumericSymbol(start);
|
|
743
|
+
return true;
|
|
744
|
+
}
|
|
745
|
+
if (!isDigit(text[i])) {
|
|
746
|
+
i = start;
|
|
747
|
+
return false;
|
|
748
|
+
}
|
|
749
|
+
while (isDigit(text[i])) {
|
|
750
|
+
i++;
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
// if we're not at the end of the number by this point, allow this to be parsed as another type
|
|
755
|
+
if (!atEndOfNumber()) {
|
|
756
|
+
i = start;
|
|
757
|
+
return false;
|
|
758
|
+
}
|
|
759
|
+
if (i > start) {
|
|
760
|
+
// repair a number with leading zeros like "00789"
|
|
761
|
+
const num = text.slice(start, i);
|
|
762
|
+
const hasInvalidLeadingZero = /^0\d/.test(num);
|
|
763
|
+
output += hasInvalidLeadingZero ? `"${num}"` : num;
|
|
764
|
+
return true;
|
|
765
|
+
}
|
|
766
|
+
return false;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
/**
|
|
770
|
+
* Parse keywords true, false, null
|
|
771
|
+
* Repair Python keywords True, False, None
|
|
772
|
+
*/
|
|
773
|
+
function parseKeywords() {
|
|
774
|
+
return parseKeyword('true', 'true') || parseKeyword('false', 'false') || parseKeyword('null', 'null') ||
|
|
775
|
+
// repair Python keywords True, False, None
|
|
776
|
+
parseKeyword('True', 'true') || parseKeyword('False', 'false') || parseKeyword('None', 'null');
|
|
777
|
+
}
|
|
778
|
+
function parseKeyword(name, value) {
|
|
779
|
+
if (text.slice(i, i + name.length) === name) {
|
|
780
|
+
output += value;
|
|
781
|
+
i += name.length;
|
|
782
|
+
return true;
|
|
783
|
+
}
|
|
784
|
+
return false;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
/**
|
|
788
|
+
* Repair an unquoted string by adding quotes around it
|
|
789
|
+
* Repair a MongoDB function call like NumberLong("2")
|
|
790
|
+
* Repair a JSONP function call like callback({...});
|
|
791
|
+
*/
|
|
792
|
+
function parseUnquotedString(isKey) {
|
|
793
|
+
// note that the symbol can end with whitespaces: we stop at the next delimiter
|
|
794
|
+
// also, note that we allow strings to contain a slash / in order to support repairing regular expressions
|
|
795
|
+
const start = i;
|
|
796
|
+
if (isFunctionNameCharStart(text[i])) {
|
|
797
|
+
while (i < text.length && isFunctionNameChar(text[i])) {
|
|
798
|
+
i++;
|
|
799
|
+
}
|
|
800
|
+
let j = i;
|
|
801
|
+
while (isWhitespace(text, j)) {
|
|
802
|
+
j++;
|
|
803
|
+
}
|
|
804
|
+
if (text[j] === '(') {
|
|
805
|
+
// repair a MongoDB function call like NumberLong("2")
|
|
806
|
+
// repair a JSONP function call like callback({...});
|
|
807
|
+
i = j + 1;
|
|
808
|
+
parseValue();
|
|
809
|
+
if (text[i] === ')') {
|
|
810
|
+
// repair: skip close bracket of function call
|
|
811
|
+
i++;
|
|
812
|
+
if (text[i] === ';') {
|
|
813
|
+
// repair: skip semicolon after JSONP call
|
|
814
|
+
i++;
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
return true;
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
while (i < text.length && !isUnquotedStringDelimiter(text[i]) && !isQuote(text[i]) && (!isKey || text[i] !== ':')) {
|
|
821
|
+
i++;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
// test start of an url like "https://..." (this would be parsed as a comment)
|
|
825
|
+
if (text[i - 1] === ':' && regexUrlStart.test(text.substring(start, i + 2))) {
|
|
826
|
+
while (i < text.length && regexUrlChar.test(text[i])) {
|
|
827
|
+
i++;
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
if (i > start) {
|
|
831
|
+
// repair unquoted string
|
|
832
|
+
// also, repair undefined into null
|
|
833
|
+
|
|
834
|
+
// first, go back to prevent getting trailing whitespaces in the string
|
|
835
|
+
while (isWhitespace(text, i - 1) && i > 0) {
|
|
836
|
+
i--;
|
|
837
|
+
}
|
|
838
|
+
const symbol = text.slice(start, i);
|
|
839
|
+
output += symbol === 'undefined' ? 'null' : JSON.stringify(symbol);
|
|
840
|
+
if (text[i] === '"') {
|
|
841
|
+
// we had a missing start quote, but now we encountered the end quote, so we can skip that one
|
|
842
|
+
i++;
|
|
843
|
+
}
|
|
844
|
+
return true;
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
function parseRegex() {
|
|
848
|
+
if (text[i] === '/') {
|
|
849
|
+
const start = i;
|
|
850
|
+
i++;
|
|
851
|
+
while (i < text.length && (text[i] !== '/' || text[i - 1] === '\\')) {
|
|
852
|
+
i++;
|
|
853
|
+
}
|
|
854
|
+
i++;
|
|
855
|
+
output += `"${text.substring(start, i)}"`;
|
|
856
|
+
return true;
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
function prevNonWhitespaceIndex(start) {
|
|
860
|
+
let prev = start;
|
|
861
|
+
while (prev > 0 && isWhitespace(text, prev)) {
|
|
862
|
+
prev--;
|
|
863
|
+
}
|
|
864
|
+
return prev;
|
|
865
|
+
}
|
|
866
|
+
function atEndOfNumber() {
|
|
867
|
+
return i >= text.length || isDelimiter(text[i]) || isWhitespace(text, i);
|
|
868
|
+
}
|
|
869
|
+
function repairNumberEndingWithNumericSymbol(start) {
|
|
870
|
+
// repair numbers cut off at the end
|
|
871
|
+
// this will only be called when we end after a '.', '-', or 'e' and does not
|
|
872
|
+
// change the number more than it needs to make it valid JSON
|
|
873
|
+
output += `${text.slice(start, i)}0`;
|
|
874
|
+
}
|
|
875
|
+
function throwInvalidCharacter(char) {
|
|
876
|
+
throw new JSONRepairError(`Invalid character ${JSON.stringify(char)}`, i);
|
|
877
|
+
}
|
|
878
|
+
function throwUnexpectedCharacter() {
|
|
879
|
+
throw new JSONRepairError(`Unexpected character ${JSON.stringify(text[i])}`, i);
|
|
880
|
+
}
|
|
881
|
+
function throwUnexpectedEnd() {
|
|
882
|
+
throw new JSONRepairError('Unexpected end of json string', text.length);
|
|
883
|
+
}
|
|
884
|
+
function throwObjectKeyExpected() {
|
|
885
|
+
throw new JSONRepairError('Object key expected', i);
|
|
886
|
+
}
|
|
887
|
+
function throwColonExpected() {
|
|
888
|
+
throw new JSONRepairError('Colon expected', i);
|
|
889
|
+
}
|
|
890
|
+
function throwInvalidUnicodeCharacter() {
|
|
891
|
+
const chars = text.slice(i, i + 6);
|
|
892
|
+
throw new JSONRepairError(`Invalid unicode character "${chars}"`, i);
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
function atEndOfBlockComment(text, i) {
|
|
896
|
+
return text[i] === '*' && text[i + 1] === '/';
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
exports.JSONRepairError = JSONRepairError;
|
|
900
|
+
exports.jsonrepair = jsonrepair;
|
|
901
|
+
|
|
902
|
+
}));
|
|
903
|
+
//# sourceMappingURL=jsonrepair.js.map
|