Files
order_site/apps/backend/src/utils/json.ts
T
yml2213 042574dea3 继续收紧 TypeScript:ClaimIdentity 契约与 flow 出口类型
- 新增 types/claim-identity,统一 ClaimIdentity / Payload / AdminSummary
- 导出 KuaishouCloudFlow、KuaishouFeifeiFlow 作为 normalize 出口
- 生产路径 JsonObject 统一从 types/json 导入,去掉 rebind as any
- 前后端 claimIdentity 类型字段对齐
2026-07-10 15:56:25 +08:00

70 lines
1.7 KiB
TypeScript

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]'
}