@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,5 +1,5 @@
1
1
  import { describe, expect, it } from "@effect/vitest"
2
- import * as Either from "effect/Either"
2
+ import * as Result from "effect/Result"
3
3
  import * as Schema from "effect/Schema"
4
4
  import type { ContentHash, PageId } from "../src/Brand.js"
5
5
  import {
@@ -17,11 +17,11 @@ describe("Schemas", () => {
17
17
  rootPageId: "123456",
18
18
  baseUrl: "https://mysite.atlassian.net"
19
19
  }
20
- const result = Schema.decodeUnknownEither(ConfluenceConfigFileSchema)(config)
21
- expect(Either.isRight(result)).toBe(true)
22
- if (Either.isRight(result)) {
23
- expect(result.right.docsPath).toBe(".confluence/docs")
24
- expect(result.right.excludePatterns).toEqual([])
20
+ const result = Schema.decodeUnknownResult(ConfluenceConfigFileSchema)(config)
21
+ expect(Result.isSuccess(result)).toBe(true)
22
+ if (Result.isSuccess(result)) {
23
+ expect(result.success.docsPath).toBe(".confluence/docs")
24
+ expect(result.success.excludePatterns).toEqual([])
25
25
  }
26
26
  })
27
27
 
@@ -33,11 +33,11 @@ describe("Schemas", () => {
33
33
  docsPath: "docs",
34
34
  excludePatterns: ["*.tmp"]
35
35
  }
36
- const result = Schema.decodeUnknownEither(ConfluenceConfigFileSchema)(config)
37
- expect(Either.isRight(result)).toBe(true)
38
- if (Either.isRight(result)) {
39
- expect(result.right.spaceKey).toBe("DEV")
40
- expect(result.right.docsPath).toBe("docs")
36
+ const result = Schema.decodeUnknownResult(ConfluenceConfigFileSchema)(config)
37
+ expect(Result.isSuccess(result)).toBe(true)
38
+ if (Result.isSuccess(result)) {
39
+ expect(result.success.spaceKey).toBe("DEV")
40
+ expect(result.success.docsPath).toBe("docs")
41
41
  }
42
42
  })
43
43
 
@@ -46,14 +46,14 @@ describe("Schemas", () => {
46
46
  rootPageId: "123456",
47
47
  baseUrl: "http://invalid.com"
48
48
  }
49
- const result = Schema.decodeUnknownEither(ConfluenceConfigFileSchema)(config)
50
- expect(Either.isLeft(result)).toBe(true)
49
+ const result = Schema.decodeUnknownResult(ConfluenceConfigFileSchema)(config)
50
+ expect(Result.isFailure(result)).toBe(true)
51
51
  })
52
52
 
53
53
  it("rejects missing required fields", () => {
54
54
  const config = { baseUrl: "https://mysite.atlassian.net" }
55
- const result = Schema.decodeUnknownEither(ConfluenceConfigFileSchema)(config)
56
- expect(Either.isLeft(result)).toBe(true)
55
+ const result = Schema.decodeUnknownResult(ConfluenceConfigFileSchema)(config)
56
+ expect(Result.isFailure(result)).toBe(true)
57
57
  })
58
58
  })
59
59
 
@@ -68,8 +68,8 @@ describe("Schemas", () => {
68
68
  updated: new Date().toISOString(),
69
69
  contentHash: validHash
70
70
  }
71
- const result = Schema.decodeUnknownEither(PageFrontMatterSchema)(fm)
72
- expect(Either.isRight(result)).toBe(true)
71
+ const result = Schema.decodeUnknownResult(PageFrontMatterSchema)(fm)
72
+ expect(Result.isSuccess(result)).toBe(true)
73
73
  })
74
74
 
75
75
  it("decodes front matter with optional fields", () => {
@@ -82,11 +82,11 @@ describe("Schemas", () => {
82
82
  position: 0,
83
83
  contentHash: validHash
84
84
  }
85
- const result = Schema.decodeUnknownEither(PageFrontMatterSchema)(fm)
86
- expect(Either.isRight(result)).toBe(true)
87
- if (Either.isRight(result)) {
88
- expect(result.right.parentId).toBe("456")
89
- expect(result.right.position).toBe(0)
85
+ const result = Schema.decodeUnknownResult(PageFrontMatterSchema)(fm)
86
+ expect(Result.isSuccess(result)).toBe(true)
87
+ if (Result.isSuccess(result)) {
88
+ expect(result.success.parentId).toBe("456")
89
+ expect(result.success.position).toBe(0)
90
90
  }
91
91
  })
92
92
 
@@ -98,8 +98,8 @@ describe("Schemas", () => {
98
98
  updated: new Date().toISOString(),
99
99
  contentHash: validHash
100
100
  }
101
- const result = Schema.decodeUnknownEither(PageFrontMatterSchema)(fm)
102
- expect(Either.isLeft(result)).toBe(true)
101
+ const result = Schema.decodeUnknownResult(PageFrontMatterSchema)(fm)
102
+ expect(Result.isFailure(result)).toBe(true)
103
103
  })
104
104
  })
105
105
 
@@ -113,8 +113,8 @@ describe("Schemas", () => {
113
113
  cloud_id: "abc123",
114
114
  site_url: "https://mysite.atlassian.net"
115
115
  }
116
- const result = Schema.decodeUnknownEither(OAuthTokenSchema)(token)
117
- expect(Either.isRight(result)).toBe(true)
116
+ const result = Schema.decodeUnknownResult(OAuthTokenSchema)(token)
117
+ expect(Result.isSuccess(result)).toBe(true)
118
118
  })
119
119
 
120
120
  it("decodes token with user info", () => {
@@ -131,10 +131,10 @@ describe("Schemas", () => {
131
131
  email: "test@example.com"
132
132
  }
133
133
  }
134
- const result = Schema.decodeUnknownEither(OAuthTokenSchema)(token)
135
- expect(Either.isRight(result)).toBe(true)
136
- if (Either.isRight(result)) {
137
- expect(result.right.user?.name).toBe("Test User")
134
+ const result = Schema.decodeUnknownResult(OAuthTokenSchema)(token)
135
+ expect(Result.isSuccess(result)).toBe(true)
136
+ if (Result.isSuccess(result)) {
137
+ expect(result.success.user?.name).toBe("Test User")
138
138
  }
139
139
  })
140
140
 
@@ -147,8 +147,8 @@ describe("Schemas", () => {
147
147
  cloud_id: "abc123",
148
148
  site_url: "https://mysite.atlassian.net"
149
149
  }
150
- const result = Schema.decodeUnknownEither(OAuthTokenSchema)(token)
151
- expect(Either.isLeft(result)).toBe(true)
150
+ const result = Schema.decodeUnknownResult(OAuthTokenSchema)(token)
151
+ expect(Result.isFailure(result)).toBe(true)
152
152
  })
153
153
  })
154
154
 
@@ -158,16 +158,16 @@ describe("Schemas", () => {
158
158
  clientId: "client_id_value",
159
159
  clientSecret: "client_secret_value"
160
160
  }
161
- const result = Schema.decodeUnknownEither(OAuthConfigSchema)(config)
162
- expect(Either.isRight(result)).toBe(true)
161
+ const result = Schema.decodeUnknownResult(OAuthConfigSchema)(config)
162
+ expect(Result.isSuccess(result)).toBe(true)
163
163
  })
164
164
 
165
165
  it("rejects missing clientSecret", () => {
166
166
  const config = {
167
167
  clientId: "client_id_value"
168
168
  }
169
- const result = Schema.decodeUnknownEither(OAuthConfigSchema)(config)
170
- expect(Either.isLeft(result)).toBe(true)
169
+ const result = Schema.decodeUnknownResult(OAuthConfigSchema)(config)
170
+ expect(Result.isFailure(result)).toBe(true)
171
171
  })
172
172
  })
173
173
 
@@ -178,8 +178,8 @@ describe("Schemas", () => {
178
178
  name: "Test User",
179
179
  email: "test@example.com"
180
180
  }
181
- const result = Schema.decodeUnknownEither(OAuthUserSchema)(user)
182
- expect(Either.isRight(result)).toBe(true)
181
+ const result = Schema.decodeUnknownResult(OAuthUserSchema)(user)
182
+ expect(Result.isSuccess(result)).toBe(true)
183
183
  })
184
184
 
185
185
  it("rejects missing email", () => {
@@ -187,8 +187,8 @@ describe("Schemas", () => {
187
187
  account_id: "user123",
188
188
  name: "Test User"
189
189
  }
190
- const result = Schema.decodeUnknownEither(OAuthUserSchema)(user)
191
- expect(Either.isLeft(result)).toBe(true)
190
+ const result = Schema.decodeUnknownResult(OAuthUserSchema)(user)
191
+ expect(Result.isFailure(result)).toBe(true)
192
192
  })
193
193
  })
194
194
  })
@@ -0,0 +1,110 @@
1
+ import { describe, expect, it } from "@effect/vitest"
2
+ import { collectAdfMetadataHrefs, externalizeAdfMetadata, hydrateAdfMetadata } from "../src/internal/adfMetadata.js"
3
+
4
+ const toBase64 = (text: string): string => {
5
+ const bytes = new TextEncoder().encode(text)
6
+ return btoa(String.fromCharCode(...bytes))
7
+ }
8
+
9
+ describe("ADF metadata sidecars", () => {
10
+ it("moves decoded placeholder metadata into a linked sidecar", () => {
11
+ const markdown = [
12
+ `<!-- adf:panel type=info attrs={"panelType":"info"} -->`,
13
+ "",
14
+ "Body",
15
+ "",
16
+ "<!-- adf:/panel -->",
17
+ "",
18
+ `Regular link: <!-- adf:inlineCard attrs={"url":"https://www.atlassian.com"} -->.`
19
+ ].join("\n")
20
+
21
+ const prepared = externalizeAdfMetadata(markdown, "./123.adf.json")
22
+
23
+ expect(prepared.markdown).toContain("<!-- adf:panel type=info ref=./123.adf.json#panel-1 -->")
24
+ expect(prepared.markdown).toContain("<!-- adf:inlineCard ref=./123.adf.json#inlineCard-2 -->")
25
+ expect(prepared.markdown).not.toContain("attrs={")
26
+ expect(prepared.sidecar).toEqual({
27
+ version: 1,
28
+ entries: {
29
+ "panel-1": {
30
+ kind: "attrs",
31
+ value: { panelType: "info" }
32
+ },
33
+ "inlineCard-2": {
34
+ kind: "attrs",
35
+ value: { url: "https://www.atlassian.com" }
36
+ }
37
+ }
38
+ })
39
+ })
40
+
41
+ it("moves base64 placeholder metadata into a linked sidecar", () => {
42
+ const attrs = {
43
+ extensionKey: "toc",
44
+ extensionType: "com.atlassian.confluence.macro.core",
45
+ parameters: {
46
+ macroMetadata: {
47
+ title: "Table of Contents"
48
+ }
49
+ }
50
+ }
51
+ const encodedAttrs = toBase64(JSON.stringify(attrs))
52
+ const markdown =
53
+ `| **On this page** | <!-- adf:extension key=toc type=com.atlassian.confluence.macro.core attrs=${encodedAttrs} --> |`
54
+
55
+ const prepared = externalizeAdfMetadata(markdown, "./2731114497.adf.json")
56
+
57
+ expect(prepared.markdown).toBe(
58
+ "| **On this page** | <!-- adf:extension key=toc type=com.atlassian.confluence.macro.core ref=./2731114497.adf.json#extension-1 --> |"
59
+ )
60
+ expect(prepared.markdown).not.toContain(encodedAttrs)
61
+ expect(prepared.sidecar).toEqual({
62
+ version: 1,
63
+ entries: {
64
+ "extension-1": {
65
+ kind: "attrs",
66
+ value: attrs
67
+ }
68
+ }
69
+ })
70
+ })
71
+
72
+ it("hydrates linked sidecar metadata back into placeholders for push", () => {
73
+ const markdown = `<!-- adf:taskList ref=./123.adf.json#taskList-1 -->`
74
+ const hydrated = hydrateAdfMetadata(
75
+ markdown,
76
+ new Map([
77
+ [
78
+ "./123.adf.json",
79
+ {
80
+ version: 1,
81
+ entries: {
82
+ "taskList-1": {
83
+ kind: "node",
84
+ value: {
85
+ type: "taskList",
86
+ attrs: { localId: "task-list" },
87
+ content: [{ type: "taskItem", attrs: { state: "DONE" } }]
88
+ }
89
+ }
90
+ }
91
+ }
92
+ ]
93
+ ])
94
+ )
95
+
96
+ expect(hydrated).toBe(
97
+ `<!-- adf:taskList node={"attrs":{"localId":"task-list"},"content":[{"attrs":{"state":"DONE"},"type":"taskItem"}],"type":"taskList"} -->`
98
+ )
99
+ })
100
+
101
+ it("collects each referenced sidecar href once", () => {
102
+ const hrefs = collectAdfMetadataHrefs([
103
+ "<!-- adf:panel ref=./123.adf.json#panel-1 -->",
104
+ "<!-- adf:taskList ref=./123.adf.json#taskList-2 -->",
105
+ "<!-- adf:panel ref=./456.adf.json#panel-1 -->"
106
+ ].join("\n"))
107
+
108
+ expect([...hrefs].sort()).toEqual(["./123.adf.json", "./456.adf.json"])
109
+ })
110
+ })
@@ -0,0 +1,36 @@
1
+ import { describe, expect, it } from "@effect/vitest"
2
+ import { cleanMarkdown } from "../src/internal/cleanMarkdown.js"
3
+
4
+ describe("cleanMarkdown", () => {
5
+ it("removes full-line ADF metadata comments", () => {
6
+ const markdown = [
7
+ "# Title",
8
+ "",
9
+ "<!-- adf:panel type=info attrs={} -->",
10
+ "",
11
+ "Body",
12
+ "",
13
+ "<!-- adf:/panel -->"
14
+ ].join("\n")
15
+
16
+ expect(cleanMarkdown(markdown)).toBe("# Title\n\nBody\n")
17
+ })
18
+
19
+ it("removes inline ADF metadata comments", () => {
20
+ expect(cleanMarkdown("Card <!-- adf:inlineCard attrs={} --> text")).toBe("Card text\n")
21
+ })
22
+
23
+ it("keeps regular markdown, native macros, status spans, and non-ADF comments", () => {
24
+ const markdown = [
25
+ "[[toc]]",
26
+ "",
27
+ "<span data-adf-status=\"todo\" data-adf-color=\"blue\">TODO</span>",
28
+ "",
29
+ "<!-- keep this comment -->",
30
+ "",
31
+ "- item"
32
+ ].join("\n")
33
+
34
+ expect(cleanMarkdown(markdown)).toBe(`${markdown}\n`)
35
+ })
36
+ })
@@ -0,0 +1,79 @@
1
+ import { describe, expect, it } from "@effect/vitest"
2
+ import * as Effect from "effect/Effect"
3
+ import * as Layer from "effect/Layer"
4
+ import * as Ref from "effect/Ref"
5
+ import type { PageId } from "../src/Brand.js"
6
+ import { makeFetchCommand } from "../src/commands/fetch.js"
7
+ import { ConfluenceClient } from "../src/ConfluenceClient.js"
8
+ import type { PageResponse } from "../src/Schemas.js"
9
+ import { CommandHarnessLayer, runConfluenceCommand } from "./commandHarness.js"
10
+
11
+ const page: PageResponse = {
12
+ id: "2333334354",
13
+ title: "Harness Page",
14
+ version: { number: 1 },
15
+ body: {
16
+ atlas_doc_format: {
17
+ representation: "atlas_doc_format",
18
+ value: JSON.stringify({ type: "doc", version: 1, content: [] })
19
+ }
20
+ }
21
+ }
22
+
23
+ const FetchClientLayer = Layer.succeed(
24
+ ConfluenceClient,
25
+ ConfluenceClient.of({
26
+ getPage: (pageId: PageId) =>
27
+ pageId === "2333334354"
28
+ ? Effect.succeed(page)
29
+ : Effect.die(`unexpected page ID: ${pageId}`),
30
+ getChildren: () => Effect.die("unused"),
31
+ getAllChildren: () => Effect.die("unused"),
32
+ createPage: () => Effect.die("unused"),
33
+ updatePage: () => Effect.die("unused"),
34
+ deletePage: () => Effect.die("unused"),
35
+ getPageVersions: () => Effect.die("unused"),
36
+ getUser: () => Effect.die("unused"),
37
+ getSpaceId: () => Effect.die("unused"),
38
+ setEditorVersion: () => Effect.die("unused")
39
+ })
40
+ )
41
+
42
+ describe("command harness", () => {
43
+ it.effect("runs fetch through the root command without calling GitService", () =>
44
+ Effect.gen(function*() {
45
+ const gitCalls = yield* Ref.make(0)
46
+ const stdout = yield* Ref.make("")
47
+ const fetch = makeFetchCommand({ makeClientLayer: () => FetchClientLayer })
48
+
49
+ const exit = yield* runConfluenceCommand([
50
+ "fetch",
51
+ "--url",
52
+ "https://example.atlassian.net/wiki/pages/2333334354"
53
+ ], { fetch }).pipe(Effect.provide(CommandHarnessLayer({ gitCalls, stdout })))
54
+ const calls = yield* Ref.get(gitCalls)
55
+ const output = yield* Ref.get(stdout)
56
+
57
+ expect(exit._tag).toBe("Success")
58
+ expect(output).toBe("# Harness Page\n")
59
+ expect(calls).toBe(0)
60
+ }))
61
+
62
+ it.effect("rejects conflicting clone URL input before calling GitService", () =>
63
+ Effect.gen(function*() {
64
+ const gitCalls = yield* Ref.make(0)
65
+ const stdout = yield* Ref.make("")
66
+
67
+ const exit = yield* runConfluenceCommand([
68
+ "clone",
69
+ "--url",
70
+ "https://example.atlassian.net/wiki/pages/2333334354",
71
+ "--base-url",
72
+ "https://example.atlassian.net"
73
+ ]).pipe(Effect.provide(CommandHarnessLayer({ gitCalls, stdout })))
74
+ const calls = yield* Ref.get(gitCalls)
75
+
76
+ expect(exit._tag).toBe("Failure")
77
+ expect(calls).toBe(0)
78
+ }))
79
+ })
@@ -0,0 +1,147 @@
1
+ import * as Effect from "effect/Effect"
2
+ import * as Layer from "effect/Layer"
3
+ import * as Ref from "effect/Ref"
4
+ import * as Terminal from "effect/Terminal"
5
+ import { Command } from "effect/unstable/cli"
6
+ import type { PageId } from "../src/Brand.js"
7
+ import type { ConfluenceCommandOptions } from "../src/commands/root.js"
8
+ import { makeConfluenceCommand } from "../src/commands/root.js"
9
+ import { ConfluenceAuth } from "../src/ConfluenceAuth.js"
10
+ import { ConfluenceClient } from "../src/ConfluenceClient.js"
11
+ import { layerFromValues as ConfluenceConfigLayerFromValues } from "../src/ConfluenceConfig.js"
12
+ import { GitService } from "../src/GitService.js"
13
+ import { MarkdownConverter } from "../src/MarkdownConverter.js"
14
+ import { SyncEngine } from "../src/SyncEngine.js"
15
+
16
+ const notCalled = (calls: Ref.Ref<number>) =>
17
+ Ref.update(calls, (count) => count + 1).pipe(
18
+ Effect.flatMap(() => Effect.die("GitService should not be called"))
19
+ )
20
+
21
+ export const GitShouldNotBeCalledLayer = (calls: Ref.Ref<number>) =>
22
+ Layer.succeed(
23
+ GitService,
24
+ GitService.of({
25
+ validateGit: () => notCalled(calls),
26
+ init: () => notCalled(calls),
27
+ isInitialized: () => notCalled(calls),
28
+ status: () => notCalled(calls),
29
+ commit: () => notCalled(calls),
30
+ log: () => notCalled(calls),
31
+ diff: () => notCalled(calls),
32
+ addAll: () => notCalled(calls),
33
+ hasConflicts: () => notCalled(calls),
34
+ mergeContinue: () => notCalled(calls),
35
+ syncFromDocs: () => notCalled(calls),
36
+ syncToDocs: () => notCalled(calls),
37
+ getHead: () => notCalled(calls),
38
+ getCurrentBranch: () => notCalled(calls),
39
+ createBranch: () => notCalled(calls),
40
+ checkout: () => notCalled(calls),
41
+ reset: () => notCalled(calls),
42
+ deleteBranch: () => notCalled(calls),
43
+ getParent: () => notCalled(calls),
44
+ cherryPick: () => notCalled(calls),
45
+ getChangedFiles: () => notCalled(calls),
46
+ showFile: () => notCalled(calls),
47
+ amend: () => notCalled(calls),
48
+ logRange: () => notCalled(calls),
49
+ branchExists: () => notCalled(calls),
50
+ updateBranch: () => notCalled(calls),
51
+ merge: () => notCalled(calls),
52
+ getDeletedFiles: () => notCalled(calls),
53
+ getFileContentAt: () => notCalled(calls)
54
+ })
55
+ )
56
+
57
+ export interface CommandHarnessRefs {
58
+ readonly gitCalls: Ref.Ref<number>
59
+ readonly stdout: Ref.Ref<string>
60
+ }
61
+
62
+ const CaptureTerminalLayer = (stdout: Ref.Ref<string>) =>
63
+ Layer.succeed(
64
+ Terminal.Terminal,
65
+ Terminal.Terminal.of({
66
+ columns: Effect.succeed(80),
67
+ rows: Effect.succeed(24),
68
+ readInput: Effect.die("readInput should not be called"),
69
+ readLine: Effect.die("readLine should not be called"),
70
+ display: (text) => Ref.update(stdout, (output) => output + text)
71
+ })
72
+ )
73
+
74
+ const AuthLayer = Layer.succeed(
75
+ ConfluenceAuth,
76
+ ConfluenceAuth.of({
77
+ configure: () => Effect.void,
78
+ isConfigured: () => Effect.succeed(true),
79
+ login: () => Effect.void,
80
+ logout: () => Effect.void,
81
+ getAccessToken: () => Effect.succeed("access-token"),
82
+ getCloudId: () => Effect.succeed("cloud-id"),
83
+ getCurrentUser: () => Effect.succeed(null),
84
+ isLoggedIn: () => Effect.succeed(true)
85
+ })
86
+ )
87
+
88
+ const SyncEngineLayer = Layer.succeed(
89
+ SyncEngine,
90
+ SyncEngine.of({
91
+ pull: () => Effect.die("SyncEngine should not be called"),
92
+ push: () => Effect.die("SyncEngine should not be called"),
93
+ status: () => Effect.die("SyncEngine should not be called")
94
+ })
95
+ )
96
+
97
+ const MarkdownConverterLayer = Layer.succeed(
98
+ MarkdownConverter,
99
+ MarkdownConverter.of({
100
+ adfToMarkdown: () => Effect.succeed("# Harness Page\n"),
101
+ markdownToAdf: () => Effect.die("markdownToAdf should not be called")
102
+ })
103
+ )
104
+
105
+ const DummyConfluenceClientLayer = Layer.succeed(
106
+ ConfluenceClient,
107
+ ConfluenceClient.of({
108
+ getPage: () => Effect.die("Use an injected fetch client layer in command tests"),
109
+ getChildren: () => Effect.die("ConfluenceClient should not be called"),
110
+ getAllChildren: () => Effect.die("ConfluenceClient should not be called"),
111
+ createPage: () => Effect.die("ConfluenceClient should not be called"),
112
+ updatePage: () => Effect.die("ConfluenceClient should not be called"),
113
+ deletePage: () => Effect.die("ConfluenceClient should not be called"),
114
+ getPageVersions: () => Effect.die("ConfluenceClient should not be called"),
115
+ getUser: () => Effect.die("ConfluenceClient should not be called"),
116
+ getSpaceId: () => Effect.die("ConfluenceClient should not be called"),
117
+ setEditorVersion: () => Effect.die("ConfluenceClient should not be called")
118
+ })
119
+ )
120
+
121
+ const ConfigLayer = ConfluenceConfigLayerFromValues({
122
+ rootPageId: "dummy" as PageId,
123
+ baseUrl: "https://dummy.atlassian.net",
124
+ docsPath: ".confluence/docs",
125
+ excludePatterns: [],
126
+ saveSource: false,
127
+ trackedPaths: ["**/*.md"]
128
+ })
129
+
130
+ export const CommandHarnessLayer = (refs: CommandHarnessRefs) =>
131
+ Layer.mergeAll(
132
+ AuthLayer,
133
+ GitShouldNotBeCalledLayer(refs.gitCalls),
134
+ SyncEngineLayer,
135
+ MarkdownConverterLayer,
136
+ DummyConfluenceClientLayer,
137
+ ConfigLayer,
138
+ CaptureTerminalLayer(refs.stdout)
139
+ )
140
+
141
+ export const runConfluenceCommand = (
142
+ args: ReadonlyArray<string>,
143
+ options: ConfluenceCommandOptions = {}
144
+ ) => {
145
+ const cli = Command.runWith(makeConfluenceCommand(options), { version: "0.0.0-test" })
146
+ return cli(args).pipe(Effect.exit)
147
+ }
@@ -0,0 +1,61 @@
1
+ import { describe, expect, it } from "@effect/vitest"
2
+ import * as Effect from "effect/Effect"
3
+ import * as Layer from "effect/Layer"
4
+ import type { PageId } from "../src/Brand.js"
5
+ import { fetchPageMarkdown } from "../src/commands/fetch.js"
6
+ import { ConfluenceClient } from "../src/ConfluenceClient.js"
7
+ import { MarkdownConverter } from "../src/MarkdownConverter.js"
8
+ import type { PageResponse } from "../src/Schemas.js"
9
+
10
+ const page: PageResponse = {
11
+ id: "2333334354",
12
+ title: "Fetched Page",
13
+ version: { number: 7 },
14
+ body: {
15
+ atlas_doc_format: {
16
+ representation: "atlas_doc_format",
17
+ value: JSON.stringify({ type: "doc", version: 1, content: [] })
18
+ }
19
+ }
20
+ }
21
+
22
+ const TestLayer = Layer.mergeAll(
23
+ Layer.succeed(
24
+ ConfluenceClient,
25
+ ConfluenceClient.of({
26
+ getPage: () => Effect.succeed(page),
27
+ getChildren: () => Effect.die("unused"),
28
+ getAllChildren: () => Effect.die("unused"),
29
+ createPage: () => Effect.die("unused"),
30
+ updatePage: () => Effect.die("unused"),
31
+ deletePage: () => Effect.die("unused"),
32
+ getPageVersions: () => Effect.die("unused"),
33
+ getUser: () => Effect.die("unused"),
34
+ getSpaceId: () => Effect.die("unused"),
35
+ setEditorVersion: () => Effect.die("unused")
36
+ })
37
+ ),
38
+ Layer.succeed(
39
+ MarkdownConverter,
40
+ MarkdownConverter.of({
41
+ adfToMarkdown: () => Effect.succeed("# Fetched Page\n\n<!-- adf:panel attrs={} -->\n\nBody\n"),
42
+ markdownToAdf: () => Effect.die("unused")
43
+ })
44
+ )
45
+ )
46
+
47
+ describe("fetchPageMarkdown", () => {
48
+ it.effect("returns preserving markdown by default", () =>
49
+ Effect.gen(function*() {
50
+ const markdown = yield* fetchPageMarkdown("2333334354" as PageId, { cleanMarkdown: false })
51
+
52
+ expect(markdown).toContain("<!-- adf:panel attrs={} -->")
53
+ }).pipe(Effect.provide(TestLayer)))
54
+
55
+ it.effect("can return clean markdown", () =>
56
+ Effect.gen(function*() {
57
+ const markdown = yield* fetchPageMarkdown("2333334354" as PageId, { cleanMarkdown: true })
58
+
59
+ expect(markdown).toBe("# Fetched Page\n\nBody\n")
60
+ }).pipe(Effect.provide(TestLayer)))
61
+ })
@@ -0,0 +1,41 @@
1
+ import { describe, expect, it } from "@effect/vitest"
2
+ import * as Effect from "effect/Effect"
3
+ import { ContentHash, PageId } from "../src/Brand.js"
4
+ import { parseMarkdown, serializeMarkdown, serializeNewPageMarkdown } from "../src/internal/frontmatter.js"
5
+
6
+ describe("frontmatter serialization", () => {
7
+ it.effect("serializes existing page frontmatter without gray-matter safeDump", () =>
8
+ Effect.gen(function*() {
9
+ const serialized = serializeMarkdown(
10
+ {
11
+ pageId: PageId("123"),
12
+ version: 7,
13
+ title: "A page",
14
+ updated: new Date("2026-06-24T10:00:00.000Z"),
15
+ parentId: PageId("456"),
16
+ contentHash: ContentHash("a".repeat(64))
17
+ },
18
+ "Body\n"
19
+ )
20
+
21
+ expect(serialized).toContain("pageId: '123'")
22
+ expect(serialized).toContain("version: 7")
23
+ expect(serialized).toContain("updated: '2026-06-24T10:00:00.000Z'")
24
+
25
+ const parsed = yield* parseMarkdown("page.md", serialized)
26
+ expect(parsed.isNew).toBe(false)
27
+ expect(parsed.content).toBe("Body")
28
+ }))
29
+
30
+ it("serializes new page frontmatter", () => {
31
+ const serialized = serializeNewPageMarkdown(
32
+ {
33
+ title: "New page",
34
+ parentId: PageId("456")
35
+ },
36
+ "Draft"
37
+ )
38
+
39
+ expect(serialized).toBe("---\ntitle: New page\nparentId: '456'\n---\nDraft\n")
40
+ })
41
+ })