@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
package/src/ConfluenceClient.ts
CHANGED
|
@@ -1,19 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Confluence REST API v2 client service.
|
|
3
3
|
*
|
|
4
|
+
* Wraps @knpkv/confluence-api-client with rate limit retry logic and pagination helpers.
|
|
5
|
+
*
|
|
4
6
|
* @module
|
|
5
7
|
*/
|
|
6
8
|
import * as HttpClient from "@effect/platform/HttpClient"
|
|
7
|
-
import
|
|
9
|
+
import {
|
|
10
|
+
ConfluenceApiClient,
|
|
11
|
+
ConfluenceApiConfig,
|
|
12
|
+
type V1ApiError,
|
|
13
|
+
type V2ApiError
|
|
14
|
+
} from "@knpkv/confluence-api-client"
|
|
8
15
|
import * as Context from "effect/Context"
|
|
9
16
|
import * as Effect from "effect/Effect"
|
|
10
17
|
import * as Layer from "effect/Layer"
|
|
18
|
+
import * as Redacted from "effect/Redacted"
|
|
11
19
|
import * as Schedule from "effect/Schedule"
|
|
12
|
-
import * as Schema from "effect/Schema"
|
|
13
20
|
import type { PageId } from "./Brand.js"
|
|
14
|
-
import {
|
|
15
|
-
import
|
|
16
|
-
import {
|
|
21
|
+
import type { RateLimitError } from "./ConfluenceError.js"
|
|
22
|
+
import { ApiError } from "./ConfluenceError.js"
|
|
23
|
+
import type { AtlassianUser, PageChildrenResponse, PageListItem, PageResponse, PageVersion } from "./Schemas.js"
|
|
17
24
|
|
|
18
25
|
/**
|
|
19
26
|
* Request to create a new page.
|
|
@@ -100,6 +107,30 @@ export class ConfluenceClient extends Context.Tag(
|
|
|
100
107
|
* Delete a page.
|
|
101
108
|
*/
|
|
102
109
|
readonly deletePage: (id: PageId) => Effect.Effect<void, ApiError | RateLimitError>
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Get version history for a page.
|
|
113
|
+
*/
|
|
114
|
+
readonly getPageVersions: (
|
|
115
|
+
id: PageId,
|
|
116
|
+
options?: { since?: number; includeBody?: boolean }
|
|
117
|
+
) => Effect.Effect<ReadonlyArray<PageVersion>, ApiError | RateLimitError>
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Get user info by account ID.
|
|
121
|
+
*/
|
|
122
|
+
readonly getUser: (accountId: string) => Effect.Effect<AtlassianUser, ApiError | RateLimitError>
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Get space ID for a page.
|
|
126
|
+
*/
|
|
127
|
+
readonly getSpaceId: (pageId: PageId) => Effect.Effect<string, ApiError | RateLimitError>
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Set editor version for a page (v1 or v2).
|
|
131
|
+
* Uses V1 API to set page property.
|
|
132
|
+
*/
|
|
133
|
+
readonly setEditorVersion: (pageId: PageId, version: "v1" | "v2") => Effect.Effect<void, ApiError | RateLimitError>
|
|
103
134
|
}
|
|
104
135
|
>() {}
|
|
105
136
|
|
|
@@ -117,9 +148,16 @@ export interface ConfluenceClientConfig {
|
|
|
117
148
|
} | {
|
|
118
149
|
readonly type: "oauth2"
|
|
119
150
|
readonly accessToken: string
|
|
151
|
+
readonly cloudId: string
|
|
120
152
|
}
|
|
121
153
|
}
|
|
122
154
|
|
|
155
|
+
/** Maximum pagination iterations to prevent infinite loops */
|
|
156
|
+
const MAX_PAGINATION_ITERATIONS = 100
|
|
157
|
+
|
|
158
|
+
/** Default page size for version fetching */
|
|
159
|
+
const VERSIONS_PAGE_SIZE = 50
|
|
160
|
+
|
|
123
161
|
/**
|
|
124
162
|
* Rate limit retry schedule with exponential backoff.
|
|
125
163
|
*/
|
|
@@ -129,6 +167,17 @@ const rateLimitSchedule = Schedule.exponential("1 second").pipe(
|
|
|
129
167
|
Schedule.intersect(Schedule.recurs(3))
|
|
130
168
|
)
|
|
131
169
|
|
|
170
|
+
/**
|
|
171
|
+
* Map API client errors to domain errors.
|
|
172
|
+
*/
|
|
173
|
+
const mapApiError = (error: V1ApiError | V2ApiError, endpoint: string, pageId?: string): ApiError =>
|
|
174
|
+
new ApiError({
|
|
175
|
+
status: error.status,
|
|
176
|
+
message: error.message,
|
|
177
|
+
endpoint,
|
|
178
|
+
...(pageId !== undefined && { pageId })
|
|
179
|
+
})
|
|
180
|
+
|
|
132
181
|
/**
|
|
133
182
|
* Create the Confluence client service.
|
|
134
183
|
*/
|
|
@@ -136,168 +185,128 @@ const make = (
|
|
|
136
185
|
config: ConfluenceClientConfig
|
|
137
186
|
): Effect.Effect<Context.Tag.Service<typeof ConfluenceClient>, never, HttpClient.HttpClient> =>
|
|
138
187
|
Effect.gen(function*() {
|
|
139
|
-
|
|
188
|
+
// Create underlying API client
|
|
189
|
+
const apiConfigLayer = Layer.succeed(ConfluenceApiConfig, {
|
|
190
|
+
baseUrl: config.baseUrl,
|
|
191
|
+
auth: config.auth.type === "token"
|
|
192
|
+
? { type: "basic", email: config.auth.email, apiToken: Redacted.make(config.auth.token) }
|
|
193
|
+
: { type: "oauth2", accessToken: Redacted.make(config.auth.accessToken), cloudId: config.auth.cloudId }
|
|
194
|
+
})
|
|
140
195
|
|
|
141
|
-
const
|
|
142
|
-
? `Basic ${Buffer.from(`${config.auth.email}:${config.auth.token}`).toString("base64")}`
|
|
143
|
-
: `Bearer ${config.auth.accessToken}`
|
|
196
|
+
const httpClient = yield* HttpClient.HttpClient
|
|
144
197
|
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
198
|
+
const apiClient = yield* ConfluenceApiClient.pipe(
|
|
199
|
+
Effect.provide(ConfluenceApiClient.layer),
|
|
200
|
+
Effect.provide(apiConfigLayer),
|
|
201
|
+
Effect.provide(Layer.succeed(HttpClient.HttpClient, httpClient))
|
|
149
202
|
)
|
|
150
203
|
|
|
151
|
-
/**
|
|
152
|
-
* Make an HTTP request to the Confluence API.
|
|
153
|
-
* Returns raw JSON - callers must validate with Schema.decodeUnknown.
|
|
154
|
-
*/
|
|
155
|
-
const request = (
|
|
156
|
-
method: "GET" | "POST" | "PUT" | "DELETE",
|
|
157
|
-
path: string,
|
|
158
|
-
body?: unknown
|
|
159
|
-
): Effect.Effect<unknown, ApiError | RateLimitError, never> =>
|
|
160
|
-
Effect.gen(function*() {
|
|
161
|
-
let req = baseRequest.pipe(
|
|
162
|
-
HttpClientRequest.setMethod(method),
|
|
163
|
-
HttpClientRequest.setUrl(`${config.baseUrl}/wiki/api/v2${path}`)
|
|
164
|
-
)
|
|
165
|
-
|
|
166
|
-
if (body !== undefined) {
|
|
167
|
-
req = HttpClientRequest.bodyJson(req, body).pipe(
|
|
168
|
-
Effect.catchAll(() => Effect.succeed(req)),
|
|
169
|
-
Effect.runSync
|
|
170
|
-
)
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const response = yield* httpClient.execute(req).pipe(
|
|
174
|
-
Effect.mapError((error) =>
|
|
175
|
-
new ApiError({
|
|
176
|
-
status: 0,
|
|
177
|
-
message: `Request failed: ${error.message}`,
|
|
178
|
-
endpoint: path
|
|
179
|
-
})
|
|
180
|
-
)
|
|
181
|
-
)
|
|
182
|
-
|
|
183
|
-
if (response.status === 429) {
|
|
184
|
-
const retryAfterHeader = response.headers["retry-after"]
|
|
185
|
-
const retryAfter = retryAfterHeader ? parseInt(retryAfterHeader, 10) : undefined
|
|
186
|
-
return yield* Effect.fail(
|
|
187
|
-
retryAfter !== undefined
|
|
188
|
-
? new RateLimitError({ retryAfter })
|
|
189
|
-
: new RateLimitError({})
|
|
190
|
-
)
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
if (response.status >= 400) {
|
|
194
|
-
const text = yield* response.text.pipe(
|
|
195
|
-
Effect.catchAll(() => Effect.succeed(""))
|
|
196
|
-
)
|
|
197
|
-
return yield* Effect.fail(
|
|
198
|
-
new ApiError({
|
|
199
|
-
status: response.status,
|
|
200
|
-
message: text || `HTTP ${response.status}`,
|
|
201
|
-
endpoint: path
|
|
202
|
-
})
|
|
203
|
-
)
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
if (method === "DELETE" && response.status === 204) {
|
|
207
|
-
return undefined
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
const json = yield* response.json.pipe(
|
|
211
|
-
Effect.mapError((error) =>
|
|
212
|
-
new ApiError({
|
|
213
|
-
status: response.status,
|
|
214
|
-
message: `Failed to parse response: ${error}`,
|
|
215
|
-
endpoint: path
|
|
216
|
-
})
|
|
217
|
-
)
|
|
218
|
-
)
|
|
219
|
-
|
|
220
|
-
return json
|
|
221
|
-
}).pipe(
|
|
222
|
-
Effect.retry(rateLimitSchedule)
|
|
223
|
-
)
|
|
224
|
-
|
|
225
204
|
const getPage = (id: PageId): Effect.Effect<PageResponse, ApiError | RateLimitError> =>
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
)
|
|
231
|
-
return yield* Schema.decodeUnknown(PageResponseSchema)(raw).pipe(
|
|
232
|
-
Effect.mapError((error) =>
|
|
233
|
-
new ApiError({
|
|
234
|
-
status: 0,
|
|
235
|
-
message: `Invalid response schema: ${error.message}`,
|
|
236
|
-
endpoint: `/pages/${id}`,
|
|
237
|
-
pageId: id
|
|
238
|
-
})
|
|
239
|
-
)
|
|
240
|
-
)
|
|
241
|
-
})
|
|
205
|
+
apiClient.v2.getPageById(id, { bodyFormat: "storage" }).pipe(
|
|
206
|
+
Effect.mapError((e) => mapApiError(e, `/pages/${id}`, id)),
|
|
207
|
+
Effect.retry(rateLimitSchedule)
|
|
208
|
+
) as Effect.Effect<PageResponse, ApiError | RateLimitError>
|
|
242
209
|
|
|
243
210
|
const getChildren = (id: PageId): Effect.Effect<PageChildrenResponse, ApiError | RateLimitError> =>
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
)
|
|
249
|
-
return yield* Schema.decodeUnknown(PageChildrenResponseSchema)(raw).pipe(
|
|
250
|
-
Effect.mapError((error) =>
|
|
251
|
-
new ApiError({
|
|
252
|
-
status: 0,
|
|
253
|
-
message: `Invalid response schema: ${error.message}`,
|
|
254
|
-
endpoint: `/pages/${id}/children`,
|
|
255
|
-
pageId: id
|
|
256
|
-
})
|
|
257
|
-
)
|
|
258
|
-
)
|
|
259
|
-
})
|
|
211
|
+
apiClient.v2.getPageChildren(id, { bodyFormat: "storage" }).pipe(
|
|
212
|
+
Effect.mapError((e) => mapApiError(e, `/pages/${id}/children`, id)),
|
|
213
|
+
Effect.retry(rateLimitSchedule)
|
|
214
|
+
) as Effect.Effect<PageChildrenResponse, ApiError | RateLimitError>
|
|
260
215
|
|
|
261
216
|
const getAllChildren = (id: PageId): Effect.Effect<ReadonlyArray<PageListItem>, ApiError | RateLimitError> =>
|
|
262
217
|
Effect.gen(function*() {
|
|
263
218
|
const allChildren: Array<PageListItem> = []
|
|
264
219
|
let cursor: string | undefined
|
|
265
220
|
let iterations = 0
|
|
266
|
-
const maxIterations = 100 // Prevent unbounded pagination
|
|
267
221
|
|
|
268
222
|
do {
|
|
269
|
-
if (iterations >=
|
|
223
|
+
if (iterations >= MAX_PAGINATION_ITERATIONS) {
|
|
270
224
|
return yield* Effect.fail(
|
|
271
225
|
new ApiError({
|
|
272
226
|
status: 0,
|
|
273
|
-
message: `Pagination limit exceeded: more than ${
|
|
227
|
+
message: `Pagination limit exceeded: more than ${MAX_PAGINATION_ITERATIONS} pages of children`,
|
|
274
228
|
endpoint: `/pages/${id}/children`,
|
|
275
229
|
pageId: id
|
|
276
230
|
})
|
|
277
231
|
)
|
|
278
232
|
}
|
|
279
233
|
|
|
280
|
-
const
|
|
281
|
-
|
|
282
|
-
|
|
234
|
+
const response = yield* apiClient.v2.getPageChildren(id, {
|
|
235
|
+
bodyFormat: "storage",
|
|
236
|
+
cursor
|
|
237
|
+
}).pipe(
|
|
238
|
+
Effect.mapError((e) => mapApiError(e, `/pages/${id}/children`, id)),
|
|
239
|
+
Effect.retry(rateLimitSchedule)
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
for (const child of response.results) {
|
|
243
|
+
allChildren.push(child as PageListItem)
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
cursor = response._links?.next
|
|
247
|
+
? new URL(response._links.next, config.baseUrl).searchParams.get("cursor") ?? undefined
|
|
248
|
+
: undefined
|
|
249
|
+
|
|
250
|
+
iterations++
|
|
251
|
+
} while (cursor)
|
|
252
|
+
|
|
253
|
+
return allChildren
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
const createPage = (req: CreatePageRequest): Effect.Effect<PageResponse, ApiError | RateLimitError> =>
|
|
257
|
+
apiClient.v2.createPage(req).pipe(
|
|
258
|
+
Effect.mapError((e) => mapApiError(e, "/pages")),
|
|
259
|
+
Effect.retry(rateLimitSchedule)
|
|
260
|
+
) as Effect.Effect<PageResponse, ApiError | RateLimitError>
|
|
283
261
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
262
|
+
const updatePage = (req: UpdatePageRequest): Effect.Effect<PageResponse, ApiError | RateLimitError> =>
|
|
263
|
+
apiClient.v2.updatePage(req.id, req).pipe(
|
|
264
|
+
Effect.mapError((e) => mapApiError(e, `/pages/${req.id}`, req.id)),
|
|
265
|
+
Effect.retry(rateLimitSchedule)
|
|
266
|
+
) as Effect.Effect<PageResponse, ApiError | RateLimitError>
|
|
267
|
+
|
|
268
|
+
const deletePage = (id: PageId): Effect.Effect<void, ApiError | RateLimitError> =>
|
|
269
|
+
apiClient.v2.deletePage(id).pipe(
|
|
270
|
+
Effect.mapError((e) => mapApiError(e, `/pages/${id}`, id)),
|
|
271
|
+
Effect.retry(rateLimitSchedule)
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
const getPageVersions = (
|
|
275
|
+
id: PageId,
|
|
276
|
+
options?: { since?: number; includeBody?: boolean }
|
|
277
|
+
): Effect.Effect<ReadonlyArray<PageVersion>, ApiError | RateLimitError> =>
|
|
278
|
+
Effect.gen(function*() {
|
|
279
|
+
const allVersions: Array<PageVersion> = []
|
|
280
|
+
let cursor: string | undefined
|
|
281
|
+
let iterations = 0
|
|
282
|
+
|
|
283
|
+
do {
|
|
284
|
+
if (iterations >= MAX_PAGINATION_ITERATIONS) {
|
|
285
|
+
return yield* Effect.fail(
|
|
287
286
|
new ApiError({
|
|
288
287
|
status: 0,
|
|
289
|
-
message: `
|
|
290
|
-
endpoint:
|
|
288
|
+
message: `Pagination limit exceeded: more than ${MAX_PAGINATION_ITERATIONS} pages of versions`,
|
|
289
|
+
endpoint: `/pages/${id}/versions`,
|
|
291
290
|
pageId: id
|
|
292
291
|
})
|
|
293
292
|
)
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const response = yield* apiClient.v2.getPageVersions(id, {
|
|
296
|
+
bodyFormat: options?.includeBody ? "storage" : undefined,
|
|
297
|
+
cursor,
|
|
298
|
+
limit: VERSIONS_PAGE_SIZE
|
|
299
|
+
}).pipe(
|
|
300
|
+
Effect.mapError((e) => mapApiError(e, `/pages/${id}/versions`, id)),
|
|
301
|
+
Effect.retry(rateLimitSchedule)
|
|
294
302
|
)
|
|
295
303
|
|
|
296
|
-
for (const
|
|
297
|
-
|
|
304
|
+
for (const version of response.results) {
|
|
305
|
+
if (options?.since === undefined || (version.number ?? 0) > options.since) {
|
|
306
|
+
allVersions.push(version as PageVersion)
|
|
307
|
+
}
|
|
298
308
|
}
|
|
299
309
|
|
|
300
|
-
// Extract cursor from next link if present
|
|
301
310
|
cursor = response._links?.next
|
|
302
311
|
? new URL(response._links.next, config.baseUrl).searchParams.get("cursor") ?? undefined
|
|
303
312
|
: undefined
|
|
@@ -305,40 +314,59 @@ const make = (
|
|
|
305
314
|
iterations++
|
|
306
315
|
} while (cursor)
|
|
307
316
|
|
|
308
|
-
return
|
|
317
|
+
return allVersions
|
|
309
318
|
})
|
|
310
319
|
|
|
311
|
-
const
|
|
320
|
+
const getUser = (accountId: string): Effect.Effect<AtlassianUser, ApiError | RateLimitError> =>
|
|
321
|
+
apiClient.v1.getUser({ accountId }).pipe(
|
|
322
|
+
Effect.mapError((e) => mapApiError(e, `/user?accountId=${accountId}`)),
|
|
323
|
+
Effect.retry(rateLimitSchedule)
|
|
324
|
+
) as Effect.Effect<AtlassianUser, ApiError | RateLimitError>
|
|
325
|
+
|
|
326
|
+
const getSpaceId = (pageId: PageId): Effect.Effect<string, ApiError | RateLimitError> =>
|
|
312
327
|
Effect.gen(function*() {
|
|
313
|
-
const
|
|
314
|
-
|
|
315
|
-
Effect.
|
|
328
|
+
const page = yield* getPage(pageId)
|
|
329
|
+
if (!page.spaceId) {
|
|
330
|
+
return yield* Effect.fail(
|
|
316
331
|
new ApiError({
|
|
317
332
|
status: 0,
|
|
318
|
-
message: `
|
|
319
|
-
endpoint:
|
|
333
|
+
message: `Page ${pageId} does not have spaceId`,
|
|
334
|
+
endpoint: `/pages/${pageId}`,
|
|
335
|
+
pageId
|
|
320
336
|
})
|
|
321
337
|
)
|
|
322
|
-
|
|
338
|
+
}
|
|
339
|
+
return page.spaceId
|
|
323
340
|
})
|
|
324
341
|
|
|
325
|
-
const
|
|
342
|
+
const setEditorVersion = (pageId: PageId, version: "v1" | "v2"): Effect.Effect<void, ApiError | RateLimitError> =>
|
|
326
343
|
Effect.gen(function*() {
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
Effect.
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
})
|
|
336
|
-
)
|
|
344
|
+
// Try to get current property version, only treat 404 as "not exists"
|
|
345
|
+
const propertyVersion = yield* apiClient.v1.getContentProperty(pageId, "editor").pipe(
|
|
346
|
+
Effect.map((prop) => prop.version.number + 1),
|
|
347
|
+
Effect.catchIf(
|
|
348
|
+
(e) => e.status === 404,
|
|
349
|
+
() => Effect.succeed(1)
|
|
350
|
+
),
|
|
351
|
+
Effect.mapError((e) => mapApiError(e, `/content/${pageId}/property/editor`, pageId))
|
|
337
352
|
)
|
|
338
|
-
})
|
|
339
353
|
|
|
340
|
-
|
|
341
|
-
|
|
354
|
+
const payload = {
|
|
355
|
+
key: "editor",
|
|
356
|
+
value: version,
|
|
357
|
+
version: { number: propertyVersion }
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (propertyVersion === 1) {
|
|
361
|
+
yield* apiClient.v1.createContentProperty(pageId, { payload }).pipe(
|
|
362
|
+
Effect.mapError((e) => mapApiError(e, `/content/${pageId}/property/editor`, pageId))
|
|
363
|
+
)
|
|
364
|
+
} else {
|
|
365
|
+
yield* apiClient.v1.updateContentProperty(pageId, "editor", { payload }).pipe(
|
|
366
|
+
Effect.mapError((e) => mapApiError(e, `/content/${pageId}/property/editor`, pageId))
|
|
367
|
+
)
|
|
368
|
+
}
|
|
369
|
+
}).pipe(Effect.retry(rateLimitSchedule))
|
|
342
370
|
|
|
343
371
|
return ConfluenceClient.of({
|
|
344
372
|
getPage,
|
|
@@ -346,7 +374,11 @@ const make = (
|
|
|
346
374
|
getAllChildren,
|
|
347
375
|
createPage,
|
|
348
376
|
updatePage,
|
|
349
|
-
deletePage
|
|
377
|
+
deletePage,
|
|
378
|
+
getPageVersions,
|
|
379
|
+
getUser,
|
|
380
|
+
getSpaceId,
|
|
381
|
+
setEditorVersion
|
|
350
382
|
})
|
|
351
383
|
})
|
|
352
384
|
|
package/src/ConfluenceConfig.ts
CHANGED
|
@@ -46,13 +46,22 @@ export class ConfluenceConfig extends Context.Tag(
|
|
|
46
46
|
readonly docsPath: string
|
|
47
47
|
/** Glob patterns to exclude */
|
|
48
48
|
readonly excludePatterns: ReadonlyArray<string>
|
|
49
|
+
/** Save original Confluence HTML alongside markdown */
|
|
50
|
+
readonly saveSource: boolean
|
|
51
|
+
/** Glob patterns for files to track in git */
|
|
52
|
+
readonly trackedPaths: ReadonlyArray<string>
|
|
49
53
|
}
|
|
50
54
|
>() {}
|
|
51
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Default config directory.
|
|
58
|
+
*/
|
|
59
|
+
const CONFIG_DIR = ".confluence"
|
|
60
|
+
|
|
52
61
|
/**
|
|
53
62
|
* Default config file name.
|
|
54
63
|
*/
|
|
55
|
-
const CONFIG_FILE_NAME = ".
|
|
64
|
+
const CONFIG_FILE_NAME = "config.json"
|
|
56
65
|
|
|
57
66
|
/**
|
|
58
67
|
* Load configuration from a file.
|
|
@@ -108,6 +117,35 @@ const loadConfig = (
|
|
|
108
117
|
*
|
|
109
118
|
* @category Layers
|
|
110
119
|
*/
|
|
120
|
+
/**
|
|
121
|
+
* Validate docsPath configuration.
|
|
122
|
+
* docsPath must be either:
|
|
123
|
+
* - ".confluence/docs" (default, files in git repo)
|
|
124
|
+
* - A path outside ".confluence/" (external docs directory)
|
|
125
|
+
* NOT: ".confluence/other" which would cause confusion
|
|
126
|
+
*/
|
|
127
|
+
const validateDocsPath = (
|
|
128
|
+
docsPath: string,
|
|
129
|
+
configPath: string
|
|
130
|
+
): Effect.Effect<void, ConfigParseError> => {
|
|
131
|
+
const isDefault = docsPath === ".confluence/docs"
|
|
132
|
+
const isInsideConfluence = docsPath.startsWith(".confluence/") || docsPath.startsWith(".confluence\\")
|
|
133
|
+
const isOutside = !isInsideConfluence
|
|
134
|
+
|
|
135
|
+
if (isDefault || isOutside) {
|
|
136
|
+
return Effect.void
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// docsPath is inside .confluence/ but not the default - this is invalid
|
|
140
|
+
return Effect.fail(
|
|
141
|
+
new ConfigParseError({
|
|
142
|
+
path: configPath,
|
|
143
|
+
cause:
|
|
144
|
+
`Invalid docsPath "${docsPath}". Must be either ".confluence/docs" (default) or a path outside ".confluence/" (e.g., "docs" for external docs).`
|
|
145
|
+
})
|
|
146
|
+
)
|
|
147
|
+
}
|
|
148
|
+
|
|
111
149
|
export const layer = (
|
|
112
150
|
configPath?: string
|
|
113
151
|
): Layer.Layer<ConfluenceConfig, ConfigNotFoundError | ConfigParseError, FileSystem.FileSystem | Path.Path> =>
|
|
@@ -115,15 +153,20 @@ export const layer = (
|
|
|
115
153
|
ConfluenceConfig,
|
|
116
154
|
Effect.gen(function*() {
|
|
117
155
|
const path = yield* Path.Path
|
|
118
|
-
const resolvedPath = configPath ?? path.join(process.cwd(), CONFIG_FILE_NAME)
|
|
156
|
+
const resolvedPath = configPath ?? path.join(process.cwd(), CONFIG_DIR, CONFIG_FILE_NAME)
|
|
119
157
|
const config = yield* loadConfig(resolvedPath)
|
|
120
158
|
|
|
159
|
+
// Validate docsPath
|
|
160
|
+
yield* validateDocsPath(config.docsPath, resolvedPath)
|
|
161
|
+
|
|
121
162
|
return ConfluenceConfig.of({
|
|
122
163
|
rootPageId: config.rootPageId,
|
|
123
164
|
baseUrl: config.baseUrl,
|
|
124
165
|
...(config.spaceKey !== undefined ? { spaceKey: config.spaceKey } : {}),
|
|
125
166
|
docsPath: config.docsPath,
|
|
126
|
-
excludePatterns: config.excludePatterns
|
|
167
|
+
excludePatterns: config.excludePatterns,
|
|
168
|
+
saveSource: config.saveSource,
|
|
169
|
+
trackedPaths: config.trackedPaths
|
|
127
170
|
})
|
|
128
171
|
})
|
|
129
172
|
)
|
|
@@ -143,7 +186,9 @@ export const layerFromValues = (
|
|
|
143
186
|
baseUrl: config.baseUrl,
|
|
144
187
|
...(config.spaceKey !== undefined ? { spaceKey: config.spaceKey } : {}),
|
|
145
188
|
docsPath: config.docsPath,
|
|
146
|
-
excludePatterns: config.excludePatterns
|
|
189
|
+
excludePatterns: config.excludePatterns,
|
|
190
|
+
saveSource: config.saveSource,
|
|
191
|
+
trackedPaths: config.trackedPaths
|
|
147
192
|
})
|
|
148
193
|
)
|
|
149
194
|
|
|
@@ -161,13 +206,16 @@ export const createConfigFile = (
|
|
|
161
206
|
const fs = yield* FileSystem.FileSystem
|
|
162
207
|
const pathService = yield* Path.Path
|
|
163
208
|
|
|
164
|
-
const
|
|
209
|
+
const configDir = pathService.join(process.cwd(), CONFIG_DIR)
|
|
210
|
+
const resolvedPath = configPath ?? pathService.join(configDir, CONFIG_FILE_NAME)
|
|
165
211
|
|
|
166
212
|
const config: ConfluenceConfigFile = {
|
|
167
213
|
rootPageId: rootPageId as PageId,
|
|
168
214
|
baseUrl,
|
|
169
|
-
docsPath: ".docs
|
|
170
|
-
excludePatterns: []
|
|
215
|
+
docsPath: ".confluence/docs",
|
|
216
|
+
excludePatterns: [],
|
|
217
|
+
saveSource: false,
|
|
218
|
+
trackedPaths: ["**/*.md"]
|
|
171
219
|
}
|
|
172
220
|
|
|
173
221
|
// Validate the config
|
|
@@ -175,6 +223,14 @@ export const createConfigFile = (
|
|
|
175
223
|
Effect.mapError((cause) => new ConfigParseError({ path: resolvedPath, cause }))
|
|
176
224
|
)
|
|
177
225
|
|
|
226
|
+
// Create .confluence directory if it doesn't exist
|
|
227
|
+
const dirExists = yield* fs.exists(configDir).pipe(Effect.catchAll(() => Effect.succeed(false)))
|
|
228
|
+
if (!dirExists) {
|
|
229
|
+
yield* fs.makeDirectory(configDir, { recursive: true }).pipe(
|
|
230
|
+
Effect.mapError((cause) => new ConfigParseError({ path: configDir, cause }))
|
|
231
|
+
)
|
|
232
|
+
}
|
|
233
|
+
|
|
178
234
|
const content = JSON.stringify(config, null, 2)
|
|
179
235
|
yield* fs.writeFileString(resolvedPath, content).pipe(
|
|
180
236
|
Effect.mapError((cause) => new ConfigParseError({ path: resolvedPath, cause }))
|