export type StoredFileAttachment = { /** Display filename */ name: string /** Full URL when known (opens in new tab) */ url: string } function safeDecodeUriComponent(value: string): string { try { return decodeURIComponent(value) } catch { return value } } function displayNameFromUploadUrl(url: string): string { try { const pathname = new URL(url).pathname const base = pathname.split('/').pop() || url return safeDecodeUriComponent(base) } catch { const parts = url.split('/').filter(Boolean) const last = parts[parts.length - 1] return safeDecodeUriComponent(last || url) } } /** Submission storage: single URL or JSON-encoded array (`SubmissionUploadService`). */ export function parseStoredFileUploadUrls(raw: unknown): StoredFileAttachment[] { if (raw === null || raw === undefined) return [] const s = typeof raw === 'string' ? raw.trim() : '' if (s === '') return [] const fromUrlCandidate = (value: unknown): StoredFileAttachment | null => { const url = typeof value === 'string' ? value.trim() : '' if (!url) return null const absolute = /^https?:\/\//i.test(url) const relative = url.startsWith('/') && !/\s/.test(url) if (!(absolute || relative)) return null return { url, name: displayNameFromUploadUrl(url) } } if (s.startsWith('[')) { try { const parsed = JSON.parse(s) if (Array.isArray(parsed)) { const out: StoredFileAttachment[] = [] for (const item of parsed) { const att = fromUrlCandidate(item) if (att) out.push(att) } return out } } catch { /* treat as plain string below */ } } const single = fromUrlCandidate(s) return single ? [single] : [] }