import { createRequestError } from './request-client.js' import { normalizeContentPasswordPayload } from './protected-content-contract.js' import { normalizeResourcePathId } from './request-normalizers.js' export const CONTENT_PASSWORD_RESOURCE_TYPE = Object.freeze({ ARTICLE: 'ARTICLE', GROWTH_RECORD: 'GROWTH_RECORD', PERSON_DOCUMENT: 'PERSON_DOCUMENT' }) const resourceTypes = new Set(Object.values(CONTENT_PASSWORD_RESOURCE_TYPE)) export const normalizeContentPasswordResource = (resourceType, resourceId) => { if (!resourceTypes.has(resourceType)) throw new TypeError('内容密码找回资源类型无效') return { resourceType, resourceId: normalizeResourcePathId(resourceId, '内容密码资源标识') } } const recoveryError = (message) => createRequestError(message, 'CONTENT_PASSWORD_RECOVERY_RESPONSE_INVALID') const normalizeRecoveryText = (value, field, { required = false } = {}) => { if (value === undefined || value === null) { if (required) throw recoveryError(`内容密码找回响应缺少${field}`) return '' } if (typeof value !== 'string') throw recoveryError(`内容密码找回响应${field}无效`) const normalized = value.trim() if (required && !normalized) throw recoveryError(`内容密码找回响应缺少${field}`) return normalized } export const normalizeContentPasswordRecoveryCapability = (value) => { if (!value || typeof value !== 'object' || Array.isArray(value) || typeof value.available !== 'boolean') { throw recoveryError('内容密码找回能力响应无效') } const maskedPhone = normalizeRecoveryText(value.mobileMasked, 'mobileMasked') if (value.available && !maskedPhone) throw recoveryError('已开放找回但缺少脱敏手机号') return { enabled: value.available, maskedPhone, disabledReason: normalizeRecoveryText(value.disabledReason, 'disabledReason'), cooldownSeconds: 0 } } export const normalizeContentPasswordResetPayload = ({ smsCode, newPassword }) => { if (typeof smsCode !== 'string' || !/^\d{4}$/.test(smsCode)) { throw new TypeError('内容密码找回验证码必须是4位数字') } return { smsCode, newPassword: normalizeContentPasswordPayload(newPassword).password } }