tailprint 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +160 -2
  3. package/dist/config/env.d.ts +77 -0
  4. package/dist/config/env.js +38 -0
  5. package/dist/config/env.js.map +1 -0
  6. package/dist/config/plans.d.ts +15 -0
  7. package/dist/config/plans.js +104 -0
  8. package/dist/config/plans.js.map +1 -0
  9. package/dist/config/templates.d.ts +10 -0
  10. package/dist/config/templates.js +746 -0
  11. package/dist/config/templates.js.map +1 -0
  12. package/dist/mcp/server.d.ts +10 -0
  13. package/dist/mcp/server.js +897 -0
  14. package/dist/mcp/server.js.map +1 -0
  15. package/dist/mcp/stdio.d.ts +2 -0
  16. package/dist/mcp/stdio.js +27 -0
  17. package/dist/mcp/stdio.js.map +1 -0
  18. package/dist/routes/dashboard.d.ts +2 -0
  19. package/dist/routes/dashboard.js +454 -0
  20. package/dist/routes/dashboard.js.map +1 -0
  21. package/dist/routes/mcp.d.ts +2 -0
  22. package/dist/routes/mcp.js +329 -0
  23. package/dist/routes/mcp.js.map +1 -0
  24. package/dist/routes/render.d.ts +2 -0
  25. package/dist/routes/render.js +444 -0
  26. package/dist/routes/render.js.map +1 -0
  27. package/dist/routes/scrape.d.ts +2 -0
  28. package/dist/routes/scrape.js +132 -0
  29. package/dist/routes/scrape.js.map +1 -0
  30. package/dist/routes/templates.d.ts +2 -0
  31. package/dist/routes/templates.js +2449 -0
  32. package/dist/routes/templates.js.map +1 -0
  33. package/dist/routes/usage.d.ts +31 -0
  34. package/dist/routes/usage.js +73 -0
  35. package/dist/routes/usage.js.map +1 -0
  36. package/dist/routes/webhooks.d.ts +2 -0
  37. package/dist/routes/webhooks.js +227 -0
  38. package/dist/routes/webhooks.js.map +1 -0
  39. package/dist/schemas/render.d.ts +422 -0
  40. package/dist/schemas/render.js +91 -0
  41. package/dist/schemas/render.js.map +1 -0
  42. package/dist/schemas/scrape.d.ts +65 -0
  43. package/dist/schemas/scrape.js +24 -0
  44. package/dist/schemas/scrape.js.map +1 -0
  45. package/dist/server.d.ts +2 -0
  46. package/dist/server.js +206 -0
  47. package/dist/server.js.map +1 -0
  48. package/dist/services/browser.d.ts +21 -0
  49. package/dist/services/browser.js +299 -0
  50. package/dist/services/browser.js.map +1 -0
  51. package/dist/services/carousel.d.ts +43 -0
  52. package/dist/services/carousel.js +300 -0
  53. package/dist/services/carousel.js.map +1 -0
  54. package/dist/services/chart.d.ts +29 -0
  55. package/dist/services/chart.js +129 -0
  56. package/dist/services/chart.js.map +1 -0
  57. package/dist/services/eInvoice.d.ts +62 -0
  58. package/dist/services/eInvoice.js +413 -0
  59. package/dist/services/eInvoice.js.map +1 -0
  60. package/dist/services/email.d.ts +24 -0
  61. package/dist/services/email.js +177 -0
  62. package/dist/services/email.js.map +1 -0
  63. package/dist/services/mockup.d.ts +14 -0
  64. package/dist/services/mockup.js +130 -0
  65. package/dist/services/mockup.js.map +1 -0
  66. package/dist/services/pdfStamper.d.ts +16 -0
  67. package/dist/services/pdfStamper.js +47 -0
  68. package/dist/services/pdfStamper.js.map +1 -0
  69. package/dist/services/qr.d.ts +20 -0
  70. package/dist/services/qr.js +44 -0
  71. package/dist/services/qr.js.map +1 -0
  72. package/dist/services/redis.d.ts +65 -0
  73. package/dist/services/redis.js +318 -0
  74. package/dist/services/redis.js.map +1 -0
  75. package/dist/services/scraper.d.ts +16 -0
  76. package/dist/services/scraper.js +138 -0
  77. package/dist/services/scraper.js.map +1 -0
  78. package/dist/services/storage.d.ts +38 -0
  79. package/dist/services/storage.js +123 -0
  80. package/dist/services/storage.js.map +1 -0
  81. package/dist/services/stripe.d.ts +32 -0
  82. package/dist/services/stripe.js +229 -0
  83. package/dist/services/stripe.js.map +1 -0
  84. package/dist/services/visualDiff.d.ts +33 -0
  85. package/dist/services/visualDiff.js +218 -0
  86. package/dist/services/visualDiff.js.map +1 -0
  87. package/package.json +74 -4
@@ -0,0 +1,300 @@
1
+ import { browserService } from './browser.js';
2
+ import { storageService } from './storage.js';
3
+ export class CarouselService {
4
+ static instance = null;
5
+ constructor() { }
6
+ static getInstance() {
7
+ if (!CarouselService.instance) {
8
+ CarouselService.instance = new CarouselService();
9
+ }
10
+ return CarouselService.instance;
11
+ }
12
+ /**
13
+ * Parses simple markdown divided by "---" into carousel slides.
14
+ */
15
+ parseMarkdownToSlides(markdown) {
16
+ const rawSlides = markdown.split(/\n\s*---\s*\n/).filter((s) => s.trim().length > 0);
17
+ const slides = [];
18
+ for (let i = 0; i < rawSlides.length; i++) {
19
+ const lines = rawSlides[i].trim().split('\n');
20
+ let headline = '';
21
+ let tag = `SLIDE 0${i + 1}`;
22
+ let takeaway = '';
23
+ const contentLines = [];
24
+ for (const line of lines) {
25
+ const trimmed = line.trim();
26
+ if (trimmed.startsWith('# ') || trimmed.startsWith('## ')) {
27
+ headline = trimmed.replace(/^#+\s*/, '');
28
+ }
29
+ else if (trimmed.toLowerCase().startsWith('tag:') || trimmed.toLowerCase().startsWith('category:')) {
30
+ tag = trimmed.replace(/^(tag|category):\s*/i, '');
31
+ }
32
+ else if (trimmed.toLowerCase().startsWith('takeaway:') || trimmed.toLowerCase().startsWith('pro tip:')) {
33
+ takeaway = trimmed.replace(/^(takeaway|pro tip):\s*/i, '');
34
+ }
35
+ else if (trimmed.length > 0) {
36
+ contentLines.push(trimmed);
37
+ }
38
+ }
39
+ if (!headline && contentLines.length > 0) {
40
+ headline = contentLines.shift() || `Insight #${i + 1}`;
41
+ }
42
+ slides.push({
43
+ headline: headline || `Key Takeaway #${i + 1}`,
44
+ content: contentLines.length > 0 ? contentLines : 'Follow this framework to streamline your workflow.',
45
+ tag,
46
+ takeaway: takeaway || undefined,
47
+ slideNumber: i + 1,
48
+ });
49
+ }
50
+ return slides;
51
+ }
52
+ /**
53
+ * Generates a multi-page 1080x1350 px carousel PDF (LinkedIn standard)
54
+ * and a high-resolution PNG preview of the cover slide.
55
+ */
56
+ async renderCarousel(options) {
57
+ let slides = [];
58
+ if (options.slides && options.slides.length > 0) {
59
+ slides = options.slides.map((s, idx) => ({
60
+ ...s,
61
+ slideNumber: s.slideNumber || idx + 1,
62
+ tag: s.tag || `0${idx + 1}`,
63
+ }));
64
+ }
65
+ else if (options.markdown) {
66
+ slides = this.parseMarkdownToSlides(options.markdown);
67
+ }
68
+ else {
69
+ slides = [
70
+ {
71
+ headline: options.title || '10x Your Productivity with AI Automation',
72
+ content: 'Modern tools allow agents to execute complex visual workflows without human intervention.',
73
+ tag: 'FRAMEWORK',
74
+ takeaway: 'Autonomous tools convert natural language into production assets.',
75
+ slideNumber: 1,
76
+ },
77
+ {
78
+ headline: 'Eliminate Browser Infrastructure',
79
+ content: [
80
+ 'Running headless Chrome locally causes OOM crashes and zombie processes.',
81
+ 'Cloud render engines give you sub-500ms execution with zero server maintenance.',
82
+ 'Directly render Tailwind HTML without build steps.',
83
+ ],
84
+ tag: 'INFRASTRUCTURE',
85
+ takeaway: 'Focus on your core app; let specialized microservices handle rendering.',
86
+ slideNumber: 2,
87
+ },
88
+ {
89
+ headline: 'Start Automating Today',
90
+ content: 'Connect Tailprint to your agent runtime and generate print-ready PDFs in seconds.',
91
+ tag: 'NEXT STEPS',
92
+ takeaway: 'Save this post and follow for weekly architectural blueprints.',
93
+ slideNumber: 3,
94
+ },
95
+ ];
96
+ }
97
+ const totalSlides = slides.length;
98
+ const authorName = options.author?.name || 'Tailprint Insights';
99
+ const authorHandle = options.author?.handle || '@tailprint';
100
+ const authorRole = options.author?.role || 'High-Performance PDF & Render Engine';
101
+ const theme = options.theme || 'dark';
102
+ // Theme color palette definitions
103
+ const themeStyles = {
104
+ dark: {
105
+ bg: 'bg-[#080c14]',
106
+ border: 'border-slate-800',
107
+ textHeadline: 'text-white',
108
+ textContent: 'text-slate-300',
109
+ cardBg: 'bg-slate-900/80',
110
+ accentGradient: 'from-indigo-500 to-purple-500',
111
+ accentText: 'text-indigo-400',
112
+ tagBg: 'bg-indigo-950/70 border-indigo-800/50 text-indigo-300',
113
+ takeawayBox: 'bg-indigo-950/40 border-indigo-500/30 text-indigo-200',
114
+ },
115
+ light: {
116
+ bg: 'bg-slate-50',
117
+ border: 'border-slate-200',
118
+ textHeadline: 'text-slate-900',
119
+ textContent: 'text-slate-700',
120
+ cardBg: 'bg-white',
121
+ accentGradient: 'from-blue-600 to-indigo-600',
122
+ accentText: 'text-indigo-600',
123
+ tagBg: 'bg-indigo-50 border-indigo-200 text-indigo-700',
124
+ takeawayBox: 'bg-indigo-50 border-indigo-300 text-indigo-900',
125
+ },
126
+ emerald: {
127
+ bg: 'bg-[#06110f]',
128
+ border: 'border-emerald-900/50',
129
+ textHeadline: 'text-white',
130
+ textContent: 'text-slate-300',
131
+ cardBg: 'bg-slate-900/80',
132
+ accentGradient: 'from-emerald-400 to-teal-400',
133
+ accentText: 'text-emerald-400',
134
+ tagBg: 'bg-emerald-950/70 border-emerald-800/50 text-emerald-300',
135
+ takeawayBox: 'bg-emerald-950/40 border-emerald-500/30 text-emerald-200',
136
+ },
137
+ gradient: {
138
+ bg: 'bg-gradient-to-br from-[#0a0e1c] via-[#09152e] to-[#0a0e1c]',
139
+ border: 'border-indigo-800/40',
140
+ textHeadline: 'text-white',
141
+ textContent: 'text-slate-200',
142
+ cardBg: 'bg-slate-900/60',
143
+ accentGradient: 'from-purple-400 via-pink-400 to-amber-300',
144
+ accentText: 'text-pink-400',
145
+ tagBg: 'bg-purple-950/70 border-purple-800/50 text-purple-300',
146
+ takeawayBox: 'bg-purple-950/40 border-pink-500/30 text-pink-200',
147
+ },
148
+ }[theme];
149
+ // Build multi-page HTML
150
+ const slidesHtml = slides
151
+ .map((slide, index) => {
152
+ const isCover = index === 0;
153
+ const isLast = index === totalSlides - 1;
154
+ const slideNum = index + 1;
155
+ const contentHtml = Array.isArray(slide.content)
156
+ ? `<ul class="space-y-6 text-2xl ${themeStyles.textContent}">
157
+ ${slide.content
158
+ .map((c) => `<li class="flex items-start space-x-4">
159
+ <span class="flex-shrink-0 w-8 h-8 rounded-full ${themeStyles.tagBg} flex items-center justify-center text-base font-bold mt-1">✓</span>
160
+ <span class="leading-relaxed">${c}</span>
161
+ </li>`)
162
+ .join('')}
163
+ </ul>`
164
+ : `<p class="text-3xl leading-relaxed ${themeStyles.textContent} font-normal">${slide.content}</p>`;
165
+ return `
166
+ <div class="slide ${themeStyles.bg} flex flex-col justify-between p-20 relative">
167
+
168
+ <!-- Top Decorative Glow -->
169
+ <div class="absolute -top-32 -right-32 w-96 h-96 bg-gradient-to-br ${themeStyles.accentGradient} opacity-20 blur-3xl pointer-events-none rounded-full"></div>
170
+
171
+ <!-- Slide Header -->
172
+ <header class="flex items-center justify-between pb-8 border-b ${themeStyles.border} relative z-10">
173
+ <div class="flex items-center space-x-5">
174
+ <div class="w-16 h-16 rounded-2xl bg-gradient-to-tr ${themeStyles.accentGradient} p-0.5 shadow-lg">
175
+ <div class="w-full h-full bg-[#080c14] rounded-2xl flex items-center justify-center font-black text-xl text-white">
176
+ ${authorName.slice(0, 2).toUpperCase()}
177
+ </div>
178
+ </div>
179
+ <div>
180
+ <div class="text-2xl font-bold ${themeStyles.textHeadline}">${authorName}</div>
181
+ <div class="text-lg text-slate-400 font-medium">${authorRole}</div>
182
+ </div>
183
+ </div>
184
+
185
+ <div class="px-5 py-2 rounded-full text-sm font-black tracking-wider uppercase border ${themeStyles.tagBg}">
186
+ ${slide.tag || `PART 0${slideNum}`}
187
+ </div>
188
+ </header>
189
+
190
+ <!-- Center Slide Main Content -->
191
+ <main class="my-auto py-12 relative z-10 space-y-10">
192
+
193
+ <h2 class="text-6xl sm:text-7xl font-black ${themeStyles.textHeadline} tracking-tight leading-[1.15]">
194
+ ${slide.headline}
195
+ </h2>
196
+
197
+ <div class="pt-4">
198
+ ${contentHtml}
199
+ </div>
200
+
201
+ ${slide.takeaway
202
+ ? `<div class="p-8 rounded-3xl border ${themeStyles.takeawayBox} shadow-xl backdrop-blur-sm">
203
+ <div class="text-xs font-black uppercase tracking-wider mb-2 opacity-80">💡 Key Takeaway</div>
204
+ <div class="text-2xl font-bold leading-snug">${slide.takeaway}</div>
205
+ </div>`
206
+ : ''}
207
+
208
+ </main>
209
+
210
+ <!-- Slide Footer & Pagination Strip -->
211
+ <footer class="pt-8 border-t ${themeStyles.border} flex items-center justify-between relative z-10 text-xl text-slate-400 font-mono">
212
+ <div class="flex items-center space-x-3">
213
+ <span class="font-bold ${themeStyles.accentText}">${authorHandle}</span>
214
+ <span>•</span>
215
+ <span class="text-slate-500">${slideNum} / ${totalSlides}</span>
216
+ </div>
217
+
218
+ <div class="flex items-center space-x-4">
219
+ ${isLast
220
+ ? `<div class="px-6 py-2.5 rounded-2xl bg-gradient-to-r ${themeStyles.accentGradient} text-white font-black text-base shadow-lg">
221
+ 📌 Save & Share
222
+ </div>`
223
+ : `<div class="flex items-center space-x-2 font-bold ${themeStyles.accentText}">
224
+ <span>Swipe</span>
225
+ <span>→</span>
226
+ </div>`}
227
+ </div>
228
+ </footer>
229
+
230
+ </div>`;
231
+ })
232
+ .join('\n');
233
+ const fullHtml = `<!DOCTYPE html>
234
+ <html>
235
+ <head>
236
+ <meta charset="utf-8" />
237
+ <script src="https://cdn.tailwindcss.com"></script>
238
+ <style>
239
+ @page {
240
+ size: 1080px 1350px;
241
+ margin: 0;
242
+ }
243
+ body {
244
+ margin: 0;
245
+ padding: 0;
246
+ background-color: transparent;
247
+ -webkit-print-color-adjust: exact;
248
+ }
249
+ .slide {
250
+ width: 1080px;
251
+ height: 1350px;
252
+ page-break-after: always;
253
+ box-sizing: border-box;
254
+ overflow: hidden;
255
+ }
256
+ .slide:last-child {
257
+ page-break-after: avoid;
258
+ }
259
+ </style>
260
+ </head>
261
+ <body class="m-0 p-0 font-sans antialiased">
262
+ ${slidesHtml}
263
+ </body>
264
+ </html>`;
265
+ // 1. Render multi-page vector PDF for LinkedIn
266
+ const pdfBuffer = await browserService.renderPdf({
267
+ html: fullHtml,
268
+ format: 'A4', // overridden by CSS @page size 1080px 1350px
269
+ print_background: true,
270
+ });
271
+ // 2. Render cover slide as high-res PNG preview
272
+ const previewImageBuffer = await browserService.renderImage({
273
+ html: fullHtml,
274
+ width: 1080,
275
+ height: 1350,
276
+ type: 'png',
277
+ delay_ms: 100,
278
+ });
279
+ // 3. Store on Tailprint Hosted CDN
280
+ const pdfStored = await storageService.saveFile(pdfBuffer, {
281
+ mimeType: 'application/pdf',
282
+ extension: 'pdf',
283
+ filename: 'linkedin-carousel.pdf',
284
+ });
285
+ const previewStored = await storageService.saveFile(previewImageBuffer, {
286
+ mimeType: 'image/png',
287
+ extension: 'png',
288
+ filename: 'carousel-cover.png',
289
+ });
290
+ return {
291
+ pdfBuffer,
292
+ previewImageBuffer,
293
+ slideCount: totalSlides,
294
+ pdfStored,
295
+ previewStored,
296
+ };
297
+ }
298
+ }
299
+ export const carouselService = CarouselService.getInstance();
300
+ //# sourceMappingURL=carousel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"carousel.js","sourceRoot":"","sources":["../../src/services/carousel.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ export interface ChartDataset {
2
+ label: string;
3
+ data: number[];
4
+ backgroundColor?: string | string[];
5
+ borderColor?: string | string[];
6
+ borderWidth?: number;
7
+ fill?: boolean;
8
+ }
9
+ export interface ChartData {
10
+ labels: string[];
11
+ datasets: ChartDataset[];
12
+ }
13
+ export interface RenderChartOptions {
14
+ type: 'bar' | 'line' | 'pie' | 'doughnut' | 'radar' | 'polarArea';
15
+ data: ChartData;
16
+ title?: string;
17
+ theme?: 'dark' | 'light';
18
+ width?: number;
19
+ height?: number;
20
+ }
21
+ export declare class ChartService {
22
+ private static instance;
23
+ static getInstance(): ChartService;
24
+ /**
25
+ * Renders high-DPI data visualizations using Chart.js inside headless Chromium.
26
+ */
27
+ renderChart(options: RenderChartOptions): Promise<Buffer>;
28
+ }
29
+ export declare const chartService: ChartService;
@@ -0,0 +1,129 @@
1
+ import { browserService } from './browser.js';
2
+ export class ChartService {
3
+ static instance = null;
4
+ static getInstance() {
5
+ if (!ChartService.instance) {
6
+ ChartService.instance = new ChartService();
7
+ }
8
+ return ChartService.instance;
9
+ }
10
+ /**
11
+ * Renders high-DPI data visualizations using Chart.js inside headless Chromium.
12
+ */
13
+ async renderChart(options) {
14
+ const { type, data, title, theme = 'dark', width = 1000, height = 600, } = options;
15
+ const isDark = theme === 'dark';
16
+ const bgColor = isDark ? '#090d16' : '#ffffff';
17
+ const textColor = isDark ? '#f8fafc' : '#0f172a';
18
+ const subtextColor = isDark ? '#94a3b8' : '#64748b';
19
+ const gridColor = isDark ? 'rgba(255, 255, 255, 0.08)' : 'rgba(0, 0, 0, 0.06)';
20
+ // Provide modern vibrant default colors if none provided
21
+ const palette = [
22
+ '#6366f1', // indigo
23
+ '#10b981', // emerald
24
+ '#f59e0b', // amber
25
+ '#ec4899', // pink
26
+ '#06b6d4', // cyan
27
+ '#8b5cf6', // purple
28
+ ];
29
+ const processedData = {
30
+ labels: data.labels,
31
+ datasets: data.datasets.map((ds, idx) => {
32
+ const defaultColor = palette[idx % palette.length];
33
+ return {
34
+ ...ds,
35
+ backgroundColor: ds.backgroundColor ||
36
+ (['pie', 'doughnut', 'polarArea'].includes(type) ? palette : defaultColor),
37
+ borderColor: ds.borderColor || (type === 'line' ? defaultColor : undefined),
38
+ borderWidth: ds.borderWidth ?? (type === 'line' ? 3 : 1),
39
+ };
40
+ }),
41
+ };
42
+ const isRadial = ['pie', 'doughnut', 'radar', 'polarArea'].includes(type);
43
+ const html = `<!DOCTYPE html>
44
+ <html lang="en">
45
+ <head>
46
+ <meta charset="UTF-8">
47
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
48
+ <style>
49
+ * { box-sizing: border-box; }
50
+ body {
51
+ margin: 0;
52
+ padding: 32px;
53
+ background-color: ${bgColor};
54
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
55
+ display: flex;
56
+ flex-direction: column;
57
+ align-items: center;
58
+ justify-content: center;
59
+ width: ${width}px;
60
+ height: ${height}px;
61
+ overflow: hidden;
62
+ }
63
+ #container {
64
+ width: 100%;
65
+ height: 100%;
66
+ position: relative;
67
+ }
68
+ </style>
69
+ </head>
70
+ <body>
71
+ <div id="container">
72
+ <canvas id="chartCanvas"></canvas>
73
+ </div>
74
+ <script>
75
+ const ctx = document.getElementById('chartCanvas').getContext('2d');
76
+ new Chart(ctx, {
77
+ type: ${JSON.stringify(type)},
78
+ data: ${JSON.stringify(processedData)},
79
+ options: {
80
+ responsive: true,
81
+ maintainAspectRatio: false,
82
+ animation: false,
83
+ plugins: {
84
+ title: {
85
+ display: ${title ? 'true' : 'false'},
86
+ text: ${JSON.stringify(title || '')},
87
+ color: ${JSON.stringify(textColor)},
88
+ font: { size: 20, weight: 'bold' },
89
+ padding: { top: 0, bottom: 20 }
90
+ },
91
+ legend: {
92
+ display: true,
93
+ position: 'top',
94
+ labels: {
95
+ color: ${JSON.stringify(subtextColor)},
96
+ font: { size: 13, weight: '500' },
97
+ padding: 16
98
+ }
99
+ }
100
+ },
101
+ scales: ${isRadial
102
+ ? '{}'
103
+ : `{
104
+ x: {
105
+ grid: { color: ${JSON.stringify(gridColor)} },
106
+ ticks: { color: ${JSON.stringify(subtextColor)}, font: { size: 12 } }
107
+ },
108
+ y: {
109
+ grid: { color: ${JSON.stringify(gridColor)} },
110
+ ticks: { color: ${JSON.stringify(subtextColor)}, font: { size: 12 } }
111
+ }
112
+ }`}
113
+ }
114
+ });
115
+ </script>
116
+ </body>
117
+ </html>`;
118
+ return browserService.renderImage({
119
+ html,
120
+ width,
121
+ height,
122
+ type: 'png',
123
+ wait_for: '#chartCanvas',
124
+ delay_ms: 150,
125
+ });
126
+ }
127
+ }
128
+ export const chartService = ChartService.getInstance();
129
+ //# sourceMappingURL=chart.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chart.js","sourceRoot":"","sources":["../../src/services/chart.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ import { type StoredFileMetadata } from './storage.js';
2
+ export interface EInvoiceParty {
3
+ name: string;
4
+ vatId?: string;
5
+ country: string;
6
+ street?: string;
7
+ city?: string;
8
+ postcode?: string;
9
+ email?: string;
10
+ }
11
+ export interface EInvoiceLineItem {
12
+ name: string;
13
+ quantity: number;
14
+ unitPrice: number;
15
+ vatRate?: number;
16
+ }
17
+ export interface EInvoiceData {
18
+ invoiceNumber: string;
19
+ issueDate?: string;
20
+ dueDate?: string;
21
+ currency?: string;
22
+ seller: EInvoiceParty;
23
+ buyer: EInvoiceParty;
24
+ lineItems: EInvoiceLineItem[];
25
+ paymentIban?: string;
26
+ paymentBic?: string;
27
+ notes?: string;
28
+ profile?: 'BASIC' | 'EN16931';
29
+ expiresIn?: '1h' | '24h' | '7d';
30
+ }
31
+ export interface GenerateEInvoiceResult {
32
+ pdfBuffer: Buffer;
33
+ xml: string;
34
+ invoiceNumber: string;
35
+ currency: string;
36
+ netTotal: number;
37
+ taxTotal: number;
38
+ grossTotal: number;
39
+ pdfStored?: StoredFileMetadata;
40
+ }
41
+ export declare class EInvoiceService {
42
+ private static instance;
43
+ private constructor();
44
+ static getInstance(): EInvoiceService;
45
+ private escapeXml;
46
+ private formatDate102;
47
+ /**
48
+ * Generates a fully compliant Factur-X / ZUGFeRD / EN 16931 electronic invoice XML (CII syntax).
49
+ */
50
+ generateFacturXXml(data: EInvoiceData): string;
51
+ /**
52
+ * Attaches the Factur-X / ZUGFeRD XML document directly into the PDF document catalog,
53
+ * producing a compliant Hybrid PDF/A-3 electronic invoice.
54
+ */
55
+ embedFacturXXml(pdfBuffer: Buffer, xmlString: string): Promise<Buffer>;
56
+ /**
57
+ * Generates both a pixel-perfect visual HTML invoice with Tailwind CSS
58
+ * and attaches the Factur-X XML into the resulting PDF.
59
+ */
60
+ generateHybridInvoice(data: EInvoiceData): Promise<GenerateEInvoiceResult>;
61
+ }
62
+ export declare const eInvoiceService: EInvoiceService;