@knpkv/confluence-to-markdown 0.6.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 (357) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/LICENSE +21 -0
  3. package/README.md +22 -13
  4. package/dist/AdfPlaceholders.d.ts +42 -0
  5. package/dist/AdfPlaceholders.d.ts.map +1 -0
  6. package/dist/AdfPlaceholders.js +547 -0
  7. package/dist/AdfPlaceholders.js.map +1 -0
  8. package/dist/AdfSchemaValidator.d.ts +37 -0
  9. package/dist/AdfSchemaValidator.d.ts.map +1 -0
  10. package/dist/AdfSchemaValidator.js +37 -0
  11. package/dist/AdfSchemaValidator.js.map +1 -0
  12. package/dist/AdfWalker.d.ts +39 -0
  13. package/dist/AdfWalker.d.ts.map +1 -0
  14. package/dist/AdfWalker.js +527 -0
  15. package/dist/AdfWalker.js.map +1 -0
  16. package/dist/AtlaskitTransformers.d.ts +35 -0
  17. package/dist/AtlaskitTransformers.d.ts.map +1 -0
  18. package/dist/AtlaskitTransformers.js +48 -0
  19. package/dist/AtlaskitTransformers.js.map +1 -0
  20. package/dist/Brand.d.ts +6 -6
  21. package/dist/Brand.d.ts.map +1 -1
  22. package/dist/Brand.js +8 -6
  23. package/dist/Brand.js.map +1 -1
  24. package/dist/ConfluenceAuth.d.ts +4 -4
  25. package/dist/ConfluenceAuth.d.ts.map +1 -1
  26. package/dist/ConfluenceAuth.js +37 -39
  27. package/dist/ConfluenceAuth.js.map +1 -1
  28. package/dist/ConfluenceClient.d.ts +7 -17
  29. package/dist/ConfluenceClient.d.ts.map +1 -1
  30. package/dist/ConfluenceClient.js +81 -38
  31. package/dist/ConfluenceClient.js.map +1 -1
  32. package/dist/ConfluenceConfig.d.ts +3 -3
  33. package/dist/ConfluenceConfig.d.ts.map +1 -1
  34. package/dist/ConfluenceConfig.js +13 -11
  35. package/dist/ConfluenceConfig.js.map +1 -1
  36. package/dist/ConfluenceError.d.ts +68 -16
  37. package/dist/ConfluenceError.d.ts.map +1 -1
  38. package/dist/ConfluenceError.js +30 -1
  39. package/dist/ConfluenceError.js.map +1 -1
  40. package/dist/GitError.d.ts +5 -5
  41. package/dist/GitService.d.ts +11 -3
  42. package/dist/GitService.d.ts.map +1 -1
  43. package/dist/GitService.js +22 -27
  44. package/dist/GitService.js.map +1 -1
  45. package/dist/LocalFileSystem.d.ts +3 -3
  46. package/dist/LocalFileSystem.d.ts.map +1 -1
  47. package/dist/LocalFileSystem.js +6 -6
  48. package/dist/LocalFileSystem.js.map +1 -1
  49. package/dist/MarkdownConverter.d.ts +16 -65
  50. package/dist/MarkdownConverter.d.ts.map +1 -1
  51. package/dist/MarkdownConverter.js +64 -85
  52. package/dist/MarkdownConverter.js.map +1 -1
  53. package/dist/Schemas.d.ts +128 -141
  54. package/dist/Schemas.d.ts.map +1 -1
  55. package/dist/Schemas.js +21 -23
  56. package/dist/Schemas.js.map +1 -1
  57. package/dist/SyncEngine.d.ts +8 -5
  58. package/dist/SyncEngine.d.ts.map +1 -1
  59. package/dist/SyncEngine.js +189 -113
  60. package/dist/SyncEngine.js.map +1 -1
  61. package/dist/bin.js +23 -35
  62. package/dist/bin.js.map +1 -1
  63. package/dist/commands/auth.d.ts +2 -14
  64. package/dist/commands/auth.d.ts.map +1 -1
  65. package/dist/commands/auth.js +11 -16
  66. package/dist/commands/auth.js.map +1 -1
  67. package/dist/commands/clone.d.ts +4 -6
  68. package/dist/commands/clone.d.ts.map +1 -1
  69. package/dist/commands/clone.js +34 -32
  70. package/dist/commands/clone.js.map +1 -1
  71. package/dist/commands/delete.d.ts +2 -10
  72. package/dist/commands/delete.d.ts.map +1 -1
  73. package/dist/commands/delete.js +5 -4
  74. package/dist/commands/delete.js.map +1 -1
  75. package/dist/commands/errorHandler.d.ts +2 -1
  76. package/dist/commands/errorHandler.d.ts.map +1 -1
  77. package/dist/commands/errorHandler.js +22 -15
  78. package/dist/commands/errorHandler.js.map +1 -1
  79. package/dist/commands/fetch.d.ts +27 -0
  80. package/dist/commands/fetch.d.ts.map +1 -0
  81. package/dist/commands/fetch.js +48 -0
  82. package/dist/commands/fetch.js.map +1 -0
  83. package/dist/commands/git.d.ts +7 -10
  84. package/dist/commands/git.d.ts.map +1 -1
  85. package/dist/commands/git.js +6 -6
  86. package/dist/commands/git.js.map +1 -1
  87. package/dist/commands/index.d.ts +1 -0
  88. package/dist/commands/index.d.ts.map +1 -1
  89. package/dist/commands/index.js +1 -0
  90. package/dist/commands/index.js.map +1 -1
  91. package/dist/commands/layers.d.ts +10 -9
  92. package/dist/commands/layers.d.ts.map +1 -1
  93. package/dist/commands/layers.js +41 -30
  94. package/dist/commands/layers.js.map +1 -1
  95. package/dist/commands/new.d.ts +2 -6
  96. package/dist/commands/new.d.ts.map +1 -1
  97. package/dist/commands/new.js +5 -4
  98. package/dist/commands/new.js.map +1 -1
  99. package/dist/commands/pageInput.d.ts +19 -0
  100. package/dist/commands/pageInput.d.ts.map +1 -0
  101. package/dist/commands/pageInput.js +68 -0
  102. package/dist/commands/pageInput.js.map +1 -0
  103. package/dist/commands/root.d.ts +8 -0
  104. package/dist/commands/root.d.ts.map +1 -0
  105. package/dist/commands/root.js +29 -0
  106. package/dist/commands/root.js.map +1 -0
  107. package/dist/commands/sync.d.ts +6 -9
  108. package/dist/commands/sync.d.ts.map +1 -1
  109. package/dist/commands/sync.js +5 -6
  110. package/dist/commands/sync.js.map +1 -1
  111. package/dist/index.d.ts +3 -8
  112. package/dist/index.d.ts.map +1 -1
  113. package/dist/index.js +2 -13
  114. package/dist/index.js.map +1 -1
  115. package/dist/internal/NodeLayers.d.ts.map +1 -1
  116. package/dist/internal/NodeLayers.js +1 -2
  117. package/dist/internal/NodeLayers.js.map +1 -1
  118. package/dist/internal/adfMetadata.d.ts +30 -0
  119. package/dist/internal/adfMetadata.d.ts.map +1 -0
  120. package/dist/internal/adfMetadata.js +126 -0
  121. package/dist/internal/adfMetadata.js.map +1 -0
  122. package/dist/internal/cleanMarkdown.d.ts +5 -0
  123. package/dist/internal/cleanMarkdown.d.ts.map +1 -0
  124. package/dist/internal/cleanMarkdown.js +13 -0
  125. package/dist/internal/cleanMarkdown.js.map +1 -0
  126. package/dist/internal/frontmatter.d.ts.map +1 -1
  127. package/dist/internal/frontmatter.js +41 -8
  128. package/dist/internal/frontmatter.js.map +1 -1
  129. package/dist/internal/gitCommands.d.ts +9 -3
  130. package/dist/internal/gitCommands.d.ts.map +1 -1
  131. package/dist/internal/gitCommands.js +18 -9
  132. package/dist/internal/gitCommands.js.map +1 -1
  133. package/dist/internal/hashUtils.d.ts +1 -1
  134. package/dist/internal/hashUtils.d.ts.map +1 -1
  135. package/dist/internal/hashUtils.js +1 -1
  136. package/dist/internal/hashUtils.js.map +1 -1
  137. package/dist/internal/oauthServer.d.ts +10 -5
  138. package/dist/internal/oauthServer.d.ts.map +1 -1
  139. package/dist/internal/oauthServer.js +19 -40
  140. package/dist/internal/oauthServer.js.map +1 -1
  141. package/dist/internal/pathUtils.d.ts +1 -1
  142. package/dist/internal/pathUtils.d.ts.map +1 -1
  143. package/dist/internal/pathUtils.js +1 -1
  144. package/dist/internal/pathUtils.js.map +1 -1
  145. package/dist/internal/process.d.ts +15 -0
  146. package/dist/internal/process.d.ts.map +1 -0
  147. package/dist/internal/process.js +10 -0
  148. package/dist/internal/process.js.map +1 -0
  149. package/dist/internal/stdio.d.ts +6 -0
  150. package/dist/internal/stdio.d.ts.map +1 -0
  151. package/dist/internal/stdio.js +15 -0
  152. package/dist/internal/stdio.js.map +1 -0
  153. package/dist/internal/tokenStorage.d.ts +3 -13
  154. package/dist/internal/tokenStorage.d.ts.map +1 -1
  155. package/dist/internal/tokenStorage.js +26 -24
  156. package/dist/internal/tokenStorage.js.map +1 -1
  157. package/dist/internal/userCache.d.ts +1 -1
  158. package/dist/internal/userCache.d.ts.map +1 -1
  159. package/dist/internal/userCache.js +1 -1
  160. package/dist/internal/userCache.js.map +1 -1
  161. package/package.json +30 -30
  162. package/skills/confluence/SKILL.md +143 -0
  163. package/skills/confluence/agents/openai.yaml +4 -0
  164. package/src/AdfPlaceholders.ts +310 -13
  165. package/src/AdfSchemaValidator.ts +2 -4
  166. package/src/AdfWalker.ts +122 -42
  167. package/src/AtlaskitTransformers.ts +2 -4
  168. package/src/Brand.ts +11 -16
  169. package/src/ConfluenceAuth.ts +22 -30
  170. package/src/ConfluenceClient.ts +24 -20
  171. package/src/ConfluenceConfig.ts +14 -14
  172. package/src/GitService.ts +39 -49
  173. package/src/LocalFileSystem.ts +7 -9
  174. package/src/MarkdownConverter.ts +2 -4
  175. package/src/Schemas.ts +13 -12
  176. package/src/SyncEngine.ts +151 -53
  177. package/src/bin.ts +30 -56
  178. package/src/commands/auth.ts +21 -18
  179. package/src/commands/clone.ts +38 -37
  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 +53 -33
  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/internal/NodeLayers.ts +1 -2
  191. package/src/internal/adfMetadata.ts +145 -0
  192. package/src/internal/cleanMarkdown.ts +15 -0
  193. package/src/internal/frontmatter.ts +45 -8
  194. package/src/internal/gitCommands.ts +23 -17
  195. package/src/internal/hashUtils.ts +2 -2
  196. package/src/internal/oauthServer.ts +84 -105
  197. package/src/internal/pathUtils.ts +1 -1
  198. package/src/internal/process.ts +15 -0
  199. package/src/internal/stdio.ts +22 -0
  200. package/src/internal/tokenStorage.ts +39 -29
  201. package/src/internal/userCache.ts +2 -2
  202. package/test/AdfPlaceholders.test.ts +213 -0
  203. package/test/AdfSchemaValidator.test.ts +6 -6
  204. package/test/AdfWalker.test.ts +167 -21
  205. package/test/AtlaskitTransformers.test.ts +4 -4
  206. package/test/Brand.test.ts +11 -11
  207. package/test/GitService.test.ts +6 -2
  208. package/test/MarkdownConverter.test.ts +12 -11
  209. package/test/RoundTrip.test.ts +258 -3
  210. package/test/Schemas.test.ts +40 -40
  211. package/test/adfMetadata.test.ts +110 -0
  212. package/test/cleanMarkdown.test.ts +36 -0
  213. package/test/commandHarness.test.ts +79 -0
  214. package/test/commandHarness.ts +147 -0
  215. package/test/fetch.test.ts +61 -0
  216. package/test/frontmatter.test.ts +41 -0
  217. package/test/integration.test.ts +569 -156
  218. package/test/layers.test.ts +12 -0
  219. package/test/oauthServer.test.ts +4 -5
  220. package/test/pageInput.test.ts +83 -0
  221. package/test/tokenStorage.test.ts +17 -17
  222. package/dist/SchemaConverterError.d.ts +0 -108
  223. package/dist/SchemaConverterError.d.ts.map +0 -1
  224. package/dist/SchemaConverterError.js +0 -84
  225. package/dist/SchemaConverterError.js.map +0 -1
  226. package/dist/ast/BlockNode.d.ts +0 -468
  227. package/dist/ast/BlockNode.d.ts.map +0 -1
  228. package/dist/ast/BlockNode.js +0 -319
  229. package/dist/ast/BlockNode.js.map +0 -1
  230. package/dist/ast/Document.d.ts +0 -244
  231. package/dist/ast/Document.d.ts.map +0 -1
  232. package/dist/ast/Document.js +0 -69
  233. package/dist/ast/Document.js.map +0 -1
  234. package/dist/ast/InlineNode.d.ts +0 -477
  235. package/dist/ast/InlineNode.d.ts.map +0 -1
  236. package/dist/ast/InlineNode.js +0 -263
  237. package/dist/ast/InlineNode.js.map +0 -1
  238. package/dist/ast/MacroNode.d.ts +0 -267
  239. package/dist/ast/MacroNode.d.ts.map +0 -1
  240. package/dist/ast/MacroNode.js +0 -164
  241. package/dist/ast/MacroNode.js.map +0 -1
  242. package/dist/ast/index.d.ts +0 -10
  243. package/dist/ast/index.d.ts.map +0 -1
  244. package/dist/ast/index.js +0 -14
  245. package/dist/ast/index.js.map +0 -1
  246. package/dist/parsers/ConfluenceParser.d.ts +0 -26
  247. package/dist/parsers/ConfluenceParser.d.ts.map +0 -1
  248. package/dist/parsers/ConfluenceParser.js +0 -792
  249. package/dist/parsers/ConfluenceParser.js.map +0 -1
  250. package/dist/parsers/MarkdownParser.d.ts +0 -26
  251. package/dist/parsers/MarkdownParser.d.ts.map +0 -1
  252. package/dist/parsers/MarkdownParser.js +0 -873
  253. package/dist/parsers/MarkdownParser.js.map +0 -1
  254. package/dist/parsers/index.d.ts +0 -8
  255. package/dist/parsers/index.d.ts.map +0 -1
  256. package/dist/parsers/index.js +0 -8
  257. package/dist/parsers/index.js.map +0 -1
  258. package/dist/parsers/preprocessing/ConfluencePreprocessing.d.ts +0 -23
  259. package/dist/parsers/preprocessing/ConfluencePreprocessing.d.ts.map +0 -1
  260. package/dist/parsers/preprocessing/ConfluencePreprocessing.js +0 -323
  261. package/dist/parsers/preprocessing/ConfluencePreprocessing.js.map +0 -1
  262. package/dist/parsers/preprocessing/index.d.ts +0 -7
  263. package/dist/parsers/preprocessing/index.d.ts.map +0 -1
  264. package/dist/parsers/preprocessing/index.js +0 -7
  265. package/dist/parsers/preprocessing/index.js.map +0 -1
  266. package/dist/schemas/ConfluenceSchema.d.ts +0 -21
  267. package/dist/schemas/ConfluenceSchema.d.ts.map +0 -1
  268. package/dist/schemas/ConfluenceSchema.js +0 -38
  269. package/dist/schemas/ConfluenceSchema.js.map +0 -1
  270. package/dist/schemas/ConversionSchema.d.ts +0 -35
  271. package/dist/schemas/ConversionSchema.d.ts.map +0 -1
  272. package/dist/schemas/ConversionSchema.js +0 -208
  273. package/dist/schemas/ConversionSchema.js.map +0 -1
  274. package/dist/schemas/MarkdownSchema.d.ts +0 -21
  275. package/dist/schemas/MarkdownSchema.d.ts.map +0 -1
  276. package/dist/schemas/MarkdownSchema.js +0 -38
  277. package/dist/schemas/MarkdownSchema.js.map +0 -1
  278. package/dist/schemas/hast/HastFromHtml.d.ts +0 -27
  279. package/dist/schemas/hast/HastFromHtml.d.ts.map +0 -1
  280. package/dist/schemas/hast/HastFromHtml.js +0 -107
  281. package/dist/schemas/hast/HastFromHtml.js.map +0 -1
  282. package/dist/schemas/hast/HastSchema.d.ts +0 -195
  283. package/dist/schemas/hast/HastSchema.d.ts.map +0 -1
  284. package/dist/schemas/hast/HastSchema.js +0 -183
  285. package/dist/schemas/hast/HastSchema.js.map +0 -1
  286. package/dist/schemas/hast/index.d.ts +0 -9
  287. package/dist/schemas/hast/index.d.ts.map +0 -1
  288. package/dist/schemas/hast/index.js +0 -3
  289. package/dist/schemas/hast/index.js.map +0 -1
  290. package/dist/schemas/index.d.ts +0 -14
  291. package/dist/schemas/index.d.ts.map +0 -1
  292. package/dist/schemas/index.js +0 -16
  293. package/dist/schemas/index.js.map +0 -1
  294. package/dist/schemas/mdast/MdastFromMarkdown.d.ts +0 -30
  295. package/dist/schemas/mdast/MdastFromMarkdown.d.ts.map +0 -1
  296. package/dist/schemas/mdast/MdastFromMarkdown.js +0 -79
  297. package/dist/schemas/mdast/MdastFromMarkdown.js.map +0 -1
  298. package/dist/schemas/mdast/MdastSchema.d.ts +0 -385
  299. package/dist/schemas/mdast/MdastSchema.d.ts.map +0 -1
  300. package/dist/schemas/mdast/MdastSchema.js +0 -266
  301. package/dist/schemas/mdast/MdastSchema.js.map +0 -1
  302. package/dist/schemas/mdast/index.d.ts +0 -10
  303. package/dist/schemas/mdast/index.d.ts.map +0 -1
  304. package/dist/schemas/mdast/index.js +0 -4
  305. package/dist/schemas/mdast/index.js.map +0 -1
  306. package/dist/schemas/mdast/mdastToString.d.ts +0 -13
  307. package/dist/schemas/mdast/mdastToString.d.ts.map +0 -1
  308. package/dist/schemas/mdast/mdastToString.js +0 -85
  309. package/dist/schemas/mdast/mdastToString.js.map +0 -1
  310. package/dist/schemas/nodes/block/BlockSchema.d.ts +0 -43
  311. package/dist/schemas/nodes/block/BlockSchema.d.ts.map +0 -1
  312. package/dist/schemas/nodes/block/BlockSchema.js +0 -634
  313. package/dist/schemas/nodes/block/BlockSchema.js.map +0 -1
  314. package/dist/schemas/nodes/block/index.d.ts +0 -7
  315. package/dist/schemas/nodes/block/index.d.ts.map +0 -1
  316. package/dist/schemas/nodes/block/index.js +0 -7
  317. package/dist/schemas/nodes/block/index.js.map +0 -1
  318. package/dist/schemas/nodes/index.d.ts +0 -9
  319. package/dist/schemas/nodes/index.d.ts.map +0 -1
  320. package/dist/schemas/nodes/index.js +0 -12
  321. package/dist/schemas/nodes/index.js.map +0 -1
  322. package/dist/schemas/nodes/inline/InlineSchema.d.ts +0 -48
  323. package/dist/schemas/nodes/inline/InlineSchema.d.ts.map +0 -1
  324. package/dist/schemas/nodes/inline/InlineSchema.js +0 -436
  325. package/dist/schemas/nodes/inline/InlineSchema.js.map +0 -1
  326. package/dist/schemas/nodes/inline/index.d.ts +0 -7
  327. package/dist/schemas/nodes/inline/index.d.ts.map +0 -1
  328. package/dist/schemas/nodes/inline/index.js +0 -7
  329. package/dist/schemas/nodes/inline/index.js.map +0 -1
  330. package/dist/schemas/nodes/macro/MacroSchema.d.ts +0 -27
  331. package/dist/schemas/nodes/macro/MacroSchema.d.ts.map +0 -1
  332. package/dist/schemas/nodes/macro/MacroSchema.js +0 -162
  333. package/dist/schemas/nodes/macro/MacroSchema.js.map +0 -1
  334. package/dist/schemas/nodes/macro/index.d.ts +0 -7
  335. package/dist/schemas/nodes/macro/index.d.ts.map +0 -1
  336. package/dist/schemas/nodes/macro/index.js +0 -7
  337. package/dist/schemas/nodes/macro/index.js.map +0 -1
  338. package/dist/schemas/preprocessing/ConfluencePreprocessor.d.ts +0 -53
  339. package/dist/schemas/preprocessing/ConfluencePreprocessor.d.ts.map +0 -1
  340. package/dist/schemas/preprocessing/ConfluencePreprocessor.js +0 -349
  341. package/dist/schemas/preprocessing/ConfluencePreprocessor.js.map +0 -1
  342. package/dist/schemas/preprocessing/index.d.ts +0 -8
  343. package/dist/schemas/preprocessing/index.d.ts.map +0 -1
  344. package/dist/schemas/preprocessing/index.js +0 -2
  345. package/dist/schemas/preprocessing/index.js.map +0 -1
  346. package/dist/serializers/ConfluenceSerializer.d.ts +0 -30
  347. package/dist/serializers/ConfluenceSerializer.d.ts.map +0 -1
  348. package/dist/serializers/ConfluenceSerializer.js +0 -551
  349. package/dist/serializers/ConfluenceSerializer.js.map +0 -1
  350. package/dist/serializers/MarkdownSerializer.d.ts +0 -34
  351. package/dist/serializers/MarkdownSerializer.d.ts.map +0 -1
  352. package/dist/serializers/MarkdownSerializer.js +0 -355
  353. package/dist/serializers/MarkdownSerializer.js.map +0 -1
  354. package/dist/serializers/index.d.ts +0 -8
  355. package/dist/serializers/index.d.ts.map +0 -1
  356. package/dist/serializers/index.js +0 -8
  357. package/dist/serializers/index.js.map +0 -1
@@ -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
+ })