/**
* Research PDF Generation Script
* Generates PDFs from standalone markdown research documents
*/
const puppeteer = require('puppeteer');
const marked = require('marked');
const fs = require('fs').promises;
const path = require('path');
const RESEARCH_DIR = path.join(__dirname, '../docs/research');
const OUTPUT_DIR = path.join(__dirname, '../public/downloads');
/**
* HTML template for research PDFs
*/
function generatePdfHtml(title, content, metadata = {}) {
return `
${title}
${content}
`;
}
/**
* Extract metadata from markdown frontmatter or header
*/
function extractMetadata(markdown) {
const lines = markdown.split('\n');
const metadata = {};
// Look for title in first H1
const h1Match = markdown.match(/^#\s+(.+)$/m);
if (h1Match) {
metadata.title = h1Match[1];
}
// Look for version
const versionMatch = markdown.match(/\*\*Version:\*\*\s+(.+)/);
if (versionMatch) {
metadata.version = versionMatch[1];
}
// Look for date
const dateMatch = markdown.match(/\*\*Date:\*\*\s+(.+)/);
if (dateMatch) {
metadata.date = dateMatch[1];
}
return metadata;
}
/**
* Generate PDF from markdown file
*/
async function generatePdfFromMarkdown(inputFile, outputFilename, browser) {
try {
console.log(`\nProcessing: ${path.basename(inputFile)}`);
// Read markdown
const markdown = await fs.readFile(inputFile, 'utf-8');
// Extract metadata
const metadata = extractMetadata(markdown);
const title = metadata.title || path.basename(inputFile, '.md');
console.log(` Title: ${title}`);
// Convert markdown to HTML
const contentHtml = marked.parse(markdown);
// Generate full HTML
const html = generatePdfHtml(title, contentHtml, {
version: metadata.version,
date: metadata.date,
type: 'Research Paper'
});
// Create page
const page = await browser.newPage();
await page.setContent(html, { waitUntil: 'networkidle0' });
// Generate PDF
const outputPath = path.join(OUTPUT_DIR, outputFilename);
await page.pdf({
path: outputPath,
format: 'A4',
printBackground: true,
margin: {
top: '2cm',
right: '2cm',
bottom: '2cm',
left: '2cm'
},
displayHeaderFooter: true,
headerTemplate: '',
footerTemplate: `
/
`
});
await page.close();
console.log(` ✓ Generated: ${outputFilename}`);
return { success: true, filename: outputFilename };
} catch (error) {
console.error(` ✗ Failed: ${error.message}`);
return { success: false, error: error.message };
}
}
/**
* Main execution
*/
async function main() {
console.log('=== Research PDF Generation ===');
let browser;
try {
// Ensure output directory exists
await fs.mkdir(OUTPUT_DIR, { recursive: true });
console.log(`Output directory: ${OUTPUT_DIR}`);
// Launch browser
console.log('\nLaunching browser...');
browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox']
});
console.log('✓ Browser ready');
// Generate PDFs
const results = [];
// Full research paper
results.push(await generatePdfFromMarkdown(
path.join(RESEARCH_DIR, 'tractatus-inflection-point-2025.md'),
'structural-governance-for-agentic-ai-tractatus-inflection-point.pdf',
browser
));
// Executive summary
results.push(await generatePdfFromMarkdown(
path.join(RESEARCH_DIR, 'executive-summary-tractatus-inflection-point.md'),
'executive-summary-tractatus-inflection-point.pdf',
browser
));
// Summary
console.log('\n=== Generation Complete ===\n');
const successful = results.filter(r => r.success).length;
const failed = results.filter(r => !r.success).length;
console.log(`✓ Successful: ${successful}`);
if (failed > 0) {
console.log(`✗ Failed: ${failed}`);
}
console.log(`\nPDFs saved to: ${OUTPUT_DIR}`);
} catch (error) {
console.error('\n✗ Error:', error.message);
process.exit(1);
} finally {
if (browser) await browser.close();
}
}
// Run if called directly
if (require.main === module) {
main();
}
module.exports = { generatePdfFromMarkdown, generatePdfHtml };