jsonresume-theme-academic 1.3.1 → 1.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.umd.cjs","sources":["../src/constants.ts","../src/utils/escape.ts","../src/utils/dates.ts","../src/utils/icons.ts","../src/utils/sanitize.ts","../src/utils/text.ts","../src/sections/header.ts","../src/sections/shared.ts","../src/sections/summary.ts","../src/sections/skills.ts","../src/sections/work.ts","../src/sections/projects.ts","../src/sections/education.ts","../src/sections/volunteer.ts","../src/sections/certificates.ts","../src/sections/additional.ts","../src/render.ts","../src/index.ts"],"sourcesContent":["export const MONTHS: readonly string[] = [\n 'January',\n 'February',\n 'March',\n 'April',\n 'May',\n 'June',\n 'July',\n 'August',\n 'September',\n 'October',\n 'November',\n 'December',\n];\n\nexport const COUNTRY_NAMES: Readonly<Record<string, string>> = {\n GB: 'United Kingdom',\n US: 'United States',\n CA: 'Canada',\n AU: 'Australia',\n DE: 'Germany',\n FR: 'France',\n IN: 'India',\n JP: 'Japan',\n CN: 'China',\n BR: 'Brazil',\n NL: 'Netherlands',\n IT: 'Italy',\n ES: 'Spain',\n SE: 'Sweden',\n CH: 'Switzerland',\n SG: 'Singapore',\n NZ: 'New Zealand',\n IE: 'Ireland',\n AE: 'UAE',\n};\n\nexport const DEFAULT_HEADINGS: Readonly<Record<string, string>> = {\n summary: 'Summary',\n skills: 'Core Skills',\n experience: 'Experience',\n projects: 'Selected Projects',\n education: 'Education',\n volunteer: 'Leadership & Volunteering',\n certifications: 'Credentials',\n additional: 'Additional',\n};\n","/** Decode common HTML entities to literal characters. */\nexport function decodeEntities(text: string): string {\n return text\n .replace(/&#(\\d+);/g, (_, c) => String.fromCharCode(Number(c)))\n .replace(/&#x([0-9a-fA-F]+);/g, (_, h) => String.fromCharCode(parseInt(h, 16)))\n .replace(/&amp;/g, '&')\n .replace(/&lt;/g, '<')\n .replace(/&gt;/g, '>')\n .replace(/&quot;/g, '\"')\n .replace(/&#39;/g, \"'\")\n .replace(/&apos;/g, \"'\")\n .replace(/&nbsp;/g, ' ')\n .replace(/&mdash;/g, '\\u2014')\n .replace(/&ndash;/g, '\\u2013')\n .replace(/&hellip;/g, '\\u2026');\n}\n\n/** HTML-escape for safe output (plain text fields like names, dates). */\nexport function esc(text: string | null | undefined): string {\n if (text == null) return '';\n return String(text)\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;');\n}\n","import { MONTHS, COUNTRY_NAMES } from '../constants.js';\n\nexport function formatDate(dateStr: string | null | undefined): string {\n if (!dateStr) return 'Present';\n if (dateStr.toLowerCase() === 'present') return 'Present';\n const parts = String(dateStr).split('-');\n if (parts.length === 1) return parts[0];\n const year = parts[0];\n const month = MONTHS[parseInt(parts[1], 10) - 1] || '';\n return `${month} ${year}`;\n}\n\nexport function dateRange(start: string | null | undefined, end: string | null | undefined): string {\n const s = formatDate(start);\n const e = formatDate(end);\n if (!s || s === 'Present') return ''; // start date should not be empty or present\n if (!e) return s;\n return `${s} to ${e}`;\n}\n\nexport function regionName(code: string | null | undefined): string {\n if (!code) return '';\n return COUNTRY_NAMES[code.toUpperCase()] || code;\n}\n","export function profileIcon(network: string | null | undefined): string {\n if (!network) return '<i class=\"fa-solid fa-link\"></i>';\n const n = network.toLowerCase();\n if (n === 'linkedin') return '<i class=\"fa-brands fa-linkedin-in\"></i>';\n if (n === 'github') return '<i class=\"fa-brands fa-github\"></i>';\n if (n === 'twitter' || n === 'x') return '<i class=\"fa-brands fa-x-twitter\"></i>';\n if (n === 'stackoverflow') return '<i class=\"fa-brands fa-stack-overflow\"></i>';\n return '<i class=\"fa-solid fa-link\"></i>';\n}\n","/** Tags allowed through sanitization — safe inline/block formatting. */\nconst BLOCK_TAGS = new Set(['p', 'ul', 'ol', 'li', 'blockquote']);\nconst INLINE_TAGS = new Set([\n 'br', 'b', 'strong', 'i', 'em', 'u', 's', 'mark', 'a', 'sub', 'sup', 'code',\n]);\nconst ALLOWED_TAGS = new Set([...BLOCK_TAGS, ...INLINE_TAGS]);\n\n/** URL schemes safe for <a href>. Everything else (javascript:, data:, vbscript:) is stripped. */\nconst SAFE_URL_SCHEMES = /^(?:https?|mailto|tel):/i;\nconst RELATIVE_URL = /^[#/?.]/;\n\nfunction isSafeUrl(href: string): boolean {\n const trimmed = href.trim();\n if (!trimmed) return false;\n return SAFE_URL_SCHEMES.test(trimmed) || RELATIVE_URL.test(trimmed);\n}\n\ninterface SanitizeOptions {\n readonly inline?: boolean;\n}\n\n/**\n * Sanitize HTML — keep allowed formatting tags, strip everything else.\n * Attributes are stripped from all tags except <a href> (safe schemes only) and <mark class>.\n * If inline=true, block tags (p, ul, ol, li, blockquote) are also stripped.\n */\nexport function sanitizeHtml(html: string | null | undefined, { inline = false }: SanitizeOptions = {}): string {\n if (!html) return '';\n const allowed = inline ? INLINE_TAGS : ALLOWED_TAGS;\n\n return String(html)\n .replace(/<a\\s+[^>]*href=\"([^\"]*)\"[^>]*>/gi, (_, href: string) => {\n if (!isSafeUrl(href)) return '';\n return `<a href=\"${href.replace(/\"/g, '&quot;')}\" target=\"_blank\" rel=\"noopener\">`;\n })\n .replace(/<mark\\s+[^>]*class=\"([^\"]*)\"[^>]*>/gi, (_, cls: string) =>\n `<mark class=\"${cls.replace(/\"/g, '&quot;')}\">`,\n )\n .replace(/<(\\/?)([a-z][a-z0-9]*)\\s*[^>]*?(\\/?)>/gi, (match, slash: string, tag: string, selfClose: string) => {\n const lower = tag.toLowerCase();\n if (lower === 'a' && !slash && match.includes('href=')) return match;\n if (lower === 'mark' && !slash && match.includes('class=')) return match;\n if (allowed.has(lower)) return `<${slash}${lower}${selfClose}>`;\n if (inline && BLOCK_TAGS.has(lower)) return slash ? ' ' : '';\n return '';\n })\n .replace(/<p>\\s*<\\/p>/gi, '')\n .replace(/\\s{2,}/g, ' ')\n .trim();\n}\n","import { decodeEntities, esc } from './escape.js';\nimport { sanitizeHtml } from './sanitize.js';\n\n/** Strip HTML tags → plain text (for fields parsed as data, not displayed). */\nexport function stripHtml(text: string | null | undefined): string {\n if (!text) return '';\n return decodeEntities(\n String(text)\n .replace(/<br\\s*\\/?>/gi, ' ')\n .replace(/<\\/?(p|div|li)[^>]*>/gi, ' ')\n .replace(/<[^>]+>/g, ''),\n )\n .replace(/\\s{2,}/g, ' ')\n .trim();\n}\n\nconst isHtml = (text: string): boolean => /<[a-z][\\s\\S]*>/i.test(text);\nconst hasEntities = (text: string): boolean => /&(?:amp|lt|gt|quot|#\\d+|#x[0-9a-f]+);/i.test(text);\n\n/**\n * Render user-provided rich text → safe HTML output.\n * block=true: preserve <p>, <ul>, <ol> etc.\n * block=false (default): inline mode — strip block tags, keep only inline formatting.\n */\nexport function richText(text: string | null | undefined, { block = false }: { block?: boolean } = {}): string {\n if (text == null) return '';\n const s = String(text);\n if (isHtml(s)) return sanitizeHtml(s, { inline: !block });\n if (hasEntities(s)) return esc(decodeEntities(s));\n return esc(s);\n}\n\n/** Check if an array is non-empty. */\nexport function has<T>(arr: readonly T[] | null | undefined): arr is readonly T[] & { length: number } {\n return Array.isArray(arr) && arr.length > 0;\n}\n","import type { ResumeBasics } from '../types/resume.js';\nimport { esc } from '../utils/escape.js';\nimport { regionName } from '../utils/dates.js';\nimport { profileIcon } from '../utils/icons.js';\nimport { has } from '../utils/text.js';\n\ninterface ContactLine {\n readonly icon: string;\n readonly text: string;\n}\n\nexport function renderHeader(basics: ResumeBasics | undefined): string {\n if (!basics) return '';\n\n const location = basics.location\n ? [basics.location.city, regionName(basics.location.region)].filter(Boolean).join(', ')\n : '';\n\n const lines: ContactLine[] = [];\n if (location)\n lines.push({ icon: '<i class=\"fa-solid fa-location-dot\"></i>', text: location });\n if (basics.phone)\n lines.push({ icon: '<i class=\"fa-solid fa-square-phone\"></i>', text: basics.phone });\n if (basics.email)\n lines.push({ icon: '<i class=\"fa-solid fa-envelope\"></i>', text: basics.email });\n if (basics.url) {\n const display = basics.url.replace(/^https?:\\/\\//, '');\n lines.push({ icon: '<i class=\"fa-solid fa-globe\"></i>', text: display });\n }\n if (has(basics.profiles)) {\n for (const p of basics.profiles) {\n lines.push({ icon: profileIcon(p.network), text: p.username || p.url || '' });\n }\n }\n\n return `\n <header class=\"header\">\n <div class=\"header-left\">\n <h1 class=\"name\">${esc(basics.name)}</h1>\n <div class=\"label\">${esc(basics.label)}</div>\n </div>\n <div class=\"contact-info\">\n ${lines.map((l) => `<div class=\"contact-line\">${esc(l.text)} ${l.icon}</div>`).join('\\n ')}\n </div>\n </header>\n <hr class=\"header-rule\" />`;\n}\n","import { esc } from '../utils/escape.js';\n\nexport function sectionTitle(title: string): string {\n return `<h2 class=\"section-title\">${esc(title)}</h2>`;\n}\n","import { richText } from '../utils/text.js';\nimport { sectionTitle } from './shared.js';\n\nexport function renderSummary(summary: string | undefined, heading: string): string {\n if (!summary) return '';\n const html = richText(summary, { block: true });\n if (!html) return '';\n return `\n ${sectionTitle(heading)}\n <div class=\"summary\">${html}</div>`;\n}\n","import type { ResumeSkill } from '../types/resume.js';\nimport { esc } from '../utils/escape.js';\nimport { has } from '../utils/text.js';\nimport { sectionTitle } from './shared.js';\n\nexport function renderSkills(skills: readonly ResumeSkill[] | undefined, heading: string): string {\n if (!has(skills)) return '';\n return `\n ${sectionTitle(heading)}\n <div class=\"section-body\">\n ${skills\n .map(\n (s) => `\n <div class=\"bullet-item\">\n <span class=\"skill-name\">${esc(s.name)}:</span> ${esc((s.keywords || []).join(', '))}\n </div>`,\n )\n .join('')}\n </div>`;\n}\n","import type { ResumeWorkEntry } from '../types/resume.js';\nimport { esc } from '../utils/escape.js';\nimport { dateRange } from '../utils/dates.js';\nimport { has, richText, stripHtml } from '../utils/text.js';\nimport { sectionTitle } from './shared.js';\n\n/** Check whether summary uses structured Tech-stack / Client format. */\nfunction isStructuredMeta(text: string): boolean {\n return /Tech-stack:\\s/i.test(text) || /Client:\\s/i.test(text);\n}\n\n/** Extract tech-stack, client, and any remaining narrative from a work summary. */\nfunction parseWorkMeta(summary: string | undefined): {\n techStack: string;\n client: string;\n narrative: string;\n} {\n if (!summary) return { techStack: '', client: '', narrative: '' };\n const text = stripHtml(summary).replace(/\\n/g, ' ').trim();\n if (!isStructuredMeta(text)) return { techStack: '', client: '', narrative: text };\n\n let remaining = text;\n\n const clientMatch = remaining.match(/Client:\\s*(.+)$/i);\n const client = clientMatch ? clientMatch[1].trim() : '';\n if (clientMatch) remaining = remaining.slice(0, clientMatch.index).trim();\n\n const techMatch = remaining.match(/Tech-stack:\\s*(.+)/i);\n const techStack = techMatch ? techMatch[1].trim() : '';\n if (techMatch) remaining = remaining.slice(0, techMatch.index).trim();\n\n return { techStack, client, narrative: remaining };\n}\n\nfunction renderWorkEntry(entry: ResumeWorkEntry): string {\n const { techStack, client, narrative } = parseWorkMeta(entry.summary);\n const duration = dateRange(entry.startDate, entry.endDate);\n\n return `\n <div class=\"work-entry\">\n <div class=\"work-header\">\n <div class=\"work-title\">${esc(entry.name)} - ${esc(entry.position)}</div>\n <div class=\"work-duration\">${duration ? `Duration: ${esc(duration)}` : ''}</div>\n </div>\n ${\n techStack || client\n ? `\n <div class=\"work-meta\">\n <div class=\"work-tech\">${techStack ? `Tech-stack: ${esc(techStack)}` : ''}</div>\n <div class=\"work-client\">${client ? `Client: ${esc(client)}` : ''}</div>\n </div>`\n : ''\n }\n ${narrative ? `<p class=\"work-summary\">${richText(narrative, { block: false })}</p>` : ''}\n ${\n has(entry.highlights)\n ? `\n <ul class=\"work-highlights\">\n ${entry.highlights.map((h) => `<li>${richText(h)}</li>`).join('\\n ')}\n </ul>`\n : ''\n }\n </div>`;\n}\n\nexport function renderWork(work: readonly ResumeWorkEntry[] | undefined, heading: string): string {\n if (!has(work)) return '';\n return `\n ${sectionTitle(heading)}\n <div class=\"section-body\">\n ${work.map(renderWorkEntry).join('')}\n </div>`;\n}\n","import type { ResumeProject } from '../types/resume.js';\nimport { esc } from '../utils/escape.js';\nimport { has, richText } from '../utils/text.js';\nimport { sectionTitle } from './shared.js';\n\nexport function renderProjects(\n projects: readonly ResumeProject[] | undefined,\n heading: string,\n): string {\n if (!has(projects)) return '';\n return `\n ${sectionTitle(heading)}\n <div class=\"section-body\">\n ${projects\n .map((p) => {\n const desc = richText(p.description);\n return `\n <div class=\"bullet-item\">\n <span class=\"project-name\">${esc(p.name)}:</span> ${desc}\n </div>`;\n })\n .join('')}\n </div>`;\n}\n","import type { ResumeEducationEntry } from '../types/resume.js';\nimport { esc } from '../utils/escape.js';\nimport { formatDate } from '../utils/dates.js';\nimport { has, richText } from '../utils/text.js';\nimport { sectionTitle } from './shared.js';\n\nfunction renderEducationEntry(entry: ResumeEducationEntry): string {\n const degree = [entry.studyType, entry.area].filter(Boolean).join(' ');\n\n let yearDisplay = '';\n if (entry.endDate) {\n yearDisplay = formatDate(entry.endDate);\n } else if (entry.startDate) {\n const year = parseInt(String(entry.startDate).split('-')[0], 10);\n const currentYear = new Date().getFullYear();\n yearDisplay = year >= currentYear ? `Expected ${formatDate(entry.startDate)}` : formatDate(entry.startDate);\n }\n\n const instParts = [entry.institution];\n if (entry.score) instParts.push(entry.score);\n const instLine = instParts.filter(Boolean).join(' | ');\n\n return `\n <div class=\"edu-entry\">\n <div class=\"edu-header\">\n <div class=\"edu-degree\">${esc(degree)}</div>\n <div class=\"edu-year\">${esc(yearDisplay)}</div>\n </div>\n ${entry.institution ? `<div class=\"edu-institution\">${esc(instLine)}</div>` : ''}\n ${\n has(entry.courses)\n ? `<div class=\"edu-courses\">Coursework: ${entry.courses.map((c) => richText(c)).join(', ')}</div>`\n : ''\n }\n </div>`;\n}\n\nexport function renderEducation(\n education: readonly ResumeEducationEntry[] | undefined,\n heading: string,\n): string {\n if (!has(education)) return '';\n return `\n ${sectionTitle(heading)}\n <div class=\"section-body\">\n ${education.map(renderEducationEntry).join('')}\n </div>`;\n}\n","import type { ResumeVolunteerEntry } from '../types/resume.js';\nimport { esc } from '../utils/escape.js';\nimport { has, richText } from '../utils/text.js';\nimport { sectionTitle } from './shared.js';\n\nexport function renderVolunteer(\n volunteer: readonly ResumeVolunteerEntry[] | undefined,\n heading: string,\n): string {\n if (!has(volunteer)) return '';\n return `\n ${sectionTitle(heading)}\n <div class=\"section-body\">\n ${volunteer\n .map((v) => {\n const years = v.endDate\n ? `${v.startDate}\\u2013${v.endDate}`\n : v.startDate || '';\n const title = v.position\n ? `${v.position} \\u2013 ${v.organization}`\n : v.organization;\n const summary = richText(v.summary);\n\n return `\n <div class=\"bullet-item volunteer-item\">\n <span class=\"vol-title\">${esc(title)}${years ? ` (${esc(years)})` : ''}:</span> ${summary}\n </div>`;\n })\n .join('')}\n </div>`;\n}\n","import type { ResumeCertificate } from '../types/resume.js';\nimport { has, richText } from '../utils/text.js';\nimport { sectionTitle } from './shared.js';\n\nexport function renderCertificates(\n certificates: readonly ResumeCertificate[] | undefined,\n heading: string,\n): string {\n if (!has(certificates)) return '';\n return `\n ${sectionTitle(heading)}\n <div class=\"section-body\">\n ${certificates\n .map(\n (c) => `\n <div class=\"bullet-item\">${richText(c.name)}</div>`,\n )\n .join('')}\n </div>`;\n}\n","import type { ResumeSchema } from '../types/resume.js';\nimport { esc } from '../utils/escape.js';\nimport { has } from '../utils/text.js';\nimport { sectionTitle } from './shared.js';\n\nexport function renderAdditional(resume: ResumeSchema, heading: string): string {\n const parts: string[] = [];\n\n if (has(resume.interests)) {\n for (const interest of resume.interests) {\n const keywords = has(interest.keywords) ? interest.keywords.join(', ') : '';\n const label = interest.name || 'Interests';\n if (keywords) {\n parts.push(\n `<div class=\"additional-line\"><span class=\"additional-label\">${esc(label)}:</span> ${esc(keywords)}</div>`,\n );\n }\n }\n }\n\n if (has(resume.languages)) {\n const langStr = resume.languages\n .map((l) => `${l.language}${l.fluency ? ` (${l.fluency})` : ''}`)\n .join(', ');\n parts.push(\n `<div class=\"additional-line\"><span class=\"additional-label\">Languages:</span> ${esc(langStr)}</div>`,\n );\n }\n\n if (has(resume.references)) {\n parts.push(\n `<div class=\"additional-line\"><span class=\"additional-label\">Referees:</span> Available on request</div>`,\n );\n }\n\n if (parts.length === 0) return '';\n\n return `\n ${sectionTitle(heading)}\n <div class=\"section-body\">\n ${parts.join('\\n ')}\n </div>`;\n}\n","import type { ResumeSchema } from './types/resume.js';\nimport { DEFAULT_HEADINGS } from './constants.js';\nimport { esc } from './utils/escape.js';\nimport { renderHeader } from './sections/header.js';\nimport { renderSummary } from './sections/summary.js';\nimport { renderSkills } from './sections/skills.js';\nimport { renderWork } from './sections/work.js';\nimport { renderProjects } from './sections/projects.js';\nimport { renderEducation } from './sections/education.js';\nimport { renderVolunteer } from './sections/volunteer.js';\nimport { renderCertificates } from './sections/certificates.js';\nimport { renderAdditional } from './sections/additional.js';\nimport css from './styles/academic.css?inline';\n\nexport function render(resume: ResumeSchema): string {\n const h = { ...DEFAULT_HEADINGS };\n\n const metaHeadings = resume?.meta?.headings;\n if (metaHeadings) {\n for (const [key, value] of Object.entries(metaHeadings)) {\n if (value && key in h) {\n h[key] = value;\n }\n }\n }\n\n const basics = resume?.basics || {};\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>${esc(basics.name || 'Resume')}</title>\n <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\" />\n <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin />\n <link href=\"https://fonts.googleapis.com/css2?family=EB+Garamond:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500&display=swap\" rel=\"stylesheet\" />\n <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css\" />\n <style>${css}</style>\n</head>\n<body>\n <div class=\"resume\">\n ${renderHeader(basics)}\n ${renderSummary(basics.summary, h.summary)}\n ${renderSkills(resume.skills, h.skills)}\n ${renderWork(resume.work, h.experience)}\n ${renderProjects(resume.projects, h.projects)}\n ${renderEducation(resume.education, h.education)}\n ${renderVolunteer(resume.volunteer, h.volunteer)}\n ${renderCertificates(resume.certificates, h.certifications)}\n ${renderAdditional(resume, h.additional)}\n </div>\n</body>\n</html>`;\n}\n","export { render } from './render.js';\nexport type { ResumeSchema } from './types/resume.js';\n\n/** PDF rendering options for Puppeteer/Gotenberg. */\nexport const pdfRenderOptions = {\n mediaType: 'print' as const,\n format: 'A4' as const,\n margin: {\n top: '12mm',\n right: '14mm',\n bottom: '12mm',\n left: '14mm',\n },\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.umd.cjs","sources":["../src/constants.ts","../src/utils/escape.ts","../src/utils/dates.ts","../src/utils/icons.ts","../src/utils/sanitize.ts","../src/utils/text.ts","../src/sections/header.ts","../src/sections/shared.ts","../src/sections/summary.ts","../src/sections/skills.ts","../src/sections/work.ts","../src/sections/projects.ts","../src/sections/education.ts","../src/sections/volunteer.ts","../src/sections/certificates.ts","../src/sections/additional.ts","../src/render.ts","../src/index.ts"],"sourcesContent":["export const MONTHS: readonly string[] = [\r\n 'January',\r\n 'February',\r\n 'March',\r\n 'April',\r\n 'May',\r\n 'June',\r\n 'July',\r\n 'August',\r\n 'September',\r\n 'October',\r\n 'November',\r\n 'December',\r\n];\r\n\r\nexport const COUNTRY_NAMES: Readonly<Record<string, string>> = {\r\n GB: 'United Kingdom',\r\n US: 'United States',\r\n CA: 'Canada',\r\n AU: 'Australia',\r\n DE: 'Germany',\r\n FR: 'France',\r\n IN: 'India',\r\n JP: 'Japan',\r\n CN: 'China',\r\n BR: 'Brazil',\r\n NL: 'Netherlands',\r\n IT: 'Italy',\r\n ES: 'Spain',\r\n SE: 'Sweden',\r\n CH: 'Switzerland',\r\n SG: 'Singapore',\r\n NZ: 'New Zealand',\r\n IE: 'Ireland',\r\n AE: 'UAE',\r\n};\r\n\r\nexport const DEFAULT_HEADINGS: Readonly<Record<string, string>> = {\r\n summary: 'Summary',\r\n skills: 'Core Skills',\r\n experience: 'Experience',\r\n projects: 'Selected Projects',\r\n education: 'Education',\r\n volunteer: 'Leadership & Volunteering',\r\n certifications: 'Credentials',\r\n additional: 'Additional',\r\n};\r\n","/** Decode common HTML entities to literal characters. */\nexport function decodeEntities(text: string): string {\n return text\n .replace(/&#(\\d+);/g, (_, c) => String.fromCharCode(Number(c)))\n .replace(/&#x([0-9a-fA-F]+);/g, (_, h) => String.fromCharCode(parseInt(h, 16)))\n .replace(/&amp;/g, '&')\n .replace(/&lt;/g, '<')\n .replace(/&gt;/g, '>')\n .replace(/&quot;/g, '\"')\n .replace(/&#39;/g, \"'\")\n .replace(/&apos;/g, \"'\")\n .replace(/&nbsp;/g, ' ')\n .replace(/&mdash;/g, '\\u2014')\n .replace(/&ndash;/g, '\\u2013')\n .replace(/&hellip;/g, '\\u2026');\n}\n\n/** HTML-escape for safe output (plain text fields like names, dates). */\nexport function esc(text: string | null | undefined): string {\n if (text == null) return '';\n return String(text)\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;');\n}\n","import { MONTHS, COUNTRY_NAMES } from '../constants.js';\n\nexport function formatDate(dateStr: string | null | undefined): string {\n if (!dateStr) return 'Present';\n if (dateStr.toLowerCase() === 'present') return 'Present';\n const parts = String(dateStr).split('-');\n if (parts.length === 1) return parts[0];\n const year = parts[0];\n const month = MONTHS[parseInt(parts[1], 10) - 1] || '';\n return `${month} ${year}`;\n}\n\nexport function dateRange(start: string | null | undefined, end: string | null | undefined): string {\n const s = formatDate(start);\n const e = formatDate(end);\n if (!s || s === 'Present') return ''; // start date should not be empty or present\n if (!e) return s;\n return `${s} to ${e}`;\n}\n\nexport function regionName(code: string | null | undefined): string {\n if (!code) return '';\n return COUNTRY_NAMES[code.toUpperCase()] || code;\n}\n","export function profileIcon(network: string | null | undefined): string {\n if (!network) return '<i class=\"fa-solid fa-link\"></i>';\n const n = network.toLowerCase();\n if (n === 'linkedin') return '<i class=\"fa-brands fa-linkedin-in\"></i>';\n if (n === 'github') return '<i class=\"fa-brands fa-github\"></i>';\n if (n === 'twitter' || n === 'x') return '<i class=\"fa-brands fa-x-twitter\"></i>';\n if (n === 'stackoverflow') return '<i class=\"fa-brands fa-stack-overflow\"></i>';\n return '<i class=\"fa-solid fa-link\"></i>';\n}\n","/** Tags allowed through sanitization — safe inline/block formatting. */\nconst BLOCK_TAGS = new Set(['p', 'ul', 'ol', 'li', 'blockquote']);\nconst INLINE_TAGS = new Set([\n 'br', 'b', 'strong', 'i', 'em', 'u', 's', 'mark', 'a', 'sub', 'sup', 'code',\n]);\nconst ALLOWED_TAGS = new Set([...BLOCK_TAGS, ...INLINE_TAGS]);\n\n/** URL schemes safe for <a href>. Everything else (javascript:, data:, vbscript:) is stripped. */\nconst SAFE_URL_SCHEMES = /^(?:https?|mailto|tel):/i;\nconst RELATIVE_URL = /^[#/?.]/;\n\nfunction isSafeUrl(href: string): boolean {\n const trimmed = href.trim();\n if (!trimmed) return false;\n return SAFE_URL_SCHEMES.test(trimmed) || RELATIVE_URL.test(trimmed);\n}\n\ninterface SanitizeOptions {\n readonly inline?: boolean;\n}\n\n/**\n * Sanitize HTML — keep allowed formatting tags, strip everything else.\n * Attributes are stripped from all tags except <a href> (safe schemes only) and <mark class>.\n * If inline=true, block tags (p, ul, ol, li, blockquote) are also stripped.\n */\nexport function sanitizeHtml(html: string | null | undefined, { inline = false }: SanitizeOptions = {}): string {\n if (!html) return '';\n const allowed = inline ? INLINE_TAGS : ALLOWED_TAGS;\n\n return String(html)\n .replace(/<a\\s+[^>]*href=\"([^\"]*)\"[^>]*>/gi, (_, href: string) => {\n if (!isSafeUrl(href)) return '';\n return `<a href=\"${href.replace(/\"/g, '&quot;')}\" target=\"_blank\" rel=\"noopener\">`;\n })\n .replace(/<mark\\s+[^>]*class=\"([^\"]*)\"[^>]*>/gi, (_, cls: string) =>\n `<mark class=\"${cls.replace(/\"/g, '&quot;')}\">`,\n )\n .replace(/<(\\/?)([a-z][a-z0-9]*)\\s*[^>]*?(\\/?)>/gi, (match, slash: string, tag: string, selfClose: string) => {\n const lower = tag.toLowerCase();\n if (lower === 'a' && !slash && match.includes('href=')) return match;\n if (lower === 'mark' && !slash && match.includes('class=')) return match;\n if (allowed.has(lower)) return `<${slash}${lower}${selfClose}>`;\n if (inline && BLOCK_TAGS.has(lower)) return slash ? ' ' : '';\n return '';\n })\n .replace(/<p>\\s*<\\/p>/gi, '')\n .replace(/\\s{2,}/g, ' ')\n .trim();\n}\n","import { decodeEntities, esc } from './escape.js';\nimport { sanitizeHtml } from './sanitize.js';\n\n/** Strip HTML tags → plain text (for fields parsed as data, not displayed). */\nexport function stripHtml(text: string | null | undefined): string {\n if (!text) return '';\n return decodeEntities(\n String(text)\n .replace(/<br\\s*\\/?>/gi, ' ')\n .replace(/<\\/?(p|div|li)[^>]*>/gi, ' ')\n .replace(/<[^>]+>/g, ''),\n )\n .replace(/\\s{2,}/g, ' ')\n .trim();\n}\n\nconst isHtml = (text: string): boolean => /<[a-z][\\s\\S]*>/i.test(text);\nconst hasEntities = (text: string): boolean => /&(?:amp|lt|gt|quot|#\\d+|#x[0-9a-f]+);/i.test(text);\n\n/**\n * Render user-provided rich text → safe HTML output.\n * block=true: preserve <p>, <ul>, <ol> etc.\n * block=false (default): inline mode — strip block tags, keep only inline formatting.\n */\nexport function richText(text: string | null | undefined, { block = false }: { block?: boolean } = {}): string {\n if (text == null) return '';\n const s = String(text);\n if (isHtml(s)) return sanitizeHtml(s, { inline: !block });\n if (hasEntities(s)) return esc(decodeEntities(s));\n return esc(s);\n}\n\n/** Check if an array is non-empty. */\nexport function has<T>(arr: readonly T[] | null | undefined): arr is readonly T[] & { length: number } {\n return Array.isArray(arr) && arr.length > 0;\n}\n","import type { ResumeBasics } from '../types/resume.js';\nimport { esc } from '../utils/escape.js';\nimport { regionName } from '../utils/dates.js';\nimport { profileIcon } from '../utils/icons.js';\nimport { has } from '../utils/text.js';\n\ninterface ContactLine {\n readonly icon: string;\n readonly text: string;\n}\n\nexport function renderHeader(basics: ResumeBasics | undefined): string {\n if (!basics) return '';\n\n const location = basics.location\n ? [basics.location.city, regionName(basics.location.region)].filter(Boolean).join(', ')\n : '';\n\n const lines: ContactLine[] = [];\n if (location)\n lines.push({ icon: '<i class=\"fa-solid fa-location-dot\"></i>', text: location });\n if (basics.phone)\n lines.push({ icon: '<i class=\"fa-solid fa-square-phone\"></i>', text: basics.phone });\n if (basics.email)\n lines.push({ icon: '<i class=\"fa-solid fa-envelope\"></i>', text: basics.email });\n if (basics.url) {\n const display = basics.url.replace(/^https?:\\/\\//, '');\n lines.push({ icon: '<i class=\"fa-solid fa-globe\"></i>', text: display });\n }\n if (has(basics.profiles)) {\n for (const p of basics.profiles) {\n lines.push({ icon: profileIcon(p.network), text: p.username || p.url || '' });\n }\n }\n\n return `\n <header class=\"header\">\n <div class=\"header-left\">\n <h1 class=\"name\">${esc(basics.name)}</h1>\n <div class=\"label\">${esc(basics.label)}</div>\n </div>\n <div class=\"contact-info\">\n ${lines.map((l) => `<div class=\"contact-line\">${esc(l.text)} ${l.icon}</div>`).join('\\n ')}\n </div>\n </header>\n <hr class=\"header-rule\" />`;\n}\n","import { esc } from '../utils/escape.js';\n\nexport function sectionTitle(title: string): string {\n return `<h2 class=\"section-title\">${esc(title)}</h2>`;\n}\n","import { richText } from '../utils/text.js';\r\nimport { sectionTitle } from './shared.js';\r\n\r\nexport function renderSummary(summary: string | undefined, heading: string): string {\r\n if (!summary) return '';\r\n const html = richText(summary, { block: true });\r\n if (!html) return '';\r\n return `\r\n ${sectionTitle(heading)}\r\n <div class=\"summary\">${html}</div>`;\r\n}\r\n","import type { ResumeSkill } from '../types/resume.js';\nimport { esc } from '../utils/escape.js';\nimport { has } from '../utils/text.js';\nimport { sectionTitle } from './shared.js';\n\nexport function renderSkills(skills: readonly ResumeSkill[] | undefined, heading: string): string {\n if (!has(skills)) return '';\n return `\n ${sectionTitle(heading)}\n <div class=\"section-body\">\n ${skills\n .map(\n (s) => `\n <div class=\"bullet-item\">\n <span class=\"skill-name\">${esc(s.name)}:</span> ${esc((s.keywords || []).join(', '))}\n </div>`,\n )\n .join('')}\n </div>`;\n}\n","import type { ResumeWorkEntry } from '../types/resume.js';\r\nimport { esc } from '../utils/escape.js';\r\nimport { dateRange } from '../utils/dates.js';\r\nimport { has, richText, stripHtml } from '../utils/text.js';\r\nimport { sectionTitle } from './shared.js';\r\n\r\n/** Check whether summary uses structured Tech-stack / Client format. */\r\nfunction isStructuredMeta(text: string): boolean {\r\n return /Tech-stack:\\s/i.test(text) || /Client:\\s/i.test(text);\r\n}\r\n\r\n/** Extract tech-stack, client, and any remaining narrative from a work summary. */\r\nfunction parseWorkMeta(summary: string | undefined): {\r\n techStack: string;\r\n client: string;\r\n narrative: string;\r\n} {\r\n if (!summary) return { techStack: '', client: '', narrative: '' };\r\n const text = stripHtml(summary).replace(/\\n/g, ' ').trim();\r\n if (!isStructuredMeta(text)) return { techStack: '', client: '', narrative: text };\r\n\r\n let remaining = text;\r\n\r\n const clientMatch = remaining.match(/Client:\\s*(.+)$/i);\r\n const client = clientMatch ? clientMatch[1].trim() : '';\r\n if (clientMatch) remaining = remaining.slice(0, clientMatch.index).trim();\r\n\r\n const techMatch = remaining.match(/Tech-stack:\\s*(.+)/i);\r\n const techStack = techMatch ? techMatch[1].trim() : '';\r\n if (techMatch) remaining = remaining.slice(0, techMatch.index).trim();\r\n\r\n return { techStack, client, narrative: remaining };\r\n}\r\n\r\nfunction renderWorkEntry(entry: ResumeWorkEntry): string {\r\n const { techStack, client, narrative } = parseWorkMeta(entry.summary);\r\n const duration = dateRange(entry.startDate, entry.endDate);\r\n\r\n return `\r\n <div class=\"work-entry\">\r\n <div class=\"work-header\">\r\n <div class=\"work-title\">${esc(entry.name)} - ${esc(entry.position)}</div>\r\n <div class=\"work-duration\">${duration ? `Duration: ${esc(duration)}` : ''}</div>\r\n </div>\r\n ${\r\n techStack || client\r\n ? `\r\n <div class=\"work-meta\">\r\n <div class=\"work-tech\">${techStack ? `Tech-stack: ${esc(techStack)}` : ''}</div>\r\n <div class=\"work-client\">${client ? `Client: ${esc(client)}` : ''}</div>\r\n </div>`\r\n : ''\r\n }\r\n ${narrative ? `<p class=\"work-summary\">${richText(narrative, { block: false })}</p>` : ''}\r\n ${\r\n has(entry.highlights)\r\n ? `\r\n <ul class=\"work-highlights\">\r\n ${entry.highlights.map((h) => `<li>${richText(h)}</li>`).join('\\n ')}\r\n </ul>`\r\n : ''\r\n }\r\n </div>`;\r\n}\r\n\r\nexport function renderWork(work: readonly ResumeWorkEntry[] | undefined, heading: string): string {\r\n if (!has(work)) return '';\r\n return `\r\n ${sectionTitle(heading)}\r\n <div class=\"section-body\">\r\n ${work.map(renderWorkEntry).join('')}\r\n </div>`;\r\n}\r\n","import type { ResumeProject } from '../types/resume.js';\nimport { esc } from '../utils/escape.js';\nimport { has, richText } from '../utils/text.js';\nimport { sectionTitle } from './shared.js';\n\nexport function renderProjects(\n projects: readonly ResumeProject[] | undefined,\n heading: string,\n): string {\n if (!has(projects)) return '';\n return `\n ${sectionTitle(heading)}\n <div class=\"section-body\">\n ${projects\n .map((p) => {\n const desc = richText(p.description);\n return `\n <div class=\"bullet-item\">\n <span class=\"project-name\">${esc(p.name)}:</span> ${desc}\n </div>`;\n })\n .join('')}\n </div>`;\n}\n","import type { ResumeEducationEntry } from '../types/resume.js';\r\nimport { esc } from '../utils/escape.js';\r\nimport { formatDate } from '../utils/dates.js';\r\nimport { has, richText } from '../utils/text.js';\r\nimport { sectionTitle } from './shared.js';\r\n\r\nfunction renderEducationEntry(entry: ResumeEducationEntry): string {\r\n const degree = [entry.studyType, entry.area].filter(Boolean).join(' ');\r\n\r\n let yearDisplay = '';\r\n if (entry.endDate) {\r\n yearDisplay = formatDate(entry.endDate);\r\n } else if (entry.startDate) {\r\n const year = parseInt(String(entry.startDate).split('-')[0], 10);\r\n const currentYear = new Date().getFullYear();\r\n yearDisplay = year >= currentYear ? `Expected ${formatDate(entry.startDate)}` : formatDate(entry.startDate);\r\n }\r\n\r\n const instParts = [entry.institution];\r\n if (entry.score) instParts.push(entry.score);\r\n const instLine = instParts.filter(Boolean).join(' | ');\r\n\r\n return `\r\n <div class=\"edu-entry\">\r\n <div class=\"edu-header\">\r\n <div class=\"edu-degree\">${esc(degree)}</div>\r\n <div class=\"edu-year\">${esc(yearDisplay)}</div>\r\n </div>\r\n ${entry.institution ? `<div class=\"edu-institution\">${esc(instLine)}</div>` : ''}\r\n ${\r\n has(entry.courses)\r\n ? `<div class=\"edu-courses\">Coursework: ${entry.courses.map((c) => richText(c)).join(', ')}</div>`\r\n : ''\r\n }\r\n </div>`;\r\n}\r\n\r\nexport function renderEducation(\r\n education: readonly ResumeEducationEntry[] | undefined,\r\n heading: string,\r\n): string {\r\n if (!has(education)) return '';\r\n return `\r\n ${sectionTitle(heading)}\r\n <div class=\"section-body\">\r\n ${education.map(renderEducationEntry).join('')}\r\n </div>`;\r\n}\r\n","import type { ResumeVolunteerEntry } from '../types/resume.js';\nimport { esc } from '../utils/escape.js';\nimport { has, richText } from '../utils/text.js';\nimport { sectionTitle } from './shared.js';\n\nexport function renderVolunteer(\n volunteer: readonly ResumeVolunteerEntry[] | undefined,\n heading: string,\n): string {\n if (!has(volunteer)) return '';\n return `\n ${sectionTitle(heading)}\n <div class=\"section-body\">\n ${volunteer\n .map((v) => {\n const years = v.endDate\n ? `${v.startDate}\\u2013${v.endDate}`\n : v.startDate || '';\n const title = v.position\n ? `${v.position} \\u2013 ${v.organization}`\n : v.organization;\n const summary = richText(v.summary);\n\n return `\n <div class=\"bullet-item volunteer-item\">\n <span class=\"vol-title\">${esc(title)}${years ? ` (${esc(years)})` : ''}:</span> ${summary}\n </div>`;\n })\n .join('')}\n </div>`;\n}\n","import type { ResumeCertificate } from '../types/resume.js';\nimport { has, richText } from '../utils/text.js';\nimport { sectionTitle } from './shared.js';\n\nexport function renderCertificates(\n certificates: readonly ResumeCertificate[] | undefined,\n heading: string,\n): string {\n if (!has(certificates)) return '';\n return `\n ${sectionTitle(heading)}\n <div class=\"section-body\">\n ${certificates\n .map(\n (c) => `\n <div class=\"bullet-item\">${richText(c.name)}</div>`,\n )\n .join('')}\n </div>`;\n}\n","import type { ResumeSchema } from '../types/resume.js';\nimport { esc } from '../utils/escape.js';\nimport { has } from '../utils/text.js';\nimport { sectionTitle } from './shared.js';\n\nexport function renderAdditional(resume: ResumeSchema, heading: string): string {\n const parts: string[] = [];\n\n if (has(resume.interests)) {\n for (const interest of resume.interests) {\n const keywords = has(interest.keywords) ? interest.keywords.join(', ') : '';\n const label = interest.name || 'Interests';\n if (keywords) {\n parts.push(\n `<div class=\"additional-line\"><span class=\"additional-label\">${esc(label)}:</span> ${esc(keywords)}</div>`,\n );\n }\n }\n }\n\n if (has(resume.languages)) {\n const langStr = resume.languages\n .map((l) => `${l.language}${l.fluency ? ` (${l.fluency})` : ''}`)\n .join(', ');\n parts.push(\n `<div class=\"additional-line\"><span class=\"additional-label\">Languages:</span> ${esc(langStr)}</div>`,\n );\n }\n\n if (has(resume.references)) {\n parts.push(\n `<div class=\"additional-line\"><span class=\"additional-label\">Referees:</span> Available on request</div>`,\n );\n }\n\n if (parts.length === 0) return '';\n\n return `\n ${sectionTitle(heading)}\n <div class=\"section-body\">\n ${parts.join('\\n ')}\n </div>`;\n}\n","import type { ResumeSchema } from './types/resume.js';\r\nimport { DEFAULT_HEADINGS } from './constants.js';\r\nimport { esc } from './utils/escape.js';\r\nimport { renderHeader } from './sections/header.js';\r\nimport { renderSummary } from './sections/summary.js';\r\nimport { renderSkills } from './sections/skills.js';\r\nimport { renderWork } from './sections/work.js';\r\nimport { renderProjects } from './sections/projects.js';\r\nimport { renderEducation } from './sections/education.js';\r\nimport { renderVolunteer } from './sections/volunteer.js';\r\nimport { renderCertificates } from './sections/certificates.js';\r\nimport { renderAdditional } from './sections/additional.js';\r\nimport css from './styles/academic.css?inline';\r\n\r\nexport function render(resume: ResumeSchema): string {\r\n const h = { ...DEFAULT_HEADINGS };\r\n\r\n const metaHeadings = resume?.meta?.headings;\r\n if (metaHeadings) {\r\n for (const [key, value] of Object.entries(metaHeadings)) {\r\n if (value && key in h) {\r\n h[key] = value;\r\n }\r\n }\r\n }\r\n\r\n const basics = resume?.basics || {};\r\n\r\n return `<!DOCTYPE html>\r\n<html lang=\"en\">\r\n<head>\r\n <meta charset=\"UTF-8\" />\r\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\r\n <title>${esc(basics.name || 'Resume')}</title>\r\n <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\" />\r\n <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin />\r\n <link href=\"https://fonts.googleapis.com/css2?family=EB+Garamond:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500&display=swap\" rel=\"stylesheet\" />\r\n <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css\" />\r\n <style>${css}</style>\r\n</head>\r\n<body>\r\n <div class=\"resume\">\r\n ${renderHeader(basics)}\r\n ${renderSummary(basics.summary, h.summary)}\r\n ${renderSkills(resume.skills, h.skills)}\r\n ${renderWork(resume.work, h.experience)}\r\n ${renderProjects(resume.projects, h.projects)}\r\n ${renderEducation(resume.education, h.education)}\r\n ${renderVolunteer(resume.volunteer, h.volunteer)}\r\n ${renderCertificates(resume.certificates, h.certifications)}\r\n ${renderAdditional(resume, h.additional)}\r\n </div>\r\n</body>\r\n</html>`;\r\n}\r\n","export { render } from './render.js';\nexport type { ResumeSchema } from './types/resume.js';\n\n/** PDF rendering options for Puppeteer/Gotenberg. */\nexport const pdfRenderOptions = {\n mediaType: 'print' as const,\n format: 'A4' as const,\n margin: {\n top: '12mm',\n right: '14mm',\n bottom: '12mm',\n left: '14mm',\n },\n};\n"],"names":[],"mappings":"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"}