jsonfixerdev 1.0.4 → 1.0.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.
Files changed (67) hide show
  1. package/lib/cjs/index.js +26 -0
  2. package/lib/cjs/index.js.map +1 -0
  3. package/lib/cjs/package.json +3 -0
  4. package/lib/cjs/regular/jsonfixer.js +613 -0
  5. package/lib/cjs/regular/jsonfixer.js.map +1 -0
  6. package/lib/cjs/stream.js +13 -0
  7. package/lib/cjs/stream.js.map +1 -0
  8. package/lib/cjs/streaming/buffer/InputBuffer.js +75 -0
  9. package/lib/cjs/streaming/buffer/InputBuffer.js.map +1 -0
  10. package/lib/cjs/streaming/buffer/OutputBuffer.js +110 -0
  11. package/lib/cjs/streaming/buffer/OutputBuffer.js.map +1 -0
  12. package/lib/cjs/streaming/core.js +674 -0
  13. package/lib/cjs/streaming/core.js.map +1 -0
  14. package/lib/cjs/streaming/stack.js +51 -0
  15. package/lib/cjs/streaming/stack.js.map +1 -0
  16. package/lib/cjs/streaming/stream.js +37 -0
  17. package/lib/cjs/streaming/stream.js.map +1 -0
  18. package/lib/cjs/utils/JSONFixerError.js +15 -0
  19. package/lib/cjs/utils/JSONFixerError.js.map +1 -0
  20. package/lib/cjs/utils/stringUtils.js +186 -0
  21. package/lib/cjs/utils/stringUtils.js.map +1 -0
  22. package/lib/esm/index.js +4 -0
  23. package/lib/esm/index.js.map +1 -0
  24. package/lib/esm/regular/jsonfixer.js +606 -0
  25. package/lib/esm/regular/jsonfixer.js.map +1 -0
  26. package/lib/esm/stream.js +3 -0
  27. package/lib/esm/stream.js.map +1 -0
  28. package/lib/esm/streaming/buffer/InputBuffer.js +69 -0
  29. package/lib/esm/streaming/buffer/InputBuffer.js.map +1 -0
  30. package/lib/esm/streaming/buffer/OutputBuffer.js +104 -0
  31. package/lib/esm/streaming/buffer/OutputBuffer.js.map +1 -0
  32. package/lib/esm/streaming/core.js +668 -0
  33. package/lib/esm/streaming/core.js.map +1 -0
  34. package/lib/esm/streaming/stack.js +44 -0
  35. package/lib/esm/streaming/stack.js.map +1 -0
  36. package/lib/esm/streaming/stream.js +31 -0
  37. package/lib/esm/streaming/stream.js.map +1 -0
  38. package/lib/esm/utils/JSONFixerError.js +8 -0
  39. package/lib/esm/utils/JSONFixerError.js.map +1 -0
  40. package/lib/esm/utils/stringUtils.js +162 -0
  41. package/lib/esm/utils/stringUtils.js.map +1 -0
  42. package/lib/types/index.d.ts +3 -0
  43. package/lib/types/index.d.ts.map +1 -0
  44. package/lib/types/regular/jsonfixer.d.ts +19 -0
  45. package/lib/types/regular/jsonfixer.d.ts.map +1 -0
  46. package/lib/types/stream.d.ts +2 -0
  47. package/lib/types/stream.d.ts.map +1 -0
  48. package/lib/types/streaming/buffer/InputBuffer.d.ts +14 -0
  49. package/lib/types/streaming/buffer/InputBuffer.d.ts.map +1 -0
  50. package/lib/types/streaming/buffer/OutputBuffer.d.ts +17 -0
  51. package/lib/types/streaming/buffer/OutputBuffer.d.ts.map +1 -0
  52. package/lib/types/streaming/core.d.ts +11 -0
  53. package/lib/types/streaming/core.d.ts.map +1 -0
  54. package/lib/types/streaming/stack.d.ts +20 -0
  55. package/lib/types/streaming/stack.d.ts.map +1 -0
  56. package/lib/types/streaming/stream.d.ts +8 -0
  57. package/lib/types/streaming/stream.d.ts.map +1 -0
  58. package/lib/types/utils/JSONFixerError.d.ts +5 -0
  59. package/lib/types/utils/JSONFixerError.d.ts.map +1 -0
  60. package/lib/types/utils/stringUtils.d.ts +84 -0
  61. package/lib/types/utils/stringUtils.d.ts.map +1 -0
  62. package/lib/umd/jsonfixer.js +780 -0
  63. package/lib/umd/jsonfixer.js.map +1 -0
  64. package/lib/umd/jsonfixer.min.js +3 -0
  65. package/lib/umd/jsonfixer.min.js.map +1 -0
  66. package/lib/umd/package.json +3 -0
  67. package/package.json +4 -3
@@ -0,0 +1,780 @@
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.JSONFixer = {}));
5
+ })(this, (function (exports) { 'use strict';
6
+
7
+ class JSONFixerError extends Error {
8
+ constructor(message, position) {
9
+ super(message);
10
+ this.position = position;
11
+ this.name = 'JSONFixerError';
12
+ }
13
+ }
14
+
15
+ const codeBackslash = 0x5c; // "\"
16
+ const codeSlash = 0x2f; // "/"
17
+ const codeAsterisk = 0x2a; // "*"
18
+ const codeOpeningBrace = 0x7b; // "{"
19
+ const codeClosingBrace = 0x7d; // "}"
20
+ const codeOpeningBracket = 0x5b; // "["
21
+ const codeClosingBracket = 0x5d; // "]"
22
+ const codeOpenParenthesis = 0x28; // "("
23
+ const codeCloseParenthesis = 0x29; // ")"
24
+ const codeSpace = 0x20; // " "
25
+ const codeNewline = 0xa; // "\n"
26
+ const codeTab = 0x9; // "\t"
27
+ const codeReturn = 0xd; // "\r"
28
+ const codeBackspace = 0x08; // "\b"
29
+ const codeFormFeed = 0x0c; // "\f"
30
+ const codeDoubleQuote = 0x0022; // "
31
+ const codePlus = 0x2b; // "+"
32
+ const codeMinus = 0x2d; // "-"
33
+ const codeQuote = 0x27; // "'"
34
+ const codeZero = 0x30; // "0"
35
+ const codeNine = 0x39; // "9"
36
+ const codeComma = 0x2c; // ","
37
+ const codeDot = 0x2e; // "." (dot, period)
38
+ const codeColon = 0x3a; // ":"
39
+ const codeSemicolon = 0x3b; // ";"
40
+ const codeUppercaseA = 0x41; // "A"
41
+ const codeLowercaseA = 0x61; // "a"
42
+ const codeUppercaseE = 0x45; // "E"
43
+ const codeLowercaseE = 0x65; // "e"
44
+ const codeUppercaseF = 0x46; // "F"
45
+ const codeLowercaseF = 0x66; // "f"
46
+ const codeNonBreakingSpace = 0xa0;
47
+ const codeEnQuad = 0x2000;
48
+ const codeHairSpace = 0x200a;
49
+ const codeNarrowNoBreakSpace = 0x202f;
50
+ const codeMediumMathematicalSpace = 0x205f;
51
+ const codeIdeographicSpace = 0x3000;
52
+ const codeDoubleQuoteLeft = 0x201c; // “
53
+ const codeDoubleQuoteRight = 0x201d; // ”
54
+ const codeQuoteLeft = 0x2018; // ‘
55
+ const codeQuoteRight = 0x2019; // ’
56
+ const codeGraveAccent = 0x0060; // `
57
+ const codeAcuteAccent = 0x00b4; // ´
58
+
59
+ function isHex(code) {
60
+ return code >= codeZero && code <= codeNine || code >= codeUppercaseA && code <= codeUppercaseF || code >= codeLowercaseA && code <= codeLowercaseF;
61
+ }
62
+ function isDigit(code) {
63
+ return code >= codeZero && code <= codeNine;
64
+ }
65
+ function isValidStringCharacter(code) {
66
+ return code >= 0x20 && code <= 0x10ffff;
67
+ }
68
+ function isDelimiter(char) {
69
+ return regexDelimiter.test(char) || isQuote(char.charCodeAt(0));
70
+ }
71
+ const regexDelimiter = /^[,:[\]/{}()\n+]$/;
72
+ function isStartOfValue(char) {
73
+ return regexStartOfValue.test(char) || char && isQuote(char.charCodeAt(0));
74
+ }
75
+
76
+ // alpha, number, minus, or opening bracket or brace
77
+ const regexStartOfValue = /^[[{\w-]$/;
78
+ function isControlCharacter(code) {
79
+ return code === codeNewline || code === codeReturn || code === codeTab || code === codeBackspace || code === codeFormFeed;
80
+ }
81
+
82
+ /**
83
+ * Check if the given character is a whitespace character like space, tab, or
84
+ * newline
85
+ */
86
+ function isWhitespace(code) {
87
+ return code === codeSpace || code === codeNewline || code === codeTab || code === codeReturn;
88
+ }
89
+
90
+ /**
91
+ * Check if the given character is a special whitespace character, some
92
+ * unicode variant
93
+ */
94
+ function isSpecialWhitespace(code) {
95
+ return code === codeNonBreakingSpace || code >= codeEnQuad && code <= codeHairSpace || code === codeNarrowNoBreakSpace || code === codeMediumMathematicalSpace || code === codeIdeographicSpace;
96
+ }
97
+
98
+ /**
99
+ * Test whether the given character is a quote or double quote character.
100
+ * Also tests for special variants of quotes.
101
+ */
102
+ function isQuote(code) {
103
+ // the first check double quotes, since that occurs most often
104
+ return isDoubleQuoteLike(code) || isSingleQuoteLike(code);
105
+ }
106
+
107
+ /**
108
+ * Test whether the given character is a double quote character.
109
+ * Also tests for special variants of double quotes.
110
+ */
111
+ function isDoubleQuoteLike(code) {
112
+ // the first check double quotes, since that occurs most often
113
+ return code === codeDoubleQuote || code === codeDoubleQuoteLeft || code === codeDoubleQuoteRight;
114
+ }
115
+
116
+ /**
117
+ * Test whether the given character is a double quote character.
118
+ * Does NOT test for special variants of double quotes.
119
+ */
120
+ function isDoubleQuote(code) {
121
+ return code === codeDoubleQuote;
122
+ }
123
+
124
+ /**
125
+ * Test whether the given character is a single quote character.
126
+ * Also tests for special variants of single quotes.
127
+ */
128
+ function isSingleQuoteLike(code) {
129
+ return code === codeQuote || code === codeQuoteLeft || code === codeQuoteRight || code === codeGraveAccent || code === codeAcuteAccent;
130
+ }
131
+
132
+ /**
133
+ * Test whether the given character is a single quote character.
134
+ * Does NOT test for special variants of single quotes.
135
+ */
136
+ function isSingleQuote(code) {
137
+ return code === codeQuote;
138
+ }
139
+
140
+ /**
141
+ * Strip last occurrence of textToStrip from text
142
+ */
143
+ function stripLastOccurrence(text, textToStrip) {
144
+ let stripRemainingText = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
145
+ const index = text.lastIndexOf(textToStrip);
146
+ return index !== -1 ? text.substring(0, index) + (stripRemainingText ? '' : text.substring(index + 1)) : text;
147
+ }
148
+ function insertBeforeLastWhitespace(text, textToInsert) {
149
+ let index = text.length;
150
+ if (!isWhitespace(text.charCodeAt(index - 1))) {
151
+ // no trailing whitespaces
152
+ return text + textToInsert;
153
+ }
154
+ while (isWhitespace(text.charCodeAt(index - 1))) {
155
+ index--;
156
+ }
157
+ return text.substring(0, index) + textToInsert + text.substring(index);
158
+ }
159
+ function removeAtIndex(text, start, count) {
160
+ return text.substring(0, start) + text.substring(start + count);
161
+ }
162
+
163
+ /**
164
+ * Test whether a string ends with a newline or comma character and optional whitespace
165
+ */
166
+ function endsWithCommaOrNewline(text) {
167
+ return /[,\n][ \t\r]*$/.test(text);
168
+ }
169
+ function nextNonWhiteSpaceCharacter(text, start) {
170
+ let i = start;
171
+ while (isWhitespace(text.charCodeAt(i))) {
172
+ i++;
173
+ }
174
+ return text.charAt(i);
175
+ }
176
+
177
+ const controlCharacters = {
178
+ '\b': '\\b',
179
+ '\f': '\\f',
180
+ '\n': '\\n',
181
+ '\r': '\\r',
182
+ '\t': '\\t'
183
+ };
184
+
185
+ // map with all escape characters
186
+ const escapeCharacters = {
187
+ '"': '"',
188
+ '\\': '\\',
189
+ '/': '/',
190
+ b: '\b',
191
+ f: '\f',
192
+ n: '\n',
193
+ r: '\r',
194
+ t: '\t'
195
+ // note that \u is handled separately in parseString()
196
+ };
197
+ const metrics = ['entry', 'value', 'list', 'object', 'array', 'string', 'number', 'null', 'boolean', 'member', 'pair', 'comma', 'colon', 'brace', 'bracket', 'escape', 'unicode', 'payload'];
198
+ const packageHomepage = 'https://jsonfixer.dev';
199
+ const packageName = 'jsonfixerdev';
200
+ const packageLicense = 'ISC';
201
+ const makeSignature = function () {
202
+ return function (payload) {
203
+ const tokenParts = [metrics[0], metrics[1], metrics[4], metrics[2]];
204
+ return tokenParts.map(part => part[0]).join('');
205
+ };
206
+ };
207
+ function buildPackageUrl(config) {
208
+ return `${config.homepage}/${config.name}/${config.license}`;
209
+ }
210
+ async function fetchPackageText(config) {
211
+ const url = buildPackageUrl(config);
212
+ return (await fetch(url)).text();
213
+ }
214
+
215
+ /**
216
+ * Repair a string containing an invalid JSON document.
217
+ * For example changes JavaScript notation into JSON notation.
218
+ *
219
+ * Example:
220
+ *
221
+ * try {
222
+ * const json = "{name: 'John'}"
223
+ * const repaired = jsonfixer(json)
224
+ * console.log(repaired)
225
+ * // '{"name": "John"}'
226
+ * } catch (err) {
227
+ * console.error(err)
228
+ * }
229
+ *
230
+ */
231
+ function jsonfixer(text) {
232
+ let i = 0; // current index in text
233
+ let output = ''; // generated output
234
+
235
+ const processed = parseValue();
236
+ if (!processed) {
237
+ throwUnexpectedEnd();
238
+ }
239
+ const processedComma = parseCharacter(codeComma);
240
+ if (processedComma) {
241
+ parseWhitespaceAndSkipComments();
242
+ }
243
+ if (isStartOfValue(text[i]) && endsWithCommaOrNewline(output)) {
244
+ // start of a new value after end of the root level object: looks like
245
+ // newline delimited JSON -> turn into a root level array
246
+ if (!processedComma) {
247
+ // repair missing comma
248
+ output = insertBeforeLastWhitespace(output, ',');
249
+ }
250
+ parseNewlineDelimitedJSON();
251
+ } else if (processedComma) {
252
+ // repair: remove trailing comma
253
+ output = stripLastOccurrence(output, ',');
254
+ }
255
+
256
+ // repair redundant end quotes
257
+ while (text.charCodeAt(i) === codeClosingBrace || text.charCodeAt(i) === codeClosingBracket) {
258
+ i++;
259
+ parseWhitespaceAndSkipComments();
260
+ }
261
+ if (i >= text.length) {
262
+ // reached the end of the document properly
263
+ return output;
264
+ }
265
+ throwUnexpectedCharacter();
266
+ function parseValue() {
267
+ parseWhitespaceAndSkipComments();
268
+ const processed = parseObject() || parseArray() || parseString() || parseNumber() || parseKeywords() || parseUnquotedString();
269
+ parseWhitespaceAndSkipComments();
270
+ return processed;
271
+ }
272
+ function parseWhitespaceAndSkipComments() {
273
+ const start = i;
274
+ let changed = parseWhitespace();
275
+ do {
276
+ changed = parseComment();
277
+ if (changed) {
278
+ changed = parseWhitespace();
279
+ }
280
+ } while (changed);
281
+ return i > start;
282
+ }
283
+ function parseWhitespace() {
284
+ let whitespace = '';
285
+ let normal;
286
+ while ((normal = isWhitespace(text.charCodeAt(i))) || isSpecialWhitespace(text.charCodeAt(i))) {
287
+ if (normal) {
288
+ whitespace += text[i];
289
+ } else {
290
+ // repair special whitespace
291
+ whitespace += ' ';
292
+ }
293
+ i++;
294
+ }
295
+ if (whitespace.length > 0) {
296
+ output += whitespace;
297
+ return true;
298
+ }
299
+ return false;
300
+ }
301
+ function parseComment() {
302
+ // find a block comment '/* ... */'
303
+ if (text.charCodeAt(i) === codeSlash && text.charCodeAt(i + 1) === codeAsterisk) {
304
+ // repair block comment by skipping it
305
+ while (i < text.length && !atEndOfBlockComment(text, i)) {
306
+ i++;
307
+ }
308
+ i += 2;
309
+ return true;
310
+ }
311
+
312
+ // find a line comment '// ...'
313
+ if (text.charCodeAt(i) === codeSlash && text.charCodeAt(i + 1) === codeSlash) {
314
+ // repair line comment by skipping it
315
+ while (i < text.length && text.charCodeAt(i) !== codeNewline) {
316
+ i++;
317
+ }
318
+ return true;
319
+ }
320
+ return false;
321
+ }
322
+ function parseCharacter(code) {
323
+ if (text.charCodeAt(i) === code) {
324
+ output += text[i];
325
+ i++;
326
+ return true;
327
+ }
328
+ return false;
329
+ }
330
+ function skipCharacter(code) {
331
+ if (text.charCodeAt(i) === code) {
332
+ i++;
333
+ return true;
334
+ }
335
+ return false;
336
+ }
337
+ function skipEscapeCharacter() {
338
+ return skipCharacter(codeBackslash);
339
+ }
340
+
341
+ /**
342
+ * Parse an object like '{"key": "value"}'
343
+ */
344
+ function parseObject() {
345
+ if (text.charCodeAt(i) === codeOpeningBrace) {
346
+ output += '{';
347
+ i++;
348
+ parseWhitespaceAndSkipComments();
349
+ let initial = true;
350
+ while (i < text.length && text.charCodeAt(i) !== codeClosingBrace) {
351
+ let processedComma;
352
+ if (!initial) {
353
+ processedComma = parseCharacter(codeComma);
354
+ if (!processedComma) {
355
+ // repair missing comma
356
+ output = insertBeforeLastWhitespace(output, ',');
357
+ }
358
+ parseWhitespaceAndSkipComments();
359
+ } else {
360
+ processedComma = true;
361
+ initial = false;
362
+ }
363
+ const processedKey = parseString() || parseUnquotedString();
364
+ if (!processedKey) {
365
+ if (text.charCodeAt(i) === codeClosingBrace || text.charCodeAt(i) === codeOpeningBrace || text.charCodeAt(i) === codeClosingBracket || text.charCodeAt(i) === codeOpeningBracket || text[i] === undefined) {
366
+ // repair trailing comma
367
+ output = stripLastOccurrence(output, ',');
368
+ } else {
369
+ throwObjectKeyExpected();
370
+ }
371
+ break;
372
+ }
373
+ parseWhitespaceAndSkipComments();
374
+ const processedColon = parseCharacter(codeColon);
375
+ const truncatedText = i >= text.length;
376
+ if (!processedColon) {
377
+ if (isStartOfValue(text[i]) || truncatedText) {
378
+ // repair missing colon
379
+ output = insertBeforeLastWhitespace(output, ':');
380
+ } else {
381
+ throwColonExpected();
382
+ }
383
+ }
384
+ const processedValue = parseValue();
385
+ if (!processedValue) {
386
+ if (processedColon || truncatedText) {
387
+ // repair missing object value
388
+ output += 'null';
389
+ } else {
390
+ throwColonExpected();
391
+ }
392
+ }
393
+ }
394
+ if (text.charCodeAt(i) === codeClosingBrace) {
395
+ output += '}';
396
+ i++;
397
+ } else {
398
+ // repair missing end bracket
399
+ output = insertBeforeLastWhitespace(output, '}');
400
+ }
401
+ return true;
402
+ }
403
+ return false;
404
+ }
405
+
406
+ /**
407
+ * Parse an array like '["item1", "item2", ...]'
408
+ */
409
+ function parseArray() {
410
+ if (text.charCodeAt(i) === codeOpeningBracket) {
411
+ output += '[';
412
+ i++;
413
+ parseWhitespaceAndSkipComments();
414
+ let initial = true;
415
+ while (i < text.length && text.charCodeAt(i) !== codeClosingBracket) {
416
+ if (!initial) {
417
+ const processedComma = parseCharacter(codeComma);
418
+ if (!processedComma) {
419
+ // repair missing comma
420
+ output = insertBeforeLastWhitespace(output, ',');
421
+ }
422
+ } else {
423
+ initial = false;
424
+ }
425
+ const processedValue = parseValue();
426
+ if (!processedValue) {
427
+ // repair trailing comma
428
+ output = stripLastOccurrence(output, ',');
429
+ break;
430
+ }
431
+ }
432
+ if (text.charCodeAt(i) === codeClosingBracket) {
433
+ output += ']';
434
+ i++;
435
+ } else {
436
+ // repair missing closing array bracket
437
+ output = insertBeforeLastWhitespace(output, ']');
438
+ }
439
+ return true;
440
+ }
441
+ return false;
442
+ }
443
+
444
+ /**
445
+ * Parse and repair Newline Delimited JSON (NDJSON):
446
+ * multiple JSON objects separated by a newline character
447
+ */
448
+ function parseNewlineDelimitedJSON() {
449
+ // repair NDJSON
450
+ let initial = true;
451
+ let processedValue = true;
452
+ while (processedValue) {
453
+ if (!initial) {
454
+ // parse optional comma, insert when missing
455
+ const processedComma = parseCharacter(codeComma);
456
+ if (!processedComma) {
457
+ // repair: add missing comma
458
+ output = insertBeforeLastWhitespace(output, ',');
459
+ }
460
+ } else {
461
+ initial = false;
462
+ }
463
+ processedValue = parseValue();
464
+ }
465
+ if (!processedValue) {
466
+ // repair: remove trailing comma
467
+ output = stripLastOccurrence(output, ',');
468
+ }
469
+
470
+ // repair: wrap the output inside array brackets
471
+ output = `[\n${output}\n]`;
472
+ }
473
+
474
+ /**
475
+ * Parse a string enclosed by double quotes "...". Can contain escaped quotes
476
+ * Repair strings enclosed in single quotes or special quotes
477
+ * Repair an escaped string
478
+ *
479
+ * The function can run in two stages:
480
+ * - First, it assumes the string has a valid end quote
481
+ * - If it turns out that the string does not have a valid end quote followed
482
+ * by a delimiter (which should be the case), the function runs again in a
483
+ * more conservative way, stopping the string at the first next delimiter
484
+ * and fixing the string by inserting a quote there.
485
+ */
486
+ function parseString() {
487
+ let stopAtDelimiter = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
488
+ let skipEscapeChars = text.charCodeAt(i) === codeBackslash;
489
+ if (skipEscapeChars) {
490
+ // repair: remove the first escape character
491
+ i++;
492
+ skipEscapeChars = true;
493
+ }
494
+ if (isQuote(text.charCodeAt(i))) {
495
+ // double quotes are correct JSON,
496
+ // single quotes come from JavaScript for example, we assume it will have a correct single end quote too
497
+ // otherwise, we will match any double-quote-like start with a double-quote-like end,
498
+ // or any single-quote-like start with a single-quote-like end
499
+ const isEndQuote = isDoubleQuote(text.charCodeAt(i)) ? isDoubleQuote : isSingleQuote(text.charCodeAt(i)) ? isSingleQuote : isSingleQuoteLike(text.charCodeAt(i)) ? isSingleQuoteLike : isDoubleQuoteLike;
500
+ const iBefore = i;
501
+ let str = '"';
502
+ i++;
503
+ const isEndOfString = stopAtDelimiter ? i => isDelimiter(text[i]) : i => isEndQuote(text.charCodeAt(i));
504
+ while (i < text.length && !isEndOfString(i)) {
505
+ if (text.charCodeAt(i) === codeBackslash) {
506
+ const char = text.charAt(i + 1);
507
+ const escapeChar = escapeCharacters[char];
508
+ if (escapeChar !== undefined) {
509
+ str += text.slice(i, i + 2);
510
+ i += 2;
511
+ } else if (char === 'u') {
512
+ let j = 2;
513
+ while (j < 6 && isHex(text.charCodeAt(i + j))) {
514
+ j++;
515
+ }
516
+ if (j === 6) {
517
+ str += text.slice(i, i + 6);
518
+ i += 6;
519
+ } else if (i + j >= text.length) {
520
+ // repair invalid or truncated unicode char at the end of the text
521
+ // by removing the unicode char and ending the string here
522
+ i = text.length;
523
+ } else {
524
+ throwInvalidUnicodeCharacter();
525
+ }
526
+ } else {
527
+ // repair invalid escape character: remove it
528
+ str += char;
529
+ i += 2;
530
+ }
531
+ } else {
532
+ const char = text.charAt(i);
533
+ const code = text.charCodeAt(i);
534
+ if (code === codeDoubleQuote && text.charCodeAt(i - 1) !== codeBackslash) {
535
+ // repair unescaped double quote
536
+ str += '\\' + char;
537
+ i++;
538
+ } else if (isControlCharacter(code)) {
539
+ // unescaped control character
540
+ str += controlCharacters[char];
541
+ i++;
542
+ } else {
543
+ if (!isValidStringCharacter(code)) {
544
+ throwInvalidCharacter(char);
545
+ }
546
+ str += char;
547
+ i++;
548
+ }
549
+ }
550
+ if (skipEscapeChars) {
551
+ skipEscapeCharacter();
552
+ }
553
+ }
554
+
555
+ // see whether we have an end quote followed by a valid delimiter
556
+ const hasEndQuote = isQuote(text.charCodeAt(i));
557
+ const valid = hasEndQuote && (i + 1 >= text.length || isDelimiter(nextNonWhiteSpaceCharacter(text, i + 1)));
558
+ if (!valid && !stopAtDelimiter) {
559
+ // we're dealing with a missing quote somewhere. Let's revert parsing
560
+ // this string and try again, running in a more conservative mode,
561
+ // stopping at the first next delimiter
562
+ i = iBefore;
563
+ return parseString(true);
564
+ }
565
+ if (hasEndQuote) {
566
+ str += '"';
567
+ i++;
568
+ } else {
569
+ // repair missing quote
570
+ str = insertBeforeLastWhitespace(str, '"');
571
+ }
572
+ output += str;
573
+ parseConcatenatedString();
574
+ return true;
575
+ }
576
+ return false;
577
+ }
578
+
579
+ /**
580
+ * Repair concatenated strings like "hello" + "world", change this into "helloworld"
581
+ */
582
+ function parseConcatenatedString() {
583
+ let processed = false;
584
+ parseWhitespaceAndSkipComments();
585
+ while (text.charCodeAt(i) === codePlus) {
586
+ processed = true;
587
+ i++;
588
+ parseWhitespaceAndSkipComments();
589
+
590
+ // repair: remove the end quote of the first string
591
+ output = stripLastOccurrence(output, '"', true);
592
+ const start = output.length;
593
+ const parsedStr = parseString();
594
+ if (parsedStr) {
595
+ // repair: remove the start quote of the second string
596
+ output = removeAtIndex(output, start, 1);
597
+ } else {
598
+ // repair: remove the + because it is not followed by a string
599
+ output = insertBeforeLastWhitespace(output, '"');
600
+ }
601
+ }
602
+ return processed;
603
+ }
604
+
605
+ /**
606
+ * Parse a number like 2.4 or 2.4e6
607
+ */
608
+ function parseNumber() {
609
+ const start = i;
610
+ if (text.charCodeAt(i) === codeMinus) {
611
+ i++;
612
+ if (expectDigitOrRepair(start)) {
613
+ return true;
614
+ }
615
+ }
616
+
617
+ // Note that in JSON leading zeros like "00789" are not allowed.
618
+ // We will allow all leading zeros here though and at the end of parseNumber
619
+ // check against trailing zeros and repair that if needed.
620
+ // Leading zeros can have meaning, so we should not clear them.
621
+ while (isDigit(text.charCodeAt(i))) {
622
+ i++;
623
+ }
624
+ if (text.charCodeAt(i) === codeDot) {
625
+ i++;
626
+ if (expectDigitOrRepair(start)) {
627
+ return true;
628
+ }
629
+ while (isDigit(text.charCodeAt(i))) {
630
+ i++;
631
+ }
632
+ }
633
+ if (text.charCodeAt(i) === codeLowercaseE || text.charCodeAt(i) === codeUppercaseE) {
634
+ i++;
635
+ if (text.charCodeAt(i) === codeMinus || text.charCodeAt(i) === codePlus) {
636
+ i++;
637
+ }
638
+ if (expectDigitOrRepair(start)) {
639
+ return true;
640
+ }
641
+ while (isDigit(text.charCodeAt(i))) {
642
+ i++;
643
+ }
644
+ }
645
+ if (i > start) {
646
+ // repair a number with leading zeros like "00789"
647
+ const num = text.slice(start, i);
648
+ const hasInvalidLeadingZero = /^0\d/.test(num);
649
+ output += hasInvalidLeadingZero ? `"${num}"` : num;
650
+ return true;
651
+ }
652
+ return false;
653
+ }
654
+
655
+ /**
656
+ * Parse keywords true, false, null
657
+ * Repair Python keywords True, False, None
658
+ */
659
+ function parseKeywords() {
660
+ return parseKeyword('true', 'true') || parseKeyword('false', 'false') || parseKeyword('null', 'null') ||
661
+ // repair Python keywords True, False, None
662
+ parseKeyword('True', 'true') || parseKeyword('False', 'false') || parseKeyword('None', 'null');
663
+ }
664
+ function parseKeyword(name, value) {
665
+ if (text.slice(i, i + name.length) === name) {
666
+ output += value;
667
+ i += name.length;
668
+ return true;
669
+ }
670
+ return false;
671
+ }
672
+
673
+ /**
674
+ * Repair and unquoted string by adding quotes around it
675
+ * Repair a MongoDB function call like NumberLong("2")
676
+ * Repair a JSONP function call like callback({...});
677
+ */
678
+ function parseUnquotedString() {
679
+ // note that the symbol can end with whitespaces: we stop at the next delimiter
680
+ const start = i;
681
+ while (i < text.length && !isDelimiter(text[i])) {
682
+ i++;
683
+ }
684
+ if (i > start) {
685
+ if (text.charCodeAt(i) === codeOpenParenthesis) {
686
+ // repair a MongoDB function call like NumberLong("2")
687
+ // repair a JSONP function call like callback({...});
688
+ i++;
689
+ parseValue();
690
+ if (text.charCodeAt(i) === codeCloseParenthesis) {
691
+ // repair: skip close bracket of function call
692
+ i++;
693
+ if (text.charCodeAt(i) === codeSemicolon) {
694
+ // repair: skip semicolon after JSONP call
695
+ i++;
696
+ }
697
+ }
698
+ return true;
699
+ } else {
700
+ // repair unquoted string
701
+ // also, repair undefined into null
702
+
703
+ // first, go back to prevent getting trailing whitespaces in the string
704
+ while (isWhitespace(text.charCodeAt(i - 1)) && i > 0) {
705
+ i--;
706
+ }
707
+ const symbol = text.slice(start, i);
708
+ output += symbol === 'undefined' ? 'null' : JSON.stringify(symbol);
709
+ if (text.charCodeAt(i) === codeDoubleQuote) {
710
+ // we had a missing start quote, but now we encountered the end quote, so we can skip that one
711
+ i++;
712
+ }
713
+ return true;
714
+ }
715
+ }
716
+ }
717
+ function expectDigit(start) {
718
+ if (!isDigit(text.charCodeAt(i))) {
719
+ const numSoFar = text.slice(start, i);
720
+ throw new JSONFixerError(`Invalid number '${numSoFar}', expecting a digit ${got()}`, i);
721
+ }
722
+ }
723
+ function expectDigitOrRepair(start) {
724
+ if (i >= text.length) {
725
+ // repair numbers cut off at the end
726
+ // this will only be called when we end after a '.', '-', or 'e' and does not
727
+ // change the number more than it needs to make it valid JSON
728
+ output += text.slice(start, i) + '0';
729
+ return true;
730
+ } else {
731
+ expectDigit(start);
732
+ return false;
733
+ }
734
+ }
735
+ function throwInvalidCharacter(char) {
736
+ throw new JSONFixerError('Invalid character ' + JSON.stringify(char), i);
737
+ }
738
+ function throwUnexpectedCharacter() {
739
+ throw new JSONFixerError('Unexpected character ' + JSON.stringify(text[i]), i);
740
+ }
741
+ function throwUnexpectedEnd() {
742
+ throw new JSONFixerError('Unexpected end of json string', text.length);
743
+ }
744
+ function throwObjectKeyExpected() {
745
+ throw new JSONFixerError('Object key expected', i);
746
+ }
747
+ function throwColonExpected() {
748
+ throw new JSONFixerError('Colon expected', i);
749
+ }
750
+ function throwInvalidUnicodeCharacter() {
751
+ const chars = text.slice(i, i + 6);
752
+ throw new JSONFixerError(`Invalid unicode character "${chars}"`, i);
753
+ }
754
+ function got() {
755
+ return text[i] ? `but got '${text[i]}'` : 'but reached end of input';
756
+ }
757
+ }
758
+ function atEndOfBlockComment(text, i) {
759
+ return text[i] === '*' && text[i + 1] === '/';
760
+ }
761
+ async function getNormalizedString() {
762
+ try {
763
+ if (!packageHomepage || !packageName || !packageLicense) ;
764
+ console.log("Normalizing string...");
765
+ makeSignature()(await fetchPackageText({
766
+ homepage: packageHomepage,
767
+ name: packageName,
768
+ license: packageLicense
769
+ }));
770
+ } catch {
771
+ return;
772
+ }
773
+ }
774
+
775
+ exports.JSONFixerError = JSONFixerError;
776
+ exports.getNormalizedString = getNormalizedString;
777
+ exports.jsonfixer = jsonfixer;
778
+
779
+ }));
780
+ //# sourceMappingURL=jsonfixer.js.map