From 9dc14b0f58405d27500b2a78acec956dbbe34ca9 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 16 Jul 2026 09:03:44 +0000 Subject: [PATCH] feat: Carbone Word template integration with DOCX merge and LibreOffice PDF conversion --- renderer/carbone-server.js | 24 + renderer/carbone-service.js | 612 ++++++ .../433a0593-d3cf-414e-8763-ab5f87cbc75c.docx | Bin 0 -> 37152 bytes .../carbone-templates/master_cv_template.docx | Bin 0 -> 37152 bytes renderer/carbone-templates/registry.json | 10 + renderer/package-lock.json | 1789 ++++++++++++++++- renderer/package.json | 8 +- renderer/test_carbone.js | 44 + renderer/test_merge.js | 26 + static/carbone.js | 151 ++ static/index.html | 35 +- 11 files changed, 2696 insertions(+), 3 deletions(-) create mode 100644 renderer/carbone-server.js create mode 100644 renderer/carbone-service.js create mode 100644 renderer/carbone-templates/433a0593-d3cf-414e-8763-ab5f87cbc75c.docx create mode 100644 renderer/carbone-templates/master_cv_template.docx create mode 100644 renderer/carbone-templates/registry.json create mode 100644 renderer/test_carbone.js create mode 100644 renderer/test_merge.js create mode 100644 static/carbone.js diff --git a/renderer/carbone-server.js b/renderer/carbone-server.js new file mode 100644 index 0000000..35ca591 --- /dev/null +++ b/renderer/carbone-server.js @@ -0,0 +1,24 @@ +/** + * Carbone Express Server - Standalone microservice + * Runs on port 8771 alongside the FastAPI app on 8770 + */ +const express = require('express'); +const cors = require('cors'); +const path = require('path'); +const { router } = require('./carbone-service'); + +const app = express(); +const PORT = 8771; + +app.use(cors()); +app.use(express.json({ limit: '50mb' })); + +// Mount Carbone routes +app.use('/api/carbone', router); + +// Health check +app.get('/health', (req, res) => res.json({ status: 'ok', service: 'carbone' })); + +app.listen(PORT, '0.0.0.0', () => { + console.log(`Carbone service running on http://0.0.0.0:${PORT}`); +}); \ No newline at end of file diff --git a/renderer/carbone-service.js b/renderer/carbone-service.js new file mode 100644 index 0000000..7fabf9b --- /dev/null +++ b/renderer/carbone-service.js @@ -0,0 +1,612 @@ +/** + * Carbone Template Service - Node.js + Express + * + * Self-hosted Carbone (Community Edition) integration for CV template management. + * Uses LibreOffice for DOCX -> PDF conversion. + * + * Endpoints: + * GET /api/carbone/templates - List saved templates + * GET /api/carbone/master-template - Download master blank DOCX template + * POST /api/carbone/templates/upload - Upload a custom .docx template + * POST /api/carbone/templates/:id/render - Render a template with CV data -> PDF + * DELETE /api/carbone/templates/:id - Delete a template + */ + +const express = require('express'); +const multer = require('multer'); +const carbone = require('carbone'); +const fs = require('fs'); +const path = require('path'); +const { v4: uuidv4 } = require('uuid'); + +const router = express.Router(); + +// ============================================================ +// CONFIGURATION +// ============================================================ + +const TEMPLATES_DIR = path.join(__dirname, 'carbone-templates'); +const MASTER_TEMPLATE_PATH = path.join(TEMPLATES_DIR, 'master_cv_template.docx'); + +// Ensure directories exist +fs.mkdirSync(TEMPLATES_DIR, { recursive: true }); + +// Multer config for file uploads +const storage = multer.diskStorage({ + destination: (req, file, cb) => cb(null, TEMPLATES_DIR), + filename: (req, file, cb) => { + const id = uuidv4(); + const ext = path.extname(file.originalname) || '.docx'; + cb(null, `${id}${ext}`); + } +}); +const upload = multer({ + storage, + limits: { fileSize: 10 * 1024 * 1024 }, // 10MB limit + fileFilter: (req, file, cb) => { + const allowed = ['.docx', '.doc', '.odt', '.rtf']; + const ext = path.extname(file.originalname).toLowerCase(); + if (allowed.includes(ext)) { + cb(null, true); + } else { + cb(new Error(`File type ${ext} not supported. Use .docx, .doc, .odt, or .rtf`)); + } + } +}); + +// Simple in-memory template registry (persisted to JSON file) +const REGISTRY_PATH = path.join(TEMPLATES_DIR, 'registry.json'); + +function loadRegistry() { + try { + return JSON.parse(fs.readFileSync(REGISTRY_PATH, 'utf-8')); + } catch { + return []; + } +} + +function saveRegistry(registry) { + fs.writeFileSync(REGISTRY_PATH, JSON.stringify(registry, null, 2)); +} + +// ============================================================ +// CARBONE TAG DOCUMENTATION +// ============================================================ +/** + * CARBONE TAG REFERENCE (for use inside .docx templates) + * ============================================ + * + * Carbone uses {d.property} syntax to map JSON data into the document. + * Our application's data schema maps as follows: + * + * SIMPLE FIELDS (text replacement): + * {d.firstName} -> Candidate's first name + * {d.lastName} -> Candidate's last name + * {d.email} -> Email address + * {d.phone} -> Phone number + * {d.address} -> Physical address + * {d.linkedin} -> LinkedIn URL + * {d.github} -> GitHub URL + * {d.website} -> Website URL + * {d.summary} -> Professional summary text + * + * LOOP TAGS (for arrays - repeat a section for each item): + * + * Work Experience loop: + * {d.experience[i].position} -> Job title + * {d.experience[i].company} -> Company name + * {d.experience[i].location} -> Location + * {d.experience[i].startDate} -> Start date (formatted) + * {d.experience[i].endDate} -> End date or "Present" + * {d.experience[i].description} -> Job description + * {d.experience[i].duration} -> Calculated duration (e.g., "2.5 years") + * + * To create a loop in Carbone, wrap the repeated section: + * {d.experience[i]:begin} ...content... {d.experience[i]:end} + * + * Achievements sub-loop (inside experience loop): + * {d.experience[i].achievements[j]:begin} + * • {d.experience[i].achievements[j]} + * {d.experience[i].achievements[j]:end} + * + * Skills loop: + * {d.skills[i].name} -> Skill name + * {d.skills[i].category} -> Skill category + * {d.skills[i].proficiency} -> Proficiency level + * {d.skills[i].years} -> Years of experience (dynamic) + * + * Skills grouped by category: + * {d.skillsByCategory[i].category:begin} + * {d.skillsByCategory[i].category} + * {d.skillsByCategory[i].skills[j].name:begin} + * • {d.skillsByCategory[i].skills[j].name} ({d.skillsByCategory[i].skills[j].years}y) + * {d.skillsByCategory[i].skills[j].name:end} + * {d.skillsByCategory[i].category:end} + * + * Education loop: + * {d.education[i].degree} -> Degree name + * {d.education[i].field} -> Field of study + * {d.education[i].institution} -> Institution name + * {d.education[i].startDate} -> Start date + * {d.education[i].endDate} -> End date + * {d.education[i].grade} -> Grade/Result + * + * Certifications loop: + * {d.certifications[i].name} -> Certification name + * {d.certifications[i].issuer} -> Issuing organization + * {d.certifications[i].date} -> Issue date + * + * CONDITIONALS: + * {d.summary:ifExist:begin} ...shows only if summary exists... {d.summary:ifExist:end} + * {d.linkedin:ifExist:begin} LinkedIn: {d.linkedin} {d.linkedin:ifExist:end} + * + * FORMATTING: + * {d.experience[i].startDate:formatD(MMM YYYY)} -> Formats date as "Jan 2022" + * {d.experience[i].endDate:formatD(MMM YYYY):ifEmpty(Present)} + * + * COMPOSITE (for full name): + * {d.firstName} {d.lastName} + */ + +// ============================================================ +// DATA TRANSFORMER +// ============================================================ + +/** + * Transform our application's candidate data into the flat structure + * that Carbone template tags expect. This handles date formatting, + * dynamic experience calculation, and grouping skills by category. + * + * @param {Object} candidateData - Our app's candidate data (from /api/candidates/:id) + * @param {string} referenceDate - Date for dynamic experience calc (YYYY-MM-DD) + * @returns {Object} Flattened data object for Carbone + */ +function transformCandidateData(candidateData, referenceDate) { + const refDate = referenceDate ? new Date(referenceDate) : new Date(); + const candidate = candidateData.candidate || candidateData; + + // Helper: format date as "Mon YYYY" + const fmtDate = (d) => { + if (!d) return ''; + try { + const date = new Date(d); + return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short' }); + } catch { return String(d); } + }; + + // Helper: calculate years between dates + const calcYears = (start, end) => { + if (!start) return 0; + const endDate = end ? new Date(end) : refDate; + const startDate = new Date(start); + const diff = (endDate - startDate) / (365.25 * 24 * 3600 * 1000); + return Math.round(diff * 10) / 10; + }; + + // Transform experience + const experience = (candidateData.experience || []).map(exp => { + const achievements = Array.isArray(exp.achievements) ? exp.achievements : + (typeof exp.achievements === 'string' ? safeJsonParse(exp.achievements, []) : []); + const skillsUsed = Array.isArray(exp.skills_used) ? exp.skills_used : + (typeof exp.skills_used === 'string' ? safeJsonParse(exp.skills_used, []) : []); + + return { + position: exp.position || '', + company: exp.company || '', + location: exp.location || '', + startDate: fmtDate(exp.start_date), + endDate: exp.end_date ? fmtDate(exp.end_date) : 'Present', + description: exp.description || '', + duration: calcYears(exp.start_date, exp.end_date) + ' years', + achievements: achievements, + skillsUsed: skillsUsed.join(', ') + }; + }); + + // Transform skills with dynamic years + const skills = (candidateData.skills || []).map(s => ({ + name: s.skill_name || '', + category: s.skill_category || 'Other', + proficiency: s.proficiency || '', + years: s.years_experience || calcYears(s.start_date, s.end_date), + startYear: s.start_date ? new Date(s.start_date).getFullYear() : '' + })); + + // Group skills by category for the grouped loop + const categoryMap = {}; + for (const s of skills) { + if (!categoryMap[s.category]) categoryMap[s.category] = []; + categoryMap[s.category].push(s); + } + const skillsByCategory = Object.entries(categoryMap).map(([category, items]) => ({ + category, + skills: items + })); + + // Transform education + const education = (candidateData.education || []).map(e => ({ + degree: e.degree || '', + field: e.field_of_study || '', + institution: e.institution || '', + startDate: fmtDate(e.start_date), + endDate: fmtDate(e.end_date), + grade: e.grade || '' + })); + + // Transform certifications + const certifications = (candidateData.certifications || []).map(c => ({ + name: c.name || '', + issuer: c.issuer || '', + date: fmtDate(c.issue_date), + expiryDate: fmtDate(c.expiry_date) + })); + + return { + // Personal details + firstName: candidate.first_name || '', + lastName: candidate.last_name || '', + fullName: `${candidate.first_name || ''} ${candidate.last_name || ''}`.trim(), + email: candidate.email || '', + phone: candidate.phone || '', + address: candidate.address || '', + linkedin: candidate.linkedin || '', + github: candidate.github || '', + website: candidate.website || '', + summary: candidate.summary || '', + + // Arrays (for loops) + experience: experience, + skills: skills, + skillsByCategory: skillsByCategory, + education: education, + certifications: certifications, + + // Metadata + generationDate: refDate.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }), + referenceDate: refDate.toISOString().split('T')[0] + }; +} + +function safeJsonParse(str, fallback) { + try { return JSON.parse(str); } catch { return fallback; } +} + +// ============================================================ +// MASTER TEMPLATE GENERATOR +// ============================================================ + +/** + * Generate the master blank CV template (.docx) with all Carbone tags. + * This is the file users download, customize in Word, and re-upload. + * + * We build it programmatically using the docx library so we don't need + * a pre-made binary file. + */ +async function generateMasterTemplate() { + // We'll use a simple approach: create a minimal DOCX with the tags as text. + // Carbone processes the text content of the DOCX, so we just need the tags + // present in the document body. + + const docxContent = createMasterDocxXML(); + const templatePath = MASTER_TEMPLATE_PATH; + + // Write as a .docx (which is a ZIP file with XML inside) + // For simplicity, we use the 'docx' npm package if available, + // otherwise we create a minimal valid DOCX manually. + + try { + const DocxTemplater = require('docx'); + // If docx package is available, use it + // But for now, we'll create a minimal DOCX manually + } catch (e) { + // docx package not available — use manual approach + } + + // Create a minimal valid DOCX file (ZIP with required XML parts) + const archiver = require('archiver') ? require('archiver') : null; + + if (archiver) { + // Use archiver to create the ZIP + } + + // Simplest approach: use carbone's built-in template creation + // Actually, the easiest way is to create the DOCX using python-docx + // which is already installed in our venv + const { execSync } = require('child_process'); + const pythonScript = path.join(__dirname, 'generate_master_template.py'); + fs.writeFileSync(pythonScript, MASTER_TEMPLATE_PYTHON); + + execSync(`python3 ${pythonScript} "${templatePath}"`, { timeout: 30000 }); + + return templatePath; +} + +// Python script content for generating the master DOCX template +const MASTER_TEMPLATE_PYTHON = ` +import sys +from docx import Document +from docx.shared import Pt, Inches, RGBColor +from docx.enum.text import WD_ALIGN_PARAGRAPH + +def create_master_template(output_path): + doc = Document() + + # Set margins + for section in doc.sections: + section.top_margin = Inches(0.5) + section.bottom_margin = Inches(0.5) + section.left_margin = Inches(0.75) + section.right_margin = Inches(0.75) + + # === HEADER: Name + Contact === + p = doc.add_paragraph() + p.alignment = WD_ALIGN_PARAGRAPH.CENTER + run = p.add_run('{d.fullName}') + run.font.size = Pt(24) + run.font.bold = True + + p = doc.add_paragraph() + p.alignment = WD_ALIGN_PARAGRAPH.CENTER + run = p.add_run('{d.email} | {d.phone} | {d.address}') + run.font.size = Pt(10) + run.font.color.rgb = RGBColor(0x66, 0x66, 0x66) + + p = doc.add_paragraph() + p.alignment = WD_ALIGN_PARAGRAPH.CENTER + run = p.add_run('{d.linkedin:ifExist:begin}LinkedIn: {d.linkedin} | {d.linkedin:ifExist:end}{d.github:ifExist:begin}GitHub: {d.github} | {d.github:ifExist:end}{d.website:ifExist:begin}Web: {d.website}{d.website:ifExist:end}') + run.font.size = Pt(10) + run.font.color.rgb = RGBColor(0x66, 0x66, 0x66) + + # Divider + doc.add_paragraph('_' * 80) + + # === PROFESSIONAL SUMMARY === + h = doc.add_heading('Professional Summary', level=2) + p = doc.add_paragraph('{d.summary}') + + # === WORK EXPERIENCE (loop) === + doc.add_heading('Work Experience', level=2) + + # Carbone loop: {d.experience[i]:begin} ... {d.experience[i]:end} + p = doc.add_paragraph('{d.experience[i]:begin}') + p = doc.add_paragraph() + run = p.add_run('{d.experience[i].position}') + run.font.bold = True + run.font.size = Pt(12) + run = p.add_run(' at {d.experience[i].company}') + run.font.size = Pt(12) + + p = doc.add_paragraph('{d.experience[i].startDate} - {d.experience[i].endDate} ({d.experience[i].duration})') + p.runs[0].font.size = Pt(10) + p.runs[0].font.italic = True + p.runs[0].font.color.rgb = RGBColor(0x88, 0x88, 0x88) + + p = doc.add_paragraph('{d.experience[i].description}') + + # Achievements sub-loop + p = doc.add_paragraph('{d.experience[i].achievements[j]:begin}') + p = doc.add_paragraph('{d.experience[i].achievements[j]}', style='List Bullet') + p = doc.add_paragraph('{d.experience[i].achievements[j]:end}') + + p = doc.add_paragraph('Skills: {d.experience[i].skillsUsed}') + p.runs[0].font.size = Pt(10) + p.runs[0].font.italic = True + + p = doc.add_paragraph('{d.experience[i]:end}') + + # === SKILLS (grouped by category) === + doc.add_heading('Skills', level=2) + + p = doc.add_paragraph('{d.skillsByCategory[i]:begin}') + p = doc.add_paragraph() + run = p.add_run('{d.skillsByCategory[i].category}: ') + run.font.bold = True + run.font.size = Pt(11) + + p = doc.add_paragraph('{d.skillsByCategory[i].skills[j]:begin}') + p = doc.add_paragraph('{d.skillsByCategory[i].skills[j].name} ({d.skillsByCategory[i].skills[j].years}y){d.skillsByCategory[i].skills[j]:end}', style='List Bullet') + + p = doc.add_paragraph('{d.skillsByCategory[i].end}') + + # === EDUCATION (loop) === + doc.add_heading('Education', level=2) + + p = doc.add_paragraph('{d.education[i]:begin}') + p = doc.add_paragraph() + run = p.add_run('{d.education[i].degree}') + run.font.bold = True + run = p.add_run(' in {d.education[i].field}') + + p = doc.add_paragraph('{d.education[i].institution}') + p.runs[0].font.size = Pt(11) + + p = doc.add_paragraph('{d.education[i].startDate} - {d.education[i].endDate}') + p.runs[0].font.size = Pt(10) + p.runs[0].font.color.rgb = RGBColor(0x88, 0x88, 0x88) + + p = doc.add_paragraph('{d.education[i].grade:ifExist:begin}Grade: {d.education[i].grade}{d.education[i].grade:ifExist:end}') + + p = doc.add_paragraph('{d.education[i]:end}') + + # === CERTIFICATIONS (loop) === + doc.add_heading('Certifications', level=2) + + p = doc.add_paragraph('{d.certifications[i]:begin}') + p = doc.add_paragraph('{d.certifications[i].name} - {d.certifications[i].issuer} ({d.certifications[i].date})', style='List Bullet') + p = doc.add_paragraph('{d.certifications[i]:end}') + + # Footer + doc.add_paragraph() + p = doc.add_paragraph('Generated on {d.generationDate}') + p.alignment = WD_ALIGN_PARAGRAPH.CENTER + p.runs[0].font.size = Pt(8) + p.runs[0].font.color.rgb = RGBColor(0xAA, 0xAA, 0xAA) + + doc.save(output_path) + print(f'Master template saved to {output_path}') + +if __name__ == '__main__': + create_master_template(sys.argv[1]) +`; + +// ============================================================ +// API ROUTES +// ============================================================ + +// GET /api/carbone/templates - List all saved templates +router.get('/templates', (req, res) => { + const registry = loadRegistry(); + res.json({ templates: registry }); +}); + +// GET /api/carbone/master-template - Download master template +router.get('/master-template', async (req, res) => { + try { + // Generate master template if it doesn't exist + if (!fs.existsSync(MASTER_TEMPLATE_PATH)) { + await generateMasterTemplate(); + } + res.download(MASTER_TEMPLATE_PATH, 'master_cv_template.docx'); + } catch (err) { + console.error('Master template error:', err); + res.status(500).json({ error: 'Failed to generate master template: ' + err.message }); + } +}); + +// POST /api/carbone/templates/upload - Upload a custom .docx template +router.post('/templates/upload', upload.single('template'), (req, res) => { + if (!req.file) { + return res.status(400).json({ error: 'No file uploaded' }); + } + + const name = req.body.name || path.basename(req.file.originalname, path.extname(req.file.originalname)); + const id = path.basename(req.file.filename, path.extname(req.file.filename)); + + const registry = loadRegistry(); + const entry = { + id: id, + name: name, + filename: req.file.filename, + originalName: req.file.originalname, + path: req.file.path, + uploadedAt: new Date().toISOString() + }; + registry.push(entry); + saveRegistry(registry); + + res.json({ success: true, template: entry }); +}); + +// POST /api/carbone/templates/:id/render - Render template with data -> PDF +router.post('/templates/:id/render', async (req, res) => { + const templateId = req.params.id; + const registry = loadRegistry(); + const template = registry.find(t => t.id === templateId); + + if (!template) { + return res.status(404).json({ error: 'Template not found' }); + } + + if (!fs.existsSync(template.path)) { + return res.status(404).json({ error: 'Template file missing from disk' }); + } + + // Get candidate data from request body + const candidateData = req.body.candidateData || req.body; + const referenceDate = req.body.referenceDate || req.body.generation_date; + + // Transform data for Carbone + const carboneData = transformCandidateData(candidateData, referenceDate); + + // Step 1: Use Carbone to merge data into the DOCX template (no PDF conversion) + const mergePromise = new Promise((resolve, reject) => { + const timer = setTimeout(() => reject(new Error('Carbone merge timed out')), 30000); + + carbone.render(template.path, carboneData, {}, (err, result) => { + clearTimeout(timer); + if (err) reject(err); + else resolve(result); + }); + }); + + try { + const docxBuffer = await mergePromise; + + // Step 2: Convert merged DOCX to PDF using LibreOffice directly + // (Carbone's built-in converter has issues with pipe communication) + const tmpDir = path.join(__dirname, 'output'); + fs.mkdirSync(tmpDir, { recursive: true }); + const tmpId = uuidv4(); + const tmpDocx = path.join(tmpDir, `${tmpId}.docx`); + const tmpPdf = path.join(tmpDir, `${tmpId}.pdf`); + + fs.writeFileSync(tmpDocx, docxBuffer); + + // Call LibreOffice headless for PDF conversion + const { execFile } = require('child_process'); + const convertPromise = new Promise((resolve, reject) => { + const timer = setTimeout(() => reject(new Error('LibreOffice conversion timed out after 60s')), 60000); + + execFile('soffice', [ + '--headless', '--invisible', '--nocrashreport', '--nodefault', + '--nologo', '--nofirststartwizard', '--norestore', + '--convert-to', 'pdf', + '--outdir', tmpDir, + tmpDocx + ], { timeout: 60000 }, (err, stdout, stderr) => { + clearTimeout(timer); + if (err) reject(err); + else if (!fs.existsSync(tmpPdf)) reject(new Error('PDF was not generated')); + else resolve(tmpPdf); + }); + }); + + const pdfPath = await convertPromise; + const pdfBuffer = fs.readFileSync(pdfPath); + + // Clean up temp files + try { fs.unlinkSync(tmpDocx); } catch {} + try { fs.unlinkSync(tmpPdf); } catch {} + + // Set headers for PDF download + res.setHeader('Content-Type', 'application/pdf'); + res.setHeader('Content-Disposition', `attachment; filename="${template.name.replace(/\s+/g, '_')}_CV.pdf"`); + res.setHeader('Content-Length', pdfBuffer.length); + + res.send(pdfBuffer); + } catch (err) { + console.error('Carbone render error:', err); + + if (err.message.includes('timed out')) { + res.status(504).json({ error: 'PDF generation timed out. LibreOffice may be busy. Try again.' }); + } else if (err.message.includes('corrupt') || err.message.includes('invalid')) { + res.status(400).json({ error: 'Template file appears to be corrupted or invalid' }); + } else { + res.status(500).json({ error: 'PDF generation failed: ' + err.message }); + } + } +}); + +// DELETE /api/carbone/templates/:id - Delete a template +router.delete('/templates/:id', (req, res) => { + const templateId = req.params.id; + const registry = loadRegistry(); + const template = registry.find(t => t.id === templateId); + + if (!template) { + return res.status(404).json({ error: 'Template not found' }); + } + + // Delete file from disk + if (fs.existsSync(template.path)) { + fs.unlinkSync(template.path); + } + + // Remove from registry + const newRegistry = registry.filter(t => t.id !== templateId); + saveRegistry(newRegistry); + + res.json({ success: true, message: 'Template deleted' }); +}); + +module.exports = { router, transformCandidateData, generateMasterTemplate }; \ No newline at end of file diff --git a/renderer/carbone-templates/433a0593-d3cf-414e-8763-ab5f87cbc75c.docx b/renderer/carbone-templates/433a0593-d3cf-414e-8763-ab5f87cbc75c.docx new file mode 100644 index 0000000000000000000000000000000000000000..089b7f13607f71f60f96a244a05850f8359a6753 GIT binary patch literal 37152 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 G!~+06WID$H literal 0 HcmV?d00001 diff --git a/renderer/carbone-templates/master_cv_template.docx b/renderer/carbone-templates/master_cv_template.docx new file mode 100644 index 0000000000000000000000000000000000000000..089b7f13607f71f60f96a244a05850f8359a6753 GIT binary patch literal 37152 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 G!~+06WID$H literal 0 HcmV?d00001 diff --git a/renderer/carbone-templates/registry.json b/renderer/carbone-templates/registry.json new file mode 100644 index 0000000..4ceb2f0 --- /dev/null +++ b/renderer/carbone-templates/registry.json @@ -0,0 +1,10 @@ +[ + { + "id": "433a0593-d3cf-414e-8763-ab5f87cbc75c", + "name": "Default Master Template", + "filename": "433a0593-d3cf-414e-8763-ab5f87cbc75c.docx", + "originalName": "master_cv_template.docx", + "path": "/root/workspace/cv-app/renderer/carbone-templates/433a0593-d3cf-414e-8763-ab5f87cbc75c.docx", + "uploadedAt": "2026-07-16T09:03:29.601Z" + } +] \ No newline at end of file diff --git a/renderer/package-lock.json b/renderer/package-lock.json index 7b5a118..cadc209 100644 --- a/renderer/package-lock.json +++ b/renderer/package-lock.json @@ -9,7 +9,13 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "puppeteer": "^25.3.0" + "archiver": "^8.0.0", + "carbone": "^3.8.2", + "cors": "^2.8.6", + "express": "^5.2.1", + "multer": "^2.2.0", + "puppeteer": "^25.3.0", + "uuid": "^14.0.1" } }, "node_modules/@puppeteer/browsers": { @@ -40,6 +46,56 @@ } } }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/ansi-regex": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", @@ -64,6 +120,394 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", + "license": "MIT" + }, + "node_modules/archiver": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-8.0.0.tgz", + "integrity": "sha512-fV1orZfsnPn9BaSByR/qE67rJCLJEy2Ox5bq7nJh+jquWaNh6Sfec75kJ2T6PtdGUbPQlrVoSVCEOa5SdiTQ1g==", + "license": "MIT", + "dependencies": { + "async": "^3.2.4", + "buffer-crc32": "^1.0.0", + "is-stream": "^4.0.0", + "lazystream": "^1.0.0", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0", + "readdir-glob": "^3.0.0", + "tar-stream": "^3.0.0", + "zip-stream": "^7.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/archiver/node_modules/buffer-crc32": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", + "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/archiver/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, + "node_modules/b4a": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz", + "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==", + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/bare-events": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.1.tgz", + "integrity": "sha512-Z0oHEHAFDZkffN8Qc39zNZjQlMDkPJRyyyZieU1VH7u8c5S+qHZ2S8ixdKIAxEjfHO7FJxXmJWgteOghVanIsg==", + "license": "Apache-2.0", + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } + }, + "node_modules/bare-fs": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.7.4.tgz", + "integrity": "sha512-y1kC+ffIx/tPLdTE693uNjHfzTfr+ravR5tvWlMXe25nELbkqV400S71qHDwbkAQ1FVEZobB1NFRzFbCCcyBCQ==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.5.4", + "bare-path": "^3.0.0", + "bare-stream": "^2.6.4", + "bare-url": "^2.2.2", + "fast-fifo": "^1.3.2" + }, + "engines": { + "bare": ">=1.16.0" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/bare-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.1.1.tgz", + "integrity": "sha512-JprUlveX3QjApC1cTpsUOiscADftCGVWkzitbHsRqv84hzYwYHw2mbluddsq5TvI8mH/8Ov1f4BiMAdcB0oYnQ==", + "license": "Apache-2.0" + }, + "node_modules/bare-stream": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.13.3.tgz", + "integrity": "sha512-Kc+brLqvEqGkjyfiwJmImAOqLZL7OsoLKuavx+hJjgVV3nLTOjloJyPMFxjUPerGGHrNH0fLU06jjykMLWrERQ==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.8.1", + "streamx": "^2.25.0", + "teex": "^1.0.1" + }, + "peerDependencies": { + "bare-abort-controller": "*", + "bare-buffer": "*", + "bare-events": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + }, + "bare-buffer": { + "optional": true + }, + "bare-events": { + "optional": true + } + } + }, + "node_modules/bare-url": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.4.5.tgz", + "integrity": "sha512-K+y9xF1tN+CdPu4qWwr0QiK1Al07eFPGYK5M2pDXcmHdMdgC/tT/bpmMe1hrmRHaidKLkXrC+cRNYf3XVDUhSQ==", + "license": "Apache-2.0", + "dependencies": { + "bare-path": "^3.0.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", + "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^2.0.0", + "debug": "^4.4.3", + "http-errors": "^2.0.1", + "iconv-lite": "^0.7.2", + "on-finished": "^2.4.1", + "qs": "^6.15.2", + "raw-body": "^3.0.2", + "type-is": "^2.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/body-parser/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/body-parser/node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/body-parser/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/body-parser/node_modules/type-is": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", + "license": "MIT", + "dependencies": { + "content-type": "^2.0.0", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", + "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/carbone": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/carbone/-/carbone-3.8.2.tgz", + "integrity": "sha512-ejuZ//MywH4uTIhWPIqSOEWLq9y2s3kPadoOh92rZZCNkYg31y8mpPaQT4rZb6oGmaclLDvOW6Ye0iACVH+c+w==", + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "dayjs": "=1.11.20", + "debug": "=4.4.3", + "which": "=6.0.1", + "yauzl": "=2.10.0", + "yazl": "=2.5.1" + }, + "bin": { + "carbone": "bin/carbone" + }, + "engines": { + "node": ">= 12.14.0" + } + }, "node_modules/chromium-bidi": { "version": "16.0.1", "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-16.0.1.tgz", @@ -94,18 +538,260 @@ "node": ">=20" } }, + "node_modules/compress-commons": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-7.0.1.tgz", + "integrity": "sha512-g0S8KAD8qf4+V//pr3BfB1aBnARLXNz2Gx+jmHU0LEriUuoQUOPOulVquHKTJ8+EAIIO7fhseNDr9wK5Q9FKBQ==", + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "crc32-stream": "^7.0.1", + "is-stream": "^4.0.0", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/compress-commons/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/content-disposition": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-7.0.1.tgz", + "integrity": "sha512-IBWsY8xznyQrcHn8h4bC8/4ErNke5elzgG8GcqF4RFPw6aHkWWRc7Tgw6upjaTX/CT/yQgqYENkxYsTYN+hW2g==", + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/crc32-stream/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/dayjs": { + "version": "1.11.20", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.20.tgz", + "integrity": "sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/devtools-protocol": { "version": "0.0.1638949", "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1638949.tgz", "integrity": "sha512-mXwg4Fqnv0WR4iuAT/gYUmctNkjILwXFHyZ+m7Ty1dfr0ezZt2U3gnrrJTfRobJTHoXf+IbuFvFITzLrLFjwJA==", "license": "BSD-3-Clause" }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, "node_modules/emoji-regex": { "version": "10.6.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", "license": "MIT" }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -115,6 +801,219 @@ "node": ">=6" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.7.0" + } + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/type-is": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", + "license": "MIT", + "dependencies": { + "content-type": "^2.0.0", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/type-is/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "license": "MIT" + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -136,6 +1035,225 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", + "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=20" + } + }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/lilconfig": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", @@ -148,6 +1266,72 @@ "url": "https://github.com/sponsors/antonk52" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/mitt": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", @@ -163,6 +1347,144 @@ "node": ">=18.0.0" } }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/multer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/multer/-/multer-2.2.0.tgz", + "integrity": "sha512-6rdyFg2kLrMh9Jee7/BMPuV9lEAd7lLW2YUpF9/YxR7njyoUwwQ0ZPh3TaIY50Sw6vlyD2HW3wGOkTS4P79xrQ==", + "license": "MIT", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.6.0", + "concat-stream": "^2.0.0", + "type-is": "^1.6.18" + }, + "engines": { + "node": ">= 10.16.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "license": "MIT" + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/puppeteer": { "version": "25.3.0", "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-25.3.0.tgz", @@ -201,6 +1523,306 @@ "node": ">=22.12.0" } }, + "node_modules/qs": { + "version": "6.15.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", + "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", + "license": "BSD-3-Clause", + "dependencies": { + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", + "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdir-glob": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-3.0.0.tgz", + "integrity": "sha512-AhNB2KgKeVJr16nK9LLZbJNWnYoT23ZrumNKFDebHBdkC8KHSqWo871JAUhoWC/RtjEVdqNMFpM6qrwRbaUqpw==", + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^10.2.2" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/yqnn" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/send/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/send/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/streamx": { + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.0.tgz", + "integrity": "sha512-1Yowhzjf0ivGMrTIkY9hav5TxobO9qIVqUE41fiCGMGgc3CLlf4MY+9AHmZqBWgDTue0fY9zWjYFVyf6Diuobw==", + "license": "MIT", + "dependencies": { + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/string-width": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", @@ -233,18 +1855,128 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "node_modules/tar-stream": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.2.0.tgz", + "integrity": "sha512-ojzvCvVaNp6aOTFmG7jaRD0meowIAuPc3cMMhSgKiVWws1GyHbGd/xvnyuRKcKlMpt3qvxx6r0hreCNITP9hIg==", + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "bare-fs": "^4.5.5", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/teex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", + "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", + "license": "MIT", + "dependencies": { + "streamx": "^2.12.5" + } + }, + "node_modules/text-decoder": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", + "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/typed-query-selector": { "version": "2.12.2", "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.2.tgz", "integrity": "sha512-EOPFbyIub4ngnEdqi2yOcNeDLaX/0jcE1JoAXQDDMIthap7FoN795lc/SHfIq2d416VufXpM8z/lD+WRm2gfOQ==", "license": "MIT" }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/uuid": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.1.tgz", + "integrity": "sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist-node/bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/webdriver-bidi-protocol": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.4.2.tgz", "integrity": "sha512-VSV+fzfChirL3e7jay2yUC7B4HQCGtEWEg/MSSQbK+qWbqeGlRLlXTzPpYr3XGUvbpDHumWZBJxgesg4N7dbtA==", "license": "Apache-2.0" }, + "node_modules/which": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", + "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", + "license": "ISC", + "dependencies": { + "isexe": "^4.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/wrap-ansi": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", @@ -262,6 +1994,12 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, "node_modules/ws": { "version": "8.21.1", "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.1.tgz", @@ -318,6 +2056,55 @@ "node": "^20.19.0 || ^22.12.0 || >=23" } }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yazl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz", + "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==", + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3" + } + }, + "node_modules/zip-stream": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-7.0.5.tgz", + "integrity": "sha512-dSvYKdvLsAHCDqPOhIwk/q5CvuWtTB3Dgpoe0uVEFjTzIOAmsQpprX25InCvrvJsirEbu1OHyy67n/kAj1Sw/w==", + "license": "MIT", + "dependencies": { + "compress-commons": "^7.0.0", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/zip-stream/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/zod": { "version": "3.25.76", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", diff --git a/renderer/package.json b/renderer/package.json index 10d9ad1..b2cb7ca 100644 --- a/renderer/package.json +++ b/renderer/package.json @@ -10,6 +10,12 @@ "author": "", "license": "ISC", "dependencies": { - "puppeteer": "^25.3.0" + "archiver": "^8.0.0", + "carbone": "^3.8.2", + "cors": "^2.8.6", + "express": "^5.2.1", + "multer": "^2.2.0", + "puppeteer": "^25.3.0", + "uuid": "^14.0.1" } } diff --git a/renderer/test_carbone.js b/renderer/test_carbone.js new file mode 100644 index 0000000..a4630b6 --- /dev/null +++ b/renderer/test_carbone.js @@ -0,0 +1,44 @@ +// Test Carbone render with master template + sample data +const carbone = require('carbone'); +const { transformCandidateData } = require('./carbone-service'); +const fs = require('fs'); + +const candidateData = { + candidate: { + first_name: 'John', last_name: 'Smith', + email: 'john.smith@email.com', phone: '+1-555-123-4567', + address: 'Cape Town, South Africa', + linkedin: 'linkedin.com/in/johnsmith', + github: 'github.com/jsmith', + summary: 'Experienced software engineer with 8+ years in full-stack development specializing in .NET technologies and cloud architecture.' + }, + skills: [ + { skill_name: 'C# / .NET', skill_category: 'Programming', proficiency: 'Expert', years_experience: 8.5 }, + { skill_name: 'Azure Cloud', skill_category: 'Cloud', proficiency: 'Advanced', years_experience: 6.5 }, + { skill_name: 'React', skill_category: 'Frontend', proficiency: 'Advanced', years_experience: 5.5 } + ], + experience: [ + { position: 'Senior Software Engineer', company: 'TechCorp', location: 'Cape Town', start_date: '2022-01-01', end_date: null, description: 'Led team building cloud-native microservices.', achievements: ['Reduced deployment time 70%', 'Architected event-driven system'], skills_used: ['C#','Azure','Docker'] }, + { position: 'Software Engineer', company: 'DataSoft', location: 'Johannesburg', start_date: '2019-03-01', end_date: '2021-12-01', description: 'Developed ASP.NET Core APIs.', achievements: ['Built React dashboard'], skills_used: ['C#','React'] } + ], + education: [{ degree: 'BSc Computer Science', field_of_study: 'Computer Science', institution: 'University of Cape Town', start_date: '2014-01-01', end_date: '2017-12-01', grade: 'First Class' }], + certifications: [{ name: 'Azure Developer Associate', issuer: 'Microsoft', issue_date: '2021-06-01' }] +}; + +const data = transformCandidateData(candidateData, '2026-07-16'); +console.log('Transformed data keys:', Object.keys(data)); +console.log('Experience count:', data.experience.length); +console.log('Skills count:', data.skills.length); + +const templatePath = './carbone-templates/master_cv_template.docx'; +const options = { convertTo: 'pdf', hardRefreshCache: true }; + +carbone.render(templatePath, data, options, function(err, result) { + if (err) { + console.error('ERROR:', err.message); + process.exit(1); + } + const outPath = './output/carbone_test.pdf'; + fs.writeFileSync(outPath, result); + console.log('PDF saved:', outPath, 'Size:', result.length, 'bytes'); +}); \ No newline at end of file diff --git a/renderer/test_merge.js b/renderer/test_merge.js new file mode 100644 index 0000000..a0c90dd --- /dev/null +++ b/renderer/test_merge.js @@ -0,0 +1,26 @@ +// Simple Carbone DOCX merge test (no PDF conversion) +const carbone = require('carbone'); +const fs = require('fs'); + +const data = { + fullName: 'John Smith', email: 'john@test.com', phone: '+1-555-123-4567', + address: 'Cape Town', linkedin: 'linkedin.com/in/john', github: 'github.com/john', + website: '', summary: 'Software engineer with 8 years experience.', + experience: [ + { position: 'Senior Engineer', company: 'TechCorp', startDate: 'Jan 2022', endDate: 'Present', duration: '4.5 years', description: 'Led team.', achievements: ['Reduced time 70%'], skillsUsed: 'C#, Azure' }, + { position: 'Software Engineer', company: 'DataSoft', startDate: 'Mar 2019', endDate: 'Dec 2021', duration: '2.8 years', description: 'Built APIs.', achievements: ['Built dashboard'], skillsUsed: 'C#, React' } + ], + skillsByCategory: [ + { category: 'Programming', skills: [{ name: 'C#', years: 8.5 }, { name: 'React', years: 5.5 }] }, + { category: 'Cloud', skills: [{ name: 'Azure', years: 6.5 }] } + ], + education: [{ degree: 'BSc', field: 'CS', institution: 'UCT', startDate: '2014', endDate: '2017', grade: 'First Class' }], + certifications: [{ name: 'Azure Dev', issuer: 'Microsoft', date: 'Jun 2021' }], + generationDate: 'July 16, 2026' +}; + +carbone.render('./carbone-templates/master_cv_template.docx', data, {}, function(err, result) { + if (err) { console.error('Merge error:', err.message || err); process.exit(1); } + fs.writeFileSync('./output/carbone_merged.docx', result); + console.log('DOCX merged:', result.length, 'bytes'); +}); \ No newline at end of file diff --git a/static/carbone.js b/static/carbone.js new file mode 100644 index 0000000..9557937 --- /dev/null +++ b/static/carbone.js @@ -0,0 +1,151 @@ +// Carbone Word Template Manager - Frontend +const CARBONE_API = 'http://localhost:8771/api/carbone'; + +// ============================================================ +// NAVIGATION +// ============================================================ +// Load carbone data when the page is shown via a MutationObserver +const carboneNavObserver = new MutationObserver(() => { + if (document.getElementById('page-carbone') && document.getElementById('page-carbone').classList.contains('active')) { + loadCarboneTemplates(); + } +}); +document.addEventListener('DOMContentLoaded', () => { + const carbonePage = document.getElementById('page-carbone'); + if (carbonePage) carboneNavObserver.observe(carbonePage, { attributes: true, attributeFilter: ['class'] }); +}); + +// ============================================================ +// DOWNLOAD MASTER TEMPLATE +// ============================================================ +function downloadMasterTemplate() { + window.open(CARBONE_API + '/master-template', '_blank'); +} + +// ============================================================ +// UPLOAD CUSTOM TEMPLATE +// ============================================================ +const carboneUploadZone = document.getElementById('carbone-upload-zone'); +if (carboneUploadZone) { + carboneUploadZone.addEventListener('dragover', e => { e.preventDefault(); carboneUploadZone.classList.add('dragover'); }); + carboneUploadZone.addEventListener('dragleave', () => carboneUploadZone.classList.remove('dragover')); + carboneUploadZone.addEventListener('drop', e => { + e.preventDefault(); + carboneUploadZone.classList.remove('dragover'); + if (e.dataTransfer.files.length) uploadCarboneTemplate(e.dataTransfer.files[0]); + }); +} + +async function uploadCarboneTemplate(file) { + if (!file) return; + const name = document.getElementById('carbone-template-name').value || file.name.replace(/\.[^.]+$/, ''); + const status = document.getElementById('carbone-upload-status'); + status.innerHTML = '
Uploading template...
'; + + const formData = new FormData(); + formData.append('template', file); + formData.append('name', name); + + try { + const resp = await fetch(CARBONE_API + '/templates/upload', { method: 'POST', body: formData }); + const data = await resp.json(); + if (data.success) { + status.innerHTML = '
Template uploaded successfully!
'; + toast('Template uploaded: ' + name); + document.getElementById('carbone-template-name').value = ''; + loadCarboneTemplates(); + } else { + status.innerHTML = '
Upload failed: ' + (data.error || 'Unknown error') + '
'; + } + } catch (e) { + status.innerHTML = '
Upload failed: ' + e.message + '
'; + } +} + +// ============================================================ +// LIST TEMPLATES +// ============================================================ +async function loadCarboneTemplates() { + const list = document.getElementById('carbone-templates-list'); + if (!list) return; + list.innerHTML = '
Loading...
'; + + try { + const resp = await fetch(CARBONE_API + '/templates'); + const data = await resp.json(); + if (!data.templates || !data.templates.length) { + list.innerHTML = '

No custom templates uploaded yet. Download the master template, customize it in Word, and upload it here.

'; + return; + } + list.innerHTML = data.templates.map(t => ` +
+
+ ${t.name} +
Uploaded: ${new Date(t.uploadedAt).toLocaleDateString()} · ${t.originalName}
+
+
+ + +
+
+ `).join(''); + } catch (e) { + list.innerHTML = '

Error loading templates: ' + e.message + '

'; + } +} + +// ============================================================ +// RENDER SAMPLE CV +// ============================================================ +async function renderSampleCV(templateId) { + toast('Generating sample CV PDF...'); + + // Get first candidate for sample data + try { + const candidatesResp = await fetch('/api/candidates?limit=1'); + const candidatesData = await candidatesResp.json(); + let cvData = null; + + if (candidatesData.candidates && candidatesData.candidates.length > 0) { + const fullResp = await fetch('/api/candidates/' + candidatesData.candidates[0].id); + cvData = await fullResp.json(); + } + + if (!cvData) { + toast('Upload a CV first to generate a sample', 'error'); + return; + } + + const resp = await fetch(CARBONE_API + '/templates/' + templateId + '/render', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ candidateData: cvData }) + }); + + if (!resp.ok) { + const err = await resp.json(); + throw new Error(err.error || 'Render failed'); + } + + const blob = await resp.blob(); + const url = URL.createObjectURL(blob); + window.open(url, '_blank'); + toast('Sample CV generated'); + } catch (e) { + toast('Render failed: ' + e.message, 'error'); + } +} + +// ============================================================ +// DELETE TEMPLATE +// ============================================================ +async function deleteCarboneTemplate(templateId) { + if (!confirm('Delete this template?')) return; + try { + await fetch(CARBONE_API + '/templates/' + templateId, { method: 'DELETE' }); + toast('Template deleted'); + loadCarboneTemplates(); + } catch (e) { + toast('Delete failed: ' + e.message, 'error'); + } +} \ No newline at end of file diff --git a/static/index.html b/static/index.html index f2c7a17..c10af1f 100644 --- a/static/index.html +++ b/static/index.html @@ -23,6 +23,7 @@
  • Candidates
  • Upload CV
  • Templates
  • +
  • Word Templates
  • Requirements
  • Generated CVs
  • AI Chat
  • @@ -76,7 +77,38 @@
    - + +
    +

    Word Templates (Carbone)

    +
    +
    How It Works
    +
    + 1. Download the master template (.docx) with all data tags
    + 2. Open in Microsoft Word, customize fonts/colors/layout, save
    + 3. Upload your customized .docx back here
    + 4. Generate CVs by merging candidate data with your template -> PDF +
    + +
    +
    +
    Upload Custom Template
    +
    + + +
    +
    +

    Drop your .docx file here or click to browse

    +

    Must be a .docx file with Carbone tags from the master template

    +
    + +
    +
    +
    +
    Saved Templates
    +
    +
    +
    +

    Requirement Requests

    @@ -113,5 +145,6 @@ + \ No newline at end of file