import type { PostImportSourceIssue } from '@/lib/postImportTypes' const MAX_ROWS = 100 const MAX_URL_BYTES = 20 * 1024 const truncateUrl = (value: string): string => value.length > 120 ? `${ value.slice (0, 117) }…` : value const bytesize = (value: string): number => new TextEncoder ().encode (value).length const normaliseImportUrl = (value: string): string | null => { const trimmed = value.trim () if (!(trimmed)) return null try { const url = new URL (trimmed) if (!(url.protocol === 'http:' || url.protocol === 'https:')) return null if (!(url.host)) return null url.hostname = url.hostname.toLowerCase () if (url.pathname.endsWith ('/')) url.pathname = url.pathname.replace (/\/+$/, '') return url.toString () } catch { return null } } export const countImportSourceLines = (source: string): number => source .split (/\r\n|\n|\r/) .map (line => line.trim ()) .filter (line => line !== '') .length export const validateImportSource = ( source: string, ): PostImportSourceIssue[] => { const lines = source.split (/\r\n|\n|\r/) const issues: PostImportSourceIssue[] = [] const seen = new Map () let count = 0 lines.forEach ((rawLine, index) => { const value = rawLine.trim () if (!(value)) return ++count const sourceRow = index + 1 const displayUrl = truncateUrl (value) const normalised = normaliseImportUrl (value) if (count > MAX_ROWS) { issues.push ({ sourceRow, message: `取込件数は ${ MAX_ROWS } 件までです.`, url: displayUrl }) return } if (!(value.startsWith ('http://') || value.startsWith ('https://'))) { issues.push ({ sourceRow, message: 'HTTP または HTTPS の URL ではありません.', url: displayUrl }) return } if (bytesize (value) > MAX_URL_BYTES) { issues.push ({ sourceRow, message: 'URL が長すぎます.', url: displayUrl }) return } if (normalised == null) { issues.push ({ sourceRow, message: 'URL の形式が不正です.', url: displayUrl }) return } const duplicateRow = seen.get (normalised) if (duplicateRow != null) { issues.push ({ sourceRow, message: `${ duplicateRow } 行目と同じ URL です.`, url: displayUrl }) return } seen.set (normalised, sourceRow) }) return issues }