@knpkv/confluence-to-markdown 0.2.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (336) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/LICENSE +21 -0
  3. package/README.md +282 -14
  4. package/dist/ConfluenceAuth.d.ts +76 -0
  5. package/dist/ConfluenceAuth.d.ts.map +1 -0
  6. package/dist/ConfluenceAuth.js +356 -0
  7. package/dist/ConfluenceAuth.js.map +1 -0
  8. package/dist/ConfluenceClient.d.ts +26 -2
  9. package/dist/ConfluenceClient.d.ts.map +1 -1
  10. package/dist/ConfluenceClient.js +98 -92
  11. package/dist/ConfluenceClient.js.map +1 -1
  12. package/dist/ConfluenceConfig.d.ts +4 -24
  13. package/dist/ConfluenceConfig.d.ts.map +1 -1
  14. package/dist/ConfluenceConfig.js +45 -7
  15. package/dist/ConfluenceConfig.js.map +1 -1
  16. package/dist/ConfluenceError.d.ts +89 -6
  17. package/dist/ConfluenceError.d.ts.map +1 -1
  18. package/dist/ConfluenceError.js +88 -5
  19. package/dist/ConfluenceError.js.map +1 -1
  20. package/dist/GitError.d.ts +103 -0
  21. package/dist/GitError.d.ts.map +1 -0
  22. package/dist/GitError.js +85 -0
  23. package/dist/GitError.js.map +1 -0
  24. package/dist/GitService.d.ts +175 -0
  25. package/dist/GitService.d.ts.map +1 -0
  26. package/dist/GitService.js +431 -0
  27. package/dist/GitService.js.map +1 -0
  28. package/dist/LocalFileSystem.d.ts +29 -4
  29. package/dist/LocalFileSystem.d.ts.map +1 -1
  30. package/dist/LocalFileSystem.js +80 -6
  31. package/dist/LocalFileSystem.js.map +1 -1
  32. package/dist/MarkdownConverter.d.ts +49 -2
  33. package/dist/MarkdownConverter.d.ts.map +1 -1
  34. package/dist/MarkdownConverter.js +73 -111
  35. package/dist/MarkdownConverter.js.map +1 -1
  36. package/dist/SchemaConverterError.d.ts +108 -0
  37. package/dist/SchemaConverterError.d.ts.map +1 -0
  38. package/dist/SchemaConverterError.js +84 -0
  39. package/dist/SchemaConverterError.js.map +1 -0
  40. package/dist/Schemas.d.ts +225 -1
  41. package/dist/Schemas.d.ts.map +1 -1
  42. package/dist/Schemas.js +155 -6
  43. package/dist/Schemas.js.map +1 -1
  44. package/dist/SyncEngine.d.ts +30 -20
  45. package/dist/SyncEngine.d.ts.map +1 -1
  46. package/dist/SyncEngine.js +566 -117
  47. package/dist/SyncEngine.js.map +1 -1
  48. package/dist/ast/BlockNode.d.ts +468 -0
  49. package/dist/ast/BlockNode.d.ts.map +1 -0
  50. package/dist/ast/BlockNode.js +319 -0
  51. package/dist/ast/BlockNode.js.map +1 -0
  52. package/dist/ast/Document.d.ts +244 -0
  53. package/dist/ast/Document.d.ts.map +1 -0
  54. package/dist/ast/Document.js +69 -0
  55. package/dist/ast/Document.js.map +1 -0
  56. package/dist/ast/InlineNode.d.ts +477 -0
  57. package/dist/ast/InlineNode.d.ts.map +1 -0
  58. package/dist/ast/InlineNode.js +263 -0
  59. package/dist/ast/InlineNode.js.map +1 -0
  60. package/dist/ast/MacroNode.d.ts +267 -0
  61. package/dist/ast/MacroNode.d.ts.map +1 -0
  62. package/dist/ast/MacroNode.js +164 -0
  63. package/dist/ast/MacroNode.js.map +1 -0
  64. package/dist/ast/index.d.ts +10 -0
  65. package/dist/ast/index.d.ts.map +1 -0
  66. package/dist/ast/index.js +14 -0
  67. package/dist/ast/index.js.map +1 -0
  68. package/dist/bin.js +33 -149
  69. package/dist/bin.js.map +1 -1
  70. package/dist/commands/auth.d.ts +15 -0
  71. package/dist/commands/auth.d.ts.map +1 -0
  72. package/dist/commands/auth.js +86 -0
  73. package/dist/commands/auth.js.map +1 -0
  74. package/dist/commands/clone.d.ts +12 -0
  75. package/dist/commands/clone.d.ts.map +1 -0
  76. package/dist/commands/clone.js +93 -0
  77. package/dist/commands/clone.js.map +1 -0
  78. package/dist/commands/delete.d.ts +13 -0
  79. package/dist/commands/delete.d.ts.map +1 -0
  80. package/dist/commands/delete.js +48 -0
  81. package/dist/commands/delete.js.map +1 -0
  82. package/dist/commands/errorHandler.d.ts +14 -0
  83. package/dist/commands/errorHandler.d.ts.map +1 -0
  84. package/dist/commands/errorHandler.js +33 -0
  85. package/dist/commands/errorHandler.js.map +1 -0
  86. package/dist/commands/git.d.ts +22 -0
  87. package/dist/commands/git.d.ts.map +1 -0
  88. package/dist/commands/git.js +72 -0
  89. package/dist/commands/git.js.map +1 -0
  90. package/dist/commands/index.d.ts +11 -0
  91. package/dist/commands/index.d.ts.map +1 -0
  92. package/dist/commands/index.js +11 -0
  93. package/dist/commands/index.js.map +1 -0
  94. package/dist/commands/layers.d.ts +31 -0
  95. package/dist/commands/layers.d.ts.map +1 -0
  96. package/dist/commands/layers.js +137 -0
  97. package/dist/commands/layers.js.map +1 -0
  98. package/dist/commands/new.d.ts +9 -0
  99. package/dist/commands/new.d.ts.map +1 -0
  100. package/dist/commands/new.js +80 -0
  101. package/dist/commands/new.js.map +1 -0
  102. package/dist/commands/pageTree.d.ts +18 -0
  103. package/dist/commands/pageTree.d.ts.map +1 -0
  104. package/dist/commands/pageTree.js +20 -0
  105. package/dist/commands/pageTree.js.map +1 -0
  106. package/dist/commands/shared.d.ts +15 -0
  107. package/dist/commands/shared.d.ts.map +1 -0
  108. package/dist/commands/shared.js +27 -0
  109. package/dist/commands/shared.js.map +1 -0
  110. package/dist/commands/sync.d.ts +15 -0
  111. package/dist/commands/sync.d.ts.map +1 -0
  112. package/dist/commands/sync.js +101 -0
  113. package/dist/commands/sync.js.map +1 -0
  114. package/dist/index.d.ts +10 -1
  115. package/dist/index.d.ts.map +1 -1
  116. package/dist/index.js +14 -0
  117. package/dist/index.js.map +1 -1
  118. package/dist/internal/NodeLayers.d.ts +7 -0
  119. package/dist/internal/NodeLayers.d.ts.map +1 -0
  120. package/dist/internal/NodeLayers.js +19 -0
  121. package/dist/internal/NodeLayers.js.map +1 -0
  122. package/dist/internal/frontmatter.d.ts +10 -0
  123. package/dist/internal/frontmatter.d.ts.map +1 -1
  124. package/dist/internal/frontmatter.js +16 -0
  125. package/dist/internal/frontmatter.js.map +1 -1
  126. package/dist/internal/gitCommands.d.ts +78 -0
  127. package/dist/internal/gitCommands.d.ts.map +1 -0
  128. package/dist/internal/gitCommands.js +156 -0
  129. package/dist/internal/gitCommands.js.map +1 -0
  130. package/dist/internal/hashUtils.d.ts +42 -1
  131. package/dist/internal/hashUtils.d.ts.map +1 -1
  132. package/dist/internal/hashUtils.js +38 -2
  133. package/dist/internal/hashUtils.js.map +1 -1
  134. package/dist/internal/oauthServer.d.ts +55 -0
  135. package/dist/internal/oauthServer.d.ts.map +1 -0
  136. package/dist/internal/oauthServer.js +110 -0
  137. package/dist/internal/oauthServer.js.map +1 -0
  138. package/dist/internal/pathUtils.d.ts +21 -4
  139. package/dist/internal/pathUtils.d.ts.map +1 -1
  140. package/dist/internal/pathUtils.js +24 -13
  141. package/dist/internal/pathUtils.js.map +1 -1
  142. package/dist/internal/tokenStorage.d.ts +75 -0
  143. package/dist/internal/tokenStorage.d.ts.map +1 -0
  144. package/dist/internal/tokenStorage.js +149 -0
  145. package/dist/internal/tokenStorage.js.map +1 -0
  146. package/dist/internal/userCache.d.ts +42 -0
  147. package/dist/internal/userCache.d.ts.map +1 -0
  148. package/dist/internal/userCache.js +51 -0
  149. package/dist/internal/userCache.js.map +1 -0
  150. package/dist/parsers/ConfluenceParser.d.ts +26 -0
  151. package/dist/parsers/ConfluenceParser.d.ts.map +1 -0
  152. package/dist/parsers/ConfluenceParser.js +792 -0
  153. package/dist/parsers/ConfluenceParser.js.map +1 -0
  154. package/dist/parsers/MarkdownParser.d.ts +26 -0
  155. package/dist/parsers/MarkdownParser.d.ts.map +1 -0
  156. package/dist/parsers/MarkdownParser.js +873 -0
  157. package/dist/parsers/MarkdownParser.js.map +1 -0
  158. package/dist/parsers/index.d.ts +8 -0
  159. package/dist/parsers/index.d.ts.map +1 -0
  160. package/dist/parsers/index.js +8 -0
  161. package/dist/parsers/index.js.map +1 -0
  162. package/dist/schemas/ConfluenceSchema.d.ts +21 -0
  163. package/dist/schemas/ConfluenceSchema.d.ts.map +1 -0
  164. package/dist/schemas/ConfluenceSchema.js +38 -0
  165. package/dist/schemas/ConfluenceSchema.js.map +1 -0
  166. package/dist/schemas/ConversionSchema.d.ts +35 -0
  167. package/dist/schemas/ConversionSchema.d.ts.map +1 -0
  168. package/dist/schemas/ConversionSchema.js +208 -0
  169. package/dist/schemas/ConversionSchema.js.map +1 -0
  170. package/dist/schemas/MarkdownSchema.d.ts +21 -0
  171. package/dist/schemas/MarkdownSchema.d.ts.map +1 -0
  172. package/dist/schemas/MarkdownSchema.js +38 -0
  173. package/dist/schemas/MarkdownSchema.js.map +1 -0
  174. package/dist/schemas/hast/HastFromHtml.d.ts +27 -0
  175. package/dist/schemas/hast/HastFromHtml.d.ts.map +1 -0
  176. package/dist/schemas/hast/HastFromHtml.js +107 -0
  177. package/dist/schemas/hast/HastFromHtml.js.map +1 -0
  178. package/dist/schemas/hast/HastSchema.d.ts +195 -0
  179. package/dist/schemas/hast/HastSchema.d.ts.map +1 -0
  180. package/dist/schemas/hast/HastSchema.js +183 -0
  181. package/dist/schemas/hast/HastSchema.js.map +1 -0
  182. package/dist/schemas/hast/index.d.ts +9 -0
  183. package/dist/schemas/hast/index.d.ts.map +1 -0
  184. package/dist/schemas/hast/index.js +3 -0
  185. package/dist/schemas/hast/index.js.map +1 -0
  186. package/dist/schemas/index.d.ts +14 -0
  187. package/dist/schemas/index.d.ts.map +1 -0
  188. package/dist/schemas/index.js +16 -0
  189. package/dist/schemas/index.js.map +1 -0
  190. package/dist/schemas/mdast/MdastFromMarkdown.d.ts +30 -0
  191. package/dist/schemas/mdast/MdastFromMarkdown.d.ts.map +1 -0
  192. package/dist/schemas/mdast/MdastFromMarkdown.js +79 -0
  193. package/dist/schemas/mdast/MdastFromMarkdown.js.map +1 -0
  194. package/dist/schemas/mdast/MdastSchema.d.ts +385 -0
  195. package/dist/schemas/mdast/MdastSchema.d.ts.map +1 -0
  196. package/dist/schemas/mdast/MdastSchema.js +266 -0
  197. package/dist/schemas/mdast/MdastSchema.js.map +1 -0
  198. package/dist/schemas/mdast/index.d.ts +10 -0
  199. package/dist/schemas/mdast/index.d.ts.map +1 -0
  200. package/dist/schemas/mdast/index.js +4 -0
  201. package/dist/schemas/mdast/index.js.map +1 -0
  202. package/dist/schemas/mdast/mdastToString.d.ts +13 -0
  203. package/dist/schemas/mdast/mdastToString.d.ts.map +1 -0
  204. package/dist/schemas/mdast/mdastToString.js +85 -0
  205. package/dist/schemas/mdast/mdastToString.js.map +1 -0
  206. package/dist/schemas/nodes/block/BlockSchema.d.ts +43 -0
  207. package/dist/schemas/nodes/block/BlockSchema.d.ts.map +1 -0
  208. package/dist/schemas/nodes/block/BlockSchema.js +634 -0
  209. package/dist/schemas/nodes/block/BlockSchema.js.map +1 -0
  210. package/dist/schemas/nodes/block/index.d.ts +7 -0
  211. package/dist/schemas/nodes/block/index.d.ts.map +1 -0
  212. package/dist/schemas/nodes/block/index.js +7 -0
  213. package/dist/schemas/nodes/block/index.js.map +1 -0
  214. package/dist/schemas/nodes/index.d.ts +9 -0
  215. package/dist/schemas/nodes/index.d.ts.map +1 -0
  216. package/dist/schemas/nodes/index.js +12 -0
  217. package/dist/schemas/nodes/index.js.map +1 -0
  218. package/dist/schemas/nodes/inline/InlineSchema.d.ts +48 -0
  219. package/dist/schemas/nodes/inline/InlineSchema.d.ts.map +1 -0
  220. package/dist/schemas/nodes/inline/InlineSchema.js +436 -0
  221. package/dist/schemas/nodes/inline/InlineSchema.js.map +1 -0
  222. package/dist/schemas/nodes/inline/index.d.ts +7 -0
  223. package/dist/schemas/nodes/inline/index.d.ts.map +1 -0
  224. package/dist/schemas/nodes/inline/index.js +7 -0
  225. package/dist/schemas/nodes/inline/index.js.map +1 -0
  226. package/dist/schemas/nodes/macro/MacroSchema.d.ts +27 -0
  227. package/dist/schemas/nodes/macro/MacroSchema.d.ts.map +1 -0
  228. package/dist/schemas/nodes/macro/MacroSchema.js +162 -0
  229. package/dist/schemas/nodes/macro/MacroSchema.js.map +1 -0
  230. package/dist/schemas/nodes/macro/index.d.ts +7 -0
  231. package/dist/schemas/nodes/macro/index.d.ts.map +1 -0
  232. package/dist/schemas/nodes/macro/index.js +7 -0
  233. package/dist/schemas/nodes/macro/index.js.map +1 -0
  234. package/dist/schemas/preprocessing/ConfluencePreprocessor.d.ts +24 -0
  235. package/dist/schemas/preprocessing/ConfluencePreprocessor.d.ts.map +1 -0
  236. package/dist/schemas/preprocessing/ConfluencePreprocessor.js +351 -0
  237. package/dist/schemas/preprocessing/ConfluencePreprocessor.js.map +1 -0
  238. package/dist/schemas/preprocessing/index.d.ts +8 -0
  239. package/dist/schemas/preprocessing/index.d.ts.map +1 -0
  240. package/dist/schemas/preprocessing/index.js +2 -0
  241. package/dist/schemas/preprocessing/index.js.map +1 -0
  242. package/dist/serializers/ConfluenceSerializer.d.ts +30 -0
  243. package/dist/serializers/ConfluenceSerializer.d.ts.map +1 -0
  244. package/dist/serializers/ConfluenceSerializer.js +551 -0
  245. package/dist/serializers/ConfluenceSerializer.js.map +1 -0
  246. package/dist/serializers/MarkdownSerializer.d.ts +34 -0
  247. package/dist/serializers/MarkdownSerializer.d.ts.map +1 -0
  248. package/dist/serializers/MarkdownSerializer.js +355 -0
  249. package/dist/serializers/MarkdownSerializer.js.map +1 -0
  250. package/dist/serializers/index.d.ts +8 -0
  251. package/dist/serializers/index.d.ts.map +1 -0
  252. package/dist/serializers/index.js +8 -0
  253. package/dist/serializers/index.js.map +1 -0
  254. package/package.json +27 -16
  255. package/src/ConfluenceAuth.ts +571 -0
  256. package/src/ConfluenceClient.ts +188 -156
  257. package/src/ConfluenceConfig.ts +63 -7
  258. package/src/ConfluenceError.ts +110 -14
  259. package/src/GitError.ts +92 -0
  260. package/src/GitService.ts +859 -0
  261. package/src/LocalFileSystem.ts +179 -9
  262. package/src/MarkdownConverter.ts +126 -122
  263. package/src/SchemaConverterError.ts +108 -0
  264. package/src/Schemas.ts +223 -6
  265. package/src/SyncEngine.ts +745 -162
  266. package/src/ast/BlockNode.ts +425 -0
  267. package/src/ast/Document.ts +90 -0
  268. package/src/ast/InlineNode.ts +323 -0
  269. package/src/ast/MacroNode.ts +245 -0
  270. package/src/ast/index.ts +83 -0
  271. package/src/bin.ts +50 -249
  272. package/src/commands/auth.ts +117 -0
  273. package/src/commands/clone.ts +145 -0
  274. package/src/commands/delete.ts +57 -0
  275. package/src/commands/errorHandler.ts +32 -0
  276. package/src/commands/git.ts +114 -0
  277. package/src/commands/index.ts +10 -0
  278. package/src/commands/layers.ts +211 -0
  279. package/src/commands/new.ts +99 -0
  280. package/src/commands/pageTree.ts +40 -0
  281. package/src/commands/shared.ts +35 -0
  282. package/src/commands/sync.ts +129 -0
  283. package/src/index.ts +21 -1
  284. package/src/internal/NodeLayers.ts +21 -0
  285. package/src/internal/frontmatter.ts +21 -0
  286. package/src/internal/gitCommands.ts +229 -0
  287. package/src/internal/hashUtils.ts +65 -3
  288. package/src/internal/oauthServer.ts +199 -0
  289. package/src/internal/pathUtils.ts +34 -17
  290. package/src/internal/tokenStorage.ts +240 -0
  291. package/src/internal/userCache.ts +90 -0
  292. package/src/parsers/ConfluenceParser.ts +950 -0
  293. package/src/parsers/MarkdownParser.ts +1198 -0
  294. package/src/parsers/index.ts +8 -0
  295. package/src/schemas/ConfluenceSchema.ts +56 -0
  296. package/src/schemas/ConversionSchema.ts +318 -0
  297. package/src/schemas/MarkdownSchema.ts +56 -0
  298. package/src/schemas/hast/HastFromHtml.ts +153 -0
  299. package/src/schemas/hast/HastSchema.ts +274 -0
  300. package/src/schemas/hast/index.ts +35 -0
  301. package/src/schemas/index.ts +20 -0
  302. package/src/schemas/mdast/MdastFromMarkdown.ts +118 -0
  303. package/src/schemas/mdast/MdastSchema.ts +566 -0
  304. package/src/schemas/mdast/index.ts +59 -0
  305. package/src/schemas/mdast/mdastToString.ts +102 -0
  306. package/src/schemas/nodes/block/BlockSchema.ts +773 -0
  307. package/src/schemas/nodes/block/index.ts +13 -0
  308. package/src/schemas/nodes/index.ts +20 -0
  309. package/src/schemas/nodes/inline/InlineSchema.ts +523 -0
  310. package/src/schemas/nodes/inline/index.ts +14 -0
  311. package/src/schemas/nodes/macro/MacroSchema.ts +226 -0
  312. package/src/schemas/nodes/macro/index.ts +6 -0
  313. package/src/schemas/preprocessing/ConfluencePreprocessor.ts +446 -0
  314. package/src/schemas/preprocessing/index.ts +8 -0
  315. package/src/serializers/ConfluenceSerializer.ts +717 -0
  316. package/src/serializers/MarkdownSerializer.ts +493 -0
  317. package/src/serializers/index.ts +8 -0
  318. package/test/GitService.test.ts +209 -0
  319. package/test/MarkdownConverter.test.ts +37 -3
  320. package/test/Schemas.test.ts +97 -2
  321. package/test/ast/BlockNode.test.ts +265 -0
  322. package/test/ast/Document.test.ts +126 -0
  323. package/test/ast/InlineNode.test.ts +161 -0
  324. package/test/fixtures/integration-test.html.fixture +103 -0
  325. package/test/fixtures/integration-test.md.expected +257 -0
  326. package/test/integration.test.ts +269 -0
  327. package/test/oauthServer.test.ts +50 -0
  328. package/test/parsers/ConfluenceParser.test.ts +283 -0
  329. package/test/schemas/ConfluencePreprocessor.test.ts +180 -0
  330. package/test/schemas/ConversionSchema.test.ts +159 -0
  331. package/test/schemas/HastSchema.test.ts +138 -0
  332. package/test/schemas/MdastSchema.test.ts +145 -0
  333. package/test/schemas/nodes/block/BlockSchema.test.ts +173 -0
  334. package/test/schemas/nodes/inline/InlineSchema.test.ts +198 -0
  335. package/test/schemas/nodes/macro/MacroSchema.test.ts +142 -0
  336. package/test/tokenStorage.test.ts +99 -0
@@ -0,0 +1,240 @@
1
+ /**
2
+ * Token storage for OAuth credentials.
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 { FileSystemError } from "../ConfluenceError.js"
13
+ import { type OAuthConfig, OAuthConfigSchema, type OAuthToken, OAuthTokenSchema } from "../Schemas.js"
14
+
15
+ const TOKEN_DIR_NAME = ".confluence"
16
+ const TOKEN_FILE_NAME = "auth.json"
17
+ const CONFIG_FILE_NAME = "config.json"
18
+
19
+ /**
20
+ * Service for getting the home directory.
21
+ * This allows mocking in tests.
22
+ *
23
+ * @category Services
24
+ */
25
+ export interface HomeDirectory {
26
+ readonly get: () => Effect.Effect<string>
27
+ }
28
+
29
+ /**
30
+ * Tag for the HomeDirectory service.
31
+ *
32
+ * @category Services
33
+ */
34
+ export class HomeDirectoryTag extends Context.Tag("@knpkv/confluence-to-markdown/HomeDirectory")<
35
+ HomeDirectoryTag,
36
+ HomeDirectory
37
+ >() {}
38
+
39
+ /**
40
+ * Default implementation using process.env.HOME.
41
+ *
42
+ * @category Layers
43
+ */
44
+ export const HomeDirectoryLive: Layer.Layer<HomeDirectoryTag> = Layer.succeed(
45
+ HomeDirectoryTag,
46
+ {
47
+ get: () => Effect.sync(() => process.env.HOME ?? process.env.USERPROFILE ?? "/")
48
+ }
49
+ )
50
+
51
+ /**
52
+ * Get the token directory path.
53
+ */
54
+ const getTokenDir = (): Effect.Effect<string, never, HomeDirectoryTag | Path.Path> =>
55
+ Effect.gen(function*() {
56
+ const homeDir = yield* HomeDirectoryTag
57
+ const path = yield* Path.Path
58
+ const home = yield* homeDir.get()
59
+ return path.join(home, TOKEN_DIR_NAME)
60
+ })
61
+
62
+ /**
63
+ * Get the token file path.
64
+ */
65
+ const getTokenPath = (): Effect.Effect<string, never, HomeDirectoryTag | Path.Path> =>
66
+ Effect.gen(function*() {
67
+ const path = yield* Path.Path
68
+ const tokenDir = yield* getTokenDir()
69
+ return path.join(tokenDir, TOKEN_FILE_NAME)
70
+ })
71
+
72
+ /**
73
+ * Get the config file path.
74
+ */
75
+ const getConfigPath = (): Effect.Effect<string, never, HomeDirectoryTag | Path.Path> =>
76
+ Effect.gen(function*() {
77
+ const path = yield* Path.Path
78
+ const tokenDir = yield* getTokenDir()
79
+ return path.join(tokenDir, CONFIG_FILE_NAME)
80
+ })
81
+
82
+ /**
83
+ * Load stored OAuth token from disk.
84
+ *
85
+ * @returns The stored token or null if not found/invalid
86
+ *
87
+ * @category Token Storage
88
+ */
89
+ export const loadToken = (): Effect.Effect<
90
+ OAuthToken | null,
91
+ FileSystemError,
92
+ FileSystem.FileSystem | Path.Path | HomeDirectoryTag
93
+ > =>
94
+ Effect.gen(function*() {
95
+ const fs = yield* FileSystem.FileSystem
96
+ const tokenPath = yield* getTokenPath()
97
+
98
+ const exists = yield* fs.exists(tokenPath).pipe(
99
+ Effect.catchAll(() => Effect.succeed(false))
100
+ )
101
+ if (!exists) {
102
+ return null
103
+ }
104
+
105
+ const content = yield* fs.readFileString(tokenPath).pipe(
106
+ Effect.mapError((cause) => new FileSystemError({ operation: "read", path: tokenPath, cause }))
107
+ )
108
+
109
+ let parsed: unknown
110
+ try {
111
+ parsed = JSON.parse(content)
112
+ } catch {
113
+ return null
114
+ }
115
+
116
+ const decoded = yield* Schema.decodeUnknown(OAuthTokenSchema)(parsed).pipe(
117
+ Effect.catchAll(() => Effect.succeed(null))
118
+ )
119
+
120
+ return decoded
121
+ })
122
+
123
+ /**
124
+ * Save OAuth token to disk with secure permissions.
125
+ *
126
+ * @param token - The token to save
127
+ *
128
+ * @category Token Storage
129
+ */
130
+ export const saveToken = (
131
+ token: OAuthToken
132
+ ): Effect.Effect<void, FileSystemError, FileSystem.FileSystem | Path.Path | HomeDirectoryTag> =>
133
+ Effect.gen(function*() {
134
+ const fs = yield* FileSystem.FileSystem
135
+ const tokenDir = yield* getTokenDir()
136
+ const tokenPath = yield* getTokenPath()
137
+
138
+ yield* fs.makeDirectory(tokenDir, { recursive: true }).pipe(
139
+ Effect.mapError((cause) => new FileSystemError({ operation: "mkdir", path: tokenDir, cause }))
140
+ )
141
+
142
+ yield* fs.writeFileString(tokenPath, JSON.stringify(token, null, 2)).pipe(
143
+ Effect.mapError((cause) => new FileSystemError({ operation: "write", path: tokenPath, cause }))
144
+ )
145
+
146
+ // Set secure permissions (owner only)
147
+ yield* fs.chmod(tokenPath, 0o600).pipe(
148
+ Effect.mapError((cause) => new FileSystemError({ operation: "write", path: tokenPath, cause }))
149
+ )
150
+ })
151
+
152
+ /**
153
+ * Delete stored OAuth token.
154
+ *
155
+ * @category Token Storage
156
+ */
157
+ export const deleteToken = (): Effect.Effect<
158
+ void,
159
+ FileSystemError,
160
+ FileSystem.FileSystem | Path.Path | HomeDirectoryTag
161
+ > =>
162
+ Effect.gen(function*() {
163
+ const fs = yield* FileSystem.FileSystem
164
+ const tokenPath = yield* getTokenPath()
165
+
166
+ yield* fs.remove(tokenPath).pipe(
167
+ Effect.catchAll(() => Effect.void),
168
+ Effect.mapError((cause) => new FileSystemError({ operation: "delete", path: tokenPath, cause }))
169
+ )
170
+ })
171
+
172
+ /**
173
+ * Load OAuth client configuration from disk.
174
+ *
175
+ * @returns The stored config or null if not found/invalid
176
+ *
177
+ * @category OAuth Config
178
+ */
179
+ export const loadOAuthConfig = (): Effect.Effect<
180
+ OAuthConfig | null,
181
+ FileSystemError,
182
+ FileSystem.FileSystem | Path.Path | HomeDirectoryTag
183
+ > =>
184
+ Effect.gen(function*() {
185
+ const fs = yield* FileSystem.FileSystem
186
+ const configPath = yield* getConfigPath()
187
+
188
+ const exists = yield* fs.exists(configPath).pipe(
189
+ Effect.catchAll(() => Effect.succeed(false))
190
+ )
191
+ if (!exists) {
192
+ return null
193
+ }
194
+
195
+ const content = yield* fs.readFileString(configPath).pipe(
196
+ Effect.mapError((cause) => new FileSystemError({ operation: "read", path: configPath, cause }))
197
+ )
198
+
199
+ let parsed: unknown
200
+ try {
201
+ parsed = JSON.parse(content)
202
+ } catch {
203
+ return null
204
+ }
205
+
206
+ const decoded = yield* Schema.decodeUnknown(OAuthConfigSchema)(parsed).pipe(
207
+ Effect.catchAll(() => Effect.succeed(null))
208
+ )
209
+
210
+ return decoded
211
+ })
212
+
213
+ /**
214
+ * Save OAuth client configuration to disk with secure permissions.
215
+ *
216
+ * @param config - The config to save
217
+ *
218
+ * @category OAuth Config
219
+ */
220
+ export const saveOAuthConfig = (
221
+ config: OAuthConfig
222
+ ): Effect.Effect<void, FileSystemError, FileSystem.FileSystem | Path.Path | HomeDirectoryTag> =>
223
+ Effect.gen(function*() {
224
+ const fs = yield* FileSystem.FileSystem
225
+ const tokenDir = yield* getTokenDir()
226
+ const configPath = yield* getConfigPath()
227
+
228
+ yield* fs.makeDirectory(tokenDir, { recursive: true }).pipe(
229
+ Effect.mapError((cause) => new FileSystemError({ operation: "mkdir", path: tokenDir, cause }))
230
+ )
231
+
232
+ yield* fs.writeFileString(configPath, JSON.stringify(config, null, 2)).pipe(
233
+ Effect.mapError((cause) => new FileSystemError({ operation: "write", path: configPath, cause }))
234
+ )
235
+
236
+ // Set secure permissions (owner only)
237
+ yield* fs.chmod(configPath, 0o600).pipe(
238
+ Effect.mapError((cause) => new FileSystemError({ operation: "write", path: configPath, cause }))
239
+ )
240
+ })
@@ -0,0 +1,90 @@
1
+ /**
2
+ * In-memory cache for Atlassian user info.
3
+ *
4
+ * @module
5
+ */
6
+ import * as Context from "effect/Context"
7
+ import * as Effect from "effect/Effect"
8
+ import * as Layer from "effect/Layer"
9
+ import * as Ref from "effect/Ref"
10
+ import type { AtlassianUser } from "../Schemas.js"
11
+
12
+ /**
13
+ * User cache service for caching Atlassian user lookups.
14
+ *
15
+ * @category Cache
16
+ */
17
+ export class UserCache extends Context.Tag("@knpkv/confluence-to-markdown/UserCache")<
18
+ UserCache,
19
+ {
20
+ /**
21
+ * Get user from cache, or fetch and cache if not present.
22
+ */
23
+ readonly getOrFetch: (
24
+ accountId: string,
25
+ fetch: (accountId: string) => Effect.Effect<AtlassianUser, unknown>
26
+ ) => Effect.Effect<AtlassianUser, unknown>
27
+
28
+ /**
29
+ * Get user from cache only (no fetch).
30
+ */
31
+ readonly get: (accountId: string) => Effect.Effect<AtlassianUser | undefined>
32
+
33
+ /**
34
+ * Put user into cache.
35
+ */
36
+ readonly put: (accountId: string, user: AtlassianUser) => Effect.Effect<void>
37
+
38
+ /**
39
+ * Clear the cache.
40
+ */
41
+ readonly clear: () => Effect.Effect<void>
42
+ }
43
+ >() {}
44
+
45
+ /**
46
+ * Create the user cache service.
47
+ */
48
+ const make = Effect.gen(function*() {
49
+ const cache = yield* Ref.make<Map<string, AtlassianUser>>(new Map())
50
+
51
+ const get = (accountId: string): Effect.Effect<AtlassianUser | undefined> =>
52
+ Ref.get(cache).pipe(Effect.map((m) => m.get(accountId)))
53
+
54
+ const put = (accountId: string, user: AtlassianUser): Effect.Effect<void> =>
55
+ Ref.update(cache, (m) => {
56
+ const newMap = new Map(m)
57
+ newMap.set(accountId, user)
58
+ return newMap
59
+ })
60
+
61
+ const getOrFetch = <E>(
62
+ accountId: string,
63
+ fetch: (accountId: string) => Effect.Effect<AtlassianUser, E>
64
+ ): Effect.Effect<AtlassianUser, E> =>
65
+ Effect.gen(function*() {
66
+ const cached = yield* get(accountId)
67
+ if (cached) {
68
+ return cached
69
+ }
70
+ const user = yield* fetch(accountId)
71
+ yield* put(accountId, user)
72
+ return user
73
+ })
74
+
75
+ const clear = (): Effect.Effect<void> => Ref.set(cache, new Map())
76
+
77
+ return UserCache.of({
78
+ getOrFetch,
79
+ get,
80
+ put,
81
+ clear
82
+ })
83
+ })
84
+
85
+ /**
86
+ * Layer that provides UserCache.
87
+ *
88
+ * @category Layers
89
+ */
90
+ export const UserCacheLayer: Layer.Layer<UserCache> = Layer.effect(UserCache, make)