@pyreon/document 0.9.0 → 0.11.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.
- package/lib/analysis/index.js.html +1 -1
- package/lib/confluence-Bd3ua1Ut.js.map +1 -1
- package/lib/csv-COrS4qdy.js.map +1 -1
- package/lib/discord-BLUnkEh9.js.map +1 -1
- package/lib/{dist-BsqdI2nY.js → dist-CYL41kqQ.js} +2 -2
- package/lib/dist-CYL41kqQ.js.map +1 -0
- package/lib/{docx-BEBOihjl.js → docx-uNAel545.js} +7 -2
- package/lib/docx-uNAel545.js.map +1 -0
- package/lib/email-D0bbfWq4.js.map +1 -1
- package/lib/{exceljs-BoIDUUaw.js → exceljs-BYETsesT.js} +314 -314
- package/lib/exceljs-BYETsesT.js.map +1 -0
- package/lib/google-chat-CkKCBUWC.js.map +1 -1
- package/lib/html-B5biprN2.js.map +1 -1
- package/lib/index.js +17 -8
- package/lib/index.js.map +1 -1
- package/lib/markdown-CdtlFGC0.js.map +1 -1
- package/lib/notion-iG2C5bEY.js.map +1 -1
- package/lib/{pdf-DIUQUEdj.js → pdf-IuBgTb3T.js} +9 -3
- package/lib/pdf-IuBgTb3T.js.map +1 -0
- package/lib/{pdfmake-DnmLxK4Q.js → pdfmake-CKMX5URW.js} +2 -4
- package/lib/pdfmake-CKMX5URW.js.map +1 -0
- package/lib/{pptx-Dd33oL3_.js → pptx-DXiMiYFM.js} +7 -2
- package/lib/pptx-DXiMiYFM.js.map +1 -0
- package/lib/{pptxgen.es-COcgXsyx.js → pptxgen.es-FsqHs8mD.js} +3 -6
- package/lib/pptxgen.es-FsqHs8mD.js.map +1 -0
- package/lib/sanitize-O_3j1mNJ.js.map +1 -1
- package/lib/slack-BI3EQwYm.js.map +1 -1
- package/lib/svg-BKxumy-p.js.map +1 -1
- package/lib/teams-Cwz9lce0.js.map +1 -1
- package/lib/telegram-gYFqyMXb.js.map +1 -1
- package/lib/text-l1XNXBOC.js.map +1 -1
- package/lib/types/index.d.ts +43 -39
- package/lib/types/index.d.ts.map +1 -1
- package/lib/{vfs_fonts-Df1kkZ4Y.js → vfs_fonts-Cap07Jg3.js} +2 -2
- package/lib/vfs_fonts-Cap07Jg3.js.map +1 -0
- package/lib/whatsapp-CjSGoOKx.js.map +1 -1
- package/lib/{xlsx-Bb5TWyXQ.js → xlsx-Cvu4LBNy.js} +8 -2
- package/lib/xlsx-Cvu4LBNy.js.map +1 -0
- package/package.json +19 -7
- package/src/builder.ts +53 -44
- package/src/download.ts +32 -36
- package/src/env.d.ts +3 -17
- package/src/index.ts +6 -8
- package/src/nodes.ts +45 -45
- package/src/render.ts +45 -118
- package/src/renderers/confluence.ts +64 -80
- package/src/renderers/csv.ts +11 -18
- package/src/renderers/discord.ts +38 -50
- package/src/renderers/docx.ts +78 -120
- package/src/renderers/email.ts +73 -92
- package/src/renderers/google-chat.ts +35 -47
- package/src/renderers/html.ts +78 -101
- package/src/renderers/markdown.ts +43 -53
- package/src/renderers/notion.ts +63 -85
- package/src/renderers/pdf.ts +92 -115
- package/src/renderers/pptx.ts +60 -66
- package/src/renderers/slack.ts +49 -61
- package/src/renderers/svg.ts +49 -63
- package/src/renderers/teams.ts +68 -80
- package/src/renderers/telegram.ts +40 -54
- package/src/renderers/text.ts +44 -66
- package/src/renderers/whatsapp.ts +34 -48
- package/src/renderers/xlsx.ts +47 -61
- package/src/sanitize.ts +21 -25
- package/src/tests/document.test.ts +1337 -1385
- package/src/tests/stress.test.ts +350 -0
- package/src/types.ts +66 -65
- package/lib/dist-BsqdI2nY.js.map +0 -1
- package/lib/docx-BEBOihjl.js.map +0 -1
- package/lib/exceljs-BoIDUUaw.js.map +0 -1
- package/lib/pdf-DIUQUEdj.js.map +0 -1
- package/lib/pdfmake-DnmLxK4Q.js.map +0 -1
- package/lib/pptx-Dd33oL3_.js.map +0 -1
- package/lib/pptxgen.es-COcgXsyx.js.map +0 -1
- package/lib/vfs_fonts-Df1kkZ4Y.js.map +0 -1
- package/lib/xlsx-Bb5TWyXQ.js.map +0 -1
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest"
|
|
2
|
+
import {
|
|
3
|
+
Document,
|
|
4
|
+
Heading,
|
|
5
|
+
List,
|
|
6
|
+
ListItem,
|
|
7
|
+
Page,
|
|
8
|
+
PageBreak,
|
|
9
|
+
Quote,
|
|
10
|
+
render,
|
|
11
|
+
Section,
|
|
12
|
+
Spacer,
|
|
13
|
+
Table,
|
|
14
|
+
Text,
|
|
15
|
+
} from "../index"
|
|
16
|
+
import type { DocNode } from "../types"
|
|
17
|
+
|
|
18
|
+
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
function generateRows(count: number, cols: number): string[][] {
|
|
21
|
+
return Array.from({ length: count }, (_row, i) =>
|
|
22
|
+
Array.from({ length: cols }, (_col, j) => `Row ${i + 1} Col ${j + 1}`),
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function generateLargeDocument(pages: number, rowsPerTable: number): DocNode {
|
|
27
|
+
const pageNodes: DocNode[] = []
|
|
28
|
+
for (let p = 0; p < pages; p++) {
|
|
29
|
+
pageNodes.push(
|
|
30
|
+
Page({
|
|
31
|
+
children: [
|
|
32
|
+
Heading({ level: 1, children: `Page ${p + 1}` }),
|
|
33
|
+
Text({
|
|
34
|
+
children: `This is page ${p + 1} of the stress test document.`,
|
|
35
|
+
}),
|
|
36
|
+
Table({
|
|
37
|
+
columns: ["ID", "Name", "Value", "Status", "Notes"],
|
|
38
|
+
rows: generateRows(rowsPerTable, 5),
|
|
39
|
+
striped: true,
|
|
40
|
+
headerStyle: { background: "#1a1a2e", color: "#fff" },
|
|
41
|
+
}),
|
|
42
|
+
Spacer({ height: 20 }),
|
|
43
|
+
List({
|
|
44
|
+
ordered: true,
|
|
45
|
+
children: Array.from({ length: 10 }, (_, i) =>
|
|
46
|
+
ListItem({ children: `Item ${i + 1} on page ${p + 1}` }),
|
|
47
|
+
),
|
|
48
|
+
}),
|
|
49
|
+
Quote({ children: `Summary for page ${p + 1}` }),
|
|
50
|
+
...(p < pages - 1 ? [PageBreak()] : []),
|
|
51
|
+
],
|
|
52
|
+
}),
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return Document({
|
|
57
|
+
title: "Stress Test Document",
|
|
58
|
+
author: "Pyreon Test Suite",
|
|
59
|
+
children: pageNodes,
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ─── HTML Stress Tests ──────────────────────────────────────────────────────
|
|
64
|
+
|
|
65
|
+
describe("HTML stress tests", () => {
|
|
66
|
+
it("renders 50-page document", async () => {
|
|
67
|
+
const doc = generateLargeDocument(50, 20)
|
|
68
|
+
const html = (await render(doc, "html")) as string
|
|
69
|
+
expect(html.length).toBeGreaterThan(100000)
|
|
70
|
+
expect(html).toContain("Page 1")
|
|
71
|
+
expect(html).toContain("Page 50")
|
|
72
|
+
expect(html).toContain("Row 20 Col 5")
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it("renders 1000-row table", async () => {
|
|
76
|
+
const doc = Document({
|
|
77
|
+
children: Table({
|
|
78
|
+
columns: ["A", "B", "C", "D", "E", "F", "G", "H"],
|
|
79
|
+
rows: generateRows(1000, 8),
|
|
80
|
+
striped: true,
|
|
81
|
+
bordered: true,
|
|
82
|
+
}),
|
|
83
|
+
})
|
|
84
|
+
const html = (await render(doc, "html")) as string
|
|
85
|
+
expect(html).toContain("Row 1 Col 1")
|
|
86
|
+
expect(html).toContain("Row 1000 Col 8")
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it("renders deeply nested sections", async () => {
|
|
90
|
+
let node: DocNode = Text({ children: "Deep content" })
|
|
91
|
+
for (let i = 0; i < 20; i++) {
|
|
92
|
+
node = Section({ children: node })
|
|
93
|
+
}
|
|
94
|
+
const doc = Document({ children: node })
|
|
95
|
+
const html = (await render(doc, "html")) as string
|
|
96
|
+
expect(html).toContain("Deep content")
|
|
97
|
+
})
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
// ─── Email Stress Tests ─────────────────────────────────────────────────────
|
|
101
|
+
|
|
102
|
+
describe("email stress tests", () => {
|
|
103
|
+
it("renders large email with multiple sections", async () => {
|
|
104
|
+
const doc = generateLargeDocument(5, 50)
|
|
105
|
+
const html = (await render(doc, "email")) as string
|
|
106
|
+
expect(html).toContain("max-width:600px")
|
|
107
|
+
expect(html).toContain("Page 5")
|
|
108
|
+
expect(html).toContain("Row 50 Col 5")
|
|
109
|
+
})
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
// ─── Markdown Stress Tests ──────────────────────────────────────────────────
|
|
113
|
+
|
|
114
|
+
describe("Markdown stress tests", () => {
|
|
115
|
+
it("renders 1000-row table as pipe table", async () => {
|
|
116
|
+
const doc = Document({
|
|
117
|
+
children: Table({
|
|
118
|
+
columns: ["Name", "Value"],
|
|
119
|
+
rows: generateRows(1000, 2),
|
|
120
|
+
}),
|
|
121
|
+
})
|
|
122
|
+
const md = (await render(doc, "md")) as string
|
|
123
|
+
expect(md).toContain("Row 1 Col 1")
|
|
124
|
+
expect(md).toContain("Row 1000 Col 2")
|
|
125
|
+
// Count pipe rows
|
|
126
|
+
const pipeLines = md.split("\n").filter((l) => l.startsWith("|"))
|
|
127
|
+
expect(pipeLines.length).toBeGreaterThanOrEqual(1002) // header + separator + 1000 rows
|
|
128
|
+
})
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
// ─── CSV Stress Tests ───────────────────────────────────────────────────────
|
|
132
|
+
|
|
133
|
+
describe("CSV stress tests", () => {
|
|
134
|
+
it("renders multiple large tables", async () => {
|
|
135
|
+
const doc = Document({
|
|
136
|
+
children: [
|
|
137
|
+
Table({
|
|
138
|
+
columns: ["A", "B", "C"],
|
|
139
|
+
rows: generateRows(500, 3),
|
|
140
|
+
caption: "Table 1",
|
|
141
|
+
}),
|
|
142
|
+
Table({
|
|
143
|
+
columns: ["X", "Y", "Z"],
|
|
144
|
+
rows: generateRows(500, 3),
|
|
145
|
+
caption: "Table 2",
|
|
146
|
+
}),
|
|
147
|
+
],
|
|
148
|
+
})
|
|
149
|
+
const csv = (await render(doc, "csv")) as string
|
|
150
|
+
expect(csv).toContain("# Table 1")
|
|
151
|
+
expect(csv).toContain("# Table 2")
|
|
152
|
+
const lines = csv.split("\n").filter((l) => l.trim().length > 0)
|
|
153
|
+
expect(lines.length).toBeGreaterThanOrEqual(1004) // 2 tables × (header + 500 rows) + 2 captions
|
|
154
|
+
})
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
// ─── Text Stress Tests ──────────────────────────────────────────────────────
|
|
158
|
+
|
|
159
|
+
describe("text stress tests", () => {
|
|
160
|
+
it("renders large aligned table", async () => {
|
|
161
|
+
const doc = Document({
|
|
162
|
+
children: Table({
|
|
163
|
+
columns: [
|
|
164
|
+
{ header: "ID", align: "right" as const },
|
|
165
|
+
{ header: "Name", align: "left" as const },
|
|
166
|
+
{ header: "Amount", align: "right" as const },
|
|
167
|
+
],
|
|
168
|
+
rows: generateRows(200, 3),
|
|
169
|
+
}),
|
|
170
|
+
})
|
|
171
|
+
const text = (await render(doc, "text")) as string
|
|
172
|
+
expect(text).toContain("Row 200 Col 3")
|
|
173
|
+
})
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
// ─── SVG Stress Tests ───────────────────────────────────────────────────────
|
|
177
|
+
|
|
178
|
+
describe("SVG stress tests", () => {
|
|
179
|
+
it("renders document with many elements", async () => {
|
|
180
|
+
const children: DocNode[] = []
|
|
181
|
+
for (let i = 0; i < 100; i++) {
|
|
182
|
+
children.push(Heading({ level: 2, children: `Section ${i + 1}` }))
|
|
183
|
+
children.push(Text({ children: `Content for section ${i + 1}` }))
|
|
184
|
+
}
|
|
185
|
+
const doc = Document({ children })
|
|
186
|
+
const svg = (await render(doc, "svg")) as string
|
|
187
|
+
expect(svg).toContain("<svg")
|
|
188
|
+
expect(svg).toContain("Section 100")
|
|
189
|
+
// Height should be large
|
|
190
|
+
const match = svg.match(/height="(\d+)"/)
|
|
191
|
+
expect(Number(match?.[1])).toBeGreaterThan(3000)
|
|
192
|
+
})
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
// ─── Slack Stress Tests ─────────────────────────────────────────────────────
|
|
196
|
+
|
|
197
|
+
describe("Slack stress tests", () => {
|
|
198
|
+
it("renders large document to blocks", async () => {
|
|
199
|
+
const doc = generateLargeDocument(10, 10)
|
|
200
|
+
const json = (await render(doc, "slack")) as string
|
|
201
|
+
const parsed = JSON.parse(json)
|
|
202
|
+
expect(parsed.blocks.length).toBeGreaterThan(50)
|
|
203
|
+
})
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
// ─── PDF Stress Tests ───────────────────────────────────────────────────────
|
|
207
|
+
|
|
208
|
+
describe("PDF stress tests", () => {
|
|
209
|
+
it("renders 10-page document with large tables", async () => {
|
|
210
|
+
const doc = generateLargeDocument(10, 50)
|
|
211
|
+
const pdf = await render(doc, "pdf")
|
|
212
|
+
expect(pdf).toBeInstanceOf(Uint8Array)
|
|
213
|
+
expect((pdf as Uint8Array).length).toBeGreaterThan(10000)
|
|
214
|
+
// PDF header
|
|
215
|
+
const header = String.fromCharCode(...(pdf as Uint8Array).slice(0, 5))
|
|
216
|
+
expect(header).toBe("%PDF-")
|
|
217
|
+
}, 30000)
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
// ─── DOCX Stress Tests ──────────────────────────────────────────────────────
|
|
221
|
+
|
|
222
|
+
describe("DOCX stress tests", () => {
|
|
223
|
+
it("renders document with 500-row table", async () => {
|
|
224
|
+
const doc = Document({
|
|
225
|
+
title: "Large DOCX",
|
|
226
|
+
children: Page({
|
|
227
|
+
children: [
|
|
228
|
+
Heading({ children: "Large Table" }),
|
|
229
|
+
Table({
|
|
230
|
+
columns: ["A", "B", "C", "D"],
|
|
231
|
+
rows: generateRows(500, 4),
|
|
232
|
+
striped: true,
|
|
233
|
+
bordered: true,
|
|
234
|
+
}),
|
|
235
|
+
],
|
|
236
|
+
}),
|
|
237
|
+
})
|
|
238
|
+
const docx = await render(doc, "docx")
|
|
239
|
+
expect(docx).toBeInstanceOf(Uint8Array)
|
|
240
|
+
expect((docx as Uint8Array).length).toBeGreaterThan(5000)
|
|
241
|
+
// DOCX is a ZIP — starts with PK
|
|
242
|
+
const header = String.fromCharCode(...(docx as Uint8Array).slice(0, 2))
|
|
243
|
+
expect(header).toBe("PK")
|
|
244
|
+
}, 30000)
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
// ─── XLSX Stress Tests ──────────────────────────────────────────────────────
|
|
248
|
+
|
|
249
|
+
describe("XLSX stress tests", () => {
|
|
250
|
+
it("renders 1000-row spreadsheet", async () => {
|
|
251
|
+
const doc = Document({
|
|
252
|
+
title: "Large XLSX",
|
|
253
|
+
children: Table({
|
|
254
|
+
columns: ["ID", "Name", "Revenue", "Growth", "Region"],
|
|
255
|
+
rows: Array.from({ length: 1000 }, (_, i) => [
|
|
256
|
+
String(i + 1),
|
|
257
|
+
`Company ${i + 1}`,
|
|
258
|
+
`$${(Math.random() * 1000000).toFixed(0)}`,
|
|
259
|
+
`${(Math.random() * 100).toFixed(1)}%`,
|
|
260
|
+
["US", "EU", "APAC", "LATAM"][i % 4]!,
|
|
261
|
+
]),
|
|
262
|
+
striped: true,
|
|
263
|
+
}),
|
|
264
|
+
})
|
|
265
|
+
const xlsx = await render(doc, "xlsx")
|
|
266
|
+
expect(xlsx).toBeInstanceOf(Uint8Array)
|
|
267
|
+
expect((xlsx as Uint8Array).length).toBeGreaterThan(10000)
|
|
268
|
+
}, 30000)
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
// ─── PPTX Stress Tests ──────────────────────────────────────────────────────
|
|
272
|
+
|
|
273
|
+
describe("PPTX stress tests", () => {
|
|
274
|
+
it("renders 20-slide presentation", async () => {
|
|
275
|
+
const pages: DocNode[] = []
|
|
276
|
+
for (let i = 0; i < 20; i++) {
|
|
277
|
+
pages.push(
|
|
278
|
+
Page({
|
|
279
|
+
children: [
|
|
280
|
+
Heading({ children: `Slide ${i + 1}` }),
|
|
281
|
+
Text({ children: `Content for slide ${i + 1}` }),
|
|
282
|
+
Table({
|
|
283
|
+
columns: ["Metric", "Value"],
|
|
284
|
+
rows: [
|
|
285
|
+
["Revenue", `$${(i + 1) * 100}K`],
|
|
286
|
+
["Growth", `${(i + 1) * 5}%`],
|
|
287
|
+
],
|
|
288
|
+
}),
|
|
289
|
+
],
|
|
290
|
+
}),
|
|
291
|
+
)
|
|
292
|
+
}
|
|
293
|
+
const doc = Document({ title: "Large Presentation", children: pages })
|
|
294
|
+
const pptx = await render(doc, "pptx")
|
|
295
|
+
expect(pptx).toBeInstanceOf(Uint8Array)
|
|
296
|
+
expect((pptx as Uint8Array).length).toBeGreaterThan(5000)
|
|
297
|
+
}, 30000)
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
// ─── Edge Cases ─────────────────────────────────────────────────────────────
|
|
301
|
+
|
|
302
|
+
describe("edge cases", () => {
|
|
303
|
+
it("handles empty document", async () => {
|
|
304
|
+
const doc = Document({ children: [] as unknown as undefined })
|
|
305
|
+
const html = (await render(doc, "html")) as string
|
|
306
|
+
expect(html).toContain("<!DOCTYPE html>")
|
|
307
|
+
})
|
|
308
|
+
|
|
309
|
+
it("handles empty table", async () => {
|
|
310
|
+
const doc = Document({
|
|
311
|
+
children: Table({ columns: ["A", "B"], rows: [] }),
|
|
312
|
+
})
|
|
313
|
+
const html = (await render(doc, "html")) as string
|
|
314
|
+
expect(html).toContain("<table")
|
|
315
|
+
})
|
|
316
|
+
|
|
317
|
+
it("handles special characters in text", async () => {
|
|
318
|
+
const doc = Document({
|
|
319
|
+
children: [
|
|
320
|
+
Text({ children: "Hello <world> & \"quotes\" 'apostrophe'" }),
|
|
321
|
+
Heading({ children: "Heading with <html>" }),
|
|
322
|
+
],
|
|
323
|
+
})
|
|
324
|
+
const html = (await render(doc, "html")) as string
|
|
325
|
+
expect(html).toContain("<world>")
|
|
326
|
+
expect(html).toContain("&")
|
|
327
|
+
expect(html).toContain(""")
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
it("handles unicode text", async () => {
|
|
331
|
+
const doc = Document({
|
|
332
|
+
children: [
|
|
333
|
+
Text({ children: "日本語テスト" }),
|
|
334
|
+
Text({ children: "العربية" }),
|
|
335
|
+
Text({ children: "🎉🚀✨" }),
|
|
336
|
+
],
|
|
337
|
+
})
|
|
338
|
+
const html = (await render(doc, "html")) as string
|
|
339
|
+
expect(html).toContain("日本語テスト")
|
|
340
|
+
expect(html).toContain("العربية")
|
|
341
|
+
expect(html).toContain("🎉🚀✨")
|
|
342
|
+
})
|
|
343
|
+
|
|
344
|
+
it("handles very long text", async () => {
|
|
345
|
+
const longText = "A".repeat(100000)
|
|
346
|
+
const doc = Document({ children: Text({ children: longText }) })
|
|
347
|
+
const html = (await render(doc, "html")) as string
|
|
348
|
+
expect(html.length).toBeGreaterThan(100000)
|
|
349
|
+
})
|
|
350
|
+
})
|
package/src/types.ts
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
// ─── Node Types ─────────────────────────────────────────────────────────────
|
|
2
2
|
|
|
3
3
|
export type NodeType =
|
|
4
|
-
|
|
|
5
|
-
|
|
|
6
|
-
|
|
|
7
|
-
|
|
|
8
|
-
|
|
|
9
|
-
|
|
|
10
|
-
|
|
|
11
|
-
|
|
|
12
|
-
|
|
|
13
|
-
|
|
|
14
|
-
|
|
|
15
|
-
|
|
|
16
|
-
|
|
|
17
|
-
|
|
|
18
|
-
|
|
|
19
|
-
|
|
|
20
|
-
|
|
|
21
|
-
|
|
|
4
|
+
| "document"
|
|
5
|
+
| "page"
|
|
6
|
+
| "section"
|
|
7
|
+
| "row"
|
|
8
|
+
| "column"
|
|
9
|
+
| "heading"
|
|
10
|
+
| "text"
|
|
11
|
+
| "link"
|
|
12
|
+
| "image"
|
|
13
|
+
| "table"
|
|
14
|
+
| "list"
|
|
15
|
+
| "list-item"
|
|
16
|
+
| "page-break"
|
|
17
|
+
| "code"
|
|
18
|
+
| "divider"
|
|
19
|
+
| "spacer"
|
|
20
|
+
| "button"
|
|
21
|
+
| "quote"
|
|
22
22
|
|
|
23
23
|
/** A format-agnostic document node. */
|
|
24
24
|
export interface DocNode {
|
|
@@ -36,12 +36,12 @@ export type DocChild = DocNode | string
|
|
|
36
36
|
export interface ResolvedStyles {
|
|
37
37
|
fontSize?: number
|
|
38
38
|
fontFamily?: string
|
|
39
|
-
fontWeight?:
|
|
40
|
-
fontStyle?:
|
|
41
|
-
textDecoration?:
|
|
39
|
+
fontWeight?: "normal" | "bold" | number
|
|
40
|
+
fontStyle?: "normal" | "italic"
|
|
41
|
+
textDecoration?: "none" | "underline" | "line-through"
|
|
42
42
|
color?: string
|
|
43
43
|
backgroundColor?: string
|
|
44
|
-
textAlign?:
|
|
44
|
+
textAlign?: "left" | "center" | "right" | "justify"
|
|
45
45
|
lineHeight?: number
|
|
46
46
|
letterSpacing?: number
|
|
47
47
|
padding?: number | [number, number] | [number, number, number, number]
|
|
@@ -49,7 +49,7 @@ export interface ResolvedStyles {
|
|
|
49
49
|
borderRadius?: number
|
|
50
50
|
borderWidth?: number
|
|
51
51
|
borderColor?: string
|
|
52
|
-
borderStyle?:
|
|
52
|
+
borderStyle?: "solid" | "dashed" | "dotted"
|
|
53
53
|
width?: number | string
|
|
54
54
|
height?: number | string
|
|
55
55
|
maxWidth?: number | string
|
|
@@ -67,8 +67,8 @@ export interface DocumentProps {
|
|
|
67
67
|
children?: unknown
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
export type PageSize =
|
|
71
|
-
export type PageOrientation =
|
|
70
|
+
export type PageSize = "A4" | "A3" | "A5" | "letter" | "legal" | "tabloid"
|
|
71
|
+
export type PageOrientation = "portrait" | "landscape"
|
|
72
72
|
|
|
73
73
|
export interface PageProps {
|
|
74
74
|
size?: PageSize
|
|
@@ -82,7 +82,7 @@ export interface PageProps {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
export interface SectionProps {
|
|
85
|
-
direction?:
|
|
85
|
+
direction?: "column" | "row"
|
|
86
86
|
gap?: number
|
|
87
87
|
padding?: number | [number, number] | [number, number, number, number]
|
|
88
88
|
background?: string
|
|
@@ -93,20 +93,20 @@ export interface SectionProps {
|
|
|
93
93
|
|
|
94
94
|
export interface RowProps {
|
|
95
95
|
gap?: number
|
|
96
|
-
align?:
|
|
96
|
+
align?: "start" | "center" | "end" | "stretch"
|
|
97
97
|
children?: unknown
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
export interface ColumnProps {
|
|
101
101
|
width?: number | string
|
|
102
|
-
align?:
|
|
102
|
+
align?: "start" | "center" | "end"
|
|
103
103
|
children?: unknown
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
export interface HeadingProps {
|
|
107
107
|
level?: 1 | 2 | 3 | 4 | 5 | 6
|
|
108
108
|
color?: string
|
|
109
|
-
align?:
|
|
109
|
+
align?: "left" | "center" | "right"
|
|
110
110
|
children?: unknown
|
|
111
111
|
}
|
|
112
112
|
|
|
@@ -117,7 +117,7 @@ export interface TextProps {
|
|
|
117
117
|
italic?: boolean
|
|
118
118
|
underline?: boolean
|
|
119
119
|
strikethrough?: boolean
|
|
120
|
-
align?:
|
|
120
|
+
align?: "left" | "center" | "right" | "justify"
|
|
121
121
|
lineHeight?: number
|
|
122
122
|
children?: unknown
|
|
123
123
|
}
|
|
@@ -133,14 +133,14 @@ export interface ImageProps {
|
|
|
133
133
|
width?: number
|
|
134
134
|
height?: number
|
|
135
135
|
alt?: string
|
|
136
|
-
align?:
|
|
136
|
+
align?: "left" | "center" | "right"
|
|
137
137
|
caption?: string
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
export interface TableColumn {
|
|
141
141
|
header: string
|
|
142
142
|
width?: number | string
|
|
143
|
-
align?:
|
|
143
|
+
align?: "left" | "center" | "right"
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
export interface TableProps {
|
|
@@ -187,7 +187,7 @@ export interface ButtonProps {
|
|
|
187
187
|
color?: string
|
|
188
188
|
borderRadius?: number
|
|
189
189
|
padding?: number | [number, number]
|
|
190
|
-
align?:
|
|
190
|
+
align?: "left" | "center" | "right"
|
|
191
191
|
children?: unknown
|
|
192
192
|
}
|
|
193
193
|
|
|
@@ -199,24 +199,24 @@ export interface QuoteProps {
|
|
|
199
199
|
// ─── Render Types ───────────────────────────────────────────────────────────
|
|
200
200
|
|
|
201
201
|
export type OutputFormat =
|
|
202
|
-
|
|
|
203
|
-
|
|
|
204
|
-
|
|
|
205
|
-
|
|
|
206
|
-
|
|
|
207
|
-
|
|
|
208
|
-
|
|
|
209
|
-
|
|
|
210
|
-
|
|
|
211
|
-
|
|
|
212
|
-
|
|
|
213
|
-
|
|
|
214
|
-
|
|
|
215
|
-
|
|
|
216
|
-
|
|
|
217
|
-
|
|
|
218
|
-
|
|
|
219
|
-
|
|
|
202
|
+
| "html"
|
|
203
|
+
| "pdf"
|
|
204
|
+
| "docx"
|
|
205
|
+
| "pptx"
|
|
206
|
+
| "email"
|
|
207
|
+
| "xlsx"
|
|
208
|
+
| "md"
|
|
209
|
+
| "text"
|
|
210
|
+
| "csv"
|
|
211
|
+
| "svg"
|
|
212
|
+
| "slack"
|
|
213
|
+
| "teams"
|
|
214
|
+
| "discord"
|
|
215
|
+
| "telegram"
|
|
216
|
+
| "notion"
|
|
217
|
+
| "confluence"
|
|
218
|
+
| "whatsapp"
|
|
219
|
+
| "google-chat"
|
|
220
220
|
|
|
221
221
|
export interface RenderOptions {
|
|
222
222
|
/** Custom styles to apply (overrides component styles). */
|
|
@@ -224,12 +224,9 @@ export interface RenderOptions {
|
|
|
224
224
|
/** Base URL for relative image sources. */
|
|
225
225
|
baseUrl?: string
|
|
226
226
|
/** Text direction — 'ltr' (default) or 'rtl'. */
|
|
227
|
-
direction?:
|
|
227
|
+
direction?: "ltr" | "rtl"
|
|
228
228
|
/** Custom font configuration for PDF. */
|
|
229
|
-
fonts?: Record<
|
|
230
|
-
string,
|
|
231
|
-
{ normal?: string; bold?: string; italics?: string; bolditalics?: string }
|
|
232
|
-
>
|
|
229
|
+
fonts?: Record<string, { normal?: string; bold?: string; italics?: string; bolditalics?: string }>
|
|
233
230
|
}
|
|
234
231
|
|
|
235
232
|
export type RenderResult = string | Uint8Array
|
|
@@ -242,19 +239,23 @@ export interface DocumentRenderer {
|
|
|
242
239
|
// ─── Builder Types ──────────────────────────────────────────────────────────
|
|
243
240
|
|
|
244
241
|
export interface DocumentBuilder {
|
|
245
|
-
heading(text: string, props?: Omit<HeadingProps,
|
|
246
|
-
text(text: string, props?: Omit<TextProps,
|
|
247
|
-
paragraph(text: string, props?: Omit<TextProps,
|
|
248
|
-
image(src: string, props?: Omit<ImageProps,
|
|
242
|
+
heading(text: string, props?: Omit<HeadingProps, "children">): DocumentBuilder
|
|
243
|
+
text(text: string, props?: Omit<TextProps, "children">): DocumentBuilder
|
|
244
|
+
paragraph(text: string, props?: Omit<TextProps, "children">): DocumentBuilder
|
|
245
|
+
image(src: string, props?: Omit<ImageProps, "src">): DocumentBuilder
|
|
249
246
|
table(props: TableProps): DocumentBuilder
|
|
250
|
-
list(items: string[], props?: Omit<ListProps,
|
|
251
|
-
code(text: string, props?: Omit<CodeProps,
|
|
247
|
+
list(items: string[], props?: Omit<ListProps, "children">): DocumentBuilder
|
|
248
|
+
code(text: string, props?: Omit<CodeProps, "children">): DocumentBuilder
|
|
252
249
|
divider(props?: DividerProps): DocumentBuilder
|
|
253
250
|
spacer(height: number): DocumentBuilder
|
|
254
|
-
quote(text: string, props?: Omit<QuoteProps,
|
|
255
|
-
button(text: string, props: Omit<ButtonProps,
|
|
256
|
-
link(text: string, props: Omit<LinkProps,
|
|
251
|
+
quote(text: string, props?: Omit<QuoteProps, "children">): DocumentBuilder
|
|
252
|
+
button(text: string, props: Omit<ButtonProps, "children">): DocumentBuilder
|
|
253
|
+
link(text: string, props: Omit<LinkProps, "children">): DocumentBuilder
|
|
257
254
|
pageBreak(): DocumentBuilder
|
|
255
|
+
/** Add an arbitrary DocNode (or fragment returned by a helper function). */
|
|
256
|
+
add(node: DocNode | DocNode[]): DocumentBuilder
|
|
257
|
+
/** Add a group of nodes as a logical section. */
|
|
258
|
+
section(children: DocNode[]): DocumentBuilder
|
|
258
259
|
/** Add a chart snapshot from a @pyreon/charts instance. */
|
|
259
260
|
chart(
|
|
260
261
|
instance: unknown,
|