@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,452 +0,0 @@
1
- import * as NodeFileSystem from "@effect/platform-node/NodeFileSystem"
2
- import * as NodePath from "@effect/platform-node/NodePath"
3
- import * as FileSystem from "@effect/platform/FileSystem"
4
- import * as Path from "@effect/platform/Path"
5
- import { describe, expect, it } from "@effect/vitest"
6
- import * as Effect from "effect/Effect"
7
- import * as Layer from "effect/Layer"
8
- import { parseConfluenceHtml } from "../../src/parsers/ConfluenceParser.js"
9
- import { parseMarkdown } from "../../src/parsers/MarkdownParser.js"
10
- import { serializeToConfluence } from "../../src/serializers/ConfluenceSerializer.js"
11
- import { serializeToMarkdown } from "../../src/serializers/MarkdownSerializer.js"
12
-
13
- const PlatformLive = Layer.mergeAll(NodeFileSystem.layer, NodePath.layer)
14
-
15
- const getFixturesDir = Effect.gen(function*() {
16
- const path = yield* Path.Path
17
- return path.join(import.meta.dirname, "../fixtures")
18
- })
19
-
20
- const readFixture = (filename: string) =>
21
- Effect.gen(function*() {
22
- const fs = yield* FileSystem.FileSystem
23
- const path = yield* Path.Path
24
- const fixturesDir = yield* getFixturesDir
25
- return yield* fs.readFileString(path.join(fixturesDir, filename))
26
- }).pipe(Effect.provide(PlatformLive))
27
-
28
- describe("ConfluenceParser", () => {
29
- describe("parseConfluenceHtml", () => {
30
- it.effect("parses basic headings", () =>
31
- Effect.gen(function*() {
32
- const html = "<h1>Title</h1><h2>Subtitle</h2>"
33
- const doc = yield* parseConfluenceHtml(html)
34
- expect(doc.children.length).toBe(2)
35
- expect(doc.children[0]?._tag).toBe("Heading")
36
- }))
37
-
38
- it.effect("parses paragraphs", () =>
39
- Effect.gen(function*() {
40
- const html = "<p>Hello world</p>"
41
- const doc = yield* parseConfluenceHtml(html)
42
- expect(doc.children.length).toBe(1)
43
- expect(doc.children[0]?._tag).toBe("Paragraph")
44
- }))
45
-
46
- it.effect("parses task lists", () =>
47
- Effect.gen(function*() {
48
- const html = `<ac:task-list ac:task-list-id="test">
49
- <ac:task>
50
- <ac:task-id>1</ac:task-id>
51
- <ac:task-uuid>uuid-1</ac:task-uuid>
52
- <ac:task-status>incomplete</ac:task-status>
53
- <ac:task-body><span>Task 1</span></ac:task-body>
54
- </ac:task>
55
- <ac:task>
56
- <ac:task-id>2</ac:task-id>
57
- <ac:task-uuid>uuid-2</ac:task-uuid>
58
- <ac:task-status>complete</ac:task-status>
59
- <ac:task-body><span>Task 2</span></ac:task-body>
60
- </ac:task>
61
- </ac:task-list>`
62
- const doc = yield* parseConfluenceHtml(html)
63
- expect(doc.children.length).toBe(1)
64
- const taskList = doc.children[0]
65
- expect(taskList?._tag).toBe("TaskList")
66
- if (taskList?._tag === "TaskList") {
67
- expect(taskList.children.length).toBe(2)
68
- expect(taskList.children[0]?.status).toBe("incomplete")
69
- expect(taskList.children[0]?.body[0]?._tag).toBe("Text")
70
- expect(taskList.children[1]?.status).toBe("complete")
71
- }
72
- }))
73
-
74
- it.effect("parses images with attachments", () =>
75
- Effect.gen(function*() {
76
- const html = `<ac:image ac:align="center" ac:width="250" ac:alt="logo">
77
- <ri:attachment ri:filename="logo.svg"/>
78
- </ac:image>`
79
- const doc = yield* parseConfluenceHtml(html)
80
- expect(doc.children.length).toBe(1)
81
- const image = doc.children[0]
82
- expect(image?._tag).toBe("Image")
83
- if (image?._tag === "Image") {
84
- expect(image.attachment?.filename).toBe("logo.svg")
85
- expect(image.align).toBe("center")
86
- expect(image.width).toBe(250)
87
- expect(image.alt).toBe("logo")
88
- }
89
- }))
90
-
91
- it.effect("parses emoticons", () =>
92
- Effect.gen(function*() {
93
- const html =
94
- `<p><ac:emoticon ac:emoji-shortname=":grinning:" ac:emoji-id="1f600" ac:emoji-fallback="smile"/></p>`
95
- const doc = yield* parseConfluenceHtml(html)
96
- expect(doc.children.length).toBe(1)
97
- const para = doc.children[0]
98
- expect(para?._tag).toBe("Paragraph")
99
- if (para?._tag === "Paragraph") {
100
- const emoticon = para.children[0]
101
- expect(emoticon?._tag).toBe("Emoticon")
102
- if (emoticon?._tag === "Emoticon") {
103
- expect(emoticon.shortname).toBe(":grinning:")
104
- expect(emoticon.emojiId).toBe("1f600")
105
- expect(emoticon.fallback).toBe("smile")
106
- }
107
- }
108
- }))
109
-
110
- it.effect("parses user mentions", () =>
111
- Effect.gen(function*() {
112
- const html = `<p><ac:link><ri:user ri:account-id="557058:abc123"/></ac:link></p>`
113
- const doc = yield* parseConfluenceHtml(html)
114
- expect(doc.children.length).toBe(1)
115
- const para = doc.children[0]
116
- if (para?._tag === "Paragraph") {
117
- const mention = para.children[0]
118
- expect(mention?._tag).toBe("UserMention")
119
- if (mention?._tag === "UserMention") {
120
- expect(mention.accountId).toBe("557058:abc123")
121
- }
122
- }
123
- }))
124
-
125
- it.effect("parses colored text", () =>
126
- Effect.gen(function*() {
127
- const html = `<p><span style="color: rgb(255,0,0);">Red text</span></p>`
128
- const doc = yield* parseConfluenceHtml(html)
129
- const para = doc.children[0]
130
- if (para?._tag === "Paragraph") {
131
- const colored = para.children[0]
132
- expect(colored?._tag).toBe("ColoredText")
133
- if (colored?._tag === "ColoredText") {
134
- expect(colored.color).toBe("rgb(255,0,0)")
135
- }
136
- }
137
- }))
138
-
139
- it.effect("parses highlighted text", () =>
140
- Effect.gen(function*() {
141
- const html = `<p><span style="background-color: rgb(255,255,0);">Highlighted</span></p>`
142
- const doc = yield* parseConfluenceHtml(html)
143
- const para = doc.children[0]
144
- if (para?._tag === "Paragraph") {
145
- const highlight = para.children[0]
146
- expect(highlight?._tag).toBe("Highlight")
147
- if (highlight?._tag === "Highlight") {
148
- expect(highlight.backgroundColor).toBe("rgb(255,255,0)")
149
- }
150
- }
151
- }))
152
-
153
- it.effect("parses paragraph with alignment", () =>
154
- Effect.gen(function*() {
155
- const html = `<p style="text-align: center;">Centered</p>`
156
- const doc = yield* parseConfluenceHtml(html)
157
- const para = doc.children[0]
158
- expect(para?._tag).toBe("Paragraph")
159
- if (para?._tag === "Paragraph") {
160
- expect(para.alignment).toBe("center")
161
- }
162
- }))
163
-
164
- it.effect("parses paragraph with indent", () =>
165
- Effect.gen(function*() {
166
- const html = `<p style="margin-left: 30px;">Indented</p>`
167
- const doc = yield* parseConfluenceHtml(html)
168
- const para = doc.children[0]
169
- expect(para?._tag === "Paragraph").toBe(true)
170
- if (para?._tag === "Paragraph") {
171
- expect(para.indent).toBe(30)
172
- }
173
- }))
174
-
175
- it.effect("parses underline, subscript, superscript", () =>
176
- Effect.gen(function*() {
177
- const html = `<p><u>Underline</u> <sub>Sub</sub> <sup>Sup</sup></p>`
178
- const doc = yield* parseConfluenceHtml(html)
179
- const para = doc.children[0]
180
- if (para?._tag === "Paragraph") {
181
- const [u, , sub, , sup] = para.children
182
- expect(u?._tag).toBe("Underline")
183
- expect(sub?._tag).toBe("Subscript")
184
- expect(sup?._tag).toBe("Superscript")
185
- }
186
- }))
187
-
188
- it.effect("parses tables with proper cell content", () =>
189
- Effect.gen(function*() {
190
- const html = `<table>
191
- <thead><tr><th><p>Header 1</p></th><th><p>Header 2</p></th></tr></thead>
192
- <tbody><tr><td><p>Cell 1</p></td><td><p>Cell 2</p></td></tr></tbody>
193
- </table>`
194
- const doc = yield* parseConfluenceHtml(html)
195
- const table = doc.children[0]
196
- expect(table?._tag).toBe("Table")
197
- if (table?._tag === "Table") {
198
- // Header should have 2 cells
199
- expect(table.header?.cells.length).toBe(2)
200
- // First header cell should have text "Header 1"
201
- const headerCell = table.header?.cells[0]
202
- if (headerCell) {
203
- expect(headerCell.children[0]?._tag).toBe("Text")
204
- if (headerCell.children[0]?._tag === "Text") {
205
- expect(headerCell.children[0].value).toBe("Header 1")
206
- }
207
- }
208
- // Body rows
209
- expect(table.rows.length).toBe(1)
210
- const bodyCell = table.rows[0]?.cells[0]
211
- if (bodyCell) {
212
- expect(bodyCell.children[0]?._tag).toBe("Text")
213
- if (bodyCell.children[0]?._tag === "Text") {
214
- expect(bodyCell.children[0].value).toBe("Cell 1")
215
- }
216
- }
217
- }
218
- }))
219
-
220
- it.effect("parses nested unordered list as nested List AST", () =>
221
- Effect.gen(function*() {
222
- const html = `<ul><li><p>Outer</p><ul><li><p>Inner A</p></li><li><p>Inner B</p></li></ul></li></ul>`
223
- const doc = yield* parseConfluenceHtml(html)
224
- const outerList = doc.children[0]
225
- expect(outerList?._tag).toBe("List")
226
- if (outerList?._tag === "List") {
227
- expect(outerList.children.length).toBe(1)
228
- const outerItem = outerList.children[0]
229
- expect(outerItem?.children.length).toBe(2)
230
- // First child is the "Outer" paragraph
231
- expect(outerItem?.children[0]?._tag).toBe("Paragraph")
232
- // Second child is the nested List (no longer UnsupportedBlock)
233
- const nested = outerItem?.children[1]
234
- expect(nested?._tag).toBe("List")
235
- if (nested?._tag === "List") {
236
- expect(nested.children.length).toBe(2)
237
- const firstInner = nested.children[0]
238
- expect(firstInner?.children[0]?._tag).toBe("Paragraph")
239
- }
240
- }
241
- }))
242
-
243
- it.effect("serializes nested list as indented markdown bullets", () =>
244
- Effect.gen(function*() {
245
- const html = `<ul><li><p>Outer</p><ul><li><p>Inner A</p></li><li><p>Inner B</p></li></ul></li></ul>`
246
- const doc = yield* parseConfluenceHtml(html)
247
- const md = yield* serializeToMarkdown(doc, { includeRawSource: false })
248
- expect(md).toContain("- Outer")
249
- expect(md).toContain(" - Inner A")
250
- expect(md).toContain(" - Inner B")
251
- expect(md).not.toContain("<ul")
252
- expect(md).not.toContain("local-id")
253
- }))
254
-
255
- it.effect("unwraps table cell with leading empty <p> placeholder", () =>
256
- Effect.gen(function*() {
257
- // Confluence editor frequently emits an empty <p/> alongside the real content.
258
- const html = `<table><tbody>
259
- <tr><td><p local-id="empty"/><p><strong>Must</strong></p></td><td><p>Text</p></td></tr>
260
- </tbody></table>`
261
- const doc = yield* parseConfluenceHtml(html)
262
- const table = doc.children[0]
263
- expect(table?._tag).toBe("Table")
264
- if (table?._tag === "Table") {
265
- const cell = table.rows[0]?.cells[0]
266
- expect(cell?.children.length).toBe(1)
267
- const strong = cell?.children[0]
268
- expect(strong?._tag).toBe("Strong")
269
- }
270
- }))
271
- })
272
-
273
- describe("integration test fixture", () => {
274
- it.effect("parses and serializes integration test fixture", () =>
275
- Effect.gen(function*() {
276
- const html = yield* readFixture("integration-test.html.fixture")
277
- const doc = yield* parseConfluenceHtml(html)
278
-
279
- // Doc should have children - layout markers plus actual content
280
- expect(doc.children.length).toBeGreaterThan(0)
281
-
282
- // Serialize to markdown - content should be readable (not URL-encoded)
283
- const markdown = yield* serializeToMarkdown(doc)
284
- // Layout markers should be present as comments
285
- expect(markdown).toContain("cf:layout-start")
286
- expect(markdown).toContain("cf:section:")
287
- // Content should be readable markdown, not URL-encoded
288
- expect(markdown).toContain("# Heading 1")
289
- expect(markdown).toContain("## Heading 2")
290
-
291
- // Serialize to confluence - should reconstruct layout structure
292
- const confluenceHtml = yield* serializeToConfluence(doc)
293
- expect(confluenceHtml).toContain("<ac:layout>")
294
- expect(confluenceHtml).toContain("<ac:layout-section")
295
- expect(confluenceHtml).toContain("<ac:layout-cell>")
296
- // Note: ac:task-list may have attributes, so search for the tag name
297
- expect(confluenceHtml).toContain("ac:task-list")
298
- expect(confluenceHtml).toContain("<ac:task-status>incomplete</ac:task-status>")
299
- expect(confluenceHtml).toContain("<ac:task-status>complete</ac:task-status>")
300
- expect(confluenceHtml).toContain("ac:emoticon")
301
- expect(confluenceHtml).toContain("ri:account-id")
302
- }))
303
- })
304
-
305
- describe("roundtrip HTML -> MD -> HTML", () => {
306
- it.effect("roundtrips integration-test.html with 1-to-1 preservation", () =>
307
- Effect.gen(function*() {
308
- const originalHtml = yield* readFixture("integration-test.html.fixture")
309
- const doc1 = yield* parseConfluenceHtml(originalHtml)
310
- const md = yield* serializeToMarkdown(doc1)
311
- const doc2 = yield* parseMarkdown(md)
312
- const finalHtml = yield* serializeToConfluence(doc2)
313
-
314
- // 1-to-1 roundtrip: finalHtml should EXACTLY match originalHtml
315
- expect(finalHtml).toBe(originalHtml)
316
- }))
317
-
318
- it.effect("renders UserMention as visible markdown link and roundtrips", () =>
319
- Effect.gen(function*() {
320
- const originalHtml = `<p><ac:link><ri:user ri:account-id="557058:abc123"/></ac:link></p>`
321
- const doc1 = yield* parseConfluenceHtml(originalHtml)
322
- const md = yield* serializeToMarkdown(doc1, { includeRawSource: false })
323
- expect(md).toContain("[@557058:abc123](#cf-user:")
324
- expect(md).not.toContain("<!--cf:user:")
325
- const doc2 = yield* parseMarkdown(md)
326
- const finalHtml = yield* serializeToConfluence(doc2)
327
- expect(finalHtml).toContain("ri:account-id=\"557058:abc123\"")
328
- }))
329
-
330
- it.effect("renders inline StatusMacro as visible link and roundtrips", () =>
331
- Effect.gen(function*() {
332
- const originalHtml =
333
- `<p><ac:structured-macro ac:name="status"><ac:parameter ac:name="title">READY</ac:parameter><ac:parameter ac:name="colour">Blue</ac:parameter></ac:structured-macro></p>`
334
- const doc1 = yield* parseConfluenceHtml(originalHtml)
335
- const md = yield* serializeToMarkdown(doc1, { includeRawSource: false })
336
- expect(md).toContain("[READY](#cf-status:Blue)")
337
- expect(md).not.toContain("<!--cf:status:")
338
- const doc2 = yield* parseMarkdown(md)
339
- const finalHtml = yield* serializeToConfluence(doc2)
340
- expect(finalHtml).toContain("ac:name=\"status\"")
341
- expect(finalHtml).toContain("READY")
342
- expect(finalHtml).toContain("Blue")
343
- }))
344
-
345
- it.effect("decodes multi-word status title for visible link text", () =>
346
- Effect.gen(function*() {
347
- const originalHtml =
348
- `<p><ac:structured-macro ac:name="status"><ac:parameter ac:name="title">In Progress</ac:parameter><ac:parameter ac:name="colour">Yellow</ac:parameter></ac:structured-macro></p>`
349
- const doc1 = yield* parseConfluenceHtml(originalHtml)
350
- const md = yield* serializeToMarkdown(doc1, { includeRawSource: false })
351
- // Display text is decoded; URL fragment carries the encoded value.
352
- expect(md).toContain("[In Progress](#cf-status:Yellow)")
353
- expect(md).not.toContain("In%20Progress")
354
- const finalHtml = yield* serializeToConfluence(yield* parseMarkdown(md))
355
- expect(finalHtml).toContain("In Progress")
356
- }))
357
-
358
- it.effect("preserves real <thead> with empty header cells (no synthetic-header drop)", () =>
359
- Effect.gen(function*() {
360
- // A legitimate empty <thead> in the source must round-trip intact —
361
- // it must NOT be confused with the synthetic empty header the
362
- // serializer emits for <tbody>-only tables.
363
- const originalHtml =
364
- `<table><thead><tr><th></th><th></th></tr></thead><tbody><tr><td>a</td><td>b</td></tr></tbody></table>`
365
- const doc1 = yield* parseConfluenceHtml(originalHtml)
366
- const md = yield* serializeToMarkdown(doc1, { includeRawSource: false })
367
- expect(md).not.toContain("cf:synth-thead")
368
- const doc2 = yield* parseMarkdown(md)
369
- const finalHtml = yield* serializeToConfluence(doc2)
370
- expect(finalHtml).toBe(originalHtml)
371
- }))
372
-
373
- it.effect("renders view-file macro as a link to the attachment", () =>
374
- Effect.gen(function*() {
375
- const originalHtml =
376
- `<p><ac:structured-macro ac:name="view-file"><ac:parameter ac:name="name"><ri:attachment ri:filename="guidelines.md"/></ac:parameter></ac:structured-macro></p>`
377
- const doc1 = yield* parseConfluenceHtml(originalHtml)
378
- const md = yield* serializeToMarkdown(doc1, { includeRawSource: false })
379
- expect(md).toContain("[guidelines.md](attachment:guidelines.md)")
380
- const doc2 = yield* parseMarkdown(md)
381
- const finalHtml = yield* serializeToConfluence(doc2)
382
- expect(finalHtml).toContain("ac:name=\"view-file\"")
383
- expect(finalHtml).toContain("ri:filename=\"guidelines.md\"")
384
- }))
385
-
386
- it.effect("roundtrips nested unordered list HTML -> markdown -> HTML", () =>
387
- Effect.gen(function*() {
388
- const originalHtml = `<ul><li><p>Outer</p><ul><li><p>Inner A</p></li><li><p>Inner B</p></li></ul></li></ul>`
389
- const doc1 = yield* parseConfluenceHtml(originalHtml)
390
- const md = yield* serializeToMarkdown(doc1, { includeRawSource: false })
391
- const doc2 = yield* parseMarkdown(md)
392
- const finalHtml = yield* serializeToConfluence(doc2)
393
- expect(finalHtml).toBe(originalHtml)
394
- }))
395
-
396
- it.effect("emits markdown header divider for headerless table and roundtrips", () =>
397
- Effect.gen(function*() {
398
- // Tables without <thead> need a synthetic header in markdown to render.
399
- const originalHtml =
400
- `<table><tbody><tr><td>Cell A1</td><td>Cell A2</td></tr><tr><td>Cell B1</td><td>Cell B2</td></tr></tbody></table>`
401
- const doc1 = yield* parseConfluenceHtml(originalHtml)
402
- const md = yield* serializeToMarkdown(doc1, { includeRawSource: false })
403
- // Synthetic empty header + divider so md viewers render it as a table.
404
- expect(md).toMatch(/\|\s*\|\s*\|\n\| --- \| --- \|/)
405
- expect(md).toContain("| Cell A1 | Cell A2 |")
406
- expect(md).toContain("| Cell B1 | Cell B2 |")
407
- // Roundtrip: the synthetic empty header is dropped on parse, so the
408
- // Confluence output omits <thead> just like the source.
409
- const doc2 = yield* parseMarkdown(md)
410
- const finalHtml = yield* serializeToConfluence(doc2)
411
- expect(finalHtml).toBe(originalHtml)
412
- }))
413
-
414
- it.effect("renders ExpandMacro as <details>/<summary> and roundtrips", () =>
415
- Effect.gen(function*() {
416
- const originalHtml =
417
- `<ac:structured-macro ac:name="expand"><ac:parameter ac:name="title">Glossary term</ac:parameter><ac:rich-text-body><p>Body content here.</p></ac:rich-text-body></ac:structured-macro>`
418
- const doc1 = yield* parseConfluenceHtml(originalHtml)
419
- const md = yield* serializeToMarkdown(doc1, { includeRawSource: false })
420
- expect(md).toContain("<details>")
421
- expect(md).toContain("<summary>Glossary term</summary>")
422
- expect(md).toContain("Body content here.")
423
- expect(md).toContain("</details>")
424
- // No more opaque cf:expand comment.
425
- expect(md).not.toContain("<!--cf:expand:")
426
-
427
- const doc2 = yield* parseMarkdown(md)
428
- expect(doc2.children[0]?._tag).toBe("ExpandMacro")
429
- const finalHtml = yield* serializeToConfluence(doc2)
430
- expect(finalHtml).toContain("ac:name=\"expand\"")
431
- expect(finalHtml).toContain("Glossary term")
432
- expect(finalHtml).toContain("Body content here.")
433
- }))
434
-
435
- it.effect("roundtrips TOC macro", () =>
436
- Effect.gen(function*() {
437
- const html =
438
- `<ac:structured-macro ac:name="toc"><ac:parameter ac:name="minLevel">2</ac:parameter></ac:structured-macro>`
439
- const doc1 = yield* parseConfluenceHtml(html)
440
- expect(doc1.children[0]?._tag).toBe("TocMacro")
441
-
442
- const md = yield* serializeToMarkdown(doc1)
443
- expect(md).toContain("[[toc]]")
444
-
445
- const doc2 = yield* parseMarkdown(md)
446
- expect(doc2.children[0]?._tag).toBe("TocMacro")
447
-
448
- const html2 = yield* serializeToConfluence(doc2)
449
- expect(html2).toContain("<ac:structured-macro ac:name=\"toc\">")
450
- }))
451
- })
452
- })
@@ -1,180 +0,0 @@
1
- import { describe, expect, it } from "@effect/vitest"
2
- import * as Effect from "effect/Effect"
3
- import * as Either from "effect/Either"
4
- import * as Schema from "effect/Schema"
5
- import { PreprocessedHtmlFromConfluence, PreprocessedHtmlSchema } from "../../src/schemas/preprocessing/index.js"
6
-
7
- describe("PreprocessedHtmlSchema", () => {
8
- it("validates branded string", () => {
9
- const result = Schema.decodeUnknownEither(PreprocessedHtmlSchema)("<p>Hello</p>")
10
- expect(Either.isRight(result)).toBe(true)
11
- })
12
- })
13
-
14
- describe("PreprocessedHtmlFromConfluence", () => {
15
- describe("size limit", () => {
16
- it.effect("rejects HTML exceeding size limit", () =>
17
- Effect.gen(function*() {
18
- const largeHtml = "x".repeat(1024 * 1024 + 1)
19
- const result = yield* Effect.either(Schema.decode(PreprocessedHtmlFromConfluence)(largeHtml))
20
- expect(Either.isLeft(result)).toBe(true)
21
- }))
22
-
23
- it.effect("accepts HTML within size limit", () =>
24
- Effect.gen(function*() {
25
- const html = "<p>Hello</p>"
26
- const result = yield* Schema.decode(PreprocessedHtmlFromConfluence)(html)
27
- expect(result).toContain("Hello")
28
- }))
29
- })
30
-
31
- describe("structured macro expansion", () => {
32
- it.effect("expands code macro", () =>
33
- Effect.gen(function*() {
34
- // Single line format that matches the regex pattern
35
- const html =
36
- `<ac:structured-macro ac:name="code"><ac:parameter ac:name="language">typescript</ac:parameter><ac:plain-text-body><![CDATA[const x = 1]]></ac:plain-text-body></ac:structured-macro>`
37
- const result = yield* Schema.decode(PreprocessedHtmlFromConfluence)(html)
38
- expect(result).toContain("data-macro=\"code\"")
39
- expect(result).toContain("data-language=\"typescript\"")
40
- expect(result).toContain("const x = 1")
41
- }))
42
-
43
- it.effect("expands info panel macro", () =>
44
- Effect.gen(function*() {
45
- const html = `<ac:structured-macro ac:name="info">
46
- <ac:parameter ac:name="title">Note</ac:parameter>
47
- <ac:rich-text-body><p>Content here</p></ac:rich-text-body>
48
- </ac:structured-macro>`
49
- const result = yield* Schema.decode(PreprocessedHtmlFromConfluence)(html)
50
- expect(result).toContain("data-macro=\"info\"")
51
- expect(result).toContain("data-title=\"Note\"")
52
- expect(result).toContain("<p>Content here</p>")
53
- }))
54
-
55
- it.effect("expands expand macro", () =>
56
- Effect.gen(function*() {
57
- const html = `<ac:structured-macro ac:name="expand">
58
- <ac:parameter ac:name="title">Click to expand</ac:parameter>
59
- <ac:rich-text-body><p>Hidden content</p></ac:rich-text-body>
60
- </ac:structured-macro>`
61
- const result = yield* Schema.decode(PreprocessedHtmlFromConfluence)(html)
62
- expect(result).toContain("<details")
63
- expect(result).toContain("<summary>Click to expand</summary>")
64
- expect(result).toContain("<p>Hidden content</p>")
65
- }))
66
-
67
- it.effect("expands TOC macro", () =>
68
- Effect.gen(function*() {
69
- const html = `<ac:structured-macro ac:name="toc">
70
- <ac:parameter ac:name="minLevel">2</ac:parameter>
71
- <ac:parameter ac:name="maxLevel">4</ac:parameter>
72
- </ac:structured-macro>`
73
- const result = yield* Schema.decode(PreprocessedHtmlFromConfluence)(html)
74
- expect(result).toContain("data-macro=\"toc\"")
75
- expect(result).toContain("data-min=\"2\"")
76
- expect(result).toContain("data-max=\"4\"")
77
- }))
78
-
79
- it.effect("expands status macro", () =>
80
- Effect.gen(function*() {
81
- const html = `<ac:structured-macro ac:name="status">
82
- <ac:parameter ac:name="colour">Green</ac:parameter>
83
- <ac:parameter ac:name="title">Done</ac:parameter>
84
- </ac:structured-macro>`
85
- const result = yield* Schema.decode(PreprocessedHtmlFromConfluence)(html)
86
- expect(result).toContain("data-macro=\"status\"")
87
- expect(result).toContain("data-color=\"Green\"")
88
- expect(result).toContain("Done")
89
- }))
90
- })
91
-
92
- describe("task list preprocessing", () => {
93
- it.effect("converts task list to ul", () =>
94
- Effect.gen(function*() {
95
- const html = `<ac:task-list>
96
- <ac:task>
97
- <ac:task-id>1</ac:task-id>
98
- <ac:task-status>incomplete</ac:task-status>
99
- <ac:task-body>Task one</ac:task-body>
100
- </ac:task>
101
- </ac:task-list>`
102
- const result = yield* Schema.decode(PreprocessedHtmlFromConfluence)(html)
103
- expect(result).toContain("<ul data-macro=\"task-list\">")
104
- expect(result).toContain("data-task-id=\"1\"")
105
- expect(result).toContain("data-task-status=\"incomplete\"")
106
- expect(result).toContain("Task one")
107
- }))
108
- })
109
-
110
- describe("image preprocessing", () => {
111
- it.effect("converts attachment images", () =>
112
- Effect.gen(function*() {
113
- const html = `<ac:image ac:align="center" ac:width="250">
114
- <ri:attachment ri:filename="test.png"/>
115
- </ac:image>`
116
- const result = yield* Schema.decode(PreprocessedHtmlFromConfluence)(html)
117
- expect(result).toContain("data-attachment=\"test.png\"")
118
- expect(result).toContain("data-align=\"center\"")
119
- expect(result).toContain("data-width=\"250\"")
120
- }))
121
- })
122
-
123
- describe("emoticon preprocessing", () => {
124
- it.effect("converts emoticons", () =>
125
- Effect.gen(function*() {
126
- const html = `<ac:emoticon ac:emoji-shortname=":grinning:" ac:emoji-id="1f600" ac:emoji-fallback="😀"/>`
127
- const result = yield* Schema.decode(PreprocessedHtmlFromConfluence)(html)
128
- expect(result).toContain("data-emoji=\":grinning:\"")
129
- expect(result).toContain("data-emoji-id=\"1f600\"")
130
- expect(result).toContain("😀")
131
- }))
132
- })
133
-
134
- describe("user mention preprocessing", () => {
135
- it.effect("converts user mentions", () =>
136
- Effect.gen(function*() {
137
- const html = `<ac:link><ri:user ri:account-id="557058:abc123"/></ac:link>`
138
- const result = yield* Schema.decode(PreprocessedHtmlFromConfluence)(html)
139
- expect(result).toContain("data-user-mention=\"557058:abc123\"")
140
- }))
141
- })
142
-
143
- describe("namespace stripping", () => {
144
- it.effect("removes ac: and ri: tags", () =>
145
- Effect.gen(function*() {
146
- const html = `<p>Text<ac:parameter ac:name="foo">bar</ac:parameter></p>`
147
- const result = yield* Schema.decode(PreprocessedHtmlFromConfluence)(html)
148
- expect(result).not.toContain("ac:parameter")
149
- expect(result).toContain("<p>Text</p>")
150
- }))
151
- })
152
-
153
- describe("layout preprocessing", () => {
154
- it.effect("adds layout markers", () =>
155
- Effect.gen(function*() {
156
- const html = `<ac:layout>
157
- <ac:layout-section ac:type="two_equal">
158
- <ac:layout-cell><p>Cell 1</p></ac:layout-cell>
159
- <ac:layout-cell><p>Cell 2</p></ac:layout-cell>
160
- </ac:layout-section>
161
- </ac:layout>`
162
- const result = yield* Schema.decode(PreprocessedHtmlFromConfluence)(html)
163
- expect(result).toContain("<!--cf:layout-start-->")
164
- expect(result).toContain("<!--cf:layout-end-->")
165
- expect(result).toContain("<!--cf:section:0;")
166
- expect(result).toContain("<p>Cell 1</p>")
167
- expect(result).toContain("<p>Cell 2</p>")
168
- }))
169
- })
170
-
171
- describe("encode (identity)", () => {
172
- it.effect("encode returns same string", () =>
173
- Effect.gen(function*() {
174
- const html = "<p>Hello</p>"
175
- const preprocessed = yield* Schema.decode(PreprocessedHtmlFromConfluence)(html)
176
- const encoded = yield* Schema.encode(PreprocessedHtmlFromConfluence)(preprocessed)
177
- expect(encoded).toBe(preprocessed)
178
- }))
179
- })
180
- })