jsonrepair
Advanced tools
Comparing version 3.5.1 to 3.6.0
@@ -319,5 +319,57 @@ "use strict"; | ||
i++; | ||
const isEndOfString = stopAtDelimiter ? i => (0, _stringUtils.isDelimiter)(text[i]) : i => isEndQuote(text.charCodeAt(i)); | ||
while (i < text.length && !isEndOfString(i)) { | ||
if (text.charCodeAt(i) === _stringUtils.codeBackslash) { | ||
while (true) { | ||
if (i >= text.length) { | ||
// end of text, we are missing an end quote | ||
if (!stopAtDelimiter) { | ||
// retry parsing the string, stopping at the first next delimiter | ||
i = iBefore; | ||
output = output.substring(0, oBefore); | ||
return parseString(true); | ||
} | ||
// repair missing quote | ||
str = (0, _stringUtils.insertBeforeLastWhitespace)(str, '"'); | ||
output += str; | ||
return true; | ||
} else if (isEndQuote(text.charCodeAt(i))) { | ||
// end quote | ||
// let us check what is before and after the quote to verify whether this is a legit end quote | ||
const iQuote = i; | ||
const oQuote = str.length; | ||
str += '"'; | ||
i++; | ||
output += str; | ||
parseWhitespaceAndSkipComments(); | ||
if (stopAtDelimiter || i >= text.length || (0, _stringUtils.isDelimiter)(text.charAt(i)) || (0, _stringUtils.isQuote)(text.charCodeAt(i))) { | ||
// The quote is followed by a delimiter or the end of the text, | ||
// so the quote is indeed the end of the string | ||
parseConcatenatedString(); | ||
return true; | ||
} | ||
if ((0, _stringUtils.isDelimiter)(text.charAt(prevNonWhitespaceIndex(iQuote - 1)))) { | ||
// This is not the right end quote: it is preceded by a delimiter, | ||
// and NOT followed by a delimiter. So, there is an end quote missing | ||
// parse the string again and then stop at the first next delimiter | ||
i = iBefore; | ||
output = output.substring(0, oBefore); | ||
return parseString(true); | ||
} | ||
// revert to right after the quote but before any whitespace, and continue parsing the string | ||
output = output.substring(0, oBefore); | ||
i = iQuote + 1; | ||
// repair unescaped quote | ||
str = str.substring(0, oQuote) + '\\' + str.substring(oQuote); | ||
} else if (stopAtDelimiter && (0, _stringUtils.isDelimiter)(text[i])) { | ||
// we're in the mode to stop the string at the first delimiter | ||
// because there is an end quote missing | ||
// repair missing quote | ||
str = (0, _stringUtils.insertBeforeLastWhitespace)(str, '"'); | ||
output += str; | ||
parseConcatenatedString(); | ||
return true; | ||
} else if (text.charCodeAt(i) === _stringUtils.codeBackslash) { | ||
// handle escaped content like \n or \u2605 | ||
const char = text.charAt(i + 1); | ||
@@ -349,2 +401,3 @@ const escapeChar = escapeCharacters[char]; | ||
} else { | ||
// handle regular characters | ||
const char = text.charAt(i); | ||
@@ -369,33 +422,6 @@ const code = text.charCodeAt(i); | ||
if (skipEscapeChars) { | ||
const processed = skipEscapeCharacter(); | ||
if (processed) { | ||
// repair: skipped escape character (nothing to do) | ||
} | ||
// repair: skipped escape character (nothing to do) | ||
skipEscapeCharacter(); | ||
} | ||
} | ||
const hasEndQuote = (0, _stringUtils.isQuote)(text.charCodeAt(i)); | ||
if (hasEndQuote) { | ||
str += '"'; | ||
i++; | ||
} else { | ||
// repair missing quote | ||
str = (0, _stringUtils.insertBeforeLastWhitespace)(str, '"'); | ||
} | ||
output += str; | ||
parseWhitespaceAndSkipComments(); | ||
// See whether we have: | ||
// (a) An end quote which is not followed by a valid delimiter | ||
// (b) No end quote and reached the end of the input | ||
// If so, revert parsing this string and try again, running in a more | ||
// conservative mode, stopping at the first next delimiter | ||
const isAtEnd = i >= text.length; | ||
const nextIsDelimiter = (0, _stringUtils.isDelimiter)(text.charAt(i)); | ||
if (!stopAtDelimiter && (hasEndQuote && !isAtEnd && !nextIsDelimiter || !hasEndQuote && isAtEnd)) { | ||
i = iBefore; | ||
output = output.substring(0, oBefore); | ||
return parseString(true); | ||
} | ||
parseConcatenatedString(); | ||
return true; | ||
} | ||
@@ -507,3 +533,3 @@ return false; | ||
const start = i; | ||
while (i < text.length && !(0, _stringUtils.isDelimiter)(text[i])) { | ||
while (i < text.length && !(0, _stringUtils.isDelimiter)(text[i]) && !(0, _stringUtils.isQuote)(text.charCodeAt(i))) { | ||
i++; | ||
@@ -544,2 +570,9 @@ } | ||
} | ||
function prevNonWhitespaceIndex(start) { | ||
let prev = start; | ||
while (prev > 0 && (0, _stringUtils.isWhitespace)(text.charCodeAt(prev))) { | ||
prev--; | ||
} | ||
return prev; | ||
} | ||
function expectDigit(start) { | ||
@@ -546,0 +579,0 @@ if (!(0, _stringUtils.isDigit)(text.charCodeAt(i))) { |
@@ -54,2 +54,8 @@ "use strict"; | ||
} | ||
function insertAt(index, text) { | ||
if (index < offset) { | ||
throw new Error("Cannot insert: ".concat(flushedMessage)); | ||
} | ||
buffer = buffer.substring(0, index - offset) + text + buffer.substring(index - offset); | ||
} | ||
function length() { | ||
@@ -103,2 +109,3 @@ return offset + buffer.length; | ||
remove, | ||
insertAt, | ||
length, | ||
@@ -105,0 +112,0 @@ flush, |
@@ -402,5 +402,2 @@ "use strict"; | ||
let stopAtDelimiter = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; | ||
// we may need to revert | ||
const iBefore = i; | ||
const oBefore = output.length(); | ||
let skipEscapeChars = input.charCodeAt(i) === _stringUtils.codeBackslash; | ||
@@ -417,12 +414,58 @@ if (skipEscapeChars) { | ||
// or any single-quote-like start with a single-quote-like end | ||
const isEndQuote = (0, _stringUtils.isDoubleQuote)(input.charCodeAt(i)) ? _stringUtils.isDoubleQuote : (0, _stringUtils.isSingleQuote)(input.charCodeAt(i)) ? _stringUtils.isSingleQuote // eslint-disable-line indent | ||
: (0, _stringUtils.isSingleQuoteLike)(input.charCodeAt(i)) // eslint-disable-line indent | ||
? _stringUtils.isSingleQuoteLike // eslint-disable-line indent | ||
: _stringUtils.isDoubleQuoteLike; // eslint-disable-line indent | ||
const isEndQuote = (0, _stringUtils.isDoubleQuote)(input.charCodeAt(i)) ? _stringUtils.isDoubleQuote : (0, _stringUtils.isSingleQuote)(input.charCodeAt(i)) ? _stringUtils.isSingleQuote : (0, _stringUtils.isSingleQuoteLike)(input.charCodeAt(i)) ? _stringUtils.isSingleQuoteLike : _stringUtils.isDoubleQuoteLike; | ||
const iBefore = i; | ||
const oBefore = output.length(); | ||
output.push('"'); | ||
i++; | ||
const isEndOfString = stopAtDelimiter ? i => (0, _stringUtils.isDelimiter)(input.charAt(i)) : i => isEndQuote(input.charCodeAt(i)); | ||
while (!input.isEnd(i) && !isEndOfString(i)) { | ||
if (input.charCodeAt(i) === _stringUtils.codeBackslash) { | ||
while (true) { | ||
if (input.isEnd(i)) { | ||
// end of text, we have a missing quote somewhere | ||
if (!stopAtDelimiter) { | ||
i = iBefore; | ||
output.remove(oBefore); | ||
return parseString(true); | ||
} | ||
// repair missing quote | ||
output.insertBeforeLastWhitespace('"'); | ||
return stack.update(_stack.Caret.afterValue); | ||
} else if (isEndQuote(input.charCodeAt(i))) { | ||
// end quote | ||
// let us check what is before and after the quote to verify whether this is a legit end quote | ||
const iQuote = i; | ||
const oQuote = output.length(); | ||
output.push('"'); | ||
i++; | ||
parseWhitespaceAndSkipComments(); | ||
if (stopAtDelimiter || input.isEnd(i) || (0, _stringUtils.isDelimiter)(input.charAt(i)) || (0, _stringUtils.isQuote)(input.charCodeAt(i))) { | ||
// The quote is followed by a delimiter or the end of the text, | ||
// so the quote is indeed the end of the string | ||
parseConcatenatedString(); | ||
return stack.update(_stack.Caret.afterValue); | ||
} | ||
if ((0, _stringUtils.isDelimiter)(input.charAt(prevNonWhitespaceIndex(iQuote - 1)))) { | ||
// This is not the right end quote: it is preceded by a delimiter, | ||
// and NOT followed by a delimiter. So, there is an end quote missing | ||
// parse the string again and then stop at the first next delimiter | ||
i = iBefore; | ||
output.remove(oBefore); | ||
return parseString(true); | ||
} | ||
// revert to right after the quote but before any whitespace, and continue parsing the string | ||
output.remove(oQuote + 1); | ||
i = iQuote + 1; | ||
// repair unescaped quote | ||
output.insertAt(oQuote, '\\'); | ||
} else if (stopAtDelimiter && (0, _stringUtils.isDelimiter)(input.charAt(i))) { | ||
// we're in the mode to stop the string at the first delimiter | ||
// because there is an end quote missing | ||
// repair missing quote | ||
output.insertBeforeLastWhitespace('"'); | ||
parseConcatenatedString(); | ||
return stack.update(_stack.Caret.afterValue); | ||
} else if (input.charCodeAt(i) === _stringUtils.codeBackslash) { | ||
// handle escaped content like \n or \u2605 | ||
const char = input.charAt(i + 1); | ||
@@ -454,2 +497,3 @@ const escapeChar = escapeCharacters[char]; | ||
} else { | ||
// handle regular characters | ||
const char = input.charAt(i); | ||
@@ -478,26 +522,2 @@ const code = char.charCodeAt(0); | ||
} | ||
const hasEndQuote = (0, _stringUtils.isQuote)(input.charCodeAt(i)); | ||
if (hasEndQuote) { | ||
output.push('"'); | ||
i++; | ||
} else { | ||
// repair missing quote | ||
output.insertBeforeLastWhitespace('"'); | ||
} | ||
parseWhitespaceAndSkipComments(); | ||
// See whether we have: | ||
// (a) An end quote which is not followed by a valid delimiter | ||
// (b) No end quote and reached the end of the input | ||
// If so, revert parsing this string and try again, running in a more | ||
// conservative mode, stopping at the first next delimiter | ||
const isAtEnd = input.isEnd(i); | ||
const nextIsDelimiter = (0, _stringUtils.isDelimiter)(input.charAt(i)); | ||
if (!stopAtDelimiter && (hasEndQuote && !isAtEnd && !nextIsDelimiter || !hasEndQuote && isAtEnd)) { | ||
i = iBefore; | ||
output.remove(oBefore); | ||
return parseString(true); | ||
} | ||
parseConcatenatedString(); | ||
return stack.update(_stack.Caret.afterValue); | ||
} | ||
@@ -621,3 +641,3 @@ return false; | ||
let j = i; | ||
while (!input.isEnd(j) && !(0, _stringUtils.isDelimiter)(input.charAt(j))) { | ||
while (!input.isEnd(j) && !(0, _stringUtils.isDelimiter)(input.charAt(j)) && !(0, _stringUtils.isQuote)(input.charCodeAt(j))) { | ||
j++; | ||
@@ -627,2 +647,9 @@ } | ||
} | ||
function prevNonWhitespaceIndex(start) { | ||
let prev = start; | ||
while (prev > 0 && (0, _stringUtils.isWhitespace)(input.charCodeAt(prev))) { | ||
prev--; | ||
} | ||
return prev; | ||
} | ||
function expectDigit(start) { | ||
@@ -629,0 +656,0 @@ if (!(0, _stringUtils.isDigit)(input.charCodeAt(i))) { |
@@ -78,5 +78,5 @@ "use strict"; | ||
function isDelimiter(char) { | ||
return regexDelimiter.test(char) || isQuote(char.charCodeAt(0)); | ||
return regexDelimiter.test(char); | ||
} | ||
const regexDelimiter = /^[,:[\]{}()\n+]$/; | ||
const regexDelimiter = /^[,:[\]/{}()\n+]$/; | ||
function isStartOfValue(char) { | ||
@@ -83,0 +83,0 @@ return regexStartOfValue.test(char) || char && isQuote(char.charCodeAt(0)); |
@@ -313,5 +313,57 @@ import { JSONRepairError } from '../utils/JSONRepairError.js'; | ||
i++; | ||
const isEndOfString = stopAtDelimiter ? i => isDelimiter(text[i]) : i => isEndQuote(text.charCodeAt(i)); | ||
while (i < text.length && !isEndOfString(i)) { | ||
if (text.charCodeAt(i) === codeBackslash) { | ||
while (true) { | ||
if (i >= text.length) { | ||
// end of text, we are missing an end quote | ||
if (!stopAtDelimiter) { | ||
// retry parsing the string, stopping at the first next delimiter | ||
i = iBefore; | ||
output = output.substring(0, oBefore); | ||
return parseString(true); | ||
} | ||
// repair missing quote | ||
str = insertBeforeLastWhitespace(str, '"'); | ||
output += str; | ||
return true; | ||
} else if (isEndQuote(text.charCodeAt(i))) { | ||
// end quote | ||
// let us check what is before and after the quote to verify whether this is a legit end quote | ||
const iQuote = i; | ||
const oQuote = str.length; | ||
str += '"'; | ||
i++; | ||
output += str; | ||
parseWhitespaceAndSkipComments(); | ||
if (stopAtDelimiter || i >= text.length || isDelimiter(text.charAt(i)) || isQuote(text.charCodeAt(i))) { | ||
// The quote is followed by a delimiter or the end of the text, | ||
// so the quote is indeed the end of the string | ||
parseConcatenatedString(); | ||
return true; | ||
} | ||
if (isDelimiter(text.charAt(prevNonWhitespaceIndex(iQuote - 1)))) { | ||
// This is not the right end quote: it is preceded by a delimiter, | ||
// and NOT followed by a delimiter. So, there is an end quote missing | ||
// parse the string again and then stop at the first next delimiter | ||
i = iBefore; | ||
output = output.substring(0, oBefore); | ||
return parseString(true); | ||
} | ||
// revert to right after the quote but before any whitespace, and continue parsing the string | ||
output = output.substring(0, oBefore); | ||
i = iQuote + 1; | ||
// repair unescaped quote | ||
str = str.substring(0, oQuote) + '\\' + str.substring(oQuote); | ||
} else if (stopAtDelimiter && isDelimiter(text[i])) { | ||
// we're in the mode to stop the string at the first delimiter | ||
// because there is an end quote missing | ||
// repair missing quote | ||
str = insertBeforeLastWhitespace(str, '"'); | ||
output += str; | ||
parseConcatenatedString(); | ||
return true; | ||
} else if (text.charCodeAt(i) === codeBackslash) { | ||
// handle escaped content like \n or \u2605 | ||
const char = text.charAt(i + 1); | ||
@@ -343,2 +395,3 @@ const escapeChar = escapeCharacters[char]; | ||
} else { | ||
// handle regular characters | ||
const char = text.charAt(i); | ||
@@ -363,33 +416,6 @@ const code = text.charCodeAt(i); | ||
if (skipEscapeChars) { | ||
const processed = skipEscapeCharacter(); | ||
if (processed) { | ||
// repair: skipped escape character (nothing to do) | ||
} | ||
// repair: skipped escape character (nothing to do) | ||
skipEscapeCharacter(); | ||
} | ||
} | ||
const hasEndQuote = isQuote(text.charCodeAt(i)); | ||
if (hasEndQuote) { | ||
str += '"'; | ||
i++; | ||
} else { | ||
// repair missing quote | ||
str = insertBeforeLastWhitespace(str, '"'); | ||
} | ||
output += str; | ||
parseWhitespaceAndSkipComments(); | ||
// See whether we have: | ||
// (a) An end quote which is not followed by a valid delimiter | ||
// (b) No end quote and reached the end of the input | ||
// If so, revert parsing this string and try again, running in a more | ||
// conservative mode, stopping at the first next delimiter | ||
const isAtEnd = i >= text.length; | ||
const nextIsDelimiter = isDelimiter(text.charAt(i)); | ||
if (!stopAtDelimiter && (hasEndQuote && !isAtEnd && !nextIsDelimiter || !hasEndQuote && isAtEnd)) { | ||
i = iBefore; | ||
output = output.substring(0, oBefore); | ||
return parseString(true); | ||
} | ||
parseConcatenatedString(); | ||
return true; | ||
} | ||
@@ -501,3 +527,3 @@ return false; | ||
const start = i; | ||
while (i < text.length && !isDelimiter(text[i])) { | ||
while (i < text.length && !isDelimiter(text[i]) && !isQuote(text.charCodeAt(i))) { | ||
i++; | ||
@@ -538,2 +564,9 @@ } | ||
} | ||
function prevNonWhitespaceIndex(start) { | ||
let prev = start; | ||
while (prev > 0 && isWhitespace(text.charCodeAt(prev))) { | ||
prev--; | ||
} | ||
return prev; | ||
} | ||
function expectDigit(start) { | ||
@@ -540,0 +573,0 @@ if (!isDigit(text.charCodeAt(i))) { |
@@ -48,2 +48,8 @@ import { isWhitespace } from '../../utils/stringUtils.js'; | ||
} | ||
function insertAt(index, text) { | ||
if (index < offset) { | ||
throw new Error("Cannot insert: ".concat(flushedMessage)); | ||
} | ||
buffer = buffer.substring(0, index - offset) + text + buffer.substring(index - offset); | ||
} | ||
function length() { | ||
@@ -97,2 +103,3 @@ return offset + buffer.length; | ||
remove, | ||
insertAt, | ||
length, | ||
@@ -99,0 +106,0 @@ flush, |
@@ -396,5 +396,2 @@ import { createInputBuffer } from './buffer/InputBuffer.js'; | ||
let stopAtDelimiter = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; | ||
// we may need to revert | ||
const iBefore = i; | ||
const oBefore = output.length(); | ||
let skipEscapeChars = input.charCodeAt(i) === codeBackslash; | ||
@@ -411,12 +408,58 @@ if (skipEscapeChars) { | ||
// or any single-quote-like start with a single-quote-like end | ||
const isEndQuote = isDoubleQuote(input.charCodeAt(i)) ? isDoubleQuote : isSingleQuote(input.charCodeAt(i)) ? isSingleQuote // eslint-disable-line indent | ||
: isSingleQuoteLike(input.charCodeAt(i)) // eslint-disable-line indent | ||
? isSingleQuoteLike // eslint-disable-line indent | ||
: isDoubleQuoteLike; // eslint-disable-line indent | ||
const isEndQuote = isDoubleQuote(input.charCodeAt(i)) ? isDoubleQuote : isSingleQuote(input.charCodeAt(i)) ? isSingleQuote : isSingleQuoteLike(input.charCodeAt(i)) ? isSingleQuoteLike : isDoubleQuoteLike; | ||
const iBefore = i; | ||
const oBefore = output.length(); | ||
output.push('"'); | ||
i++; | ||
const isEndOfString = stopAtDelimiter ? i => isDelimiter(input.charAt(i)) : i => isEndQuote(input.charCodeAt(i)); | ||
while (!input.isEnd(i) && !isEndOfString(i)) { | ||
if (input.charCodeAt(i) === codeBackslash) { | ||
while (true) { | ||
if (input.isEnd(i)) { | ||
// end of text, we have a missing quote somewhere | ||
if (!stopAtDelimiter) { | ||
i = iBefore; | ||
output.remove(oBefore); | ||
return parseString(true); | ||
} | ||
// repair missing quote | ||
output.insertBeforeLastWhitespace('"'); | ||
return stack.update(Caret.afterValue); | ||
} else if (isEndQuote(input.charCodeAt(i))) { | ||
// end quote | ||
// let us check what is before and after the quote to verify whether this is a legit end quote | ||
const iQuote = i; | ||
const oQuote = output.length(); | ||
output.push('"'); | ||
i++; | ||
parseWhitespaceAndSkipComments(); | ||
if (stopAtDelimiter || input.isEnd(i) || isDelimiter(input.charAt(i)) || isQuote(input.charCodeAt(i))) { | ||
// The quote is followed by a delimiter or the end of the text, | ||
// so the quote is indeed the end of the string | ||
parseConcatenatedString(); | ||
return stack.update(Caret.afterValue); | ||
} | ||
if (isDelimiter(input.charAt(prevNonWhitespaceIndex(iQuote - 1)))) { | ||
// This is not the right end quote: it is preceded by a delimiter, | ||
// and NOT followed by a delimiter. So, there is an end quote missing | ||
// parse the string again and then stop at the first next delimiter | ||
i = iBefore; | ||
output.remove(oBefore); | ||
return parseString(true); | ||
} | ||
// revert to right after the quote but before any whitespace, and continue parsing the string | ||
output.remove(oQuote + 1); | ||
i = iQuote + 1; | ||
// repair unescaped quote | ||
output.insertAt(oQuote, '\\'); | ||
} else if (stopAtDelimiter && isDelimiter(input.charAt(i))) { | ||
// we're in the mode to stop the string at the first delimiter | ||
// because there is an end quote missing | ||
// repair missing quote | ||
output.insertBeforeLastWhitespace('"'); | ||
parseConcatenatedString(); | ||
return stack.update(Caret.afterValue); | ||
} else if (input.charCodeAt(i) === codeBackslash) { | ||
// handle escaped content like \n or \u2605 | ||
const char = input.charAt(i + 1); | ||
@@ -448,2 +491,3 @@ const escapeChar = escapeCharacters[char]; | ||
} else { | ||
// handle regular characters | ||
const char = input.charAt(i); | ||
@@ -472,26 +516,2 @@ const code = char.charCodeAt(0); | ||
} | ||
const hasEndQuote = isQuote(input.charCodeAt(i)); | ||
if (hasEndQuote) { | ||
output.push('"'); | ||
i++; | ||
} else { | ||
// repair missing quote | ||
output.insertBeforeLastWhitespace('"'); | ||
} | ||
parseWhitespaceAndSkipComments(); | ||
// See whether we have: | ||
// (a) An end quote which is not followed by a valid delimiter | ||
// (b) No end quote and reached the end of the input | ||
// If so, revert parsing this string and try again, running in a more | ||
// conservative mode, stopping at the first next delimiter | ||
const isAtEnd = input.isEnd(i); | ||
const nextIsDelimiter = isDelimiter(input.charAt(i)); | ||
if (!stopAtDelimiter && (hasEndQuote && !isAtEnd && !nextIsDelimiter || !hasEndQuote && isAtEnd)) { | ||
i = iBefore; | ||
output.remove(oBefore); | ||
return parseString(true); | ||
} | ||
parseConcatenatedString(); | ||
return stack.update(Caret.afterValue); | ||
} | ||
@@ -615,3 +635,3 @@ return false; | ||
let j = i; | ||
while (!input.isEnd(j) && !isDelimiter(input.charAt(j))) { | ||
while (!input.isEnd(j) && !isDelimiter(input.charAt(j)) && !isQuote(input.charCodeAt(j))) { | ||
j++; | ||
@@ -621,2 +641,9 @@ } | ||
} | ||
function prevNonWhitespaceIndex(start) { | ||
let prev = start; | ||
while (prev > 0 && isWhitespace(input.charCodeAt(prev))) { | ||
prev--; | ||
} | ||
return prev; | ||
} | ||
function expectDigit(start) { | ||
@@ -623,0 +650,0 @@ if (!isDigit(input.charCodeAt(i))) { |
@@ -55,5 +55,5 @@ export const codeBackslash = 0x5c; // "\" | ||
export function isDelimiter(char) { | ||
return regexDelimiter.test(char) || isQuote(char.charCodeAt(0)); | ||
return regexDelimiter.test(char); | ||
} | ||
const regexDelimiter = /^[,:[\]{}()\n+]$/; | ||
const regexDelimiter = /^[,:[\]/{}()\n+]$/; | ||
export function isStartOfValue(char) { | ||
@@ -60,0 +60,0 @@ return regexStartOfValue.test(char) || char && isQuote(char.charCodeAt(0)); |
@@ -5,2 +5,3 @@ export interface OutputBuffer { | ||
remove: (start: number, end?: number) => void; | ||
insertAt: (index: number, text: string) => void; | ||
length: () => number; | ||
@@ -7,0 +8,0 @@ flush: () => void; |
@@ -68,5 +68,5 @@ (function (global, factory) { | ||
function isDelimiter(char) { | ||
return regexDelimiter.test(char) || isQuote(char.charCodeAt(0)); | ||
return regexDelimiter.test(char); | ||
} | ||
const regexDelimiter = /^[,:[\]{}()\n+]$/; | ||
const regexDelimiter = /^[,:[\]/{}()\n+]$/; | ||
function isStartOfValue(char) { | ||
@@ -480,5 +480,57 @@ return regexStartOfValue.test(char) || char && isQuote(char.charCodeAt(0)); | ||
i++; | ||
const isEndOfString = stopAtDelimiter ? i => isDelimiter(text[i]) : i => isEndQuote(text.charCodeAt(i)); | ||
while (i < text.length && !isEndOfString(i)) { | ||
if (text.charCodeAt(i) === codeBackslash) { | ||
while (true) { | ||
if (i >= text.length) { | ||
// end of text, we are missing an end quote | ||
if (!stopAtDelimiter) { | ||
// retry parsing the string, stopping at the first next delimiter | ||
i = iBefore; | ||
output = output.substring(0, oBefore); | ||
return parseString(true); | ||
} | ||
// repair missing quote | ||
str = insertBeforeLastWhitespace(str, '"'); | ||
output += str; | ||
return true; | ||
} else if (isEndQuote(text.charCodeAt(i))) { | ||
// end quote | ||
// let us check what is before and after the quote to verify whether this is a legit end quote | ||
const iQuote = i; | ||
const oQuote = str.length; | ||
str += '"'; | ||
i++; | ||
output += str; | ||
parseWhitespaceAndSkipComments(); | ||
if (stopAtDelimiter || i >= text.length || isDelimiter(text.charAt(i)) || isQuote(text.charCodeAt(i))) { | ||
// The quote is followed by a delimiter or the end of the text, | ||
// so the quote is indeed the end of the string | ||
parseConcatenatedString(); | ||
return true; | ||
} | ||
if (isDelimiter(text.charAt(prevNonWhitespaceIndex(iQuote - 1)))) { | ||
// This is not the right end quote: it is preceded by a delimiter, | ||
// and NOT followed by a delimiter. So, there is an end quote missing | ||
// parse the string again and then stop at the first next delimiter | ||
i = iBefore; | ||
output = output.substring(0, oBefore); | ||
return parseString(true); | ||
} | ||
// revert to right after the quote but before any whitespace, and continue parsing the string | ||
output = output.substring(0, oBefore); | ||
i = iQuote + 1; | ||
// repair unescaped quote | ||
str = str.substring(0, oQuote) + '\\' + str.substring(oQuote); | ||
} else if (stopAtDelimiter && isDelimiter(text[i])) { | ||
// we're in the mode to stop the string at the first delimiter | ||
// because there is an end quote missing | ||
// repair missing quote | ||
str = insertBeforeLastWhitespace(str, '"'); | ||
output += str; | ||
parseConcatenatedString(); | ||
return true; | ||
} else if (text.charCodeAt(i) === codeBackslash) { | ||
// handle escaped content like \n or \u2605 | ||
const char = text.charAt(i + 1); | ||
@@ -510,2 +562,3 @@ const escapeChar = escapeCharacters[char]; | ||
} else { | ||
// handle regular characters | ||
const char = text.charAt(i); | ||
@@ -530,30 +583,6 @@ const code = text.charCodeAt(i); | ||
if (skipEscapeChars) { | ||
// repair: skipped escape character (nothing to do) | ||
skipEscapeCharacter(); | ||
} | ||
} | ||
const hasEndQuote = isQuote(text.charCodeAt(i)); | ||
if (hasEndQuote) { | ||
str += '"'; | ||
i++; | ||
} else { | ||
// repair missing quote | ||
str = insertBeforeLastWhitespace(str, '"'); | ||
} | ||
output += str; | ||
parseWhitespaceAndSkipComments(); | ||
// See whether we have: | ||
// (a) An end quote which is not followed by a valid delimiter | ||
// (b) No end quote and reached the end of the input | ||
// If so, revert parsing this string and try again, running in a more | ||
// conservative mode, stopping at the first next delimiter | ||
const isAtEnd = i >= text.length; | ||
const nextIsDelimiter = isDelimiter(text.charAt(i)); | ||
if (!stopAtDelimiter && (hasEndQuote && !isAtEnd && !nextIsDelimiter || !hasEndQuote && isAtEnd)) { | ||
i = iBefore; | ||
output = output.substring(0, oBefore); | ||
return parseString(true); | ||
} | ||
parseConcatenatedString(); | ||
return true; | ||
} | ||
@@ -665,3 +694,3 @@ return false; | ||
const start = i; | ||
while (i < text.length && !isDelimiter(text[i])) { | ||
while (i < text.length && !isDelimiter(text[i]) && !isQuote(text.charCodeAt(i))) { | ||
i++; | ||
@@ -702,2 +731,9 @@ } | ||
} | ||
function prevNonWhitespaceIndex(start) { | ||
let prev = start; | ||
while (prev > 0 && isWhitespace(text.charCodeAt(prev))) { | ||
prev--; | ||
} | ||
return prev; | ||
} | ||
function expectDigit(start) { | ||
@@ -704,0 +740,0 @@ if (!isDigit(text.charCodeAt(i))) { |
@@ -1,1 +0,1 @@ | ||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).JSONRepair={})}(this,function(t){"use strict";class y extends Error{constructor(t,e){super(t+" at position "+e),this.position=e}}const u=125,e=32,O=10,N=9,J=13,S=8,j=12,I=34,r=39,k=48,m=57,T=65,$=97,E=70,R=102,h=160,d=8192,l=8202,s=8239,A=8287,C=12288,n=8220,o=8221,c=8216,i=8217,f=96,a=180;function U(t){return t>=k&&t<=m}function F(t){return g.test(t)||B(t.charCodeAt(0))}const g=/^[,:[\]{}()\n+]$/;function q(t){return v.test(t)||t&&B(t.charCodeAt(0))}const v=/^[[{\w-]$/;function z(t){return t===e||t===O||t===N||t===J}function B(t){return D(t)||H(t)}function D(t){return t===I||t===n||t===o}function G(t){return t===I}function H(t){return t===r||t===c||t===i||t===f||t===a}function K(t){return t===r}function L(t,e,r){r=2<arguments.length&&void 0!==r&&r,e=t.lastIndexOf(e);return-1!==e?t.substring(0,e)+(r?"":t.substring(e+1)):t}function M(t,e){let r=t.length;if(!z(t.charCodeAt(r-1)))return t+e;for(;z(t.charCodeAt(r-1));)r--;return t.substring(0,r)+e+t.substring(r)}const P={"\b":"\\b","\f":"\\f","\n":"\\n","\r":"\\r","\t":"\\t"},Q={'"':'"',"\\":"\\","/":"/",b:"\b",f:"\f",n:"\n",r:"\r",t:"\t"};t.JSONRepairError=y,t.jsonrepair=function(g){let v=0,b="";if(!c())throw new y("Unexpected end of json string",g.length);var t=i(44);if(t&&p(),q(g[v])&&/[,\n][ \t\r]*$/.test(b)){t||(b=M(b,","));{let t=!0,e=!0;for(;e;)t?t=!1:i(44)||(b=M(b,",")),e=c();e||(b=L(b,","));b="[\n".concat(b,"\n]")}}else t&&(b=L(b,","));for(;g.charCodeAt(v)===u||93===g.charCodeAt(v);)v++,p();if(v>=g.length)return b;throw new y("Unexpected character "+JSON.stringify(g[v]),v);function c(){p();var t=function(){if(123!==g.charCodeAt(v))return!1;{b+="{",v++,p();let e=!0;for(;v<g.length&&g.charCodeAt(v)!==u;){let t;if(e?(t=!0,e=!1):((t=i(44))||(b=M(b,",")),p()),!(x()||f())){g.charCodeAt(v)===u||123===g.charCodeAt(v)||93===g.charCodeAt(v)||91===g.charCodeAt(v)||void 0===g[v]?b=L(b,","):function(){throw new y("Object key expected",v)}();break}p();var r=i(58),n=v>=g.length,o=(r||(q(g[v])||n?b=M(b,":"):a()),c());o||(r||n?b+="null":a())}return g.charCodeAt(v)===u?(b+="}",v++):b=M(b,"}"),!0}}()||function(){if(91!==g.charCodeAt(v))return!1;{b+="[",v++,p();let t=!0;for(;v<g.length&&93!==g.charCodeAt(v);){t?t=!1:i(44)||(b=M(b,","));var e=c();if(!e){b=L(b,",");break}}return 93===g.charCodeAt(v)?(b+="]",v++):b=M(b,"]"),!0}}()||x()||function(){var t=v;if(45===g.charCodeAt(v)&&(v++,o(t)))return!0;for(;U(g.charCodeAt(v));)v++;if(46===g.charCodeAt(v)){if(v++,o(t))return!0;for(;U(g.charCodeAt(v));)v++}if(101===g.charCodeAt(v)||69===g.charCodeAt(v)){if(v++,45!==g.charCodeAt(v)&&43!==g.charCodeAt(v)||v++,o(t))return!0;for(;U(g.charCodeAt(v));)v++}{var e;if(v>t)return t=g.slice(t,v),e=/^0\d/.test(t),b+=e?'"'.concat(t,'"'):t,!0}return!1}()||r("true","true")||r("false","false")||r("null","null")||r("True","true")||r("False","false")||r("None","null")||f();return p(),t}function p(){v;let t=e();for(;t=(t=function(){if(47===g.charCodeAt(v)&&42===g.charCodeAt(v+1)){for(;v<g.length&&!function(t,e){return"*"===t[e]&&"/"===t[e+1]}(g,v);)v++;v+=2}else{if(47!==g.charCodeAt(v)||47!==g.charCodeAt(v+1))return!1;for(;v<g.length&&g.charCodeAt(v)!==O;)v++}return!0}())&&e(););v}function e(){let t="";for(var e,r;(e=z(g.charCodeAt(v)))||(r=g.charCodeAt(v))===h||r>=d&&r<=l||r===s||r===A||r===C;)t+=e?g[v]:" ",v++;return 0<t.length&&(b+=t,!0)}function i(t){return g.charCodeAt(v)===t&&(b+=g[v],v++,!0)}function w(){92===g.charCodeAt(v)&&v++}function x(t){var r,t=0<arguments.length&&void 0!==t&&t;let n=92===g.charCodeAt(v);if(n&&(v++,n=!0),B(g.charCodeAt(v))){const C=G(g.charCodeAt(v))?G:K(g.charCodeAt(v))?K:H(g.charCodeAt(v))?H:D;var o=v,c=b.length;let e='"';v++;for(var i=t?t=>F(g[t]):t=>C(g.charCodeAt(t));v<g.length&&!i(v);){if(92===g.charCodeAt(v)){var f=g.charAt(v+1);if(void 0!==Q[f])e+=g.slice(v,v+2),v+=2;else if("u"===f){let t=2;for(;t<6&&((r=g.charCodeAt(v+t))>=k&&r<=m||r>=T&&r<=E||r>=$&&r<=R);)t++;if(6===t)e+=g.slice(v,v+6),v+=6;else{if(!(v+t>=g.length))throw u=void 0,u=g.slice(v,v+6),new y('Invalid unicode character "'.concat(u,'"'),v);v=g.length}}else e+=f,v+=2}else{var a,u=g.charAt(v),f=g.charCodeAt(v);if(f===I&&92!==g.charCodeAt(v-1))e+="\\"+u;else if((a=f)===O||a===J||a===N||a===S||a===j)e+=P[u];else{if(!(32<=(a=f)&&a<=1114111))throw a=void 0,a=u,new y("Invalid character "+JSON.stringify(a),v);e+=u}v++}n&&w()}var h=B(g.charCodeAt(v)),d=(h?(e+='"',v++):e=M(e,'"'),b+=e,p(),v>=g.length),l=F(g.charAt(v));if(!t&&(h&&!d&&!l||!h&&d))return v=o,b=b.substring(0,c),x(!0);{let t=!1;p();for(;43===g.charCodeAt(v);){t=!0,v++,p();var s=(b=L(b,'"',!0)).length,A=x();b=A?function(t,e,r){return t.substring(0,e)+t.substring(e+r)}(b,s,1):M(b,'"')}t}return!0}return!1}function r(t,e){return g.slice(v,v+t.length)===t&&(b+=e,v+=t.length,!0)}function f(){for(var t=v;v<g.length&&!F(g[v]);)v++;if(v>t){if(40===g.charCodeAt(v))v++,c(),41===g.charCodeAt(v)&&(v++,59===g.charCodeAt(v))&&v++;else{for(;z(g.charCodeAt(v-1))&&0<v;)v--;t=g.slice(t,v);b+="undefined"===t?"null":JSON.stringify(t),g.charCodeAt(v)===I&&v++}return!0}}function n(t){if(!U(g.charCodeAt(v)))throw t=g.slice(t,v),new y("Invalid number '".concat(t,"', expecting a digit ").concat(g[v]?"but got '".concat(g[v],"'"):"but reached end of input"),v)}function o(t){if(v>=g.length)return b+=g.slice(t,v)+"0",1;n(t)}function a(){throw new y("Colon expected",v)}}}); | ||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).JSONRepair={})}(this,function(t){"use strict";class w extends Error{constructor(t,e){super(t+" at position "+e),this.position=e}}const a=125,e=32,x=10,y=9,O=13,N=8,J=12,S=34,r=39,j=48,I=57,k=65,m=97,T=70,$=102,h=160,d=8192,l=8202,E=8239,R=8287,U=12288,n=8220,o=8221,c=8216,i=8217,f=96,u=180;function F(t){return t>=j&&t<=I}function q(t){return s.test(t)}const s=/^[,:[\]/{}()\n+]$/;function z(t){return A.test(t)||t&&D(t.charCodeAt(0))}const A=/^[[{\w-]$/;function B(t){return t===e||t===x||t===y||t===O}function D(t){return G(t)||K(t)}function G(t){return t===S||t===n||t===o}function H(t){return t===S}function K(t){return t===r||t===c||t===i||t===f||t===u}function L(t){return t===r}function M(t,e,r){r=2<arguments.length&&void 0!==r&&r,e=t.lastIndexOf(e);return-1!==e?t.substring(0,e)+(r?"":t.substring(e+1)):t}function P(t,e){let r=t.length;if(!B(t.charCodeAt(r-1)))return t+e;for(;B(t.charCodeAt(r-1));)r--;return t.substring(0,r)+e+t.substring(r)}const Q={"\b":"\\b","\f":"\\f","\n":"\\n","\r":"\\r","\t":"\\t"},V={'"':'"',"\\":"\\","/":"/",b:"\b",f:"\f",n:"\n",r:"\r",t:"\t"};t.JSONRepairError=w,t.jsonrepair=function(s){let A=0,C="";if(!c())throw new w("Unexpected end of json string",s.length);var t=i(44);if(t&&g(),z(s[A])&&/[,\n][ \t\r]*$/.test(C)){t||(C=P(C,","));{let t=!0,e=!0;for(;e;)t?t=!1:i(44)||(C=P(C,",")),e=c();e||(C=M(C,","));C="[\n".concat(C,"\n]")}}else t&&(C=M(C,","));for(;s.charCodeAt(A)===a||93===s.charCodeAt(A);)A++,g();if(A>=s.length)return C;throw new w("Unexpected character "+JSON.stringify(s[A]),A);function c(){g();var t=function(){if(123!==s.charCodeAt(A))return!1;{C+="{",A++,g();let e=!0;for(;A<s.length&&s.charCodeAt(A)!==a;){let t;if(e?(t=!0,e=!1):((t=i(44))||(C=P(C,",")),g()),!(v()||f())){s.charCodeAt(A)===a||123===s.charCodeAt(A)||93===s.charCodeAt(A)||91===s.charCodeAt(A)||void 0===s[A]?C=M(C,","):function(){throw new w("Object key expected",A)}();break}g();var r=i(58),n=A>=s.length,o=(r||(z(s[A])||n?C=P(C,":"):u()),c());o||(r||n?C+="null":u())}return s.charCodeAt(A)===a?(C+="}",A++):C=P(C,"}"),!0}}()||function(){if(91!==s.charCodeAt(A))return!1;{C+="[",A++,g();let t=!0;for(;A<s.length&&93!==s.charCodeAt(A);){t?t=!1:i(44)||(C=P(C,","));var e=c();if(!e){C=M(C,",");break}}return 93===s.charCodeAt(A)?(C+="]",A++):C=P(C,"]"),!0}}()||v()||function(){var t=A;if(45===s.charCodeAt(A)&&(A++,o(t)))return!0;for(;F(s.charCodeAt(A));)A++;if(46===s.charCodeAt(A)){if(A++,o(t))return!0;for(;F(s.charCodeAt(A));)A++}if(101===s.charCodeAt(A)||69===s.charCodeAt(A)){if(A++,45!==s.charCodeAt(A)&&43!==s.charCodeAt(A)||A++,o(t))return!0;for(;F(s.charCodeAt(A));)A++}{var e;if(A>t)return t=s.slice(t,A),e=/^0\d/.test(t),C+=e?'"'.concat(t,'"'):t,!0}return!1}()||r("true","true")||r("false","false")||r("null","null")||r("True","true")||r("False","false")||r("None","null")||f();return g(),t}function g(){A;let t=e();for(;t=(t=function(){if(47===s.charCodeAt(A)&&42===s.charCodeAt(A+1)){for(;A<s.length&&!function(t,e){return"*"===t[e]&&"/"===t[e+1]}(s,A);)A++;A+=2}else{if(47!==s.charCodeAt(A)||47!==s.charCodeAt(A+1))return!1;for(;A<s.length&&s.charCodeAt(A)!==x;)A++}return!0}())&&e(););A}function e(){let t="";for(var e,r;(e=B(s.charCodeAt(A)))||(r=s.charCodeAt(A))===h||r>=d&&r<=l||r===E||r===R||r===U;)t+=e?s[A]:" ",A++;return 0<t.length&&(C+=t,!0)}function i(t){return s.charCodeAt(A)===t&&(C+=s[A],A++,!0)}function b(){92===s.charCodeAt(A)&&A++}function v(t){var r,n,o=0<arguments.length&&void 0!==t&&t;let c=92===s.charCodeAt(A);if(c&&(A++,c=!0),D(s.charCodeAt(A))){var i=H(s.charCodeAt(A))?H:L(s.charCodeAt(A))?L:K(s.charCodeAt(A))?K:G,f=A,u=C.length;let e='"';for(A++;;){if(A>=s.length)return o?(e=P(e,'"'),C+=e,!0):(A=f,C=C.substring(0,u),v(!0));if(i(s.charCodeAt(A))){var a=A,h=e.length;if(e+='"',A++,C+=e,g(),o||A>=s.length||q(s.charAt(A))||D(s.charCodeAt(A)))return p(),!0;if(q(s.charAt(function(t){let e=t;for(;0<e&&B(s.charCodeAt(e));)e--;return e}(a-1))))return A=f,C=C.substring(0,u),v(!0);C=C.substring(0,u),A=a+1,e=e.substring(0,h)+"\\"+e.substring(h)}else{if(o&&q(s[A]))return e=P(e,'"'),C+=e,p(),!0;if(92===s.charCodeAt(A)){a=s.charAt(A+1);if(void 0!==V[a])e+=s.slice(A,A+2),A+=2;else if("u"===a){let t=2;for(;t<6&&((n=s.charCodeAt(A+t))>=j&&n<=I||n>=k&&n<=T||n>=m&&n<=$);)t++;if(6===t)e+=s.slice(A,A+6),A+=6;else{if(!(A+t>=s.length))throw h=void 0,h=s.slice(A,A+6),new w('Invalid unicode character "'.concat(h,'"'),A);A=s.length}}else e+=a,A+=2}else{var d=s.charAt(A),l=s.charCodeAt(A);if(l===S&&92!==s.charCodeAt(A-1))e+="\\"+d;else if((r=l)===x||r===O||r===y||r===N||r===J)e+=Q[d];else{if(!(32<=(r=l)&&r<=1114111))throw l=void 0,l=d,new w("Invalid character "+JSON.stringify(l),A);e+=d}A++}}c&&b()}}return!1}function p(){let t=!1;for(g();43===s.charCodeAt(A);){t=!0,A++,g();var e=(C=M(C,'"',!0)).length,r=v();C=r?(r=C,e=e,n=1,r.substring(0,e)+r.substring(e+n)):P(C,'"')}var n;t}function r(t,e){return s.slice(A,A+t.length)===t&&(C+=e,A+=t.length,!0)}function f(){for(var t=A;A<s.length&&!q(s[A])&&!D(s.charCodeAt(A));)A++;if(A>t){if(40===s.charCodeAt(A))A++,c(),41===s.charCodeAt(A)&&(A++,59===s.charCodeAt(A))&&A++;else{for(;B(s.charCodeAt(A-1))&&0<A;)A--;t=s.slice(t,A);C+="undefined"===t?"null":JSON.stringify(t),s.charCodeAt(A)===S&&A++}return!0}}function n(t){if(!F(s.charCodeAt(A)))throw t=s.slice(t,A),new w("Invalid number '".concat(t,"', expecting a digit ").concat(s[A]?"but got '".concat(s[A],"'"):"but reached end of input"),A)}function o(t){if(A>=s.length)return C+=s.slice(t,A)+"0",1;n(t)}function u(){throw new w("Colon expected",A)}}}); |
{ | ||
"name": "jsonrepair", | ||
"version": "3.5.1", | ||
"version": "3.6.0", | ||
"description": "Repair broken JSON documents", | ||
@@ -60,3 +60,3 @@ "repository": { | ||
"release-dry-run": "npm run build-and-test && standard-version --dry-run", | ||
"prepare": "husky install" | ||
"prepare": "husky" | ||
}, | ||
@@ -71,12 +71,12 @@ "files": [ | ||
"devDependencies": { | ||
"@babel/cli": "7.23.4", | ||
"@babel/core": "7.23.7", | ||
"@babel/cli": "7.23.9", | ||
"@babel/core": "7.23.9", | ||
"@babel/plugin-transform-typescript": "7.23.6", | ||
"@babel/preset-env": "7.23.8", | ||
"@babel/preset-env": "7.23.9", | ||
"@babel/preset-typescript": "7.23.3", | ||
"@commitlint/cli": "18.4.4", | ||
"@commitlint/config-conventional": "18.4.4", | ||
"@types/node": "20.10.8", | ||
"@typescript-eslint/eslint-plugin": "6.18.1", | ||
"@typescript-eslint/parser": "6.18.1", | ||
"@commitlint/cli": "18.6.0", | ||
"@commitlint/config-conventional": "18.6.0", | ||
"@types/node": "20.11.17", | ||
"@typescript-eslint/eslint-plugin": "7.0.1", | ||
"@typescript-eslint/parser": "7.0.1", | ||
"benchmark": "2.1.4", | ||
@@ -91,6 +91,5 @@ "cpy-cli": "5.0.0", | ||
"eslint-plugin-promise": "6.1.1", | ||
"husky": "8.0.3", | ||
"husky": "9.0.10", | ||
"npm-run-all": "4.1.5", | ||
"prettier": "3.1.1", | ||
"rollup": "4.9.4", | ||
"rollup": "4.10.0", | ||
"standard-version": "9.5.0", | ||
@@ -100,4 +99,4 @@ "ts-node": "10.9.2", | ||
"uglify-js": "3.17.4", | ||
"vitest": "1.1.3" | ||
"vitest": "1.2.2" | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
469432
27
4491