@knpkv/confluence-to-markdown 0.2.0 → 0.4.2

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 (336) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/LICENSE +21 -0
  3. package/README.md +282 -14
  4. package/dist/ConfluenceAuth.d.ts +76 -0
  5. package/dist/ConfluenceAuth.d.ts.map +1 -0
  6. package/dist/ConfluenceAuth.js +366 -0
  7. package/dist/ConfluenceAuth.js.map +1 -0
  8. package/dist/ConfluenceClient.d.ts +26 -12
  9. package/dist/ConfluenceClient.d.ts.map +1 -1
  10. package/dist/ConfluenceClient.js +139 -97
  11. package/dist/ConfluenceClient.js.map +1 -1
  12. package/dist/ConfluenceConfig.d.ts +4 -24
  13. package/dist/ConfluenceConfig.d.ts.map +1 -1
  14. package/dist/ConfluenceConfig.js +45 -7
  15. package/dist/ConfluenceConfig.js.map +1 -1
  16. package/dist/ConfluenceError.d.ts +99 -16
  17. package/dist/ConfluenceError.d.ts.map +1 -1
  18. package/dist/ConfluenceError.js +88 -5
  19. package/dist/ConfluenceError.js.map +1 -1
  20. package/dist/GitError.d.ts +103 -0
  21. package/dist/GitError.d.ts.map +1 -0
  22. package/dist/GitError.js +85 -0
  23. package/dist/GitError.js.map +1 -0
  24. package/dist/GitService.d.ts +175 -0
  25. package/dist/GitService.d.ts.map +1 -0
  26. package/dist/GitService.js +431 -0
  27. package/dist/GitService.js.map +1 -0
  28. package/dist/LocalFileSystem.d.ts +29 -4
  29. package/dist/LocalFileSystem.d.ts.map +1 -1
  30. package/dist/LocalFileSystem.js +80 -6
  31. package/dist/LocalFileSystem.js.map +1 -1
  32. package/dist/MarkdownConverter.d.ts +49 -2
  33. package/dist/MarkdownConverter.d.ts.map +1 -1
  34. package/dist/MarkdownConverter.js +73 -111
  35. package/dist/MarkdownConverter.js.map +1 -1
  36. package/dist/SchemaConverterError.d.ts +108 -0
  37. package/dist/SchemaConverterError.d.ts.map +1 -0
  38. package/dist/SchemaConverterError.js +84 -0
  39. package/dist/SchemaConverterError.js.map +1 -0
  40. package/dist/Schemas.d.ts +225 -1
  41. package/dist/Schemas.d.ts.map +1 -1
  42. package/dist/Schemas.js +155 -6
  43. package/dist/Schemas.js.map +1 -1
  44. package/dist/SyncEngine.d.ts +30 -20
  45. package/dist/SyncEngine.d.ts.map +1 -1
  46. package/dist/SyncEngine.js +566 -117
  47. package/dist/SyncEngine.js.map +1 -1
  48. package/dist/ast/BlockNode.d.ts +468 -0
  49. package/dist/ast/BlockNode.d.ts.map +1 -0
  50. package/dist/ast/BlockNode.js +319 -0
  51. package/dist/ast/BlockNode.js.map +1 -0
  52. package/dist/ast/Document.d.ts +244 -0
  53. package/dist/ast/Document.d.ts.map +1 -0
  54. package/dist/ast/Document.js +69 -0
  55. package/dist/ast/Document.js.map +1 -0
  56. package/dist/ast/InlineNode.d.ts +477 -0
  57. package/dist/ast/InlineNode.d.ts.map +1 -0
  58. package/dist/ast/InlineNode.js +263 -0
  59. package/dist/ast/InlineNode.js.map +1 -0
  60. package/dist/ast/MacroNode.d.ts +267 -0
  61. package/dist/ast/MacroNode.d.ts.map +1 -0
  62. package/dist/ast/MacroNode.js +164 -0
  63. package/dist/ast/MacroNode.js.map +1 -0
  64. package/dist/ast/index.d.ts +10 -0
  65. package/dist/ast/index.d.ts.map +1 -0
  66. package/dist/ast/index.js +14 -0
  67. package/dist/ast/index.js.map +1 -0
  68. package/dist/bin.js +33 -149
  69. package/dist/bin.js.map +1 -1
  70. package/dist/commands/auth.d.ts +15 -0
  71. package/dist/commands/auth.d.ts.map +1 -0
  72. package/dist/commands/auth.js +86 -0
  73. package/dist/commands/auth.js.map +1 -0
  74. package/dist/commands/clone.d.ts +12 -0
  75. package/dist/commands/clone.d.ts.map +1 -0
  76. package/dist/commands/clone.js +93 -0
  77. package/dist/commands/clone.js.map +1 -0
  78. package/dist/commands/delete.d.ts +13 -0
  79. package/dist/commands/delete.d.ts.map +1 -0
  80. package/dist/commands/delete.js +48 -0
  81. package/dist/commands/delete.js.map +1 -0
  82. package/dist/commands/errorHandler.d.ts +14 -0
  83. package/dist/commands/errorHandler.d.ts.map +1 -0
  84. package/dist/commands/errorHandler.js +33 -0
  85. package/dist/commands/errorHandler.js.map +1 -0
  86. package/dist/commands/git.d.ts +22 -0
  87. package/dist/commands/git.d.ts.map +1 -0
  88. package/dist/commands/git.js +72 -0
  89. package/dist/commands/git.js.map +1 -0
  90. package/dist/commands/index.d.ts +11 -0
  91. package/dist/commands/index.d.ts.map +1 -0
  92. package/dist/commands/index.js +11 -0
  93. package/dist/commands/index.js.map +1 -0
  94. package/dist/commands/layers.d.ts +31 -0
  95. package/dist/commands/layers.d.ts.map +1 -0
  96. package/dist/commands/layers.js +137 -0
  97. package/dist/commands/layers.js.map +1 -0
  98. package/dist/commands/new.d.ts +9 -0
  99. package/dist/commands/new.d.ts.map +1 -0
  100. package/dist/commands/new.js +80 -0
  101. package/dist/commands/new.js.map +1 -0
  102. package/dist/commands/pageTree.d.ts +18 -0
  103. package/dist/commands/pageTree.d.ts.map +1 -0
  104. package/dist/commands/pageTree.js +20 -0
  105. package/dist/commands/pageTree.js.map +1 -0
  106. package/dist/commands/shared.d.ts +15 -0
  107. package/dist/commands/shared.d.ts.map +1 -0
  108. package/dist/commands/shared.js +27 -0
  109. package/dist/commands/shared.js.map +1 -0
  110. package/dist/commands/sync.d.ts +15 -0
  111. package/dist/commands/sync.d.ts.map +1 -0
  112. package/dist/commands/sync.js +101 -0
  113. package/dist/commands/sync.js.map +1 -0
  114. package/dist/index.d.ts +10 -1
  115. package/dist/index.d.ts.map +1 -1
  116. package/dist/index.js +14 -0
  117. package/dist/index.js.map +1 -1
  118. package/dist/internal/NodeLayers.d.ts +7 -0
  119. package/dist/internal/NodeLayers.d.ts.map +1 -0
  120. package/dist/internal/NodeLayers.js +19 -0
  121. package/dist/internal/NodeLayers.js.map +1 -0
  122. package/dist/internal/frontmatter.d.ts +10 -0
  123. package/dist/internal/frontmatter.d.ts.map +1 -1
  124. package/dist/internal/frontmatter.js +16 -0
  125. package/dist/internal/frontmatter.js.map +1 -1
  126. package/dist/internal/gitCommands.d.ts +78 -0
  127. package/dist/internal/gitCommands.d.ts.map +1 -0
  128. package/dist/internal/gitCommands.js +156 -0
  129. package/dist/internal/gitCommands.js.map +1 -0
  130. package/dist/internal/hashUtils.d.ts +42 -1
  131. package/dist/internal/hashUtils.d.ts.map +1 -1
  132. package/dist/internal/hashUtils.js +38 -2
  133. package/dist/internal/hashUtils.js.map +1 -1
  134. package/dist/internal/oauthServer.d.ts +55 -0
  135. package/dist/internal/oauthServer.d.ts.map +1 -0
  136. package/dist/internal/oauthServer.js +110 -0
  137. package/dist/internal/oauthServer.js.map +1 -0
  138. package/dist/internal/pathUtils.d.ts +21 -4
  139. package/dist/internal/pathUtils.d.ts.map +1 -1
  140. package/dist/internal/pathUtils.js +24 -13
  141. package/dist/internal/pathUtils.js.map +1 -1
  142. package/dist/internal/tokenStorage.d.ts +75 -0
  143. package/dist/internal/tokenStorage.d.ts.map +1 -0
  144. package/dist/internal/tokenStorage.js +149 -0
  145. package/dist/internal/tokenStorage.js.map +1 -0
  146. package/dist/internal/userCache.d.ts +42 -0
  147. package/dist/internal/userCache.d.ts.map +1 -0
  148. package/dist/internal/userCache.js +51 -0
  149. package/dist/internal/userCache.js.map +1 -0
  150. package/dist/parsers/ConfluenceParser.d.ts +26 -0
  151. package/dist/parsers/ConfluenceParser.d.ts.map +1 -0
  152. package/dist/parsers/ConfluenceParser.js +792 -0
  153. package/dist/parsers/ConfluenceParser.js.map +1 -0
  154. package/dist/parsers/MarkdownParser.d.ts +26 -0
  155. package/dist/parsers/MarkdownParser.d.ts.map +1 -0
  156. package/dist/parsers/MarkdownParser.js +873 -0
  157. package/dist/parsers/MarkdownParser.js.map +1 -0
  158. package/dist/parsers/index.d.ts +8 -0
  159. package/dist/parsers/index.d.ts.map +1 -0
  160. package/dist/parsers/index.js +8 -0
  161. package/dist/parsers/index.js.map +1 -0
  162. package/dist/schemas/ConfluenceSchema.d.ts +21 -0
  163. package/dist/schemas/ConfluenceSchema.d.ts.map +1 -0
  164. package/dist/schemas/ConfluenceSchema.js +38 -0
  165. package/dist/schemas/ConfluenceSchema.js.map +1 -0
  166. package/dist/schemas/ConversionSchema.d.ts +35 -0
  167. package/dist/schemas/ConversionSchema.d.ts.map +1 -0
  168. package/dist/schemas/ConversionSchema.js +208 -0
  169. package/dist/schemas/ConversionSchema.js.map +1 -0
  170. package/dist/schemas/MarkdownSchema.d.ts +21 -0
  171. package/dist/schemas/MarkdownSchema.d.ts.map +1 -0
  172. package/dist/schemas/MarkdownSchema.js +38 -0
  173. package/dist/schemas/MarkdownSchema.js.map +1 -0
  174. package/dist/schemas/hast/HastFromHtml.d.ts +27 -0
  175. package/dist/schemas/hast/HastFromHtml.d.ts.map +1 -0
  176. package/dist/schemas/hast/HastFromHtml.js +107 -0
  177. package/dist/schemas/hast/HastFromHtml.js.map +1 -0
  178. package/dist/schemas/hast/HastSchema.d.ts +195 -0
  179. package/dist/schemas/hast/HastSchema.d.ts.map +1 -0
  180. package/dist/schemas/hast/HastSchema.js +183 -0
  181. package/dist/schemas/hast/HastSchema.js.map +1 -0
  182. package/dist/schemas/hast/index.d.ts +9 -0
  183. package/dist/schemas/hast/index.d.ts.map +1 -0
  184. package/dist/schemas/hast/index.js +3 -0
  185. package/dist/schemas/hast/index.js.map +1 -0
  186. package/dist/schemas/index.d.ts +14 -0
  187. package/dist/schemas/index.d.ts.map +1 -0
  188. package/dist/schemas/index.js +16 -0
  189. package/dist/schemas/index.js.map +1 -0
  190. package/dist/schemas/mdast/MdastFromMarkdown.d.ts +30 -0
  191. package/dist/schemas/mdast/MdastFromMarkdown.d.ts.map +1 -0
  192. package/dist/schemas/mdast/MdastFromMarkdown.js +79 -0
  193. package/dist/schemas/mdast/MdastFromMarkdown.js.map +1 -0
  194. package/dist/schemas/mdast/MdastSchema.d.ts +385 -0
  195. package/dist/schemas/mdast/MdastSchema.d.ts.map +1 -0
  196. package/dist/schemas/mdast/MdastSchema.js +266 -0
  197. package/dist/schemas/mdast/MdastSchema.js.map +1 -0
  198. package/dist/schemas/mdast/index.d.ts +10 -0
  199. package/dist/schemas/mdast/index.d.ts.map +1 -0
  200. package/dist/schemas/mdast/index.js +4 -0
  201. package/dist/schemas/mdast/index.js.map +1 -0
  202. package/dist/schemas/mdast/mdastToString.d.ts +13 -0
  203. package/dist/schemas/mdast/mdastToString.d.ts.map +1 -0
  204. package/dist/schemas/mdast/mdastToString.js +85 -0
  205. package/dist/schemas/mdast/mdastToString.js.map +1 -0
  206. package/dist/schemas/nodes/block/BlockSchema.d.ts +43 -0
  207. package/dist/schemas/nodes/block/BlockSchema.d.ts.map +1 -0
  208. package/dist/schemas/nodes/block/BlockSchema.js +634 -0
  209. package/dist/schemas/nodes/block/BlockSchema.js.map +1 -0
  210. package/dist/schemas/nodes/block/index.d.ts +7 -0
  211. package/dist/schemas/nodes/block/index.d.ts.map +1 -0
  212. package/dist/schemas/nodes/block/index.js +7 -0
  213. package/dist/schemas/nodes/block/index.js.map +1 -0
  214. package/dist/schemas/nodes/index.d.ts +9 -0
  215. package/dist/schemas/nodes/index.d.ts.map +1 -0
  216. package/dist/schemas/nodes/index.js +12 -0
  217. package/dist/schemas/nodes/index.js.map +1 -0
  218. package/dist/schemas/nodes/inline/InlineSchema.d.ts +48 -0
  219. package/dist/schemas/nodes/inline/InlineSchema.d.ts.map +1 -0
  220. package/dist/schemas/nodes/inline/InlineSchema.js +436 -0
  221. package/dist/schemas/nodes/inline/InlineSchema.js.map +1 -0
  222. package/dist/schemas/nodes/inline/index.d.ts +7 -0
  223. package/dist/schemas/nodes/inline/index.d.ts.map +1 -0
  224. package/dist/schemas/nodes/inline/index.js +7 -0
  225. package/dist/schemas/nodes/inline/index.js.map +1 -0
  226. package/dist/schemas/nodes/macro/MacroSchema.d.ts +27 -0
  227. package/dist/schemas/nodes/macro/MacroSchema.d.ts.map +1 -0
  228. package/dist/schemas/nodes/macro/MacroSchema.js +162 -0
  229. package/dist/schemas/nodes/macro/MacroSchema.js.map +1 -0
  230. package/dist/schemas/nodes/macro/index.d.ts +7 -0
  231. package/dist/schemas/nodes/macro/index.d.ts.map +1 -0
  232. package/dist/schemas/nodes/macro/index.js +7 -0
  233. package/dist/schemas/nodes/macro/index.js.map +1 -0
  234. package/dist/schemas/preprocessing/ConfluencePreprocessor.d.ts +24 -0
  235. package/dist/schemas/preprocessing/ConfluencePreprocessor.d.ts.map +1 -0
  236. package/dist/schemas/preprocessing/ConfluencePreprocessor.js +351 -0
  237. package/dist/schemas/preprocessing/ConfluencePreprocessor.js.map +1 -0
  238. package/dist/schemas/preprocessing/index.d.ts +8 -0
  239. package/dist/schemas/preprocessing/index.d.ts.map +1 -0
  240. package/dist/schemas/preprocessing/index.js +2 -0
  241. package/dist/schemas/preprocessing/index.js.map +1 -0
  242. package/dist/serializers/ConfluenceSerializer.d.ts +30 -0
  243. package/dist/serializers/ConfluenceSerializer.d.ts.map +1 -0
  244. package/dist/serializers/ConfluenceSerializer.js +551 -0
  245. package/dist/serializers/ConfluenceSerializer.js.map +1 -0
  246. package/dist/serializers/MarkdownSerializer.d.ts +34 -0
  247. package/dist/serializers/MarkdownSerializer.d.ts.map +1 -0
  248. package/dist/serializers/MarkdownSerializer.js +355 -0
  249. package/dist/serializers/MarkdownSerializer.js.map +1 -0
  250. package/dist/serializers/index.d.ts +8 -0
  251. package/dist/serializers/index.d.ts.map +1 -0
  252. package/dist/serializers/index.js +8 -0
  253. package/dist/serializers/index.js.map +1 -0
  254. package/package.json +32 -21
  255. package/src/ConfluenceAuth.ts +581 -0
  256. package/src/ConfluenceClient.ts +230 -165
  257. package/src/ConfluenceConfig.ts +63 -7
  258. package/src/ConfluenceError.ts +110 -14
  259. package/src/GitError.ts +92 -0
  260. package/src/GitService.ts +859 -0
  261. package/src/LocalFileSystem.ts +179 -9
  262. package/src/MarkdownConverter.ts +126 -122
  263. package/src/SchemaConverterError.ts +108 -0
  264. package/src/Schemas.ts +223 -6
  265. package/src/SyncEngine.ts +745 -162
  266. package/src/ast/BlockNode.ts +425 -0
  267. package/src/ast/Document.ts +90 -0
  268. package/src/ast/InlineNode.ts +323 -0
  269. package/src/ast/MacroNode.ts +245 -0
  270. package/src/ast/index.ts +83 -0
  271. package/src/bin.ts +50 -249
  272. package/src/commands/auth.ts +117 -0
  273. package/src/commands/clone.ts +145 -0
  274. package/src/commands/delete.ts +57 -0
  275. package/src/commands/errorHandler.ts +32 -0
  276. package/src/commands/git.ts +114 -0
  277. package/src/commands/index.ts +10 -0
  278. package/src/commands/layers.ts +211 -0
  279. package/src/commands/new.ts +99 -0
  280. package/src/commands/pageTree.ts +40 -0
  281. package/src/commands/shared.ts +35 -0
  282. package/src/commands/sync.ts +129 -0
  283. package/src/index.ts +21 -1
  284. package/src/internal/NodeLayers.ts +21 -0
  285. package/src/internal/frontmatter.ts +21 -0
  286. package/src/internal/gitCommands.ts +229 -0
  287. package/src/internal/hashUtils.ts +65 -3
  288. package/src/internal/oauthServer.ts +199 -0
  289. package/src/internal/pathUtils.ts +34 -17
  290. package/src/internal/tokenStorage.ts +240 -0
  291. package/src/internal/userCache.ts +90 -0
  292. package/src/parsers/ConfluenceParser.ts +950 -0
  293. package/src/parsers/MarkdownParser.ts +1198 -0
  294. package/src/parsers/index.ts +8 -0
  295. package/src/schemas/ConfluenceSchema.ts +56 -0
  296. package/src/schemas/ConversionSchema.ts +318 -0
  297. package/src/schemas/MarkdownSchema.ts +56 -0
  298. package/src/schemas/hast/HastFromHtml.ts +153 -0
  299. package/src/schemas/hast/HastSchema.ts +274 -0
  300. package/src/schemas/hast/index.ts +35 -0
  301. package/src/schemas/index.ts +20 -0
  302. package/src/schemas/mdast/MdastFromMarkdown.ts +118 -0
  303. package/src/schemas/mdast/MdastSchema.ts +566 -0
  304. package/src/schemas/mdast/index.ts +59 -0
  305. package/src/schemas/mdast/mdastToString.ts +102 -0
  306. package/src/schemas/nodes/block/BlockSchema.ts +773 -0
  307. package/src/schemas/nodes/block/index.ts +13 -0
  308. package/src/schemas/nodes/index.ts +20 -0
  309. package/src/schemas/nodes/inline/InlineSchema.ts +523 -0
  310. package/src/schemas/nodes/inline/index.ts +14 -0
  311. package/src/schemas/nodes/macro/MacroSchema.ts +226 -0
  312. package/src/schemas/nodes/macro/index.ts +6 -0
  313. package/src/schemas/preprocessing/ConfluencePreprocessor.ts +446 -0
  314. package/src/schemas/preprocessing/index.ts +8 -0
  315. package/src/serializers/ConfluenceSerializer.ts +717 -0
  316. package/src/serializers/MarkdownSerializer.ts +493 -0
  317. package/src/serializers/index.ts +8 -0
  318. package/test/GitService.test.ts +209 -0
  319. package/test/MarkdownConverter.test.ts +37 -3
  320. package/test/Schemas.test.ts +97 -2
  321. package/test/ast/BlockNode.test.ts +265 -0
  322. package/test/ast/Document.test.ts +126 -0
  323. package/test/ast/InlineNode.test.ts +161 -0
  324. package/test/fixtures/integration-test.html.fixture +103 -0
  325. package/test/fixtures/integration-test.md.expected +257 -0
  326. package/test/integration.test.ts +269 -0
  327. package/test/oauthServer.test.ts +50 -0
  328. package/test/parsers/ConfluenceParser.test.ts +283 -0
  329. package/test/schemas/ConfluencePreprocessor.test.ts +180 -0
  330. package/test/schemas/ConversionSchema.test.ts +159 -0
  331. package/test/schemas/HastSchema.test.ts +138 -0
  332. package/test/schemas/MdastSchema.test.ts +145 -0
  333. package/test/schemas/nodes/block/BlockSchema.test.ts +173 -0
  334. package/test/schemas/nodes/inline/InlineSchema.test.ts +198 -0
  335. package/test/schemas/nodes/macro/MacroSchema.test.ts +142 -0
  336. package/test/tokenStorage.test.ts +99 -0
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Parsers for Confluence HTML and Markdown to AST.
3
+ *
4
+ * @module
5
+ */
6
+
7
+ export { parseConfluenceHtml } from "./ConfluenceParser.js"
8
+ export { parseMarkdown } from "./MarkdownParser.js"
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Bi-directional schema for Confluence HTML <-> AST transformation.
3
+ *
4
+ * @module
5
+ */
6
+ import * as Effect from "effect/Effect"
7
+ import * as ParseResult from "effect/ParseResult"
8
+ import * as Schema from "effect/Schema"
9
+ import type { Document } from "../ast/Document.js"
10
+ import { parseConfluenceHtml } from "../parsers/ConfluenceParser.js"
11
+ import { serializeToConfluence } from "../serializers/ConfluenceSerializer.js"
12
+
13
+ /**
14
+ * Schema.Type placeholder for Document to use with transformOrFail.
15
+ * We use Schema.Any since Document is already a union type.
16
+ */
17
+ const DocumentSchema = Schema.Any as Schema.Schema<Document, Document>
18
+
19
+ /**
20
+ * Bi-directional schema: Confluence HTML string <-> Document AST.
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * import { DocumentFromConfluence } from "@knpkv/confluence-to-markdown/schemas/ConfluenceSchema"
25
+ * import { Schema, Effect } from "effect"
26
+ *
27
+ * // Decode: HTML -> AST
28
+ * const decodeResult = Schema.decodeUnknown(DocumentFromConfluence)(htmlString)
29
+ *
30
+ * // Encode: AST -> HTML
31
+ * const encodeResult = Schema.encode(DocumentFromConfluence)(document)
32
+ * ```
33
+ *
34
+ * @category Schemas
35
+ */
36
+ export const DocumentFromConfluence: Schema.Schema<
37
+ Document,
38
+ string,
39
+ never
40
+ > = Schema.transformOrFail(
41
+ Schema.String,
42
+ DocumentSchema,
43
+ {
44
+ strict: true,
45
+ decode: (html, _opts, ast) =>
46
+ Effect.mapError(
47
+ parseConfluenceHtml(html),
48
+ (err) => new ParseResult.Type(ast, html, err.message)
49
+ ),
50
+ encode: (doc, _opts, ast) =>
51
+ Effect.mapError(
52
+ serializeToConfluence(doc),
53
+ (err) => new ParseResult.Type(ast, doc, err.message)
54
+ )
55
+ }
56
+ )
@@ -0,0 +1,318 @@
1
+ /**
2
+ * Direct conversion schema: Confluence HTML <-> Markdown.
3
+ *
4
+ * Composes preprocessing, parsing, and serialization into a single transform.
5
+ *
6
+ * @module
7
+ */
8
+ import * as Effect from "effect/Effect"
9
+ import * as ParseResult from "effect/ParseResult"
10
+ import * as Schema from "effect/Schema"
11
+ import type { BlockNode } from "../ast/BlockNode.js"
12
+ import { type Document, makeDocument } from "../ast/Document.js"
13
+ import type { MacroNode } from "../ast/MacroNode.js"
14
+ import type { HastElement, HastNode, HastRoot } from "./hast/index.js"
15
+ import { HastFromHtml, isHastElement } from "./hast/index.js"
16
+ import type { MdastBlockContent, MdastRoot } from "./mdast/index.js"
17
+ import { MdastFromMarkdown, mdastToString } from "./mdast/index.js"
18
+ import { blockNodeFromHastElement, blockNodeFromMdast, blockNodeToMdast } from "./nodes/index.js"
19
+ import { macroNodeFromHastElement, macroNodeToMdast } from "./nodes/macro/index.js"
20
+ import { PreprocessedHtmlFromConfluence } from "./preprocessing/index.js"
21
+
22
+ type DocumentNode = BlockNode | MacroNode
23
+
24
+ /**
25
+ * Parse HAST children to simple block nodes.
26
+ * Note: Returns BlockNode which is broader than SimpleBlock used by macros,
27
+ * but at runtime only simple blocks are returned from single-level parsing.
28
+ */
29
+ const parseBlockChildren = (
30
+ children: ReadonlyArray<HastNode>
31
+ ): Effect.Effect<ReadonlyArray<BlockNode>, ParseResult.ParseError> =>
32
+ Effect.forEach(
33
+ children.filter(isHastElement),
34
+ (el) =>
35
+ blockNodeFromHastElement(el).pipe(
36
+ Effect.map((node): ReadonlyArray<BlockNode> => (node !== null ? [node] : []))
37
+ )
38
+ ).pipe(Effect.map((arrays) => arrays.flat()))
39
+
40
+ /**
41
+ * Parse single HAST element to document node (block or macro).
42
+ */
43
+ const documentNodeFromHastElement = (
44
+ element: HastElement
45
+ ): Effect.Effect<DocumentNode | null, ParseResult.ParseError> =>
46
+ Effect.gen(function*() {
47
+ // Try macro first
48
+ const macro = yield* macroNodeFromHastElement(element, parseBlockChildren)
49
+ if (macro !== null) {
50
+ return macro
51
+ }
52
+
53
+ // Fall back to block
54
+ const block = yield* blockNodeFromHastElement(element)
55
+ return block
56
+ })
57
+
58
+ /**
59
+ * Parse HAST root to Document AST.
60
+ */
61
+ const documentFromHastRoot = (
62
+ root: HastRoot,
63
+ rawConfluence?: string
64
+ ): Effect.Effect<Document, ParseResult.ParseError> =>
65
+ Effect.gen(function*() {
66
+ const elements = root.children.filter(isHastElement)
67
+ const nodeArrays = yield* Effect.forEach(elements, (el) =>
68
+ documentNodeFromHastElement(el).pipe(
69
+ Effect.map((node) => (node !== null ? [node] : []))
70
+ ))
71
+ const children = nodeArrays.flat()
72
+ return makeDocument(children, rawConfluence)
73
+ })
74
+
75
+ /**
76
+ * Parse MDAST root to Document AST.
77
+ */
78
+ const documentFromMdastRoot = (root: MdastRoot): Document => {
79
+ const children: Array<DocumentNode> = []
80
+ for (const node of root.children) {
81
+ const blockContent = node as MdastBlockContent
82
+ const block = blockNodeFromMdast(blockContent)
83
+ children.push(block)
84
+ }
85
+ return makeDocument(children)
86
+ }
87
+
88
+ /**
89
+ * Check if node is a macro node.
90
+ */
91
+ const isMacroNode = (node: DocumentNode): node is MacroNode => {
92
+ return node._tag === "InfoPanel" ||
93
+ node._tag === "ExpandMacro" ||
94
+ node._tag === "TocMacro" ||
95
+ node._tag === "CodeMacro" ||
96
+ node._tag === "StatusMacro"
97
+ }
98
+
99
+ /**
100
+ * Serialize Document AST to Markdown.
101
+ */
102
+ const documentToMarkdown = (doc: Document): string => {
103
+ const parts: Array<string> = []
104
+ for (const node of doc.children) {
105
+ const mdast = isMacroNode(node)
106
+ ? macroNodeToMdast(node)
107
+ : blockNodeToMdast(node as BlockNode)
108
+ parts.push(mdastToString(mdast))
109
+ }
110
+ return parts.join("\n\n")
111
+ }
112
+
113
+ /**
114
+ * Schema for transforming preprocessed Confluence HTML to Document AST.
115
+ */
116
+ export const DocumentFromHast: Schema.Schema<
117
+ Document,
118
+ HastRoot,
119
+ never
120
+ > = Schema.transformOrFail(
121
+ Schema.Any as Schema.Schema<HastRoot, HastRoot>,
122
+ Schema.Any as Schema.Schema<Document, Document>,
123
+ {
124
+ strict: true,
125
+ decode: (root, _options, ast) =>
126
+ documentFromHastRoot(root).pipe(
127
+ Effect.mapError((e) => new ParseResult.Type(ast, root, e.message))
128
+ ),
129
+ encode: (_doc, _options, ast) =>
130
+ Effect.fail(new ParseResult.Type(ast, _doc, "Document to HAST encoding not implemented"))
131
+ }
132
+ )
133
+
134
+ /**
135
+ * Schema for transforming MDAST root to Document AST.
136
+ */
137
+ export const DocumentFromMdast: Schema.Schema<
138
+ Document,
139
+ MdastRoot,
140
+ never
141
+ > = Schema.transformOrFail(
142
+ Schema.Any as Schema.Schema<MdastRoot, MdastRoot>,
143
+ Schema.Any as Schema.Schema<Document, Document>,
144
+ {
145
+ strict: true,
146
+ decode: (root, _options, _ast) => Effect.succeed(documentFromMdastRoot(root)),
147
+ encode: (_doc, _options, ast) =>
148
+ Effect.fail(new ParseResult.Type(ast, _doc, "Document to MDAST encoding not implemented"))
149
+ }
150
+ )
151
+
152
+ /**
153
+ * Direct conversion schema: Confluence HTML string <-> Markdown string.
154
+ *
155
+ * @example
156
+ * ```typescript
157
+ * import { ConfluenceToMarkdown } from "@knpkv/confluence-to-markdown/schemas/ConversionSchema"
158
+ * import { Schema, Effect } from "effect"
159
+ *
160
+ * // Decode: Confluence HTML -> Markdown
161
+ * const markdown = Effect.runSync(
162
+ * Schema.decode(ConfluenceToMarkdown)(confluenceHtml)
163
+ * )
164
+ *
165
+ * // Encode: Markdown -> Confluence HTML (limited support)
166
+ * const html = Effect.runSync(
167
+ * Schema.encode(ConfluenceToMarkdown)(markdown)
168
+ * )
169
+ * ```
170
+ *
171
+ * @category Schemas
172
+ */
173
+ export const ConfluenceToMarkdown: Schema.Schema<
174
+ string,
175
+ string,
176
+ never
177
+ > = Schema.transformOrFail(
178
+ Schema.String,
179
+ Schema.String,
180
+ {
181
+ strict: true,
182
+ decode: (html, _options, ast) =>
183
+ Effect.gen(function*() {
184
+ // Preprocess
185
+ const preprocessed = yield* Schema.decode(PreprocessedHtmlFromConfluence)(html).pipe(
186
+ Effect.mapError((e) => new ParseResult.Type(ast, html, `Preprocessing failed: ${e.message}`))
187
+ )
188
+
189
+ // Parse to HAST
190
+ const hast = yield* Schema.decode(HastFromHtml)(preprocessed).pipe(
191
+ Effect.mapError((e) => new ParseResult.Type(ast, html, `HAST parsing failed: ${e.message}`))
192
+ )
193
+
194
+ // Convert to Document
195
+ const doc = yield* documentFromHastRoot(hast, html).pipe(
196
+ Effect.mapError((e) => new ParseResult.Type(ast, html, `Document conversion failed: ${e.message}`))
197
+ )
198
+
199
+ // Serialize to Markdown
200
+ return documentToMarkdown(doc)
201
+ }),
202
+ encode: (md, _options, ast) =>
203
+ Schema.decode(MdastFromMarkdown)(md).pipe(
204
+ Effect.mapError((e) => new ParseResult.Type(ast, md, `MDAST parsing failed: ${e.message}`)),
205
+ Effect.map((mdast) => {
206
+ const doc = documentFromMdastRoot(mdast)
207
+
208
+ // Note: Full HTML serialization is complex; return basic HTML
209
+ // For full fidelity, use the existing ConfluenceSerializer
210
+ const parts: Array<string> = []
211
+ for (const node of doc.children) {
212
+ parts.push(documentNodeToHtml(node))
213
+ }
214
+ return parts.join("\n")
215
+ })
216
+ )
217
+ }
218
+ )
219
+
220
+ /**
221
+ * Basic document node to HTML conversion.
222
+ */
223
+ const documentNodeToHtml = (node: DocumentNode): string => {
224
+ switch (node._tag) {
225
+ case "Heading":
226
+ return `<h${node.level}>${node.children.map(inlineToHtml).join("")}</h${node.level}>`
227
+ case "Paragraph":
228
+ return `<p>${node.children.map(inlineToHtml).join("")}</p>`
229
+ case "CodeBlock":
230
+ return `<pre><code${node.language ? ` class="language-${node.language}"` : ""}>${
231
+ escapeHtml(node.code)
232
+ }</code></pre>`
233
+ case "ThematicBreak":
234
+ return "<hr />"
235
+ case "Image":
236
+ return `<img src="${escapeHtml(node.src ?? "")}" alt="${escapeHtml(node.alt ?? "")}" />`
237
+ case "BlockQuote":
238
+ return `<blockquote>${node.children.map((c) => documentNodeToHtml(c as DocumentNode)).join("")}</blockquote>`
239
+ case "List": {
240
+ const tag = node.ordered ? "ol" : "ul"
241
+ return `<${tag}>${
242
+ node.children.map((item) =>
243
+ `<li>${item.children.map((c) => documentNodeToHtml(c as DocumentNode)).join("")}</li>`
244
+ ).join("")
245
+ }</${tag}>`
246
+ }
247
+ case "Table":
248
+ return `<table>${
249
+ node.rows.map((row) =>
250
+ `<tr>${
251
+ row.cells.map((cell) =>
252
+ cell.isHeader
253
+ ? `<th>${cell.children.map(inlineToHtml).join("")}</th>`
254
+ : `<td>${cell.children.map(inlineToHtml).join("")}</td>`
255
+ ).join("")
256
+ }</tr>`
257
+ ).join("")
258
+ }</table>`
259
+ case "InfoPanel":
260
+ return `<div data-macro="${node.panelType}">${
261
+ node.children.map((c) => documentNodeToHtml(c as DocumentNode)).join("")
262
+ }</div>`
263
+ case "ExpandMacro":
264
+ return `<details><summary>${escapeHtml(node.title ?? "")}</summary>${
265
+ node.children.map((c) => documentNodeToHtml(c as DocumentNode)).join("")
266
+ }</details>`
267
+ case "TocMacro":
268
+ return `<nav data-macro="toc"></nav>`
269
+ case "CodeMacro":
270
+ return `<pre data-macro="code"${node.language ? ` data-language="${node.language}"` : ""}><code>${
271
+ escapeHtml(node.code)
272
+ }</code></pre>`
273
+ case "StatusMacro":
274
+ return `<span data-macro="status" data-color="${node.color}">${escapeHtml(node.text)}</span>`
275
+ default:
276
+ return ""
277
+ }
278
+ }
279
+
280
+ /**
281
+ * Basic inline node to HTML.
282
+ */
283
+ const inlineToHtml = (
284
+ node: { _tag: string; value?: string; children?: ReadonlyArray<unknown>; url?: string }
285
+ ): string => {
286
+ switch (node._tag) {
287
+ case "Text":
288
+ return escapeHtml(node.value ?? "")
289
+ case "Strong":
290
+ return `<strong>${
291
+ (node.children as ReadonlyArray<{ _tag: string; value?: string }>)?.map(inlineToHtml).join("") ?? ""
292
+ }</strong>`
293
+ case "Emphasis":
294
+ return `<em>${
295
+ (node.children as ReadonlyArray<{ _tag: string; value?: string }>)?.map(inlineToHtml).join("") ?? ""
296
+ }</em>`
297
+ case "InlineCode":
298
+ return `<code>${escapeHtml(node.value ?? "")}</code>`
299
+ case "Link":
300
+ return `<a href="${escapeHtml(node.url ?? "")}">${
301
+ (node.children as ReadonlyArray<{ _tag: string; value?: string }>)?.map(inlineToHtml).join("") ?? ""
302
+ }</a>`
303
+ case "LineBreak":
304
+ return "<br />"
305
+ default:
306
+ return ""
307
+ }
308
+ }
309
+
310
+ /**
311
+ * Escape HTML special characters.
312
+ */
313
+ const escapeHtml = (str: string): string =>
314
+ str
315
+ .replace(/&/g, "&amp;")
316
+ .replace(/</g, "&lt;")
317
+ .replace(/>/g, "&gt;")
318
+ .replace(/"/g, "&quot;")
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Bi-directional schema for Markdown <-> AST transformation.
3
+ *
4
+ * @module
5
+ */
6
+ import * as Effect from "effect/Effect"
7
+ import * as ParseResult from "effect/ParseResult"
8
+ import * as Schema from "effect/Schema"
9
+ import type { Document } from "../ast/Document.js"
10
+ import { parseMarkdown } from "../parsers/MarkdownParser.js"
11
+ import { serializeToMarkdown } from "../serializers/MarkdownSerializer.js"
12
+
13
+ /**
14
+ * Schema.Type placeholder for Document to use with transformOrFail.
15
+ * We use Schema.Any since Document is already a union type.
16
+ */
17
+ const DocumentSchema = Schema.Any as Schema.Schema<Document, Document>
18
+
19
+ /**
20
+ * Bi-directional schema: Markdown string <-> Document AST.
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * import { DocumentFromMarkdown } from "@knpkv/confluence-to-markdown/schemas/MarkdownSchema"
25
+ * import { Schema, Effect } from "effect"
26
+ *
27
+ * // Decode: Markdown -> AST
28
+ * const decodeResult = Schema.decodeUnknown(DocumentFromMarkdown)(markdownString)
29
+ *
30
+ * // Encode: AST -> Markdown
31
+ * const encodeResult = Schema.encode(DocumentFromMarkdown)(document)
32
+ * ```
33
+ *
34
+ * @category Schemas
35
+ */
36
+ export const DocumentFromMarkdown: Schema.Schema<
37
+ Document,
38
+ string,
39
+ never
40
+ > = Schema.transformOrFail(
41
+ Schema.String,
42
+ DocumentSchema,
43
+ {
44
+ strict: true,
45
+ decode: (md, _opts, ast) =>
46
+ Effect.mapError(
47
+ parseMarkdown(md),
48
+ (err) => new ParseResult.Type(ast, md, err.message)
49
+ ),
50
+ encode: (doc, _opts, ast) =>
51
+ Effect.mapError(
52
+ serializeToMarkdown(doc),
53
+ (err) => new ParseResult.Type(ast, doc, err.message)
54
+ )
55
+ }
56
+ )
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Schema transformation from HTML string to HAST (Hypertext Abstract Syntax Tree).
3
+ *
4
+ * Uses rehype-parse for HTML parsing and wraps the result in Effect Schema types.
5
+ *
6
+ * @module
7
+ */
8
+ import * as Effect from "effect/Effect"
9
+ import * as ParseResult from "effect/ParseResult"
10
+ import * as Schema from "effect/Schema"
11
+ import rehypeParse from "rehype-parse"
12
+ import rehypeStringify from "rehype-stringify"
13
+ import { unified } from "unified"
14
+ import type { HastNode, HastRoot } from "./HastSchema.js"
15
+ import {
16
+ HastRootSchema,
17
+ makeHastComment,
18
+ makeHastDoctype,
19
+ makeHastElement,
20
+ makeHastRoot,
21
+ makeHastText
22
+ } from "./HastSchema.js"
23
+
24
+ // Unist/Hast types from rehype-parse (simplified)
25
+ interface UnistNode {
26
+ type: string
27
+ value?: string
28
+ tagName?: string
29
+ properties?: Record<string, unknown>
30
+ children?: Array<UnistNode>
31
+ }
32
+
33
+ interface UnistRoot {
34
+ type: "root"
35
+ children: Array<UnistNode>
36
+ }
37
+
38
+ /**
39
+ * Convert unist node from rehype-parse to our HastNode type.
40
+ */
41
+ const convertToHastNode = (node: UnistNode): HastNode => {
42
+ switch (node.type) {
43
+ case "text":
44
+ return makeHastText(node.value ?? "")
45
+ case "comment":
46
+ return makeHastComment(node.value ?? "")
47
+ case "doctype":
48
+ return makeHastDoctype()
49
+ case "element":
50
+ return makeHastElement(
51
+ node.tagName ?? "div",
52
+ node.properties ?? {},
53
+ (node.children ?? []).map(convertToHastNode)
54
+ )
55
+ default:
56
+ // Unknown node type - treat as comment to preserve
57
+ return makeHastComment(`unknown:${node.type}`)
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Convert our HastNode type back to unist format for serialization.
63
+ */
64
+ const convertFromHastNode = (node: HastNode): UnistNode => {
65
+ switch (node._tag) {
66
+ case "text":
67
+ return { type: "text", value: node.value }
68
+ case "comment":
69
+ return { type: "comment", value: node.value }
70
+ case "doctype":
71
+ return { type: "doctype" }
72
+ case "element":
73
+ return {
74
+ type: "element",
75
+ tagName: node.tagName,
76
+ properties: node.properties,
77
+ children: node.children.map(convertFromHastNode)
78
+ }
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Convert unist root to HastRoot.
84
+ */
85
+ const convertToHastRoot = (root: UnistRoot): HastRoot => makeHastRoot(root.children.map(convertToHastNode))
86
+
87
+ /**
88
+ * Convert HastRoot back to unist root for serialization.
89
+ */
90
+ const convertFromHastRoot = (root: HastRoot): UnistRoot => ({
91
+ type: "root",
92
+ children: root.children.map(convertFromHastNode)
93
+ })
94
+
95
+ /**
96
+ * Transform HTML string to HAST root.
97
+ *
98
+ * Uses rehype-parse to parse HTML and converts the result to our schema types.
99
+ * Supports bidirectional transformation (decode HTML -> HAST, encode HAST -> HTML).
100
+ *
101
+ * @example
102
+ * ```typescript
103
+ * import { HastFromHtml } from "@knpkv/confluence-to-markdown/schemas/hast"
104
+ * import * as Schema from "effect/Schema"
105
+ * import { Effect } from "effect"
106
+ *
107
+ * const program = Effect.gen(function* () {
108
+ * const hast = yield* Schema.decode(HastFromHtml)("<h1>Hello</h1><p>World</p>")
109
+ * console.log(hast.children.length) // 2
110
+ * console.log(hast.children[0]._tag) // "element"
111
+ * })
112
+ *
113
+ * Effect.runPromise(program)
114
+ * ```
115
+ *
116
+ * @category Hast
117
+ */
118
+ export const HastFromHtml: Schema.Schema<HastRoot, string> = Schema.transformOrFail(
119
+ Schema.String,
120
+ HastRootSchema,
121
+ {
122
+ strict: true,
123
+ decode: (html, _options, ast) =>
124
+ Effect.try({
125
+ try: () => {
126
+ const result = unified()
127
+ .use(rehypeParse, { fragment: true })
128
+ .parse(html) as UnistRoot
129
+ return convertToHastRoot(result)
130
+ },
131
+ catch: (error) =>
132
+ new ParseResult.Type(
133
+ ast,
134
+ html,
135
+ `HTML parse error: ${error instanceof Error ? error.message : String(error)}`
136
+ )
137
+ }),
138
+ encode: (hast, _options, ast) =>
139
+ Effect.try({
140
+ try: () => {
141
+ const unist = convertFromHastRoot(hast)
142
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
143
+ return unified().use(rehypeStringify).stringify(unist as any)
144
+ },
145
+ catch: (error) =>
146
+ new ParseResult.Type(
147
+ ast,
148
+ hast,
149
+ `HTML stringify error: ${error instanceof Error ? error.message : String(error)}`
150
+ )
151
+ })
152
+ }
153
+ )