@wonseok-han/confluence-sync 2.0.0 → 2.0.2

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.
@@ -75,9 +75,10 @@ export function validateEnv(text) { const env = parse(text); const base = env.CO
75
75
  /** Reject local link destinations that would make shared CLI code read server files.
76
76
  * Metadata-only Fetch does not follow document links; keep env/mapping checks.
77
77
  */
78
- export async function validateWorkspace(mounts, checkEnv = true, checkDocuments = true) {
78
+ export async function validateWorkspace(mounts, checkEnv = true, checkDocuments = true, documentRoots) {
79
79
  const parser = new MarkdownIt({ html: true });
80
- const entries = await snapshot(mounts);
80
+ // Existing output files are destinations, not input documents or workspace metadata.
81
+ const entries = await snapshot(mounts.filter((mount, index) => index === 0 || mount.readonly));
81
82
  const allowed = (file, target, webLink = false) => {
82
83
  try {
83
84
  target = target.replace(/^<(.*)>$/, '$1');
@@ -113,7 +114,7 @@ export async function validateWorkspace(mounts, checkEnv = true, checkDocuments
113
114
  for (const key of Object.keys(rel.endsWith('index.json') ? value.entries ?? {} : value))
114
115
  safeRelative(key.replace(/\/$/, ''));
115
116
  }
116
- if (!checkDocuments || !/\.md$/i.test(file) || rel.startsWith('.csync/'))
117
+ if (!checkDocuments || !/\.md$/i.test(file) || rel.startsWith('.csync/') || documentRoots && !documentRoots.some(root => within(root, file)))
117
118
  continue;
118
119
  const text = await readFile(file, 'utf8');
119
120
  const scan = (part) => {
@@ -266,7 +266,7 @@ export async function startHostedWeb(options = {}) {
266
266
  input.out = s.mounts[1].root;
267
267
  }
268
268
  if (!['/api/sync/config'].includes(url.pathname))
269
- await validateWorkspace(s.mounts, url.pathname.startsWith('/api/sync/') && !input.envId && !url.pathname.endsWith('/env/import'), url.pathname !== '/api/sync/fetch' && !(url.pathname === '/api/sync/staging' && input?.remoteOnly === true));
269
+ await validateWorkspace(s.mounts, url.pathname.startsWith('/api/sync/') && !input.envId && !url.pathname.endsWith('/env/import'), url.pathname !== '/api/sync/fetch' && !(url.pathname === '/api/sync/staging' && input?.remoteOnly === true), url.pathname === '/api/convert' ? (typeof input?.file === 'string' ? [input.file] : []) : undefined);
270
270
  const response = await fetch(s.core.url + url.pathname, { method: 'POST', headers: { 'X-Csync-Token': s.core.token, 'Content-Type': 'application/json' }, body: JSON.stringify(input) });
271
271
  const data = await response.json();
272
272
  send(response.status, publicValue(s, data));
package/dist/web/sync.js CHANGED
@@ -167,6 +167,8 @@ export function createWebSync(runner = runSyncCLI, initialImports = []) {
167
167
  args.push(target);
168
168
  if (kind === 'preview')
169
169
  args.push('--dry-run');
170
+ if (kind === 'fetch')
171
+ args.push('--refresh');
170
172
  if (kind === 'restore') {
171
173
  args.push(...prepared.selection.restorePaths);
172
174
  if (prepared.selection.restoreOverwrite)
package/docs/user/CLI.md CHANGED
@@ -39,8 +39,12 @@ csync push --dry-run --verify
39
39
  csync push
40
40
  ```
41
41
 
42
+ Obsidian이 같은 문서를 가리키는 위키링크를 볼트 전체 경로로 자동 갱신한 경우는 로컬 수정에서 제외합니다. 본문·표시 이름·앵커·연결 대상·첨부의 실제 변경은 계속 감지합니다. `diff --remote`는 연결된 원격 문서 링크를 로컬 경로와 같은 표기로 맞춰 비교합니다.
43
+
42
44
  `status`는 로컬 문서와 **저장된 마지막 Fetch 결과**를 함께 보여주며 API를 호출하거나 문서·매핑·선택을 저장하지 않습니다. 원격 조회 시각을 표시하고 조회 전에는 **원격 미확인**, 2분이 지나면 **이전 조회 결과**로 안내합니다. 현재 원격 확인은 `fetch`로 실행하세요. `받을 변경`은 Pull 대상, `양쪽 변경`은 비교 대상이며 재추가 필요·파일 누락·원격 조회 불가·기준 버전 미확인은 `확인 필요`로 구분합니다. `[추가됨]`·`[미선택]`은 업로드 선택 상태이며 원격 충돌 검사를 대신하지 않습니다. 로컬 삭제를 원격 삭제로 처리하지 않습니다.
43
45
 
46
+ 원격에서 상위 폴더를 옮기거나 이름을 바꾸면 문서 버전이 그대로여도 하위 문서를 받을 변경으로 표시합니다. Pull은 원격 폴더 이름도 로컬에 반영하며, 문서 파일명은 유지합니다. 폴더 메타데이터는 최대 100개씩 묶어 조회합니다. 웹의 수동 Fetch는 항상 최신을 조회하고, CLI에서 캐시를 우회하려면 `csync fetch --refresh`를 사용하세요. Pull은 이동 대상에 로컬 수정·삭제가 없고 새 경로가 비어 있을 때 폴더와 문서를 옮깁니다. 로컬 변경이 있으면 해당 폴더 전체를 보류하고, 적용 도중 실패하면 원래 위치로 복원합니다. 서로 다른 곳으로 이동한 중첩 폴더는 안전을 위해 자동 적용하지 않습니다.
47
+
44
48
  터미널에서는 받을 변경을 청록색, 보낼 변경을 파란색, 업로드에 추가한 문서를 초록색, 양쪽 변경·오류를 빨간색, 재확인 항목을 노란색으로 표시합니다. `diff`의 삭제 줄은 빨강, 추가 줄은 초록입니다. 문구와 기호도 유지하므로 색 없이 구분할 수 있습니다. 파이프·리다이렉트에서는 기본적으로 색을 제거하며 `--no-color` 또는 `NO_COLOR=1`로 끌 수 있습니다. `FORCE_COLOR=1`은 강제 활성, `FORCE_COLOR=0`은 비활성이고 명시적인 색상 끄기가 우선합니다.
45
49
 
46
50
  CLI 작업 중에는 시간 표시 없이 터미널 한 줄에 **로딩 표시·현재 단계**를 보여줍니다. 스피너는 별도 스레드에서 출력해 동기 파일 검사·Markdown 계산 중에도 움직이며, 완료·오류·중단 시 표시용 스레드를 정리합니다. Push의 전처리·원격 검증·문서 처리, Pull의 다운로드·기준 계산, Fetch의 조회 구간을 구분합니다. 전체 크기를 모르는 다운로드에는 진행률을 추정해 붙이지 않습니다. 로그는 그대로 남기고 완료·실패·중단 결과를 표시합니다. 파이프·리다이렉트·CI·단순 터미널에서는 회전 문자와 줄 덮어쓰기 대신 시작·종료 안내만 **stderr**에 남겨 stdout의 상태·diff 내용을 유지합니다. 도움말·버전·대화형 .env 입력과 웹 서버 대기에는 로딩을 표시하지 않습니다.
package/docs/user/WEB.md CHANGED
@@ -10,6 +10,8 @@
10
10
  - [표 서식 편집](#표-서식-편집)
11
11
  - [Confluence: 업로드(Push)와 가져오기(Pull)](#confluence-업로드push와-가져오기pull)
12
12
 
13
+ 문서 변환과 Confluence 탭의 기준폴더는 각각 설정합니다. 배포 웹은 탭 전환 시 화면을 다시 불러와 해당 탭에 저장된 폴더 연결을 복원합니다. 한 탭의 기준폴더 변경이나 연결 해제는 다른 탭에 적용되지 않습니다. 기존 공용 연결은 처음 연 탭에 유지되며, 다른 탭에서는 사용할 폴더를 선택하세요.
14
+
13
15
  ## 웹에서 변환·동기화하기
14
16
 
15
17
  **[웹 열기](https://confluence-sync-six.vercel.app/)** — CLI 설치 없이 브라우저에서 로컬 문서 폴더를 열어 변환·Fetch·Pull·Push를 사용할 수 있습니다. 데스크톱 Chrome·Edge에서 폴더의 읽기·쓰기 권한을 허용하세요. 직접 로컬 웹을 실행하려면 아래 명령을 사용합니다.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wonseok-han/confluence-sync",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "type": "module",
5
5
  "description": "Confluence Cloud ↔ Markdown 양방향 동기화 CLI — 마크다운을 Confluence 페이지로 발행(push)하고, Confluence 페이지·폴더·스페이스를 마크다운으로 가져옵니다(pull).",
6
6
  "keywords": [