@wonseok-han/confluence-sync 1.7.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.
- package/README.md +44 -409
- package/dist/cli/args.js +1 -1
- package/dist/cli/help.js +214 -115
- package/dist/cli/init.js +44 -5
- package/dist/cli/main.js +42 -11
- package/dist/cli/status.js +83 -0
- package/dist/confluence/client.js +157 -86
- package/dist/confluence/config.js +12 -8
- package/dist/confluence/env-file.js +23 -0
- package/dist/confluence/links.js +18 -0
- package/dist/conversion/asset-paths.js +5 -0
- package/dist/conversion/attachments.js +29 -7
- package/dist/conversion/convert.js +10 -22
- package/dist/conversion/footnotes.js +114 -11
- package/dist/conversion/markdown.js +26 -0
- package/dist/conversion/pdf-pages.js +10 -8
- package/dist/documents/anchors.js +9 -0
- package/dist/documents/callout-roundtrip.js +75 -0
- package/dist/documents/callouts.js +74 -0
- package/dist/documents/catalog.js +2 -2
- package/dist/documents/footnote-roundtrip.js +96 -0
- package/dist/documents/format.js +116 -0
- package/dist/documents/html-tables.js +30 -0
- package/dist/documents/html-to-markdown.js +47 -13
- package/dist/documents/markdown.js +83 -12
- package/dist/documents/obsidian.js +6 -3
- package/dist/documents/tables.js +89 -0
- package/dist/runtime/network.js +4 -0
- package/dist/shared/colors.js +2 -2
- package/dist/shared/progress.js +164 -0
- package/dist/shared/version.js +12 -0
- package/dist/site/assets/Monaco-LICENSE.txt +21 -0
- package/dist/site/assets/Monaco-ThirdPartyNotices.txt +448 -0
- package/dist/site/assets/PDFJS-LICENSE.txt +177 -0
- package/dist/site/assets/app.css +1 -0
- package/dist/site/assets/app.js +4 -0
- package/dist/site/assets/chunks/chunk-TEW74QPN.js +35 -0
- package/dist/site/assets/chunks/markdown-S6623E66.js +1 -0
- package/dist/site/assets/chunks/monaco-36ZGUH4Y.js +897 -0
- package/dist/site/assets/chunks/monaco-HFLEBJTI.css +1 -0
- package/dist/site/assets/chunks/pdf-DDN7TXTP.js +55 -0
- package/dist/site/assets/confluence-env-example.txt +32 -0
- package/dist/site/assets/editor.worker.js +1 -0
- package/dist/site/assets/favicon.svg +3 -0
- package/dist/site/assets/guide/01-account-settings.png +0 -0
- package/dist/site/assets/guide/02-atlassian-security.png +0 -0
- package/dist/site/assets/guide/03-api-tokens.png +0 -0
- package/dist/site/assets/guide/04-scope-api-token-step1.png +0 -0
- package/dist/site/assets/guide/05-scope-api-token-step2.png +0 -0
- package/dist/site/assets/guide/06-scope-api-token-step3.png +0 -0
- package/dist/site/assets/guide/07-scope-api-token-step4.png +0 -0
- package/dist/site/assets/guide/web/document-preview.png +0 -0
- package/dist/site/assets/guide/web/remote-diff.png +0 -0
- package/dist/site/assets/guide/web/sync-workspace.png +0 -0
- package/dist/site/assets/pdf/cmaps/78-EUC-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/78-EUC-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/78-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/78-RKSJ-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/78-RKSJ-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/78-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/78ms-RKSJ-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/78ms-RKSJ-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/83pv-RKSJ-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/90ms-RKSJ-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/90ms-RKSJ-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/90msp-RKSJ-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/90msp-RKSJ-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/90pv-RKSJ-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/90pv-RKSJ-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Add-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Add-RKSJ-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Add-RKSJ-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Add-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-CNS1-0.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-CNS1-1.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-CNS1-2.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-CNS1-3.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-CNS1-4.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-CNS1-5.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-CNS1-6.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-CNS1-UCS2.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-GB1-0.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-GB1-1.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-GB1-2.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-GB1-3.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-GB1-4.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-GB1-5.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-GB1-UCS2.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-Japan1-0.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-Japan1-1.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-Japan1-2.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-Japan1-3.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-Japan1-4.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-Japan1-5.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-Japan1-6.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-Japan1-UCS2.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-Korea1-0.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-Korea1-1.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-Korea1-2.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-Korea1-UCS2.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/B5-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/B5-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/B5pc-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/B5pc-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/CNS-EUC-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/CNS-EUC-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/CNS1-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/CNS1-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/CNS2-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/CNS2-V.bcmap +3 -0
- package/dist/site/assets/pdf/cmaps/ETHK-B5-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/ETHK-B5-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/ETen-B5-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/ETen-B5-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/ETenms-B5-H.bcmap +3 -0
- package/dist/site/assets/pdf/cmaps/ETenms-B5-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/EUC-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/EUC-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Ext-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Ext-RKSJ-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Ext-RKSJ-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Ext-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/GB-EUC-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/GB-EUC-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/GB-H.bcmap +4 -0
- package/dist/site/assets/pdf/cmaps/GB-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/GBK-EUC-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/GBK-EUC-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/GBK2K-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/GBK2K-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/GBKp-EUC-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/GBKp-EUC-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/GBT-EUC-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/GBT-EUC-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/GBT-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/GBT-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/GBTpc-EUC-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/GBTpc-EUC-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/GBpc-EUC-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/GBpc-EUC-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/HKdla-B5-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/HKdla-B5-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/HKdlb-B5-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/HKdlb-B5-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/HKgccs-B5-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/HKgccs-B5-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/HKm314-B5-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/HKm314-B5-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/HKm471-B5-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/HKm471-B5-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/HKscs-B5-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/HKscs-B5-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Hankaku.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Hiragana.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/KSC-EUC-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/KSC-EUC-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/KSC-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/KSC-Johab-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/KSC-Johab-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/KSC-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/KSCms-UHC-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/KSCms-UHC-HW-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/KSCms-UHC-HW-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/KSCms-UHC-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/KSCpc-EUC-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/KSCpc-EUC-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Katakana.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/LICENSE +36 -0
- package/dist/site/assets/pdf/cmaps/NWP-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/NWP-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/RKSJ-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/RKSJ-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Roman.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniCNS-UCS2-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniCNS-UCS2-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniCNS-UTF16-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniCNS-UTF16-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniCNS-UTF32-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniCNS-UTF32-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniCNS-UTF8-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniCNS-UTF8-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniGB-UCS2-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniGB-UCS2-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniGB-UTF16-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniGB-UTF16-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniGB-UTF32-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniGB-UTF32-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniGB-UTF8-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniGB-UTF8-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJIS-UCS2-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJIS-UCS2-HW-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJIS-UCS2-HW-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJIS-UCS2-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJIS-UTF16-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJIS-UTF16-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJIS-UTF32-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJIS-UTF32-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJIS-UTF8-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJIS-UTF8-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJIS2004-UTF16-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJIS2004-UTF16-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJIS2004-UTF32-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJIS2004-UTF32-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJIS2004-UTF8-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJIS2004-UTF8-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJISPro-UCS2-HW-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJISPro-UCS2-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJISPro-UTF8-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJISX0213-UTF32-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJISX0213-UTF32-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJISX02132004-UTF32-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJISX02132004-UTF32-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniKS-UCS2-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniKS-UCS2-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniKS-UTF16-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniKS-UTF16-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniKS-UTF32-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniKS-UTF32-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniKS-UTF8-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniKS-UTF8-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/WP-Symbol.bcmap +0 -0
- package/dist/site/assets/pdf/standard_fonts/FoxitDingbats.pfb +0 -0
- package/dist/site/assets/pdf/standard_fonts/FoxitFixed.pfb +0 -0
- package/dist/site/assets/pdf/standard_fonts/FoxitFixedBold.pfb +0 -0
- package/dist/site/assets/pdf/standard_fonts/FoxitFixedBoldItalic.pfb +0 -0
- package/dist/site/assets/pdf/standard_fonts/FoxitFixedItalic.pfb +0 -0
- package/dist/site/assets/pdf/standard_fonts/FoxitSerif.pfb +0 -0
- package/dist/site/assets/pdf/standard_fonts/FoxitSerifBold.pfb +0 -0
- package/dist/site/assets/pdf/standard_fonts/FoxitSerifBoldItalic.pfb +0 -0
- package/dist/site/assets/pdf/standard_fonts/FoxitSerifItalic.pfb +0 -0
- package/dist/site/assets/pdf/standard_fonts/FoxitSymbol.pfb +0 -0
- package/dist/site/assets/pdf/standard_fonts/LICENSE_FOXIT +27 -0
- package/dist/site/assets/pdf/standard_fonts/LICENSE_LIBERATION +363 -0
- package/dist/site/assets/pdf/standard_fonts/LiberationSans-Bold.ttf +0 -0
- package/dist/site/assets/pdf/standard_fonts/LiberationSans-BoldItalic.ttf +0 -0
- package/dist/site/assets/pdf/standard_fonts/LiberationSans-Italic.ttf +0 -0
- package/dist/site/assets/pdf/standard_fonts/LiberationSans-Regular.ttf +0 -0
- package/dist/site/assets/pdf/wasm/LICENSE_JBIG2 +196 -0
- package/dist/site/assets/pdf/wasm/LICENSE_OPENJPEG +39 -0
- package/dist/site/assets/pdf/wasm/LICENSE_PDFJS_JBIG2 +13 -0
- package/dist/site/assets/pdf/wasm/LICENSE_PDFJS_OPENJPEG +22 -0
- package/dist/site/assets/pdf/wasm/LICENSE_PDFJS_QCMS +19 -0
- package/dist/site/assets/pdf/wasm/LICENSE_QCMS +21 -0
- package/dist/site/assets/pdf/wasm/jbig2.wasm +0 -0
- package/dist/site/assets/pdf/wasm/jbig2_nowasm_fallback.js +15 -0
- package/dist/site/assets/pdf/wasm/openjpeg.wasm +0 -0
- package/dist/site/assets/pdf/wasm/openjpeg_nowasm_fallback.js +17 -0
- package/dist/site/assets/pdf/wasm/qcms_bg.wasm +0 -0
- package/dist/site/assets/pdf/wasm/quickjs-eval.js +16 -0
- package/dist/site/assets/pdf/wasm/quickjs-eval.wasm +0 -0
- package/dist/site/assets/pdf.worker.min.mjs +29 -0
- package/dist/site/assets/workspace.worker.js +409 -0
- package/dist/site/index.html +253 -0
- package/dist/sync/baseline.js +45 -0
- package/dist/sync/changes.js +51 -0
- package/dist/sync/diff.js +80 -0
- package/dist/sync/fetch.js +287 -0
- package/dist/sync/ignore.js +4 -4
- package/dist/sync/local-state.js +12 -0
- package/dist/sync/mapping.js +31 -8
- package/dist/sync/metadata.js +57 -0
- package/dist/sync/prepare.js +52 -0
- package/dist/sync/pull.js +618 -141
- package/dist/sync/push.js +262 -207
- package/dist/sync/reconcile.js +51 -0
- package/dist/sync/references.js +2 -2
- package/dist/sync/remap.js +17 -5
- package/dist/sync/render.js +3 -2
- package/dist/sync/restore.js +28 -0
- package/dist/sync/scope.js +47 -0
- package/dist/sync/staging.js +193 -0
- package/dist/sync/workspace.js +100 -0
- package/dist/web/assets/PDFJS-LICENSE.txt +177 -0
- package/dist/web/assets/app.css +1 -1
- package/dist/web/assets/app.js +3 -938
- package/dist/web/assets/chunks/chunk-TEW74QPN.js +35 -0
- package/dist/web/assets/chunks/markdown-S6623E66.js +1 -0
- package/dist/web/assets/chunks/monaco-36ZGUH4Y.js +897 -0
- package/dist/web/assets/chunks/monaco-HFLEBJTI.css +1 -0
- package/dist/web/assets/chunks/pdf-DDN7TXTP.js +55 -0
- package/dist/web/assets/confluence-env-example.txt +32 -0
- package/dist/web/assets/editor.worker.js +1 -30
- package/dist/web/assets/favicon.svg +3 -0
- package/dist/web/assets/guide/01-account-settings.png +0 -0
- package/dist/web/assets/guide/02-atlassian-security.png +0 -0
- package/dist/web/assets/guide/03-api-tokens.png +0 -0
- package/dist/web/assets/guide/04-scope-api-token-step1.png +0 -0
- package/dist/web/assets/guide/05-scope-api-token-step2.png +0 -0
- package/dist/web/assets/guide/06-scope-api-token-step3.png +0 -0
- package/dist/web/assets/guide/07-scope-api-token-step4.png +0 -0
- package/dist/web/assets/guide/web/document-preview.png +0 -0
- package/dist/web/assets/guide/web/remote-diff.png +0 -0
- package/dist/web/assets/guide/web/sync-workspace.png +0 -0
- package/dist/web/assets/pdf/cmaps/78-EUC-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/78-EUC-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/78-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/78-RKSJ-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/78-RKSJ-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/78-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/78ms-RKSJ-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/78ms-RKSJ-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/83pv-RKSJ-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/90ms-RKSJ-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/90ms-RKSJ-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/90msp-RKSJ-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/90msp-RKSJ-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/90pv-RKSJ-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/90pv-RKSJ-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Add-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Add-RKSJ-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Add-RKSJ-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Add-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-CNS1-0.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-CNS1-1.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-CNS1-2.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-CNS1-3.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-CNS1-4.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-CNS1-5.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-CNS1-6.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-CNS1-UCS2.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-GB1-0.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-GB1-1.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-GB1-2.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-GB1-3.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-GB1-4.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-GB1-5.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-GB1-UCS2.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-Japan1-0.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-Japan1-1.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-Japan1-2.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-Japan1-3.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-Japan1-4.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-Japan1-5.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-Japan1-6.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-Japan1-UCS2.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-Korea1-0.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-Korea1-1.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-Korea1-2.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-Korea1-UCS2.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/B5-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/B5-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/B5pc-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/B5pc-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/CNS-EUC-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/CNS-EUC-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/CNS1-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/CNS1-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/CNS2-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/CNS2-V.bcmap +3 -0
- package/dist/web/assets/pdf/cmaps/ETHK-B5-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/ETHK-B5-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/ETen-B5-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/ETen-B5-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/ETenms-B5-H.bcmap +3 -0
- package/dist/web/assets/pdf/cmaps/ETenms-B5-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/EUC-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/EUC-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Ext-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Ext-RKSJ-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Ext-RKSJ-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Ext-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/GB-EUC-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/GB-EUC-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/GB-H.bcmap +4 -0
- package/dist/web/assets/pdf/cmaps/GB-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/GBK-EUC-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/GBK-EUC-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/GBK2K-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/GBK2K-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/GBKp-EUC-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/GBKp-EUC-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/GBT-EUC-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/GBT-EUC-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/GBT-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/GBT-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/GBTpc-EUC-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/GBTpc-EUC-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/GBpc-EUC-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/GBpc-EUC-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/HKdla-B5-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/HKdla-B5-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/HKdlb-B5-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/HKdlb-B5-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/HKgccs-B5-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/HKgccs-B5-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/HKm314-B5-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/HKm314-B5-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/HKm471-B5-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/HKm471-B5-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/HKscs-B5-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/HKscs-B5-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Hankaku.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Hiragana.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/KSC-EUC-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/KSC-EUC-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/KSC-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/KSC-Johab-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/KSC-Johab-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/KSC-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/KSCms-UHC-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/KSCms-UHC-HW-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/KSCms-UHC-HW-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/KSCms-UHC-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/KSCpc-EUC-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/KSCpc-EUC-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Katakana.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/LICENSE +36 -0
- package/dist/web/assets/pdf/cmaps/NWP-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/NWP-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/RKSJ-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/RKSJ-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Roman.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniCNS-UCS2-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniCNS-UCS2-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniCNS-UTF16-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniCNS-UTF16-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniCNS-UTF32-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniCNS-UTF32-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniCNS-UTF8-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniCNS-UTF8-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniGB-UCS2-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniGB-UCS2-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniGB-UTF16-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniGB-UTF16-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniGB-UTF32-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniGB-UTF32-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniGB-UTF8-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniGB-UTF8-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJIS-UCS2-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJIS-UCS2-HW-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJIS-UCS2-HW-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJIS-UCS2-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJIS-UTF16-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJIS-UTF16-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJIS-UTF32-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJIS-UTF32-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJIS-UTF8-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJIS-UTF8-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJIS2004-UTF16-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJIS2004-UTF16-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJIS2004-UTF32-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJIS2004-UTF32-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJIS2004-UTF8-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJIS2004-UTF8-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJISPro-UCS2-HW-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJISPro-UCS2-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJISPro-UTF8-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJISX0213-UTF32-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJISX0213-UTF32-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJISX02132004-UTF32-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJISX02132004-UTF32-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniKS-UCS2-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniKS-UCS2-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniKS-UTF16-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniKS-UTF16-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniKS-UTF32-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniKS-UTF32-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniKS-UTF8-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniKS-UTF8-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/WP-Symbol.bcmap +0 -0
- package/dist/web/assets/pdf/standard_fonts/FoxitDingbats.pfb +0 -0
- package/dist/web/assets/pdf/standard_fonts/FoxitFixed.pfb +0 -0
- package/dist/web/assets/pdf/standard_fonts/FoxitFixedBold.pfb +0 -0
- package/dist/web/assets/pdf/standard_fonts/FoxitFixedBoldItalic.pfb +0 -0
- package/dist/web/assets/pdf/standard_fonts/FoxitFixedItalic.pfb +0 -0
- package/dist/web/assets/pdf/standard_fonts/FoxitSerif.pfb +0 -0
- package/dist/web/assets/pdf/standard_fonts/FoxitSerifBold.pfb +0 -0
- package/dist/web/assets/pdf/standard_fonts/FoxitSerifBoldItalic.pfb +0 -0
- package/dist/web/assets/pdf/standard_fonts/FoxitSerifItalic.pfb +0 -0
- package/dist/web/assets/pdf/standard_fonts/FoxitSymbol.pfb +0 -0
- package/dist/web/assets/pdf/standard_fonts/LICENSE_FOXIT +27 -0
- package/dist/web/assets/pdf/standard_fonts/LICENSE_LIBERATION +363 -0
- package/dist/web/assets/pdf/standard_fonts/LiberationSans-Bold.ttf +0 -0
- package/dist/web/assets/pdf/standard_fonts/LiberationSans-BoldItalic.ttf +0 -0
- package/dist/web/assets/pdf/standard_fonts/LiberationSans-Italic.ttf +0 -0
- package/dist/web/assets/pdf/standard_fonts/LiberationSans-Regular.ttf +0 -0
- package/dist/web/assets/pdf/wasm/LICENSE_JBIG2 +196 -0
- package/dist/web/assets/pdf/wasm/LICENSE_OPENJPEG +39 -0
- package/dist/web/assets/pdf/wasm/LICENSE_PDFJS_JBIG2 +13 -0
- package/dist/web/assets/pdf/wasm/LICENSE_PDFJS_OPENJPEG +22 -0
- package/dist/web/assets/pdf/wasm/LICENSE_PDFJS_QCMS +19 -0
- package/dist/web/assets/pdf/wasm/LICENSE_QCMS +21 -0
- package/dist/web/assets/pdf/wasm/jbig2.wasm +0 -0
- package/dist/web/assets/pdf/wasm/jbig2_nowasm_fallback.js +15 -0
- package/dist/web/assets/pdf/wasm/openjpeg.wasm +0 -0
- package/dist/web/assets/pdf/wasm/openjpeg_nowasm_fallback.js +17 -0
- package/dist/web/assets/pdf/wasm/qcms_bg.wasm +0 -0
- package/dist/web/assets/pdf/wasm/quickjs-eval.js +16 -0
- package/dist/web/assets/pdf/wasm/quickjs-eval.wasm +0 -0
- package/dist/web/assets/pdf.worker.min.mjs +29 -0
- package/dist/web/assets/workspace.worker.js +409 -0
- package/dist/web/document-source.js +14 -0
- package/dist/web/hosted-files.js +189 -0
- package/dist/web/hosted.js +316 -0
- package/dist/web/node-runner.js +26 -0
- package/dist/web/preview.js +74 -0
- package/dist/web/server.js +85 -240
- package/dist/web/sync.js +365 -71
- package/dist/web/table-editor-script.js +99 -0
- package/dist/web/table-format.js +224 -0
- package/dist/web/views/help.js +15 -10
- package/dist/web/views/sync-panel.js +90 -19
- package/dist/web/views/token-guide.js +27 -0
- package/dist/web/views/usage-guide.js +12 -0
- package/dist/web/views/workspace.js +85 -15
- package/dist/web/workspace-api.js +371 -0
- package/docs/user/CLI.md +366 -0
- package/docs/user/CONFIGURATION.md +125 -0
- package/docs/user/OBSIDIAN.md +117 -0
- package/docs/user/WEB.md +90 -0
- package/docs/user/images/web/document-preview.png +0 -0
- package/docs/user/images/web/remote-diff.png +0 -0
- package/docs/user/images/web/sync-workspace.png +0 -0
- 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
|
+
}
|
package/dist/sync/ignore.js
CHANGED
|
@@ -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 {
|
|
6
|
+
import { ignorePath } from './metadata.js';
|
|
7
7
|
import ignore from 'ignore';
|
|
8
|
-
export
|
|
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 =
|
|
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
|
+
}
|
package/dist/sync/mapping.js
CHANGED
|
@@ -1,18 +1,41 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 매핑(파일 경로 ↔ pageId ↔ hash) 영속화.
|
|
3
|
-
* 기본 위치는 <base>/.
|
|
3
|
+
* 기본 위치는 <base>/.csync/mapping.json 이며 문서셋과 함께 버전관리된다.
|
|
4
4
|
*/
|
|
5
|
-
import { readFileSync, writeFileSync } from 'node:fs';
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
|
|
12
|
+
data = JSON.parse(readFileSync(path, 'utf8'));
|
|
11
13
|
}
|
|
12
|
-
catch {
|
|
13
|
-
|
|
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
|
-
|
|
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
|
+
}
|