@knpkv/confluence-to-markdown 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +62 -0
  3. package/dist/Brand.d.ts +77 -0
  4. package/dist/Brand.d.ts.map +1 -0
  5. package/dist/Brand.js +44 -0
  6. package/dist/Brand.js.map +1 -0
  7. package/dist/ConfluenceClient.d.ts +140 -0
  8. package/dist/ConfluenceClient.d.ts.map +1 -0
  9. package/dist/ConfluenceClient.js +195 -0
  10. package/dist/ConfluenceClient.js.map +1 -0
  11. package/dist/ConfluenceConfig.d.ts +83 -0
  12. package/dist/ConfluenceConfig.d.ts.map +1 -0
  13. package/dist/ConfluenceConfig.js +122 -0
  14. package/dist/ConfluenceConfig.js.map +1 -0
  15. package/dist/ConfluenceError.d.ts +178 -0
  16. package/dist/ConfluenceError.d.ts.map +1 -0
  17. package/dist/ConfluenceError.js +131 -0
  18. package/dist/ConfluenceError.js.map +1 -0
  19. package/dist/LocalFileSystem.d.ts +85 -0
  20. package/dist/LocalFileSystem.d.ts.map +1 -0
  21. package/dist/LocalFileSystem.js +101 -0
  22. package/dist/LocalFileSystem.js.map +1 -0
  23. package/dist/MarkdownConverter.d.ts +50 -0
  24. package/dist/MarkdownConverter.d.ts.map +1 -0
  25. package/dist/MarkdownConverter.js +151 -0
  26. package/dist/MarkdownConverter.js.map +1 -0
  27. package/dist/Schemas.d.ts +225 -0
  28. package/dist/Schemas.d.ts.map +1 -0
  29. package/dist/Schemas.js +164 -0
  30. package/dist/Schemas.js.map +1 -0
  31. package/dist/SyncEngine.d.ts +132 -0
  32. package/dist/SyncEngine.d.ts.map +1 -0
  33. package/dist/SyncEngine.js +267 -0
  34. package/dist/SyncEngine.js.map +1 -0
  35. package/dist/bin.d.ts +3 -0
  36. package/dist/bin.d.ts.map +1 -0
  37. package/dist/bin.js +163 -0
  38. package/dist/bin.js.map +1 -0
  39. package/dist/index.d.ts +16 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +16 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/internal/frontmatter.d.ts +38 -0
  44. package/dist/internal/frontmatter.d.ts.map +1 -0
  45. package/dist/internal/frontmatter.js +69 -0
  46. package/dist/internal/frontmatter.js.map +1 -0
  47. package/dist/internal/hashUtils.d.ts +11 -0
  48. package/dist/internal/hashUtils.d.ts.map +1 -0
  49. package/dist/internal/hashUtils.js +17 -0
  50. package/dist/internal/hashUtils.js.map +1 -0
  51. package/dist/internal/pathUtils.d.ts +41 -0
  52. package/dist/internal/pathUtils.d.ts.map +1 -0
  53. package/dist/internal/pathUtils.js +69 -0
  54. package/dist/internal/pathUtils.js.map +1 -0
  55. package/package.json +113 -0
  56. package/src/Brand.ts +104 -0
  57. package/src/ConfluenceClient.ts +387 -0
  58. package/src/ConfluenceConfig.ts +184 -0
  59. package/src/ConfluenceError.ts +193 -0
  60. package/src/LocalFileSystem.ts +225 -0
  61. package/src/MarkdownConverter.ts +187 -0
  62. package/src/Schemas.ts +235 -0
  63. package/src/SyncEngine.ts +429 -0
  64. package/src/bin.ts +269 -0
  65. package/src/index.ts +35 -0
  66. package/src/internal/frontmatter.ts +98 -0
  67. package/src/internal/hashUtils.ts +19 -0
  68. package/src/internal/pathUtils.ts +77 -0
  69. package/test/Brand.test.ts +72 -0
  70. package/test/MarkdownConverter.test.ts +108 -0
  71. package/test/Schemas.test.ts +99 -0
  72. package/tsconfig.json +32 -0
  73. package/vitest.config.integration.ts +12 -0
  74. package/vitest.config.ts +13 -0
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Configuration service for Confluence sync.
3
+ *
4
+ * @module
5
+ */
6
+ import * as FileSystem from "@effect/platform/FileSystem";
7
+ import * as Path from "@effect/platform/Path";
8
+ import * as Context from "effect/Context";
9
+ import * as Effect from "effect/Effect";
10
+ import * as Layer from "effect/Layer";
11
+ import * as Schema from "effect/Schema";
12
+ import { ConfigNotFoundError, ConfigParseError } from "./ConfluenceError.js";
13
+ import { ConfluenceConfigFileSchema } from "./Schemas.js";
14
+ /**
15
+ * Configuration service for Confluence operations.
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * import { ConfluenceConfig } from "@knpkv/confluence-to-markdown/ConfluenceConfig"
20
+ * import { Effect } from "effect"
21
+ *
22
+ * const program = Effect.gen(function* () {
23
+ * const config = yield* ConfluenceConfig
24
+ * console.log(config.rootPageId)
25
+ * console.log(config.baseUrl)
26
+ * })
27
+ * ```
28
+ *
29
+ * @category Config
30
+ */
31
+ export class ConfluenceConfig extends Context.Tag("@knpkv/confluence-to-markdown/ConfluenceConfig")() {
32
+ }
33
+ /**
34
+ * Default config file name.
35
+ */
36
+ const CONFIG_FILE_NAME = ".confluence.json";
37
+ /**
38
+ * Load configuration from a file.
39
+ */
40
+ const loadConfig = (configPath) => Effect.gen(function* () {
41
+ const fs = yield* FileSystem.FileSystem;
42
+ const exists = yield* fs.exists(configPath).pipe(Effect.catchAll(() => Effect.succeed(false)));
43
+ if (!exists) {
44
+ return yield* Effect.fail(new ConfigNotFoundError({ path: configPath }));
45
+ }
46
+ const content = yield* fs.readFileString(configPath).pipe(Effect.mapError((cause) => new ConfigParseError({ path: configPath, cause })));
47
+ const json = yield* Effect.try({
48
+ try: () => JSON.parse(content),
49
+ catch: (cause) => new ConfigParseError({ path: configPath, cause })
50
+ });
51
+ return yield* Schema.decodeUnknown(ConfluenceConfigFileSchema)(json).pipe(Effect.mapError((cause) => new ConfigParseError({ path: configPath, cause })));
52
+ });
53
+ /**
54
+ * Layer that provides ConfluenceConfig from a config file.
55
+ *
56
+ * @example
57
+ * ```typescript
58
+ * import { ConfluenceConfig } from "@knpkv/confluence-to-markdown/ConfluenceConfig"
59
+ * import { NodeFileSystem } from "@effect/platform-node"
60
+ * import { Effect } from "effect"
61
+ *
62
+ * const program = Effect.gen(function* () {
63
+ * const config = yield* ConfluenceConfig
64
+ * console.log(config.rootPageId)
65
+ * })
66
+ *
67
+ * Effect.runPromise(
68
+ * program.pipe(
69
+ * Effect.provide(ConfluenceConfig.layer()),
70
+ * Effect.provide(NodeFileSystem.layer)
71
+ * )
72
+ * )
73
+ * ```
74
+ *
75
+ * @category Layers
76
+ */
77
+ export const layer = (configPath) => Layer.effect(ConfluenceConfig, Effect.gen(function* () {
78
+ const path = yield* Path.Path;
79
+ const resolvedPath = configPath ?? path.join(process.cwd(), CONFIG_FILE_NAME);
80
+ const config = yield* loadConfig(resolvedPath);
81
+ return ConfluenceConfig.of({
82
+ rootPageId: config.rootPageId,
83
+ baseUrl: config.baseUrl,
84
+ ...(config.spaceKey !== undefined ? { spaceKey: config.spaceKey } : {}),
85
+ docsPath: config.docsPath,
86
+ excludePatterns: config.excludePatterns
87
+ });
88
+ }));
89
+ /**
90
+ * Layer that provides ConfluenceConfig with direct values.
91
+ *
92
+ * @category Layers
93
+ */
94
+ export const layerFromValues = (config) => Layer.succeed(ConfluenceConfig, ConfluenceConfig.of({
95
+ rootPageId: config.rootPageId,
96
+ baseUrl: config.baseUrl,
97
+ ...(config.spaceKey !== undefined ? { spaceKey: config.spaceKey } : {}),
98
+ docsPath: config.docsPath,
99
+ excludePatterns: config.excludePatterns
100
+ }));
101
+ /**
102
+ * Create a new config file.
103
+ *
104
+ * @category Utilities
105
+ */
106
+ export const createConfigFile = (rootPageId, baseUrl, configPath) => Effect.gen(function* () {
107
+ const fs = yield* FileSystem.FileSystem;
108
+ const pathService = yield* Path.Path;
109
+ const resolvedPath = configPath ?? pathService.join(process.cwd(), CONFIG_FILE_NAME);
110
+ const config = {
111
+ rootPageId: rootPageId,
112
+ baseUrl,
113
+ docsPath: ".docs/confluence",
114
+ excludePatterns: []
115
+ };
116
+ // Validate the config
117
+ yield* Schema.decodeUnknown(ConfluenceConfigFileSchema)(config).pipe(Effect.mapError((cause) => new ConfigParseError({ path: resolvedPath, cause })));
118
+ const content = JSON.stringify(config, null, 2);
119
+ yield* fs.writeFileString(resolvedPath, content).pipe(Effect.mapError((cause) => new ConfigParseError({ path: resolvedPath, cause })));
120
+ return resolvedPath;
121
+ });
122
+ //# sourceMappingURL=ConfluenceConfig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConfluenceConfig.js","sourceRoot":"","sources":["../src/ConfluenceConfig.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,178 @@
1
+ declare const ConfigNotFoundError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
2
+ readonly _tag: "ConfigNotFoundError";
3
+ } & Readonly<A>;
4
+ /**
5
+ * Error thrown when .confluence.json is not found.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { Effect } from "effect"
10
+ * import { ConfigNotFoundError } from "@knpkv/confluence-to-markdown/ConfluenceError"
11
+ *
12
+ * Effect.gen(function* () {
13
+ * // ... operation that needs config
14
+ * }).pipe(
15
+ * Effect.catchTag("ConfigNotFoundError", (error) =>
16
+ * Effect.sync(() => console.error(`Config not found at: ${error.path}`))
17
+ * )
18
+ * )
19
+ * ```
20
+ *
21
+ * @category Errors
22
+ */
23
+ export declare class ConfigNotFoundError extends ConfigNotFoundError_base<{
24
+ readonly path: string;
25
+ }> {
26
+ }
27
+ declare const ConfigParseError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
28
+ readonly _tag: "ConfigParseError";
29
+ } & Readonly<A>;
30
+ /**
31
+ * Error thrown when .confluence.json parsing fails.
32
+ *
33
+ * @category Errors
34
+ */
35
+ export declare class ConfigParseError extends ConfigParseError_base<{
36
+ readonly path: string;
37
+ readonly cause: unknown;
38
+ }> {
39
+ }
40
+ declare const AuthMissingError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
41
+ readonly _tag: "AuthMissingError";
42
+ } & Readonly<A>;
43
+ /**
44
+ * Error thrown when authentication is missing.
45
+ *
46
+ * @example
47
+ * ```typescript
48
+ * import { Effect } from "effect"
49
+ * import { AuthMissingError } from "@knpkv/confluence-to-markdown/ConfluenceError"
50
+ *
51
+ * Effect.gen(function* () {
52
+ * // ... operation that needs auth
53
+ * }).pipe(
54
+ * Effect.catchTag("AuthMissingError", () =>
55
+ * Effect.sync(() => console.error("Set CONFLUENCE_API_KEY or run: confluence auth login"))
56
+ * )
57
+ * )
58
+ * ```
59
+ *
60
+ * @category Errors
61
+ */
62
+ export declare class AuthMissingError extends AuthMissingError_base<{
63
+ readonly message: string;
64
+ }> {
65
+ constructor();
66
+ }
67
+ declare const ApiError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
68
+ readonly _tag: "ApiError";
69
+ } & Readonly<A>;
70
+ /**
71
+ * Error thrown when Confluence API request fails.
72
+ *
73
+ * @category Errors
74
+ */
75
+ export declare class ApiError extends ApiError_base<{
76
+ readonly status: number;
77
+ readonly message: string;
78
+ readonly endpoint: string;
79
+ readonly pageId?: string;
80
+ }> {
81
+ }
82
+ declare const RateLimitError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
83
+ readonly _tag: "RateLimitError";
84
+ } & Readonly<A>;
85
+ /**
86
+ * Error thrown when rate limit is exceeded.
87
+ *
88
+ * @category Errors
89
+ */
90
+ export declare class RateLimitError extends RateLimitError_base<{
91
+ readonly retryAfter?: number;
92
+ }> {
93
+ }
94
+ declare const ConversionError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
95
+ readonly _tag: "ConversionError";
96
+ } & Readonly<A>;
97
+ /**
98
+ * Error thrown when HTML/Markdown conversion fails.
99
+ *
100
+ * @category Errors
101
+ */
102
+ export declare class ConversionError extends ConversionError_base<{
103
+ readonly direction: "htmlToMarkdown" | "markdownToHtml";
104
+ readonly cause: unknown;
105
+ }> {
106
+ }
107
+ declare const ConflictError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
108
+ readonly _tag: "ConflictError";
109
+ } & Readonly<A>;
110
+ /**
111
+ * Error thrown when sync conflict is detected.
112
+ *
113
+ * @category Errors
114
+ */
115
+ export declare class ConflictError extends ConflictError_base<{
116
+ readonly pageId: string;
117
+ readonly localVersion: number;
118
+ readonly remoteVersion: number;
119
+ readonly path: string;
120
+ }> {
121
+ }
122
+ declare const FileSystemError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
123
+ readonly _tag: "FileSystemError";
124
+ } & Readonly<A>;
125
+ /**
126
+ * Error thrown when file system operation fails.
127
+ *
128
+ * @category Errors
129
+ */
130
+ export declare class FileSystemError extends FileSystemError_base<{
131
+ readonly operation: "read" | "write" | "delete" | "mkdir" | "rename";
132
+ readonly path: string;
133
+ readonly cause: unknown;
134
+ }> {
135
+ }
136
+ declare const OAuthError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
137
+ readonly _tag: "OAuthError";
138
+ } & Readonly<A>;
139
+ /**
140
+ * Error thrown when OAuth2 flow fails.
141
+ *
142
+ * @category Errors
143
+ */
144
+ export declare class OAuthError extends OAuthError_base<{
145
+ readonly step: "authorize" | "token" | "refresh";
146
+ readonly cause: unknown;
147
+ }> {
148
+ }
149
+ declare const FrontMatterError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
150
+ readonly _tag: "FrontMatterError";
151
+ } & Readonly<A>;
152
+ /**
153
+ * Error thrown when front-matter parsing fails.
154
+ *
155
+ * @category Errors
156
+ */
157
+ export declare class FrontMatterError extends FrontMatterError_base<{
158
+ readonly path: string;
159
+ readonly cause: unknown;
160
+ }> {
161
+ }
162
+ /**
163
+ * Union of all Confluence errors.
164
+ *
165
+ * @category Errors
166
+ */
167
+ export type ConfluenceError = ConfigNotFoundError | ConfigParseError | AuthMissingError | ApiError | RateLimitError | ConversionError | ConflictError | FileSystemError | OAuthError | FrontMatterError;
168
+ /**
169
+ * Type guard to check if error is a ConfluenceError.
170
+ *
171
+ * @param error - The error to check
172
+ * @returns True if error is a ConfluenceError
173
+ *
174
+ * @category Utilities
175
+ */
176
+ export declare const isConfluenceError: (error: unknown) => error is ConfluenceError;
177
+ export {};
178
+ //# sourceMappingURL=ConfluenceError.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConfluenceError.d.ts","sourceRoot":"","sources":["../src/ConfluenceError.ts"],"names":[],"mappings":";;;AAOA;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,mBAAoB,SAAQ,yBAAwC;IAC/E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB,CAAC;CAAG;;;;AAEL;;;;GAIG;AACH,qBAAa,gBAAiB,SAAQ,sBAAqC;IACzE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;CACxB,CAAC;CAAG;;;;AAEL;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,gBAAiB,SAAQ,sBAAqC;IACzE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB,CAAC;;CAID;;;;AAED;;;;GAIG;AACH,qBAAa,QAAS,SAAQ,cAA6B;IACzD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CACzB,CAAC;CAAG;;;;AAEL;;;;GAIG;AACH,qBAAa,cAAe,SAAQ,oBAAmC;IACrE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAC7B,CAAC;CAAG;;;;AAEL;;;;GAIG;AACH,qBAAa,eAAgB,SAAQ,qBAAoC;IACvE,QAAQ,CAAC,SAAS,EAAE,gBAAgB,GAAG,gBAAgB,CAAA;IACvD,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;CACxB,CAAC;CAAG;;;;AAEL;;;;GAIG;AACH,qBAAa,aAAc,SAAQ,mBAAkC;IACnE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB,CAAC;CAAG;;;;AAEL;;;;GAIG;AACH,qBAAa,eAAgB,SAAQ,qBAAoC;IACvE,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAA;IACpE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;CACxB,CAAC;CAAG;;;;AAEL;;;;GAIG;AACH,qBAAa,UAAW,SAAQ,gBAA+B;IAC7D,QAAQ,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,GAAG,SAAS,CAAA;IAChD,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;CACxB,CAAC;CAAG;;;;AAEL;;;;GAIG;AACH,qBAAa,gBAAiB,SAAQ,sBAAqC;IACzE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;CACxB,CAAC;CAAG;AAEL;;;;GAIG;AACH,MAAM,MAAM,eAAe,GACvB,mBAAmB,GACnB,gBAAgB,GAChB,gBAAgB,GAChB,QAAQ,GACR,cAAc,GACd,eAAe,GACf,aAAa,GACb,eAAe,GACf,UAAU,GACV,gBAAgB,CAAA;AAEpB;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,eAed,CAAA"}
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Error types for Confluence operations.
3
+ *
4
+ * @module
5
+ */
6
+ import * as Data from "effect/Data";
7
+ /**
8
+ * Error thrown when .confluence.json is not found.
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * import { Effect } from "effect"
13
+ * import { ConfigNotFoundError } from "@knpkv/confluence-to-markdown/ConfluenceError"
14
+ *
15
+ * Effect.gen(function* () {
16
+ * // ... operation that needs config
17
+ * }).pipe(
18
+ * Effect.catchTag("ConfigNotFoundError", (error) =>
19
+ * Effect.sync(() => console.error(`Config not found at: ${error.path}`))
20
+ * )
21
+ * )
22
+ * ```
23
+ *
24
+ * @category Errors
25
+ */
26
+ export class ConfigNotFoundError extends Data.TaggedError("ConfigNotFoundError") {
27
+ }
28
+ /**
29
+ * Error thrown when .confluence.json parsing fails.
30
+ *
31
+ * @category Errors
32
+ */
33
+ export class ConfigParseError extends Data.TaggedError("ConfigParseError") {
34
+ }
35
+ /**
36
+ * Error thrown when authentication is missing.
37
+ *
38
+ * @example
39
+ * ```typescript
40
+ * import { Effect } from "effect"
41
+ * import { AuthMissingError } from "@knpkv/confluence-to-markdown/ConfluenceError"
42
+ *
43
+ * Effect.gen(function* () {
44
+ * // ... operation that needs auth
45
+ * }).pipe(
46
+ * Effect.catchTag("AuthMissingError", () =>
47
+ * Effect.sync(() => console.error("Set CONFLUENCE_API_KEY or run: confluence auth login"))
48
+ * )
49
+ * )
50
+ * ```
51
+ *
52
+ * @category Errors
53
+ */
54
+ export class AuthMissingError extends Data.TaggedError("AuthMissingError") {
55
+ constructor() {
56
+ super({ message: "CONFLUENCE_API_KEY env var or OAuth2 credentials required" });
57
+ }
58
+ }
59
+ /**
60
+ * Error thrown when Confluence API request fails.
61
+ *
62
+ * @category Errors
63
+ */
64
+ export class ApiError extends Data.TaggedError("ApiError") {
65
+ }
66
+ /**
67
+ * Error thrown when rate limit is exceeded.
68
+ *
69
+ * @category Errors
70
+ */
71
+ export class RateLimitError extends Data.TaggedError("RateLimitError") {
72
+ }
73
+ /**
74
+ * Error thrown when HTML/Markdown conversion fails.
75
+ *
76
+ * @category Errors
77
+ */
78
+ export class ConversionError extends Data.TaggedError("ConversionError") {
79
+ }
80
+ /**
81
+ * Error thrown when sync conflict is detected.
82
+ *
83
+ * @category Errors
84
+ */
85
+ export class ConflictError extends Data.TaggedError("ConflictError") {
86
+ }
87
+ /**
88
+ * Error thrown when file system operation fails.
89
+ *
90
+ * @category Errors
91
+ */
92
+ export class FileSystemError extends Data.TaggedError("FileSystemError") {
93
+ }
94
+ /**
95
+ * Error thrown when OAuth2 flow fails.
96
+ *
97
+ * @category Errors
98
+ */
99
+ export class OAuthError extends Data.TaggedError("OAuthError") {
100
+ }
101
+ /**
102
+ * Error thrown when front-matter parsing fails.
103
+ *
104
+ * @category Errors
105
+ */
106
+ export class FrontMatterError extends Data.TaggedError("FrontMatterError") {
107
+ }
108
+ /**
109
+ * Type guard to check if error is a ConfluenceError.
110
+ *
111
+ * @param error - The error to check
112
+ * @returns True if error is a ConfluenceError
113
+ *
114
+ * @category Utilities
115
+ */
116
+ export const isConfluenceError = (error) => typeof error === "object" &&
117
+ error !== null &&
118
+ "_tag" in error &&
119
+ [
120
+ "ConfigNotFoundError",
121
+ "ConfigParseError",
122
+ "AuthMissingError",
123
+ "ApiError",
124
+ "RateLimitError",
125
+ "ConversionError",
126
+ "ConflictError",
127
+ "FileSystemError",
128
+ "OAuthError",
129
+ "FrontMatterError"
130
+ ].includes(error._tag);
131
+ //# sourceMappingURL=ConfluenceError.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConfluenceError.js","sourceRoot":"","sources":["../src/ConfluenceError.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,IAAI,MAAM,aAAa,CAAA;AAEnC;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,OAAO,mBAAoB,SAAQ,IAAI,CAAC,WAAW,CAAC,qBAAqB,CAE7E;CAAG;AAEL;;;;GAIG;AACH,MAAM,OAAO,gBAAiB,SAAQ,IAAI,CAAC,WAAW,CAAC,kBAAkB,CAGvE;CAAG;AAEL;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,OAAO,gBAAiB,SAAQ,IAAI,CAAC,WAAW,CAAC,kBAAkB,CAEvE;IACA;QACE,KAAK,CAAC,EAAE,OAAO,EAAE,2DAA2D,EAAE,CAAC,CAAA;IACjF,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,OAAO,QAAS,SAAQ,IAAI,CAAC,WAAW,CAAC,UAAU,CAKvD;CAAG;AAEL;;;;GAIG;AACH,MAAM,OAAO,cAAe,SAAQ,IAAI,CAAC,WAAW,CAAC,gBAAgB,CAEnE;CAAG;AAEL;;;;GAIG;AACH,MAAM,OAAO,eAAgB,SAAQ,IAAI,CAAC,WAAW,CAAC,iBAAiB,CAGrE;CAAG;AAEL;;;;GAIG;AACH,MAAM,OAAO,aAAc,SAAQ,IAAI,CAAC,WAAW,CAAC,eAAe,CAKjE;CAAG;AAEL;;;;GAIG;AACH,MAAM,OAAO,eAAgB,SAAQ,IAAI,CAAC,WAAW,CAAC,iBAAiB,CAIrE;CAAG;AAEL;;;;GAIG;AACH,MAAM,OAAO,UAAW,SAAQ,IAAI,CAAC,WAAW,CAAC,YAAY,CAG3D;CAAG;AAEL;;;;GAIG;AACH,MAAM,OAAO,gBAAiB,SAAQ,IAAI,CAAC,WAAW,CAAC,kBAAkB,CAGvE;CAAG;AAmBL;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,KAAc,EAA4B,EAAE,CAC5E,OAAO,KAAK,KAAK,QAAQ;IACzB,KAAK,KAAK,IAAI;IACd,MAAM,IAAI,KAAK;IACf;QACE,qBAAqB;QACrB,kBAAkB;QAClB,kBAAkB;QAClB,UAAU;QACV,gBAAgB;QAChB,iBAAiB;QACjB,eAAe;QACf,iBAAiB;QACjB,YAAY;QACZ,kBAAkB;KACnB,CAAC,QAAQ,CAAE,KAA0B,CAAC,IAAI,CAAC,CAAA"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Local file system operations for markdown files.
3
+ *
4
+ * @module
5
+ */
6
+ import * as FileSystem from "@effect/platform/FileSystem";
7
+ import * as Path from "@effect/platform/Path";
8
+ import * as Context from "effect/Context";
9
+ import * as Effect from "effect/Effect";
10
+ import * as Layer from "effect/Layer";
11
+ import type { ContentHash } from "./Brand.js";
12
+ import type { FrontMatterError } from "./ConfluenceError.js";
13
+ import { FileSystemError } from "./ConfluenceError.js";
14
+ import type { PageFrontMatter } from "./Schemas.js";
15
+ /**
16
+ * Local markdown file representation.
17
+ */
18
+ export interface LocalFile {
19
+ readonly path: string;
20
+ readonly frontMatter: PageFrontMatter | null;
21
+ readonly content: string;
22
+ readonly contentHash: ContentHash;
23
+ readonly isNew: boolean;
24
+ }
25
+ declare const LocalFileSystem_base: Context.TagClass<LocalFileSystem, "@knpkv/confluence-to-markdown/LocalFileSystem", {
26
+ /**
27
+ * Read a markdown file with front-matter.
28
+ */
29
+ readonly readMarkdownFile: (filePath: string) => Effect.Effect<LocalFile, FileSystemError | FrontMatterError>;
30
+ /**
31
+ * Write a markdown file with front-matter.
32
+ */
33
+ readonly writeMarkdownFile: (filePath: string, frontMatter: PageFrontMatter, content: string) => Effect.Effect<void, FileSystemError>;
34
+ /**
35
+ * List all markdown files in a directory recursively.
36
+ */
37
+ readonly listMarkdownFiles: (dirPath: string) => Effect.Effect<ReadonlyArray<string>, FileSystemError>;
38
+ /**
39
+ * Ensure a directory exists.
40
+ */
41
+ readonly ensureDir: (dirPath: string) => Effect.Effect<void, FileSystemError>;
42
+ /**
43
+ * Delete a file.
44
+ */
45
+ readonly deleteFile: (filePath: string) => Effect.Effect<void, FileSystemError>;
46
+ /**
47
+ * Check if a file exists.
48
+ */
49
+ readonly exists: (filePath: string) => Effect.Effect<boolean, FileSystemError>;
50
+ /**
51
+ * Get the file path for a page.
52
+ */
53
+ readonly getPagePath: (title: string, hasChildren: boolean, parentPath: string) => string;
54
+ /**
55
+ * Get the directory path for a page's children.
56
+ */
57
+ readonly getPageDir: (title: string, parentPath: string) => string;
58
+ }>;
59
+ /**
60
+ * Local file system service for markdown operations.
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * import { LocalFileSystem } from "@knpkv/confluence-to-markdown/LocalFileSystem"
65
+ * import { Effect } from "effect"
66
+ *
67
+ * const program = Effect.gen(function* () {
68
+ * const fs = yield* LocalFileSystem
69
+ * const files = yield* fs.listMarkdownFiles(".docs/confluence")
70
+ * console.log(files)
71
+ * })
72
+ * ```
73
+ *
74
+ * @category FileSystem
75
+ */
76
+ export declare class LocalFileSystem extends LocalFileSystem_base {
77
+ }
78
+ /**
79
+ * Layer that provides LocalFileSystem.
80
+ *
81
+ * @category Layers
82
+ */
83
+ export declare const layer: Layer.Layer<LocalFileSystem, never, FileSystem.FileSystem | Path.Path>;
84
+ export {};
85
+ //# sourceMappingURL=LocalFileSystem.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LocalFileSystem.d.ts","sourceRoot":"","sources":["../src/LocalFileSystem.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,UAAU,MAAM,6BAA6B,CAAA;AACzD,OAAO,KAAK,IAAI,MAAM,uBAAuB,CAAA;AAC7C,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAA;AACzC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AACrC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAC7C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAKtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAEnD;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,WAAW,EAAE,eAAe,GAAG,IAAI,CAAA;IAC5C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;IACjC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;CACxB;;IAwBG;;OAEG;+BACwB,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,eAAe,GAAG,gBAAgB,CAAC;IAE7G;;OAEG;gCACyB,CAC1B,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,eAAe,EAC5B,OAAO,EAAE,MAAM,KACZ,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,CAAC;IAEzC;;OAEG;gCACyB,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,eAAe,CAAC;IAEtG;;OAEG;wBACiB,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,CAAC;IAE7E;;OAEG;yBACkB,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,CAAC;IAE/E;;OAEG;qBACc,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,eAAe,CAAC;IAE9E;;OAEG;0BACmB,CACpB,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,OAAO,EACpB,UAAU,EAAE,MAAM,KACf,MAAM;IAEX;;OAEG;yBACkB,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,MAAM;;AApEtE;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,eAAgB,SAAQ,oBAqDlC;CAAG;AAEN;;;;GAIG;AACH,eAAO,MAAM,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,eAAe,EAAE,KAAK,EAAE,UAAU,CAAC,UAAU,GAAG,IAAI,CAAC,IAAI,CAqHxF,CAAA"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Local file system operations for markdown files.
3
+ *
4
+ * @module
5
+ */
6
+ import * as FileSystem from "@effect/platform/FileSystem";
7
+ import * as Path from "@effect/platform/Path";
8
+ import * as Context from "effect/Context";
9
+ import * as Effect from "effect/Effect";
10
+ import * as Layer from "effect/Layer";
11
+ import { FileSystemError } from "./ConfluenceError.js";
12
+ import { parseMarkdown, serializeMarkdown } from "./internal/frontmatter.js";
13
+ import { computeHash } from "./internal/hashUtils.js";
14
+ import { pageToDir, pageToPath } from "./internal/pathUtils.js";
15
+ /**
16
+ * Local file system service for markdown operations.
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * import { LocalFileSystem } from "@knpkv/confluence-to-markdown/LocalFileSystem"
21
+ * import { Effect } from "effect"
22
+ *
23
+ * const program = Effect.gen(function* () {
24
+ * const fs = yield* LocalFileSystem
25
+ * const files = yield* fs.listMarkdownFiles(".docs/confluence")
26
+ * console.log(files)
27
+ * })
28
+ * ```
29
+ *
30
+ * @category FileSystem
31
+ */
32
+ export class LocalFileSystem extends Context.Tag("@knpkv/confluence-to-markdown/LocalFileSystem")() {
33
+ }
34
+ /**
35
+ * Layer that provides LocalFileSystem.
36
+ *
37
+ * @category Layers
38
+ */
39
+ export const layer = Layer.effect(LocalFileSystem, Effect.gen(function* () {
40
+ const fs = yield* FileSystem.FileSystem;
41
+ const pathService = yield* Path.Path;
42
+ const readMarkdownFile = (filePath) => Effect.gen(function* () {
43
+ const content = yield* fs.readFileString(filePath).pipe(Effect.mapError((cause) => new FileSystemError({ operation: "read", path: filePath, cause })));
44
+ const parsed = yield* parseMarkdown(filePath, content);
45
+ const contentHash = computeHash(parsed.content);
46
+ return {
47
+ path: filePath,
48
+ frontMatter: parsed.frontMatter && "pageId" in parsed.frontMatter
49
+ ? parsed.frontMatter
50
+ : null,
51
+ content: parsed.content,
52
+ contentHash,
53
+ isNew: parsed.isNew
54
+ };
55
+ });
56
+ const writeMarkdownFile = (filePath, frontMatter, content) => Effect.gen(function* () {
57
+ const dir = pathService.dirname(filePath);
58
+ yield* fs.makeDirectory(dir, { recursive: true }).pipe(Effect.catchAll(() => Effect.void));
59
+ const serialized = serializeMarkdown(frontMatter, content);
60
+ // Atomic write: write to temp file, then rename
61
+ const tempPath = `${filePath}.tmp.${Date.now()}`;
62
+ yield* fs.writeFileString(tempPath, serialized).pipe(Effect.mapError((cause) => new FileSystemError({ operation: "write", path: filePath, cause })));
63
+ yield* fs.rename(tempPath, filePath).pipe(Effect.mapError((cause) => new FileSystemError({ operation: "rename", path: filePath, cause })));
64
+ });
65
+ const listMarkdownFiles = (dirPath) => Effect.gen(function* () {
66
+ const exists = yield* fs.exists(dirPath).pipe(Effect.mapError((cause) => new FileSystemError({ operation: "read", path: dirPath, cause })));
67
+ if (!exists) {
68
+ return [];
69
+ }
70
+ const files = [];
71
+ const walkDir = (dir) => Effect.gen(function* () {
72
+ const entries = yield* fs.readDirectory(dir).pipe(Effect.mapError((cause) => new FileSystemError({ operation: "read", path: dir, cause })));
73
+ for (const entryName of entries) {
74
+ const fullPath = pathService.join(dir, entryName);
75
+ const stat = yield* fs.stat(fullPath).pipe(Effect.mapError((cause) => new FileSystemError({ operation: "read", path: fullPath, cause })));
76
+ if (stat.type === "Directory") {
77
+ yield* walkDir(fullPath);
78
+ }
79
+ else if (stat.type === "File" && entryName.endsWith(".md")) {
80
+ files.push(fullPath);
81
+ }
82
+ }
83
+ });
84
+ yield* walkDir(dirPath);
85
+ return files;
86
+ });
87
+ const ensureDir = (dirPath) => fs.makeDirectory(dirPath, { recursive: true }).pipe(Effect.mapError((cause) => new FileSystemError({ operation: "mkdir", path: dirPath, cause })), Effect.asVoid);
88
+ const deleteFile = (filePath) => fs.remove(filePath).pipe(Effect.mapError((cause) => new FileSystemError({ operation: "delete", path: filePath, cause })));
89
+ const exists = (filePath) => fs.exists(filePath).pipe(Effect.mapError((cause) => new FileSystemError({ operation: "read", path: filePath, cause })));
90
+ return LocalFileSystem.of({
91
+ readMarkdownFile,
92
+ writeMarkdownFile,
93
+ listMarkdownFiles,
94
+ ensureDir,
95
+ deleteFile,
96
+ exists,
97
+ getPagePath: pageToPath,
98
+ getPageDir: pageToDir
99
+ });
100
+ }));
101
+ //# sourceMappingURL=LocalFileSystem.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LocalFileSystem.js","sourceRoot":"","sources":["../src/LocalFileSystem.ts"],"names":[],"mappings":"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"}