@knpkv/confluence-to-markdown 0.4.2 → 0.6.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 (93) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +45 -10
  3. package/dist/ConfluenceAuth.d.ts.map +1 -1
  4. package/dist/ConfluenceAuth.js +12 -22
  5. package/dist/ConfluenceAuth.js.map +1 -1
  6. package/dist/ConfluenceClient.d.ts +13 -3
  7. package/dist/ConfluenceClient.d.ts.map +1 -1
  8. package/dist/ConfluenceClient.js +34 -70
  9. package/dist/ConfluenceClient.js.map +1 -1
  10. package/dist/ConfluenceError.d.ts +12 -12
  11. package/dist/GitError.d.ts +5 -5
  12. package/dist/GitService.d.ts.map +1 -1
  13. package/dist/GitService.js +0 -3
  14. package/dist/GitService.js.map +1 -1
  15. package/dist/SchemaConverterError.d.ts +3 -3
  16. package/dist/parsers/preprocessing/ConfluencePreprocessing.d.ts +23 -0
  17. package/dist/parsers/preprocessing/ConfluencePreprocessing.d.ts.map +1 -0
  18. package/dist/parsers/preprocessing/ConfluencePreprocessing.js +323 -0
  19. package/dist/parsers/preprocessing/ConfluencePreprocessing.js.map +1 -0
  20. package/dist/parsers/preprocessing/index.d.ts +7 -0
  21. package/dist/parsers/preprocessing/index.d.ts.map +1 -0
  22. package/dist/parsers/preprocessing/index.js +7 -0
  23. package/dist/parsers/preprocessing/index.js.map +1 -0
  24. package/dist/schemas/preprocessing/ConfluencePreprocessor.d.ts +29 -0
  25. package/dist/schemas/preprocessing/ConfluencePreprocessor.d.ts.map +1 -1
  26. package/dist/schemas/preprocessing/ConfluencePreprocessor.js +3 -5
  27. package/dist/schemas/preprocessing/ConfluencePreprocessor.js.map +1 -1
  28. package/package.json +35 -26
  29. package/src/AdfPlaceholders.ts +266 -0
  30. package/src/AdfSchemaValidator.ts +67 -0
  31. package/src/AdfWalker.ts +511 -0
  32. package/src/AtlaskitTransformers.ts +72 -0
  33. package/src/ConfluenceClient.ts +4 -4
  34. package/src/ConfluenceError.ts +65 -3
  35. package/src/MarkdownConverter.ts +106 -139
  36. package/src/Schemas.ts +4 -4
  37. package/src/SyncEngine.ts +130 -83
  38. package/src/atlaskit-adf-schema.d.ts +3 -0
  39. package/src/commands/clone.ts +8 -1
  40. package/src/commands/layers.ts +11 -4
  41. package/src/index.ts +3 -18
  42. package/test/AdfPlaceholders.test.ts +295 -0
  43. package/test/AdfSchemaValidator.test.ts +34 -0
  44. package/test/AdfWalker.test.ts +530 -0
  45. package/test/AtlaskitTransformers.test.ts +25 -0
  46. package/test/MarkdownConverter.test.ts +120 -105
  47. package/test/RoundTrip.test.ts +266 -0
  48. package/LICENSE +0 -21
  49. package/src/SchemaConverterError.ts +0 -108
  50. package/src/ast/BlockNode.ts +0 -425
  51. package/src/ast/Document.ts +0 -90
  52. package/src/ast/InlineNode.ts +0 -323
  53. package/src/ast/MacroNode.ts +0 -245
  54. package/src/ast/index.ts +0 -83
  55. package/src/parsers/ConfluenceParser.ts +0 -950
  56. package/src/parsers/MarkdownParser.ts +0 -1198
  57. package/src/parsers/index.ts +0 -8
  58. package/src/schemas/ConfluenceSchema.ts +0 -56
  59. package/src/schemas/ConversionSchema.ts +0 -318
  60. package/src/schemas/MarkdownSchema.ts +0 -56
  61. package/src/schemas/hast/HastFromHtml.ts +0 -153
  62. package/src/schemas/hast/HastSchema.ts +0 -274
  63. package/src/schemas/hast/index.ts +0 -35
  64. package/src/schemas/index.ts +0 -20
  65. package/src/schemas/mdast/MdastFromMarkdown.ts +0 -118
  66. package/src/schemas/mdast/MdastSchema.ts +0 -566
  67. package/src/schemas/mdast/index.ts +0 -59
  68. package/src/schemas/mdast/mdastToString.ts +0 -102
  69. package/src/schemas/nodes/block/BlockSchema.ts +0 -773
  70. package/src/schemas/nodes/block/index.ts +0 -13
  71. package/src/schemas/nodes/index.ts +0 -20
  72. package/src/schemas/nodes/inline/InlineSchema.ts +0 -523
  73. package/src/schemas/nodes/inline/index.ts +0 -14
  74. package/src/schemas/nodes/macro/MacroSchema.ts +0 -226
  75. package/src/schemas/nodes/macro/index.ts +0 -6
  76. package/src/schemas/preprocessing/ConfluencePreprocessor.ts +0 -446
  77. package/src/schemas/preprocessing/index.ts +0 -8
  78. package/src/serializers/ConfluenceSerializer.ts +0 -717
  79. package/src/serializers/MarkdownSerializer.ts +0 -493
  80. package/src/serializers/index.ts +0 -8
  81. package/test/ast/BlockNode.test.ts +0 -265
  82. package/test/ast/Document.test.ts +0 -126
  83. package/test/ast/InlineNode.test.ts +0 -161
  84. package/test/fixtures/integration-test.html.fixture +0 -103
  85. package/test/fixtures/integration-test.md.expected +0 -257
  86. package/test/parsers/ConfluenceParser.test.ts +0 -283
  87. package/test/schemas/ConfluencePreprocessor.test.ts +0 -180
  88. package/test/schemas/ConversionSchema.test.ts +0 -159
  89. package/test/schemas/HastSchema.test.ts +0 -138
  90. package/test/schemas/MdastSchema.test.ts +0 -145
  91. package/test/schemas/nodes/block/BlockSchema.test.ts +0 -173
  92. package/test/schemas/nodes/inline/InlineSchema.test.ts +0 -198
  93. package/test/schemas/nodes/macro/MacroSchema.test.ts +0 -142
@@ -1,493 +0,0 @@
1
- /**
2
- * Serializer for AST to Markdown.
3
- *
4
- * @module
5
- */
6
- import * as Effect from "effect/Effect"
7
- import type { CodeBlock, Heading, Image, Paragraph, Table, ThematicBreak, UnsupportedBlock } from "../ast/BlockNode.js"
8
- import type { Document, DocumentNode } from "../ast/Document.js"
9
- import type { InlineNode } from "../ast/InlineNode.js"
10
- import type { SerializeError } from "../SchemaConverterError.js"
11
-
12
- /**
13
- * Serialize Document AST to Markdown.
14
- *
15
- * @example
16
- * ```typescript
17
- * import { serializeToMarkdown } from "@knpkv/confluence-to-markdown/serializers/MarkdownSerializer"
18
- * import { makeDocument, Heading, Text } from "@knpkv/confluence-to-markdown/ast"
19
- * import { Effect } from "effect"
20
- *
21
- * Effect.gen(function* () {
22
- * const doc = makeDocument([
23
- * new Heading({ level: 1, children: [new Text({ value: "Title" })] })
24
- * ])
25
- * const md = yield* serializeToMarkdown(doc)
26
- * console.log(md) // # Title
27
- * })
28
- * ```
29
- *
30
- * @category Serializers
31
- */
32
- export interface SerializeOptions {
33
- /** Include raw Confluence HTML for lossless roundtrip. Default: true */
34
- readonly includeRawSource?: boolean
35
- }
36
-
37
- export const serializeToMarkdown = (
38
- doc: Document,
39
- options: SerializeOptions = {}
40
- ): Effect.Effect<string, SerializeError> =>
41
- Effect.gen(function*() {
42
- const { includeRawSource = true } = options
43
- const parts: Array<string> = []
44
- for (const node of doc.children) {
45
- const serialized = yield* serializeDocumentNode(node)
46
- parts.push(serialized)
47
- }
48
-
49
- const content = parts.join("\n\n")
50
-
51
- // Embed rawConfluence in comment for 1-to-1 roundtrip preservation
52
- if (includeRawSource && doc.rawConfluence !== undefined) {
53
- // Encode entire raw HTML for roundtrip
54
- const encoded = Buffer.from(doc.rawConfluence, "utf-8").toString("base64")
55
- return `${content}\n\n<!--cf:raw:${encoded}-->`
56
- }
57
-
58
- return content
59
- })
60
-
61
- /**
62
- * Serialize a document node to Markdown.
63
- */
64
- const serializeDocumentNode = (node: DocumentNode): Effect.Effect<string, SerializeError> =>
65
- Effect.gen(function*() {
66
- switch (node._tag) {
67
- // Block nodes
68
- case "Heading":
69
- return yield* serializeHeading(node)
70
- case "Paragraph":
71
- return yield* serializeParagraph({
72
- children: node.children,
73
- alignment: node.alignment,
74
- indent: node.indent
75
- })
76
- case "CodeBlock":
77
- return serializeCodeBlock(node)
78
- case "ThematicBreak":
79
- return "---"
80
- case "Image":
81
- return serializeImage(node)
82
- case "Table":
83
- return yield* serializeTable(node)
84
- case "List":
85
- return yield* serializeList(node)
86
- case "BlockQuote":
87
- return yield* serializeBlockQuote(node)
88
- case "UnsupportedBlock":
89
- return node.rawMarkdown || node.rawHtml || ""
90
-
91
- // Macro nodes
92
- case "InfoPanel":
93
- return yield* serializeInfoPanel(node)
94
- case "ExpandMacro":
95
- return yield* serializeExpandMacro(node)
96
- case "TocMacro":
97
- return serializeTocMacro(node)
98
- case "CodeMacro":
99
- return serializeCodeMacro(node)
100
- case "StatusMacro":
101
- return serializeStatusMacro(node)
102
- case "TaskList":
103
- return yield* serializeTaskList(node.children)
104
-
105
- default:
106
- return ""
107
- }
108
- })
109
-
110
- /**
111
- * Serialize heading.
112
- */
113
- const serializeHeading = (
114
- node: { level: 1 | 2 | 3 | 4 | 5 | 6; children: ReadonlyArray<InlineNode> }
115
- ): Effect.Effect<string, SerializeError> =>
116
- Effect.gen(function*() {
117
- const prefix = "#".repeat(node.level)
118
- const content = yield* serializeInlineNodes(node.children)
119
- return `${prefix} ${content}`
120
- })
121
-
122
- /**
123
- * Serialize paragraph.
124
- */
125
- const serializeParagraph = (
126
- node: {
127
- children: ReadonlyArray<InlineNode>
128
- alignment?: "left" | "center" | "right" | undefined
129
- indent?: number | undefined
130
- }
131
- ): Effect.Effect<string, SerializeError> =>
132
- Effect.gen(function*() {
133
- const content = yield* serializeInlineNodes(node.children)
134
- // If has alignment or indent, wrap in HTML div for roundtrip
135
- if (node.alignment || node.indent) {
136
- const styles: Array<string> = []
137
- if (node.alignment) styles.push(`text-align: ${node.alignment};`)
138
- if (node.indent) styles.push(`margin-left: ${node.indent}px;`)
139
- return `<p style="${styles.join(" ")}">${content}</p>`
140
- }
141
- return content
142
- })
143
-
144
- /**
145
- * Serialize code block.
146
- */
147
- const serializeCodeBlock = (node: { code: string; language?: string | undefined }): string => {
148
- const lang = node.language || ""
149
- return `\`\`\`${lang}\n${node.code}\n\`\`\``
150
- }
151
-
152
- /**
153
- * Serialize image (supports both URL and Confluence attachments).
154
- * Uses comment-encoding for attachments to preserve roundtrip fidelity.
155
- */
156
- const serializeImage = (node: {
157
- src?: string | undefined
158
- attachment?: { filename: string; version?: number | undefined } | undefined
159
- alt?: string | undefined
160
- title?: string | undefined
161
- align?: string | undefined
162
- width?: number | undefined
163
- }): string => {
164
- // If image has attachment or align/width, use comment encoding for roundtrip
165
- if (node.attachment || node.align || node.width) {
166
- const parts: Array<string> = []
167
- if (node.attachment) {
168
- parts.push(`f=${encodeURIComponent(node.attachment.filename)}`)
169
- if (node.attachment.version) parts.push(`v=${node.attachment.version}`)
170
- }
171
- if (node.src) parts.push(`s=${encodeURIComponent(node.src)}`)
172
- if (node.alt) parts.push(`a=${encodeURIComponent(node.alt)}`)
173
- if (node.title) parts.push(`t=${encodeURIComponent(node.title)}`)
174
- if (node.align) parts.push(`al=${node.align}`)
175
- if (node.width) parts.push(`w=${node.width}`)
176
- return `<!--cf:image:${parts.join("|")}-->`
177
- }
178
-
179
- // Simple external image - use markdown syntax
180
- const alt = node.alt || ""
181
- const title = node.title ? ` "${node.title}"` : ""
182
- const src = node.src || ""
183
- return `![${alt}](${src}${title})`
184
- }
185
-
186
- /**
187
- * Serialize table.
188
- */
189
- const serializeTable = (
190
- node: {
191
- header?: { cells: ReadonlyArray<{ children: ReadonlyArray<InlineNode> }> } | undefined
192
- rows: ReadonlyArray<{ cells: ReadonlyArray<{ children: ReadonlyArray<InlineNode> }> }>
193
- }
194
- ): Effect.Effect<string, SerializeError> =>
195
- Effect.gen(function*() {
196
- const lines: Array<string> = []
197
-
198
- // Header
199
- if (node.header) {
200
- const headerCells: Array<string> = []
201
- for (const cell of node.header.cells) {
202
- headerCells.push(yield* serializeInlineNodes(cell.children))
203
- }
204
- lines.push(`| ${headerCells.join(" | ")} |`)
205
- lines.push(`| ${headerCells.map(() => "---").join(" | ")} |`)
206
- }
207
-
208
- // Body rows
209
- for (const row of node.rows) {
210
- const cells: Array<string> = []
211
- for (const cell of row.cells) {
212
- cells.push(yield* serializeInlineNodes(cell.children))
213
- }
214
- lines.push(`| ${cells.join(" | ")} |`)
215
- }
216
-
217
- return lines.join("\n")
218
- })
219
-
220
- // Simple block type for list items
221
- type SimpleBlock =
222
- | Heading
223
- | Paragraph
224
- | CodeBlock
225
- | ThematicBreak
226
- | Image
227
- | Table
228
- | UnsupportedBlock
229
-
230
- // List item type
231
- type ListItemType = {
232
- readonly _tag: "ListItem"
233
- readonly checked?: boolean | undefined
234
- readonly children: ReadonlyArray<SimpleBlock>
235
- }
236
-
237
- /**
238
- * Serialize list.
239
- */
240
- const serializeList = (
241
- node: { ordered: boolean; start?: number | undefined; children: ReadonlyArray<ListItemType> }
242
- ): Effect.Effect<string, SerializeError> =>
243
- Effect.gen(function*() {
244
- const lines: Array<string> = []
245
- let counter = node.start || 1
246
-
247
- for (const item of node.children) {
248
- const prefix = node.ordered ? `${counter}.` : "-"
249
- const checkbox = item.checked !== undefined ? (item.checked ? "[x] " : "[ ] ") : ""
250
-
251
- // Serialize item content
252
- const itemParts: Array<string> = []
253
- for (const child of item.children) {
254
- const serialized = yield* serializeSimpleBlock(child)
255
- itemParts.push(serialized)
256
- }
257
-
258
- const content = itemParts.join("\n")
259
- const indentedContent = content
260
- .split("\n")
261
- .map((line, i) => (i === 0 ? `${prefix} ${checkbox}${line}` : ` ${line}`))
262
- .join("\n")
263
-
264
- lines.push(indentedContent)
265
- counter++
266
- }
267
-
268
- return lines.join("\n")
269
- })
270
-
271
- /**
272
- * Serialize simple block (for nested content).
273
- */
274
- const serializeSimpleBlock = (node: SimpleBlock): Effect.Effect<string, SerializeError> =>
275
- Effect.gen(function*() {
276
- switch (node._tag) {
277
- case "Heading":
278
- return yield* serializeHeading(
279
- node as unknown as { level: 1 | 2 | 3 | 4 | 5 | 6; children: ReadonlyArray<InlineNode> }
280
- )
281
- case "Paragraph":
282
- return yield* serializeParagraph(node as unknown as { children: ReadonlyArray<InlineNode> })
283
- case "CodeBlock":
284
- return serializeCodeBlock(node as unknown as { code: string; language?: string | undefined })
285
- case "ThematicBreak":
286
- return "---"
287
- case "Image":
288
- return serializeImage(node as unknown as { src: string; alt?: string | undefined; title?: string | undefined })
289
- case "Table":
290
- return yield* serializeTable(
291
- node as unknown as {
292
- header?: { cells: ReadonlyArray<{ children: ReadonlyArray<InlineNode> }> } | undefined
293
- rows: ReadonlyArray<{ cells: ReadonlyArray<{ children: ReadonlyArray<InlineNode> }> }>
294
- }
295
- )
296
- case "UnsupportedBlock": {
297
- const unsupported = node as unknown as { rawMarkdown?: string; rawHtml?: string }
298
- return unsupported.rawMarkdown || unsupported.rawHtml || ""
299
- }
300
- default:
301
- return ""
302
- }
303
- })
304
-
305
- /**
306
- * Serialize block quote.
307
- */
308
- const serializeBlockQuote = (
309
- node: { children: ReadonlyArray<SimpleBlock> }
310
- ): Effect.Effect<string, SerializeError> =>
311
- Effect.gen(function*() {
312
- const lines: Array<string> = []
313
- for (const child of node.children) {
314
- const serialized = yield* serializeSimpleBlock(child as SimpleBlock)
315
- const quoted = serialized.split("\n").map((line) => `> ${line}`).join("\n")
316
- lines.push(quoted)
317
- }
318
- return lines.join("\n>\n")
319
- })
320
-
321
- /**
322
- * Serialize info panel to container syntax.
323
- */
324
- const serializeInfoPanel = (
325
- node: { panelType: string; title?: string | undefined; children: ReadonlyArray<SimpleBlock> }
326
- ): Effect.Effect<string, SerializeError> =>
327
- Effect.gen(function*() {
328
- const type = node.panelType
329
- const title = node.title ? ` ${node.title}` : ""
330
- const lines: Array<string> = [`:::${type}${title}`]
331
-
332
- for (const child of node.children) {
333
- const serialized = yield* serializeSimpleBlock(child as SimpleBlock)
334
- lines.push(serialized)
335
- }
336
-
337
- lines.push(":::")
338
- return lines.join("\n")
339
- })
340
-
341
- /**
342
- * Serialize expand macro - use comment encoding for roundtrip.
343
- */
344
- const serializeExpandMacro = (
345
- node: { title?: string | undefined; children: ReadonlyArray<SimpleBlock> }
346
- ): Effect.Effect<string, SerializeError> =>
347
- Effect.gen(function*() {
348
- const title = node.title || ""
349
- const contentParts: Array<string> = []
350
-
351
- for (const child of node.children) {
352
- const serialized = yield* serializeSimpleBlock(child as SimpleBlock)
353
- contentParts.push(serialized)
354
- }
355
-
356
- const content = contentParts.join("\n")
357
- // Use comment encoding for roundtrip
358
- return `<!--cf:expand:${encodeURIComponent(title)}:${encodeURIComponent(content)}-->`
359
- })
360
-
361
- /**
362
- * Serialize TOC macro.
363
- */
364
- const serializeTocMacro = (_node: { minLevel?: number | undefined; maxLevel?: number | undefined }): string => {
365
- return "[[toc]]"
366
- }
367
-
368
- /**
369
- * Serialize code macro (similar to code block but may have title).
370
- */
371
- const serializeCodeMacro = (
372
- node: { language?: string | undefined; title?: string | undefined; code: string }
373
- ): string => {
374
- const lang = node.language || ""
375
- const title = node.title ? ` title="${node.title}"` : ""
376
- return `\`\`\`${lang}${title}\n${node.code}\n\`\`\``
377
- }
378
-
379
- /**
380
- * Serialize status macro.
381
- */
382
- const serializeStatusMacro = (node: { text: string; color: string }): string => {
383
- return `**[${node.text}]**`
384
- }
385
-
386
- /**
387
- * Serialize task list - preserve as comment-encoded for roundtrip (single line).
388
- */
389
- const serializeTaskList = (
390
- children: ReadonlyArray<{
391
- _tag: "TaskItem"
392
- id: string
393
- uuid: string
394
- status: "incomplete" | "complete"
395
- body: ReadonlyArray<InlineNode>
396
- }>
397
- ): Effect.Effect<string, SerializeError> =>
398
- Effect.gen(function*() {
399
- const items: Array<string> = []
400
- for (const item of children) {
401
- const body = yield* serializeInlineNodes(item.body)
402
- // Encode task item - use | separator to avoid : in content issues
403
- items.push(`${item.id}|${item.uuid}|${item.status}|${encodeURIComponent(body)}`)
404
- }
405
- // Single line comment to prevent remark from splitting
406
- return `<!--cf:tasklist:${items.join(";")}-->`
407
- })
408
-
409
- /**
410
- * Serialize inline nodes to Markdown.
411
- */
412
- const serializeInlineNodes = (
413
- nodes: ReadonlyArray<InlineNode>
414
- ): Effect.Effect<string, SerializeError> =>
415
- Effect.gen(function*() {
416
- const parts: Array<string> = []
417
- for (const node of nodes) {
418
- parts.push(yield* serializeInlineNode(node))
419
- }
420
- return parts.join("")
421
- })
422
-
423
- /**
424
- * Serialize inline node to Markdown.
425
- */
426
- const serializeInlineNode = (node: InlineNode): Effect.Effect<string, SerializeError> =>
427
- Effect.gen(function*() {
428
- switch (node._tag) {
429
- case "Text":
430
- return node.value
431
- case "Strong": {
432
- const content = yield* serializeInlineNodes(node.children)
433
- return `**${content}**`
434
- }
435
- case "Emphasis": {
436
- const content = yield* serializeInlineNodes(node.children)
437
- return `*${content}*`
438
- }
439
- case "Underline": {
440
- // No native markdown support, use HTML
441
- const content = yield* serializeInlineNodes(node.children)
442
- return `<u>${content}</u>`
443
- }
444
- case "Strikethrough": {
445
- const content = yield* serializeInlineNodes(node.children)
446
- return `~~${content}~~`
447
- }
448
- case "Subscript": {
449
- // No native markdown support, use HTML
450
- const content = yield* serializeInlineNodes(node.children)
451
- return `<sub>${content}</sub>`
452
- }
453
- case "Superscript": {
454
- // No native markdown support, use HTML
455
- const content = yield* serializeInlineNodes(node.children)
456
- return `<sup>${content}</sup>`
457
- }
458
- case "InlineCode":
459
- return `\`${node.value}\``
460
- case "Link": {
461
- const content = yield* serializeInlineNodes(node.children)
462
- const title = node.title ? ` "${node.title}"` : ""
463
- return `[${content}](${node.href}${title})`
464
- }
465
- case "LineBreak":
466
- return " \n"
467
- case "Emoticon":
468
- // Wrap in HTML comment with URL-encoded values
469
- return `<!--cf:emoticon:${encodeURIComponent(node.shortname)}|${encodeURIComponent(node.emojiId)}|${
470
- encodeURIComponent(node.fallback)
471
- }-->`
472
- case "UserMention":
473
- // Wrap in HTML comment to prevent remark from parsing
474
- return `<!--cf:user:${node.accountId}-->`
475
- case "DateTime":
476
- // Wrap in HTML comment to prevent remark from parsing
477
- return `<!--cf:date:${node.datetime}-->`
478
- case "ColoredText": {
479
- // Preserve as HTML for roundtrip
480
- const content = yield* serializeInlineNodes(node.children)
481
- return `<span style="color: ${node.color};">${content}</span>`
482
- }
483
- case "Highlight": {
484
- // Preserve as HTML for roundtrip
485
- const content = yield* serializeInlineNodes(node.children)
486
- return `<span style="background-color: ${node.backgroundColor};">${content}</span>`
487
- }
488
- case "UnsupportedInline":
489
- return node.raw
490
- default:
491
- return ""
492
- }
493
- })
@@ -1,8 +0,0 @@
1
- /**
2
- * Serializers for AST to Confluence HTML and Markdown.
3
- *
4
- * @module
5
- */
6
-
7
- export { serializeToConfluence } from "./ConfluenceSerializer.js"
8
- export { serializeToMarkdown } from "./MarkdownSerializer.js"