@knpkv/confluence-to-markdown 0.5.0 → 0.7.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 (395) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/README.md +58 -14
  3. package/dist/AdfPlaceholders.d.ts +42 -0
  4. package/dist/AdfPlaceholders.d.ts.map +1 -0
  5. package/dist/AdfPlaceholders.js +547 -0
  6. package/dist/AdfPlaceholders.js.map +1 -0
  7. package/dist/AdfSchemaValidator.d.ts +37 -0
  8. package/dist/AdfSchemaValidator.d.ts.map +1 -0
  9. package/dist/AdfSchemaValidator.js +37 -0
  10. package/dist/AdfSchemaValidator.js.map +1 -0
  11. package/dist/AdfWalker.d.ts +39 -0
  12. package/dist/AdfWalker.d.ts.map +1 -0
  13. package/dist/AdfWalker.js +527 -0
  14. package/dist/AdfWalker.js.map +1 -0
  15. package/dist/AtlaskitTransformers.d.ts +35 -0
  16. package/dist/AtlaskitTransformers.d.ts.map +1 -0
  17. package/dist/AtlaskitTransformers.js +48 -0
  18. package/dist/AtlaskitTransformers.js.map +1 -0
  19. package/dist/Brand.d.ts +6 -6
  20. package/dist/Brand.d.ts.map +1 -1
  21. package/dist/Brand.js +8 -6
  22. package/dist/Brand.js.map +1 -1
  23. package/dist/ConfluenceAuth.d.ts +4 -4
  24. package/dist/ConfluenceAuth.d.ts.map +1 -1
  25. package/dist/ConfluenceAuth.js +15 -27
  26. package/dist/ConfluenceAuth.js.map +1 -1
  27. package/dist/ConfluenceClient.d.ts +4 -4
  28. package/dist/ConfluenceClient.d.ts.map +1 -1
  29. package/dist/ConfluenceClient.js +21 -14
  30. package/dist/ConfluenceClient.js.map +1 -1
  31. package/dist/ConfluenceConfig.d.ts +3 -3
  32. package/dist/ConfluenceConfig.d.ts.map +1 -1
  33. package/dist/ConfluenceConfig.js +13 -11
  34. package/dist/ConfluenceConfig.js.map +1 -1
  35. package/dist/ConfluenceError.d.ts +56 -4
  36. package/dist/ConfluenceError.d.ts.map +1 -1
  37. package/dist/ConfluenceError.js +30 -1
  38. package/dist/ConfluenceError.js.map +1 -1
  39. package/dist/GitService.d.ts +11 -3
  40. package/dist/GitService.d.ts.map +1 -1
  41. package/dist/GitService.js +19 -27
  42. package/dist/GitService.js.map +1 -1
  43. package/dist/LocalFileSystem.d.ts +3 -3
  44. package/dist/LocalFileSystem.d.ts.map +1 -1
  45. package/dist/LocalFileSystem.js +6 -6
  46. package/dist/LocalFileSystem.js.map +1 -1
  47. package/dist/MarkdownConverter.d.ts +16 -65
  48. package/dist/MarkdownConverter.d.ts.map +1 -1
  49. package/dist/MarkdownConverter.js +64 -85
  50. package/dist/MarkdownConverter.js.map +1 -1
  51. package/dist/Schemas.d.ts +128 -141
  52. package/dist/Schemas.d.ts.map +1 -1
  53. package/dist/Schemas.js +21 -23
  54. package/dist/Schemas.js.map +1 -1
  55. package/dist/SyncEngine.d.ts +8 -5
  56. package/dist/SyncEngine.d.ts.map +1 -1
  57. package/dist/SyncEngine.js +189 -113
  58. package/dist/SyncEngine.js.map +1 -1
  59. package/dist/bin.js +23 -35
  60. package/dist/bin.js.map +1 -1
  61. package/dist/commands/auth.d.ts +2 -14
  62. package/dist/commands/auth.d.ts.map +1 -1
  63. package/dist/commands/auth.js +11 -16
  64. package/dist/commands/auth.js.map +1 -1
  65. package/dist/commands/clone.d.ts +4 -6
  66. package/dist/commands/clone.d.ts.map +1 -1
  67. package/dist/commands/clone.js +34 -32
  68. package/dist/commands/clone.js.map +1 -1
  69. package/dist/commands/delete.d.ts +2 -10
  70. package/dist/commands/delete.d.ts.map +1 -1
  71. package/dist/commands/delete.js +5 -4
  72. package/dist/commands/delete.js.map +1 -1
  73. package/dist/commands/errorHandler.d.ts +2 -1
  74. package/dist/commands/errorHandler.d.ts.map +1 -1
  75. package/dist/commands/errorHandler.js +22 -15
  76. package/dist/commands/errorHandler.js.map +1 -1
  77. package/dist/commands/fetch.d.ts +27 -0
  78. package/dist/commands/fetch.d.ts.map +1 -0
  79. package/dist/commands/fetch.js +48 -0
  80. package/dist/commands/fetch.js.map +1 -0
  81. package/dist/commands/git.d.ts +7 -10
  82. package/dist/commands/git.d.ts.map +1 -1
  83. package/dist/commands/git.js +6 -6
  84. package/dist/commands/git.js.map +1 -1
  85. package/dist/commands/index.d.ts +1 -0
  86. package/dist/commands/index.d.ts.map +1 -1
  87. package/dist/commands/index.js +1 -0
  88. package/dist/commands/index.js.map +1 -1
  89. package/dist/commands/layers.d.ts +10 -9
  90. package/dist/commands/layers.d.ts.map +1 -1
  91. package/dist/commands/layers.js +41 -30
  92. package/dist/commands/layers.js.map +1 -1
  93. package/dist/commands/new.d.ts +2 -6
  94. package/dist/commands/new.d.ts.map +1 -1
  95. package/dist/commands/new.js +5 -4
  96. package/dist/commands/new.js.map +1 -1
  97. package/dist/commands/pageInput.d.ts +19 -0
  98. package/dist/commands/pageInput.d.ts.map +1 -0
  99. package/dist/commands/pageInput.js +68 -0
  100. package/dist/commands/pageInput.js.map +1 -0
  101. package/dist/commands/root.d.ts +8 -0
  102. package/dist/commands/root.d.ts.map +1 -0
  103. package/dist/commands/root.js +29 -0
  104. package/dist/commands/root.js.map +1 -0
  105. package/dist/commands/sync.d.ts +6 -9
  106. package/dist/commands/sync.d.ts.map +1 -1
  107. package/dist/commands/sync.js +5 -6
  108. package/dist/commands/sync.js.map +1 -1
  109. package/dist/index.d.ts +3 -8
  110. package/dist/index.d.ts.map +1 -1
  111. package/dist/index.js +2 -13
  112. package/dist/index.js.map +1 -1
  113. package/dist/internal/NodeLayers.d.ts.map +1 -1
  114. package/dist/internal/NodeLayers.js +1 -2
  115. package/dist/internal/NodeLayers.js.map +1 -1
  116. package/dist/internal/adfMetadata.d.ts +30 -0
  117. package/dist/internal/adfMetadata.d.ts.map +1 -0
  118. package/dist/internal/adfMetadata.js +126 -0
  119. package/dist/internal/adfMetadata.js.map +1 -0
  120. package/dist/internal/cleanMarkdown.d.ts +5 -0
  121. package/dist/internal/cleanMarkdown.d.ts.map +1 -0
  122. package/dist/internal/cleanMarkdown.js +13 -0
  123. package/dist/internal/cleanMarkdown.js.map +1 -0
  124. package/dist/internal/frontmatter.d.ts.map +1 -1
  125. package/dist/internal/frontmatter.js +41 -8
  126. package/dist/internal/frontmatter.js.map +1 -1
  127. package/dist/internal/gitCommands.d.ts +9 -3
  128. package/dist/internal/gitCommands.d.ts.map +1 -1
  129. package/dist/internal/gitCommands.js +18 -9
  130. package/dist/internal/gitCommands.js.map +1 -1
  131. package/dist/internal/hashUtils.d.ts +1 -1
  132. package/dist/internal/hashUtils.d.ts.map +1 -1
  133. package/dist/internal/hashUtils.js +1 -1
  134. package/dist/internal/hashUtils.js.map +1 -1
  135. package/dist/internal/oauthServer.d.ts +10 -5
  136. package/dist/internal/oauthServer.d.ts.map +1 -1
  137. package/dist/internal/oauthServer.js +19 -40
  138. package/dist/internal/oauthServer.js.map +1 -1
  139. package/dist/internal/pathUtils.d.ts +1 -1
  140. package/dist/internal/pathUtils.d.ts.map +1 -1
  141. package/dist/internal/pathUtils.js +1 -1
  142. package/dist/internal/pathUtils.js.map +1 -1
  143. package/dist/internal/process.d.ts +15 -0
  144. package/dist/internal/process.d.ts.map +1 -0
  145. package/dist/internal/process.js +10 -0
  146. package/dist/internal/process.js.map +1 -0
  147. package/dist/internal/stdio.d.ts +6 -0
  148. package/dist/internal/stdio.d.ts.map +1 -0
  149. package/dist/internal/stdio.js +15 -0
  150. package/dist/internal/stdio.js.map +1 -0
  151. package/dist/internal/tokenStorage.d.ts +3 -13
  152. package/dist/internal/tokenStorage.d.ts.map +1 -1
  153. package/dist/internal/tokenStorage.js +26 -24
  154. package/dist/internal/tokenStorage.js.map +1 -1
  155. package/dist/internal/userCache.d.ts +1 -1
  156. package/dist/internal/userCache.d.ts.map +1 -1
  157. package/dist/internal/userCache.js +1 -1
  158. package/dist/internal/userCache.js.map +1 -1
  159. package/package.json +29 -20
  160. package/skills/confluence/SKILL.md +143 -0
  161. package/skills/confluence/agents/openai.yaml +4 -0
  162. package/src/AdfPlaceholders.ts +563 -0
  163. package/src/AdfSchemaValidator.ts +65 -0
  164. package/src/AdfWalker.ts +591 -0
  165. package/src/AtlaskitTransformers.ts +70 -0
  166. package/src/Brand.ts +11 -16
  167. package/src/ConfluenceAuth.ts +22 -30
  168. package/src/ConfluenceClient.ts +28 -24
  169. package/src/ConfluenceConfig.ts +14 -14
  170. package/src/ConfluenceError.ts +65 -3
  171. package/src/GitService.ts +39 -49
  172. package/src/LocalFileSystem.ts +7 -9
  173. package/src/MarkdownConverter.ts +108 -143
  174. package/src/Schemas.ts +17 -16
  175. package/src/SyncEngine.ts +272 -127
  176. package/src/atlaskit-adf-schema.d.ts +3 -0
  177. package/src/bin.ts +30 -56
  178. package/src/commands/auth.ts +21 -18
  179. package/src/commands/clone.ts +46 -38
  180. package/src/commands/delete.ts +5 -4
  181. package/src/commands/errorHandler.ts +25 -18
  182. package/src/commands/fetch.ts +90 -0
  183. package/src/commands/git.ts +6 -6
  184. package/src/commands/index.ts +1 -0
  185. package/src/commands/layers.ts +64 -37
  186. package/src/commands/new.ts +5 -4
  187. package/src/commands/pageInput.ts +103 -0
  188. package/src/commands/root.ts +59 -0
  189. package/src/commands/sync.ts +7 -6
  190. package/src/index.ts +3 -18
  191. package/src/internal/NodeLayers.ts +1 -2
  192. package/src/internal/adfMetadata.ts +145 -0
  193. package/src/internal/cleanMarkdown.ts +15 -0
  194. package/src/internal/frontmatter.ts +45 -8
  195. package/src/internal/gitCommands.ts +23 -17
  196. package/src/internal/hashUtils.ts +2 -2
  197. package/src/internal/oauthServer.ts +84 -105
  198. package/src/internal/pathUtils.ts +1 -1
  199. package/src/internal/process.ts +15 -0
  200. package/src/internal/stdio.ts +22 -0
  201. package/src/internal/tokenStorage.ts +39 -29
  202. package/src/internal/userCache.ts +2 -2
  203. package/test/AdfPlaceholders.test.ts +508 -0
  204. package/test/AdfSchemaValidator.test.ts +34 -0
  205. package/test/AdfWalker.test.ts +676 -0
  206. package/test/AtlaskitTransformers.test.ts +25 -0
  207. package/test/Brand.test.ts +11 -11
  208. package/test/GitService.test.ts +6 -2
  209. package/test/MarkdownConverter.test.ts +121 -105
  210. package/test/RoundTrip.test.ts +521 -0
  211. package/test/Schemas.test.ts +40 -40
  212. package/test/adfMetadata.test.ts +110 -0
  213. package/test/cleanMarkdown.test.ts +36 -0
  214. package/test/commandHarness.test.ts +79 -0
  215. package/test/commandHarness.ts +147 -0
  216. package/test/fetch.test.ts +61 -0
  217. package/test/frontmatter.test.ts +41 -0
  218. package/test/integration.test.ts +569 -156
  219. package/test/layers.test.ts +12 -0
  220. package/test/oauthServer.test.ts +4 -5
  221. package/test/pageInput.test.ts +83 -0
  222. package/test/tokenStorage.test.ts +17 -17
  223. package/dist/SchemaConverterError.d.ts +0 -108
  224. package/dist/SchemaConverterError.d.ts.map +0 -1
  225. package/dist/SchemaConverterError.js +0 -84
  226. package/dist/SchemaConverterError.js.map +0 -1
  227. package/dist/ast/BlockNode.d.ts +0 -453
  228. package/dist/ast/BlockNode.d.ts.map +0 -1
  229. package/dist/ast/BlockNode.js +0 -310
  230. package/dist/ast/BlockNode.js.map +0 -1
  231. package/dist/ast/Document.d.ts +0 -216
  232. package/dist/ast/Document.d.ts.map +0 -1
  233. package/dist/ast/Document.js +0 -69
  234. package/dist/ast/Document.js.map +0 -1
  235. package/dist/ast/InlineNode.d.ts +0 -477
  236. package/dist/ast/InlineNode.d.ts.map +0 -1
  237. package/dist/ast/InlineNode.js +0 -263
  238. package/dist/ast/InlineNode.js.map +0 -1
  239. package/dist/ast/MacroNode.d.ts +0 -267
  240. package/dist/ast/MacroNode.d.ts.map +0 -1
  241. package/dist/ast/MacroNode.js +0 -164
  242. package/dist/ast/MacroNode.js.map +0 -1
  243. package/dist/ast/index.d.ts +0 -10
  244. package/dist/ast/index.d.ts.map +0 -1
  245. package/dist/ast/index.js +0 -14
  246. package/dist/ast/index.js.map +0 -1
  247. package/dist/parsers/ConfluenceParser.d.ts +0 -26
  248. package/dist/parsers/ConfluenceParser.d.ts.map +0 -1
  249. package/dist/parsers/ConfluenceParser.js +0 -797
  250. package/dist/parsers/ConfluenceParser.js.map +0 -1
  251. package/dist/parsers/MarkdownParser.d.ts +0 -26
  252. package/dist/parsers/MarkdownParser.d.ts.map +0 -1
  253. package/dist/parsers/MarkdownParser.js +0 -982
  254. package/dist/parsers/MarkdownParser.js.map +0 -1
  255. package/dist/parsers/index.d.ts +0 -8
  256. package/dist/parsers/index.d.ts.map +0 -1
  257. package/dist/parsers/index.js +0 -8
  258. package/dist/parsers/index.js.map +0 -1
  259. package/dist/schemas/ConfluenceSchema.d.ts +0 -21
  260. package/dist/schemas/ConfluenceSchema.d.ts.map +0 -1
  261. package/dist/schemas/ConfluenceSchema.js +0 -38
  262. package/dist/schemas/ConfluenceSchema.js.map +0 -1
  263. package/dist/schemas/ConversionSchema.d.ts +0 -35
  264. package/dist/schemas/ConversionSchema.d.ts.map +0 -1
  265. package/dist/schemas/ConversionSchema.js +0 -208
  266. package/dist/schemas/ConversionSchema.js.map +0 -1
  267. package/dist/schemas/MarkdownSchema.d.ts +0 -21
  268. package/dist/schemas/MarkdownSchema.d.ts.map +0 -1
  269. package/dist/schemas/MarkdownSchema.js +0 -38
  270. package/dist/schemas/MarkdownSchema.js.map +0 -1
  271. package/dist/schemas/hast/HastFromHtml.d.ts +0 -27
  272. package/dist/schemas/hast/HastFromHtml.d.ts.map +0 -1
  273. package/dist/schemas/hast/HastFromHtml.js +0 -107
  274. package/dist/schemas/hast/HastFromHtml.js.map +0 -1
  275. package/dist/schemas/hast/HastSchema.d.ts +0 -195
  276. package/dist/schemas/hast/HastSchema.d.ts.map +0 -1
  277. package/dist/schemas/hast/HastSchema.js +0 -183
  278. package/dist/schemas/hast/HastSchema.js.map +0 -1
  279. package/dist/schemas/hast/index.d.ts +0 -9
  280. package/dist/schemas/hast/index.d.ts.map +0 -1
  281. package/dist/schemas/hast/index.js +0 -3
  282. package/dist/schemas/hast/index.js.map +0 -1
  283. package/dist/schemas/index.d.ts +0 -14
  284. package/dist/schemas/index.d.ts.map +0 -1
  285. package/dist/schemas/index.js +0 -16
  286. package/dist/schemas/index.js.map +0 -1
  287. package/dist/schemas/mdast/MdastFromMarkdown.d.ts +0 -30
  288. package/dist/schemas/mdast/MdastFromMarkdown.d.ts.map +0 -1
  289. package/dist/schemas/mdast/MdastFromMarkdown.js +0 -79
  290. package/dist/schemas/mdast/MdastFromMarkdown.js.map +0 -1
  291. package/dist/schemas/mdast/MdastSchema.d.ts +0 -385
  292. package/dist/schemas/mdast/MdastSchema.d.ts.map +0 -1
  293. package/dist/schemas/mdast/MdastSchema.js +0 -266
  294. package/dist/schemas/mdast/MdastSchema.js.map +0 -1
  295. package/dist/schemas/mdast/index.d.ts +0 -10
  296. package/dist/schemas/mdast/index.d.ts.map +0 -1
  297. package/dist/schemas/mdast/index.js +0 -4
  298. package/dist/schemas/mdast/index.js.map +0 -1
  299. package/dist/schemas/mdast/mdastToString.d.ts +0 -13
  300. package/dist/schemas/mdast/mdastToString.d.ts.map +0 -1
  301. package/dist/schemas/mdast/mdastToString.js +0 -85
  302. package/dist/schemas/mdast/mdastToString.js.map +0 -1
  303. package/dist/schemas/nodes/block/BlockSchema.d.ts +0 -43
  304. package/dist/schemas/nodes/block/BlockSchema.d.ts.map +0 -1
  305. package/dist/schemas/nodes/block/BlockSchema.js +0 -634
  306. package/dist/schemas/nodes/block/BlockSchema.js.map +0 -1
  307. package/dist/schemas/nodes/block/index.d.ts +0 -7
  308. package/dist/schemas/nodes/block/index.d.ts.map +0 -1
  309. package/dist/schemas/nodes/block/index.js +0 -7
  310. package/dist/schemas/nodes/block/index.js.map +0 -1
  311. package/dist/schemas/nodes/index.d.ts +0 -9
  312. package/dist/schemas/nodes/index.d.ts.map +0 -1
  313. package/dist/schemas/nodes/index.js +0 -12
  314. package/dist/schemas/nodes/index.js.map +0 -1
  315. package/dist/schemas/nodes/inline/InlineSchema.d.ts +0 -48
  316. package/dist/schemas/nodes/inline/InlineSchema.d.ts.map +0 -1
  317. package/dist/schemas/nodes/inline/InlineSchema.js +0 -436
  318. package/dist/schemas/nodes/inline/InlineSchema.js.map +0 -1
  319. package/dist/schemas/nodes/inline/index.d.ts +0 -7
  320. package/dist/schemas/nodes/inline/index.d.ts.map +0 -1
  321. package/dist/schemas/nodes/inline/index.js +0 -7
  322. package/dist/schemas/nodes/inline/index.js.map +0 -1
  323. package/dist/schemas/nodes/macro/MacroSchema.d.ts +0 -27
  324. package/dist/schemas/nodes/macro/MacroSchema.d.ts.map +0 -1
  325. package/dist/schemas/nodes/macro/MacroSchema.js +0 -162
  326. package/dist/schemas/nodes/macro/MacroSchema.js.map +0 -1
  327. package/dist/schemas/nodes/macro/index.d.ts +0 -7
  328. package/dist/schemas/nodes/macro/index.d.ts.map +0 -1
  329. package/dist/schemas/nodes/macro/index.js +0 -7
  330. package/dist/schemas/nodes/macro/index.js.map +0 -1
  331. package/dist/schemas/preprocessing/ConfluencePreprocessor.d.ts +0 -24
  332. package/dist/schemas/preprocessing/ConfluencePreprocessor.d.ts.map +0 -1
  333. package/dist/schemas/preprocessing/ConfluencePreprocessor.js +0 -359
  334. package/dist/schemas/preprocessing/ConfluencePreprocessor.js.map +0 -1
  335. package/dist/schemas/preprocessing/index.d.ts +0 -8
  336. package/dist/schemas/preprocessing/index.d.ts.map +0 -1
  337. package/dist/schemas/preprocessing/index.js +0 -2
  338. package/dist/schemas/preprocessing/index.js.map +0 -1
  339. package/dist/serializers/ConfluenceSerializer.d.ts +0 -30
  340. package/dist/serializers/ConfluenceSerializer.d.ts.map +0 -1
  341. package/dist/serializers/ConfluenceSerializer.js +0 -560
  342. package/dist/serializers/ConfluenceSerializer.js.map +0 -1
  343. package/dist/serializers/MarkdownSerializer.d.ts +0 -34
  344. package/dist/serializers/MarkdownSerializer.d.ts.map +0 -1
  345. package/dist/serializers/MarkdownSerializer.js +0 -395
  346. package/dist/serializers/MarkdownSerializer.js.map +0 -1
  347. package/dist/serializers/index.d.ts +0 -8
  348. package/dist/serializers/index.d.ts.map +0 -1
  349. package/dist/serializers/index.js +0 -8
  350. package/dist/serializers/index.js.map +0 -1
  351. package/src/SchemaConverterError.ts +0 -108
  352. package/src/ast/BlockNode.ts +0 -469
  353. package/src/ast/Document.ts +0 -90
  354. package/src/ast/InlineNode.ts +0 -323
  355. package/src/ast/MacroNode.ts +0 -245
  356. package/src/ast/index.ts +0 -83
  357. package/src/parsers/ConfluenceParser.ts +0 -956
  358. package/src/parsers/MarkdownParser.ts +0 -1338
  359. package/src/parsers/index.ts +0 -8
  360. package/src/schemas/ConfluenceSchema.ts +0 -56
  361. package/src/schemas/ConversionSchema.ts +0 -318
  362. package/src/schemas/MarkdownSchema.ts +0 -56
  363. package/src/schemas/hast/HastFromHtml.ts +0 -153
  364. package/src/schemas/hast/HastSchema.ts +0 -274
  365. package/src/schemas/hast/index.ts +0 -35
  366. package/src/schemas/index.ts +0 -20
  367. package/src/schemas/mdast/MdastFromMarkdown.ts +0 -118
  368. package/src/schemas/mdast/MdastSchema.ts +0 -566
  369. package/src/schemas/mdast/index.ts +0 -59
  370. package/src/schemas/mdast/mdastToString.ts +0 -102
  371. package/src/schemas/nodes/block/BlockSchema.ts +0 -773
  372. package/src/schemas/nodes/block/index.ts +0 -13
  373. package/src/schemas/nodes/index.ts +0 -20
  374. package/src/schemas/nodes/inline/InlineSchema.ts +0 -523
  375. package/src/schemas/nodes/inline/index.ts +0 -14
  376. package/src/schemas/nodes/macro/MacroSchema.ts +0 -226
  377. package/src/schemas/nodes/macro/index.ts +0 -6
  378. package/src/schemas/preprocessing/ConfluencePreprocessor.ts +0 -455
  379. package/src/schemas/preprocessing/index.ts +0 -8
  380. package/src/serializers/ConfluenceSerializer.ts +0 -737
  381. package/src/serializers/MarkdownSerializer.ts +0 -543
  382. package/src/serializers/index.ts +0 -8
  383. package/test/ast/BlockNode.test.ts +0 -265
  384. package/test/ast/Document.test.ts +0 -126
  385. package/test/ast/InlineNode.test.ts +0 -161
  386. package/test/fixtures/integration-test.html.fixture +0 -103
  387. package/test/fixtures/integration-test.md.expected +0 -257
  388. package/test/parsers/ConfluenceParser.test.ts +0 -452
  389. package/test/schemas/ConfluencePreprocessor.test.ts +0 -180
  390. package/test/schemas/ConversionSchema.test.ts +0 -159
  391. package/test/schemas/HastSchema.test.ts +0 -138
  392. package/test/schemas/MdastSchema.test.ts +0 -145
  393. package/test/schemas/nodes/block/BlockSchema.test.ts +0 -173
  394. package/test/schemas/nodes/inline/InlineSchema.test.ts +0 -198
  395. package/test/schemas/nodes/macro/MacroSchema.test.ts +0 -142
@@ -1,737 +0,0 @@
1
- /**
2
- * Serializer for AST to Confluence storage format (HTML).
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 Confluence storage format HTML.
14
- *
15
- * @example
16
- * ```typescript
17
- * import { serializeToConfluence } from "@knpkv/confluence-to-markdown/serializers/ConfluenceSerializer"
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 html = yield* serializeToConfluence(doc)
26
- * console.log(html) // <h1>Title</h1>
27
- * })
28
- * ```
29
- *
30
- * @category Serializers
31
- */
32
- export const serializeToConfluence = (doc: Document): Effect.Effect<string, SerializeError> =>
33
- Effect.gen(function*() {
34
- // 1-to-1 roundtrip: if rawConfluence is available, return it as-is
35
- if (doc.rawConfluence !== undefined) {
36
- return doc.rawConfluence
37
- }
38
-
39
- const parts: Array<string> = []
40
- for (const node of doc.children) {
41
- const serialized = yield* serializeDocumentNode(node)
42
- parts.push(serialized)
43
- }
44
- const raw = parts.join("\n")
45
- // Post-process to reconstruct layouts from markers
46
- return reconstructLayouts(raw)
47
- })
48
-
49
- /**
50
- * Reconstruct layouts from marker comments.
51
- *
52
- * Markers:
53
- * - <!--cf:layout-start-->
54
- * - <!--cf:section:index;type;breakoutMode;breakoutWidth;cellCount-->
55
- * - <!--cf:cell:sectionIndex;cellIndex-->
56
- * - <!--cf:section-end:index-->
57
- * - <!--cf:layout-end-->
58
- */
59
- const reconstructLayouts = (html: string): string => {
60
- // Check if there are any layout markers
61
- if (!html.includes("<!--cf:layout-start-->")) {
62
- return html
63
- }
64
-
65
- let result = html
66
-
67
- // Process each layout block
68
- const layoutRegex = /<!--cf:layout-start-->([\s\S]*?)<!--cf:layout-end-->/g
69
- result = result.replace(layoutRegex, (_, layoutContent: string) => {
70
- // Parse sections from the content
71
- const sections: Array<{
72
- type: string
73
- breakoutMode: string
74
- breakoutWidth: string
75
- cells: Array<string>
76
- }> = []
77
-
78
- // Find all section markers
79
- const sectionRegex = /<!--cf:section:(\d+);([^;]*);([^;]*);([^;]*);(\d+)-->/g
80
- let sectionMatch
81
- const sectionMeta: Array<
82
- { index: number; type: string; breakoutMode: string; breakoutWidth: string; cellCount: number }
83
- > = []
84
-
85
- while ((sectionMatch = sectionRegex.exec(layoutContent)) !== null) {
86
- sectionMeta.push({
87
- index: parseInt(sectionMatch[1] ?? "0"),
88
- type: decodeURIComponent(sectionMatch[2] ?? "fixed-width"),
89
- breakoutMode: decodeURIComponent(sectionMatch[3] ?? ""),
90
- breakoutWidth: decodeURIComponent(sectionMatch[4] ?? ""),
91
- cellCount: parseInt(sectionMatch[5] ?? "0")
92
- })
93
- }
94
-
95
- // For each section, extract cell content
96
- for (const meta of sectionMeta) {
97
- const cells: Array<string> = []
98
-
99
- for (let cellIndex = 0; cellIndex < meta.cellCount; cellIndex++) {
100
- const cellStartMarker = `<!--cf:cell:${meta.index};${cellIndex}-->`
101
- const nextCellMarker = `<!--cf:cell:${meta.index};${cellIndex + 1}-->`
102
- const sectionEndMarker = `<!--cf:section-end:${meta.index}-->`
103
- const nextSectionMarker = `<!--cf:section:${meta.index + 1};`
104
-
105
- const cellStart = layoutContent.indexOf(cellStartMarker)
106
- if (cellStart === -1) continue
107
-
108
- const contentStart = cellStart + cellStartMarker.length
109
-
110
- // Find where this cell ends - either next cell, section end, or next section
111
- let cellEnd = layoutContent.length
112
- const nextCell = layoutContent.indexOf(nextCellMarker, contentStart)
113
- const secEnd = layoutContent.indexOf(sectionEndMarker, contentStart)
114
- const nextSec = layoutContent.indexOf(nextSectionMarker, contentStart)
115
-
116
- if (nextCell !== -1 && nextCell < cellEnd) cellEnd = nextCell
117
- if (secEnd !== -1 && secEnd < cellEnd) cellEnd = secEnd
118
- if (nextSec !== -1 && nextSec < cellEnd) cellEnd = nextSec
119
-
120
- const cellContent = layoutContent.slice(contentStart, cellEnd).trim()
121
- cells.push(cellContent)
122
- }
123
-
124
- sections.push({
125
- type: meta.type,
126
- breakoutMode: meta.breakoutMode,
127
- breakoutWidth: meta.breakoutWidth,
128
- cells
129
- })
130
- }
131
-
132
- // Build the layout HTML
133
- const sectionHtml = sections.map((section) => {
134
- const typeAttr = ` ac:type="${escapeHtml(section.type)}"`
135
- const breakoutModeAttr = section.breakoutMode ? ` ac:breakout-mode="${escapeHtml(section.breakoutMode)}"` : ""
136
- const breakoutWidthAttr = section.breakoutWidth ? ` ac:breakout-width="${escapeHtml(section.breakoutWidth)}"` : ""
137
- const cellsHtml = section.cells.map((c) => `<ac:layout-cell>${c}</ac:layout-cell>`).join("")
138
- return `<ac:layout-section${typeAttr}${breakoutModeAttr}${breakoutWidthAttr}>${cellsHtml}</ac:layout-section>`
139
- }).join("")
140
-
141
- return `<ac:layout>${sectionHtml}</ac:layout>`
142
- })
143
-
144
- return result
145
- }
146
-
147
- /**
148
- * Serialize a document node to Confluence HTML.
149
- */
150
- const serializeDocumentNode = (node: DocumentNode): Effect.Effect<string, SerializeError> =>
151
- Effect.gen(function*() {
152
- switch (node._tag) {
153
- // Block nodes
154
- case "Heading":
155
- return yield* serializeHeading({ level: node.level, children: node.children })
156
- case "Paragraph":
157
- return yield* serializeParagraph({
158
- children: node.children,
159
- alignment: node.alignment,
160
- indent: node.indent
161
- })
162
- case "CodeBlock":
163
- return serializeCodeBlock({ code: node.code, language: node.language })
164
- case "ThematicBreak":
165
- return "<hr/>"
166
- case "Image":
167
- return serializeImage({
168
- src: node.src,
169
- attachment: node.attachment,
170
- alt: node.alt,
171
- title: node.title,
172
- align: node.align,
173
- width: node.width
174
- })
175
- case "Table":
176
- return yield* serializeTable({ header: node.header, rows: node.rows })
177
- case "List":
178
- return yield* serializeList({
179
- ordered: node.ordered,
180
- start: node.start,
181
- children: node.children as unknown as Array<ListItemType>
182
- })
183
- case "BlockQuote":
184
- return yield* serializeBlockQuote({ children: node.children as unknown as Array<SimpleBlock> })
185
- case "UnsupportedBlock": {
186
- const raw = node.rawHtml || node.rawMarkdown || ""
187
- // Check for comment-encoded decision list
188
- const decisionMatch = raw.match(/<!--cf:decision:(.*)-->/)
189
- if (decisionMatch) {
190
- const itemsStr = decisionMatch[1] ?? ""
191
- const items = itemsStr.split("|").map((item) => {
192
- const parts = item.split(";")
193
- return {
194
- localId: decodeURIComponent(parts[0] ?? ""),
195
- state: decodeURIComponent(parts[1] ?? ""),
196
- content: decodeURIComponent(parts[2] ?? "")
197
- }
198
- })
199
- const decisionItems = items.map((item) =>
200
- `<ac:adf-node type="decision-item"><ac:adf-attribute key="local-id">${
201
- escapeHtml(item.localId)
202
- }</ac:adf-attribute><ac:adf-attribute key="state">${
203
- escapeHtml(item.state)
204
- }</ac:adf-attribute><ac:adf-content>${escapeHtml(item.content)}</ac:adf-content></ac:adf-node>`
205
- ).join("")
206
- const fallbackItems = items.map((item) => `<li>${escapeHtml(item.content)}</li>`).join("")
207
- return `<ac:adf-extension><ac:adf-node type="decision-list">${decisionItems}</ac:adf-node><ac:adf-fallback><ul class="decision-list">${fallbackItems}</ul></ac:adf-fallback></ac:adf-extension>`
208
- }
209
- // Layout markers are passed through - reconstructLayouts will process them
210
- return raw
211
- }
212
-
213
- // Macro nodes - serialize to Confluence macros
214
- case "InfoPanel":
215
- return yield* serializeInfoPanel({
216
- panelType: node.panelType,
217
- title: node.title,
218
- children: node.children as unknown as Array<SimpleBlock>
219
- })
220
- case "ExpandMacro":
221
- return yield* serializeExpandMacro({
222
- title: node.title,
223
- children: node.children as unknown as Array<SimpleBlock>
224
- })
225
- case "TocMacro":
226
- return serializeTocMacro({ minLevel: node.minLevel, maxLevel: node.maxLevel })
227
- case "CodeMacro":
228
- return serializeCodeMacro({
229
- language: node.language,
230
- title: node.title,
231
- code: node.code,
232
- lineNumbers: node.lineNumbers,
233
- collapse: node.collapse,
234
- firstLine: node.firstLine
235
- })
236
- case "StatusMacro":
237
- return serializeStatusMacro({ text: node.text, color: node.color })
238
- case "TaskList":
239
- return yield* serializeTaskList(node.children)
240
-
241
- default:
242
- return ""
243
- }
244
- })
245
-
246
- /**
247
- * Serialize heading.
248
- */
249
- const serializeHeading = (
250
- node: { level: 1 | 2 | 3 | 4 | 5 | 6; children: ReadonlyArray<InlineNode> }
251
- ): Effect.Effect<string, SerializeError> =>
252
- Effect.gen(function*() {
253
- const content = yield* serializeInlineNodes(node.children)
254
- return `<h${node.level}>${content}</h${node.level}>`
255
- })
256
-
257
- /**
258
- * Serialize paragraph (with optional alignment and indent).
259
- */
260
- const serializeParagraph = (
261
- node: {
262
- children: ReadonlyArray<InlineNode>
263
- alignment?: "left" | "center" | "right" | undefined
264
- indent?: number | undefined
265
- }
266
- ): Effect.Effect<string, SerializeError> =>
267
- Effect.gen(function*() {
268
- const content = yield* serializeInlineNodes(node.children)
269
- const styles: Array<string> = []
270
- if (node.alignment) {
271
- styles.push(`text-align: ${node.alignment};`)
272
- }
273
- if (node.indent) {
274
- styles.push(`margin-left: ${node.indent}px;`)
275
- }
276
- const styleAttr = styles.length > 0 ? ` style="${styles.join(" ")}"` : ""
277
- return `<p${styleAttr}>${content}</p>`
278
- })
279
-
280
- /**
281
- * Serialize code block as Confluence code macro.
282
- */
283
- const serializeCodeBlock = (node: { code: string; language?: string | undefined }): string => {
284
- const lang = node.language ? `<ac:parameter ac:name="language">${escapeHtml(node.language)}</ac:parameter>` : ""
285
- return `<ac:structured-macro ac:name="code">${lang}<ac:plain-text-body><![CDATA[${node.code}]]></ac:plain-text-body></ac:structured-macro>`
286
- }
287
-
288
- /**
289
- * Serialize image (supports both URL and Confluence attachments).
290
- */
291
- const serializeImage = (node: {
292
- src?: string | undefined
293
- attachment?: { filename: string; version?: number | undefined } | undefined
294
- alt?: string | undefined
295
- title?: string | undefined
296
- align?: string | undefined
297
- width?: number | undefined
298
- }): string => {
299
- // Confluence attachment
300
- if (node.attachment) {
301
- const alignAttr = node.align ? ` ac:align="${node.align}"` : ""
302
- const widthAttr = node.width ? ` ac:width="${node.width}"` : ""
303
- const altAttr = node.alt ? ` ac:alt="${escapeHtml(node.alt)}"` : ""
304
- const versionAttr = node.attachment.version ? ` ri:version-at-save="${node.attachment.version}"` : ""
305
- return `<ac:image${alignAttr}${widthAttr}${altAttr}><ri:attachment ri:filename="${
306
- escapeHtml(node.attachment.filename)
307
- }"${versionAttr}/></ac:image>`
308
- }
309
-
310
- // URL-based image
311
- const src = node.src ?? ""
312
- const alt = node.alt ? ` alt="${escapeHtml(node.alt)}"` : ""
313
- const title = node.title ? ` title="${escapeHtml(node.title)}"` : ""
314
- return `<img src="${escapeHtml(src)}"${alt}${title}/>`
315
- }
316
-
317
- /**
318
- * Serialize table.
319
- */
320
- const serializeTable = (
321
- node: {
322
- header?:
323
- | { cells: ReadonlyArray<{ isHeader?: boolean | undefined; children: ReadonlyArray<InlineNode> }> }
324
- | undefined
325
- rows: ReadonlyArray<
326
- { cells: ReadonlyArray<{ isHeader?: boolean | undefined; children: ReadonlyArray<InlineNode> }> }
327
- >
328
- }
329
- ): Effect.Effect<string, SerializeError> =>
330
- Effect.gen(function*() {
331
- const parts: Array<string> = ["<table>"]
332
-
333
- // Header
334
- if (node.header) {
335
- parts.push("<thead><tr>")
336
- for (const cell of node.header.cells) {
337
- const content = yield* serializeInlineNodes(cell.children)
338
- parts.push(`<th>${content}</th>`)
339
- }
340
- parts.push("</tr></thead>")
341
- }
342
-
343
- // Body
344
- if (node.rows.length > 0) {
345
- parts.push("<tbody>")
346
- for (const row of node.rows) {
347
- parts.push("<tr>")
348
- for (const cell of row.cells) {
349
- const tag = cell.isHeader ? "th" : "td"
350
- const content = yield* serializeInlineNodes(cell.children)
351
- parts.push(`<${tag}>${content}</${tag}>`)
352
- }
353
- parts.push("</tr>")
354
- }
355
- parts.push("</tbody>")
356
- }
357
-
358
- parts.push("</table>")
359
- return parts.join("")
360
- })
361
-
362
- // Simple block type for list items (allows nested Lists for sub-bullets).
363
- type SimpleBlock =
364
- | Heading
365
- | Paragraph
366
- | CodeBlock
367
- | ThematicBreak
368
- | Image
369
- | Table
370
- | UnsupportedBlock
371
- | NestedList
372
-
373
- // Structural shape of a nested list inside a list item.
374
- type NestedList = {
375
- readonly _tag: "List"
376
- readonly ordered: boolean
377
- readonly start?: number | undefined
378
- readonly children: ReadonlyArray<ListItemType>
379
- }
380
-
381
- // List item type
382
- type ListItemType = {
383
- readonly _tag: "ListItem"
384
- readonly checked?: boolean | undefined
385
- readonly children: ReadonlyArray<SimpleBlock>
386
- }
387
-
388
- /**
389
- * Serialize list.
390
- */
391
- const serializeList = (
392
- node: { ordered: boolean; start?: number | undefined; children: ReadonlyArray<ListItemType> }
393
- ): Effect.Effect<string, SerializeError> =>
394
- Effect.gen(function*() {
395
- const tag = node.ordered ? "ol" : "ul"
396
- const startAttr = node.ordered && node.start && node.start !== 1 ? ` start="${node.start}"` : ""
397
- const parts: Array<string> = [`<${tag}${startAttr}>`]
398
-
399
- for (const item of node.children) {
400
- parts.push("<li>")
401
- if (item.checked !== undefined) {
402
- const checked = item.checked ? " checked" : ""
403
- parts.push(`<input type="checkbox"${checked}/>`)
404
- }
405
- for (const child of item.children) {
406
- parts.push(yield* serializeSimpleBlock(child))
407
- }
408
- parts.push("</li>")
409
- }
410
-
411
- parts.push(`</${tag}>`)
412
- return parts.join("")
413
- })
414
-
415
- /**
416
- * Serialize simple block.
417
- */
418
- const serializeSimpleBlock = (node: SimpleBlock): Effect.Effect<string, SerializeError> =>
419
- Effect.gen(function*() {
420
- switch (node._tag) {
421
- case "Heading":
422
- return yield* serializeHeading(
423
- node as unknown as { level: 1 | 2 | 3 | 4 | 5 | 6; children: ReadonlyArray<InlineNode> }
424
- )
425
- case "Paragraph":
426
- return yield* serializeParagraph(node as unknown as { children: ReadonlyArray<InlineNode> })
427
- case "CodeBlock":
428
- return serializeCodeBlock(node as unknown as { code: string; language?: string | undefined })
429
- case "ThematicBreak":
430
- return "<hr/>"
431
- case "Image":
432
- return serializeImage(node as unknown as { src: string; alt?: string | undefined; title?: string | undefined })
433
- case "Table":
434
- return yield* serializeTable(
435
- node as unknown as {
436
- header?:
437
- | { cells: ReadonlyArray<{ isHeader?: boolean | undefined; children: ReadonlyArray<InlineNode> }> }
438
- | undefined
439
- rows: ReadonlyArray<
440
- { cells: ReadonlyArray<{ isHeader?: boolean | undefined; children: ReadonlyArray<InlineNode> }> }
441
- >
442
- }
443
- )
444
- case "UnsupportedBlock": {
445
- const unsupported = node as unknown as { rawHtml?: string; rawMarkdown?: string }
446
- return unsupported.rawHtml || unsupported.rawMarkdown || ""
447
- }
448
- case "List":
449
- return yield* serializeList(node)
450
- default:
451
- return ""
452
- }
453
- })
454
-
455
- /**
456
- * Serialize block quote.
457
- */
458
- const serializeBlockQuote = (
459
- node: { children: ReadonlyArray<SimpleBlock> }
460
- ): Effect.Effect<string, SerializeError> =>
461
- Effect.gen(function*() {
462
- const parts: Array<string> = ["<blockquote>"]
463
- for (const child of node.children) {
464
- parts.push(yield* serializeSimpleBlock(child as SimpleBlock))
465
- }
466
- parts.push("</blockquote>")
467
- return parts.join("")
468
- })
469
-
470
- /**
471
- * Serialize info panel as Confluence macro.
472
- */
473
- const serializeInfoPanel = (
474
- node: { panelType: string; title?: string | undefined; children: ReadonlyArray<SimpleBlock> }
475
- ): Effect.Effect<string, SerializeError> =>
476
- Effect.gen(function*() {
477
- const titleParam = node.title
478
- ? `<ac:parameter ac:name="title">${escapeHtml(node.title)}</ac:parameter>`
479
- : ""
480
-
481
- const parts: Array<string> = [
482
- `<ac:structured-macro ac:name="${node.panelType}">`,
483
- titleParam,
484
- "<ac:rich-text-body>"
485
- ]
486
-
487
- for (const child of node.children) {
488
- parts.push(yield* serializeSimpleBlock(child as SimpleBlock))
489
- }
490
-
491
- parts.push("</ac:rich-text-body>")
492
- parts.push("</ac:structured-macro>")
493
- return parts.join("")
494
- })
495
-
496
- /**
497
- * Serialize expand macro as Confluence macro.
498
- */
499
- const serializeExpandMacro = (
500
- node: { title?: string | undefined; children: ReadonlyArray<SimpleBlock> }
501
- ): Effect.Effect<string, SerializeError> =>
502
- Effect.gen(function*() {
503
- const titleParam = node.title
504
- ? `<ac:parameter ac:name="title">${escapeHtml(node.title)}</ac:parameter>`
505
- : ""
506
-
507
- const parts: Array<string> = [
508
- `<ac:structured-macro ac:name="expand">`,
509
- titleParam,
510
- "<ac:rich-text-body>"
511
- ]
512
-
513
- for (const child of node.children) {
514
- parts.push(yield* serializeSimpleBlock(child as SimpleBlock))
515
- }
516
-
517
- parts.push("</ac:rich-text-body>")
518
- parts.push("</ac:structured-macro>")
519
- return parts.join("")
520
- })
521
-
522
- /**
523
- * Serialize TOC macro.
524
- */
525
- const serializeTocMacro = (node: { minLevel?: number | undefined; maxLevel?: number | undefined }): string => {
526
- const params: Array<string> = []
527
- if (node.minLevel) {
528
- params.push(`<ac:parameter ac:name="minLevel">${node.minLevel}</ac:parameter>`)
529
- }
530
- if (node.maxLevel) {
531
- params.push(`<ac:parameter ac:name="maxLevel">${node.maxLevel}</ac:parameter>`)
532
- }
533
- return `<ac:structured-macro ac:name="toc">${params.join("")}</ac:structured-macro>`
534
- }
535
-
536
- /**
537
- * Serialize code macro with full options.
538
- */
539
- const serializeCodeMacro = (
540
- node: {
541
- language?: string | undefined
542
- title?: string | undefined
543
- code: string
544
- lineNumbers?: boolean | undefined
545
- collapse?: boolean | undefined
546
- firstLine?: number | undefined
547
- }
548
- ): string => {
549
- const params: Array<string> = []
550
- if (node.language) {
551
- params.push(`<ac:parameter ac:name="language">${escapeHtml(node.language)}</ac:parameter>`)
552
- }
553
- if (node.title) {
554
- params.push(`<ac:parameter ac:name="title">${escapeHtml(node.title)}</ac:parameter>`)
555
- }
556
- if (node.lineNumbers) {
557
- params.push(`<ac:parameter ac:name="linenumbers">true</ac:parameter>`)
558
- }
559
- if (node.collapse) {
560
- params.push(`<ac:parameter ac:name="collapse">true</ac:parameter>`)
561
- }
562
- if (node.firstLine) {
563
- params.push(`<ac:parameter ac:name="firstline">${node.firstLine}</ac:parameter>`)
564
- }
565
-
566
- return `<ac:structured-macro ac:name="code">${
567
- params.join("")
568
- }<ac:plain-text-body><![CDATA[${node.code}]]></ac:plain-text-body></ac:structured-macro>`
569
- }
570
-
571
- /**
572
- * Serialize status macro.
573
- */
574
- const serializeStatusMacro = (node: { text: string; color: string }): string => {
575
- return `<ac:structured-macro ac:name="status"><ac:parameter ac:name="colour">${
576
- escapeHtml(node.color)
577
- }</ac:parameter><ac:parameter ac:name="title">${escapeHtml(node.text)}</ac:parameter></ac:structured-macro>`
578
- }
579
-
580
- /**
581
- * Serialize task list to Confluence storage format.
582
- */
583
- const serializeTaskList = (
584
- children: ReadonlyArray<{
585
- _tag: "TaskItem"
586
- id: string
587
- uuid: string
588
- status: "incomplete" | "complete"
589
- body: ReadonlyArray<InlineNode>
590
- }>
591
- ): Effect.Effect<string, SerializeError> =>
592
- Effect.gen(function*() {
593
- const parts: Array<string> = [`<ac:task-list>`]
594
-
595
- for (const item of children) {
596
- const body = yield* serializeInlineNodes(item.body)
597
- parts.push(
598
- `<ac:task>` +
599
- `<ac:task-id>${item.id}</ac:task-id>` +
600
- `<ac:task-uuid>${item.uuid}</ac:task-uuid>` +
601
- `<ac:task-status>${item.status}</ac:task-status>` +
602
- `<ac:task-body><span class="placeholder-inline-tasks">${body}</span></ac:task-body>` +
603
- `</ac:task>`
604
- )
605
- }
606
-
607
- parts.push(`</ac:task-list>`)
608
- return parts.join("\n")
609
- })
610
-
611
- /**
612
- * Serialize inline nodes to HTML.
613
- */
614
- const serializeInlineNodes = (
615
- nodes: ReadonlyArray<InlineNode>
616
- ): Effect.Effect<string, SerializeError> =>
617
- Effect.gen(function*() {
618
- const parts: Array<string> = []
619
- for (const node of nodes) {
620
- parts.push(yield* serializeInlineNode(node))
621
- }
622
- return parts.join("")
623
- })
624
-
625
- /**
626
- * Serialize inline node to HTML.
627
- */
628
- const serializeInlineNode = (node: InlineNode): Effect.Effect<string, SerializeError> =>
629
- Effect.gen(function*() {
630
- switch (node._tag) {
631
- case "Text":
632
- return escapeHtml(node.value)
633
- case "Strong": {
634
- const content = yield* serializeInlineNodes(node.children)
635
- return `<strong>${content}</strong>`
636
- }
637
- case "Emphasis": {
638
- const content = yield* serializeInlineNodes(node.children)
639
- return `<em>${content}</em>`
640
- }
641
- case "Underline": {
642
- const content = yield* serializeInlineNodes(node.children)
643
- return `<u>${content}</u>`
644
- }
645
- case "Strikethrough": {
646
- const content = yield* serializeInlineNodes(node.children)
647
- return `<del>${content}</del>`
648
- }
649
- case "Subscript": {
650
- const content = yield* serializeInlineNodes(node.children)
651
- return `<sub>${content}</sub>`
652
- }
653
- case "Superscript": {
654
- const content = yield* serializeInlineNodes(node.children)
655
- return `<sup>${content}</sup>`
656
- }
657
- case "InlineCode":
658
- return `<code>${escapeHtml(node.value)}</code>`
659
- case "Link": {
660
- // Round-trip view-file macro: a Link with href="attachment:FILENAME"
661
- // came from a Confluence ac:structured-macro name="view-file".
662
- const attachmentMatch = node.href.match(/^attachment:(.+)$/)
663
- if (attachmentMatch) {
664
- const filename = attachmentMatch[1] ?? ""
665
- return `<ac:structured-macro ac:name="view-file"><ac:parameter ac:name="name"><ri:attachment ri:filename="${
666
- escapeHtml(filename)
667
- }"/></ac:parameter></ac:structured-macro>`
668
- }
669
- const content = yield* serializeInlineNodes(node.children)
670
- const title = node.title ? ` title="${escapeHtml(node.title)}"` : ""
671
- return `<a href="${escapeHtml(node.href)}"${title}>${content}</a>`
672
- }
673
- case "LineBreak":
674
- return "<br/>"
675
- case "Emoticon":
676
- return `<ac:emoticon ac:emoji-shortname="${escapeHtml(node.shortname)}" ac:emoji-id="${
677
- escapeHtml(node.emojiId)
678
- }" ac:emoji-fallback="${escapeHtml(node.fallback)}"/>`
679
- case "UserMention":
680
- return `<ac:link><ri:user ri:account-id="${escapeHtml(node.accountId)}"/></ac:link>`
681
- case "DateTime":
682
- return `<time datetime="${escapeHtml(node.datetime)}"/>`
683
- case "ColoredText": {
684
- const content = yield* serializeInlineNodes(node.children)
685
- return `<span style="color: ${escapeHtml(node.color)};">${content}</span>`
686
- }
687
- case "Highlight": {
688
- const content = yield* serializeInlineNodes(node.children)
689
- return `<span style="background-color: ${escapeHtml(node.backgroundColor)};">${content}</span>`
690
- }
691
- case "UnsupportedInline": {
692
- // Check for comment-encoded TOC and convert back to Confluence macro
693
- const tocMatch = node.raw.match(/<!--cf:toc:([^;]*);([^;]*)-->/)
694
- if (tocMatch) {
695
- const minLevel = tocMatch[1]
696
- const maxLevel = tocMatch[2]
697
- let params = ""
698
- if (minLevel) params += `<ac:parameter ac:name="minLevel">${minLevel}</ac:parameter>`
699
- if (maxLevel) params += `<ac:parameter ac:name="maxLevel">${maxLevel}</ac:parameter>`
700
- return `<ac:structured-macro ac:name="toc">${params}</ac:structured-macro>`
701
- }
702
- // Check for comment-encoded Status macro
703
- const statusMatch = node.raw.match(/<!--cf:status:([^;]*);([^;]*)-->/)
704
- if (statusMatch) {
705
- const title = decodeURIComponent(statusMatch[1] ?? "")
706
- const color = decodeURIComponent(statusMatch[2] ?? "")
707
- let params = ""
708
- if (title) params += `<ac:parameter ac:name="title">${escapeHtml(title)}</ac:parameter>`
709
- if (color) params += `<ac:parameter ac:name="colour">${escapeHtml(color)}</ac:parameter>`
710
- return `<ac:structured-macro ac:name="status">${params}</ac:structured-macro>`
711
- }
712
- // Check for comment-encoded Smart link (Jira, etc.)
713
- const smartLinkMatch = node.raw.match(/<!--cf:smartlink:([^;]*);([^;]*);(.*)-->/)
714
- if (smartLinkMatch) {
715
- const href = decodeURIComponent(smartLinkMatch[1] ?? "")
716
- const appearance = decodeURIComponent(smartLinkMatch[2] ?? "")
717
- const datasource = decodeURIComponent(smartLinkMatch[3] ?? "")
718
- return `<a href="${escapeHtml(href)}" data-card-appearance="${escapeHtml(appearance)}" data-datasource="${
719
- escapeHtml(datasource)
720
- }">${escapeHtml(href)}</a>`
721
- }
722
- return node.raw
723
- }
724
- default:
725
- return ""
726
- }
727
- })
728
-
729
- /**
730
- * Escape HTML special characters.
731
- */
732
- const escapeHtml = (str: string): string =>
733
- str
734
- .replace(/&/g, "&amp;")
735
- .replace(/</g, "&lt;")
736
- .replace(/>/g, "&gt;")
737
- .replace(/"/g, "&quot;")