@knpkv/confluence-to-markdown 0.2.0 → 0.4.1

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 +60 -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 +356 -0
  7. package/dist/ConfluenceAuth.js.map +1 -0
  8. package/dist/ConfluenceClient.d.ts +26 -2
  9. package/dist/ConfluenceClient.d.ts.map +1 -1
  10. package/dist/ConfluenceClient.js +98 -92
  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 +89 -6
  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 +27 -16
  255. package/src/ConfluenceAuth.ts +571 -0
  256. package/src/ConfluenceClient.ts +188 -156
  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,773 @@
1
+ /**
2
+ * Transform schemas for block nodes (Hast <-> AST <-> Mdast).
3
+ *
4
+ * Provides bidirectional transforms between HAST elements, AST block nodes,
5
+ * and MDAST block content for structural document elements.
6
+ *
7
+ * @module
8
+ */
9
+ import * as Effect from "effect/Effect"
10
+ import * as ParseResult from "effect/ParseResult"
11
+ import * as Schema from "effect/Schema"
12
+ import {
13
+ type BlockNode,
14
+ type BlockQuote,
15
+ CodeBlock,
16
+ Heading,
17
+ Image,
18
+ type List,
19
+ type ListItem,
20
+ Paragraph,
21
+ Table,
22
+ TableCell,
23
+ TableRow,
24
+ type TaskItem,
25
+ type TaskList,
26
+ ThematicBreak,
27
+ UnsupportedBlock
28
+ } from "../../../ast/BlockNode.js"
29
+ import type { InlineNode } from "../../../ast/InlineNode.js"
30
+ import type { HastElement, HastNode, HastText } from "../../hast/index.js"
31
+ import { getTextContent, isHastElement, isHastText, makeHastElement, makeHastText } from "../../hast/index.js"
32
+ import type { MdastBlockContent } from "../../mdast/index.js"
33
+ import { makeMdastCode, makeMdastHeading, makeMdastParagraph } from "../../mdast/index.js"
34
+ import { inlineNodeFromHastElement, inlineNodeToHast, inlineNodeToMdast, textFromHastText } from "../inline/index.js"
35
+
36
+ /**
37
+ * Parse HAST children to inline nodes.
38
+ */
39
+ const parseHastChildrenToInline = (
40
+ children: ReadonlyArray<HastNode>
41
+ ): Effect.Effect<ReadonlyArray<InlineNode>, ParseResult.ParseError> =>
42
+ Effect.gen(function*() {
43
+ const nodes: Array<InlineNode> = []
44
+ const parseChildren = (
45
+ c: ReadonlyArray<HastNode>
46
+ ): Effect.Effect<ReadonlyArray<InlineNode>, ParseResult.ParseError> => parseHastChildrenToInline(c)
47
+
48
+ for (const child of children) {
49
+ if (isHastText(child)) {
50
+ if ((child as HastText).value.trim() || nodes.length > 0) {
51
+ nodes.push(textFromHastText(child))
52
+ }
53
+ } else if (isHastElement(child)) {
54
+ const node = yield* inlineNodeFromHastElement(child, parseChildren)
55
+ if (node) nodes.push(node)
56
+ }
57
+ }
58
+ return nodes
59
+ })
60
+
61
+ /**
62
+ * Convert HAST element to AST block node.
63
+ */
64
+ export const blockNodeFromHastElement = (
65
+ element: HastElement
66
+ ): Effect.Effect<BlockNode | null, ParseResult.ParseError> =>
67
+ Effect.gen(function*() {
68
+ const tagName = element.tagName.toLowerCase()
69
+
70
+ // Heading
71
+ if (/^h[1-6]$/.test(tagName)) {
72
+ const levelStr = tagName[1]
73
+ if (!levelStr) return null
74
+ const level = parseInt(levelStr) as 1 | 2 | 3 | 4 | 5 | 6
75
+ const children = yield* parseHastChildrenToInline(element.children)
76
+ return new Heading({ level, children })
77
+ }
78
+
79
+ // Paragraph
80
+ if (tagName === "p") {
81
+ const children = yield* parseHastChildrenToInline(element.children)
82
+ const style = element.properties?.style as string | undefined
83
+ let alignment: "left" | "center" | "right" | undefined
84
+ let indent: number | undefined
85
+
86
+ if (style) {
87
+ const alignMatch = style.match(/text-align:\s*(left|center|right)/)
88
+ if (alignMatch?.[1]) {
89
+ alignment = alignMatch[1] as "left" | "center" | "right"
90
+ }
91
+ const marginMatch = style.match(/margin-left:\s*(\d+(?:\.\d+)?)\s*px/)
92
+ if (marginMatch?.[1]) {
93
+ indent = parseFloat(marginMatch[1])
94
+ }
95
+ }
96
+
97
+ if (alignment !== undefined || indent !== undefined) {
98
+ return new Paragraph({
99
+ children,
100
+ ...(alignment !== undefined ? { alignment } : {}),
101
+ ...(indent !== undefined ? { indent } : {})
102
+ })
103
+ }
104
+ return new Paragraph({ children })
105
+ }
106
+
107
+ // Code block
108
+ if (tagName === "pre") {
109
+ const codeEl = element.children.find(
110
+ (c): c is HastElement => isHastElement(c) && c.tagName === "code"
111
+ )
112
+ const code = codeEl ? getTextContent(codeEl) : getTextContent(element)
113
+ const language = (element.properties?.["dataLanguage"] as string) || undefined
114
+ return new CodeBlock({ code, language })
115
+ }
116
+
117
+ // Thematic break
118
+ if (tagName === "hr") {
119
+ return new ThematicBreak({})
120
+ }
121
+
122
+ // Image
123
+ if (tagName === "img") {
124
+ const src = element.properties?.src as string | undefined
125
+ const dataAttachment = element.properties?.["dataAttachment"] as string | undefined
126
+ const dataAlign = element.properties?.["dataAlign"] as string | undefined
127
+ const dataWidth = element.properties?.["dataWidth"] as string | undefined
128
+ const alt = (element.properties?.alt as string) || undefined
129
+
130
+ if (dataAttachment) {
131
+ return new Image({
132
+ attachment: { filename: dataAttachment },
133
+ alt,
134
+ ...(dataAlign ? { align: dataAlign } : {}),
135
+ ...(dataWidth ? { width: parseInt(dataWidth) } : {})
136
+ })
137
+ }
138
+
139
+ if (!src) return null
140
+ return new Image({
141
+ src,
142
+ alt,
143
+ title: (element.properties?.title as string) || undefined
144
+ })
145
+ }
146
+
147
+ // Table
148
+ if (tagName === "table") {
149
+ return yield* parseTable(element)
150
+ }
151
+
152
+ // Task list
153
+ if (tagName === "ul" && element.properties?.["dataMacro"] === "task-list") {
154
+ return yield* parseTaskList(element)
155
+ }
156
+
157
+ // Lists
158
+ if (tagName === "ul" || tagName === "ol") {
159
+ return yield* parseList(element, tagName === "ol")
160
+ }
161
+
162
+ // Block quote
163
+ if (tagName === "blockquote") {
164
+ const children = yield* parseHastChildrenToSimpleBlocks(element.children)
165
+ return {
166
+ _tag: "BlockQuote" as const,
167
+ version: 1,
168
+ children
169
+ } satisfies BlockQuote
170
+ }
171
+
172
+ // Unknown block element
173
+ return new UnsupportedBlock({
174
+ rawHtml: hastElementToHtml(element),
175
+ source: "confluence"
176
+ })
177
+ })
178
+
179
+ /**
180
+ * Convert AST block node to HAST element.
181
+ */
182
+ export const blockNodeToHast = (node: BlockNode): HastElement => {
183
+ switch (node._tag) {
184
+ case "Heading":
185
+ return makeHastElement(
186
+ `h${node.level}`,
187
+ {},
188
+ node.children.map(inlineNodeToHast)
189
+ )
190
+ case "Paragraph": {
191
+ const style = [
192
+ node.alignment ? `text-align: ${node.alignment}` : "",
193
+ node.indent ? `margin-left: ${node.indent}px` : ""
194
+ ].filter(Boolean).join("; ")
195
+ return makeHastElement(
196
+ "p",
197
+ style ? { style } : {},
198
+ node.children.map(inlineNodeToHast)
199
+ )
200
+ }
201
+ case "CodeBlock":
202
+ return makeHastElement(
203
+ "pre",
204
+ node.language ? { dataLanguage: node.language } : {},
205
+ [makeHastElement("code", {}, [makeHastText(node.code)])]
206
+ )
207
+ case "ThematicBreak":
208
+ return makeHastElement("hr")
209
+ case "Image":
210
+ if (node.attachment) {
211
+ return makeHastElement("img", {
212
+ dataAttachment: node.attachment.filename,
213
+ ...(node.alt ? { alt: node.alt } : {}),
214
+ ...(node.align ? { dataAlign: node.align } : {}),
215
+ ...(node.width ? { dataWidth: String(node.width) } : {})
216
+ })
217
+ }
218
+ return makeHastElement("img", {
219
+ src: node.src ?? "",
220
+ ...(node.alt ? { alt: node.alt } : {}),
221
+ ...(node.title ? { title: node.title } : {})
222
+ })
223
+ case "Table":
224
+ return tableToHast(node)
225
+ case "BlockQuote":
226
+ return makeHastElement("blockquote", {}, node.children.map(blockNodeToHast))
227
+ case "List":
228
+ return listToHast(node)
229
+ case "TaskList":
230
+ return taskListToHast(node)
231
+ case "UnsupportedBlock":
232
+ return makeHastElement("div", { dangerouslySetInnerHTML: node.rawHtml ?? node.rawMarkdown ?? "" })
233
+ }
234
+ }
235
+
236
+ /**
237
+ * Convert AST block node to MDAST block content.
238
+ */
239
+ export const blockNodeToMdast = (node: BlockNode): MdastBlockContent => {
240
+ switch (node._tag) {
241
+ case "Heading":
242
+ return makeMdastHeading(node.level, node.children.map(inlineNodeToMdast))
243
+ case "Paragraph":
244
+ return makeMdastParagraph(node.children.map(inlineNodeToMdast))
245
+ case "CodeBlock":
246
+ return makeMdastCode(node.code, node.language)
247
+ case "ThematicBreak":
248
+ return { type: "thematicBreak" }
249
+ case "Image":
250
+ // MDAST doesn't have block images at root - wrap in paragraph
251
+ return makeMdastParagraph([{
252
+ type: "image",
253
+ url: node.src ?? node.attachment?.filename ?? "",
254
+ alt: node.alt ?? null,
255
+ title: node.title ?? null
256
+ }])
257
+ case "Table":
258
+ return tableToMdast(node)
259
+ case "BlockQuote":
260
+ return {
261
+ type: "blockquote",
262
+ children: node.children.map(blockNodeToMdast)
263
+ }
264
+ case "List":
265
+ return listToMdast(node)
266
+ case "TaskList":
267
+ // Convert to list with checkboxes
268
+ return {
269
+ type: "list",
270
+ ordered: false,
271
+ children: node.children.map((item) => ({
272
+ type: "listItem",
273
+ checked: item.status === "complete",
274
+ children: [{
275
+ type: "paragraph",
276
+ children: item.body.map(inlineNodeToMdast)
277
+ }]
278
+ }))
279
+ }
280
+ case "UnsupportedBlock":
281
+ return {
282
+ type: "html",
283
+ value: node.rawHtml ?? node.rawMarkdown ?? ""
284
+ }
285
+ }
286
+ }
287
+
288
+ /**
289
+ * Convert MDAST block content to AST block node.
290
+ */
291
+ export const blockNodeFromMdast = (node: MdastBlockContent): BlockNode => {
292
+ switch (node.type) {
293
+ case "heading":
294
+ return new Heading({
295
+ level: node.depth,
296
+ children: [] // Would need full inline conversion
297
+ })
298
+ case "paragraph":
299
+ return new Paragraph({ children: [] })
300
+ case "code":
301
+ return new CodeBlock({ code: node.value, language: node.lang })
302
+ case "thematicBreak":
303
+ return new ThematicBreak({})
304
+ case "blockquote":
305
+ return {
306
+ _tag: "BlockQuote",
307
+ version: 1,
308
+ children: node.children.map(blockNodeFromMdast)
309
+ } as BlockQuote
310
+ case "list":
311
+ return {
312
+ _tag: "List",
313
+ version: 1,
314
+ ordered: node.ordered ?? false,
315
+ start: node.start,
316
+ children: node.children.map((item) => ({
317
+ _tag: "ListItem" as const,
318
+ checked: item.checked ?? undefined,
319
+ children: [] // Would need full block conversion
320
+ }))
321
+ } as List
322
+ case "table":
323
+ return new Table({ rows: [] })
324
+ case "html":
325
+ return new UnsupportedBlock({ rawMarkdown: node.value, source: "markdown" })
326
+ default:
327
+ return new UnsupportedBlock({ rawMarkdown: JSON.stringify(node), source: "markdown" })
328
+ }
329
+ }
330
+
331
+ // Helper functions
332
+
333
+ /**
334
+ * Parse table element.
335
+ */
336
+ const parseTable = (element: HastElement): Effect.Effect<Table, ParseResult.ParseError> =>
337
+ Effect.gen(function*() {
338
+ let header: TableRow | undefined
339
+ const rows: Array<TableRow> = []
340
+
341
+ for (const child of element.children) {
342
+ if (!isHastElement(child)) continue
343
+
344
+ if (child.tagName === "thead") {
345
+ const tr = child.children.find(
346
+ (c): c is HastElement => isHastElement(c) && c.tagName === "tr"
347
+ )
348
+ if (tr) {
349
+ header = yield* parseTableRow(tr, true)
350
+ }
351
+ } else if (child.tagName === "tbody") {
352
+ for (const row of child.children) {
353
+ if (isHastElement(row) && row.tagName === "tr") {
354
+ const allTh = row.children
355
+ .filter(isHastElement)
356
+ .every((c) => c.tagName === "th")
357
+ if (allTh && !header && rows.length === 0) {
358
+ header = yield* parseTableRow(row, true)
359
+ } else {
360
+ rows.push(yield* parseTableRow(row, false))
361
+ }
362
+ }
363
+ }
364
+ } else if (child.tagName === "tr") {
365
+ rows.push(yield* parseTableRow(child, false))
366
+ }
367
+ }
368
+
369
+ return new Table({ header, rows })
370
+ })
371
+
372
+ /**
373
+ * Parse table row.
374
+ */
375
+ const parseTableRow = (
376
+ element: HastElement,
377
+ isHeader: boolean
378
+ ): Effect.Effect<TableRow, ParseResult.ParseError> =>
379
+ Effect.gen(function*() {
380
+ const cells: Array<TableCell> = []
381
+ for (const child of element.children) {
382
+ if (isHastElement(child) && (child.tagName === "td" || child.tagName === "th")) {
383
+ const cellIsHeader = isHeader || child.tagName === "th"
384
+ const children = yield* parseCellContent(child.children)
385
+ cells.push(new TableCell({ isHeader: cellIsHeader, children }))
386
+ }
387
+ }
388
+ return new TableRow({ cells })
389
+ })
390
+
391
+ /**
392
+ * Parse cell content, unwrapping single <p> elements.
393
+ */
394
+ const parseCellContent = (
395
+ children: ReadonlyArray<HastNode>
396
+ ): Effect.Effect<ReadonlyArray<InlineNode>, ParseResult.ParseError> =>
397
+ Effect.gen(function*() {
398
+ const elementChildren = children.filter((c) => {
399
+ if (isHastElement(c)) return true
400
+ if (isHastText(c) && c.value.trim()) return true
401
+ return false
402
+ })
403
+
404
+ if (elementChildren.length === 1) {
405
+ const first = elementChildren[0]
406
+ if (first && isHastElement(first) && first.tagName === "p") {
407
+ return yield* parseHastChildrenToInline(first.children)
408
+ }
409
+ }
410
+
411
+ return yield* parseHastChildrenToInline(children)
412
+ })
413
+
414
+ /**
415
+ * Parse task list element.
416
+ */
417
+ const parseTaskList = (element: HastElement): Effect.Effect<TaskList, ParseResult.ParseError> =>
418
+ Effect.gen(function*() {
419
+ const items: Array<TaskItem> = []
420
+
421
+ for (const child of element.children) {
422
+ if (isHastElement(child) && child.tagName === "li") {
423
+ const id = (child.properties?.["dataTaskId"] as string) || ""
424
+ const uuid = (child.properties?.["dataTaskUuid"] as string) || ""
425
+ const status = (child.properties?.["dataTaskStatus"] as string) === "complete"
426
+ ? "complete" as const
427
+ : "incomplete" as const
428
+ const body = yield* parseHastChildrenToInline(child.children)
429
+
430
+ items.push({
431
+ _tag: "TaskItem",
432
+ id,
433
+ uuid,
434
+ status,
435
+ body
436
+ })
437
+ }
438
+ }
439
+
440
+ return {
441
+ _tag: "TaskList" as const,
442
+ version: 1,
443
+ children: items
444
+ }
445
+ })
446
+
447
+ /**
448
+ * Parse list element.
449
+ */
450
+ const parseList = (
451
+ element: HastElement,
452
+ ordered: boolean
453
+ ): Effect.Effect<List, ParseResult.ParseError> =>
454
+ Effect.gen(function*() {
455
+ const items: Array<ListItem> = []
456
+ const startProp = element.properties?.start
457
+ const start = ordered && startProp ? parseInt(String(startProp)) : undefined
458
+
459
+ for (const child of element.children) {
460
+ if (isHastElement(child) && child.tagName === "li") {
461
+ const children = yield* parseListItemContent(child.children)
462
+ const checkbox = child.children.find(
463
+ (c): c is HastElement =>
464
+ isHastElement(c) &&
465
+ c.tagName === "input" &&
466
+ c.properties?.type === "checkbox"
467
+ )
468
+ const checked = checkbox ? (checkbox.properties?.checked === true) : undefined
469
+ if (checked !== undefined) {
470
+ items.push({ _tag: "ListItem", checked, children })
471
+ } else {
472
+ items.push({ _tag: "ListItem", children })
473
+ }
474
+ }
475
+ }
476
+
477
+ if (start !== undefined) {
478
+ return { _tag: "List" as const, version: 1, ordered, start, children: items }
479
+ }
480
+ return { _tag: "List" as const, version: 1, ordered, children: items }
481
+ })
482
+
483
+ type SimpleBlock = Heading | Paragraph | CodeBlock | ThematicBreak | Image | Table | UnsupportedBlock
484
+
485
+ /**
486
+ * Parse HAST children to simple block nodes.
487
+ */
488
+ const parseHastChildrenToSimpleBlocks = (
489
+ children: ReadonlyArray<HastNode>
490
+ ): Effect.Effect<Array<SimpleBlock>, ParseResult.ParseError> =>
491
+ Effect.gen(function*() {
492
+ const blocks: Array<SimpleBlock> = []
493
+ for (const child of children) {
494
+ if (isHastElement(child)) {
495
+ const tagName = child.tagName.toLowerCase()
496
+
497
+ if (/^h[1-6]$/.test(tagName)) {
498
+ const levelStr = tagName[1]
499
+ if (levelStr) {
500
+ const level = parseInt(levelStr) as 1 | 2 | 3 | 4 | 5 | 6
501
+ const inlineChildren = yield* parseHastChildrenToInline(child.children)
502
+ blocks.push(new Heading({ level, children: inlineChildren }))
503
+ }
504
+ } else if (tagName === "p") {
505
+ const inlineChildren = yield* parseHastChildrenToInline(child.children)
506
+ blocks.push(new Paragraph({ children: inlineChildren }))
507
+ } else if (tagName === "pre") {
508
+ const codeEl = child.children.find(
509
+ (c): c is HastElement => isHastElement(c) && c.tagName === "code"
510
+ )
511
+ const code = codeEl ? getTextContent(codeEl) : getTextContent(child)
512
+ blocks.push(new CodeBlock({ code }))
513
+ } else if (tagName === "hr") {
514
+ blocks.push(new ThematicBreak({}))
515
+ } else if (tagName === "img") {
516
+ const src = child.properties?.src as string | undefined
517
+ if (src) blocks.push(new Image({ src }))
518
+ } else if (tagName === "table") {
519
+ blocks.push(yield* parseTable(child))
520
+ } else {
521
+ blocks.push(new UnsupportedBlock({ rawHtml: hastElementToHtml(child), source: "confluence" }))
522
+ }
523
+ }
524
+ }
525
+ return blocks
526
+ })
527
+
528
+ /**
529
+ * Parse list item content.
530
+ */
531
+ const parseListItemContent = (
532
+ children: ReadonlyArray<HastNode>
533
+ ): Effect.Effect<Array<SimpleBlock>, ParseResult.ParseError> =>
534
+ Effect.gen(function*() {
535
+ const blocks: Array<SimpleBlock> = []
536
+
537
+ const hasDirectInlineContent = children.some((child) => {
538
+ if (isHastText(child)) {
539
+ return child.value.trim() !== ""
540
+ }
541
+ if (isHastElement(child)) {
542
+ const tagName = child.tagName.toLowerCase()
543
+ return ["a", "strong", "em", "b", "i", "u", "code", "span", "del", "sub", "sup"].includes(tagName)
544
+ }
545
+ return false
546
+ })
547
+
548
+ if (hasDirectInlineContent) {
549
+ const inlineChildren = yield* parseHastChildrenToInline(children)
550
+ if (inlineChildren.length > 0) {
551
+ blocks.push(new Paragraph({ children: inlineChildren }))
552
+ }
553
+ return blocks
554
+ }
555
+
556
+ for (const child of children) {
557
+ if (!isHastElement(child)) continue
558
+ const tagName = child.tagName.toLowerCase()
559
+
560
+ if (tagName === "p") {
561
+ const inlineChildren = yield* parseHastChildrenToInline(child.children)
562
+ blocks.push(new Paragraph({ children: inlineChildren }))
563
+ } else if (tagName === "ul" || tagName === "ol") {
564
+ blocks.push(new UnsupportedBlock({ rawHtml: hastElementToHtml(child), source: "confluence" }))
565
+ } else if (tagName === "pre") {
566
+ const codeEl = child.children.find(
567
+ (c): c is HastElement => isHastElement(c) && c.tagName === "code"
568
+ )
569
+ const code = codeEl ? getTextContent(codeEl) : getTextContent(child)
570
+ blocks.push(new CodeBlock({ code }))
571
+ } else if (tagName === "hr") {
572
+ blocks.push(new ThematicBreak({}))
573
+ } else if (tagName === "img") {
574
+ const src = child.properties?.src as string | undefined
575
+ if (src) blocks.push(new Image({ src }))
576
+ } else if (tagName === "table") {
577
+ blocks.push(yield* parseTable(child))
578
+ } else if (/^h[1-6]$/.test(tagName)) {
579
+ const levelStr = tagName[1]
580
+ if (levelStr) {
581
+ const level = parseInt(levelStr) as 1 | 2 | 3 | 4 | 5 | 6
582
+ const inlineChildren = yield* parseHastChildrenToInline(child.children)
583
+ blocks.push(new Heading({ level, children: inlineChildren }))
584
+ }
585
+ }
586
+ }
587
+
588
+ return blocks
589
+ })
590
+
591
+ /**
592
+ * Convert table to HAST.
593
+ */
594
+ const tableToHast = (table: Table): HastElement => {
595
+ const rows = table.rows.map((row) =>
596
+ makeHastElement(
597
+ "tr",
598
+ {},
599
+ row.cells.map((cell) =>
600
+ makeHastElement(
601
+ cell.isHeader ? "th" : "td",
602
+ {},
603
+ cell.children.map(inlineNodeToHast)
604
+ )
605
+ )
606
+ )
607
+ )
608
+
609
+ if (table.header) {
610
+ const headerRow = makeHastElement(
611
+ "tr",
612
+ {},
613
+ table.header.cells.map((cell) => makeHastElement("th", {}, cell.children.map(inlineNodeToHast)))
614
+ )
615
+ return makeHastElement("table", {}, [
616
+ makeHastElement("thead", {}, [headerRow]),
617
+ makeHastElement("tbody", {}, rows)
618
+ ])
619
+ }
620
+
621
+ return makeHastElement("table", {}, [makeHastElement("tbody", {}, rows)])
622
+ }
623
+
624
+ /**
625
+ * Convert table to MDAST.
626
+ */
627
+ const tableToMdast = (table: Table): MdastBlockContent => {
628
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
629
+ const rows: Array<any> = []
630
+
631
+ if (table.header) {
632
+ rows.push({
633
+ type: "tableRow",
634
+ children: table.header.cells.map((cell) => ({
635
+ type: "tableCell",
636
+ children: cell.children.map(inlineNodeToMdast)
637
+ }))
638
+ })
639
+ }
640
+
641
+ for (const row of table.rows) {
642
+ rows.push({
643
+ type: "tableRow",
644
+ children: row.cells.map((cell) => ({
645
+ type: "tableCell",
646
+ children: cell.children.map(inlineNodeToMdast)
647
+ }))
648
+ })
649
+ }
650
+
651
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
652
+ return { type: "table", children: rows } as any
653
+ }
654
+
655
+ /**
656
+ * Convert list to HAST.
657
+ */
658
+ const listToHast = (list: List): HastElement => {
659
+ const items = list.children.map((item) => makeHastElement("li", {}, item.children.map(blockNodeToHast)))
660
+ return makeHastElement(
661
+ list.ordered ? "ol" : "ul",
662
+ list.start !== undefined ? { start: String(list.start) } : {},
663
+ items
664
+ )
665
+ }
666
+
667
+ /**
668
+ * Convert list to MDAST.
669
+ */
670
+ const listToMdast = (list: List): MdastBlockContent => {
671
+ const result = {
672
+ type: "list" as const,
673
+ ordered: list.ordered,
674
+ children: list.children.map((item) => ({
675
+ type: "listItem" as const,
676
+ checked: item.checked ?? null,
677
+ children: item.children.map(blockNodeToMdast)
678
+ }))
679
+ }
680
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
681
+ if (list.start !== undefined) (result as any).start = list.start
682
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
683
+ return result as any
684
+ }
685
+
686
+ /**
687
+ * Convert task list to HAST.
688
+ */
689
+ const taskListToHast = (taskList: TaskList): HastElement => {
690
+ const items = taskList.children.map((item) =>
691
+ makeHastElement(
692
+ "li",
693
+ {
694
+ dataTaskId: item.id,
695
+ dataTaskUuid: item.uuid,
696
+ dataTaskStatus: item.status
697
+ },
698
+ item.body.map(inlineNodeToHast)
699
+ )
700
+ )
701
+ return makeHastElement("ul", { dataMacro: "task-list" }, items)
702
+ }
703
+
704
+ /**
705
+ * Convert HAST element to HTML string.
706
+ */
707
+ const hastElementToHtml = (element: HastElement): string => {
708
+ const props = Object.entries(element.properties || {})
709
+ .map(([k, v]) => {
710
+ const attrName = k.replace(/([A-Z])/g, "-$1").toLowerCase()
711
+ return `${attrName}="${String(v)}"`
712
+ })
713
+ .join(" ")
714
+ const openTag = props ? `<${element.tagName} ${props}>` : `<${element.tagName}>`
715
+ const closeTag = `</${element.tagName}>`
716
+ const content = element.children
717
+ .map((c) => {
718
+ if (isHastText(c)) return c.value
719
+ if (isHastElement(c)) return hastElementToHtml(c)
720
+ return ""
721
+ })
722
+ .join("")
723
+ return `${openTag}${content}${closeTag}`
724
+ }
725
+
726
+ /**
727
+ * Schema-based HAST to BlockNode array transform.
728
+ *
729
+ * @category Schemas
730
+ */
731
+ export const BlockNodesFromHast = Schema.transformOrFail(
732
+ Schema.Array(Schema.Unknown),
733
+ Schema.Array(Schema.Any),
734
+ {
735
+ strict: false,
736
+ decode: (hastNodes, _options, ast) =>
737
+ Effect.gen(function*() {
738
+ const results: Array<BlockNode> = []
739
+
740
+ for (const hastNode of hastNodes) {
741
+ if (isHastElement(hastNode as HastNode)) {
742
+ const node = yield* blockNodeFromHastElement(hastNode as HastElement)
743
+ if (node) results.push(node)
744
+ }
745
+ }
746
+
747
+ return results
748
+ }).pipe(
749
+ Effect.mapError((e) =>
750
+ e instanceof ParseResult.ParseError
751
+ ? e.issue
752
+ : new ParseResult.Type(ast, hastNodes, String(e))
753
+ )
754
+ ),
755
+ encode: (nodes, _options, _ast) => Effect.succeed(nodes.map(blockNodeToHast) as ReadonlyArray<unknown>)
756
+ }
757
+ )
758
+
759
+ /**
760
+ * Schema-based MDAST to BlockNode array transform.
761
+ *
762
+ * @category Schemas
763
+ */
764
+ export const BlockNodesFromMdast = Schema.transformOrFail(
765
+ Schema.Array(Schema.Unknown),
766
+ Schema.Array(Schema.Any),
767
+ {
768
+ strict: false,
769
+ decode: (mdastNodes, _options, _ast) =>
770
+ Effect.succeed(mdastNodes.map((n) => blockNodeFromMdast(n as MdastBlockContent))),
771
+ encode: (nodes, _options, _ast) => Effect.succeed(nodes.map(blockNodeToMdast) as ReadonlyArray<unknown>)
772
+ }
773
+ )