@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.
- package/CHANGELOG.md +73 -0
- package/LICENSE +21 -0
- package/README.md +282 -14
- package/dist/ConfluenceAuth.d.ts +76 -0
- package/dist/ConfluenceAuth.d.ts.map +1 -0
- package/dist/ConfluenceAuth.js +366 -0
- package/dist/ConfluenceAuth.js.map +1 -0
- package/dist/ConfluenceClient.d.ts +26 -12
- package/dist/ConfluenceClient.d.ts.map +1 -1
- package/dist/ConfluenceClient.js +139 -97
- package/dist/ConfluenceClient.js.map +1 -1
- package/dist/ConfluenceConfig.d.ts +4 -24
- package/dist/ConfluenceConfig.d.ts.map +1 -1
- package/dist/ConfluenceConfig.js +45 -7
- package/dist/ConfluenceConfig.js.map +1 -1
- package/dist/ConfluenceError.d.ts +99 -16
- package/dist/ConfluenceError.d.ts.map +1 -1
- package/dist/ConfluenceError.js +88 -5
- package/dist/ConfluenceError.js.map +1 -1
- package/dist/GitError.d.ts +103 -0
- package/dist/GitError.d.ts.map +1 -0
- package/dist/GitError.js +85 -0
- package/dist/GitError.js.map +1 -0
- package/dist/GitService.d.ts +175 -0
- package/dist/GitService.d.ts.map +1 -0
- package/dist/GitService.js +431 -0
- package/dist/GitService.js.map +1 -0
- package/dist/LocalFileSystem.d.ts +29 -4
- package/dist/LocalFileSystem.d.ts.map +1 -1
- package/dist/LocalFileSystem.js +80 -6
- package/dist/LocalFileSystem.js.map +1 -1
- package/dist/MarkdownConverter.d.ts +49 -2
- package/dist/MarkdownConverter.d.ts.map +1 -1
- package/dist/MarkdownConverter.js +73 -111
- package/dist/MarkdownConverter.js.map +1 -1
- package/dist/SchemaConverterError.d.ts +108 -0
- package/dist/SchemaConverterError.d.ts.map +1 -0
- package/dist/SchemaConverterError.js +84 -0
- package/dist/SchemaConverterError.js.map +1 -0
- package/dist/Schemas.d.ts +225 -1
- package/dist/Schemas.d.ts.map +1 -1
- package/dist/Schemas.js +155 -6
- package/dist/Schemas.js.map +1 -1
- package/dist/SyncEngine.d.ts +30 -20
- package/dist/SyncEngine.d.ts.map +1 -1
- package/dist/SyncEngine.js +566 -117
- package/dist/SyncEngine.js.map +1 -1
- package/dist/ast/BlockNode.d.ts +468 -0
- package/dist/ast/BlockNode.d.ts.map +1 -0
- package/dist/ast/BlockNode.js +319 -0
- package/dist/ast/BlockNode.js.map +1 -0
- package/dist/ast/Document.d.ts +244 -0
- package/dist/ast/Document.d.ts.map +1 -0
- package/dist/ast/Document.js +69 -0
- package/dist/ast/Document.js.map +1 -0
- package/dist/ast/InlineNode.d.ts +477 -0
- package/dist/ast/InlineNode.d.ts.map +1 -0
- package/dist/ast/InlineNode.js +263 -0
- package/dist/ast/InlineNode.js.map +1 -0
- package/dist/ast/MacroNode.d.ts +267 -0
- package/dist/ast/MacroNode.d.ts.map +1 -0
- package/dist/ast/MacroNode.js +164 -0
- package/dist/ast/MacroNode.js.map +1 -0
- package/dist/ast/index.d.ts +10 -0
- package/dist/ast/index.d.ts.map +1 -0
- package/dist/ast/index.js +14 -0
- package/dist/ast/index.js.map +1 -0
- package/dist/bin.js +33 -149
- package/dist/bin.js.map +1 -1
- package/dist/commands/auth.d.ts +15 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +86 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/clone.d.ts +12 -0
- package/dist/commands/clone.d.ts.map +1 -0
- package/dist/commands/clone.js +93 -0
- package/dist/commands/clone.js.map +1 -0
- package/dist/commands/delete.d.ts +13 -0
- package/dist/commands/delete.d.ts.map +1 -0
- package/dist/commands/delete.js +48 -0
- package/dist/commands/delete.js.map +1 -0
- package/dist/commands/errorHandler.d.ts +14 -0
- package/dist/commands/errorHandler.d.ts.map +1 -0
- package/dist/commands/errorHandler.js +33 -0
- package/dist/commands/errorHandler.js.map +1 -0
- package/dist/commands/git.d.ts +22 -0
- package/dist/commands/git.d.ts.map +1 -0
- package/dist/commands/git.js +72 -0
- package/dist/commands/git.js.map +1 -0
- package/dist/commands/index.d.ts +11 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +11 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/layers.d.ts +31 -0
- package/dist/commands/layers.d.ts.map +1 -0
- package/dist/commands/layers.js +137 -0
- package/dist/commands/layers.js.map +1 -0
- package/dist/commands/new.d.ts +9 -0
- package/dist/commands/new.d.ts.map +1 -0
- package/dist/commands/new.js +80 -0
- package/dist/commands/new.js.map +1 -0
- package/dist/commands/pageTree.d.ts +18 -0
- package/dist/commands/pageTree.d.ts.map +1 -0
- package/dist/commands/pageTree.js +20 -0
- package/dist/commands/pageTree.js.map +1 -0
- package/dist/commands/shared.d.ts +15 -0
- package/dist/commands/shared.d.ts.map +1 -0
- package/dist/commands/shared.js +27 -0
- package/dist/commands/shared.js.map +1 -0
- package/dist/commands/sync.d.ts +15 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +101 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/index.d.ts +10 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -1
- package/dist/internal/NodeLayers.d.ts +7 -0
- package/dist/internal/NodeLayers.d.ts.map +1 -0
- package/dist/internal/NodeLayers.js +19 -0
- package/dist/internal/NodeLayers.js.map +1 -0
- package/dist/internal/frontmatter.d.ts +10 -0
- package/dist/internal/frontmatter.d.ts.map +1 -1
- package/dist/internal/frontmatter.js +16 -0
- package/dist/internal/frontmatter.js.map +1 -1
- package/dist/internal/gitCommands.d.ts +78 -0
- package/dist/internal/gitCommands.d.ts.map +1 -0
- package/dist/internal/gitCommands.js +156 -0
- package/dist/internal/gitCommands.js.map +1 -0
- package/dist/internal/hashUtils.d.ts +42 -1
- package/dist/internal/hashUtils.d.ts.map +1 -1
- package/dist/internal/hashUtils.js +38 -2
- package/dist/internal/hashUtils.js.map +1 -1
- package/dist/internal/oauthServer.d.ts +55 -0
- package/dist/internal/oauthServer.d.ts.map +1 -0
- package/dist/internal/oauthServer.js +110 -0
- package/dist/internal/oauthServer.js.map +1 -0
- package/dist/internal/pathUtils.d.ts +21 -4
- package/dist/internal/pathUtils.d.ts.map +1 -1
- package/dist/internal/pathUtils.js +24 -13
- package/dist/internal/pathUtils.js.map +1 -1
- package/dist/internal/tokenStorage.d.ts +75 -0
- package/dist/internal/tokenStorage.d.ts.map +1 -0
- package/dist/internal/tokenStorage.js +149 -0
- package/dist/internal/tokenStorage.js.map +1 -0
- package/dist/internal/userCache.d.ts +42 -0
- package/dist/internal/userCache.d.ts.map +1 -0
- package/dist/internal/userCache.js +51 -0
- package/dist/internal/userCache.js.map +1 -0
- package/dist/parsers/ConfluenceParser.d.ts +26 -0
- package/dist/parsers/ConfluenceParser.d.ts.map +1 -0
- package/dist/parsers/ConfluenceParser.js +792 -0
- package/dist/parsers/ConfluenceParser.js.map +1 -0
- package/dist/parsers/MarkdownParser.d.ts +26 -0
- package/dist/parsers/MarkdownParser.d.ts.map +1 -0
- package/dist/parsers/MarkdownParser.js +873 -0
- package/dist/parsers/MarkdownParser.js.map +1 -0
- package/dist/parsers/index.d.ts +8 -0
- package/dist/parsers/index.d.ts.map +1 -0
- package/dist/parsers/index.js +8 -0
- package/dist/parsers/index.js.map +1 -0
- package/dist/schemas/ConfluenceSchema.d.ts +21 -0
- package/dist/schemas/ConfluenceSchema.d.ts.map +1 -0
- package/dist/schemas/ConfluenceSchema.js +38 -0
- package/dist/schemas/ConfluenceSchema.js.map +1 -0
- package/dist/schemas/ConversionSchema.d.ts +35 -0
- package/dist/schemas/ConversionSchema.d.ts.map +1 -0
- package/dist/schemas/ConversionSchema.js +208 -0
- package/dist/schemas/ConversionSchema.js.map +1 -0
- package/dist/schemas/MarkdownSchema.d.ts +21 -0
- package/dist/schemas/MarkdownSchema.d.ts.map +1 -0
- package/dist/schemas/MarkdownSchema.js +38 -0
- package/dist/schemas/MarkdownSchema.js.map +1 -0
- package/dist/schemas/hast/HastFromHtml.d.ts +27 -0
- package/dist/schemas/hast/HastFromHtml.d.ts.map +1 -0
- package/dist/schemas/hast/HastFromHtml.js +107 -0
- package/dist/schemas/hast/HastFromHtml.js.map +1 -0
- package/dist/schemas/hast/HastSchema.d.ts +195 -0
- package/dist/schemas/hast/HastSchema.d.ts.map +1 -0
- package/dist/schemas/hast/HastSchema.js +183 -0
- package/dist/schemas/hast/HastSchema.js.map +1 -0
- package/dist/schemas/hast/index.d.ts +9 -0
- package/dist/schemas/hast/index.d.ts.map +1 -0
- package/dist/schemas/hast/index.js +3 -0
- package/dist/schemas/hast/index.js.map +1 -0
- package/dist/schemas/index.d.ts +14 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +16 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/schemas/mdast/MdastFromMarkdown.d.ts +30 -0
- package/dist/schemas/mdast/MdastFromMarkdown.d.ts.map +1 -0
- package/dist/schemas/mdast/MdastFromMarkdown.js +79 -0
- package/dist/schemas/mdast/MdastFromMarkdown.js.map +1 -0
- package/dist/schemas/mdast/MdastSchema.d.ts +385 -0
- package/dist/schemas/mdast/MdastSchema.d.ts.map +1 -0
- package/dist/schemas/mdast/MdastSchema.js +266 -0
- package/dist/schemas/mdast/MdastSchema.js.map +1 -0
- package/dist/schemas/mdast/index.d.ts +10 -0
- package/dist/schemas/mdast/index.d.ts.map +1 -0
- package/dist/schemas/mdast/index.js +4 -0
- package/dist/schemas/mdast/index.js.map +1 -0
- package/dist/schemas/mdast/mdastToString.d.ts +13 -0
- package/dist/schemas/mdast/mdastToString.d.ts.map +1 -0
- package/dist/schemas/mdast/mdastToString.js +85 -0
- package/dist/schemas/mdast/mdastToString.js.map +1 -0
- package/dist/schemas/nodes/block/BlockSchema.d.ts +43 -0
- package/dist/schemas/nodes/block/BlockSchema.d.ts.map +1 -0
- package/dist/schemas/nodes/block/BlockSchema.js +634 -0
- package/dist/schemas/nodes/block/BlockSchema.js.map +1 -0
- package/dist/schemas/nodes/block/index.d.ts +7 -0
- package/dist/schemas/nodes/block/index.d.ts.map +1 -0
- package/dist/schemas/nodes/block/index.js +7 -0
- package/dist/schemas/nodes/block/index.js.map +1 -0
- package/dist/schemas/nodes/index.d.ts +9 -0
- package/dist/schemas/nodes/index.d.ts.map +1 -0
- package/dist/schemas/nodes/index.js +12 -0
- package/dist/schemas/nodes/index.js.map +1 -0
- package/dist/schemas/nodes/inline/InlineSchema.d.ts +48 -0
- package/dist/schemas/nodes/inline/InlineSchema.d.ts.map +1 -0
- package/dist/schemas/nodes/inline/InlineSchema.js +436 -0
- package/dist/schemas/nodes/inline/InlineSchema.js.map +1 -0
- package/dist/schemas/nodes/inline/index.d.ts +7 -0
- package/dist/schemas/nodes/inline/index.d.ts.map +1 -0
- package/dist/schemas/nodes/inline/index.js +7 -0
- package/dist/schemas/nodes/inline/index.js.map +1 -0
- package/dist/schemas/nodes/macro/MacroSchema.d.ts +27 -0
- package/dist/schemas/nodes/macro/MacroSchema.d.ts.map +1 -0
- package/dist/schemas/nodes/macro/MacroSchema.js +162 -0
- package/dist/schemas/nodes/macro/MacroSchema.js.map +1 -0
- package/dist/schemas/nodes/macro/index.d.ts +7 -0
- package/dist/schemas/nodes/macro/index.d.ts.map +1 -0
- package/dist/schemas/nodes/macro/index.js +7 -0
- package/dist/schemas/nodes/macro/index.js.map +1 -0
- package/dist/schemas/preprocessing/ConfluencePreprocessor.d.ts +24 -0
- package/dist/schemas/preprocessing/ConfluencePreprocessor.d.ts.map +1 -0
- package/dist/schemas/preprocessing/ConfluencePreprocessor.js +351 -0
- package/dist/schemas/preprocessing/ConfluencePreprocessor.js.map +1 -0
- package/dist/schemas/preprocessing/index.d.ts +8 -0
- package/dist/schemas/preprocessing/index.d.ts.map +1 -0
- package/dist/schemas/preprocessing/index.js +2 -0
- package/dist/schemas/preprocessing/index.js.map +1 -0
- package/dist/serializers/ConfluenceSerializer.d.ts +30 -0
- package/dist/serializers/ConfluenceSerializer.d.ts.map +1 -0
- package/dist/serializers/ConfluenceSerializer.js +551 -0
- package/dist/serializers/ConfluenceSerializer.js.map +1 -0
- package/dist/serializers/MarkdownSerializer.d.ts +34 -0
- package/dist/serializers/MarkdownSerializer.d.ts.map +1 -0
- package/dist/serializers/MarkdownSerializer.js +355 -0
- package/dist/serializers/MarkdownSerializer.js.map +1 -0
- package/dist/serializers/index.d.ts +8 -0
- package/dist/serializers/index.d.ts.map +1 -0
- package/dist/serializers/index.js +8 -0
- package/dist/serializers/index.js.map +1 -0
- package/package.json +32 -21
- package/src/ConfluenceAuth.ts +581 -0
- package/src/ConfluenceClient.ts +230 -165
- package/src/ConfluenceConfig.ts +63 -7
- package/src/ConfluenceError.ts +110 -14
- package/src/GitError.ts +92 -0
- package/src/GitService.ts +859 -0
- package/src/LocalFileSystem.ts +179 -9
- package/src/MarkdownConverter.ts +126 -122
- package/src/SchemaConverterError.ts +108 -0
- package/src/Schemas.ts +223 -6
- package/src/SyncEngine.ts +745 -162
- package/src/ast/BlockNode.ts +425 -0
- package/src/ast/Document.ts +90 -0
- package/src/ast/InlineNode.ts +323 -0
- package/src/ast/MacroNode.ts +245 -0
- package/src/ast/index.ts +83 -0
- package/src/bin.ts +50 -249
- package/src/commands/auth.ts +117 -0
- package/src/commands/clone.ts +145 -0
- package/src/commands/delete.ts +57 -0
- package/src/commands/errorHandler.ts +32 -0
- package/src/commands/git.ts +114 -0
- package/src/commands/index.ts +10 -0
- package/src/commands/layers.ts +211 -0
- package/src/commands/new.ts +99 -0
- package/src/commands/pageTree.ts +40 -0
- package/src/commands/shared.ts +35 -0
- package/src/commands/sync.ts +129 -0
- package/src/index.ts +21 -1
- package/src/internal/NodeLayers.ts +21 -0
- package/src/internal/frontmatter.ts +21 -0
- package/src/internal/gitCommands.ts +229 -0
- package/src/internal/hashUtils.ts +65 -3
- package/src/internal/oauthServer.ts +199 -0
- package/src/internal/pathUtils.ts +34 -17
- package/src/internal/tokenStorage.ts +240 -0
- package/src/internal/userCache.ts +90 -0
- package/src/parsers/ConfluenceParser.ts +950 -0
- package/src/parsers/MarkdownParser.ts +1198 -0
- package/src/parsers/index.ts +8 -0
- package/src/schemas/ConfluenceSchema.ts +56 -0
- package/src/schemas/ConversionSchema.ts +318 -0
- package/src/schemas/MarkdownSchema.ts +56 -0
- package/src/schemas/hast/HastFromHtml.ts +153 -0
- package/src/schemas/hast/HastSchema.ts +274 -0
- package/src/schemas/hast/index.ts +35 -0
- package/src/schemas/index.ts +20 -0
- package/src/schemas/mdast/MdastFromMarkdown.ts +118 -0
- package/src/schemas/mdast/MdastSchema.ts +566 -0
- package/src/schemas/mdast/index.ts +59 -0
- package/src/schemas/mdast/mdastToString.ts +102 -0
- package/src/schemas/nodes/block/BlockSchema.ts +773 -0
- package/src/schemas/nodes/block/index.ts +13 -0
- package/src/schemas/nodes/index.ts +20 -0
- package/src/schemas/nodes/inline/InlineSchema.ts +523 -0
- package/src/schemas/nodes/inline/index.ts +14 -0
- package/src/schemas/nodes/macro/MacroSchema.ts +226 -0
- package/src/schemas/nodes/macro/index.ts +6 -0
- package/src/schemas/preprocessing/ConfluencePreprocessor.ts +446 -0
- package/src/schemas/preprocessing/index.ts +8 -0
- package/src/serializers/ConfluenceSerializer.ts +717 -0
- package/src/serializers/MarkdownSerializer.ts +493 -0
- package/src/serializers/index.ts +8 -0
- package/test/GitService.test.ts +209 -0
- package/test/MarkdownConverter.test.ts +37 -3
- package/test/Schemas.test.ts +97 -2
- package/test/ast/BlockNode.test.ts +265 -0
- package/test/ast/Document.test.ts +126 -0
- package/test/ast/InlineNode.test.ts +161 -0
- package/test/fixtures/integration-test.html.fixture +103 -0
- package/test/fixtures/integration-test.md.expected +257 -0
- package/test/integration.test.ts +269 -0
- package/test/oauthServer.test.ts +50 -0
- package/test/parsers/ConfluenceParser.test.ts +283 -0
- package/test/schemas/ConfluencePreprocessor.test.ts +180 -0
- package/test/schemas/ConversionSchema.test.ts +159 -0
- package/test/schemas/HastSchema.test.ts +138 -0
- package/test/schemas/MdastSchema.test.ts +145 -0
- package/test/schemas/nodes/block/BlockSchema.test.ts +173 -0
- package/test/schemas/nodes/inline/InlineSchema.test.ts +198 -0
- package/test/schemas/nodes/macro/MacroSchema.test.ts +142 -0
- package/test/tokenStorage.test.ts +99 -0
package/src/LocalFileSystem.ts
CHANGED
|
@@ -12,10 +12,10 @@ import type { ContentHash } from "./Brand.js"
|
|
|
12
12
|
import type { FrontMatterError } from "./ConfluenceError.js"
|
|
13
13
|
import { FileSystemError } from "./ConfluenceError.js"
|
|
14
14
|
import type { ParsedMarkdown } from "./internal/frontmatter.js"
|
|
15
|
-
import { parseMarkdown, serializeMarkdown } from "./internal/frontmatter.js"
|
|
16
|
-
import { computeHash } from "./internal/hashUtils.js"
|
|
15
|
+
import { parseMarkdown, serializeMarkdown, serializeNewPageMarkdown } from "./internal/frontmatter.js"
|
|
16
|
+
import { computeHash, HashServiceLive } from "./internal/hashUtils.js"
|
|
17
17
|
import { pageToDir, pageToPath } from "./internal/pathUtils.js"
|
|
18
|
-
import type { PageFrontMatter } from "./Schemas.js"
|
|
18
|
+
import type { NewPageFrontMatter, PageFrontMatter } from "./Schemas.js"
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* Local markdown file representation.
|
|
@@ -28,6 +28,20 @@ export interface LocalFile {
|
|
|
28
28
|
readonly isNew: boolean
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Page tree node for directory structure.
|
|
33
|
+
*/
|
|
34
|
+
export interface PageTreeNode {
|
|
35
|
+
/** File path relative to docsPath */
|
|
36
|
+
readonly path: string
|
|
37
|
+
/** Page title */
|
|
38
|
+
readonly title: string
|
|
39
|
+
/** Page ID (null if new page) */
|
|
40
|
+
readonly pageId: string | null
|
|
41
|
+
/** Child pages */
|
|
42
|
+
readonly children: ReadonlyArray<PageTreeNode>
|
|
43
|
+
}
|
|
44
|
+
|
|
31
45
|
/**
|
|
32
46
|
* Local file system service for markdown operations.
|
|
33
47
|
*
|
|
@@ -38,7 +52,7 @@ export interface LocalFile {
|
|
|
38
52
|
*
|
|
39
53
|
* const program = Effect.gen(function* () {
|
|
40
54
|
* const fs = yield* LocalFileSystem
|
|
41
|
-
* const files = yield* fs.listMarkdownFiles(".docs
|
|
55
|
+
* const files = yield* fs.listMarkdownFiles(".confluence/docs")
|
|
42
56
|
* console.log(files)
|
|
43
57
|
* })
|
|
44
58
|
* ```
|
|
@@ -91,12 +105,35 @@ export class LocalFileSystem extends Context.Tag(
|
|
|
91
105
|
title: string,
|
|
92
106
|
hasChildren: boolean,
|
|
93
107
|
parentPath: string
|
|
94
|
-
) => string
|
|
108
|
+
) => Effect.Effect<string>
|
|
95
109
|
|
|
96
110
|
/**
|
|
97
111
|
* Get the directory path for a page's children.
|
|
98
112
|
*/
|
|
99
|
-
readonly getPageDir: (title: string, parentPath: string) => string
|
|
113
|
+
readonly getPageDir: (title: string, parentPath: string) => Effect.Effect<string>
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Write a raw file (e.g., source HTML).
|
|
117
|
+
*/
|
|
118
|
+
readonly writeFile: (filePath: string, content: string) => Effect.Effect<void, FileSystemError>
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Build a tree of pages from directory structure.
|
|
122
|
+
*/
|
|
123
|
+
readonly buildPageTree: (
|
|
124
|
+
docsPath: string,
|
|
125
|
+
rootPageId: string,
|
|
126
|
+
rootTitle: string
|
|
127
|
+
) => Effect.Effect<PageTreeNode, FileSystemError | FrontMatterError>
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Write a new page file with minimal front-matter.
|
|
131
|
+
*/
|
|
132
|
+
readonly writeNewPageFile: (
|
|
133
|
+
filePath: string,
|
|
134
|
+
frontMatter: NewPageFrontMatter,
|
|
135
|
+
content: string
|
|
136
|
+
) => Effect.Effect<void, FileSystemError>
|
|
100
137
|
}
|
|
101
138
|
>() {}
|
|
102
139
|
|
|
@@ -120,7 +157,9 @@ export const layer: Layer.Layer<LocalFileSystem, never, FileSystem.FileSystem |
|
|
|
120
157
|
)
|
|
121
158
|
|
|
122
159
|
const parsed: ParsedMarkdown = yield* parseMarkdown(filePath, content)
|
|
123
|
-
const contentHash = computeHash(parsed.content)
|
|
160
|
+
const contentHash = yield* computeHash(parsed.content).pipe(
|
|
161
|
+
Effect.provide(HashServiceLive)
|
|
162
|
+
)
|
|
124
163
|
|
|
125
164
|
return {
|
|
126
165
|
path: filePath,
|
|
@@ -211,6 +250,134 @@ export const layer: Layer.Layer<LocalFileSystem, never, FileSystem.FileSystem |
|
|
|
211
250
|
Effect.mapError((cause) => new FileSystemError({ operation: "read", path: filePath, cause }))
|
|
212
251
|
)
|
|
213
252
|
|
|
253
|
+
const writeFile = (
|
|
254
|
+
filePath: string,
|
|
255
|
+
content: string
|
|
256
|
+
): Effect.Effect<void, FileSystemError> =>
|
|
257
|
+
Effect.gen(function*() {
|
|
258
|
+
const dir = pathService.dirname(filePath)
|
|
259
|
+
yield* fs.makeDirectory(dir, { recursive: true }).pipe(
|
|
260
|
+
Effect.catchAll(() => Effect.void)
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
// Atomic write: write to temp file, then rename
|
|
264
|
+
const tempPath = `${filePath}.tmp.${Date.now()}`
|
|
265
|
+
yield* fs.writeFileString(tempPath, content).pipe(
|
|
266
|
+
Effect.mapError((cause) => new FileSystemError({ operation: "write", path: filePath, cause }))
|
|
267
|
+
)
|
|
268
|
+
yield* fs.rename(tempPath, filePath).pipe(
|
|
269
|
+
Effect.mapError((cause) => new FileSystemError({ operation: "rename", path: filePath, cause }))
|
|
270
|
+
)
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
// Wrap path functions to provide Path service
|
|
274
|
+
const getPagePath = (title: string, hasChildren: boolean, parentPath: string) =>
|
|
275
|
+
pageToPath(title, hasChildren, parentPath).pipe(
|
|
276
|
+
Effect.provide(Layer.succeed(Path.Path, pathService))
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
const getPageDir = (title: string, parentPath: string) =>
|
|
280
|
+
pageToDir(title, parentPath).pipe(
|
|
281
|
+
Effect.provide(Layer.succeed(Path.Path, pathService))
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
const writeNewPageFile = (
|
|
285
|
+
filePath: string,
|
|
286
|
+
frontMatter: NewPageFrontMatter,
|
|
287
|
+
content: string
|
|
288
|
+
): Effect.Effect<void, FileSystemError> =>
|
|
289
|
+
Effect.gen(function*() {
|
|
290
|
+
const dir = pathService.dirname(filePath)
|
|
291
|
+
yield* fs.makeDirectory(dir, { recursive: true }).pipe(
|
|
292
|
+
Effect.catchAll(() => Effect.void)
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
const serialized = serializeNewPageMarkdown(frontMatter, content)
|
|
296
|
+
|
|
297
|
+
// Atomic write: write to temp file, then rename
|
|
298
|
+
const tempPath = `${filePath}.tmp.${Date.now()}`
|
|
299
|
+
yield* fs.writeFileString(tempPath, serialized).pipe(
|
|
300
|
+
Effect.mapError((cause) => new FileSystemError({ operation: "write", path: filePath, cause }))
|
|
301
|
+
)
|
|
302
|
+
yield* fs.rename(tempPath, filePath).pipe(
|
|
303
|
+
Effect.mapError((cause) => new FileSystemError({ operation: "rename", path: filePath, cause }))
|
|
304
|
+
)
|
|
305
|
+
})
|
|
306
|
+
|
|
307
|
+
const buildPageTree = (
|
|
308
|
+
docsPath: string,
|
|
309
|
+
rootPageId: string,
|
|
310
|
+
rootTitle: string
|
|
311
|
+
): Effect.Effect<PageTreeNode, FileSystemError | FrontMatterError> =>
|
|
312
|
+
Effect.gen(function*() {
|
|
313
|
+
// Build a map of directory -> page info
|
|
314
|
+
const pagesByDir = new Map<string, { path: string; title: string; pageId: string | null }>()
|
|
315
|
+
|
|
316
|
+
// Get all markdown files
|
|
317
|
+
const files = yield* listMarkdownFiles(docsPath)
|
|
318
|
+
|
|
319
|
+
// Parse each file and map to its directory
|
|
320
|
+
for (const filePath of files) {
|
|
321
|
+
const localFile = yield* readMarkdownFile(filePath)
|
|
322
|
+
const relativePath = pathService.relative(docsPath, filePath)
|
|
323
|
+
const dirPath = pathService.dirname(relativePath)
|
|
324
|
+
const baseName = pathService.basename(filePath, ".md")
|
|
325
|
+
|
|
326
|
+
// Determine the page's "key" - the directory it owns children in
|
|
327
|
+
// e.g., "foo.md" owns "foo/" directory
|
|
328
|
+
const pageKey = dirPath === "." ? baseName : pathService.join(dirPath, baseName)
|
|
329
|
+
|
|
330
|
+
pagesByDir.set(pageKey, {
|
|
331
|
+
path: relativePath,
|
|
332
|
+
title: localFile.frontMatter?.title ?? baseName,
|
|
333
|
+
pageId: localFile.frontMatter?.pageId ?? null
|
|
334
|
+
})
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Build tree recursively
|
|
338
|
+
const buildNode = (
|
|
339
|
+
key: string,
|
|
340
|
+
path: string,
|
|
341
|
+
title: string,
|
|
342
|
+
pageId: string | null
|
|
343
|
+
): PageTreeNode => {
|
|
344
|
+
// Find children: pages whose parent directory matches this key
|
|
345
|
+
const children: Array<PageTreeNode> = []
|
|
346
|
+
|
|
347
|
+
for (const [childKey, childInfo] of pagesByDir.entries()) {
|
|
348
|
+
const childDir = pathService.dirname(childKey)
|
|
349
|
+
if (childDir === key) {
|
|
350
|
+
children.push(buildNode(childKey, childInfo.path, childInfo.title, childInfo.pageId))
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Sort children by title
|
|
355
|
+
children.sort((a, b) => a.title.localeCompare(b.title))
|
|
356
|
+
|
|
357
|
+
return { path, title, pageId, children }
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Find root-level pages (those in docsPath root, or the root page itself)
|
|
361
|
+
const rootChildren: Array<PageTreeNode> = []
|
|
362
|
+
|
|
363
|
+
for (const [key, info] of pagesByDir.entries()) {
|
|
364
|
+
const dir = pathService.dirname(key)
|
|
365
|
+
if (dir === ".") {
|
|
366
|
+
rootChildren.push(buildNode(key, info.path, info.title, info.pageId))
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Sort children by title
|
|
371
|
+
rootChildren.sort((a, b) => a.title.localeCompare(b.title))
|
|
372
|
+
|
|
373
|
+
return {
|
|
374
|
+
path: "",
|
|
375
|
+
title: rootTitle,
|
|
376
|
+
pageId: rootPageId,
|
|
377
|
+
children: rootChildren
|
|
378
|
+
}
|
|
379
|
+
})
|
|
380
|
+
|
|
214
381
|
return LocalFileSystem.of({
|
|
215
382
|
readMarkdownFile,
|
|
216
383
|
writeMarkdownFile,
|
|
@@ -218,8 +385,11 @@ export const layer: Layer.Layer<LocalFileSystem, never, FileSystem.FileSystem |
|
|
|
218
385
|
ensureDir,
|
|
219
386
|
deleteFile,
|
|
220
387
|
exists,
|
|
221
|
-
getPagePath
|
|
222
|
-
getPageDir
|
|
388
|
+
getPagePath,
|
|
389
|
+
getPageDir,
|
|
390
|
+
writeFile,
|
|
391
|
+
buildPageTree,
|
|
392
|
+
writeNewPageFile
|
|
223
393
|
})
|
|
224
394
|
})
|
|
225
395
|
)
|
package/src/MarkdownConverter.ts
CHANGED
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* HTML to Markdown conversion service using
|
|
2
|
+
* HTML to Markdown conversion service using AST-based approach.
|
|
3
3
|
*
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
6
|
import * as Context from "effect/Context"
|
|
7
7
|
import * as Effect from "effect/Effect"
|
|
8
8
|
import * as Layer from "effect/Layer"
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import rehypeStringify from "rehype-stringify"
|
|
12
|
-
import remarkGfm from "remark-gfm"
|
|
13
|
-
import remarkParse from "remark-parse"
|
|
14
|
-
import remarkRehype from "remark-rehype"
|
|
15
|
-
import remarkStringify from "remark-stringify"
|
|
16
|
-
import { unified } from "unified"
|
|
9
|
+
import * as Schema from "effect/Schema"
|
|
10
|
+
import type { Document } from "./ast/Document.js"
|
|
17
11
|
import { ConversionError } from "./ConfluenceError.js"
|
|
12
|
+
import { parseConfluenceHtml } from "./parsers/ConfluenceParser.js"
|
|
13
|
+
import { parseMarkdown } from "./parsers/MarkdownParser.js"
|
|
14
|
+
import { ParseError, type SerializeError } from "./SchemaConverterError.js"
|
|
15
|
+
import { ConfluenceToMarkdown, DocumentFromHast, DocumentFromMdast } from "./schemas/ConversionSchema.js"
|
|
16
|
+
import { HastFromHtml } from "./schemas/hast/index.js"
|
|
17
|
+
import { MdastFromMarkdown } from "./schemas/mdast/index.js"
|
|
18
|
+
import { PreprocessedHtmlFromConfluence } from "./schemas/preprocessing/index.js"
|
|
19
|
+
import { serializeToConfluence } from "./serializers/ConfluenceSerializer.js"
|
|
20
|
+
import { type SerializeOptions, serializeToMarkdown } from "./serializers/MarkdownSerializer.js"
|
|
18
21
|
|
|
19
22
|
/**
|
|
20
23
|
* Markdown conversion service for HTML <-> GFM conversion.
|
|
@@ -45,143 +48,144 @@ export class MarkdownConverter extends Context.Tag(
|
|
|
45
48
|
/**
|
|
46
49
|
* Convert Confluence storage format (HTML) to GitHub Flavored Markdown.
|
|
47
50
|
*/
|
|
48
|
-
readonly htmlToMarkdown: (
|
|
51
|
+
readonly htmlToMarkdown: (
|
|
52
|
+
html: string,
|
|
53
|
+
options?: SerializeOptions
|
|
54
|
+
) => Effect.Effect<string, ConversionError>
|
|
49
55
|
|
|
50
56
|
/**
|
|
51
57
|
* Convert GitHub Flavored Markdown to HTML (Confluence storage format).
|
|
52
58
|
*/
|
|
53
59
|
readonly markdownToHtml: (markdown: string) => Effect.Effect<string, ConversionError>
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Parse Confluence HTML to Document AST.
|
|
63
|
+
*/
|
|
64
|
+
readonly htmlToAst: (html: string) => Effect.Effect<Document, ParseError>
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Parse Markdown to Document AST.
|
|
68
|
+
*/
|
|
69
|
+
readonly markdownToAst: (markdown: string) => Effect.Effect<Document, ParseError>
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Serialize Document AST to Confluence HTML.
|
|
73
|
+
*/
|
|
74
|
+
readonly astToHtml: (doc: Document) => Effect.Effect<string, SerializeError>
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Serialize Document AST to Markdown.
|
|
78
|
+
*/
|
|
79
|
+
readonly astToMarkdown: (doc: Document) => Effect.Effect<string, SerializeError>
|
|
54
80
|
}
|
|
55
81
|
>() {}
|
|
56
82
|
|
|
57
|
-
/** Maximum HTML input size (1MB) to prevent ReDoS attacks */
|
|
58
|
-
const MAX_HTML_SIZE = 1024 * 1024
|
|
59
|
-
|
|
60
83
|
/**
|
|
61
|
-
*
|
|
62
|
-
*
|
|
84
|
+
* Layer that provides the MarkdownConverter service.
|
|
85
|
+
*
|
|
86
|
+
* @category Layers
|
|
63
87
|
*/
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
// Process structured macros iteratively to handle nesting safely
|
|
79
|
-
let iterations = 0
|
|
80
|
-
const maxIterations = 100 // Prevent infinite loops
|
|
81
|
-
|
|
82
|
-
while (iterations < maxIterations) {
|
|
83
|
-
const macroStart = result.indexOf("<ac:structured-macro")
|
|
84
|
-
if (macroStart === -1) break
|
|
85
|
-
|
|
86
|
-
// Find matching closing tag by counting nesting
|
|
87
|
-
let depth = 1
|
|
88
|
-
let pos = macroStart + 20 // Skip past opening tag start
|
|
89
|
-
let endPos = -1
|
|
90
|
-
|
|
91
|
-
while (pos < result.length && depth > 0) {
|
|
92
|
-
if (result.slice(pos, pos + 20) === "<ac:structured-macro") {
|
|
93
|
-
depth++
|
|
94
|
-
pos += 20
|
|
95
|
-
} else if (result.slice(pos, pos + 21) === "</ac:structured-macro") {
|
|
96
|
-
depth--
|
|
97
|
-
if (depth === 0) {
|
|
98
|
-
endPos = result.indexOf(">", pos) + 1
|
|
99
|
-
}
|
|
100
|
-
pos += 21
|
|
101
|
-
} else {
|
|
102
|
-
pos++
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (endPos === -1) break // Malformed HTML, stop processing
|
|
107
|
-
|
|
108
|
-
const macroContent = result.slice(macroStart, endPos)
|
|
109
|
-
|
|
110
|
-
// Extract content from macro
|
|
111
|
-
let replacement = ""
|
|
112
|
-
const plainBodyStart = macroContent.indexOf("<ac:plain-text-body><![CDATA[")
|
|
113
|
-
const plainBodyEnd = macroContent.indexOf("]]></ac:plain-text-body>")
|
|
114
|
-
if (plainBodyStart !== -1 && plainBodyEnd !== -1) {
|
|
115
|
-
const content = macroContent.slice(plainBodyStart + 29, plainBodyEnd)
|
|
116
|
-
replacement = `<pre><code>${content}</code></pre>`
|
|
117
|
-
} else {
|
|
118
|
-
const richBodyStart = macroContent.indexOf("<ac:rich-text-body>")
|
|
119
|
-
const richBodyEnd = macroContent.indexOf("</ac:rich-text-body>")
|
|
120
|
-
if (richBodyStart !== -1 && richBodyEnd !== -1) {
|
|
121
|
-
replacement = macroContent.slice(richBodyStart + 19, richBodyEnd)
|
|
122
|
-
}
|
|
123
|
-
}
|
|
88
|
+
export const layer: Layer.Layer<MarkdownConverter> = Layer.succeed(
|
|
89
|
+
MarkdownConverter,
|
|
90
|
+
MarkdownConverter.of({
|
|
91
|
+
htmlToMarkdown: (html, options) =>
|
|
92
|
+
Effect.gen(function*() {
|
|
93
|
+
// Use AST-based approach to preserve colors, underlines, etc.
|
|
94
|
+
const doc = yield* parseConfluenceHtml(html).pipe(
|
|
95
|
+
Effect.mapError((e) => new ConversionError({ direction: "htmlToMarkdown", cause: e.message }))
|
|
96
|
+
)
|
|
97
|
+
return yield* serializeToMarkdown(doc, options).pipe(
|
|
98
|
+
Effect.mapError((e) => new ConversionError({ direction: "htmlToMarkdown", cause: e.message }))
|
|
99
|
+
)
|
|
100
|
+
}),
|
|
124
101
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
102
|
+
markdownToHtml: (markdown) =>
|
|
103
|
+
Effect.gen(function*() {
|
|
104
|
+
// Use AST-based approach for consistency
|
|
105
|
+
const doc = yield* parseMarkdown(markdown).pipe(
|
|
106
|
+
Effect.mapError((e) => new ConversionError({ direction: "markdownToHtml", cause: e.message }))
|
|
107
|
+
)
|
|
108
|
+
return yield* serializeToConfluence(doc).pipe(
|
|
109
|
+
Effect.mapError((e) => new ConversionError({ direction: "markdownToHtml", cause: e.message }))
|
|
110
|
+
)
|
|
111
|
+
}),
|
|
128
112
|
|
|
129
|
-
|
|
130
|
-
result = result
|
|
131
|
-
.replace(/<ac:parameter[^>]{0,1000}>[^<]{0,10000}<\/ac:parameter>/gi, "")
|
|
132
|
-
.replace(/<\/?ac:[a-z-]{1,50}[^>]{0,1000}>/gi, "")
|
|
133
|
-
.replace(/<\/?ri:[a-z-]{1,50}[^>]{0,1000}\/?>/gi, "")
|
|
113
|
+
htmlToAst: (html) => parseConfluenceHtml(html),
|
|
134
114
|
|
|
135
|
-
|
|
136
|
-
})
|
|
115
|
+
markdownToAst: (markdown) => parseMarkdown(markdown),
|
|
137
116
|
|
|
138
|
-
|
|
139
|
-
* Create the markdown converter processor for HTML -> Markdown.
|
|
140
|
-
*/
|
|
141
|
-
const createHtmlToMdProcessor = () =>
|
|
142
|
-
unified()
|
|
143
|
-
.use(rehypeParse, { fragment: true })
|
|
144
|
-
.use(rehypeRemark)
|
|
145
|
-
.use(remarkGfm)
|
|
146
|
-
.use(remarkStringify)
|
|
117
|
+
astToHtml: (doc) => serializeToConfluence(doc),
|
|
147
118
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
const createMdToHtmlProcessor = () =>
|
|
152
|
-
unified()
|
|
153
|
-
.use(remarkParse)
|
|
154
|
-
.use(remarkGfm)
|
|
155
|
-
.use(remarkRehype)
|
|
156
|
-
.use(rehypeStringify)
|
|
119
|
+
astToMarkdown: (doc) => serializeToMarkdown(doc)
|
|
120
|
+
})
|
|
121
|
+
)
|
|
157
122
|
|
|
158
123
|
/**
|
|
159
|
-
*
|
|
124
|
+
* Schema-based layer for MarkdownConverter using Effect Schema transforms.
|
|
125
|
+
*
|
|
126
|
+
* This is an alternative implementation that uses the new Schema-based
|
|
127
|
+
* conversion pipeline. It provides the same API as the default layer.
|
|
128
|
+
*
|
|
129
|
+
* Note: For full fidelity, continue to use the default layer. This schema-based
|
|
130
|
+
* layer is useful for simpler use cases or when you want to leverage Schema
|
|
131
|
+
* composition.
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* ```typescript
|
|
135
|
+
* import { MarkdownConverter, schemaBasedLayer } from "@knpkv/confluence-to-markdown/MarkdownConverter"
|
|
136
|
+
* import { Effect } from "effect"
|
|
137
|
+
*
|
|
138
|
+
* const program = Effect.gen(function* () {
|
|
139
|
+
* const converter = yield* MarkdownConverter
|
|
140
|
+
* const md = yield* converter.htmlToMarkdown("<h1>Hello</h1>")
|
|
141
|
+
* })
|
|
142
|
+
*
|
|
143
|
+
* Effect.runPromise(
|
|
144
|
+
* program.pipe(Effect.provide(schemaBasedLayer))
|
|
145
|
+
* )
|
|
146
|
+
* ```
|
|
160
147
|
*
|
|
161
148
|
* @category Layers
|
|
162
149
|
*/
|
|
163
|
-
export const
|
|
150
|
+
export const schemaBasedLayer: Layer.Layer<MarkdownConverter> = Layer.succeed(
|
|
164
151
|
MarkdownConverter,
|
|
165
152
|
MarkdownConverter.of({
|
|
166
|
-
|
|
153
|
+
// Note: Schema-based layer doesn't support includeRawSource option yet
|
|
154
|
+
htmlToMarkdown: (html, options) =>
|
|
167
155
|
Effect.gen(function*() {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
})
|
|
176
|
-
}),
|
|
156
|
+
if (options?.includeRawSource !== undefined) {
|
|
157
|
+
yield* Effect.logWarning("schemaBasedLayer: includeRawSource option is not supported, use default layer")
|
|
158
|
+
}
|
|
159
|
+
return yield* Schema.decode(ConfluenceToMarkdown)(html)
|
|
160
|
+
}).pipe(
|
|
161
|
+
Effect.mapError((e) => new ConversionError({ direction: "htmlToMarkdown", cause: e.message }))
|
|
162
|
+
),
|
|
177
163
|
|
|
178
164
|
markdownToHtml: (markdown) =>
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
165
|
+
Schema.encode(ConfluenceToMarkdown)(markdown).pipe(
|
|
166
|
+
Effect.mapError((e) => new ConversionError({ direction: "markdownToHtml", cause: e.message }))
|
|
167
|
+
),
|
|
168
|
+
|
|
169
|
+
htmlToAst: (html) =>
|
|
170
|
+
Effect.gen(function*() {
|
|
171
|
+
const preprocessed = yield* Schema.decode(PreprocessedHtmlFromConfluence)(html)
|
|
172
|
+
const hast = yield* Schema.decode(HastFromHtml)(preprocessed)
|
|
173
|
+
return yield* Schema.decode(DocumentFromHast)(hast)
|
|
174
|
+
}).pipe(
|
|
175
|
+
Effect.mapError((e) => new ParseError({ source: "confluence", message: e.message }))
|
|
176
|
+
),
|
|
177
|
+
|
|
178
|
+
markdownToAst: (markdown) =>
|
|
179
|
+
Effect.gen(function*() {
|
|
180
|
+
const mdast = yield* Schema.decode(MdastFromMarkdown)(markdown)
|
|
181
|
+
return yield* Schema.decode(DocumentFromMdast)(mdast)
|
|
182
|
+
}).pipe(
|
|
183
|
+
Effect.mapError((e) => new ParseError({ source: "markdown", message: e.message }))
|
|
184
|
+
),
|
|
185
|
+
|
|
186
|
+
// For serialization, continue using the existing serializers for full fidelity
|
|
187
|
+
astToHtml: (doc) => serializeToConfluence(doc),
|
|
188
|
+
|
|
189
|
+
astToMarkdown: (doc) => serializeToMarkdown(doc)
|
|
186
190
|
})
|
|
187
191
|
)
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error types for schema-based conversion.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import * as Data from "effect/Data"
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Error thrown when parsing HTML or Markdown fails.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* import { Effect } from "effect"
|
|
14
|
+
* import { ParseError } from "@knpkv/confluence-to-markdown/SchemaConverterError"
|
|
15
|
+
*
|
|
16
|
+
* Effect.gen(function* () {
|
|
17
|
+
* // ... parsing operation
|
|
18
|
+
* }).pipe(
|
|
19
|
+
* Effect.catchTag("ParseError", (error) =>
|
|
20
|
+
* Effect.sync(() => console.error(`Parse error: ${error.message}`))
|
|
21
|
+
* )
|
|
22
|
+
* )
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* @category Errors
|
|
26
|
+
*/
|
|
27
|
+
export class ParseError extends Data.TaggedError("ParseError")<{
|
|
28
|
+
readonly source: "confluence" | "markdown"
|
|
29
|
+
readonly message: string
|
|
30
|
+
readonly position?: { readonly line: number; readonly column: number }
|
|
31
|
+
readonly rawContent?: string
|
|
32
|
+
}> {}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Error thrown when serializing AST to HTML or Markdown fails.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```typescript
|
|
39
|
+
* import { Effect } from "effect"
|
|
40
|
+
* import { SerializeError } from "@knpkv/confluence-to-markdown/SchemaConverterError"
|
|
41
|
+
*
|
|
42
|
+
* Effect.gen(function* () {
|
|
43
|
+
* // ... serialization operation
|
|
44
|
+
* }).pipe(
|
|
45
|
+
* Effect.catchTag("SerializeError", (error) =>
|
|
46
|
+
* Effect.sync(() => console.error(`Serialize error: ${error.message}`))
|
|
47
|
+
* )
|
|
48
|
+
* )
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
51
|
+
* @category Errors
|
|
52
|
+
*/
|
|
53
|
+
export class SerializeError extends Data.TaggedError("SerializeError")<{
|
|
54
|
+
readonly target: "confluence" | "markdown"
|
|
55
|
+
readonly nodeType: string
|
|
56
|
+
readonly message: string
|
|
57
|
+
}> {}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Error thrown when migrating between schema versions fails.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* import { Effect } from "effect"
|
|
65
|
+
* import { MigrationError } from "@knpkv/confluence-to-markdown/SchemaConverterError"
|
|
66
|
+
*
|
|
67
|
+
* Effect.gen(function* () {
|
|
68
|
+
* // ... migration operation
|
|
69
|
+
* }).pipe(
|
|
70
|
+
* Effect.catchTag("MigrationError", (error) =>
|
|
71
|
+
* Effect.sync(() =>
|
|
72
|
+
* console.error(`Migration error: ${error.nodeType} v${error.fromVersion} -> v${error.toVersion}`)
|
|
73
|
+
* )
|
|
74
|
+
* )
|
|
75
|
+
* )
|
|
76
|
+
* ```
|
|
77
|
+
*
|
|
78
|
+
* @category Errors
|
|
79
|
+
*/
|
|
80
|
+
export class MigrationError extends Data.TaggedError("MigrationError")<{
|
|
81
|
+
readonly nodeType: string
|
|
82
|
+
readonly fromVersion: number
|
|
83
|
+
readonly toVersion: number
|
|
84
|
+
readonly message: string
|
|
85
|
+
}> {}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Union of all schema converter errors.
|
|
89
|
+
*
|
|
90
|
+
* @category Errors
|
|
91
|
+
*/
|
|
92
|
+
export type SchemaConverterError = ParseError | SerializeError | MigrationError
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Type guard to check if error is a SchemaConverterError.
|
|
96
|
+
*
|
|
97
|
+
* @param error - The error to check
|
|
98
|
+
* @returns True if error is a SchemaConverterError
|
|
99
|
+
*
|
|
100
|
+
* @category Utilities
|
|
101
|
+
*/
|
|
102
|
+
export const isSchemaConverterError = (error: unknown): error is SchemaConverterError =>
|
|
103
|
+
typeof error === "object" &&
|
|
104
|
+
error !== null &&
|
|
105
|
+
"_tag" in error &&
|
|
106
|
+
["ParseError", "SerializeError", "MigrationError"].includes(
|
|
107
|
+
(error as { _tag: string })._tag
|
|
108
|
+
)
|