@wonseok-han/confluence-sync 1.8.0 → 2.0.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 (518) hide show
  1. package/README.md +44 -410
  2. package/dist/cli/args.js +1 -1
  3. package/dist/cli/help.js +214 -115
  4. package/dist/cli/init.js +44 -5
  5. package/dist/cli/main.js +42 -11
  6. package/dist/cli/status.js +83 -0
  7. package/dist/confluence/client.js +121 -57
  8. package/dist/confluence/config.js +12 -8
  9. package/dist/confluence/env-file.js +23 -0
  10. package/dist/confluence/links.js +18 -0
  11. package/dist/conversion/asset-paths.js +5 -0
  12. package/dist/conversion/attachments.js +29 -7
  13. package/dist/conversion/convert.js +10 -22
  14. package/dist/conversion/footnotes.js +114 -11
  15. package/dist/conversion/markdown.js +26 -0
  16. package/dist/conversion/pdf-pages.js +10 -8
  17. package/dist/documents/anchors.js +9 -0
  18. package/dist/documents/callout-roundtrip.js +75 -0
  19. package/dist/documents/callouts.js +74 -0
  20. package/dist/documents/catalog.js +2 -2
  21. package/dist/documents/footnote-roundtrip.js +96 -0
  22. package/dist/documents/format.js +116 -0
  23. package/dist/documents/html-tables.js +30 -0
  24. package/dist/documents/html-to-markdown.js +47 -13
  25. package/dist/documents/markdown.js +46 -4
  26. package/dist/documents/obsidian.js +6 -3
  27. package/dist/documents/tables.js +89 -0
  28. package/dist/runtime/network.js +4 -0
  29. package/dist/shared/colors.js +2 -2
  30. package/dist/shared/progress.js +164 -0
  31. package/dist/shared/version.js +12 -0
  32. package/dist/site/assets/Monaco-LICENSE.txt +21 -0
  33. package/dist/site/assets/Monaco-ThirdPartyNotices.txt +448 -0
  34. package/dist/site/assets/PDFJS-LICENSE.txt +177 -0
  35. package/dist/site/assets/app.css +1 -0
  36. package/dist/site/assets/app.js +4 -0
  37. package/dist/site/assets/chunks/chunk-TEW74QPN.js +35 -0
  38. package/dist/site/assets/chunks/markdown-S6623E66.js +1 -0
  39. package/dist/site/assets/chunks/monaco-36ZGUH4Y.js +897 -0
  40. package/dist/site/assets/chunks/monaco-HFLEBJTI.css +1 -0
  41. package/dist/site/assets/chunks/pdf-DDN7TXTP.js +55 -0
  42. package/dist/site/assets/confluence-env-example.txt +32 -0
  43. package/dist/site/assets/editor.worker.js +1 -0
  44. package/dist/site/assets/favicon.svg +3 -0
  45. package/dist/site/assets/guide/01-account-settings.png +0 -0
  46. package/dist/site/assets/guide/02-atlassian-security.png +0 -0
  47. package/dist/site/assets/guide/03-api-tokens.png +0 -0
  48. package/dist/site/assets/guide/04-scope-api-token-step1.png +0 -0
  49. package/dist/site/assets/guide/05-scope-api-token-step2.png +0 -0
  50. package/dist/site/assets/guide/06-scope-api-token-step3.png +0 -0
  51. package/dist/site/assets/guide/07-scope-api-token-step4.png +0 -0
  52. package/dist/site/assets/guide/web/document-preview.png +0 -0
  53. package/dist/site/assets/guide/web/remote-diff.png +0 -0
  54. package/dist/site/assets/guide/web/sync-workspace.png +0 -0
  55. package/dist/site/assets/pdf/cmaps/78-EUC-H.bcmap +0 -0
  56. package/dist/site/assets/pdf/cmaps/78-EUC-V.bcmap +0 -0
  57. package/dist/site/assets/pdf/cmaps/78-H.bcmap +0 -0
  58. package/dist/site/assets/pdf/cmaps/78-RKSJ-H.bcmap +0 -0
  59. package/dist/site/assets/pdf/cmaps/78-RKSJ-V.bcmap +0 -0
  60. package/dist/site/assets/pdf/cmaps/78-V.bcmap +0 -0
  61. package/dist/site/assets/pdf/cmaps/78ms-RKSJ-H.bcmap +0 -0
  62. package/dist/site/assets/pdf/cmaps/78ms-RKSJ-V.bcmap +0 -0
  63. package/dist/site/assets/pdf/cmaps/83pv-RKSJ-H.bcmap +0 -0
  64. package/dist/site/assets/pdf/cmaps/90ms-RKSJ-H.bcmap +0 -0
  65. package/dist/site/assets/pdf/cmaps/90ms-RKSJ-V.bcmap +0 -0
  66. package/dist/site/assets/pdf/cmaps/90msp-RKSJ-H.bcmap +0 -0
  67. package/dist/site/assets/pdf/cmaps/90msp-RKSJ-V.bcmap +0 -0
  68. package/dist/site/assets/pdf/cmaps/90pv-RKSJ-H.bcmap +0 -0
  69. package/dist/site/assets/pdf/cmaps/90pv-RKSJ-V.bcmap +0 -0
  70. package/dist/site/assets/pdf/cmaps/Add-H.bcmap +0 -0
  71. package/dist/site/assets/pdf/cmaps/Add-RKSJ-H.bcmap +0 -0
  72. package/dist/site/assets/pdf/cmaps/Add-RKSJ-V.bcmap +0 -0
  73. package/dist/site/assets/pdf/cmaps/Add-V.bcmap +0 -0
  74. package/dist/site/assets/pdf/cmaps/Adobe-CNS1-0.bcmap +0 -0
  75. package/dist/site/assets/pdf/cmaps/Adobe-CNS1-1.bcmap +0 -0
  76. package/dist/site/assets/pdf/cmaps/Adobe-CNS1-2.bcmap +0 -0
  77. package/dist/site/assets/pdf/cmaps/Adobe-CNS1-3.bcmap +0 -0
  78. package/dist/site/assets/pdf/cmaps/Adobe-CNS1-4.bcmap +0 -0
  79. package/dist/site/assets/pdf/cmaps/Adobe-CNS1-5.bcmap +0 -0
  80. package/dist/site/assets/pdf/cmaps/Adobe-CNS1-6.bcmap +0 -0
  81. package/dist/site/assets/pdf/cmaps/Adobe-CNS1-UCS2.bcmap +0 -0
  82. package/dist/site/assets/pdf/cmaps/Adobe-GB1-0.bcmap +0 -0
  83. package/dist/site/assets/pdf/cmaps/Adobe-GB1-1.bcmap +0 -0
  84. package/dist/site/assets/pdf/cmaps/Adobe-GB1-2.bcmap +0 -0
  85. package/dist/site/assets/pdf/cmaps/Adobe-GB1-3.bcmap +0 -0
  86. package/dist/site/assets/pdf/cmaps/Adobe-GB1-4.bcmap +0 -0
  87. package/dist/site/assets/pdf/cmaps/Adobe-GB1-5.bcmap +0 -0
  88. package/dist/site/assets/pdf/cmaps/Adobe-GB1-UCS2.bcmap +0 -0
  89. package/dist/site/assets/pdf/cmaps/Adobe-Japan1-0.bcmap +0 -0
  90. package/dist/site/assets/pdf/cmaps/Adobe-Japan1-1.bcmap +0 -0
  91. package/dist/site/assets/pdf/cmaps/Adobe-Japan1-2.bcmap +0 -0
  92. package/dist/site/assets/pdf/cmaps/Adobe-Japan1-3.bcmap +0 -0
  93. package/dist/site/assets/pdf/cmaps/Adobe-Japan1-4.bcmap +0 -0
  94. package/dist/site/assets/pdf/cmaps/Adobe-Japan1-5.bcmap +0 -0
  95. package/dist/site/assets/pdf/cmaps/Adobe-Japan1-6.bcmap +0 -0
  96. package/dist/site/assets/pdf/cmaps/Adobe-Japan1-UCS2.bcmap +0 -0
  97. package/dist/site/assets/pdf/cmaps/Adobe-Korea1-0.bcmap +0 -0
  98. package/dist/site/assets/pdf/cmaps/Adobe-Korea1-1.bcmap +0 -0
  99. package/dist/site/assets/pdf/cmaps/Adobe-Korea1-2.bcmap +0 -0
  100. package/dist/site/assets/pdf/cmaps/Adobe-Korea1-UCS2.bcmap +0 -0
  101. package/dist/site/assets/pdf/cmaps/B5-H.bcmap +0 -0
  102. package/dist/site/assets/pdf/cmaps/B5-V.bcmap +0 -0
  103. package/dist/site/assets/pdf/cmaps/B5pc-H.bcmap +0 -0
  104. package/dist/site/assets/pdf/cmaps/B5pc-V.bcmap +0 -0
  105. package/dist/site/assets/pdf/cmaps/CNS-EUC-H.bcmap +0 -0
  106. package/dist/site/assets/pdf/cmaps/CNS-EUC-V.bcmap +0 -0
  107. package/dist/site/assets/pdf/cmaps/CNS1-H.bcmap +0 -0
  108. package/dist/site/assets/pdf/cmaps/CNS1-V.bcmap +0 -0
  109. package/dist/site/assets/pdf/cmaps/CNS2-H.bcmap +0 -0
  110. package/dist/site/assets/pdf/cmaps/CNS2-V.bcmap +3 -0
  111. package/dist/site/assets/pdf/cmaps/ETHK-B5-H.bcmap +0 -0
  112. package/dist/site/assets/pdf/cmaps/ETHK-B5-V.bcmap +0 -0
  113. package/dist/site/assets/pdf/cmaps/ETen-B5-H.bcmap +0 -0
  114. package/dist/site/assets/pdf/cmaps/ETen-B5-V.bcmap +0 -0
  115. package/dist/site/assets/pdf/cmaps/ETenms-B5-H.bcmap +3 -0
  116. package/dist/site/assets/pdf/cmaps/ETenms-B5-V.bcmap +0 -0
  117. package/dist/site/assets/pdf/cmaps/EUC-H.bcmap +0 -0
  118. package/dist/site/assets/pdf/cmaps/EUC-V.bcmap +0 -0
  119. package/dist/site/assets/pdf/cmaps/Ext-H.bcmap +0 -0
  120. package/dist/site/assets/pdf/cmaps/Ext-RKSJ-H.bcmap +0 -0
  121. package/dist/site/assets/pdf/cmaps/Ext-RKSJ-V.bcmap +0 -0
  122. package/dist/site/assets/pdf/cmaps/Ext-V.bcmap +0 -0
  123. package/dist/site/assets/pdf/cmaps/GB-EUC-H.bcmap +0 -0
  124. package/dist/site/assets/pdf/cmaps/GB-EUC-V.bcmap +0 -0
  125. package/dist/site/assets/pdf/cmaps/GB-H.bcmap +4 -0
  126. package/dist/site/assets/pdf/cmaps/GB-V.bcmap +0 -0
  127. package/dist/site/assets/pdf/cmaps/GBK-EUC-H.bcmap +0 -0
  128. package/dist/site/assets/pdf/cmaps/GBK-EUC-V.bcmap +0 -0
  129. package/dist/site/assets/pdf/cmaps/GBK2K-H.bcmap +0 -0
  130. package/dist/site/assets/pdf/cmaps/GBK2K-V.bcmap +0 -0
  131. package/dist/site/assets/pdf/cmaps/GBKp-EUC-H.bcmap +0 -0
  132. package/dist/site/assets/pdf/cmaps/GBKp-EUC-V.bcmap +0 -0
  133. package/dist/site/assets/pdf/cmaps/GBT-EUC-H.bcmap +0 -0
  134. package/dist/site/assets/pdf/cmaps/GBT-EUC-V.bcmap +0 -0
  135. package/dist/site/assets/pdf/cmaps/GBT-H.bcmap +0 -0
  136. package/dist/site/assets/pdf/cmaps/GBT-V.bcmap +0 -0
  137. package/dist/site/assets/pdf/cmaps/GBTpc-EUC-H.bcmap +0 -0
  138. package/dist/site/assets/pdf/cmaps/GBTpc-EUC-V.bcmap +0 -0
  139. package/dist/site/assets/pdf/cmaps/GBpc-EUC-H.bcmap +0 -0
  140. package/dist/site/assets/pdf/cmaps/GBpc-EUC-V.bcmap +0 -0
  141. package/dist/site/assets/pdf/cmaps/H.bcmap +0 -0
  142. package/dist/site/assets/pdf/cmaps/HKdla-B5-H.bcmap +0 -0
  143. package/dist/site/assets/pdf/cmaps/HKdla-B5-V.bcmap +0 -0
  144. package/dist/site/assets/pdf/cmaps/HKdlb-B5-H.bcmap +0 -0
  145. package/dist/site/assets/pdf/cmaps/HKdlb-B5-V.bcmap +0 -0
  146. package/dist/site/assets/pdf/cmaps/HKgccs-B5-H.bcmap +0 -0
  147. package/dist/site/assets/pdf/cmaps/HKgccs-B5-V.bcmap +0 -0
  148. package/dist/site/assets/pdf/cmaps/HKm314-B5-H.bcmap +0 -0
  149. package/dist/site/assets/pdf/cmaps/HKm314-B5-V.bcmap +0 -0
  150. package/dist/site/assets/pdf/cmaps/HKm471-B5-H.bcmap +0 -0
  151. package/dist/site/assets/pdf/cmaps/HKm471-B5-V.bcmap +0 -0
  152. package/dist/site/assets/pdf/cmaps/HKscs-B5-H.bcmap +0 -0
  153. package/dist/site/assets/pdf/cmaps/HKscs-B5-V.bcmap +0 -0
  154. package/dist/site/assets/pdf/cmaps/Hankaku.bcmap +0 -0
  155. package/dist/site/assets/pdf/cmaps/Hiragana.bcmap +0 -0
  156. package/dist/site/assets/pdf/cmaps/KSC-EUC-H.bcmap +0 -0
  157. package/dist/site/assets/pdf/cmaps/KSC-EUC-V.bcmap +0 -0
  158. package/dist/site/assets/pdf/cmaps/KSC-H.bcmap +0 -0
  159. package/dist/site/assets/pdf/cmaps/KSC-Johab-H.bcmap +0 -0
  160. package/dist/site/assets/pdf/cmaps/KSC-Johab-V.bcmap +0 -0
  161. package/dist/site/assets/pdf/cmaps/KSC-V.bcmap +0 -0
  162. package/dist/site/assets/pdf/cmaps/KSCms-UHC-H.bcmap +0 -0
  163. package/dist/site/assets/pdf/cmaps/KSCms-UHC-HW-H.bcmap +0 -0
  164. package/dist/site/assets/pdf/cmaps/KSCms-UHC-HW-V.bcmap +0 -0
  165. package/dist/site/assets/pdf/cmaps/KSCms-UHC-V.bcmap +0 -0
  166. package/dist/site/assets/pdf/cmaps/KSCpc-EUC-H.bcmap +0 -0
  167. package/dist/site/assets/pdf/cmaps/KSCpc-EUC-V.bcmap +0 -0
  168. package/dist/site/assets/pdf/cmaps/Katakana.bcmap +0 -0
  169. package/dist/site/assets/pdf/cmaps/LICENSE +36 -0
  170. package/dist/site/assets/pdf/cmaps/NWP-H.bcmap +0 -0
  171. package/dist/site/assets/pdf/cmaps/NWP-V.bcmap +0 -0
  172. package/dist/site/assets/pdf/cmaps/RKSJ-H.bcmap +0 -0
  173. package/dist/site/assets/pdf/cmaps/RKSJ-V.bcmap +0 -0
  174. package/dist/site/assets/pdf/cmaps/Roman.bcmap +0 -0
  175. package/dist/site/assets/pdf/cmaps/UniCNS-UCS2-H.bcmap +0 -0
  176. package/dist/site/assets/pdf/cmaps/UniCNS-UCS2-V.bcmap +0 -0
  177. package/dist/site/assets/pdf/cmaps/UniCNS-UTF16-H.bcmap +0 -0
  178. package/dist/site/assets/pdf/cmaps/UniCNS-UTF16-V.bcmap +0 -0
  179. package/dist/site/assets/pdf/cmaps/UniCNS-UTF32-H.bcmap +0 -0
  180. package/dist/site/assets/pdf/cmaps/UniCNS-UTF32-V.bcmap +0 -0
  181. package/dist/site/assets/pdf/cmaps/UniCNS-UTF8-H.bcmap +0 -0
  182. package/dist/site/assets/pdf/cmaps/UniCNS-UTF8-V.bcmap +0 -0
  183. package/dist/site/assets/pdf/cmaps/UniGB-UCS2-H.bcmap +0 -0
  184. package/dist/site/assets/pdf/cmaps/UniGB-UCS2-V.bcmap +0 -0
  185. package/dist/site/assets/pdf/cmaps/UniGB-UTF16-H.bcmap +0 -0
  186. package/dist/site/assets/pdf/cmaps/UniGB-UTF16-V.bcmap +0 -0
  187. package/dist/site/assets/pdf/cmaps/UniGB-UTF32-H.bcmap +0 -0
  188. package/dist/site/assets/pdf/cmaps/UniGB-UTF32-V.bcmap +0 -0
  189. package/dist/site/assets/pdf/cmaps/UniGB-UTF8-H.bcmap +0 -0
  190. package/dist/site/assets/pdf/cmaps/UniGB-UTF8-V.bcmap +0 -0
  191. package/dist/site/assets/pdf/cmaps/UniJIS-UCS2-H.bcmap +0 -0
  192. package/dist/site/assets/pdf/cmaps/UniJIS-UCS2-HW-H.bcmap +0 -0
  193. package/dist/site/assets/pdf/cmaps/UniJIS-UCS2-HW-V.bcmap +0 -0
  194. package/dist/site/assets/pdf/cmaps/UniJIS-UCS2-V.bcmap +0 -0
  195. package/dist/site/assets/pdf/cmaps/UniJIS-UTF16-H.bcmap +0 -0
  196. package/dist/site/assets/pdf/cmaps/UniJIS-UTF16-V.bcmap +0 -0
  197. package/dist/site/assets/pdf/cmaps/UniJIS-UTF32-H.bcmap +0 -0
  198. package/dist/site/assets/pdf/cmaps/UniJIS-UTF32-V.bcmap +0 -0
  199. package/dist/site/assets/pdf/cmaps/UniJIS-UTF8-H.bcmap +0 -0
  200. package/dist/site/assets/pdf/cmaps/UniJIS-UTF8-V.bcmap +0 -0
  201. package/dist/site/assets/pdf/cmaps/UniJIS2004-UTF16-H.bcmap +0 -0
  202. package/dist/site/assets/pdf/cmaps/UniJIS2004-UTF16-V.bcmap +0 -0
  203. package/dist/site/assets/pdf/cmaps/UniJIS2004-UTF32-H.bcmap +0 -0
  204. package/dist/site/assets/pdf/cmaps/UniJIS2004-UTF32-V.bcmap +0 -0
  205. package/dist/site/assets/pdf/cmaps/UniJIS2004-UTF8-H.bcmap +0 -0
  206. package/dist/site/assets/pdf/cmaps/UniJIS2004-UTF8-V.bcmap +0 -0
  207. package/dist/site/assets/pdf/cmaps/UniJISPro-UCS2-HW-V.bcmap +0 -0
  208. package/dist/site/assets/pdf/cmaps/UniJISPro-UCS2-V.bcmap +0 -0
  209. package/dist/site/assets/pdf/cmaps/UniJISPro-UTF8-V.bcmap +0 -0
  210. package/dist/site/assets/pdf/cmaps/UniJISX0213-UTF32-H.bcmap +0 -0
  211. package/dist/site/assets/pdf/cmaps/UniJISX0213-UTF32-V.bcmap +0 -0
  212. package/dist/site/assets/pdf/cmaps/UniJISX02132004-UTF32-H.bcmap +0 -0
  213. package/dist/site/assets/pdf/cmaps/UniJISX02132004-UTF32-V.bcmap +0 -0
  214. package/dist/site/assets/pdf/cmaps/UniKS-UCS2-H.bcmap +0 -0
  215. package/dist/site/assets/pdf/cmaps/UniKS-UCS2-V.bcmap +0 -0
  216. package/dist/site/assets/pdf/cmaps/UniKS-UTF16-H.bcmap +0 -0
  217. package/dist/site/assets/pdf/cmaps/UniKS-UTF16-V.bcmap +0 -0
  218. package/dist/site/assets/pdf/cmaps/UniKS-UTF32-H.bcmap +0 -0
  219. package/dist/site/assets/pdf/cmaps/UniKS-UTF32-V.bcmap +0 -0
  220. package/dist/site/assets/pdf/cmaps/UniKS-UTF8-H.bcmap +0 -0
  221. package/dist/site/assets/pdf/cmaps/UniKS-UTF8-V.bcmap +0 -0
  222. package/dist/site/assets/pdf/cmaps/V.bcmap +0 -0
  223. package/dist/site/assets/pdf/cmaps/WP-Symbol.bcmap +0 -0
  224. package/dist/site/assets/pdf/standard_fonts/FoxitDingbats.pfb +0 -0
  225. package/dist/site/assets/pdf/standard_fonts/FoxitFixed.pfb +0 -0
  226. package/dist/site/assets/pdf/standard_fonts/FoxitFixedBold.pfb +0 -0
  227. package/dist/site/assets/pdf/standard_fonts/FoxitFixedBoldItalic.pfb +0 -0
  228. package/dist/site/assets/pdf/standard_fonts/FoxitFixedItalic.pfb +0 -0
  229. package/dist/site/assets/pdf/standard_fonts/FoxitSerif.pfb +0 -0
  230. package/dist/site/assets/pdf/standard_fonts/FoxitSerifBold.pfb +0 -0
  231. package/dist/site/assets/pdf/standard_fonts/FoxitSerifBoldItalic.pfb +0 -0
  232. package/dist/site/assets/pdf/standard_fonts/FoxitSerifItalic.pfb +0 -0
  233. package/dist/site/assets/pdf/standard_fonts/FoxitSymbol.pfb +0 -0
  234. package/dist/site/assets/pdf/standard_fonts/LICENSE_FOXIT +27 -0
  235. package/dist/site/assets/pdf/standard_fonts/LICENSE_LIBERATION +363 -0
  236. package/dist/site/assets/pdf/standard_fonts/LiberationSans-Bold.ttf +0 -0
  237. package/dist/site/assets/pdf/standard_fonts/LiberationSans-BoldItalic.ttf +0 -0
  238. package/dist/site/assets/pdf/standard_fonts/LiberationSans-Italic.ttf +0 -0
  239. package/dist/site/assets/pdf/standard_fonts/LiberationSans-Regular.ttf +0 -0
  240. package/dist/site/assets/pdf/wasm/LICENSE_JBIG2 +196 -0
  241. package/dist/site/assets/pdf/wasm/LICENSE_OPENJPEG +39 -0
  242. package/dist/site/assets/pdf/wasm/LICENSE_PDFJS_JBIG2 +13 -0
  243. package/dist/site/assets/pdf/wasm/LICENSE_PDFJS_OPENJPEG +22 -0
  244. package/dist/site/assets/pdf/wasm/LICENSE_PDFJS_QCMS +19 -0
  245. package/dist/site/assets/pdf/wasm/LICENSE_QCMS +21 -0
  246. package/dist/site/assets/pdf/wasm/jbig2.wasm +0 -0
  247. package/dist/site/assets/pdf/wasm/jbig2_nowasm_fallback.js +15 -0
  248. package/dist/site/assets/pdf/wasm/openjpeg.wasm +0 -0
  249. package/dist/site/assets/pdf/wasm/openjpeg_nowasm_fallback.js +17 -0
  250. package/dist/site/assets/pdf/wasm/qcms_bg.wasm +0 -0
  251. package/dist/site/assets/pdf/wasm/quickjs-eval.js +16 -0
  252. package/dist/site/assets/pdf/wasm/quickjs-eval.wasm +0 -0
  253. package/dist/site/assets/pdf.worker.min.mjs +29 -0
  254. package/dist/site/assets/workspace.worker.js +409 -0
  255. package/dist/site/index.html +253 -0
  256. package/dist/sync/baseline.js +45 -0
  257. package/dist/sync/changes.js +51 -0
  258. package/dist/sync/diff.js +80 -0
  259. package/dist/sync/fetch.js +287 -0
  260. package/dist/sync/ignore.js +4 -4
  261. package/dist/sync/local-state.js +12 -0
  262. package/dist/sync/mapping.js +31 -8
  263. package/dist/sync/metadata.js +57 -0
  264. package/dist/sync/prepare.js +52 -0
  265. package/dist/sync/pull.js +618 -141
  266. package/dist/sync/push.js +260 -213
  267. package/dist/sync/reconcile.js +51 -0
  268. package/dist/sync/references.js +2 -2
  269. package/dist/sync/remap.js +17 -5
  270. package/dist/sync/render.js +3 -2
  271. package/dist/sync/restore.js +28 -0
  272. package/dist/sync/scope.js +47 -0
  273. package/dist/sync/staging.js +193 -0
  274. package/dist/sync/workspace.js +100 -0
  275. package/dist/web/assets/PDFJS-LICENSE.txt +177 -0
  276. package/dist/web/assets/app.css +1 -1
  277. package/dist/web/assets/app.js +3 -938
  278. package/dist/web/assets/chunks/chunk-TEW74QPN.js +35 -0
  279. package/dist/web/assets/chunks/markdown-S6623E66.js +1 -0
  280. package/dist/web/assets/chunks/monaco-36ZGUH4Y.js +897 -0
  281. package/dist/web/assets/chunks/monaco-HFLEBJTI.css +1 -0
  282. package/dist/web/assets/chunks/pdf-DDN7TXTP.js +55 -0
  283. package/dist/web/assets/confluence-env-example.txt +32 -0
  284. package/dist/web/assets/editor.worker.js +1 -30
  285. package/dist/web/assets/favicon.svg +3 -0
  286. package/dist/web/assets/guide/01-account-settings.png +0 -0
  287. package/dist/web/assets/guide/02-atlassian-security.png +0 -0
  288. package/dist/web/assets/guide/03-api-tokens.png +0 -0
  289. package/dist/web/assets/guide/04-scope-api-token-step1.png +0 -0
  290. package/dist/web/assets/guide/05-scope-api-token-step2.png +0 -0
  291. package/dist/web/assets/guide/06-scope-api-token-step3.png +0 -0
  292. package/dist/web/assets/guide/07-scope-api-token-step4.png +0 -0
  293. package/dist/web/assets/guide/web/document-preview.png +0 -0
  294. package/dist/web/assets/guide/web/remote-diff.png +0 -0
  295. package/dist/web/assets/guide/web/sync-workspace.png +0 -0
  296. package/dist/web/assets/pdf/cmaps/78-EUC-H.bcmap +0 -0
  297. package/dist/web/assets/pdf/cmaps/78-EUC-V.bcmap +0 -0
  298. package/dist/web/assets/pdf/cmaps/78-H.bcmap +0 -0
  299. package/dist/web/assets/pdf/cmaps/78-RKSJ-H.bcmap +0 -0
  300. package/dist/web/assets/pdf/cmaps/78-RKSJ-V.bcmap +0 -0
  301. package/dist/web/assets/pdf/cmaps/78-V.bcmap +0 -0
  302. package/dist/web/assets/pdf/cmaps/78ms-RKSJ-H.bcmap +0 -0
  303. package/dist/web/assets/pdf/cmaps/78ms-RKSJ-V.bcmap +0 -0
  304. package/dist/web/assets/pdf/cmaps/83pv-RKSJ-H.bcmap +0 -0
  305. package/dist/web/assets/pdf/cmaps/90ms-RKSJ-H.bcmap +0 -0
  306. package/dist/web/assets/pdf/cmaps/90ms-RKSJ-V.bcmap +0 -0
  307. package/dist/web/assets/pdf/cmaps/90msp-RKSJ-H.bcmap +0 -0
  308. package/dist/web/assets/pdf/cmaps/90msp-RKSJ-V.bcmap +0 -0
  309. package/dist/web/assets/pdf/cmaps/90pv-RKSJ-H.bcmap +0 -0
  310. package/dist/web/assets/pdf/cmaps/90pv-RKSJ-V.bcmap +0 -0
  311. package/dist/web/assets/pdf/cmaps/Add-H.bcmap +0 -0
  312. package/dist/web/assets/pdf/cmaps/Add-RKSJ-H.bcmap +0 -0
  313. package/dist/web/assets/pdf/cmaps/Add-RKSJ-V.bcmap +0 -0
  314. package/dist/web/assets/pdf/cmaps/Add-V.bcmap +0 -0
  315. package/dist/web/assets/pdf/cmaps/Adobe-CNS1-0.bcmap +0 -0
  316. package/dist/web/assets/pdf/cmaps/Adobe-CNS1-1.bcmap +0 -0
  317. package/dist/web/assets/pdf/cmaps/Adobe-CNS1-2.bcmap +0 -0
  318. package/dist/web/assets/pdf/cmaps/Adobe-CNS1-3.bcmap +0 -0
  319. package/dist/web/assets/pdf/cmaps/Adobe-CNS1-4.bcmap +0 -0
  320. package/dist/web/assets/pdf/cmaps/Adobe-CNS1-5.bcmap +0 -0
  321. package/dist/web/assets/pdf/cmaps/Adobe-CNS1-6.bcmap +0 -0
  322. package/dist/web/assets/pdf/cmaps/Adobe-CNS1-UCS2.bcmap +0 -0
  323. package/dist/web/assets/pdf/cmaps/Adobe-GB1-0.bcmap +0 -0
  324. package/dist/web/assets/pdf/cmaps/Adobe-GB1-1.bcmap +0 -0
  325. package/dist/web/assets/pdf/cmaps/Adobe-GB1-2.bcmap +0 -0
  326. package/dist/web/assets/pdf/cmaps/Adobe-GB1-3.bcmap +0 -0
  327. package/dist/web/assets/pdf/cmaps/Adobe-GB1-4.bcmap +0 -0
  328. package/dist/web/assets/pdf/cmaps/Adobe-GB1-5.bcmap +0 -0
  329. package/dist/web/assets/pdf/cmaps/Adobe-GB1-UCS2.bcmap +0 -0
  330. package/dist/web/assets/pdf/cmaps/Adobe-Japan1-0.bcmap +0 -0
  331. package/dist/web/assets/pdf/cmaps/Adobe-Japan1-1.bcmap +0 -0
  332. package/dist/web/assets/pdf/cmaps/Adobe-Japan1-2.bcmap +0 -0
  333. package/dist/web/assets/pdf/cmaps/Adobe-Japan1-3.bcmap +0 -0
  334. package/dist/web/assets/pdf/cmaps/Adobe-Japan1-4.bcmap +0 -0
  335. package/dist/web/assets/pdf/cmaps/Adobe-Japan1-5.bcmap +0 -0
  336. package/dist/web/assets/pdf/cmaps/Adobe-Japan1-6.bcmap +0 -0
  337. package/dist/web/assets/pdf/cmaps/Adobe-Japan1-UCS2.bcmap +0 -0
  338. package/dist/web/assets/pdf/cmaps/Adobe-Korea1-0.bcmap +0 -0
  339. package/dist/web/assets/pdf/cmaps/Adobe-Korea1-1.bcmap +0 -0
  340. package/dist/web/assets/pdf/cmaps/Adobe-Korea1-2.bcmap +0 -0
  341. package/dist/web/assets/pdf/cmaps/Adobe-Korea1-UCS2.bcmap +0 -0
  342. package/dist/web/assets/pdf/cmaps/B5-H.bcmap +0 -0
  343. package/dist/web/assets/pdf/cmaps/B5-V.bcmap +0 -0
  344. package/dist/web/assets/pdf/cmaps/B5pc-H.bcmap +0 -0
  345. package/dist/web/assets/pdf/cmaps/B5pc-V.bcmap +0 -0
  346. package/dist/web/assets/pdf/cmaps/CNS-EUC-H.bcmap +0 -0
  347. package/dist/web/assets/pdf/cmaps/CNS-EUC-V.bcmap +0 -0
  348. package/dist/web/assets/pdf/cmaps/CNS1-H.bcmap +0 -0
  349. package/dist/web/assets/pdf/cmaps/CNS1-V.bcmap +0 -0
  350. package/dist/web/assets/pdf/cmaps/CNS2-H.bcmap +0 -0
  351. package/dist/web/assets/pdf/cmaps/CNS2-V.bcmap +3 -0
  352. package/dist/web/assets/pdf/cmaps/ETHK-B5-H.bcmap +0 -0
  353. package/dist/web/assets/pdf/cmaps/ETHK-B5-V.bcmap +0 -0
  354. package/dist/web/assets/pdf/cmaps/ETen-B5-H.bcmap +0 -0
  355. package/dist/web/assets/pdf/cmaps/ETen-B5-V.bcmap +0 -0
  356. package/dist/web/assets/pdf/cmaps/ETenms-B5-H.bcmap +3 -0
  357. package/dist/web/assets/pdf/cmaps/ETenms-B5-V.bcmap +0 -0
  358. package/dist/web/assets/pdf/cmaps/EUC-H.bcmap +0 -0
  359. package/dist/web/assets/pdf/cmaps/EUC-V.bcmap +0 -0
  360. package/dist/web/assets/pdf/cmaps/Ext-H.bcmap +0 -0
  361. package/dist/web/assets/pdf/cmaps/Ext-RKSJ-H.bcmap +0 -0
  362. package/dist/web/assets/pdf/cmaps/Ext-RKSJ-V.bcmap +0 -0
  363. package/dist/web/assets/pdf/cmaps/Ext-V.bcmap +0 -0
  364. package/dist/web/assets/pdf/cmaps/GB-EUC-H.bcmap +0 -0
  365. package/dist/web/assets/pdf/cmaps/GB-EUC-V.bcmap +0 -0
  366. package/dist/web/assets/pdf/cmaps/GB-H.bcmap +4 -0
  367. package/dist/web/assets/pdf/cmaps/GB-V.bcmap +0 -0
  368. package/dist/web/assets/pdf/cmaps/GBK-EUC-H.bcmap +0 -0
  369. package/dist/web/assets/pdf/cmaps/GBK-EUC-V.bcmap +0 -0
  370. package/dist/web/assets/pdf/cmaps/GBK2K-H.bcmap +0 -0
  371. package/dist/web/assets/pdf/cmaps/GBK2K-V.bcmap +0 -0
  372. package/dist/web/assets/pdf/cmaps/GBKp-EUC-H.bcmap +0 -0
  373. package/dist/web/assets/pdf/cmaps/GBKp-EUC-V.bcmap +0 -0
  374. package/dist/web/assets/pdf/cmaps/GBT-EUC-H.bcmap +0 -0
  375. package/dist/web/assets/pdf/cmaps/GBT-EUC-V.bcmap +0 -0
  376. package/dist/web/assets/pdf/cmaps/GBT-H.bcmap +0 -0
  377. package/dist/web/assets/pdf/cmaps/GBT-V.bcmap +0 -0
  378. package/dist/web/assets/pdf/cmaps/GBTpc-EUC-H.bcmap +0 -0
  379. package/dist/web/assets/pdf/cmaps/GBTpc-EUC-V.bcmap +0 -0
  380. package/dist/web/assets/pdf/cmaps/GBpc-EUC-H.bcmap +0 -0
  381. package/dist/web/assets/pdf/cmaps/GBpc-EUC-V.bcmap +0 -0
  382. package/dist/web/assets/pdf/cmaps/H.bcmap +0 -0
  383. package/dist/web/assets/pdf/cmaps/HKdla-B5-H.bcmap +0 -0
  384. package/dist/web/assets/pdf/cmaps/HKdla-B5-V.bcmap +0 -0
  385. package/dist/web/assets/pdf/cmaps/HKdlb-B5-H.bcmap +0 -0
  386. package/dist/web/assets/pdf/cmaps/HKdlb-B5-V.bcmap +0 -0
  387. package/dist/web/assets/pdf/cmaps/HKgccs-B5-H.bcmap +0 -0
  388. package/dist/web/assets/pdf/cmaps/HKgccs-B5-V.bcmap +0 -0
  389. package/dist/web/assets/pdf/cmaps/HKm314-B5-H.bcmap +0 -0
  390. package/dist/web/assets/pdf/cmaps/HKm314-B5-V.bcmap +0 -0
  391. package/dist/web/assets/pdf/cmaps/HKm471-B5-H.bcmap +0 -0
  392. package/dist/web/assets/pdf/cmaps/HKm471-B5-V.bcmap +0 -0
  393. package/dist/web/assets/pdf/cmaps/HKscs-B5-H.bcmap +0 -0
  394. package/dist/web/assets/pdf/cmaps/HKscs-B5-V.bcmap +0 -0
  395. package/dist/web/assets/pdf/cmaps/Hankaku.bcmap +0 -0
  396. package/dist/web/assets/pdf/cmaps/Hiragana.bcmap +0 -0
  397. package/dist/web/assets/pdf/cmaps/KSC-EUC-H.bcmap +0 -0
  398. package/dist/web/assets/pdf/cmaps/KSC-EUC-V.bcmap +0 -0
  399. package/dist/web/assets/pdf/cmaps/KSC-H.bcmap +0 -0
  400. package/dist/web/assets/pdf/cmaps/KSC-Johab-H.bcmap +0 -0
  401. package/dist/web/assets/pdf/cmaps/KSC-Johab-V.bcmap +0 -0
  402. package/dist/web/assets/pdf/cmaps/KSC-V.bcmap +0 -0
  403. package/dist/web/assets/pdf/cmaps/KSCms-UHC-H.bcmap +0 -0
  404. package/dist/web/assets/pdf/cmaps/KSCms-UHC-HW-H.bcmap +0 -0
  405. package/dist/web/assets/pdf/cmaps/KSCms-UHC-HW-V.bcmap +0 -0
  406. package/dist/web/assets/pdf/cmaps/KSCms-UHC-V.bcmap +0 -0
  407. package/dist/web/assets/pdf/cmaps/KSCpc-EUC-H.bcmap +0 -0
  408. package/dist/web/assets/pdf/cmaps/KSCpc-EUC-V.bcmap +0 -0
  409. package/dist/web/assets/pdf/cmaps/Katakana.bcmap +0 -0
  410. package/dist/web/assets/pdf/cmaps/LICENSE +36 -0
  411. package/dist/web/assets/pdf/cmaps/NWP-H.bcmap +0 -0
  412. package/dist/web/assets/pdf/cmaps/NWP-V.bcmap +0 -0
  413. package/dist/web/assets/pdf/cmaps/RKSJ-H.bcmap +0 -0
  414. package/dist/web/assets/pdf/cmaps/RKSJ-V.bcmap +0 -0
  415. package/dist/web/assets/pdf/cmaps/Roman.bcmap +0 -0
  416. package/dist/web/assets/pdf/cmaps/UniCNS-UCS2-H.bcmap +0 -0
  417. package/dist/web/assets/pdf/cmaps/UniCNS-UCS2-V.bcmap +0 -0
  418. package/dist/web/assets/pdf/cmaps/UniCNS-UTF16-H.bcmap +0 -0
  419. package/dist/web/assets/pdf/cmaps/UniCNS-UTF16-V.bcmap +0 -0
  420. package/dist/web/assets/pdf/cmaps/UniCNS-UTF32-H.bcmap +0 -0
  421. package/dist/web/assets/pdf/cmaps/UniCNS-UTF32-V.bcmap +0 -0
  422. package/dist/web/assets/pdf/cmaps/UniCNS-UTF8-H.bcmap +0 -0
  423. package/dist/web/assets/pdf/cmaps/UniCNS-UTF8-V.bcmap +0 -0
  424. package/dist/web/assets/pdf/cmaps/UniGB-UCS2-H.bcmap +0 -0
  425. package/dist/web/assets/pdf/cmaps/UniGB-UCS2-V.bcmap +0 -0
  426. package/dist/web/assets/pdf/cmaps/UniGB-UTF16-H.bcmap +0 -0
  427. package/dist/web/assets/pdf/cmaps/UniGB-UTF16-V.bcmap +0 -0
  428. package/dist/web/assets/pdf/cmaps/UniGB-UTF32-H.bcmap +0 -0
  429. package/dist/web/assets/pdf/cmaps/UniGB-UTF32-V.bcmap +0 -0
  430. package/dist/web/assets/pdf/cmaps/UniGB-UTF8-H.bcmap +0 -0
  431. package/dist/web/assets/pdf/cmaps/UniGB-UTF8-V.bcmap +0 -0
  432. package/dist/web/assets/pdf/cmaps/UniJIS-UCS2-H.bcmap +0 -0
  433. package/dist/web/assets/pdf/cmaps/UniJIS-UCS2-HW-H.bcmap +0 -0
  434. package/dist/web/assets/pdf/cmaps/UniJIS-UCS2-HW-V.bcmap +0 -0
  435. package/dist/web/assets/pdf/cmaps/UniJIS-UCS2-V.bcmap +0 -0
  436. package/dist/web/assets/pdf/cmaps/UniJIS-UTF16-H.bcmap +0 -0
  437. package/dist/web/assets/pdf/cmaps/UniJIS-UTF16-V.bcmap +0 -0
  438. package/dist/web/assets/pdf/cmaps/UniJIS-UTF32-H.bcmap +0 -0
  439. package/dist/web/assets/pdf/cmaps/UniJIS-UTF32-V.bcmap +0 -0
  440. package/dist/web/assets/pdf/cmaps/UniJIS-UTF8-H.bcmap +0 -0
  441. package/dist/web/assets/pdf/cmaps/UniJIS-UTF8-V.bcmap +0 -0
  442. package/dist/web/assets/pdf/cmaps/UniJIS2004-UTF16-H.bcmap +0 -0
  443. package/dist/web/assets/pdf/cmaps/UniJIS2004-UTF16-V.bcmap +0 -0
  444. package/dist/web/assets/pdf/cmaps/UniJIS2004-UTF32-H.bcmap +0 -0
  445. package/dist/web/assets/pdf/cmaps/UniJIS2004-UTF32-V.bcmap +0 -0
  446. package/dist/web/assets/pdf/cmaps/UniJIS2004-UTF8-H.bcmap +0 -0
  447. package/dist/web/assets/pdf/cmaps/UniJIS2004-UTF8-V.bcmap +0 -0
  448. package/dist/web/assets/pdf/cmaps/UniJISPro-UCS2-HW-V.bcmap +0 -0
  449. package/dist/web/assets/pdf/cmaps/UniJISPro-UCS2-V.bcmap +0 -0
  450. package/dist/web/assets/pdf/cmaps/UniJISPro-UTF8-V.bcmap +0 -0
  451. package/dist/web/assets/pdf/cmaps/UniJISX0213-UTF32-H.bcmap +0 -0
  452. package/dist/web/assets/pdf/cmaps/UniJISX0213-UTF32-V.bcmap +0 -0
  453. package/dist/web/assets/pdf/cmaps/UniJISX02132004-UTF32-H.bcmap +0 -0
  454. package/dist/web/assets/pdf/cmaps/UniJISX02132004-UTF32-V.bcmap +0 -0
  455. package/dist/web/assets/pdf/cmaps/UniKS-UCS2-H.bcmap +0 -0
  456. package/dist/web/assets/pdf/cmaps/UniKS-UCS2-V.bcmap +0 -0
  457. package/dist/web/assets/pdf/cmaps/UniKS-UTF16-H.bcmap +0 -0
  458. package/dist/web/assets/pdf/cmaps/UniKS-UTF16-V.bcmap +0 -0
  459. package/dist/web/assets/pdf/cmaps/UniKS-UTF32-H.bcmap +0 -0
  460. package/dist/web/assets/pdf/cmaps/UniKS-UTF32-V.bcmap +0 -0
  461. package/dist/web/assets/pdf/cmaps/UniKS-UTF8-H.bcmap +0 -0
  462. package/dist/web/assets/pdf/cmaps/UniKS-UTF8-V.bcmap +0 -0
  463. package/dist/web/assets/pdf/cmaps/V.bcmap +0 -0
  464. package/dist/web/assets/pdf/cmaps/WP-Symbol.bcmap +0 -0
  465. package/dist/web/assets/pdf/standard_fonts/FoxitDingbats.pfb +0 -0
  466. package/dist/web/assets/pdf/standard_fonts/FoxitFixed.pfb +0 -0
  467. package/dist/web/assets/pdf/standard_fonts/FoxitFixedBold.pfb +0 -0
  468. package/dist/web/assets/pdf/standard_fonts/FoxitFixedBoldItalic.pfb +0 -0
  469. package/dist/web/assets/pdf/standard_fonts/FoxitFixedItalic.pfb +0 -0
  470. package/dist/web/assets/pdf/standard_fonts/FoxitSerif.pfb +0 -0
  471. package/dist/web/assets/pdf/standard_fonts/FoxitSerifBold.pfb +0 -0
  472. package/dist/web/assets/pdf/standard_fonts/FoxitSerifBoldItalic.pfb +0 -0
  473. package/dist/web/assets/pdf/standard_fonts/FoxitSerifItalic.pfb +0 -0
  474. package/dist/web/assets/pdf/standard_fonts/FoxitSymbol.pfb +0 -0
  475. package/dist/web/assets/pdf/standard_fonts/LICENSE_FOXIT +27 -0
  476. package/dist/web/assets/pdf/standard_fonts/LICENSE_LIBERATION +363 -0
  477. package/dist/web/assets/pdf/standard_fonts/LiberationSans-Bold.ttf +0 -0
  478. package/dist/web/assets/pdf/standard_fonts/LiberationSans-BoldItalic.ttf +0 -0
  479. package/dist/web/assets/pdf/standard_fonts/LiberationSans-Italic.ttf +0 -0
  480. package/dist/web/assets/pdf/standard_fonts/LiberationSans-Regular.ttf +0 -0
  481. package/dist/web/assets/pdf/wasm/LICENSE_JBIG2 +196 -0
  482. package/dist/web/assets/pdf/wasm/LICENSE_OPENJPEG +39 -0
  483. package/dist/web/assets/pdf/wasm/LICENSE_PDFJS_JBIG2 +13 -0
  484. package/dist/web/assets/pdf/wasm/LICENSE_PDFJS_OPENJPEG +22 -0
  485. package/dist/web/assets/pdf/wasm/LICENSE_PDFJS_QCMS +19 -0
  486. package/dist/web/assets/pdf/wasm/LICENSE_QCMS +21 -0
  487. package/dist/web/assets/pdf/wasm/jbig2.wasm +0 -0
  488. package/dist/web/assets/pdf/wasm/jbig2_nowasm_fallback.js +15 -0
  489. package/dist/web/assets/pdf/wasm/openjpeg.wasm +0 -0
  490. package/dist/web/assets/pdf/wasm/openjpeg_nowasm_fallback.js +17 -0
  491. package/dist/web/assets/pdf/wasm/qcms_bg.wasm +0 -0
  492. package/dist/web/assets/pdf/wasm/quickjs-eval.js +16 -0
  493. package/dist/web/assets/pdf/wasm/quickjs-eval.wasm +0 -0
  494. package/dist/web/assets/pdf.worker.min.mjs +29 -0
  495. package/dist/web/assets/workspace.worker.js +409 -0
  496. package/dist/web/document-source.js +14 -0
  497. package/dist/web/hosted-files.js +189 -0
  498. package/dist/web/hosted.js +316 -0
  499. package/dist/web/node-runner.js +26 -0
  500. package/dist/web/preview.js +74 -0
  501. package/dist/web/server.js +85 -240
  502. package/dist/web/sync.js +364 -70
  503. package/dist/web/table-editor-script.js +99 -0
  504. package/dist/web/table-format.js +224 -0
  505. package/dist/web/views/help.js +15 -10
  506. package/dist/web/views/sync-panel.js +90 -19
  507. package/dist/web/views/token-guide.js +27 -0
  508. package/dist/web/views/usage-guide.js +12 -0
  509. package/dist/web/views/workspace.js +85 -15
  510. package/dist/web/workspace-api.js +371 -0
  511. package/docs/user/CLI.md +366 -0
  512. package/docs/user/CONFIGURATION.md +125 -0
  513. package/docs/user/OBSIDIAN.md +117 -0
  514. package/docs/user/WEB.md +90 -0
  515. package/docs/user/images/web/document-preview.png +0 -0
  516. package/docs/user/images/web/remote-diff.png +0 -0
  517. package/docs/user/images/web/sync-workspace.png +0 -0
  518. package/package.json +23 -6
@@ -0,0 +1,287 @@
1
+ import { progressStep } from '../shared/progress.js';
2
+ /** Read-only, batched remote observation. Never advances the synchronization baseline. */
3
+ import { bold, cyan, green, yellow, red, dim } from '../shared/colors.js';
4
+ import { createHash, randomUUID } from 'node:crypto';
5
+ import { existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, renameSync, rmSync, writeFileSync } from 'node:fs';
6
+ import { dirname, join, resolve } from 'node:path';
7
+ import { readEnv } from '../confluence/config.js';
8
+ import { buildFolderIndex, parentKeyOf } from '../documents/catalog.js';
9
+ import { buildIgnorer } from './ignore.js';
10
+ import { loadMapping } from './mapping.js';
11
+ import { mappingPath, METADATA_DIR } from './metadata.js';
12
+ import { scopeClient } from './scope.js';
13
+ import { assertWorkspaceConnection, inNestedWorkspace, inScope, loadWorkspace, requireScope, resolveSyncBase, within } from './workspace.js';
14
+ export const FETCH_TTL_MS = 120_000;
15
+ const BATCH_SIZE = 100, MAX_REQUESTS = 20;
16
+ const states = new Set(['unchanged', 'changed', 'unavailable', 'unknown', 'new']);
17
+ const digest = (value) => createHash('sha256').update(JSON.stringify(value)).digest('hex');
18
+ function cachePath(base) {
19
+ mappingPath(base); // Validates the metadata directory, including symlinks.
20
+ const path = join(base, METADATA_DIR, 'remote.json');
21
+ if (existsSync(path) && (!lstatSync(path).isFile() || lstatSync(path).isSymbolicLink()))
22
+ throw new Error('원격 조회 캐시는 일반 파일이어야 합니다.');
23
+ return path;
24
+ }
25
+ function fetchContext(baseInput, env, options = {}) {
26
+ const base = realpathSync.native?.(baseInput) ?? realpathSync(baseInput), workspace = loadWorkspace(base), root = requireScope(workspace);
27
+ assertWorkspaceConnection(workspace, env);
28
+ const mapping = loadMapping(options.mappingFile ?? mappingPath(base)), ignore = buildIgnorer(base, []);
29
+ const scoped = Object.entries(mapping).filter(([path]) => inScope(path.replace(/\/$/, ''), workspace) && !ignore.ignores(path) && !inNestedWorkspace(base, path));
30
+ const pages = scoped.filter(([path, entry]) => entry.type !== 'folder' && /\.md$/i.test(path)).sort(([a], [b]) => a.localeCompare(b));
31
+ if (pages.some(([, entry]) => !/^\d+$/.test(entry.pageId)))
32
+ throw new Error('매핑의 페이지 ID가 숫자가 아닙니다. 매핑을 확인하세요.');
33
+ const folderIndex = buildFolderIndex(Object.keys(mapping).filter(path => /\.md$/i.test(path)));
34
+ const expectedParents = new Map(pages.map(([path]) => { const parent = parentKeyOf(path, folderIndex); return [path, path === 'README.md' && mapping[path].pageId === root.id ? undefined : mapping[path].remoteParentId ?? (parent ? mapping[parent]?.pageId : root.id)]; }));
35
+ const key = digest(['discovery-v2', base, workspace, env.baseUrl, env.spaceKey, digest([env.email, env.token]), pages, [...expectedParents], scoped.filter(([, e]) => e.type === 'folder')]);
36
+ const binding = digest([base, workspace, env.baseUrl, env.spaceKey, digest([env.email, env.token])]);
37
+ const path = cachePath(base), now = Date.now();
38
+ return { base, workspace, root, mapping, pages, scoped, expectedParents, key, path, now, binding };
39
+ }
40
+ function cachedSnapshot(context, allowExpired = false) {
41
+ try {
42
+ const value = JSON.parse(readFileSync(context.path, 'utf8'));
43
+ const age = context.now - Date.parse(value?.checkedAt);
44
+ if (value?.version !== 1 || value.key !== context.key || age < 0 || !Number.isFinite(age) || !allowExpired && age >= FETCH_TTL_MS || !Array.isArray(value.items))
45
+ return null;
46
+ if (value.folders !== undefined && (!Array.isArray(value.folders) || value.folders.some(folder => !folder || typeof folder.path !== 'string' || !folder.path.endsWith('/') || !/^\d+$/.test(folder.pageId) || typeof folder.title !== 'string' || !within(context.base, resolve(context.base, folder.path)))))
47
+ return null;
48
+ const ids = new Set();
49
+ for (const item of value.items) {
50
+ if (!item || typeof item.path !== 'string' || !within(context.base, resolve(context.base, item.path)) || !/\.md$/i.test(item.path) || !/^\d+$/.test(item.pageId) || ids.has(item.pageId) || !states.has(item.state) || !Array.isArray(item.reasons) || item.reasons.some(r => typeof r !== 'string'))
51
+ return null;
52
+ ids.add(item.pageId);
53
+ }
54
+ if (context.pages.some(([, entry]) => !ids.has(entry.pageId)))
55
+ return null;
56
+ return value;
57
+ }
58
+ catch (error) {
59
+ if (error.code === 'ENOENT' || error instanceof SyntaxError)
60
+ return null;
61
+ throw error;
62
+ }
63
+ }
64
+ /** Reclassify saved metadata against current baselines without API calls. */
65
+ export function readFetchSnapshot(base, env) {
66
+ const context = fetchContext(base, env), strict = cachedSnapshot(context, true);
67
+ if (strict)
68
+ return strict;
69
+ let prior;
70
+ try {
71
+ prior = JSON.parse(readFileSync(context.path, 'utf8'));
72
+ }
73
+ catch (error) {
74
+ if (error.code === 'ENOENT' || error instanceof SyntaxError)
75
+ return null;
76
+ throw error;
77
+ }
78
+ if (prior?.binding !== context.binding || !cachedSnapshot({ ...context, key: prior.key }, true))
79
+ return null;
80
+ const byId = new Map(context.pages.map(([path, entry]) => [entry.pageId, { path, entry }]));
81
+ return { ...prior, key: context.key, items: prior.items.map(item => {
82
+ const mapped = byId.get(item.pageId);
83
+ if (!mapped)
84
+ return item;
85
+ const { path, entry } = mapped, reasons = [];
86
+ if (item.state === 'unavailable')
87
+ return { ...item, path, baselineVersion: entry.remoteVersion };
88
+ if (!entry.remoteVersion || !item.remoteVersion || item.remoteVersion < entry.remoteVersion)
89
+ return { ...item, path, baselineVersion: entry.remoteVersion, state: 'unknown', reasons: ['원격 버전 기준을 확인할 수 없습니다. Fetch로 갱신하세요.'] };
90
+ if (item.remoteVersion !== entry.remoteVersion)
91
+ reasons.push(`버전 v${entry.remoteVersion} → v${item.remoteVersion}`);
92
+ if (entry.title && item.title && entry.title !== item.title)
93
+ reasons.push('제목 변경');
94
+ const parent = context.expectedParents.get(path);
95
+ if (parent && item.parentId && parent !== item.parentId)
96
+ reasons.push('원격 부모 위치 변경');
97
+ return { ...item, path, baselineVersion: entry.remoteVersion, state: reasons.length ? 'changed' : 'unchanged', reasons };
98
+ }) };
99
+ }
100
+ export async function fetchRemote(baseInput, env, options = {}) {
101
+ const context = fetchContext(baseInput, env, options);
102
+ const { base, workspace, pages, scoped, key, path, now, expectedParents, binding } = context;
103
+ const cached = options.refresh ? null : cachedSnapshot(context);
104
+ if (cached)
105
+ return { snapshot: cached, cached: true, requests: 0 };
106
+ const client = scopeClient({ ...env, baseUrl: env.baseUrl?.replace(/\/$/, '') });
107
+ const remote = new Map();
108
+ let requests = 0;
109
+ const query = new URLSearchParams({ 'space-id': workspace.connection.spaceId, status: 'current', limit: String(BATCH_SIZE) });
110
+ let cursor = '', seen = new Set();
111
+ do {
112
+ if (requests >= MAX_REQUESTS)
113
+ throw new Error('원격 조회가 최대 20회에 도달했습니다. 기존 캐시는 유지합니다.');
114
+ progressStep(`스페이스 원격 메타데이터 확인 · ${remote.size}개 발견`);
115
+ const params = new URLSearchParams(query);
116
+ if (cursor)
117
+ params.set('cursor', cursor);
118
+ let link = '';
119
+ requests++;
120
+ const data = await client.api('/api/v2/pages?' + params, undefined, headers => { link = headers.get('link') ?? ''; });
121
+ if (!data || !Array.isArray(data.results))
122
+ throw new Error('원격 페이지 목록 응답이 올바르지 않습니다.');
123
+ for (const page of data.results) {
124
+ if (!page || !/^\d+$/.test(String(page.id)) || page.parentId != null && !/^\d+$/.test(String(page.parentId)) || String(page.spaceId) !== workspace.connection.spaceId || remote.has(String(page.id)))
125
+ throw new Error('원격 페이지 목록의 ID·스페이스 또는 중복 응답을 확인하세요.');
126
+ remote.set(String(page.id), page);
127
+ }
128
+ const next = data._links?.next || link.split(',').find(part => /rel\s*=\s*"?next"?/i.test(part))?.match(/<([^>]+)>/)?.[1];
129
+ cursor = '';
130
+ if (next) {
131
+ const url = new URL(next, env.baseUrl.replace(/\/$/, '') + '/api/v2/pages');
132
+ if (url.origin !== new URL(env.baseUrl).origin || !/\/api\/v2\/pages$/.test(url.pathname) || !url.searchParams.get('cursor'))
133
+ throw new Error('원격 페이지 목록의 다음 페이지 주소가 올바르지 않습니다.');
134
+ cursor = url.searchParams.get('cursor');
135
+ if (seen.has(cursor))
136
+ throw new Error('원격 페이지 목록의 페이지네이션이 반복됩니다.');
137
+ seen.add(cursor);
138
+ }
139
+ } while (cursor);
140
+ const items = pages.map(([path, entry]) => {
141
+ const page = remote.get(entry.pageId), item = { path, pageId: entry.pageId, baselineVersion: entry.remoteVersion, state: 'unknown', reasons: [] };
142
+ if (!page)
143
+ return { ...item, state: 'unavailable', reasons: ['조회 결과에 없음 — 삭제·권한 변경·보관 여부는 확인되지 않았습니다.'] };
144
+ if (typeof page.title === 'string')
145
+ item.title = page.title;
146
+ if (typeof page.parentId === 'string')
147
+ item.parentId = page.parentId;
148
+ if (Number.isInteger(page.version?.number) && page.version.number > 0)
149
+ item.remoteVersion = page.version.number;
150
+ if (!entry.remoteVersion || !item.remoteVersion || item.remoteVersion < entry.remoteVersion)
151
+ item.reasons.push('비교할 기준 버전 또는 원격 버전을 확인할 수 없습니다.');
152
+ else {
153
+ item.state = 'unchanged';
154
+ if (item.remoteVersion !== entry.remoteVersion)
155
+ item.reasons.push(`버전 v${entry.remoteVersion} → v${item.remoteVersion}`);
156
+ if (entry.title && item.title && entry.title !== item.title)
157
+ item.reasons.push('제목 변경');
158
+ const parent = expectedParents.get(path);
159
+ if (parent && item.parentId && parent !== item.parentId)
160
+ item.reasons.push('원격 부모 위치 변경');
161
+ if (item.reasons.length)
162
+ item.state = 'changed';
163
+ }
164
+ return item;
165
+ });
166
+ // Batch only unresolved parents of unmapped pages; expanded ancestry is shared.
167
+ const mappingById = new Map(Object.entries(context.mapping).map(([path, entry]) => [entry.pageId, { path, entry }]));
168
+ const folders = new Map();
169
+ const slug = (title) => { const value = title.replace(/[\/\\:*?"<>|]/g, '-').replace(/\s+/g, ' ').trim(); return !value || /^\.+$/.test(value) ? 'untitled' : value.startsWith('.') || ['attachments', 'node_modules'].includes(value) ? '_' + value : value; };
170
+ const nodes = new Map(remote);
171
+ const missingParents = [...new Set([...remote].filter(([id]) => !mappingById.has(id)).map(([, page]) => String(page.parentId ?? '')).filter(id => id && id !== context.root.id && !nodes.has(id) && !mappingById.has(id)))];
172
+ for (let offset = 0; offset < missingParents.length; offset += BATCH_SIZE) {
173
+ const ids = missingParents.slice(offset, offset + BATCH_SIZE), remaining = new Set(ids), seenStarts = new Set();
174
+ let start = '';
175
+ do {
176
+ if (requests >= MAX_REQUESTS)
177
+ throw new Error('원격 조회가 최대 20회에 도달했습니다. 기존 캐시는 유지합니다.');
178
+ const params = new URLSearchParams({ cql: `id in (${ids.join(',')})`, expand: 'ancestors,space', limit: String(BATCH_SIZE) });
179
+ if (start)
180
+ params.set('start', start);
181
+ requests++;
182
+ progressStep(`신규 문서의 부모 폴더 메타데이터 조회 · ${offset + 1}–${offset + ids.length}`);
183
+ const data = await client.api('/rest/api/content/search?' + params);
184
+ if (!Array.isArray(data?.results))
185
+ throw new Error('원격 부모 폴더 목록을 확인할 수 없습니다.');
186
+ for (const info of data.results) {
187
+ const id = String(info?.id);
188
+ if (!remaining.has(id) || info.type !== 'folder' || String(info.space?.id) !== workspace.connection.spaceId || !Array.isArray(info.ancestors))
189
+ throw new Error('원격 부모 폴더의 ID·스페이스·유형을 확인하세요.');
190
+ remaining.delete(id);
191
+ for (let i = 0; i < info.ancestors.length; i++) {
192
+ const a = info.ancestors[i];
193
+ if (!/^\d+$/.test(String(a.id)) || typeof a.title !== 'string')
194
+ throw new Error('원격 상위 폴더 정보를 확인할 수 없습니다.');
195
+ if (!nodes.has(String(a.id)))
196
+ nodes.set(String(a.id), { id: String(a.id), title: a.title, type: 'folder', parentId: info.ancestors[i - 1]?.id });
197
+ }
198
+ nodes.set(id, { id, title: info.title, type: 'folder', parentId: info.ancestors.at(-1)?.id });
199
+ }
200
+ start = '';
201
+ if (data._links?.next) {
202
+ const url = new URL(data._links.next, env.baseUrl);
203
+ if (url.origin !== new URL(env.baseUrl).origin || !/\/rest\/api\/content\/search$/.test(url.pathname) || !/^\d+$/.test(url.searchParams.get('start') ?? ''))
204
+ throw new Error('부모 폴더 목록의 다음 페이지 주소가 올바르지 않습니다.');
205
+ start = url.searchParams.get('start');
206
+ if (seenStarts.has(start))
207
+ throw new Error('부모 폴더 페이지네이션이 반복됩니다.');
208
+ seenStarts.add(start);
209
+ }
210
+ } while (start);
211
+ if (remaining.size)
212
+ throw new Error('신규 원격 문서의 부모 폴더를 찾을 수 없습니다. 기존 캐시는 유지합니다.');
213
+ }
214
+ async function container(id, visiting = new Set()) {
215
+ if (id === context.root.id)
216
+ return '';
217
+ if (visiting.has(id))
218
+ throw new Error('원격 부모 관계가 순환합니다.');
219
+ visiting.add(id);
220
+ const mapped = mappingById.get(id);
221
+ if (mapped)
222
+ return mapped.entry.type === 'folder' ? mapped.path.replace(/\/$/, '') : dirname(mapped.path) === '.' ? '' : dirname(mapped.path);
223
+ const node = nodes.get(id);
224
+ if (!node)
225
+ throw new Error(`원격 부모 정보를 확인할 수 없습니다: #${id}`);
226
+ if (typeof node.title !== 'string' || !node.title.trim())
227
+ throw new Error(`원격 제목을 확인할 수 없습니다: #${id}`);
228
+ const parent = node.parentId ? await container(String(node.parentId), visiting) : '';
229
+ const path = [parent, slug(node.title)].filter(Boolean).join('/');
230
+ if (!remote.has(id))
231
+ folders.set(id, { path: path + '/', pageId: id, title: node.title });
232
+ return path;
233
+ }
234
+ const mappedIds = new Set(Object.values(context.mapping).map(entry => entry.pageId));
235
+ for (const [id, page] of remote)
236
+ if (!mappedIds.has(id) && page.parentId)
237
+ await container(String(page.parentId));
238
+ for (const [id, page] of remote) {
239
+ if (mappedIds.has(id))
240
+ continue;
241
+ if (typeof page.title !== 'string' || !Number.isInteger(page.version?.number) || page.version.number < 1)
242
+ throw new Error(`신규 원격 문서의 제목·버전을 확인할 수 없습니다: #${id}`);
243
+ const parent = page.parentId ? await container(String(page.parentId)) : '';
244
+ const hasChildren = [...nodes.values()].some(node => String(node.parentId) === id);
245
+ const path = id === context.root.id ? 'README.md' : [parent, slug(page.title) + (hasChildren ? '/README.md' : '.md')].filter(Boolean).join('/');
246
+ if (!inScope(path, workspace) || buildIgnorer(base, []).ignores(path) || inNestedWorkspace(base, path))
247
+ continue;
248
+ items.push({ path, pageId: id, title: page.title, parentId: page.parentId, remoteVersion: page.version.number, state: 'new', reasons: ['아직 로컬에 받지 않은 원격 문서'] });
249
+ }
250
+ const snapshot = { version: 1, key, binding, checkedAt: new Date(now).toISOString(), scope: '.', requests, items, folders: [...folders.values()], foldersSkipped: scoped.filter(([, e]) => e.type === 'folder').length };
251
+ // Publish only a complete observation. Never replace a good cache with partial results.
252
+ cachePath(base);
253
+ mkdirSync(dirname(path), { recursive: true });
254
+ const temp = join(dirname(path), `.remote-${randomUUID()}.tmp`);
255
+ try {
256
+ writeFileSync(temp, JSON.stringify(snapshot, null, 2) + '\n', { mode: 0o600, flag: 'wx' });
257
+ renameSync(temp, path);
258
+ }
259
+ finally {
260
+ rmSync(temp, { force: true });
261
+ }
262
+ return { snapshot, cached: false, requests };
263
+ }
264
+ export async function runFetch(argv) {
265
+ for (let i = 0; i < argv.length; i++) {
266
+ if (argv[i] === '--base') {
267
+ if (!argv[i + 1] || argv[i + 1].startsWith('-'))
268
+ throw new Error('--base에 기준 폴더를 지정하세요.');
269
+ i++;
270
+ }
271
+ else if (!['fetch', '--no-color', '--refresh'].includes(argv[i]))
272
+ throw new Error('fetch는 연결된 작업공간을 사용합니다. 옵션은 --base와 --refresh만 지정하세요.');
273
+ }
274
+ const i = argv.indexOf('--base'), base = resolveSyncBase(i >= 0 ? argv[i + 1] : undefined);
275
+ if (!base)
276
+ throw new Error('작업공간이 없습니다. --base로 기준 폴더를 지정하세요.');
277
+ const { snapshot, cached, requests } = await fetchRemote(base, readEnv(base), { refresh: argv.includes('--refresh') });
278
+ const counts = Object.fromEntries([...states].map(state => [state, snapshot.items.filter(item => item.state === state).length]));
279
+ console.log(`${bold(cyan('원격 변경 확인 · 스페이스 작업공간'))}\n${dim(`조회 시각: ${snapshot.checkedAt}${cached ? ' · 2분 이내 캐시 사용' : ''}`)}\n이번 API 요청: ${cyan(`${requests}회`)} · 대상 ${snapshot.items.length}건`);
280
+ const tones = { new: cyan, changed: cyan, unavailable: red, unknown: yellow, unchanged: green };
281
+ for (const item of snapshot.items.filter(item => item.state !== 'unchanged'))
282
+ console.log(` ${tones[item.state](`[${({ new: '원격 신규', changed: '원격 변경', unavailable: '조회 불가', unknown: '확인 필요', unchanged: '버전 동일' })[item.state]}]`)} ${item.path} ${dim(`(#${item.pageId})`)} · ${item.reasons.join(', ')}`);
283
+ console.log(`${green(`버전 동일 ${counts.unchanged}`)} · ${cyan(`원격 신규 ${counts.new}`)} · ${cyan(`원격 변경 ${counts.changed}`)} · ${counts.unavailable ? red(`조회 불가 ${counts.unavailable}`) : dim('조회 불가 0')} · ${counts.unknown ? yellow(`확인 필요 ${counts.unknown}`) : dim('확인 필요 0')}`);
284
+ if (!snapshot.items.length)
285
+ console.log(dim('스페이스에서 가져올 문서가 없습니다.'));
286
+ console.log(dim('문서·동기화 기준은 유지합니다. 스페이스의 새 문서와 버전·위치 변경을 확인했습니다. 가져오기: csync pull'));
287
+ }
@@ -1,15 +1,15 @@
1
1
  /**
2
- * 동기화 제외 규칙. base 루트의 .confluence-syncignore(.gitignore 방식) + --exclude 옵션.
2
+ * 동기화 제외 규칙. base 루트의 .csync/ignore(구 .confluence-syncignore)(.gitignore 방식) + --exclude 옵션.
3
3
  * 매칭은 npm `ignore`(gitignore 스펙) 사용. 경로는 base 기준 상대(POSIX).
4
4
  */
5
5
  import { readFileSync, existsSync } from 'node:fs';
6
- import { join } from 'node:path';
6
+ import { ignorePath } from './metadata.js';
7
7
  import ignore from 'ignore';
8
- export const IGNORE_FILE = '.confluence-syncignore';
8
+ export { IGNORE_FILE } from './metadata.js';
9
9
  export function buildIgnorer(baseDir, excludes) {
10
10
  const ig = ignore();
11
11
  let active = false;
12
- const file = join(baseDir, IGNORE_FILE);
12
+ const file = ignorePath(baseDir);
13
13
  if (existsSync(file)) {
14
14
  ig.add(readFileSync(file, 'utf8'));
15
15
  active = true;
@@ -0,0 +1,12 @@
1
+ /** Local edit detection is independent of rendered storage and incoming anchor links. */
2
+ import { createHash } from 'node:crypto';
3
+ import { readFileSync } from 'node:fs';
4
+ import { resolve } from 'node:path';
5
+ export function localStateHash(base, rel, rendered) {
6
+ const hash = createHash('sha256').update(readFileSync(resolve(base, rel)));
7
+ for (const asset of [...rendered.attachments].sort((a, b) => a.filename.localeCompare(b.filename))) {
8
+ hash.update('\0' + asset.filename + '\0');
9
+ hash.update(createHash('sha256').update(readFileSync(asset.abs)).digest());
10
+ }
11
+ return hash.digest('hex');
12
+ }
@@ -1,18 +1,41 @@
1
1
  /**
2
2
  * 매핑(파일 경로 ↔ pageId ↔ hash) 영속화.
3
- * 기본 위치는 <base>/.confluence-sync.json 이며 문서셋과 함께 버전관리된다.
3
+ * 기본 위치는 <base>/.csync/mapping.json 이며 문서셋과 함께 버전관리된다.
4
4
  */
5
- import { readFileSync, writeFileSync } from 'node:fs';
6
- /** base 루트에 놓이는 매핑 파일 이름. 이 파일이 있는 곳이 곧 동기화 루트다. */
7
- export const MAPPING_FILE = '.confluence-sync.json';
5
+ import { readFileSync, writeFileSync, mkdirSync, renameSync, rmSync } from 'node:fs';
6
+ import { randomUUID } from 'node:crypto';
7
+ import { dirname } from 'node:path';
8
+ export { MAPPING_FILE } from './metadata.js';
8
9
  export function loadMapping(path) {
10
+ let data;
9
11
  try {
10
- return JSON.parse(readFileSync(path, 'utf8'));
12
+ data = JSON.parse(readFileSync(path, 'utf8'));
11
13
  }
12
- catch {
13
- return {};
14
+ catch (error) {
15
+ if (error.code === 'ENOENT')
16
+ return {};
17
+ throw new Error(`매핑 파일을 읽을 수 없습니다: ${path}`);
14
18
  }
19
+ if (!data || typeof data !== 'object' || Array.isArray(data) || Object.values(data).some(entry => !entry || typeof entry.pageId !== 'string' || !entry.pageId ||
20
+ (entry.sourceObject !== undefined && (typeof entry.sourceObject !== 'string' || !/^[a-f0-9]{64}$/.test(entry.sourceObject))) ||
21
+ (entry.localHash !== undefined && (typeof entry.localHash !== 'string' || !/^[a-f0-9]{64}$/.test(entry.localHash))) ||
22
+ (entry.remoteParentId !== undefined && (typeof entry.remoteParentId !== 'string' || !entry.remoteParentId)) ||
23
+ (entry.remoteVersion !== undefined && (!Number.isInteger(entry.remoteVersion) || entry.remoteVersion < 1)))) {
24
+ throw new Error(`잘못된 매핑 파일: ${path}`);
25
+ }
26
+ const ids = Object.values(data).map(entry => entry.pageId);
27
+ if (new Set(ids).size !== ids.length)
28
+ throw new Error(`매핑 pageId 중복: ${path}`);
29
+ return data;
15
30
  }
16
31
  export function saveMapping(path, m) {
17
- writeFileSync(path, JSON.stringify(m, null, 2) + '\n');
32
+ mkdirSync(dirname(path), { recursive: true });
33
+ const temporary = `${path}.${randomUUID()}.tmp`;
34
+ try {
35
+ writeFileSync(temporary, JSON.stringify(m, null, 2) + '\n', { flag: 'wx', mode: 0o600 });
36
+ renameSync(temporary, path);
37
+ }
38
+ finally {
39
+ rmSync(temporary, { force: true });
40
+ }
18
41
  }
@@ -0,0 +1,57 @@
1
+ /** Metadata locations shared by CLI, references and the web adapter. */
2
+ import { existsSync, readFileSync, mkdirSync, writeFileSync, unlinkSync, lstatSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ export const METADATA_DIR = '.csync';
5
+ export const MAPPING_FILE = '.csync/mapping.json';
6
+ export const WORKSPACE_FILE = '.csync/workspace.json';
7
+ export const IGNORE_FILE = '.csync/ignore';
8
+ const files = {
9
+ mapping: [MAPPING_FILE, '.confluence-sync.json'],
10
+ workspace: [WORKSPACE_FILE, '.confluence-workspace.json'],
11
+ ignore: [IGNORE_FILE, '.confluence-syncignore'],
12
+ };
13
+ export function metadataPath(base, kind) {
14
+ const directory = join(base, METADATA_DIR);
15
+ if (existsSync(directory) && (!lstatSync(directory).isDirectory() || lstatSync(directory).isSymbolicLink()))
16
+ throw new Error(`메타데이터 폴더가 올바르지 않습니다: ${directory}`);
17
+ const [current, legacy] = files[kind].map(name => join(base, name));
18
+ for (const path of [current, legacy])
19
+ if (existsSync(path) && (!lstatSync(path).isFile() || lstatSync(path).isSymbolicLink()))
20
+ throw new Error(`메타데이터 파일이 올바르지 않습니다: ${path}`);
21
+ if (existsSync(current) && existsSync(legacy) && !readFileSync(current).equals(readFileSync(legacy))) {
22
+ throw new Error(`기존·새 메타데이터가 충돌합니다: ${legacy}, ${current}. 사용할 기준을 먼저 정리하세요.`);
23
+ }
24
+ return existsSync(current) || !existsSync(legacy) ? current : legacy;
25
+ }
26
+ export const mappingPath = (base) => metadataPath(base, 'mapping');
27
+ export const workspacePath = (base) => metadataPath(base, 'workspace');
28
+ export const ignorePath = (base) => metadataPath(base, 'ignore');
29
+ export function hasMetadata(base) {
30
+ return existsSync(join(base, METADATA_DIR)) || ['mapping', 'workspace'].some(kind => existsSync(join(base, files[kind][1])));
31
+ }
32
+ /** Include absent locations too, so creating a competing legacy file invalidates a plan. */
33
+ export function metadataFiles(base) {
34
+ return Object.values(files).flatMap(names => names.map(name => join(base, name)));
35
+ }
36
+ /** Explicit, offline migration. Copy and verify every destination before removing any source. */
37
+ export function migrateMetadata(base, dryRun = false) {
38
+ const plan = Object.keys(files).map(kind => {
39
+ metadataPath(base, kind); // Validate every pair before any mutation.
40
+ const [next, prior] = files[kind];
41
+ return { source: join(base, prior), target: join(base, next) };
42
+ }).filter(item => existsSync(item.source)).map(item => ({ ...item, bytes: readFileSync(item.source) }));
43
+ for (const item of plan)
44
+ console.log(`${item.source} → ${item.target}`);
45
+ if (!dryRun && plan.length) {
46
+ mkdirSync(join(base, METADATA_DIR), { recursive: true });
47
+ for (const item of plan)
48
+ if (!existsSync(item.target))
49
+ writeFileSync(item.target, item.bytes, { flag: 'wx' });
50
+ for (const item of plan)
51
+ if (!readFileSync(item.target).equals(item.bytes) || !readFileSync(item.source).equals(item.bytes))
52
+ throw new Error('이전 중 메타데이터가 변경되었습니다. 원본은 제거하지 않았습니다.');
53
+ for (const item of plan)
54
+ unlinkSync(item.source);
55
+ }
56
+ return plan.length;
57
+ }
@@ -0,0 +1,52 @@
1
+ /** Prepare publication content without writing to the source workspace. */
2
+ import { copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
3
+ import { dirname, join, relative, resolve } from 'node:path';
4
+ import { tmpdir } from 'node:os';
5
+ import { buildVault, collectAssets } from '../documents/catalog.js';
6
+ import { prepareMarkdown } from '../conversion/markdown.js';
7
+ import { within } from './workspace.js';
8
+ import { buildTreeRenderer } from './render.js';
9
+ export async function preparePushTree(base, rels, selected, options) {
10
+ const stage = mkdtempSync(join(tmpdir(), 'csync-push-'));
11
+ const dispose = () => rmSync(stage, { recursive: true, force: true });
12
+ try {
13
+ const chosen = new Set(selected);
14
+ const sourceRenderer = buildTreeRenderer(base, rels);
15
+ const vault = buildVault(rels, collectAssets(base).map(file => relative(base, file).split('\\').join('/')));
16
+ for (const rel of rels) {
17
+ const original = resolve(base, rel), destination = resolve(stage, rel);
18
+ let text = readFileSync(original, 'utf8');
19
+ if (chosen.has(rel)) {
20
+ // Preview renders actual temporary PNGs too, so attachment hashes match Push.
21
+ const converted = await prepareMarkdown(text, original, base, vault, stage, false, undefined, false);
22
+ text = converted.text;
23
+ for (const [asset, source] of converted.copies) {
24
+ const target = resolve(stage, asset);
25
+ mkdirSync(dirname(target), { recursive: true });
26
+ copyFileSync(source, target);
27
+ }
28
+ }
29
+ // Use the publication renderer as the final asset authority: conversion may
30
+ // miss balanced parentheses in Markdown paths or assets inside raw HTML.
31
+ if (chosen.has(rel)) {
32
+ const doc = sourceRenderer.docs[rel];
33
+ for (const asset of sourceRenderer.render(rel, doc.body, doc.title).attachments) {
34
+ if (!within(base, asset.abs) || !existsSync(asset.abs))
35
+ continue;
36
+ const target = resolve(stage, relative(base, asset.abs));
37
+ if (existsSync(target))
38
+ continue;
39
+ mkdirSync(dirname(target), { recursive: true });
40
+ copyFileSync(asset.abs, target);
41
+ }
42
+ }
43
+ mkdirSync(dirname(destination), { recursive: true });
44
+ writeFileSync(destination, text);
45
+ }
46
+ return { ...buildTreeRenderer(stage, rels, options ? { ...options, referenceBase: base } : undefined), dispose };
47
+ }
48
+ catch (error) {
49
+ dispose();
50
+ throw error;
51
+ }
52
+ }