type OfflineContent = { title: string; kind: string; sourcePath: string; rows: [string, string][]; sections: { title: string; text: string }[]; notice: string; }; export const offlineValue = (value?: string | null) => value?.trim() || 'Não indicado'; const escapeHtml = (value: string) => value.replace( /[&<>"']/g, (character) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''', })[character]!, ); /** All content is plain text; assets and executable content are excluded. */ export function publicRecordOfflineHtml( content: OfflineContent, origin: string, savedAt: Date, ): string { const base = new URL(origin); if (!['http:', 'https:'].includes(base.protocol)) throw new Error('Origem do portal inválida.'); const source = new URL(content.sourcePath, base.origin); if ( source.origin !== base.origin || !content.sourcePath.startsWith('/') || content.sourcePath.startsWith('//') ) throw new Error('Referência da ficha inválida.'); return `${escapeHtml(content.title)} — consulta offline

${escapeHtml(content.title)}

${escapeHtml(content.kind)} — InvesteMoz

Cópia guardada em ${escapeHtml(savedAt.toISOString())}. Consulte a ficha online para verificar actualizações.

${content.rows.map(([label, value]) => `
${escapeHtml(label)}
${escapeHtml(value)}
`).join('')}
${content.sections.map((section) => `

${escapeHtml(section.title)}

${escapeHtml(section.text)}

`).join('')}

${escapeHtml(content.notice)}

Consultar a ficha de origem online

`; } export function downloadOfflineHtml( html: string, prefix: string, slug: string, ): void { const url = URL.createObjectURL( new Blob([html], { type: 'text/html;charset=utf-8' }), ); const link = document.createElement('a'); link.href = url; link.download = `${prefix}-${slug}`.replace(/[^a-zA-Z0-9_-]/g, '-').slice(0, 120) + '.html'; try { document.body.appendChild(link); link.click(); } finally { link.remove(); window.setTimeout(() => URL.revokeObjectURL(url), 1000); } }