@pyreon/document 0.10.0 → 0.11.1

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 (76) hide show
  1. package/lib/analysis/index.js.html +1 -1
  2. package/lib/confluence-Bd3ua1Ut.js.map +1 -1
  3. package/lib/csv-COrS4qdy.js.map +1 -1
  4. package/lib/discord-BLUnkEh9.js.map +1 -1
  5. package/lib/{dist-BsqdI2nY.js → dist-CYL41kqQ.js} +2 -2
  6. package/lib/dist-CYL41kqQ.js.map +1 -0
  7. package/lib/{docx-BEBOihjl.js → docx-uNAel545.js} +7 -2
  8. package/lib/docx-uNAel545.js.map +1 -0
  9. package/lib/email-D0bbfWq4.js.map +1 -1
  10. package/lib/{exceljs-BoIDUUaw.js → exceljs-BYETsesT.js} +314 -314
  11. package/lib/exceljs-BYETsesT.js.map +1 -0
  12. package/lib/google-chat-CkKCBUWC.js.map +1 -1
  13. package/lib/html-B5biprN2.js.map +1 -1
  14. package/lib/index.js +17 -8
  15. package/lib/index.js.map +1 -1
  16. package/lib/markdown-CdtlFGC0.js.map +1 -1
  17. package/lib/notion-iG2C5bEY.js.map +1 -1
  18. package/lib/{pdf-DIUQUEdj.js → pdf-IuBgTb3T.js} +9 -3
  19. package/lib/pdf-IuBgTb3T.js.map +1 -0
  20. package/lib/{pdfmake-DnmLxK4Q.js → pdfmake-CKMX5URW.js} +2 -4
  21. package/lib/pdfmake-CKMX5URW.js.map +1 -0
  22. package/lib/{pptx-Dd33oL3_.js → pptx-DXiMiYFM.js} +7 -2
  23. package/lib/pptx-DXiMiYFM.js.map +1 -0
  24. package/lib/{pptxgen.es-COcgXsyx.js → pptxgen.es-FsqHs8mD.js} +3 -6
  25. package/lib/pptxgen.es-FsqHs8mD.js.map +1 -0
  26. package/lib/sanitize-O_3j1mNJ.js.map +1 -1
  27. package/lib/slack-BI3EQwYm.js.map +1 -1
  28. package/lib/svg-BKxumy-p.js.map +1 -1
  29. package/lib/teams-Cwz9lce0.js.map +1 -1
  30. package/lib/telegram-gYFqyMXb.js.map +1 -1
  31. package/lib/text-l1XNXBOC.js.map +1 -1
  32. package/lib/types/index.d.ts +43 -39
  33. package/lib/types/index.d.ts.map +1 -1
  34. package/lib/{vfs_fonts-Df1kkZ4Y.js → vfs_fonts-Cap07Jg3.js} +2 -2
  35. package/lib/vfs_fonts-Cap07Jg3.js.map +1 -0
  36. package/lib/whatsapp-CjSGoOKx.js.map +1 -1
  37. package/lib/{xlsx-Bb5TWyXQ.js → xlsx-Cvu4LBNy.js} +8 -2
  38. package/lib/xlsx-Cvu4LBNy.js.map +1 -0
  39. package/package.json +19 -7
  40. package/src/builder.ts +53 -44
  41. package/src/download.ts +32 -36
  42. package/src/env.d.ts +3 -17
  43. package/src/index.ts +6 -8
  44. package/src/nodes.ts +45 -45
  45. package/src/render.ts +45 -118
  46. package/src/renderers/confluence.ts +64 -80
  47. package/src/renderers/csv.ts +11 -18
  48. package/src/renderers/discord.ts +38 -50
  49. package/src/renderers/docx.ts +78 -120
  50. package/src/renderers/email.ts +73 -92
  51. package/src/renderers/google-chat.ts +35 -47
  52. package/src/renderers/html.ts +78 -101
  53. package/src/renderers/markdown.ts +43 -53
  54. package/src/renderers/notion.ts +63 -85
  55. package/src/renderers/pdf.ts +92 -115
  56. package/src/renderers/pptx.ts +60 -66
  57. package/src/renderers/slack.ts +49 -61
  58. package/src/renderers/svg.ts +49 -63
  59. package/src/renderers/teams.ts +68 -80
  60. package/src/renderers/telegram.ts +40 -54
  61. package/src/renderers/text.ts +44 -66
  62. package/src/renderers/whatsapp.ts +34 -48
  63. package/src/renderers/xlsx.ts +47 -61
  64. package/src/sanitize.ts +21 -25
  65. package/src/tests/document.test.ts +1337 -1385
  66. package/src/tests/stress.test.ts +111 -111
  67. package/src/types.ts +66 -65
  68. package/lib/dist-BsqdI2nY.js.map +0 -1
  69. package/lib/docx-BEBOihjl.js.map +0 -1
  70. package/lib/exceljs-BoIDUUaw.js.map +0 -1
  71. package/lib/pdf-DIUQUEdj.js.map +0 -1
  72. package/lib/pdfmake-DnmLxK4Q.js.map +0 -1
  73. package/lib/pptx-Dd33oL3_.js.map +0 -1
  74. package/lib/pptxgen.es-COcgXsyx.js.map +0 -1
  75. package/lib/vfs_fonts-Df1kkZ4Y.js.map +0 -1
  76. package/lib/xlsx-Bb5TWyXQ.js.map +0 -1
@@ -1,11 +1,5 @@
1
- import { sanitizeHref } from '../sanitize'
2
- import type {
3
- DocChild,
4
- DocNode,
5
- DocumentRenderer,
6
- RenderOptions,
7
- TableColumn,
8
- } from '../types'
1
+ import { sanitizeHref } from "../sanitize"
2
+ import type { DocChild, DocNode, DocumentRenderer, RenderOptions, TableColumn } from "../types"
9
3
 
10
4
  /**
11
5
  * Telegram renderer — outputs HTML using Telegram's supported subset.
@@ -14,44 +8,40 @@ import type {
14
8
  */
15
9
 
16
10
  function resolveColumn(col: string | TableColumn): TableColumn {
17
- return typeof col === 'string' ? { header: col } : col
11
+ return typeof col === "string" ? { header: col } : col
18
12
  }
19
13
 
20
14
  function esc(str: string): string {
21
15
  return str
22
- .replace(/&/g, '&')
23
- .replace(/</g, '&lt;')
24
- .replace(/>/g, '&gt;')
25
- .replace(/"/g, '&quot;')
16
+ .replace(/&/g, "&amp;")
17
+ .replace(/</g, "&lt;")
18
+ .replace(/>/g, "&gt;")
19
+ .replace(/"/g, "&quot;")
26
20
  }
27
21
 
28
22
  function getTextContent(children: DocChild[]): string {
29
23
  return children
30
- .map((c) =>
31
- typeof c === 'string' ? c : getTextContent((c as DocNode).children),
32
- )
33
- .join('')
24
+ .map((c) => (typeof c === "string" ? c : getTextContent((c as DocNode).children)))
25
+ .join("")
34
26
  }
35
27
 
36
28
  function renderNode(node: DocNode): string {
37
29
  const p = node.props
38
30
 
39
31
  switch (node.type) {
40
- case 'document':
41
- case 'page':
42
- case 'section':
43
- case 'row':
44
- case 'column':
45
- return node.children
46
- .map((c) => (typeof c === 'string' ? esc(c) : renderNode(c)))
47
- .join('')
48
-
49
- case 'heading': {
32
+ case "document":
33
+ case "page":
34
+ case "section":
35
+ case "row":
36
+ case "column":
37
+ return node.children.map((c) => (typeof c === "string" ? esc(c) : renderNode(c))).join("")
38
+
39
+ case "heading": {
50
40
  const text = esc(getTextContent(node.children))
51
41
  return `<b>${text}</b>\n\n`
52
42
  }
53
43
 
54
- case 'text': {
44
+ case "text": {
55
45
  let text = esc(getTextContent(node.children))
56
46
  if (p.bold) text = `<b>${text}</b>`
57
47
  if (p.italic) text = `<i>${text}</i>`
@@ -60,47 +50,43 @@ function renderNode(node: DocNode): string {
60
50
  return `${text}\n\n`
61
51
  }
62
52
 
63
- case 'link': {
53
+ case "link": {
64
54
  const href = sanitizeHref(p.href as string)
65
55
  const text = esc(getTextContent(node.children))
66
56
  return `<a href="${esc(href)}">${text}</a>\n\n`
67
57
  }
68
58
 
69
- case 'image':
59
+ case "image":
70
60
  // Telegram doesn't support inline images in HTML
71
61
  // Images need to be sent separately via sendPhoto
72
- return ''
62
+ return ""
73
63
 
74
- case 'table': {
75
- const columns = ((p.columns ?? []) as (string | TableColumn)[]).map(
76
- resolveColumn,
77
- )
64
+ case "table": {
65
+ const columns = ((p.columns ?? []) as (string | TableColumn)[]).map(resolveColumn)
78
66
  const rows = (p.rows ?? []) as (string | number)[][]
79
67
 
80
68
  // Render as preformatted text since Telegram has no table support
81
- const header = columns.map((c) => c.header).join(' | ')
82
- const separator = columns.map(() => '---').join('-+-')
83
- const body = rows
84
- .map((row) => row.map((c) => String(c ?? '')).join(' | '))
85
- .join('\n')
69
+ const header = columns.map((c) => c.header).join(" | ")
70
+ const separator = columns.map(() => "---").join("-+-")
71
+ const body = rows.map((row) => row.map((c) => String(c ?? "")).join(" | ")).join("\n")
86
72
 
87
73
  return `<pre>${esc(header)}\n${esc(separator)}\n${esc(body)}</pre>\n\n`
88
74
  }
89
75
 
90
- case 'list': {
76
+ case "list": {
91
77
  const ordered = p.ordered as boolean | undefined
92
78
  const items = node.children
93
- .filter((c): c is DocNode => typeof c !== 'string')
79
+ .filter((c): c is DocNode => typeof c !== "string")
94
80
  .map((item, i) => {
95
- const prefix = ordered ? `${i + 1}.` : ''
81
+ const prefix = ordered ? `${i + 1}.` : ""
96
82
  return `${prefix} ${esc(getTextContent(item.children))}`
97
83
  })
98
- .join('\n')
84
+ .join("\n")
99
85
  return `${items}\n\n`
100
86
  }
101
87
 
102
- case 'code': {
103
- const lang = (p.language as string) ?? ''
88
+ case "code": {
89
+ const lang = (p.language as string) ?? ""
104
90
  const text = esc(getTextContent(node.children))
105
91
  if (lang) {
106
92
  return `<pre><code class="language-${esc(lang)}">${text}</code></pre>\n\n`
@@ -108,26 +94,26 @@ function renderNode(node: DocNode): string {
108
94
  return `<pre>${text}</pre>\n\n`
109
95
  }
110
96
 
111
- case 'divider':
112
- case 'page-break':
113
- return '───────────\n\n'
97
+ case "divider":
98
+ case "page-break":
99
+ return "───────────\n\n"
114
100
 
115
- case 'spacer':
116
- return '\n'
101
+ case "spacer":
102
+ return "\n"
117
103
 
118
- case 'button': {
104
+ case "button": {
119
105
  const href = sanitizeHref(p.href as string)
120
106
  const text = esc(getTextContent(node.children))
121
107
  return `<a href="${esc(href)}">${text}</a>\n\n`
122
108
  }
123
109
 
124
- case 'quote': {
110
+ case "quote": {
125
111
  const text = esc(getTextContent(node.children))
126
112
  return `<blockquote>${text}</blockquote>\n\n`
127
113
  }
128
114
 
129
115
  default:
130
- return ''
116
+ return ""
131
117
  }
132
118
  }
133
119
 
@@ -1,145 +1,123 @@
1
- import type {
2
- DocChild,
3
- DocNode,
4
- DocumentRenderer,
5
- RenderOptions,
6
- TableColumn,
7
- } from '../types'
1
+ import type { DocChild, DocNode, DocumentRenderer, RenderOptions, TableColumn } from "../types"
8
2
 
9
3
  function resolveColumn(col: string | TableColumn): TableColumn {
10
- return typeof col === 'string' ? { header: col } : col
4
+ return typeof col === "string" ? { header: col } : col
11
5
  }
12
6
 
13
7
  function renderChild(child: DocChild): string {
14
- if (typeof child === 'string') return child
8
+ if (typeof child === "string") return child
15
9
  return renderNode(child)
16
10
  }
17
11
 
18
12
  function renderChildren(children: DocChild[]): string {
19
- return children.map(renderChild).join('')
13
+ return children.map(renderChild).join("")
20
14
  }
21
15
 
22
- function pad(
23
- str: string,
24
- width: number,
25
- align: 'left' | 'center' | 'right' = 'left',
26
- ): string {
16
+ function pad(str: string, width: number, align: "left" | "center" | "right" = "left"): string {
27
17
  if (str.length >= width) return str.slice(0, width)
28
18
  const diff = width - str.length
29
- if (align === 'center') {
19
+ if (align === "center") {
30
20
  const left = Math.floor(diff / 2)
31
- return ' '.repeat(left) + str + ' '.repeat(diff - left)
21
+ return " ".repeat(left) + str + " ".repeat(diff - left)
32
22
  }
33
- if (align === 'right') return ' '.repeat(diff) + str
34
- return str + ' '.repeat(diff)
23
+ if (align === "right") return " ".repeat(diff) + str
24
+ return str + " ".repeat(diff)
35
25
  }
36
26
 
37
27
  function renderNode(node: DocNode): string {
38
28
  const p = node.props
39
29
 
40
30
  switch (node.type) {
41
- case 'document':
31
+ case "document":
42
32
  return renderChildren(node.children)
43
33
 
44
- case 'page':
34
+ case "page":
45
35
  return renderChildren(node.children)
46
36
 
47
- case 'section':
48
- case 'row':
49
- case 'column':
37
+ case "section":
38
+ case "row":
39
+ case "column":
50
40
  return renderChildren(node.children)
51
41
 
52
- case 'heading': {
42
+ case "heading": {
53
43
  const text = renderChildren(node.children)
54
44
  const level = (p.level as number) ?? 1
55
- if (level === 1)
56
- return `${text.toUpperCase()}\n${'='.repeat(text.length)}\n\n`
57
- if (level === 2) return `${text}\n${'-'.repeat(text.length)}\n\n`
45
+ if (level === 1) return `${text.toUpperCase()}\n${"=".repeat(text.length)}\n\n`
46
+ if (level === 2) return `${text}\n${"-".repeat(text.length)}\n\n`
58
47
  return `${text}\n\n`
59
48
  }
60
49
 
61
- case 'text':
50
+ case "text":
62
51
  return `${renderChildren(node.children)}\n\n`
63
52
 
64
- case 'link':
53
+ case "link":
65
54
  return `${renderChildren(node.children)} (${p.href})`
66
55
 
67
- case 'image': {
68
- const alt = (p.alt as string) ?? 'Image'
69
- const caption = p.caption ? ` — ${p.caption}` : ''
56
+ case "image": {
57
+ const alt = (p.alt as string) ?? "Image"
58
+ const caption = p.caption ? ` — ${p.caption}` : ""
70
59
  return `[${alt}${caption}]\n\n`
71
60
  }
72
61
 
73
- case 'table': {
74
- const columns = ((p.columns ?? []) as (string | TableColumn)[]).map(
75
- resolveColumn,
76
- )
62
+ case "table": {
63
+ const columns = ((p.columns ?? []) as (string | TableColumn)[]).map(resolveColumn)
77
64
  const rows = (p.rows ?? []) as (string | number)[][]
78
65
 
79
- if (columns.length === 0) return ''
66
+ if (columns.length === 0) return ""
80
67
 
81
68
  // Calculate column widths
82
69
  const widths = columns.map((col, i) => {
83
70
  const headerLen = col.header.length
84
- const maxDataLen = rows.reduce(
85
- (max, row) => Math.max(max, String(row[i] ?? '').length),
86
- 0,
87
- )
71
+ const maxDataLen = rows.reduce((max, row) => Math.max(max, String(row[i] ?? "").length), 0)
88
72
  return Math.max(headerLen, maxDataLen, 3)
89
73
  })
90
74
 
91
75
  // Header
92
- const header = columns
93
- .map((col, i) => pad(col.header, widths[i] ?? 3, col.align))
94
- .join(' | ')
95
- const separator = widths.map((w) => '-'.repeat(w ?? 3)).join('-+-')
76
+ const header = columns.map((col, i) => pad(col.header, widths[i] ?? 3, col.align)).join(" | ")
77
+ const separator = widths.map((w) => "-".repeat(w ?? 3)).join("-+-")
96
78
 
97
79
  // Rows
98
80
  const body = rows
99
81
  .map((row) =>
100
- columns
101
- .map((col, i) =>
102
- pad(String(row[i] ?? ''), widths[i] ?? 3, col.align),
103
- )
104
- .join(' | '),
82
+ columns.map((col, i) => pad(String(row[i] ?? ""), widths[i] ?? 3, col.align)).join(" | "),
105
83
  )
106
- .join('\n')
84
+ .join("\n")
107
85
 
108
86
  let result = `${header}\n${separator}\n${body}\n\n`
109
87
  if (p.caption) result = `${p.caption}\n\n${result}`
110
88
  return result
111
89
  }
112
90
 
113
- case 'list': {
91
+ case "list": {
114
92
  const ordered = p.ordered as boolean | undefined
115
93
  return `${node.children
116
- .filter((c): c is DocNode => typeof c !== 'string')
94
+ .filter((c): c is DocNode => typeof c !== "string")
117
95
  .map((item, i) => {
118
- const prefix = ordered ? `${i + 1}.` : '*'
96
+ const prefix = ordered ? `${i + 1}.` : "*"
119
97
  return ` ${prefix} ${renderChildren(item.children)}`
120
98
  })
121
- .join('\n')}\n\n`
99
+ .join("\n")}\n\n`
122
100
  }
123
101
 
124
- case 'list-item':
102
+ case "list-item":
125
103
  return renderChildren(node.children)
126
104
 
127
- case 'code':
105
+ case "code":
128
106
  return `${renderChildren(node.children)}\n\n`
129
107
 
130
- case 'divider':
131
- return `${''.repeat(40)}\n\n`
108
+ case "divider":
109
+ return `${"".repeat(40)}\n\n`
132
110
 
133
- case 'page-break':
134
- return `\n${''.repeat(40)}\n\n`
111
+ case "page-break":
112
+ return `\n${"".repeat(40)}\n\n`
135
113
 
136
- case 'spacer':
137
- return '\n'
114
+ case "spacer":
115
+ return "\n"
138
116
 
139
- case 'button':
117
+ case "button":
140
118
  return `[${renderChildren(node.children)}] → ${p.href}\n\n`
141
119
 
142
- case 'quote':
120
+ case "quote":
143
121
  return ` "${renderChildren(node.children)}"\n\n`
144
122
 
145
123
  default:
@@ -1,11 +1,5 @@
1
- import { sanitizeHref } from '../sanitize'
2
- import type {
3
- DocChild,
4
- DocNode,
5
- DocumentRenderer,
6
- RenderOptions,
7
- TableColumn,
8
- } from '../types'
1
+ import { sanitizeHref } from "../sanitize"
2
+ import type { DocChild, DocNode, DocumentRenderer, RenderOptions, TableColumn } from "../types"
9
3
 
10
4
  /**
11
5
  * WhatsApp renderer — outputs formatted text using WhatsApp's markup.
@@ -13,36 +7,32 @@ import type {
13
7
  */
14
8
 
15
9
  function resolveColumn(col: string | TableColumn): TableColumn {
16
- return typeof col === 'string' ? { header: col } : col
10
+ return typeof col === "string" ? { header: col } : col
17
11
  }
18
12
 
19
13
  function getTextContent(children: DocChild[]): string {
20
14
  return children
21
- .map((c) =>
22
- typeof c === 'string' ? c : getTextContent((c as DocNode).children),
23
- )
24
- .join('')
15
+ .map((c) => (typeof c === "string" ? c : getTextContent((c as DocNode).children)))
16
+ .join("")
25
17
  }
26
18
 
27
19
  function renderNode(node: DocNode): string {
28
20
  const p = node.props
29
21
 
30
22
  switch (node.type) {
31
- case 'document':
32
- case 'page':
33
- case 'section':
34
- case 'row':
35
- case 'column':
36
- return node.children
37
- .map((c) => (typeof c === 'string' ? c : renderNode(c)))
38
- .join('')
39
-
40
- case 'heading': {
23
+ case "document":
24
+ case "page":
25
+ case "section":
26
+ case "row":
27
+ case "column":
28
+ return node.children.map((c) => (typeof c === "string" ? c : renderNode(c))).join("")
29
+
30
+ case "heading": {
41
31
  const text = getTextContent(node.children)
42
32
  return `*${text}*\n\n`
43
33
  }
44
34
 
45
- case 'text': {
35
+ case "text": {
46
36
  let text = getTextContent(node.children)
47
37
  if (p.bold) text = `*${text}*`
48
38
  if (p.italic) text = `_${text}_`
@@ -50,68 +40,64 @@ function renderNode(node: DocNode): string {
50
40
  return `${text}\n\n`
51
41
  }
52
42
 
53
- case 'link': {
43
+ case "link": {
54
44
  const href = sanitizeHref(p.href as string)
55
45
  const text = getTextContent(node.children)
56
46
  return `${text}: ${href}\n\n`
57
47
  }
58
48
 
59
- case 'image':
49
+ case "image":
60
50
  // WhatsApp doesn't support inline images in text
61
- return ''
51
+ return ""
62
52
 
63
- case 'table': {
64
- const columns = ((p.columns ?? []) as (string | TableColumn)[]).map(
65
- resolveColumn,
66
- )
53
+ case "table": {
54
+ const columns = ((p.columns ?? []) as (string | TableColumn)[]).map(resolveColumn)
67
55
  const rows = (p.rows ?? []) as (string | number)[][]
68
56
 
69
- const header = columns.map((c) => `*${c.header}*`).join(' | ')
70
- const body = rows
71
- .map((row) => row.map((c) => String(c ?? '')).join(' | '))
72
- .join('\n')
57
+ const header = columns.map((c) => `*${c.header}*`).join(" | ")
58
+ const body = rows.map((row) => row.map((c) => String(c ?? "")).join(" | ")).join("\n")
73
59
 
74
60
  let result = `${header}\n${body}\n\n`
75
61
  if (p.caption) result = `_${p.caption}_\n${result}`
76
62
  return result
77
63
  }
78
64
 
79
- case 'list': {
65
+ case "list": {
80
66
  const ordered = p.ordered as boolean | undefined
81
67
  return `${node.children
82
- .filter((c): c is DocNode => typeof c !== 'string')
68
+ .filter((c): c is DocNode => typeof c !== "string")
83
69
  .map((item, i) => {
84
- const prefix = ordered ? `${i + 1}.` : ''
70
+ const prefix = ordered ? `${i + 1}.` : ""
85
71
  return `${prefix} ${getTextContent(item.children)}`
86
72
  })
87
- .join('\n')}\n\n`
73
+ .join("\n")}\n\n`
88
74
  }
89
75
 
90
- case 'code': {
76
+ case "code": {
91
77
  const text = getTextContent(node.children)
92
78
  return `\`\`\`${text}\`\`\`\n\n`
93
79
  }
94
80
 
95
- case 'divider':
96
- case 'page-break':
97
- return '───────────\n\n'
81
+ case "divider":
82
+ case "page-break":
83
+ return "───────────\n\n"
98
84
 
99
- case 'spacer':
100
- return '\n'
85
+ case "spacer":
86
+ return "\n"
101
87
 
102
- case 'button': {
88
+ case "button": {
103
89
  const href = sanitizeHref(p.href as string)
104
90
  const text = getTextContent(node.children)
105
91
  return `*${text}*: ${href}\n\n`
106
92
  }
107
93
 
108
- case 'quote': {
94
+ case "quote": {
109
95
  const text = getTextContent(node.children)
110
96
  return `> ${text}\n\n`
111
97
  }
112
98
 
113
99
  default:
114
- return ''
100
+ return ""
115
101
  }
116
102
  }
117
103