@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
package/dist/sync/push.js CHANGED
@@ -1,19 +1,31 @@
1
+ import { progressStep } from '../shared/progress.js';
2
+ import { storeBaseline } from './baseline.js';
3
+ import { buildTreeRenderer } from './render.js';
4
+ import { localStateHash } from './local-state.js';
5
+ import { loadIndex, stagedPaths, assertStaged, completeStaged } from './staging.js';
1
6
  /** Incremental Confluence push and local preview orchestration. */
7
+ import { mappingPath } from './metadata.js';
2
8
  import { positionalArgs } from '../cli/args.js';
3
- import { existsSync, statSync } from 'node:fs';
9
+ import { existsSync, statSync, readFileSync } from 'node:fs';
4
10
  import { resolve, relative } from 'node:path';
5
11
  import { readEnv, requireEnv } from '../confluence/config.js';
6
12
  import { docHash } from '../documents/markdown.js';
7
- import { buildTreeRenderer } from './render.js';
13
+ import { preparePushTree } from './prepare.js';
8
14
  import { collectMarkdown, buildFolderIndex, readDoc, parentKeyOf, containerKeyOf, sortForSync, resolveSelection, withParents, } from '../documents/catalog.js';
9
15
  import { loadMapping, saveMapping } from './mapping.js';
10
16
  import { createClient } from '../confluence/client.js';
11
17
  import { buildIgnorer } from './ignore.js';
12
- import { bold, dim, red, green, yellow, magenta, cyan, gray } from '../shared/colors.js';
18
+ import { resolveSyncBase, loadWorkspace, inScope, inNestedWorkspace, assertWorkspaceConnection, requireScope } from './workspace.js';
19
+ import { verifyScope } from './scope.js';
20
+ import { reconcilePages } from './reconcile.js';
21
+ import { bold, dim, red, green, yellow, cyan, gray } from '../shared/colors.js';
13
22
  export async function runPush(argv) {
23
+ if (argv.includes('--rebuild'))
24
+ throw new Error('--rebuild는 v2에서 제거되었습니다. 파일 이동·갱신은 push, 본문 강제 갱신은 push --force를 사용하세요. 기존 페이지는 삭제하지 않습니다.');
25
+ if (argv.includes('--scope'))
26
+ throw new Error('--scope는 제거되었습니다. 기준 폴더 안에서 추가한 문서만 업로드합니다.');
14
27
  const DRY_RUN = argv.includes('--dry-run');
15
28
  const LIST = argv.includes('--list');
16
- const REBUILD = argv.includes('--rebuild');
17
29
  const FORCE = argv.includes('--force');
18
30
  const VERIFY = argv.includes('--verify');
19
31
  function optVal(name) {
@@ -30,11 +42,11 @@ export async function runPush(argv) {
30
42
  }
31
43
  const EXCLUDES = optVals('--exclude');
32
44
  const positionals = positionalArgs(argv);
33
- const baseInput = optVal('--base') ?? process.env.CONFLUENCE_SYNC_BASE;
45
+ const baseInput = resolveSyncBase(optVal('--base'));
34
46
  if (!baseInput) {
35
47
  console.error(red('✗ 동기화 루트가 지정되지 않았습니다.') + '\n' +
36
48
  ' --base <dir> 인자 또는 env CONFLUENCE_SYNC_BASE 로 동기화할 디렉토리를 지정하세요.\n' +
37
- ` 예) ${cyan('confluence-sync --base ./docs')}\n` +
49
+ ` 예) ${cyan('confluence-sync push --base ./docs')}\n` +
38
50
  ` 설정(.env)을 만들려면: ${cyan('confluence-sync init')}\n` +
39
51
  ` 전체 도움말: ${cyan('confluence-sync --help')}`);
40
52
  process.exit(1);
@@ -42,27 +54,35 @@ export async function runPush(argv) {
42
54
  const BASE_DIR = resolve(baseInput);
43
55
  // mapping 은 base 루트에 둔다(문서셋에 종속). --mapping 으로 위치 override 가능.
44
56
  const mappingOpt = optVal('--mapping');
45
- const MAPPING_PATH = mappingOpt ? resolve(mappingOpt) : resolve(BASE_DIR, '.confluence-sync.json');
46
- const env = readEnv();
57
+ const MAPPING_PATH = mappingOpt ? resolve(mappingOpt) : mappingPath(BASE_DIR);
58
+ const env = readEnv(BASE_DIR);
59
+ const workspace = loadWorkspace(BASE_DIR);
60
+ const remoteScope = requireScope(workspace);
61
+ if ((!DRY_RUN && !LIST) || (VERIFY && !LIST))
62
+ assertWorkspaceConnection(workspace, env);
47
63
  if (!existsSync(BASE_DIR) || !statSync(BASE_DIR).isDirectory()) {
48
64
  console.error(red(`동기화 루트가 디렉토리가 아닙니다: ${BASE_DIR}`));
49
65
  process.exit(1);
50
66
  }
51
- if (!DRY_RUN && !LIST)
67
+ if ((!DRY_RUN && !LIST) || (VERIFY && !LIST))
52
68
  requireEnv(env); // list/dry 는 Confluence 호출이 없어 인증 불필요
53
69
  const ignorer = buildIgnorer(BASE_DIR, EXCLUDES);
54
70
  const collected = collectMarkdown(BASE_DIR).map((f) => relative(BASE_DIR, f).split('\\').join('/'));
55
- const allRel = collected.filter((r) => !ignorer.ignores(r)); // 제외된 문서는 모든 단계에서 빠짐
71
+ const allRel = collected.filter((r) => !ignorer.ignores(r) && !inNestedWorkspace(BASE_DIR, r)); // 제외된 문서는 모든 단계에서 빠짐
56
72
  const ignoredCount = collected.length - allRel.length;
57
73
  const folderIndex = buildFolderIndex(allRel);
58
74
  // 링크·섹션 링크 변환은 항상 트리 전체 기준이다. 선택 동기화여도 대상 문서의 제목·헤딩을 알아야 하고,
59
75
  // 다른 문서가 이 문서의 섹션을 가리킬 수 있다. 제외된 문서는 링크 대상에서도 빠진다.
60
- const { titles: titleIndex, render } = buildTreeRenderer(BASE_DIR, allRel, { referenceRoots: optVals('--reference-root'), baseUrl: env.baseUrl });
61
- // 사용자가 실제로 고른 문서. 경로 인자가 없으면 전체가 대상이다.
62
- const picked = positionals.length ? resolveSelection(allRel, positionals, BASE_DIR) : allRel;
76
+ const titleIndex = Object.fromEntries(allRel.map(rel => [rel, readDoc(BASE_DIR, rel).title]));
77
+ // 사용자가 실제로 고른 문서. 경로 인자가 없으면 저장된 업로드 선택만 사용한다.
78
+ const staged = !positionals.length && !LIST ? loadIndex(BASE_DIR).entries : undefined;
79
+ const requested = positionals.length ? resolveSelection(allRel, positionals, BASE_DIR) : LIST ? allRel : stagedPaths(BASE_DIR);
80
+ if (requested.some(rel => !allRel.includes(rel)))
81
+ throw new Error('업로드 대상 문서가 없거나 제외되었습니다. unstage 후 다시 추가하세요.');
82
+ const picked = requested.filter(rel => inScope(rel, workspace));
63
83
  const explicit = new Set(picked);
64
84
  // 상위 README 를 함께 넣는 것은 **자식의 부모 pageId 를 얻기 위해서**다(발행 대상이 아니다).
65
- const selected = positionals.length ? withParents(picked, folderIndex) : allRel;
85
+ const selected = withParents(picked, folderIndex);
66
86
  const files = sortForSync(selected);
67
87
  /** 딸려온 상위 README 인가 — 이미 페이지가 있으면 내용은 건드리지 않는다. */
68
88
  const isCarriedParent = (rel) => !explicit.has(rel);
@@ -71,7 +91,7 @@ export async function runPush(argv) {
71
91
  (files.length > explicit.size ? dim(` (+상위 ${files.length - explicit.size})`) : '')
72
92
  : `${files.length}건`;
73
93
  const ignoredNote = ignoredCount ? dim(` (제외 ${ignoredCount})`) : '';
74
- console.log(`${dim('base:')} ${cyan(BASE_DIR)}\n${dim('대상:')} ${scope}${ignoredNote}`);
94
+ console.log(`${dim('base:')} ${cyan(BASE_DIR)}\n${dim('스페이스 루트:')} ${cyan(`${remoteScope.path} (#${remoteScope.id})`)}\n${dim('작업공간:')} ${cyan(BASE_DIR)}\n${dim('대상:')} ${scope}${ignoredNote}`);
75
95
  // ---- 경로 헬퍼 ----
76
96
  const isReadmeFile = (p) => /README\.md$/.test(p);
77
97
  const segOf = (s) => (s === '' ? 0 : s.split('/').length);
@@ -100,228 +120,255 @@ export async function runPush(argv) {
100
120
  }
101
121
  return;
102
122
  }
103
- const mapping = loadMapping(MAPPING_PATH);
104
- // A remap plan is valid only for the tree it was prepared against.
105
- for (const rel of files) {
106
- const entry = mapping[rel];
107
- if (entry?.pendingMove && entry.pendingMove.parentKey !== parentKeyOf(rel, folderIndex)) {
108
- throw new Error(`구조가 remap 이후 변경되었습니다: ${rel}. remap을 다시 실행하세요.`);
109
- }
110
- const id = readDoc(BASE_DIR, rel).fm.pageId;
111
- if (entry && id && entry.pageId !== id)
112
- throw new Error(`pageId 충돌: ${rel}`);
113
- if (!entry && id && Object.values(mapping).some(e => e.pageId === id && e.pendingMove)) {
114
- throw new Error(`remap 이후 경로가 변경되었습니다: ${rel}. remap을 다시 실행하세요.`);
115
- }
116
- }
117
- if (DRY_RUN) {
118
- const seen = new Set();
119
- for (const rel of files) {
120
- for (const d of ancestorFolderDirs(rel))
121
- if (!seen.has(d)) {
122
- seen.add(d);
123
- const fex = mapping[`${d}/`];
124
- console.log(` [${fex?.pageId ? gray('폴더') : green('폴더+')}] 📁 ${d}/`);
123
+ const originalMapping = loadMapping(MAPPING_PATH);
124
+ const mapping = reconcilePages(BASE_DIR, originalMapping, allRel, picked, remoteScope);
125
+ const relinked = new Set(picked.filter(rel => !originalMapping[rel] && mapping[rel]?.pageId));
126
+ for (const rel of files)
127
+ if (isCarriedParent(rel) && !mapping[rel]?.pageId)
128
+ throw new Error(`상위 문서의 매핑이 없습니다: ${rel}. 먼저 Pull로 기존 상위 문서를 연결하거나 상위 문서도 함께 추가하세요.`);
129
+ const root = remoteScope;
130
+ if (root.type === 'page' && explicit.has('README.md') && mapping['README.md']?.pageId !== root.id)
131
+ throw new Error('루트 README.md는 선택한 원격 페이지의 본문입니다. pull로 기준을 확보하거나 새 하위 문서는 다른 파일명을 사용하세요.');
132
+ const rootParent = root.id;
133
+ console.log(dim('Markdown 전처리: 위키링크·각주·PDF 페이지 참조를 준비합니다. 원본은 변경하지 않습니다.'));
134
+ progressStep('선택한 문서·첨부 전처리');
135
+ const prepared = await preparePushTree(BASE_DIR, allRel, picked, { referenceRoots: optVals('--reference-root'), baseUrl: env.baseUrl });
136
+ try {
137
+ const { render, docs } = prepared;
138
+ const hashes = Object.fromEntries(picked.map(rel => [rel, docHash(docs[rel].title, render(rel, docs[rel].body, docs[rel].title))]));
139
+ if (staged)
140
+ for (const rel of picked)
141
+ assertStaged(BASE_DIR, rel, hashes[rel], staged[rel]);
142
+ const sourceTexts = Object.fromEntries(picked.map(rel => [rel, readFileSync(resolve(BASE_DIR, rel), 'utf8')]));
143
+ const localHashes = Object.fromEntries(picked.map(rel => [rel, localStateHash(BASE_DIR, rel, render(rel, docs[rel].body, docs[rel].title))]));
144
+ const legacy = buildTreeRenderer(BASE_DIR, allRel);
145
+ const locallyClean = (rel) => {
146
+ const entry = mapping[rel];
147
+ return entry?.localHash ? entry.localHash === localHashes[rel] : !!entry?.hash && (entry.hash === hashes[rel]
148
+ || entry.hash === docHash(legacy.docs[rel].title, legacy.render(rel, legacy.docs[rel].body, legacy.docs[rel].title))
149
+ || entry.hash === docHash(docs[rel].title, render(rel, docs[rel].body, docs[rel].title, 'panels'))
150
+ || entry.hash === docHash(docs[rel].title, render(rel, docs[rel].body, docs[rel].title, true))
151
+ || entry.hash === docHash(legacy.docs[rel].title, legacy.render(rel, legacy.docs[rel].body, legacy.docs[rel].title, 'panels'))
152
+ || entry.hash === docHash(legacy.docs[rel].title, legacy.render(rel, legacy.docs[rel].body, legacy.docs[rel].title, true)));
153
+ };
154
+ const publicationHash = (rel) => !FORCE && locallyClean(rel) ? mapping[rel].hash : hashes[rel];
155
+ const unchanged = (rel) => !FORCE && !VERIFY && locallyClean(rel) && !mapping[rel]?.pendingMove;
156
+ const needsRemote = picked.some(rel => !unchanged(rel));
157
+ let client;
158
+ let spaceId = '';
159
+ if (needsRemote && (!DRY_RUN || VERIFY)) {
160
+ client = createClient({ baseUrl: env.baseUrl, email: env.email, token: env.token }, { force: FORCE, verify: VERIFY, scope: { id: root.id, spaceId: workspace.connection.spaceId } });
161
+ progressStep('원격 연결과 문서 버전 검증');
162
+ spaceId = await verifyScope(client, workspace);
163
+ for (const rel of files) {
164
+ if (!isCarriedParent(rel) && unchanged(rel))
165
+ continue;
166
+ if (isCarriedParent(rel) && mapping[rel]?.pageId) {
167
+ await client.assertInScope(mapping[rel].pageId);
168
+ continue;
125
169
  }
126
- const ex0 = mapping[rel];
127
- // 딸려온 상위 README 는 pageId 만 쓰고 지나간다 — 고르지 않은 문서를 발행하지 않는다
128
- if (isCarriedParent(rel) && ex0?.pageId) {
129
- console.log(` [${gray('상위')}] ${rel} ${dim(`(pageId #${ex0.pageId} 만 사용)`)}`);
130
- continue;
131
- }
132
- const { title, body, fm } = readDoc(BASE_DIR, rel);
133
- const r = render(rel, body, title);
134
- for (const link of r.referenceLinks)
135
- console.log(dim(` 참조 링크: ${link}`));
136
- for (const warning of r.linkWarnings)
137
- console.error(yellow(` ⚠ ${rel}: ${warning}`));
138
- const hash = docHash(title, r);
139
- const ex = mapping[rel];
140
- // 매핑엔 없지만 frontmatter 에 pageId 가 있으면 신규 생성이 아니라 기존 페이지에 연결된다
141
- const status = !ex?.pageId
142
- ? (fm.pageId ? cyan('연결') : green('신규'))
143
- : ex.pendingMove ? cyan(ex.hash === hash ? '위치 확인/이동' : '위치 확인/이동 + 변경')
144
- : ex.hash === hash ? gray('동일') : yellow('변경');
145
- const pk = parentKeyOf(rel, folderIndex);
146
- const anchorNote = r.anchorLinks || r.anchorsPlaced
147
- ? `, 섹션링크: ${r.anchorLinks}, 앵커: ${r.anchorsPlaced}` : '';
148
- console.log(` [${status}] ${rel} ${dim('→')} ${cyan(`"${title}"`)} ${dim(`(부모: ${pk ?? 'ROOT'}, 내부링크: ${r.internalLinks}${anchorNote}, 이미지: ${r.images.length}, 첨부: ${r.attachments.length})`)}`);
149
- if (r.deadAnchors.length) {
150
- console.error(yellow(` ⚠ 대응 헤딩 없는 앵커 ${r.deadAnchors.length}개: `) + dim(r.deadAnchors.slice(0, 3).join(' ')));
170
+ const { title, body, fm } = docs[rel];
171
+ const hash = publicationHash(rel);
172
+ const entry = mapping[rel] ?? (fm.pageId ? { pageId: fm.pageId, title } : undefined);
173
+ if (entry)
174
+ await client.checkPage({ [rel]: entry }, spaceId, rel, hash);
151
175
  }
152
176
  }
153
- console.log('\n' + dim('--dry-run: 실제 호출 없음.'));
154
- return;
155
- }
156
- const client = createClient({ baseUrl: env.baseUrl, email: env.email, token: env.token }, { force: FORCE, verify: VERIFY });
157
- const spaceId = await client.getSpaceId(env.spaceKey);
158
- if (REBUILD) {
159
- await client.deleteAll(mapping);
160
- saveMapping(MAPPING_PATH, {});
161
- }
162
- const work = REBUILD ? {} : mapping;
163
- let moved = 0;
164
- let created = 0, updated = 0, skipped = 0, recreated = 0, relinked = 0, foldersMade = 0, linked = 0, carried = 0;
165
- const rendered = new Map();
166
- const recreatedTitles = new Set(); // 이번에 재생성된 문서의 제목
167
- const touched = new Set(); // 이번에 발행(생성/갱신/재생성)된 문서
168
- const handledFolders = new Map(); // 이번 실행에서 처리한 폴더 dir
169
- // README 없는 폴더를 Confluence 폴더로 생성(없으면)하고 work 에 등록. 성공 여부 반환.
170
- const ensureFolder = async (dir) => {
171
- const key = `${dir}/`;
172
- const pcKey = containerKeyOf(parentDirOf(dir), folderIndex);
173
- const parentId = pcKey ? work[pcKey]?.pageId : env.parentId;
174
- if (pcKey && !parentId) {
175
- console.error(yellow(` ✗ 폴더 건너뜀 ${dir}/ (부모 '${pcKey}' 가 아직 없음)`));
176
- return false;
177
- }
178
- const ex = work[key];
179
- try {
180
- const title = dir.split('/').pop();
181
- const result = await client.upsertFolder(spaceId, env.spaceKey, title, parentId, ex?.type === 'folder' ? ex.pageId : undefined);
182
- const fid = result.id;
183
- work[key] = { pageId: fid, title, type: 'folder' };
184
- saveMapping(MAPPING_PATH, work);
185
- console.log(` ${cyan(!result.created ? '📁 폴더 연결' : '📁 폴더')} ${dir}/ ${dim(`(부모: ${pcKey ?? 'ROOT'})`)}`);
186
- if (result.created)
187
- foldersMade++;
188
- return true;
189
- }
190
- catch (e) {
191
- console.error(red(` ✗ 폴더 준비 실패 ${dir}/`) + `\n${e.message}`);
192
- return false;
193
- }
194
- };
195
- for (const rel of files) {
196
- // 상위 README-less 폴더들 보장(shallow→deep)
197
- let folderOk = true;
198
- for (const d of ancestorFolderDirs(rel)) {
199
- if (!handledFolders.has(d))
200
- handledFolders.set(d, await ensureFolder(d));
201
- if (!handledFolders.get(d))
202
- folderOk = false;
203
- if (!folderOk)
204
- break;
205
- }
206
- if (!folderOk) {
207
- console.error(yellow(` ✗ 건너뜀 ${rel} (상위 폴더 미생성)`));
208
- continue;
209
- }
210
- // 딸려온 상위 README 는 자식의 부모 pageId 를 얻으려고 들어온 것이다.
211
- // 이미 페이지가 있으면 내용은 건드리지 않는다 — `csync <파일>` 이 고르지 않은 문서를 발행하면 안 된다.
212
- // (페이지가 아직 없으면 자식을 붙일 곳이 없으므로 아래에서 생성한다)
213
- if (isCarriedParent(rel) && work[rel]?.pageId) {
214
- console.log(` ${gray('· 상위')} ${rel} ${dim(`(pageId #${work[rel].pageId} 만 사용)`)}`);
215
- carried++;
216
- continue;
217
- }
218
- const { title, body, fm } = readDoc(BASE_DIR, rel);
219
- const r = render(rel, body, title);
220
- for (const link of r.referenceLinks)
221
- console.log(dim(` 참조 링크: ${link}`));
222
- for (const warning of r.linkWarnings)
223
- console.error(yellow(` ⚠ ${rel}: ${warning}`));
224
- const hash = docHash(title, r);
225
- const pk = parentKeyOf(rel, folderIndex);
226
- if (r.deadAnchors.length) {
227
- console.error(yellow(` ⚠ 대응 헤딩 없는 앵커 ${rel}`) + dim(` ${r.deadAnchors.slice(0, 3).join(' ')}${r.deadAnchors.length > 3 ? ` 외 ${r.deadAnchors.length - 3}` : ''}`));
228
- }
229
- const parentId = pk ? work[pk]?.pageId : env.parentId;
230
- rendered.set(rel, { ...r, title, hash, parentId });
231
- if (pk && !parentId) {
232
- console.error(yellow(` ✗ 건너뜀 ${rel} (부모 '${pk}' 가 아직 없음)`));
233
- continue;
234
- }
235
- // pull 로 받은 문서(frontmatter 에 pageId)는 매핑이 없어도 원본 페이지에 다시 연결한다.
236
- // 매핑 파일을 잃었거나 vault 를 다른 곳에 복제한 경우 중복 생성을 막아준다.
237
- if (!work[rel]?.pageId && fm.pageId) {
238
- try {
239
- if (await client.getContentOrNull(fm.pageId)) {
240
- work[rel] = { pageId: fm.pageId, title };
241
- saveMapping(MAPPING_PATH, work);
242
- console.log(` ${cyan('🔗 연결')} ${rel} ${dim(`→ 기존 페이지 #${fm.pageId}`)}`);
243
- linked++;
177
+ if (DRY_RUN) {
178
+ const seen = new Set();
179
+ for (const rel of files) {
180
+ for (const d of ancestorFolderDirs(rel))
181
+ if (!seen.has(d)) {
182
+ seen.add(d);
183
+ const fex = mapping[`${d}/`];
184
+ console.log(` [${fex?.pageId ? gray('폴더') : green('폴더+')}] 📁 ${d}/`);
185
+ }
186
+ const ex0 = mapping[rel];
187
+ // 딸려온 상위 README 는 pageId 만 쓰고 지나간다 — 고르지 않은 문서를 발행하지 않는다
188
+ if (isCarriedParent(rel) && ex0?.pageId) {
189
+ console.log(` [${gray('상위')}] ${rel} ${dim(`(pageId #${ex0.pageId} 만 사용)`)}`);
190
+ continue;
244
191
  }
245
- else {
246
- console.error(yellow(` ⚠ frontmatter 의 pageId #${fm.pageId} 를 찾을 수 없습니다 → 새로 생성 (${rel})`));
192
+ const { title, body, fm } = docs[rel];
193
+ const r = render(rel, body, title);
194
+ for (const link of r.referenceLinks)
195
+ console.log(dim(` 참조 링크: ${link}`));
196
+ for (const warning of r.linkWarnings)
197
+ console.error(yellow(` ⚠ ${rel}: ${warning}`));
198
+ const hash = publicationHash(rel);
199
+ const ex = mapping[rel];
200
+ // 매핑엔 없지만 frontmatter 에 pageId 가 있으면 신규 생성이 아니라 기존 페이지에 연결된다
201
+ const status = !ex?.pageId
202
+ ? (fm.pageId ? cyan('연결') : green('신규'))
203
+ : ex.pendingMove ? cyan(!FORCE && ex.hash === hash ? '위치 확인/이동' : '위치 확인/이동 + 변경')
204
+ : !FORCE && locallyClean(rel) ? gray(relinked.has(rel) ? '로컬 경로 연결' : '동일') : yellow('변경');
205
+ const pk = parentKeyOf(rel, folderIndex);
206
+ const anchorNote = r.anchorLinks || r.anchorsPlaced
207
+ ? `, 섹션링크: ${r.anchorLinks}, 앵커: ${r.anchorsPlaced}` : '';
208
+ console.log(` [${status}] ${rel} ${dim('→')} ${cyan(`"${title}"`)} ${dim(`(부모: ${pk ?? 'ROOT'}, 내부링크: ${r.internalLinks}${anchorNote}, 이미지: ${r.images.length}, 첨부: ${r.attachments.length})`)}`);
209
+ if (r.deadAnchors.length) {
210
+ console.error(yellow(` ⚠ 대응 헤딩 없는 앵커 ${r.deadAnchors.length}개: `) + dim(r.deadAnchors.slice(0, 3).join(' ')));
247
211
  }
248
212
  }
213
+ console.log('\n' + dim(VERIFY ? '--dry-run --verify: 원격 조회만 수행, 변경 없음.' : '--dry-run: 실제 호출 없음.'));
214
+ return;
215
+ }
216
+ const apiClient = client;
217
+ const work = mapping;
218
+ let failed = 0;
219
+ let moved = 0;
220
+ let created = 0, updated = 0, skipped = 0, foldersMade = 0, carried = 0;
221
+ const handledFolders = new Map(); // 이번 실행에서 처리한 폴더 dir
222
+ // README 없는 폴더를 Confluence 폴더로 생성(없으면)하고 work 에 등록. 성공 여부 반환.
223
+ const ensureFolder = async (dir) => {
224
+ const key = `${dir}/`;
225
+ const pcKey = containerKeyOf(parentDirOf(dir), folderIndex);
226
+ const parentId = pcKey ? work[pcKey]?.pageId : rootParent;
227
+ if (pcKey && !parentId) {
228
+ console.error(yellow(` ✗ 폴더 건너뜀 ${dir}/ (부모 '${pcKey}' 가 아직 없음)`));
229
+ return false;
230
+ }
231
+ const ex = work[key];
232
+ try {
233
+ const title = dir.split('/').pop();
234
+ const result = await apiClient.upsertFolder(spaceId, env.spaceKey, title, parentId, ex?.type === 'folder' ? ex.pageId : undefined);
235
+ const fid = result.id;
236
+ work[key] = ex?.pageId === fid ? ex : { pageId: fid, title, type: 'folder' };
237
+ saveMapping(MAPPING_PATH, work);
238
+ console.log(` ${cyan(!result.created ? '📁 폴더 연결' : '📁 폴더')} ${dir}/ ${dim(`(부모: ${pcKey ?? 'ROOT'})`)}`);
239
+ if (result.created)
240
+ foldersMade++;
241
+ return true;
242
+ }
249
243
  catch (e) {
250
- console.error(yellow(` ⚠ pageId 확인 실패 #${fm.pageId}: ${e.message}`));
244
+ console.error(red(` ✗ 폴더 준비 실패 ${dir}/`) + `\n${e.message}`);
245
+ return false;
251
246
  }
252
- }
253
- try {
254
- const result = await client.upsertPage(work, spaceId, rel, title, r.storage, hash, parentId);
255
- saveMapping(MAPPING_PATH, work);
256
- if (result === 'skipped') {
257
- console.log(` ${gray('= 변경없음')} ${rel}`);
247
+ };
248
+ for (const [index, rel] of files.entries()) {
249
+ progressStep(`문서 처리 ${index + 1}/${files.length} · ${rel}`);
250
+ if (!isCarriedParent(rel) && unchanged(rel)) {
251
+ if (relinked.has(rel)) {
252
+ saveMapping(MAPPING_PATH, work);
253
+ console.log(` ${gray('로컬 경로 연결')} ${rel} ${dim('(원격 본문·제목·위치 유지)')}`);
254
+ }
255
+ console.log(` = 변경없음 ${rel}`);
258
256
  skipped++;
257
+ completeStaged(BASE_DIR, rel, staged?.[rel]);
259
258
  continue;
260
259
  }
261
- if (result === 'moved') {
262
- console.log(` ${cyan('→ 이동')} ${rel} (부모: ${pk ?? 'ROOT'})`);
263
- moved++;
260
+ if (isCarriedParent(rel) && !needsRemote) {
261
+ carried++;
264
262
  continue;
265
263
  }
266
- const imgOk = await client.uploadAttachments(work[rel].pageId, r.attachments, BASE_DIR);
267
- const imgNote = r.attachments.length ? `, 첨부 ${imgOk}/${r.attachments.length}` : '';
268
- if (imgOk !== r.attachments.length) {
269
- delete work[rel].hash; // 부분 실패는 다음 동기화에서 재시도한다.
270
- saveMapping(MAPPING_PATH, work);
264
+ // 상위 README-less 폴더들 보장(shallow→deep)
265
+ let folderOk = true;
266
+ for (const d of ancestorFolderDirs(rel)) {
267
+ if (!handledFolders.has(d))
268
+ handledFolders.set(d, await ensureFolder(d));
269
+ if (!handledFolders.get(d))
270
+ folderOk = false;
271
+ if (!folderOk)
272
+ break;
271
273
  }
272
- const label = result === 'created' ? green('+ 생성')
273
- : result === 'recreated' ? magenta('♻ 재생성(삭제 복구)')
274
- : yellow('↻ 갱신');
275
- console.log(` ${label} ${rel} ${dim('→')} ${cyan(`"${title}"`)} ${dim(`(부모: ${pk ?? 'ROOT'}${imgNote})`)}`);
276
- if (result === 'created')
277
- created++;
278
- else if (result === 'recreated') {
279
- recreated++;
280
- recreatedTitles.add(title);
281
- }
282
- else
283
- updated++;
284
- touched.add(rel);
285
- }
286
- catch (e) {
287
- console.error(red(` ✗ 실패 ${rel}`) + `\n${e.message}`);
288
- }
289
- }
290
- // 링크 정합성: 재생성된 문서를 가리키던, 이번에 발행 안 된(스킵된) 문서를 강제 재발행해 링크를 새 페이지로 재연결
291
- if (recreatedTitles.size) {
292
- for (const rel of files) {
293
- if (touched.has(rel))
274
+ if (!folderOk) {
275
+ failed++;
276
+ console.error(yellow(` ✗ 건너뜀 ${rel} (상위 폴더 미생성)`));
294
277
  continue;
295
- const r = rendered.get(rel);
296
- if (!r || !work[rel]?.pageId)
278
+ }
279
+ // 딸려온 상위 README 는 자식의 부모 pageId 를 얻으려고 들어온 것이다.
280
+ // 이미 페이지가 있으면 내용은 건드리지 않는다 — `csync <파일>` 이 고르지 않은 문서를 발행하면 안 된다.
281
+ // (페이지가 아직 없으면 자식을 붙일 곳이 없으므로 아래에서 생성한다)
282
+ if (isCarriedParent(rel) && work[rel]?.pageId) {
283
+ console.log(` ${gray('· 상위')} ${rel} ${dim(`(pageId #${work[rel].pageId} 만 사용)`)}`);
284
+ carried++;
297
285
  continue;
298
- if (!r.linkedTitles.some((t) => recreatedTitles.has(t)))
286
+ }
287
+ const { title, body, fm } = docs[rel];
288
+ const r = render(rel, body, title);
289
+ for (const link of r.referenceLinks)
290
+ console.log(dim(` 참조 링크: ${link}`));
291
+ for (const warning of r.linkWarnings)
292
+ console.error(yellow(` ⚠ ${rel}: ${warning}`));
293
+ const hash = publicationHash(rel);
294
+ const pk = parentKeyOf(rel, folderIndex);
295
+ if (r.deadAnchors.length) {
296
+ console.error(yellow(` ⚠ 대응 헤딩 없는 앵커 ${rel}`) + dim(` ${r.deadAnchors.slice(0, 3).join(' ')}${r.deadAnchors.length > 3 ? ` 외 ${r.deadAnchors.length - 3}` : ''}`));
297
+ }
298
+ // Pull keeps existing local paths; editing content must preserve the observed remote parent.
299
+ const recordedParent = work[rel]?.pendingMove ? undefined : work[rel]?.remoteParentId;
300
+ const parentId = recordedParent ?? (pk ? work[pk]?.pageId : rootParent);
301
+ if (pk && !parentId) {
302
+ failed++;
303
+ console.error(yellow(` ✗ 건너뜀 ${rel} (부모 '${pk}' 가 아직 없음)`));
299
304
  continue;
305
+ }
306
+ // pull 로 받은 문서(frontmatter 에 pageId)는 매핑이 없어도 원본 페이지에 다시 연결한다.
307
+ // 매핑 파일을 잃었거나 vault 를 다른 곳에 복제한 경우 중복 생성을 막아준다.
308
+ if (!work[rel]?.pageId && fm.pageId)
309
+ work[rel] = { pageId: fm.pageId, title };
300
310
  try {
301
- await client.upsertPage(work, spaceId, rel, r.title, r.storage, r.hash, r.parentId, true);
311
+ if (staged)
312
+ assertStaged(BASE_DIR, rel, hashes[rel], staged[rel]);
313
+ const result = await apiClient.upsertPage(work, spaceId, rel, title, r.storage, hash, parentId);
302
314
  saveMapping(MAPPING_PATH, work);
303
- const uploaded = await client.uploadAttachments(work[rel].pageId, r.attachments, BASE_DIR);
304
- if (uploaded !== r.attachments.length) {
305
- delete work[rel].hash;
315
+ if (result === 'skipped') {
316
+ work[rel].localHash = localHashes[rel];
317
+ work[rel].sourceObject = storeBaseline(BASE_DIR, sourceTexts[rel]);
306
318
  saveMapping(MAPPING_PATH, work);
319
+ console.log(` ${gray('= 변경없음')} ${rel}`);
320
+ skipped++;
321
+ completeStaged(BASE_DIR, rel, staged?.[rel]);
322
+ continue;
307
323
  }
308
- console.log(` ${cyan('🔗 링크 재연결')} ${rel} ${dim('→')} ${cyan(`"${r.title}"`)}`);
309
- relinked++;
324
+ if (result === 'moved') {
325
+ work[rel].localHash = localHashes[rel];
326
+ work[rel].sourceObject = storeBaseline(BASE_DIR, sourceTexts[rel]);
327
+ saveMapping(MAPPING_PATH, work);
328
+ console.log(` ${cyan('→ 이동')} ${rel} (부모: ${pk ?? 'ROOT'})`);
329
+ moved++;
330
+ completeStaged(BASE_DIR, rel, staged?.[rel]);
331
+ continue;
332
+ }
333
+ const imgOk = await apiClient.uploadAttachments(work[rel].pageId, r.attachments, BASE_DIR);
334
+ const imgNote = r.attachments.length ? `, 첨부 ${imgOk}/${r.attachments.length}` : '';
335
+ if (imgOk !== r.attachments.length) {
336
+ failed++;
337
+ delete work[rel].hash; // 부분 실패는 다음 동기화에서 재시도한다.
338
+ saveMapping(MAPPING_PATH, work);
339
+ }
340
+ if (imgOk === r.attachments.length) {
341
+ work[rel].localHash = localHashes[rel];
342
+ work[rel].sourceObject = storeBaseline(BASE_DIR, sourceTexts[rel]);
343
+ saveMapping(MAPPING_PATH, work);
344
+ }
345
+ if (imgOk === r.attachments.length)
346
+ completeStaged(BASE_DIR, rel, staged?.[rel]);
347
+ const label = result === 'created' ? green('+ 생성')
348
+ : yellow('↻ 갱신');
349
+ console.log(` ${label} ${rel} ${dim('→')} ${cyan(`"${title}"`)} ${dim(`(부모: ${pk ?? 'ROOT'}${imgNote})`)}`);
350
+ if (result === 'created')
351
+ created++;
352
+ else
353
+ updated++;
310
354
  }
311
355
  catch (e) {
312
- console.error(red(` ✗ 링크 재연결 실패 ${rel}`) + `\n${e.message}`);
356
+ failed++;
357
+ console.error(red(` ✗ 실패 ${rel}`) + `\n${e.message}`);
313
358
  }
314
359
  }
360
+ if (failed)
361
+ process.exitCode = 1;
362
+ console.log('\n' + bold(failed ? '일부 실패' : '완료') +
363
+ ` ${green(`생성 ${created}`)}` +
364
+ ` ${yellow(`갱신 ${updated}`)}` +
365
+ ` ${cyan(`이동 ${moved}`)}` +
366
+ ` ${gray(`변경없음 ${skipped}`)}` +
367
+ (foldersMade ? ` ${cyan(`폴더 ${foldersMade}`)}` : '') +
368
+ (carried ? ` ${gray(`상위 ${carried}`)}` : '') +
369
+ ` ${dim(`대상 ${files.length - carried}`)}`);
370
+ }
371
+ finally {
372
+ prepared.dispose();
315
373
  }
316
- console.log('\n' + bold('완료') +
317
- ` ${green(`생성 ${created}`)}` +
318
- ` ${yellow(`갱신 ${updated}`)}` +
319
- ` ${cyan(`이동 ${moved}`)}` +
320
- ` ${magenta(`재생성 ${recreated}`)}` +
321
- ` ${cyan(`링크재연결 ${relinked}`)}` +
322
- ` ${gray(`변경없음 ${skipped}`)}` +
323
- (linked ? ` ${cyan(`연결 ${linked}`)}` : '') +
324
- (foldersMade ? ` ${cyan(`폴더 ${foldersMade}`)}` : '') +
325
- (carried ? ` ${gray(`상위 ${carried}`)}` : '') +
326
- ` ${dim(`대상 ${files.length - carried}`)}`);
327
374
  }
@@ -0,0 +1,51 @@
1
+ /** Infer local page moves without writing metadata or contacting Confluence. */
2
+ import { existsSync } from 'node:fs';
3
+ import { resolve } from 'node:path';
4
+ import { buildFolderIndex, parentKeyOf, readDoc } from '../documents/catalog.js';
5
+ export function reconcilePages(base, mapping, allFiles, selected, scope) {
6
+ const next = structuredClone(mapping);
7
+ const byId = new Map(Object.entries(mapping).map(([key, entry]) => [entry.pageId, { key, entry }]));
8
+ const oldFolders = buildFolderIndex(Object.keys(mapping).filter(key => key.endsWith('.md')));
9
+ const folders = buildFolderIndex(allFiles);
10
+ const docs = new Map(allFiles.map(key => [key, readDoc(base, key)]));
11
+ const seen = new Map();
12
+ for (const [key, doc] of docs) {
13
+ const id = doc.fm.pageId ?? mapping[key]?.pageId;
14
+ if (!id)
15
+ continue;
16
+ if (mapping[key] && mapping[key].pageId !== id)
17
+ throw new Error(`pageId 충돌: ${key}`);
18
+ if (seen.has(id))
19
+ throw new Error(`문서 pageId 중복: ${seen.get(id)}, ${key} (#${id})`);
20
+ seen.set(id, key);
21
+ if (id === scope.id && (scope.type !== 'page' || key !== 'README.md'))
22
+ throw new Error('스코프 루트의 매핑 경로는 README.md로 유지해야 합니다.');
23
+ }
24
+ for (const key of selected) {
25
+ const doc = docs.get(key);
26
+ const id = doc.fm.pageId ?? mapping[key]?.pageId;
27
+ if (!id)
28
+ continue;
29
+ const prior = byId.get(id);
30
+ if (prior?.entry.type === 'folder')
31
+ throw new Error(`폴더 ID를 문서에 사용할 수 없습니다: ${key}`);
32
+ if (prior && prior.key !== key && existsSync(resolve(base, prior.key)))
33
+ throw new Error(`기존 pageId 경로가 남아 있습니다: ${prior.key}, ${key}`);
34
+ const entry = { ...(prior?.entry ?? { pageId: id, title: doc.title }) };
35
+ const parentKey = parentKeyOf(key, folders);
36
+ const oldParentKey = prior ? parentKeyOf(prior.key, oldFolders) : undefined;
37
+ const parentId = parentKey ? mapping[parentKey]?.pageId ?? docs.get(parentKey)?.fm.pageId : scope.id;
38
+ const sameParent = !!prior && (oldParentKey === parentKey || !!parentId && prior.entry.remoteParentId === parentId);
39
+ if (id === scope.id) {
40
+ if (entry.pendingMove)
41
+ throw new Error('스코프 루트는 이동할 수 없습니다.');
42
+ }
43
+ else if (!prior || !sameParent || entry.pendingMove) {
44
+ entry.pendingMove = { parentKey };
45
+ }
46
+ if (prior && prior.key !== key)
47
+ delete next[prior.key];
48
+ next[key] = entry;
49
+ }
50
+ return next;
51
+ }
@@ -4,7 +4,7 @@ import { basename, dirname, isAbsolute, join, relative, resolve } from 'node:pat
4
4
  import { parse } from 'dotenv';
5
5
  import { collectMarkdown } from '../documents/catalog.js';
6
6
  import { buildIgnorer } from './ignore.js';
7
- import { MAPPING_FILE } from './mapping.js';
7
+ import { mappingPath } from './metadata.js';
8
8
  const key = (s) => s.normalize('NFC').toLowerCase();
9
9
  const slash = (s) => s.split('\\').join('/');
10
10
  const decode = (s) => { try {
@@ -34,7 +34,7 @@ export function buildReferenceLookup(base, rels, roots, baseUrl) {
34
34
  if (configured && configured !== server)
35
35
  throw new Error('참조 폴더의 Confluence 서버가 현재 설정과 다릅니다: ' + root);
36
36
  let mapping = {};
37
- const path = join(root, MAPPING_FILE);
37
+ const path = mappingPath(root);
38
38
  if (existsSync(path)) {
39
39
  mapping = JSON.parse(readFileSync(path, 'utf8'));
40
40
  if (!mapping || typeof mapping !== 'object' || Array.isArray(mapping)