import { createRequestError } from './request-client.js' import { normalizeOptionalResponseText } from './response-normalizers.js' import { assertPlainPayload, normalizeOptionalText } from './request-normalizers.js' const normalizeFileResponseText = (value, field) => normalizeOptionalResponseText(value, field, '文件服务响应', 'FILE_RESPONSE_INVALID') export const normalizeUploadId = (value, field) => { if (typeof value !== 'string' || !value.trim()) throw new TypeError(`${field}必须是非空字符串`) return value.trim() } const normalizeUploadFileName = (value) => { if (typeof value !== 'string' || !value.trim()) throw new TypeError('fileName必须是非空字符串') return value.trim() } export const normalizeUploadMd5 = (value, field) => { if (typeof value !== 'string' || !/^[a-f0-9]{32}$/i.test(value)) { throw new TypeError(`${field}必须是 32 位十六进制 MD5`) } return value.toLowerCase() } const normalizeUploadSize = (value, field) => { if (!Number.isSafeInteger(value) || value <= 0) throw new TypeError(`${field}必须是正安全整数`) return value } export const normalizeResumableInitPayload = (payload) => { const allowedFields = new Set(['uploadId', 'fileName', 'fileMd5', 'totalSize', 'totalChunks', 'chunkSize', 'contentType']) assertPlainPayload(payload, allowedFields, '文件初始化请求') if (!Number.isInteger(payload.totalChunks) || payload.totalChunks <= 0 || payload.totalChunks > 2147483647) { throw new TypeError('totalChunks必须是正 int32') } const normalizedPayload = { uploadId: normalizeUploadId(payload.uploadId, 'uploadId'), fileName: normalizeUploadFileName(payload.fileName), fileMd5: normalizeUploadMd5(payload.fileMd5, 'fileMd5'), totalSize: normalizeUploadSize(payload.totalSize, 'totalSize'), totalChunks: payload.totalChunks, chunkSize: normalizeUploadSize(payload.chunkSize, 'chunkSize') } const contentType = normalizeOptionalText(payload.contentType, 'contentType') if (contentType) normalizedPayload.contentType = contentType return normalizedPayload } export const normalizeResumableCompletePayload = (payload) => { const allowedFields = new Set(['uploadId', 'fileName', 'fileMd5', 'totalSize', 'totalChunks']) assertPlainPayload(payload, allowedFields, '文件完成请求') if (!Number.isInteger(payload.totalChunks) || payload.totalChunks <= 0 || payload.totalChunks > 2147483647) { throw new TypeError('totalChunks必须是正 int32') } return { uploadId: normalizeUploadId(payload.uploadId, 'uploadId'), fileName: normalizeUploadFileName(payload.fileName), fileMd5: normalizeUploadMd5(payload.fileMd5, 'fileMd5'), totalSize: normalizeUploadSize(payload.totalSize, 'totalSize'), totalChunks: payload.totalChunks } } const normalizeUploadOssId = (value, field) => { const id = typeof value === 'number' && Number.isSafeInteger(value) ? String(value) : value if (typeof id !== 'string' || !/^[1-9]\d*$/.test(id)) { throw createRequestError(`文件服务字段 ${field} 无效`, 'FILE_RESPONSE_INVALID') } return id } export const normalizeResumableInitResult = (payload) => { if (!payload || typeof payload !== 'object' || Array.isArray(payload)) { throw createRequestError('文件初始化响应格式无效', 'FILE_RESPONSE_INVALID') } if (typeof payload.instant !== 'boolean') { throw createRequestError('文件初始化响应缺少 instant', 'FILE_RESPONSE_INVALID') } const ossId = payload.ossId === undefined || payload.ossId === null ? null : normalizeUploadOssId(payload.ossId, 'ossId') if (payload.instant && !ossId) { throw createRequestError('秒传响应缺少 ossId', 'FILE_RESPONSE_INVALID') } const uploadId = payload.instant && (payload.uploadId === undefined || payload.uploadId === null) ? null : normalizeUploadId(payload.uploadId, '响应 uploadId') return { uploadId, instant: payload.instant, ossId, url: normalizeFileResponseText(payload.url, 'url'), fileName: normalizeFileResponseText(payload.fileName, 'fileName') } } export const normalizeResumableCompleteResult = (payload) => { if (!payload || typeof payload !== 'object' || Array.isArray(payload)) { throw createRequestError('文件完成响应格式无效', 'FILE_RESPONSE_INVALID') } return { ossId: normalizeUploadOssId(payload.ossId, 'ossId'), url: normalizeFileResponseText(payload.url, 'url'), thumbnailUrl: normalizeFileResponseText(payload.thumbnailUrl, 'thumbnailUrl'), fileName: normalizeFileResponseText(payload.fileName, 'fileName') } }