@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.
- package/CHANGELOG.md +60 -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 +356 -0
- package/dist/ConfluenceAuth.js.map +1 -0
- package/dist/ConfluenceClient.d.ts +26 -2
- package/dist/ConfluenceClient.d.ts.map +1 -1
- package/dist/ConfluenceClient.js +98 -92
- 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 +89 -6
- 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 +27 -16
- package/src/ConfluenceAuth.ts +571 -0
- package/src/ConfluenceClient.ts +188 -156
- 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
|
@@ -0,0 +1,859 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git operations service for internal version control.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import * as NodeCommandExecutor from "@effect/platform-node/NodeCommandExecutor"
|
|
7
|
+
import * as NodeContext from "@effect/platform-node/NodeContext"
|
|
8
|
+
import * as NodeFileSystem from "@effect/platform-node/NodeFileSystem"
|
|
9
|
+
import * as NodePath from "@effect/platform-node/NodePath"
|
|
10
|
+
import * as CommandExecutor from "@effect/platform/CommandExecutor"
|
|
11
|
+
import type * as PlatformError from "@effect/platform/Error"
|
|
12
|
+
import * as FileSystem from "@effect/platform/FileSystem"
|
|
13
|
+
import * as Path from "@effect/platform/Path"
|
|
14
|
+
import * as Context from "effect/Context"
|
|
15
|
+
import * as Effect from "effect/Effect"
|
|
16
|
+
import * as Layer from "effect/Layer"
|
|
17
|
+
import {
|
|
18
|
+
GitError,
|
|
19
|
+
GitMergeConflictError,
|
|
20
|
+
GitNoChangesError,
|
|
21
|
+
GitNotInitializedError,
|
|
22
|
+
GitNotInstalledError
|
|
23
|
+
} from "./GitError.js"
|
|
24
|
+
import {
|
|
25
|
+
getConflictedFiles,
|
|
26
|
+
GIT_LOG_FORMAT,
|
|
27
|
+
type GitLogEntry,
|
|
28
|
+
type GitStatusEntry,
|
|
29
|
+
parseGitLog,
|
|
30
|
+
parseGitStatus,
|
|
31
|
+
runGit,
|
|
32
|
+
runGitAllowEmpty
|
|
33
|
+
} from "./internal/gitCommands.js"
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Options for git commit.
|
|
37
|
+
*
|
|
38
|
+
* @category Types
|
|
39
|
+
*/
|
|
40
|
+
export interface GitCommitOptions {
|
|
41
|
+
readonly message: string
|
|
42
|
+
readonly author?: {
|
|
43
|
+
readonly name: string
|
|
44
|
+
readonly email: string
|
|
45
|
+
}
|
|
46
|
+
readonly date?: Date
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Options for git log.
|
|
51
|
+
*
|
|
52
|
+
* @category Types
|
|
53
|
+
*/
|
|
54
|
+
export interface GitLogOptions {
|
|
55
|
+
readonly oneline?: boolean
|
|
56
|
+
readonly n?: number
|
|
57
|
+
readonly since?: string
|
|
58
|
+
readonly file?: string
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Options for git diff.
|
|
63
|
+
*
|
|
64
|
+
* @category Types
|
|
65
|
+
*/
|
|
66
|
+
export interface GitDiffOptions {
|
|
67
|
+
readonly staged?: boolean
|
|
68
|
+
readonly commit?: string
|
|
69
|
+
readonly commit2?: string
|
|
70
|
+
readonly file?: string
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Options for git commit --amend.
|
|
75
|
+
*
|
|
76
|
+
* @category Types
|
|
77
|
+
*/
|
|
78
|
+
export interface GitAmendOptions {
|
|
79
|
+
readonly noEdit?: boolean
|
|
80
|
+
readonly message?: string
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Options for git reset.
|
|
85
|
+
*
|
|
86
|
+
* @category Types
|
|
87
|
+
*/
|
|
88
|
+
export interface GitResetOptions {
|
|
89
|
+
readonly hard?: boolean
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Git status result.
|
|
94
|
+
*
|
|
95
|
+
* @category Types
|
|
96
|
+
*/
|
|
97
|
+
export interface GitStatus {
|
|
98
|
+
readonly entries: ReadonlyArray<GitStatusEntry>
|
|
99
|
+
readonly hasChanges: boolean
|
|
100
|
+
readonly hasConflicts: boolean
|
|
101
|
+
readonly conflictedFiles: ReadonlyArray<string>
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Union of all git-related errors.
|
|
106
|
+
*
|
|
107
|
+
* @category Types
|
|
108
|
+
*/
|
|
109
|
+
export type GitServiceError =
|
|
110
|
+
| GitError
|
|
111
|
+
| GitNotInstalledError
|
|
112
|
+
| GitNotInitializedError
|
|
113
|
+
| GitNoChangesError
|
|
114
|
+
| GitMergeConflictError
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Git service interface.
|
|
118
|
+
*
|
|
119
|
+
* @category Service
|
|
120
|
+
*/
|
|
121
|
+
export interface GitServiceShape {
|
|
122
|
+
/** Validate git is installed, return version. */
|
|
123
|
+
readonly validateGit: () => Effect.Effect<string, GitNotInstalledError>
|
|
124
|
+
|
|
125
|
+
/** Check if .confluence/.git exists. */
|
|
126
|
+
readonly isInitialized: () => Effect.Effect<boolean>
|
|
127
|
+
|
|
128
|
+
/** Initialize git repo in .confluence/. */
|
|
129
|
+
readonly init: () => Effect.Effect<void, GitServiceError>
|
|
130
|
+
|
|
131
|
+
/** Stage all files. */
|
|
132
|
+
readonly addAll: () => Effect.Effect<void, GitServiceError>
|
|
133
|
+
|
|
134
|
+
/** Commit with options. */
|
|
135
|
+
readonly commit: (options: GitCommitOptions) => Effect.Effect<string, GitServiceError>
|
|
136
|
+
|
|
137
|
+
/** Get status. */
|
|
138
|
+
readonly status: () => Effect.Effect<GitStatus, GitServiceError>
|
|
139
|
+
|
|
140
|
+
/** Get log. */
|
|
141
|
+
readonly log: (options?: GitLogOptions) => Effect.Effect<ReadonlyArray<GitLogEntry>, GitServiceError>
|
|
142
|
+
|
|
143
|
+
/** Get diff. */
|
|
144
|
+
readonly diff: (options?: GitDiffOptions) => Effect.Effect<string, GitServiceError>
|
|
145
|
+
|
|
146
|
+
/** Check for merge conflicts. */
|
|
147
|
+
readonly hasConflicts: () => Effect.Effect<boolean, GitServiceError>
|
|
148
|
+
|
|
149
|
+
/** Continue merge after conflict resolution. */
|
|
150
|
+
readonly mergeContinue: () => Effect.Effect<void, GitServiceError>
|
|
151
|
+
|
|
152
|
+
/** Copy files from docsPath to .confluence/. */
|
|
153
|
+
readonly syncFromDocs: (
|
|
154
|
+
docsPath: string,
|
|
155
|
+
trackedPaths: ReadonlyArray<string>
|
|
156
|
+
) => Effect.Effect<void, GitServiceError>
|
|
157
|
+
|
|
158
|
+
/** Copy files from .confluence/ to docsPath. */
|
|
159
|
+
readonly syncToDocs: (
|
|
160
|
+
docsPath: string,
|
|
161
|
+
trackedPaths: ReadonlyArray<string>
|
|
162
|
+
) => Effect.Effect<void, GitServiceError>
|
|
163
|
+
|
|
164
|
+
/** Get current HEAD commit hash. */
|
|
165
|
+
readonly getHead: () => Effect.Effect<string, GitServiceError>
|
|
166
|
+
|
|
167
|
+
/** Get current branch name. */
|
|
168
|
+
readonly getCurrentBranch: () => Effect.Effect<string, GitServiceError>
|
|
169
|
+
|
|
170
|
+
/** Create a new branch at current HEAD. */
|
|
171
|
+
readonly createBranch: (name: string) => Effect.Effect<void, GitServiceError>
|
|
172
|
+
|
|
173
|
+
/** Checkout a branch or commit. */
|
|
174
|
+
readonly checkout: (ref: string) => Effect.Effect<void, GitServiceError>
|
|
175
|
+
|
|
176
|
+
/** Reset to a commit. */
|
|
177
|
+
readonly reset: (ref: string, options?: GitResetOptions) => Effect.Effect<void, GitServiceError>
|
|
178
|
+
|
|
179
|
+
/** Delete a branch. */
|
|
180
|
+
readonly deleteBranch: (name: string) => Effect.Effect<void, GitServiceError>
|
|
181
|
+
|
|
182
|
+
/** Get parent commit hash. */
|
|
183
|
+
readonly getParent: (ref: string) => Effect.Effect<string, GitServiceError>
|
|
184
|
+
|
|
185
|
+
/** Cherry-pick a commit. */
|
|
186
|
+
readonly cherryPick: (
|
|
187
|
+
ref: string,
|
|
188
|
+
options?: { strategy?: "ours" | "theirs" }
|
|
189
|
+
) => Effect.Effect<void, GitServiceError>
|
|
190
|
+
|
|
191
|
+
/** Get files changed in a commit. */
|
|
192
|
+
readonly getChangedFiles: (ref: string) => Effect.Effect<ReadonlyArray<string>, GitServiceError>
|
|
193
|
+
|
|
194
|
+
/** Get file content at a specific ref. */
|
|
195
|
+
readonly showFile: (ref: string, filePath: string) => Effect.Effect<string, GitServiceError>
|
|
196
|
+
|
|
197
|
+
/** Amend the current commit. */
|
|
198
|
+
readonly amend: (options?: GitAmendOptions) => Effect.Effect<void, GitServiceError>
|
|
199
|
+
|
|
200
|
+
/** Get commits between two refs (exclusive..inclusive). */
|
|
201
|
+
readonly logRange: (
|
|
202
|
+
from: string,
|
|
203
|
+
to: string
|
|
204
|
+
) => Effect.Effect<ReadonlyArray<GitLogEntry>, GitServiceError>
|
|
205
|
+
|
|
206
|
+
/** Check if a branch exists. */
|
|
207
|
+
readonly branchExists: (name: string) => Effect.Effect<boolean, GitServiceError>
|
|
208
|
+
|
|
209
|
+
/** Update a branch to point to a commit (without checkout). */
|
|
210
|
+
readonly updateBranch: (name: string, ref: string) => Effect.Effect<void, GitServiceError>
|
|
211
|
+
|
|
212
|
+
/** Merge a branch into current. */
|
|
213
|
+
readonly merge: (
|
|
214
|
+
branch: string,
|
|
215
|
+
options?: { noCommit?: boolean; message?: string }
|
|
216
|
+
) => Effect.Effect<void, GitServiceError>
|
|
217
|
+
|
|
218
|
+
/** Get deleted files between two refs (files in `from` but not in `to`). */
|
|
219
|
+
readonly getDeletedFiles: (
|
|
220
|
+
from: string,
|
|
221
|
+
to: string,
|
|
222
|
+
pathPrefix?: string
|
|
223
|
+
) => Effect.Effect<ReadonlyArray<string>, GitServiceError>
|
|
224
|
+
|
|
225
|
+
/** Get file content at a specific ref. */
|
|
226
|
+
readonly getFileContentAt: (
|
|
227
|
+
ref: string,
|
|
228
|
+
filePath: string
|
|
229
|
+
) => Effect.Effect<string, GitServiceError>
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Git service for internal version control operations.
|
|
234
|
+
*
|
|
235
|
+
* Manages a git repository at `.confluence/` for tracking synced files.
|
|
236
|
+
*
|
|
237
|
+
* @example
|
|
238
|
+
* ```typescript
|
|
239
|
+
* import { GitService } from "@knpkv/confluence-to-markdown/GitService"
|
|
240
|
+
* import { Effect } from "effect"
|
|
241
|
+
*
|
|
242
|
+
* const program = Effect.gen(function* () {
|
|
243
|
+
* const git = yield* GitService
|
|
244
|
+
* yield* git.init()
|
|
245
|
+
* yield* git.addAll()
|
|
246
|
+
* yield* git.commit({ message: "Initial commit" })
|
|
247
|
+
* })
|
|
248
|
+
* ```
|
|
249
|
+
*
|
|
250
|
+
* @category Service
|
|
251
|
+
*/
|
|
252
|
+
export class GitService extends Context.Tag("@knpkv/confluence-to-markdown/GitService")<
|
|
253
|
+
GitService,
|
|
254
|
+
GitServiceShape
|
|
255
|
+
>() {}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Map PlatformError to GitError.
|
|
259
|
+
*/
|
|
260
|
+
const mapFsError = (error: PlatformError.PlatformError): GitError =>
|
|
261
|
+
new GitError({ command: "filesystem", message: error.message })
|
|
262
|
+
|
|
263
|
+
// Dependencies layer for git operations (CommandExecutor + FileSystem + Path + NodeContext)
|
|
264
|
+
// NodeCommandExecutor requires FileSystem, so use provideMerge to satisfy its dependencies
|
|
265
|
+
const GitDepsLive = NodeCommandExecutor.layer.pipe(
|
|
266
|
+
Layer.provideMerge(NodeFileSystem.layer),
|
|
267
|
+
Layer.provideMerge(NodePath.layer),
|
|
268
|
+
Layer.provideMerge(NodeContext.layer)
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Copy a single file, creating parent directories as needed.
|
|
273
|
+
*/
|
|
274
|
+
const copyFileFn = (
|
|
275
|
+
fs: FileSystem.FileSystem,
|
|
276
|
+
pathService: Path.Path,
|
|
277
|
+
source: string,
|
|
278
|
+
dest: string
|
|
279
|
+
): Effect.Effect<void, GitError> =>
|
|
280
|
+
Effect.gen(function*() {
|
|
281
|
+
const destDir = pathService.dirname(dest)
|
|
282
|
+
yield* fs.makeDirectory(destDir, { recursive: true }).pipe(Effect.catchAll(() => Effect.void))
|
|
283
|
+
yield* fs.copyFile(source, dest).pipe(Effect.mapError(mapFsError))
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Copy a directory recursively.
|
|
288
|
+
*/
|
|
289
|
+
const copyDirectoryFn = (
|
|
290
|
+
fs: FileSystem.FileSystem,
|
|
291
|
+
pathService: Path.Path,
|
|
292
|
+
source: string,
|
|
293
|
+
dest: string
|
|
294
|
+
): Effect.Effect<void, GitError> =>
|
|
295
|
+
Effect.gen(function*() {
|
|
296
|
+
yield* fs.makeDirectory(dest, { recursive: true }).pipe(Effect.catchAll(() => Effect.void))
|
|
297
|
+
|
|
298
|
+
const entries = yield* fs.readDirectory(source).pipe(Effect.mapError(mapFsError))
|
|
299
|
+
|
|
300
|
+
for (const entry of entries) {
|
|
301
|
+
const sourcePath = pathService.join(source, entry)
|
|
302
|
+
const destPath = pathService.join(dest, entry)
|
|
303
|
+
|
|
304
|
+
const stat = yield* fs.stat(sourcePath).pipe(Effect.mapError(mapFsError))
|
|
305
|
+
|
|
306
|
+
if (stat.type === "Directory") {
|
|
307
|
+
yield* copyDirectoryFn(fs, pathService, sourcePath, destPath)
|
|
308
|
+
} else {
|
|
309
|
+
yield* copyFileFn(fs, pathService, sourcePath, destPath)
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Copy all markdown files recursively.
|
|
316
|
+
*/
|
|
317
|
+
const copyMarkdownFilesFn = (
|
|
318
|
+
fs: FileSystem.FileSystem,
|
|
319
|
+
pathService: Path.Path,
|
|
320
|
+
source: string,
|
|
321
|
+
dest: string
|
|
322
|
+
): Effect.Effect<void, GitError> =>
|
|
323
|
+
Effect.gen(function*() {
|
|
324
|
+
const exists = yield* fs.exists(source).pipe(Effect.catchAll(() => Effect.succeed(false)))
|
|
325
|
+
if (!exists) {
|
|
326
|
+
return
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const entries = yield* fs.readDirectory(source).pipe(Effect.mapError(mapFsError))
|
|
330
|
+
|
|
331
|
+
for (const entry of entries) {
|
|
332
|
+
// Skip .git directory and config.json
|
|
333
|
+
if (entry === ".git" || entry === "config.json") {
|
|
334
|
+
continue
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const sourcePath = pathService.join(source, entry)
|
|
338
|
+
const destPath = pathService.join(dest, entry)
|
|
339
|
+
|
|
340
|
+
const stat = yield* fs.stat(sourcePath).pipe(Effect.mapError(mapFsError))
|
|
341
|
+
|
|
342
|
+
if (stat.type === "Directory") {
|
|
343
|
+
yield* copyMarkdownFilesFn(fs, pathService, sourcePath, destPath)
|
|
344
|
+
} else if (entry.endsWith(".md")) {
|
|
345
|
+
yield* copyFileFn(fs, pathService, sourcePath, destPath)
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
})
|
|
349
|
+
|
|
350
|
+
// Get paths helper - takes pathService as param
|
|
351
|
+
const getPaths = (pathService: Path.Path) => {
|
|
352
|
+
const cwd = process.cwd()
|
|
353
|
+
const confluenceDir = pathService.join(cwd, ".confluence")
|
|
354
|
+
const gitDir = pathService.join(confluenceDir, ".git")
|
|
355
|
+
return { cwd, confluenceDir, gitDir, pathService }
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Ensure initialized helper - takes fs and gitDir
|
|
359
|
+
const ensureInitializedFn = (fs: FileSystem.FileSystem, gitDir: string) =>
|
|
360
|
+
Effect.gen(function*() {
|
|
361
|
+
const initialized = yield* fs.exists(gitDir).pipe(Effect.catchAll(() => Effect.succeed(false)))
|
|
362
|
+
if (!initialized) {
|
|
363
|
+
return yield* Effect.fail(new GitNotInitializedError())
|
|
364
|
+
}
|
|
365
|
+
})
|
|
366
|
+
|
|
367
|
+
// Create the service - capture deps at construction time
|
|
368
|
+
const make = Effect.gen(function*() {
|
|
369
|
+
const fs = yield* FileSystem.FileSystem
|
|
370
|
+
const pathService = yield* Path.Path
|
|
371
|
+
const commandExecutor = yield* CommandExecutor.CommandExecutor
|
|
372
|
+
const paths = getPaths(pathService)
|
|
373
|
+
const { confluenceDir, cwd, gitDir } = paths
|
|
374
|
+
|
|
375
|
+
// Create context with captured services for providing to returned Effects
|
|
376
|
+
const depsContext: Context.Context<FileSystem.FileSystem | Path.Path | CommandExecutor.CommandExecutor> = Context
|
|
377
|
+
.empty().pipe(
|
|
378
|
+
Context.add(FileSystem.FileSystem, fs),
|
|
379
|
+
Context.add(Path.Path, pathService),
|
|
380
|
+
Context.add(CommandExecutor.CommandExecutor, commandExecutor)
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
// Helper to provide deps to an effect - uses any for R since we know depsContext covers all needs
|
|
384
|
+
const provideDeps = <A, E, R>(effect: Effect.Effect<A, E, R>): Effect.Effect<A, E> =>
|
|
385
|
+
Effect.provide(effect, depsContext) as Effect.Effect<A, E>
|
|
386
|
+
|
|
387
|
+
// Ensure initialized helper using captured values
|
|
388
|
+
const ensureInitialized = ensureInitializedFn(fs, gitDir)
|
|
389
|
+
|
|
390
|
+
// Service implementation using captured deps - each returns Effect with deps provided
|
|
391
|
+
const validateGit = () =>
|
|
392
|
+
provideDeps(
|
|
393
|
+
runGit(["--version"], cwd).pipe(
|
|
394
|
+
Effect.map((output) => output.trim()),
|
|
395
|
+
Effect.catchTag("GitError", () => Effect.fail(new GitNotInstalledError({ message: "Git not found" })))
|
|
396
|
+
)
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
const isInitialized = () =>
|
|
400
|
+
Effect.succeed(fs).pipe(
|
|
401
|
+
Effect.flatMap((f) => f.exists(gitDir)),
|
|
402
|
+
Effect.catchAll(() => Effect.succeed(false))
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
const init = () =>
|
|
406
|
+
provideDeps(
|
|
407
|
+
Effect.gen(function*() {
|
|
408
|
+
// Check git is installed
|
|
409
|
+
yield* runGit(["--version"], cwd).pipe(
|
|
410
|
+
Effect.catchTag("GitError", () => Effect.fail(new GitNotInstalledError({ message: "Git not found" })))
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
const exists = yield* fs.exists(confluenceDir).pipe(Effect.catchAll(() => Effect.succeed(false)))
|
|
414
|
+
if (!exists) {
|
|
415
|
+
yield* fs.makeDirectory(confluenceDir, { recursive: true }).pipe(Effect.mapError(mapFsError))
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
const gitExists = yield* fs.exists(gitDir).pipe(Effect.catchAll(() => Effect.succeed(false)))
|
|
419
|
+
if (!gitExists) {
|
|
420
|
+
yield* runGit(["init"], confluenceDir)
|
|
421
|
+
// Configure local git user for commits (needed in CI environments)
|
|
422
|
+
yield* runGit(["config", "user.name", "Confluence Sync"], confluenceDir)
|
|
423
|
+
yield* runGit(["config", "user.email", "confluence-sync@local"], confluenceDir)
|
|
424
|
+
}
|
|
425
|
+
})
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
const addAll = () =>
|
|
429
|
+
provideDeps(
|
|
430
|
+
Effect.gen(function*() {
|
|
431
|
+
yield* ensureInitialized
|
|
432
|
+
yield* runGit(["add", "."], confluenceDir)
|
|
433
|
+
})
|
|
434
|
+
)
|
|
435
|
+
|
|
436
|
+
const commit = (options: GitCommitOptions) =>
|
|
437
|
+
provideDeps(
|
|
438
|
+
Effect.gen(function*() {
|
|
439
|
+
yield* ensureInitialized
|
|
440
|
+
|
|
441
|
+
// Check for changes
|
|
442
|
+
const statusOutput = yield* runGitAllowEmpty(["status", "--porcelain"], confluenceDir)
|
|
443
|
+
if (statusOutput.trim() === "") {
|
|
444
|
+
return yield* Effect.fail(new GitNoChangesError())
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const args: Array<string> = ["commit", "-m", options.message]
|
|
448
|
+
|
|
449
|
+
if (options.author) {
|
|
450
|
+
args.push("--author", `${options.author.name} <${options.author.email}>`)
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
if (options.date) {
|
|
454
|
+
args.push("--date", options.date.toISOString())
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const output = yield* runGit(args, confluenceDir)
|
|
458
|
+
|
|
459
|
+
// Extract commit hash from output
|
|
460
|
+
const match = output.match(/\[[\w-]+\s+([a-f0-9]+)\]/)
|
|
461
|
+
return match?.[1] ?? output.trim().slice(0, 7)
|
|
462
|
+
})
|
|
463
|
+
)
|
|
464
|
+
|
|
465
|
+
const status = () =>
|
|
466
|
+
provideDeps(
|
|
467
|
+
Effect.gen(function*() {
|
|
468
|
+
yield* ensureInitialized
|
|
469
|
+
|
|
470
|
+
const output = yield* runGitAllowEmpty(["status", "--porcelain"], confluenceDir)
|
|
471
|
+
const entries = parseGitStatus(output)
|
|
472
|
+
const conflictedFiles = getConflictedFiles(output)
|
|
473
|
+
|
|
474
|
+
return {
|
|
475
|
+
entries,
|
|
476
|
+
hasChanges: entries.length > 0,
|
|
477
|
+
hasConflicts: conflictedFiles.length > 0,
|
|
478
|
+
conflictedFiles
|
|
479
|
+
}
|
|
480
|
+
})
|
|
481
|
+
)
|
|
482
|
+
|
|
483
|
+
const log = (options?: GitLogOptions) =>
|
|
484
|
+
provideDeps(
|
|
485
|
+
Effect.gen(function*() {
|
|
486
|
+
yield* ensureInitialized
|
|
487
|
+
|
|
488
|
+
const args: Array<string> = ["log", `--format=${GIT_LOG_FORMAT}`]
|
|
489
|
+
|
|
490
|
+
if (options?.n !== undefined) {
|
|
491
|
+
args.push("-n", String(options.n))
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
if (options?.since) {
|
|
495
|
+
args.push("--since", options.since)
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
if (options?.file) {
|
|
499
|
+
args.push("--", options.file)
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
const output = yield* runGitAllowEmpty(args, confluenceDir)
|
|
503
|
+
return parseGitLog(output)
|
|
504
|
+
})
|
|
505
|
+
)
|
|
506
|
+
|
|
507
|
+
const diff = (options?: GitDiffOptions) =>
|
|
508
|
+
provideDeps(
|
|
509
|
+
Effect.gen(function*() {
|
|
510
|
+
yield* ensureInitialized
|
|
511
|
+
|
|
512
|
+
const args: Array<string> = ["diff"]
|
|
513
|
+
|
|
514
|
+
if (options?.staged) {
|
|
515
|
+
args.push("--staged")
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
if (options?.commit) {
|
|
519
|
+
args.push(options.commit)
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
if (options?.commit2) {
|
|
523
|
+
args.push(options.commit2)
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
if (options?.file) {
|
|
527
|
+
args.push("--", options.file)
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
return yield* runGitAllowEmpty(args, confluenceDir)
|
|
531
|
+
})
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
const hasConflicts = () =>
|
|
535
|
+
provideDeps(
|
|
536
|
+
Effect.gen(function*() {
|
|
537
|
+
yield* ensureInitialized
|
|
538
|
+
|
|
539
|
+
const output = yield* runGitAllowEmpty(["status", "--porcelain"], confluenceDir)
|
|
540
|
+
const conflicted = getConflictedFiles(output)
|
|
541
|
+
return conflicted.length > 0
|
|
542
|
+
})
|
|
543
|
+
)
|
|
544
|
+
|
|
545
|
+
const mergeContinue = () =>
|
|
546
|
+
provideDeps(
|
|
547
|
+
Effect.gen(function*() {
|
|
548
|
+
yield* ensureInitialized
|
|
549
|
+
|
|
550
|
+
// Check if there are still conflicts
|
|
551
|
+
const output = yield* runGitAllowEmpty(["status", "--porcelain"], confluenceDir)
|
|
552
|
+
const files = getConflictedFiles(output)
|
|
553
|
+
if (files.length > 0) {
|
|
554
|
+
return yield* Effect.fail(new GitMergeConflictError({ files }))
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
yield* runGit(["commit", "--no-edit"], confluenceDir)
|
|
558
|
+
})
|
|
559
|
+
)
|
|
560
|
+
|
|
561
|
+
const syncFromDocs = (docsPath: string, trackedPaths: ReadonlyArray<string>) =>
|
|
562
|
+
provideDeps(
|
|
563
|
+
Effect.gen(function*() {
|
|
564
|
+
yield* ensureInitialized
|
|
565
|
+
|
|
566
|
+
const absoluteDocsPath = pathService.isAbsolute(docsPath)
|
|
567
|
+
? docsPath
|
|
568
|
+
: pathService.join(cwd, docsPath)
|
|
569
|
+
|
|
570
|
+
// Skip if docsPath is inside .confluence/ - config validation ensures
|
|
571
|
+
// only ".confluence/docs" is allowed inside, which means no sync needed
|
|
572
|
+
if (absoluteDocsPath.startsWith(confluenceDir)) {
|
|
573
|
+
return
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// For each tracked path pattern, copy matching files
|
|
577
|
+
for (const pattern of trackedPaths) {
|
|
578
|
+
// Simple glob handling - for now just support **/*.md
|
|
579
|
+
if (pattern === "**/*.md") {
|
|
580
|
+
yield* copyMarkdownFilesFn(fs, pathService, absoluteDocsPath, confluenceDir)
|
|
581
|
+
} else {
|
|
582
|
+
// Direct file/directory copy
|
|
583
|
+
const sourcePath = pathService.join(absoluteDocsPath, pattern)
|
|
584
|
+
const destPath = pathService.join(confluenceDir, pattern)
|
|
585
|
+
|
|
586
|
+
const exists = yield* fs.exists(sourcePath).pipe(Effect.catchAll(() => Effect.succeed(false)))
|
|
587
|
+
if (exists) {
|
|
588
|
+
const stat = yield* fs.stat(sourcePath).pipe(Effect.mapError(mapFsError))
|
|
589
|
+
if (stat.type === "Directory") {
|
|
590
|
+
yield* copyDirectoryFn(fs, pathService, sourcePath, destPath)
|
|
591
|
+
} else {
|
|
592
|
+
yield* copyFileFn(fs, pathService, sourcePath, destPath)
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
})
|
|
598
|
+
)
|
|
599
|
+
|
|
600
|
+
const syncToDocs = (docsPath: string, trackedPaths: ReadonlyArray<string>) =>
|
|
601
|
+
provideDeps(
|
|
602
|
+
Effect.gen(function*() {
|
|
603
|
+
yield* ensureInitialized
|
|
604
|
+
|
|
605
|
+
const absoluteDocsPath = pathService.isAbsolute(docsPath)
|
|
606
|
+
? docsPath
|
|
607
|
+
: pathService.join(cwd, docsPath)
|
|
608
|
+
|
|
609
|
+
// Copy from .confluence/ back to docsPath
|
|
610
|
+
for (const pattern of trackedPaths) {
|
|
611
|
+
if (pattern === "**/*.md") {
|
|
612
|
+
yield* copyMarkdownFilesFn(fs, pathService, confluenceDir, absoluteDocsPath)
|
|
613
|
+
} else {
|
|
614
|
+
const sourcePath = pathService.join(confluenceDir, pattern)
|
|
615
|
+
const destPath = pathService.join(absoluteDocsPath, pattern)
|
|
616
|
+
|
|
617
|
+
const exists = yield* fs.exists(sourcePath).pipe(Effect.catchAll(() => Effect.succeed(false)))
|
|
618
|
+
if (exists) {
|
|
619
|
+
const stat = yield* fs.stat(sourcePath).pipe(Effect.mapError(mapFsError))
|
|
620
|
+
if (stat.type === "Directory") {
|
|
621
|
+
yield* copyDirectoryFn(fs, pathService, sourcePath, destPath)
|
|
622
|
+
} else {
|
|
623
|
+
yield* copyFileFn(fs, pathService, sourcePath, destPath)
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
})
|
|
629
|
+
)
|
|
630
|
+
|
|
631
|
+
const getHead = () =>
|
|
632
|
+
provideDeps(
|
|
633
|
+
Effect.gen(function*() {
|
|
634
|
+
yield* ensureInitialized
|
|
635
|
+
const output = yield* runGit(["rev-parse", "HEAD"], confluenceDir)
|
|
636
|
+
return output.trim()
|
|
637
|
+
})
|
|
638
|
+
)
|
|
639
|
+
|
|
640
|
+
const getCurrentBranch = () =>
|
|
641
|
+
provideDeps(
|
|
642
|
+
Effect.gen(function*() {
|
|
643
|
+
yield* ensureInitialized
|
|
644
|
+
const output = yield* runGit(["rev-parse", "--abbrev-ref", "HEAD"], confluenceDir)
|
|
645
|
+
return output.trim()
|
|
646
|
+
})
|
|
647
|
+
)
|
|
648
|
+
|
|
649
|
+
const createBranch = (name: string) =>
|
|
650
|
+
provideDeps(
|
|
651
|
+
Effect.gen(function*() {
|
|
652
|
+
yield* ensureInitialized
|
|
653
|
+
yield* runGit(["branch", name], confluenceDir)
|
|
654
|
+
})
|
|
655
|
+
)
|
|
656
|
+
|
|
657
|
+
const checkout = (ref: string) =>
|
|
658
|
+
provideDeps(
|
|
659
|
+
Effect.gen(function*() {
|
|
660
|
+
yield* ensureInitialized
|
|
661
|
+
yield* runGit(["checkout", ref], confluenceDir)
|
|
662
|
+
})
|
|
663
|
+
)
|
|
664
|
+
|
|
665
|
+
const reset = (ref: string, options?: GitResetOptions) =>
|
|
666
|
+
provideDeps(
|
|
667
|
+
Effect.gen(function*() {
|
|
668
|
+
yield* ensureInitialized
|
|
669
|
+
const args = ["reset"]
|
|
670
|
+
if (options?.hard) args.push("--hard")
|
|
671
|
+
args.push(ref)
|
|
672
|
+
yield* runGit(args, confluenceDir)
|
|
673
|
+
})
|
|
674
|
+
)
|
|
675
|
+
|
|
676
|
+
const deleteBranch = (name: string) =>
|
|
677
|
+
provideDeps(
|
|
678
|
+
Effect.gen(function*() {
|
|
679
|
+
yield* ensureInitialized
|
|
680
|
+
yield* runGit(["branch", "-D", name], confluenceDir)
|
|
681
|
+
})
|
|
682
|
+
)
|
|
683
|
+
|
|
684
|
+
const getParent = (ref: string) =>
|
|
685
|
+
provideDeps(
|
|
686
|
+
Effect.gen(function*() {
|
|
687
|
+
yield* ensureInitialized
|
|
688
|
+
const output = yield* runGit(["rev-parse", `${ref}^`], confluenceDir)
|
|
689
|
+
return output.trim()
|
|
690
|
+
})
|
|
691
|
+
)
|
|
692
|
+
|
|
693
|
+
const cherryPick = (ref: string, options?: { strategy?: "ours" | "theirs" }) =>
|
|
694
|
+
provideDeps(
|
|
695
|
+
Effect.gen(function*() {
|
|
696
|
+
yield* ensureInitialized
|
|
697
|
+
const args = ["cherry-pick"]
|
|
698
|
+
if (options?.strategy) {
|
|
699
|
+
args.push("-X", options.strategy)
|
|
700
|
+
}
|
|
701
|
+
args.push(ref)
|
|
702
|
+
yield* runGit(args, confluenceDir)
|
|
703
|
+
})
|
|
704
|
+
)
|
|
705
|
+
|
|
706
|
+
const getChangedFiles = (ref: string) =>
|
|
707
|
+
provideDeps(
|
|
708
|
+
Effect.gen(function*() {
|
|
709
|
+
yield* ensureInitialized
|
|
710
|
+
const output = yield* runGitAllowEmpty(
|
|
711
|
+
["diff-tree", "--no-commit-id", "--name-only", "-r", ref],
|
|
712
|
+
confluenceDir
|
|
713
|
+
)
|
|
714
|
+
return output.trim().split("\n").filter(Boolean)
|
|
715
|
+
})
|
|
716
|
+
)
|
|
717
|
+
|
|
718
|
+
const showFile = (ref: string, filePath: string) =>
|
|
719
|
+
provideDeps(
|
|
720
|
+
Effect.gen(function*() {
|
|
721
|
+
yield* ensureInitialized
|
|
722
|
+
const output = yield* runGit(["show", `${ref}:${filePath}`], confluenceDir)
|
|
723
|
+
return output
|
|
724
|
+
})
|
|
725
|
+
)
|
|
726
|
+
|
|
727
|
+
const amend = (options?: GitAmendOptions) =>
|
|
728
|
+
provideDeps(
|
|
729
|
+
Effect.gen(function*() {
|
|
730
|
+
yield* ensureInitialized
|
|
731
|
+
const args = ["commit", "--amend"]
|
|
732
|
+
if (options?.noEdit) {
|
|
733
|
+
args.push("--no-edit")
|
|
734
|
+
}
|
|
735
|
+
if (options?.message) {
|
|
736
|
+
args.push("-m", options.message)
|
|
737
|
+
}
|
|
738
|
+
yield* runGit(args, confluenceDir)
|
|
739
|
+
})
|
|
740
|
+
)
|
|
741
|
+
|
|
742
|
+
const logRange = (from: string, to: string) =>
|
|
743
|
+
provideDeps(
|
|
744
|
+
Effect.gen(function*() {
|
|
745
|
+
yield* ensureInitialized
|
|
746
|
+
const output = yield* runGitAllowEmpty(
|
|
747
|
+
["log", `--format=${GIT_LOG_FORMAT}`, `${from}..${to}`],
|
|
748
|
+
confluenceDir
|
|
749
|
+
)
|
|
750
|
+
return parseGitLog(output)
|
|
751
|
+
})
|
|
752
|
+
)
|
|
753
|
+
|
|
754
|
+
const branchExists = (name: string) =>
|
|
755
|
+
provideDeps(
|
|
756
|
+
Effect.gen(function*() {
|
|
757
|
+
yield* ensureInitialized
|
|
758
|
+
const output = yield* runGitAllowEmpty(
|
|
759
|
+
["branch", "--list", name],
|
|
760
|
+
confluenceDir
|
|
761
|
+
)
|
|
762
|
+
return output.trim().length > 0
|
|
763
|
+
})
|
|
764
|
+
)
|
|
765
|
+
|
|
766
|
+
const updateBranch = (name: string, ref: string) =>
|
|
767
|
+
provideDeps(
|
|
768
|
+
Effect.gen(function*() {
|
|
769
|
+
yield* ensureInitialized
|
|
770
|
+
yield* runGit(["branch", "-f", name, ref], confluenceDir)
|
|
771
|
+
})
|
|
772
|
+
)
|
|
773
|
+
|
|
774
|
+
const merge = (branch: string, options?: { noCommit?: boolean; message?: string }) =>
|
|
775
|
+
provideDeps(
|
|
776
|
+
Effect.gen(function*() {
|
|
777
|
+
yield* ensureInitialized
|
|
778
|
+
const args = ["merge", branch]
|
|
779
|
+
if (options?.noCommit) {
|
|
780
|
+
args.push("--no-commit")
|
|
781
|
+
}
|
|
782
|
+
if (options?.message) {
|
|
783
|
+
args.push("-m", options.message)
|
|
784
|
+
}
|
|
785
|
+
yield* runGit(args, confluenceDir)
|
|
786
|
+
})
|
|
787
|
+
)
|
|
788
|
+
|
|
789
|
+
const getDeletedFiles = (from: string, to: string, pathPrefix?: string) =>
|
|
790
|
+
provideDeps(
|
|
791
|
+
Effect.gen(function*() {
|
|
792
|
+
yield* ensureInitialized
|
|
793
|
+
// git diff --name-only --diff-filter=D from..to -- pathPrefix
|
|
794
|
+
const args = ["diff", "--name-only", "--diff-filter=D", `${from}..${to}`]
|
|
795
|
+
if (pathPrefix) {
|
|
796
|
+
args.push("--", pathPrefix)
|
|
797
|
+
}
|
|
798
|
+
const output = yield* runGitAllowEmpty(args, confluenceDir)
|
|
799
|
+
return output.trim().split("\n").filter((line) => line.length > 0)
|
|
800
|
+
})
|
|
801
|
+
)
|
|
802
|
+
|
|
803
|
+
const getFileContentAt = (ref: string, filePath: string) =>
|
|
804
|
+
provideDeps(
|
|
805
|
+
Effect.gen(function*() {
|
|
806
|
+
yield* ensureInitialized
|
|
807
|
+
// git show ref:filePath
|
|
808
|
+
return yield* runGit(["show", `${ref}:${filePath}`], confluenceDir)
|
|
809
|
+
})
|
|
810
|
+
)
|
|
811
|
+
|
|
812
|
+
return GitService.of({
|
|
813
|
+
validateGit,
|
|
814
|
+
isInitialized,
|
|
815
|
+
init,
|
|
816
|
+
addAll,
|
|
817
|
+
commit,
|
|
818
|
+
status,
|
|
819
|
+
log,
|
|
820
|
+
diff,
|
|
821
|
+
hasConflicts,
|
|
822
|
+
mergeContinue,
|
|
823
|
+
syncFromDocs,
|
|
824
|
+
syncToDocs,
|
|
825
|
+
getHead,
|
|
826
|
+
getCurrentBranch,
|
|
827
|
+
createBranch,
|
|
828
|
+
checkout,
|
|
829
|
+
reset,
|
|
830
|
+
deleteBranch,
|
|
831
|
+
getParent,
|
|
832
|
+
cherryPick,
|
|
833
|
+
getChangedFiles,
|
|
834
|
+
showFile,
|
|
835
|
+
amend,
|
|
836
|
+
logRange,
|
|
837
|
+
branchExists,
|
|
838
|
+
updateBranch,
|
|
839
|
+
merge,
|
|
840
|
+
getDeletedFiles,
|
|
841
|
+
getFileContentAt
|
|
842
|
+
})
|
|
843
|
+
})
|
|
844
|
+
|
|
845
|
+
// Layer with deps - requires FileSystem, Path, CommandExecutor
|
|
846
|
+
const layerWithDeps: Layer.Layer<
|
|
847
|
+
GitService,
|
|
848
|
+
never,
|
|
849
|
+
FileSystem.FileSystem | Path.Path | CommandExecutor.CommandExecutor
|
|
850
|
+
> = Layer.effect(GitService, make)
|
|
851
|
+
|
|
852
|
+
/**
|
|
853
|
+
* Create GitService layer with all platform dependencies.
|
|
854
|
+
*
|
|
855
|
+
* @category Layers
|
|
856
|
+
*/
|
|
857
|
+
export const layer = layerWithDeps.pipe(
|
|
858
|
+
Layer.provide(GitDepsLive)
|
|
859
|
+
)
|