@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
@@ -3,11 +3,11 @@
3
3
  *
4
4
  * @module
5
5
  */
6
- import * as FileSystem from "@effect/platform/FileSystem"
7
- import * as Path from "@effect/platform/Path"
8
6
  import * as Context from "effect/Context"
9
7
  import * as Effect from "effect/Effect"
8
+ import * as FileSystem from "effect/FileSystem"
10
9
  import * as Layer from "effect/Layer"
10
+ import * as Path from "effect/Path"
11
11
  import type { ContentHash } from "./Brand.js"
12
12
  import type { FrontMatterError } from "./ConfluenceError.js"
13
13
  import { FileSystemError } from "./ConfluenceError.js"
@@ -59,9 +59,7 @@ export interface PageTreeNode {
59
59
  *
60
60
  * @category FileSystem
61
61
  */
62
- export class LocalFileSystem extends Context.Tag(
63
- "@knpkv/confluence-to-markdown/LocalFileSystem"
64
- )<
62
+ export class LocalFileSystem extends Context.Service<
65
63
  LocalFileSystem,
66
64
  {
67
65
  /**
@@ -135,7 +133,7 @@ export class LocalFileSystem extends Context.Tag(
135
133
  content: string
136
134
  ) => Effect.Effect<void, FileSystemError>
137
135
  }
138
- >() {}
136
+ >()("@knpkv/confluence-to-markdown/LocalFileSystem") {}
139
137
 
140
138
  /**
141
139
  * Layer that provides LocalFileSystem.
@@ -180,7 +178,7 @@ export const layer: Layer.Layer<LocalFileSystem, never, FileSystem.FileSystem |
180
178
  Effect.gen(function*() {
181
179
  const dir = pathService.dirname(filePath)
182
180
  yield* fs.makeDirectory(dir, { recursive: true }).pipe(
183
- Effect.catchAll(() => Effect.void)
181
+ Effect.mapError((cause) => new FileSystemError({ operation: "mkdir", path: dir, cause }))
184
182
  )
185
183
 
186
184
  const serialized = serializeMarkdown(frontMatter, content)
@@ -257,7 +255,7 @@ export const layer: Layer.Layer<LocalFileSystem, never, FileSystem.FileSystem |
257
255
  Effect.gen(function*() {
258
256
  const dir = pathService.dirname(filePath)
259
257
  yield* fs.makeDirectory(dir, { recursive: true }).pipe(
260
- Effect.catchAll(() => Effect.void)
258
+ Effect.mapError((cause) => new FileSystemError({ operation: "mkdir", path: dir, cause }))
261
259
  )
262
260
 
263
261
  // Atomic write: write to temp file, then rename
@@ -289,7 +287,7 @@ export const layer: Layer.Layer<LocalFileSystem, never, FileSystem.FileSystem |
289
287
  Effect.gen(function*() {
290
288
  const dir = pathService.dirname(filePath)
291
289
  yield* fs.makeDirectory(dir, { recursive: true }).pipe(
292
- Effect.catchAll(() => Effect.void)
290
+ Effect.mapError((cause) => new FileSystemError({ operation: "mkdir", path: dir, cause }))
293
291
  )
294
292
 
295
293
  const serialized = serializeNewPageMarkdown(frontMatter, content)
@@ -39,9 +39,7 @@ import { ConversionError } from "./ConfluenceError.js"
39
39
  *
40
40
  * @category Conversion
41
41
  */
42
- export class MarkdownConverter extends Context.Tag(
43
- "@knpkv/confluence-to-markdown/MarkdownConverter"
44
- )<
42
+ export class MarkdownConverter extends Context.Service<
45
43
  MarkdownConverter,
46
44
  {
47
45
  /**
@@ -59,7 +57,7 @@ export class MarkdownConverter extends Context.Tag(
59
57
  */
60
58
  readonly markdownToAdf: (markdown: string) => Effect.Effect<string, ConversionError>
61
59
  }
62
- >() {}
60
+ >()("@knpkv/confluence-to-markdown/MarkdownConverter") {}
63
61
 
64
62
  const warningSummary = (w: WalkerWarning): string => {
65
63
  switch (w._tag) {
package/src/Schemas.ts CHANGED
@@ -3,6 +3,7 @@
3
3
  *
4
4
  * @module
5
5
  */
6
+ import * as Effect from "effect/Effect"
6
7
  import * as Schema from "effect/Schema"
7
8
  import { ContentHashSchema, PageIdSchema, SpaceKeySchema } from "./Brand.js"
8
9
 
@@ -27,18 +28,18 @@ export const ConfluenceConfigFileSchema = Schema.Struct({
27
28
  rootPageId: PageIdSchema,
28
29
  /** Confluence Cloud base URL */
29
30
  baseUrl: Schema.String.pipe(
30
- Schema.pattern(/^https:\/\/[a-z0-9-]+\.atlassian\.net$/)
31
+ Schema.check(Schema.isPattern(/^https:\/\/[a-z0-9-]+\.atlassian\.net$/))
31
32
  ),
32
33
  /** Optional space key */
33
34
  spaceKey: Schema.optional(SpaceKeySchema),
34
35
  /** Local docs path (default: .confluence/docs) */
35
- docsPath: Schema.optionalWith(Schema.String, { default: () => ".confluence/docs" }),
36
+ docsPath: Schema.String.pipe(Schema.withDecodingDefaultTypeKey(Effect.succeed(".confluence/docs"))),
36
37
  /** Glob patterns to exclude from sync */
37
- excludePatterns: Schema.optionalWith(Schema.Array(Schema.String), { default: () => [] }),
38
+ excludePatterns: Schema.Array(Schema.String).pipe(Schema.withDecodingDefaultTypeKey(Effect.succeed([]))),
38
39
  /** Save original Confluence HTML alongside markdown (default: false) */
39
- saveSource: Schema.optionalWith(Schema.Boolean, { default: () => false }),
40
+ saveSource: Schema.Boolean.pipe(Schema.withDecodingDefaultTypeKey(Effect.succeed(false))),
40
41
  /** Glob patterns for files to track in git */
41
- trackedPaths: Schema.optionalWith(Schema.Array(Schema.String), { default: () => ["**/*.md"] })
42
+ trackedPaths: Schema.Array(Schema.String).pipe(Schema.withDecodingDefaultTypeKey(Effect.succeed(["**/*.md"])))
42
43
  })
43
44
 
44
45
  /**
@@ -71,9 +72,9 @@ export const PageFrontMatterSchema = Schema.Struct({
71
72
  /** Confluence page ID */
72
73
  pageId: PageIdSchema,
73
74
  /** Page version number */
74
- version: Schema.Number.pipe(Schema.int(), Schema.positive()),
75
+ version: Schema.Int.pipe(Schema.check(Schema.isGreaterThan(0))),
75
76
  /** Page title */
76
- title: Schema.String.pipe(Schema.nonEmptyString()),
77
+ title: Schema.NonEmptyString,
77
78
  /** Last updated timestamp (ISO8601) */
78
79
  updated: Schema.DateFromString,
79
80
  /** Parent page ID (optional) */
@@ -104,7 +105,7 @@ export type PageFrontMatter = Schema.Schema.Type<typeof PageFrontMatterSchema>
104
105
  */
105
106
  export const NewPageFrontMatterSchema = Schema.Struct({
106
107
  /** Page title */
107
- title: Schema.String.pipe(Schema.nonEmptyString()),
108
+ title: Schema.NonEmptyString,
108
109
  /** Parent page ID (optional, determined by directory structure) */
109
110
  parentId: Schema.optional(PageIdSchema)
110
111
  })
@@ -125,9 +126,9 @@ export const SyncStateSchema = Schema.Struct({
125
126
  /** Last sync timestamp */
126
127
  lastSync: Schema.DateFromString,
127
128
  /** Map of page ID to sync info */
128
- pages: Schema.Record({
129
- key: Schema.String,
130
- value: Schema.Struct({
129
+ pages: Schema.Record(
130
+ Schema.String,
131
+ Schema.Struct({
131
132
  /** Local file path */
132
133
  localPath: Schema.String,
133
134
  /** Last synced version */
@@ -135,7 +136,7 @@ export const SyncStateSchema = Schema.Struct({
135
136
  /** Content hash at last sync */
136
137
  contentHash: ContentHashSchema
137
138
  })
138
- })
139
+ )
139
140
  })
140
141
 
141
142
  /**
package/src/SyncEngine.ts CHANGED
@@ -3,10 +3,15 @@
3
3
  *
4
4
  * @module
5
5
  */
6
- import * as Path from "@effect/platform/Path"
7
6
  import * as Context from "effect/Context"
8
7
  import * as Effect from "effect/Effect"
8
+ import * as FileSystem from "effect/FileSystem"
9
9
  import * as Layer from "effect/Layer"
10
+ import * as Path from "effect/Path"
11
+ import type * as PlatformError from "effect/PlatformError"
12
+ import * as Result from "effect/Result"
13
+ import * as Schema from "effect/Schema"
14
+ import type * as Terminal from "effect/Terminal"
10
15
  import { PageId } from "./Brand.js"
11
16
  import { ConfluenceClient } from "./ConfluenceClient.js"
12
17
  import { ConfluenceConfig } from "./ConfluenceConfig.js"
@@ -14,6 +19,14 @@ import type { ApiError, ConversionError, FrontMatterError, RateLimitError } from
14
19
  import { FileSystemError, StructureError } from "./ConfluenceError.js"
15
20
  import type { GitServiceError } from "./GitService.js"
16
21
  import { GitService } from "./GitService.js"
22
+ import type { AdfMetadataSidecar } from "./internal/adfMetadata.js"
23
+ import {
24
+ AdfMetadataSidecarSchema,
25
+ collectAdfMetadataHrefs,
26
+ externalizeAdfMetadata,
27
+ hydrateAdfMetadata
28
+ } from "./internal/adfMetadata.js"
29
+ import { parseMarkdown } from "./internal/frontmatter.js"
17
30
  import { computeHash, HashServiceLive } from "./internal/hashUtils.js"
18
31
  import { UserCache } from "./internal/userCache.js"
19
32
  import { LocalFileSystem } from "./LocalFileSystem.js"
@@ -40,7 +53,11 @@ export type SyncStatus =
40
53
  /**
41
54
  * Progress callback for version replay.
42
55
  */
43
- export type ProgressCallback = (current: number, total: number, message: string) => void
56
+ export type ProgressCallback = (
57
+ current: number,
58
+ total: number,
59
+ message: string
60
+ ) => Effect.Effect<void, PlatformError.PlatformError, Terminal.Terminal>
44
61
 
45
62
  /**
46
63
  * Options for pull operation.
@@ -138,15 +155,15 @@ const hashBody = (markdown: string) => computeHash(markdown.trim()).pipe(Effect.
138
155
  *
139
156
  * @category Sync
140
157
  */
141
- export class SyncEngine extends Context.Tag(
142
- "@knpkv/confluence-to-markdown/SyncEngine"
143
- )<
158
+ export class SyncEngine extends Context.Service<
144
159
  SyncEngine,
145
160
  {
146
161
  /**
147
162
  * Pull pages from Confluence to local markdown.
148
163
  */
149
- readonly pull: (options: PullOptions) => Effect.Effect<PullResult, SyncError>
164
+ readonly pull: (
165
+ options: PullOptions
166
+ ) => Effect.Effect<PullResult, SyncError | PlatformError.PlatformError, Terminal.Terminal>
150
167
 
151
168
  /**
152
169
  * Push local markdown changes to Confluence.
@@ -158,7 +175,7 @@ export class SyncEngine extends Context.Tag(
158
175
  */
159
176
  readonly status: () => Effect.Effect<StatusResult, SyncError>
160
177
  }
161
- >() {}
178
+ >()("@knpkv/confluence-to-markdown/SyncEngine") {}
162
179
 
163
180
  /**
164
181
  * Layer that provides SyncEngine.
@@ -168,7 +185,14 @@ export class SyncEngine extends Context.Tag(
168
185
  export const layer: Layer.Layer<
169
186
  SyncEngine,
170
187
  never,
171
- ConfluenceClient | ConfluenceConfig | MarkdownConverter | LocalFileSystem | Path.Path | GitService | UserCache
188
+ | ConfluenceClient
189
+ | ConfluenceConfig
190
+ | MarkdownConverter
191
+ | LocalFileSystem
192
+ | Path.Path
193
+ | GitService
194
+ | UserCache
195
+ | FileSystem.FileSystem
172
196
  > = Layer.effect(
173
197
  SyncEngine,
174
198
  Effect.gen(function*() {
@@ -177,10 +201,69 @@ export const layer: Layer.Layer<
177
201
  const converter = yield* MarkdownConverter
178
202
  const localFs = yield* LocalFileSystem
179
203
  const pathService = yield* Path.Path
204
+ const fs = yield* FileSystem.FileSystem
180
205
  const git = yield* GitService
181
206
  const userCache = yield* UserCache
182
207
 
183
- const docsPath = pathService.join(process.cwd(), config.docsPath)
208
+ const cwd = pathService.resolve(".")
209
+ const docsPath = pathService.join(cwd, config.docsPath)
210
+
211
+ const adfMetadataPath = (filePath: string, pageId?: string): string =>
212
+ pageId
213
+ ? pathService.join(pathService.dirname(filePath), `${pageId}.adf.json`)
214
+ : filePath.replace(/\.md$/, ".adf.json")
215
+
216
+ const adfMetadataHref = (filePath: string, pageId?: string): string =>
217
+ `./${pathService.basename(adfMetadataPath(filePath, pageId))}`
218
+
219
+ const prepareMarkdownForFile = (filePath: string, markdown: string, pageId?: string) =>
220
+ externalizeAdfMetadata(markdown, adfMetadataHref(filePath, pageId))
221
+
222
+ const writePreparedMarkdownWithAdfMetadata = (
223
+ filePath: string,
224
+ frontMatter: PageFrontMatter,
225
+ prepared: ReturnType<typeof externalizeAdfMetadata>
226
+ ): Effect.Effect<void, FileSystemError> =>
227
+ Effect.gen(function*() {
228
+ yield* localFs.writeMarkdownFile(filePath, frontMatter, prepared.markdown)
229
+ const sidecarPath = adfMetadataPath(filePath, frontMatter.pageId)
230
+ if (prepared.sidecar !== null) {
231
+ yield* localFs.writeFile(
232
+ sidecarPath,
233
+ JSON.stringify(prepared.sidecar, null, 2) + "\n"
234
+ )
235
+ } else if (yield* localFs.exists(sidecarPath)) {
236
+ yield* localFs.deleteFile(sidecarPath)
237
+ }
238
+ })
239
+
240
+ const readAdfMetadataSidecar = (sidecarPath: string): Effect.Effect<AdfMetadataSidecar | null, FileSystemError> =>
241
+ Effect.gen(function*() {
242
+ const exists = yield* localFs.exists(sidecarPath)
243
+ if (!exists) return null
244
+ const raw = yield* fs.readFileString(sidecarPath).pipe(
245
+ Effect.mapError((cause) => new FileSystemError({ operation: "read", path: sidecarPath, cause }))
246
+ )
247
+ const parsed = yield* Effect.try({
248
+ try: () => JSON.parse(raw) as unknown,
249
+ catch: (cause) => new FileSystemError({ operation: "read", path: sidecarPath, cause })
250
+ })
251
+ return yield* Schema.decodeUnknownEffect(AdfMetadataSidecarSchema)(parsed).pipe(
252
+ Effect.mapError((cause) => new FileSystemError({ operation: "read", path: sidecarPath, cause }))
253
+ )
254
+ })
255
+
256
+ const hydrateMarkdownForFile = (filePath: string, markdown: string): Effect.Effect<string, FileSystemError> =>
257
+ Effect.gen(function*() {
258
+ const sidecars = new Map<string, AdfMetadataSidecar>()
259
+ const dir = pathService.dirname(filePath)
260
+ for (const href of collectAdfMetadataHrefs(markdown)) {
261
+ const sidecarPath = pathService.resolve(dir, href)
262
+ const sidecar = yield* readAdfMetadataSidecar(sidecarPath)
263
+ if (sidecar !== null) sidecars.set(href, sidecar)
264
+ }
265
+ return hydrateAdfMetadata(markdown, sidecars)
266
+ })
184
267
 
185
268
  /**
186
269
  * Build a map of relative path (without .md) to pageId for resolving parents.
@@ -340,22 +423,29 @@ export const layer: Layer.Layer<
340
423
  */
341
424
  const getUser = (accountId: string): Effect.Effect<AtlassianUser | undefined, ApiError | RateLimitError> =>
342
425
  userCache.getOrFetch(accountId, client.getUser).pipe(
343
- Effect.catchAll(() => Effect.succeed(undefined))
426
+ Effect.catchCause(() => Effect.succeed(undefined))
344
427
  )
345
428
 
346
429
  /**
347
430
  * Convert version content to markdown and front-matter.
348
431
  */
349
432
  const versionToMarkdown = (
433
+ filePath: string,
350
434
  pageId: PageId,
351
435
  version: PageVersionContent,
352
436
  title: string,
353
437
  parentId?: string,
354
438
  position?: number
355
- ): Effect.Effect<{ markdown: string; frontMatter: PageFrontMatter }, SyncError> =>
439
+ ): Effect.Effect<{
440
+ markdown: string
441
+ frontMatter: PageFrontMatter
442
+ prepared: ReturnType<typeof externalizeAdfMetadata>
443
+ }, SyncError> =>
356
444
  Effect.gen(function*() {
357
445
  const adfJson = version.body?.atlas_doc_format?.value ?? ""
358
- const markdown = yield* converter.adfToMarkdown(adfJson)
446
+ const rawMarkdown = yield* converter.adfToMarkdown(adfJson)
447
+ const prepared = prepareMarkdownForFile(filePath, rawMarkdown, pageId)
448
+ const { markdown } = prepared
359
449
  const contentHash = yield* hashBody(markdown)
360
450
 
361
451
  // Get author info
@@ -374,7 +464,7 @@ export const layer: Layer.Layer<
374
464
  ...(author?.email ? { authorEmail: author.email } : {})
375
465
  }
376
466
 
377
- return { markdown, frontMatter }
467
+ return { markdown, frontMatter, prepared }
378
468
  })
379
469
 
380
470
  /**
@@ -387,7 +477,11 @@ export const layer: Layer.Layer<
387
477
  options: PullOptions,
388
478
  gitInitialized: boolean,
389
479
  knownParentId?: string
390
- ): Effect.Effect<{ pulled: number; commits: number }, SyncError> =>
480
+ ): Effect.Effect<
481
+ { pulled: number; commits: number },
482
+ SyncError | PlatformError.PlatformError,
483
+ Terminal.Terminal
484
+ > =>
391
485
  Effect.gen(function*() {
392
486
  const pageId = page.id as PageId
393
487
  // Get children to determine if this is a folder
@@ -448,7 +542,7 @@ export const layer: Layer.Layer<
448
542
  versionIdx++
449
543
  // Report progress
450
544
  if (options.onProgress) {
451
- options.onProgress(versionIdx, totalVersions, `${fullPage.title} v${versionInfo.number}`)
545
+ yield* options.onProgress(versionIdx, totalVersions, `${fullPage.title} v${versionInfo.number}`)
452
546
  }
453
547
 
454
548
  // Check if body content is available from the versions list
@@ -472,7 +566,8 @@ export const layer: Layer.Layer<
472
566
  }
473
567
  }
474
568
  }
475
- const { frontMatter, markdown } = yield* versionToMarkdown(
569
+ const { frontMatter, prepared } = yield* versionToMarkdown(
570
+ filePath,
476
571
  pageId,
477
572
  versionContent,
478
573
  versionInfo.page?.title ?? fullPage.title,
@@ -481,7 +576,7 @@ export const layer: Layer.Layer<
481
576
  )
482
577
 
483
578
  // Write file
484
- yield* localFs.writeMarkdownFile(filePath, frontMatter, markdown)
579
+ yield* writePreparedMarkdownWithAdfMetadata(filePath, frontMatter, prepared)
485
580
 
486
581
  // Save source ADF JSON if configured
487
582
  if (config.saveSource && versionContent.body?.atlas_doc_format?.value) {
@@ -520,7 +615,7 @@ export const layer: Layer.Layer<
520
615
 
521
616
  // Simple pull without history replay
522
617
  const adfJson = fullPage.body?.atlas_doc_format?.value ?? ""
523
- let markdown = yield* converter.adfToMarkdown(adfJson)
618
+ let rawMarkdown = yield* converter.adfToMarkdown(adfJson)
524
619
 
525
620
  // Add child page links for index pages
526
621
  if (hasChildren && config.spaceKey) {
@@ -530,9 +625,11 @@ export const layer: Layer.Layer<
530
625
  return `- [${child.title}](${pageUrl})`
531
626
  })
532
627
  .join("\n")
533
- markdown = markdown.trim() + "\n\n## Child Pages\n\n" + childLinks + "\n"
628
+ rawMarkdown = rawMarkdown.trim() + "\n\n## Child Pages\n\n" + childLinks + "\n"
534
629
  }
535
630
 
631
+ const prepared = prepareMarkdownForFile(filePath, rawMarkdown, pageId)
632
+ const { markdown } = prepared
536
633
  const contentHash = yield* hashBody(markdown)
537
634
 
538
635
  // Get author info
@@ -552,7 +649,7 @@ export const layer: Layer.Layer<
552
649
  ...(author?.email ? { authorEmail: author.email } : {})
553
650
  }
554
651
 
555
- yield* localFs.writeMarkdownFile(filePath, frontMatter, markdown)
652
+ yield* writePreparedMarkdownWithAdfMetadata(filePath, frontMatter, prepared)
556
653
 
557
654
  // Save source ADF JSON if configured
558
655
  if (config.saveSource && adfJson) {
@@ -574,7 +671,9 @@ export const layer: Layer.Layer<
574
671
  return { pulled: 1 + childPulled, commits: totalCommits + childCommits }
575
672
  })
576
673
 
577
- const pull = (options: PullOptions): Effect.Effect<PullResult, SyncError> =>
674
+ const pull = (
675
+ options: PullOptions
676
+ ): Effect.Effect<PullResult, SyncError | PlatformError.PlatformError, Terminal.Terminal> =>
578
677
  Effect.gen(function*() {
579
678
  yield* localFs.ensureDir(docsPath)
580
679
 
@@ -609,7 +708,7 @@ export const layer: Layer.Layer<
609
708
  yield* git.checkout(originalBranch)
610
709
  yield* git.merge("origin/confluence", {
611
710
  message: `Merge remote changes from Confluence`
612
- }).pipe(Effect.catchAll(() => Effect.void)) // May fail if no changes
711
+ }).pipe(Effect.catchIf(() => true, () => Effect.void)) // May fail if no changes
613
712
  }
614
713
 
615
714
  return {
@@ -629,11 +728,11 @@ export const layer: Layer.Layer<
629
728
  Effect.ensuring(
630
729
  hasRemoteBranch && originalBranch
631
730
  ? git.checkout(originalBranch).pipe(
632
- Effect.catchAll((error) =>
731
+ Effect.catchCause((cause) =>
633
732
  Effect.logWarning(
634
733
  `pull: could not restore branch '${originalBranch}' — the repo may still be on origin/confluence. ` +
635
734
  `Restore manually with \`git checkout ${originalBranch}\` (stash local changes first if needed). Cause: ${
636
- String(error)
735
+ String(cause)
637
736
  }`
638
737
  )
639
738
  )
@@ -667,22 +766,18 @@ export const layer: Layer.Layer<
667
766
 
668
767
  // For new pages, re-parse front-matter to get title
669
768
  // The localFile only has the content (body), not the original front-matter
670
- const title = yield* Effect.tryPromise({
671
- try: async () => {
672
- const fs = await import("node:fs/promises")
673
- const matter = await import("gray-matter")
674
- const rawFile = await fs.readFile(filePath, "utf-8")
675
- const parsed = matter.default(rawFile)
676
- return (parsed.data as { title?: string }).title ?? baseName
677
- },
678
- catch: (cause) => new FileSystemError({ operation: "read", path: filePath, cause })
679
- })
769
+ const rawFile = yield* fs.readFileString(filePath).pipe(
770
+ Effect.mapError((cause) => new FileSystemError({ operation: "read", path: filePath, cause }))
771
+ )
772
+ const parsed = yield* parseMarkdown(filePath, rawFile)
773
+ const title = parsed.frontMatter?.title ?? baseName
680
774
 
681
775
  // Resolve parent from directory structure
682
776
  const parentId = yield* resolveParent(filePath, pageIdMap)
683
777
 
684
778
  // Convert markdown to ADF
685
- const adfValue = yield* converter.markdownToAdf(localFile.content)
779
+ const hydratedContent = yield* hydrateMarkdownForFile(filePath, localFile.content)
780
+ const adfValue = yield* converter.markdownToAdf(hydratedContent)
686
781
 
687
782
  // Create the page
688
783
  const createdPage = yield* client.createPage({
@@ -697,7 +792,7 @@ export const layer: Layer.Layer<
697
792
 
698
793
  // Set editor version to v2 (new editor)
699
794
  yield* client.setEditorVersion(createdPage.id as PageId, "v2").pipe(
700
- Effect.catchAll((error) => {
795
+ Effect.catchIf(() => true, (error) => {
701
796
  // Log warning but don't fail the push
702
797
  return Effect.logWarning(`Failed to set editor v2 for page ${createdPage.id}: ${error.message}`)
703
798
  })
@@ -706,7 +801,9 @@ export const layer: Layer.Layer<
706
801
  // Fetch canonical content back from Confluence
707
802
  const canonicalPage = yield* client.getPage(createdPage.id as PageId)
708
803
  const canonicalAdf = canonicalPage.body?.atlas_doc_format?.value ?? ""
709
- const canonicalMarkdown = yield* converter.adfToMarkdown(canonicalAdf)
804
+ const rawCanonicalMarkdown = yield* converter.adfToMarkdown(canonicalAdf)
805
+ const preparedCanonicalMarkdown = prepareMarkdownForFile(filePath, rawCanonicalMarkdown, createdPage.id)
806
+ const { markdown: canonicalMarkdown } = preparedCanonicalMarkdown
710
807
  const canonicalHash = yield* hashBody(canonicalMarkdown)
711
808
 
712
809
  // Write canonical content with full front-matter
@@ -718,7 +815,7 @@ export const layer: Layer.Layer<
718
815
  parentId: parentId as PageId,
719
816
  contentHash: canonicalHash
720
817
  }
721
- yield* localFs.writeMarkdownFile(filePath, newFrontMatter, canonicalMarkdown)
818
+ yield* writePreparedMarkdownWithAdfMetadata(filePath, newFrontMatter, preparedCanonicalMarkdown)
722
819
 
723
820
  // Update pageIdMap with new page
724
821
  const key = relativePath.replace(/\.md$/, "")
@@ -736,7 +833,8 @@ export const layer: Layer.Layer<
736
833
 
737
834
  // Fetch current version to avoid conflicts
738
835
  const remotePage = yield* client.getPage(fm.pageId)
739
- const adfValue = yield* converter.markdownToAdf(localFile.content)
836
+ const hydratedContent = yield* hydrateMarkdownForFile(filePath, localFile.content)
837
+ const adfValue = yield* converter.markdownToAdf(hydratedContent)
740
838
  const updatedPage = yield* client.updatePage({
741
839
  id: fm.pageId,
742
840
  title: fm.title,
@@ -754,7 +852,9 @@ export const layer: Layer.Layer<
754
852
  // Fetch canonical content back from Confluence
755
853
  const canonicalPage = yield* client.getPage(fm.pageId)
756
854
  const canonicalAdf = canonicalPage.body?.atlas_doc_format?.value ?? ""
757
- const canonicalMarkdown = yield* converter.adfToMarkdown(canonicalAdf)
855
+ const rawCanonicalMarkdown = yield* converter.adfToMarkdown(canonicalAdf)
856
+ const preparedCanonicalMarkdown = prepareMarkdownForFile(filePath, rawCanonicalMarkdown, fm.pageId)
857
+ const { markdown: canonicalMarkdown } = preparedCanonicalMarkdown
758
858
  const canonicalHash = yield* hashBody(canonicalMarkdown)
759
859
 
760
860
  // Write canonical content with updated front-matter
@@ -764,7 +864,7 @@ export const layer: Layer.Layer<
764
864
  updated: new Date(canonicalPage.version.createdAt ?? new Date().toISOString()),
765
865
  contentHash: canonicalHash
766
866
  }
767
- yield* localFs.writeMarkdownFile(filePath, newFrontMatter, canonicalMarkdown)
867
+ yield* writePreparedMarkdownWithAdfMetadata(filePath, newFrontMatter, preparedCanonicalMarkdown)
768
868
 
769
869
  return { pushed: true, created: false }
770
870
  })
@@ -874,13 +974,12 @@ export const layer: Layer.Layer<
874
974
  spaceId,
875
975
  pageIdMap
876
976
  ).pipe(
877
- Effect.catchAll((error) =>
977
+ Effect.catchIf(() => true, (error) =>
878
978
  Effect.succeed({
879
979
  pushed: false,
880
980
  created: false,
881
981
  error: `Failed: ${error._tag}`
882
- })
883
- )
982
+ }))
884
983
  )
885
984
  if (result.error) errors.push(result.error)
886
985
  if (result.pushed) pushed++
@@ -937,13 +1036,13 @@ export const layer: Layer.Layer<
937
1036
  const match = content.match(/pageId:\s*['"]?(\d+)['"]?/)
938
1037
  return match ? match[1] : null
939
1038
  }),
940
- Effect.catchAll(() => Effect.succeed(null))
1039
+ Effect.catchIf(() => true, () => Effect.succeed(null))
941
1040
  )
942
1041
 
943
1042
  if (pageIdFromOrigin) {
944
1043
  yield* client.deletePage(PageId(pageIdFromOrigin)).pipe(
945
1044
  Effect.tap(() => Effect.sync(() => deleted++)),
946
- Effect.catchAll((error) => {
1045
+ Effect.catchIf(() => true, (error) => {
947
1046
  errors.push(`Failed to delete page ${pageIdFromOrigin}: ${error.message}`)
948
1047
  return Effect.void
949
1048
  })
@@ -954,13 +1053,12 @@ export const layer: Layer.Layer<
954
1053
 
955
1054
  for (const filePath of sortedFiles) {
956
1055
  const result = yield* pushFile(filePath, revisionMessage, spaceId, pageIdMap).pipe(
957
- Effect.catchAll((error) =>
1056
+ Effect.catchIf(() => true, (error) =>
958
1057
  Effect.succeed({
959
1058
  pushed: false,
960
1059
  created: false,
961
1060
  error: `Failed to push ${filePath}: ${error._tag}`
962
- })
963
- )
1061
+ }))
964
1062
  )
965
1063
  if (result.error) errors.push(result.error)
966
1064
  if (result.pushed) pushed++
@@ -970,7 +1068,7 @@ export const layer: Layer.Layer<
970
1068
  // Amend the last commit with canonical content
971
1069
  yield* git.addAll()
972
1070
  yield* git.amend({ noEdit: true }).pipe(
973
- Effect.catchAll(() => Effect.void)
1071
+ Effect.catchIf(() => true, () => Effect.void)
974
1072
  )
975
1073
 
976
1074
  // Two-branch model: update origin/confluence to match HEAD
@@ -1006,15 +1104,15 @@ export const layer: Layer.Layer<
1006
1104
  const currentHash = yield* computeHash(localFile.content).pipe(Effect.provide(HashServiceLive))
1007
1105
 
1008
1106
  // Fetch remote page
1009
- const remotePage = yield* Effect.either(client.getPage(fm.pageId))
1107
+ const remotePage = yield* Effect.result(client.getPage(fm.pageId))
1010
1108
 
1011
- if (remotePage._tag === "Left") {
1109
+ if (Result.isFailure(remotePage)) {
1012
1110
  statuses.push({ _tag: "LocalOnly", path: filePath, title: fm.title })
1013
1111
  localOnly++
1014
1112
  continue
1015
1113
  }
1016
1114
 
1017
- const page = remotePage.right
1115
+ const page = remotePage.success
1018
1116
  const localChanged = currentHash !== fm.contentHash
1019
1117
  const remoteChanged = page.version.number > fm.version
1020
1118