import type { JsonObject } from '../types/json.js' import { createHttpError } from './http.js' const LARGE_INTEGER_PATTERN = /(:\s*)(-?\d{16,})(\s*[,}\]])/g type ParseJsonObjectOptions = { preserveLargeIntegers?: boolean throwOnError?: boolean } export function parseJsonObject( rawText: unknown, { preserveLargeIntegers = false, throwOnError = false }: ParseJsonObjectOptions = {}, ): JsonObject { const normalized = String(rawText || '').trim() if (!normalized) { return {} } try { const prepared = preserveLargeIntegers ? wrapLargeIntegerLiterals(normalized) : normalized const parsed = JSON.parse(prepared) return isPlainObject(parsed) ? parsed : {} } catch (error) { if (!throwOnError) { return {} } throw createHttpError('json 参数不是合法 JSON', { statusCode: 400, errorCode: 'invalid_json_payload', cause: error, }) } } export function wrapLargeIntegerLiterals(rawText: unknown): string { return String(rawText || '').replace(LARGE_INTEGER_PATTERN, (_match, prefix, digits, suffix) => { if (!shouldPreserveIntegerToken(digits)) { return `${prefix}${digits}${suffix}` } return `${prefix}"${digits}"${suffix}` }) } function shouldPreserveIntegerToken(rawDigits: unknown): boolean { const digits = String(rawDigits || '').trim() const unsigned = digits.startsWith('-') ? digits.slice(1) : digits if (!/^\d+$/.test(unsigned)) { return false } if (unsigned.length > 16) { return true } if (unsigned.length < 16) { return false } return unsigned > '9007199254740991' } function isPlainObject(value: unknown): value is JsonObject { return Object.prototype.toString.call(value) === '[object Object]' }