mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-01 19:00:21 +00:00
144 lines
3.1 KiB
TypeScript
144 lines
3.1 KiB
TypeScript
const MAX_SAFE_INTEGER_ABS_STR = String(Number.MAX_SAFE_INTEGER);
|
|
|
|
function isAsciiDigit(ch: string | undefined): boolean {
|
|
return ch !== undefined && ch >= "0" && ch <= "9";
|
|
}
|
|
|
|
function parseJsonNumberToken(
|
|
input: string,
|
|
start: number,
|
|
): { token: string; end: number; isInteger: boolean } | null {
|
|
let idx = start;
|
|
if (input[idx] === "-") {
|
|
idx += 1;
|
|
}
|
|
if (idx >= input.length) {
|
|
return null;
|
|
}
|
|
|
|
if (input[idx] === "0") {
|
|
idx += 1;
|
|
} else if (isAsciiDigit(input[idx]) && input[idx] !== "0") {
|
|
while (isAsciiDigit(input[idx])) {
|
|
idx += 1;
|
|
}
|
|
} else {
|
|
return null;
|
|
}
|
|
|
|
let isInteger = true;
|
|
if (input[idx] === ".") {
|
|
isInteger = false;
|
|
idx += 1;
|
|
if (!isAsciiDigit(input[idx])) {
|
|
return null;
|
|
}
|
|
while (isAsciiDigit(input[idx])) {
|
|
idx += 1;
|
|
}
|
|
}
|
|
|
|
if (input[idx] === "e" || input[idx] === "E") {
|
|
isInteger = false;
|
|
idx += 1;
|
|
if (input[idx] === "+" || input[idx] === "-") {
|
|
idx += 1;
|
|
}
|
|
if (!isAsciiDigit(input[idx])) {
|
|
return null;
|
|
}
|
|
while (isAsciiDigit(input[idx])) {
|
|
idx += 1;
|
|
}
|
|
}
|
|
|
|
return {
|
|
token: input.slice(start, idx),
|
|
end: idx,
|
|
isInteger,
|
|
};
|
|
}
|
|
|
|
function isUnsafeIntegerLiteral(token: string): boolean {
|
|
const digits = token[0] === "-" ? token.slice(1) : token;
|
|
if (digits.length < MAX_SAFE_INTEGER_ABS_STR.length) {
|
|
return false;
|
|
}
|
|
if (digits.length > MAX_SAFE_INTEGER_ABS_STR.length) {
|
|
return true;
|
|
}
|
|
return digits > MAX_SAFE_INTEGER_ABS_STR;
|
|
}
|
|
|
|
function quoteUnsafeIntegerLiterals(input: string): string {
|
|
let out = "";
|
|
let inString = false;
|
|
let escaped = false;
|
|
let idx = 0;
|
|
|
|
while (idx < input.length) {
|
|
const ch = input[idx] ?? "";
|
|
if (inString) {
|
|
out += ch;
|
|
if (escaped) {
|
|
escaped = false;
|
|
} else if (ch === "\\") {
|
|
escaped = true;
|
|
} else if (ch === '"') {
|
|
inString = false;
|
|
}
|
|
idx += 1;
|
|
continue;
|
|
}
|
|
|
|
if (ch === '"') {
|
|
inString = true;
|
|
out += ch;
|
|
idx += 1;
|
|
continue;
|
|
}
|
|
|
|
if (ch === "-" || isAsciiDigit(ch)) {
|
|
const parsed = parseJsonNumberToken(input, idx);
|
|
if (parsed) {
|
|
if (parsed.isInteger && isUnsafeIntegerLiteral(parsed.token)) {
|
|
out += `"${parsed.token}"`;
|
|
} else {
|
|
out += parsed.token;
|
|
}
|
|
idx = parsed.end;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
out += ch;
|
|
idx += 1;
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
export function parseJsonPreservingUnsafeIntegers(input: string): unknown {
|
|
return JSON.parse(quoteUnsafeIntegerLiterals(input)) as unknown;
|
|
}
|
|
|
|
export function parseJsonObjectPreservingUnsafeIntegers(
|
|
value: unknown,
|
|
): Record<string, unknown> | null {
|
|
if (typeof value === "string") {
|
|
try {
|
|
const parsed = parseJsonPreservingUnsafeIntegers(value);
|
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
return parsed as Record<string, unknown>;
|
|
}
|
|
} catch {
|
|
return null;
|
|
}
|
|
return null;
|
|
}
|
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
return value as Record<string, unknown>;
|
|
}
|
|
return null;
|
|
}
|