sftp-push-sync 1.0.14 → 1.0.16

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.
@@ -48,7 +48,7 @@ const pkg = require("../package.json");
48
48
  // Colors for the State (works on dark + light background)
49
49
  const ADD = pc.green("+"); // Added
50
50
  const CHA = pc.yellow("~"); // Changed
51
- const DEL = pc.red("-"); // Deleted
51
+ const DEL = pc.red("-"); // Deleted
52
52
  const EXC = pc.redBright("-"); // Excluded
53
53
 
54
54
  const hr1 = () => "─".repeat(65); // horizontal line -
@@ -192,7 +192,7 @@ const CACHE_PATH = path.resolve(syncCacheName);
192
192
 
193
193
  let CACHE = {
194
194
  version: 1,
195
- local: {}, // key: "<TARGET>:<relPath>" -> { size, mtimeMs, hash }
195
+ local: {}, // key: "<TARGET>:<relPath>" -> { size, mtimeMs, hash }
196
196
  remote: {}, // key: "<TARGET>:<relPath>" -> { size, modifyTime, hash }
197
197
  };
198
198
 
@@ -242,11 +242,14 @@ let progressActive = false;
242
242
 
243
243
  function clearProgressLine() {
244
244
  if (!process.stdout.isTTY || !progressActive) return;
245
- const width = process.stdout.columns || 80;
246
- const blank = " ".repeat(width);
247
245
 
248
- // Beide Progress-Zeilen leeren
249
- process.stdout.write("\r" + blank + "\n" + blank + "\r");
246
+ // Zwei Progress-Zeilen ohne zusätzliche Newlines leeren:
247
+ // Cursor steht nach updateProgress2() auf der ersten Zeile.
248
+ process.stdout.write("\r"); // an Zeilenanfang
249
+ process.stdout.write("\x1b[2K"); // erste Zeile löschen
250
+ process.stdout.write("\x1b[1B"); // eine Zeile nach unten
251
+ process.stdout.write("\x1b[2K"); // zweite Zeile löschen
252
+ process.stdout.write("\x1b[1A"); // wieder nach oben
250
253
 
251
254
  progressActive = false;
252
255
  }
@@ -381,7 +384,7 @@ async function runTasks(items, workerCount, handler, label = "Tasks") {
381
384
  }
382
385
  done += 1;
383
386
  if (done % 10 === 0 || done === total) {
384
- updateProgress2(`${tab_a()}${label}: `, done, total);
387
+ updateProgress2(`${label}: `, done, total);
385
388
  }
386
389
  }
387
390
  }
@@ -426,7 +429,7 @@ async function walkLocal(root) {
426
429
  const chunk = IS_VERBOSE ? 1 : SCAN_CHUNK;
427
430
  if (scanned === 1 || scanned % chunk === 0) {
428
431
  // totally unknown → total = 0 → no automatic \n
429
- updateProgress2(`${tab_a()}Scan local: `, scanned, 0, rel);
432
+ updateProgress2("Scan local: ", scanned, 0, rel);
430
433
  }
431
434
  }
432
435
  }
@@ -436,7 +439,7 @@ async function walkLocal(root) {
436
439
 
437
440
  if (scanned > 0) {
438
441
  // last line + neat finish
439
- updateProgress2(`${tab_a()}Scan local: `, scanned, 0, "fertig");
442
+ updateProgress2("Scan local: ", scanned, 0, "fertig");
440
443
  process.stdout.write("\n");
441
444
  progressActive = false;
442
445
  }
@@ -477,16 +480,16 @@ async function walkRemote(sftp, remoteRoot) {
477
480
  scanned += 1;
478
481
  const chunk = IS_VERBOSE ? 1 : SCAN_CHUNK;
479
482
  if (scanned === 1 || scanned % chunk === 0) {
480
- updateProgress2(`${tab_a()}Scan remote: `, scanned, 0, rel);
483
+ updateProgress2("Scan remote: ", scanned, 0, rel);
481
484
  }
482
485
  }
483
486
  }
484
487
  }
485
488
 
486
- await recurse(remoteRoot);
489
+ await recurse(remoteRoot, "");
487
490
 
488
491
  if (scanned > 0) {
489
- updateProgress2(`${tab_a()}Scan remote: `, scanned, 0, "fertig");
492
+ updateProgress2("Scan remote: ", scanned, 0, "fertig");
490
493
  process.stdout.write("\n");
491
494
  progressActive = false;
492
495
  }
@@ -613,7 +616,8 @@ function describeSftpError(err) {
613
616
  async function main() {
614
617
  const start = Date.now();
615
618
 
616
- log(`\n\n${hr2()}`);
619
+ // Header-Abstand wie gehabt: zwei Leerzeilen davor
620
+ log("\n\n" + hr2());
617
621
  log(
618
622
  pc.bold(
619
623
  `🔐 SFTP Push-Synchronisation: sftp-push-sync v${pkg.version} [logLevel=${LOG_LEVEL}]`
@@ -634,7 +638,7 @@ async function main() {
634
638
  )
635
639
  );
636
640
  }
637
- log(`${hr1()}\n`);
641
+ log(hr1());
638
642
 
639
643
  const sftp = new SftpClient();
640
644
  let connected = false;
@@ -644,6 +648,7 @@ async function main() {
644
648
  const toDelete = [];
645
649
 
646
650
  try {
651
+ log("");
647
652
  log(pc.cyan("🔌 Connecting to SFTP server …"));
648
653
  await sftp.connect({
649
654
  host: CONNECTION.host,
@@ -662,6 +667,8 @@ async function main() {
662
667
  process.exit(1);
663
668
  }
664
669
 
670
+ // Phase 1 – mit exakt einer Leerzeile davor
671
+ log("");
665
672
  log(pc.bold(pc.cyan("📥 Phase 1: Scan local files …")));
666
673
  const local = await walkLocal(CONNECTION.localRoot);
667
674
  log(`${tab_a()}→ ${local.size} local files`);
@@ -675,9 +682,12 @@ async function main() {
675
682
  log("");
676
683
  }
677
684
 
685
+ // Phase 2 – auch mit einer Leerzeile davor
686
+ log("");
678
687
  log(pc.bold(pc.cyan("📤 Phase 2: Scan remote files …")));
679
688
  const remote = await walkRemote(sftp, CONNECTION.remoteRoot);
680
- log(`${tab_a()}→ ${remote.size} remote files\n`);
689
+ log(`${tab_a()}→ ${remote.size} remote files`);
690
+ log("");
681
691
 
682
692
  const localKeys = new Set(local.keys());
683
693
  const remoteKeys = new Set(remote.keys());
@@ -696,7 +706,7 @@ async function main() {
696
706
  checkedCount % chunk === 0 ||
697
707
  checkedCount === totalToCheck
698
708
  ) {
699
- updateProgress2(" Analyse: ", checkedCount, totalToCheck, rel);
709
+ updateProgress2("Analyse: ", checkedCount, totalToCheck, rel);
700
710
  }
701
711
 
702
712
  const l = local.get(rel);
@@ -778,12 +788,12 @@ async function main() {
778
788
 
779
789
  // Wenn Phase 3 nichts gefunden hat, explizit sagen
780
790
  if (toAdd.length === 0 && toUpdate.length === 0) {
791
+ log("");
781
792
  log(`${tab_a()}No differences found. Everything is up to date.`);
782
793
  }
783
794
 
784
- log(
785
- "\n" + pc.bold(pc.cyan("🧹 Phase 4: Removing orphaned remote files …"))
786
- );
795
+ log("");
796
+ log(pc.bold(pc.cyan("🧹 Phase 4: Removing orphaned remote files …")));
787
797
  for (const rel of remoteKeys) {
788
798
  if (!localKeys.has(rel)) {
789
799
  const r = remote.get(rel);
@@ -804,7 +814,8 @@ async function main() {
804
814
  // -------------------------------------------------------------------
805
815
 
806
816
  if (!DRY_RUN) {
807
- log("\n" + pc.bold(pc.cyan("🚚 Phase 5: Apply changes …")));
817
+ log("");
818
+ log(pc.bold(pc.cyan("🚚 Phase 5: Apply changes …")));
808
819
 
809
820
  // Upload new files
810
821
  await runTasks(
@@ -856,89 +867,23 @@ async function main() {
856
867
  "Deletes"
857
868
  );
858
869
  } else {
870
+ log("");
859
871
  log(
860
872
  pc.yellow(
861
- "\n💡 DRY-RUN: Connection tested, no files transferred or deleted."
873
+ "💡 DRY-RUN: Connection tested, no files transferred or deleted."
862
874
  )
863
875
  );
864
876
  }
865
877
 
866
- // -------------------------------------------------------------------
867
- // Phase 6: optional uploadList / downloadList
868
- // -------------------------------------------------------------------
869
-
870
- if (RUN_UPLOAD_LIST && UPLOAD_LIST.length > 0) {
871
- log(
872
- "\n" +
873
- pc.bold(pc.cyan("⬆️ Extra Phase: Upload-List (explicit files) …"))
874
- );
875
-
876
- const tasks = UPLOAD_LIST.map((rel) => ({
877
- rel,
878
- localPath: path.join(CONNECTION.localRoot, rel),
879
- remotePath: path.posix.join(CONNECTION.remoteRoot, toPosix(rel)),
880
- }));
881
-
882
- if (DRY_RUN) {
883
- for (const t of tasks) {
884
- log(`${tab_a()}${ADD} would upload (uploadList): ${t.rel}`);
885
- }
886
- } else {
887
- await runTasks(
888
- tasks,
889
- CONNECTION.workers,
890
- async ({ localPath, remotePath, rel }) => {
891
- const remoteDir = path.posix.dirname(remotePath);
892
- try {
893
- await sftp.mkdir(remoteDir, true);
894
- } catch {
895
- // ignore
896
- }
897
- await sftp.put(localPath, remotePath);
898
- log(`${tab_a()}${ADD} uploadList: ${rel}`);
899
- },
900
- "Upload-List"
901
- );
902
- }
903
- }
904
-
905
- if (RUN_DOWNLOAD_LIST && DOWNLOAD_LIST.length > 0) {
906
- log(
907
- "\n" +
908
- pc.bold(pc.cyan("⬇️ Extra Phase: Download-List (explicit files) …"))
909
- );
910
-
911
- const tasks = DOWNLOAD_LIST.map((rel) => ({
912
- rel,
913
- remotePath: path.posix.join(CONNECTION.remoteRoot, toPosix(rel)),
914
- localPath: path.join(CONNECTION.localRoot, rel),
915
- }));
916
-
917
- if (DRY_RUN) {
918
- for (const t of tasks) {
919
- log(`${tab_a()}${ADD} would download (downloadList): ${t.rel}`);
920
- }
921
- } else {
922
- await runTasks(
923
- tasks,
924
- CONNECTION.workers,
925
- async ({ remotePath, localPath, rel }) => {
926
- await fsp.mkdir(path.dirname(localPath), { recursive: true });
927
- await sftp.fastGet(remotePath, localPath);
928
- log(`${tab_a()}${ADD} downloadList: ${rel}`);
929
- },
930
- "Download-List"
931
- );
932
- }
933
- }
934
-
935
878
  const duration = ((Date.now() - start) / 1000).toFixed(2);
936
879
 
937
880
  // Write cache safely at the end
938
881
  await saveCache(true);
939
882
 
940
883
  // Summary
941
- log("\n" + pc.bold(pc.cyan("📊 Summary:")));
884
+ log(hr1());
885
+ log("");
886
+ log(pc.bold(pc.cyan("📊 Summary:")));
942
887
  log(`${tab_a()}Duration: ${pc.green(duration + " s")}`);
943
888
  log(`${tab_a()}${ADD} Added : ${toAdd.length}`);
944
889
  log(`${tab_a()}${CHA} Changed: ${toUpdate.length}`);
@@ -949,7 +894,8 @@ async function main() {
949
894
  );
950
895
  }
951
896
  if (toAdd.length || toUpdate.length || toDelete.length) {
952
- log("\n📄 Changes:");
897
+ log("");
898
+ log("📄 Changes:");
953
899
  [...toAdd.map((t) => t.rel)]
954
900
  .sort()
955
901
  .forEach((f) => console.log(`${tab_a()}${ADD} ${f}`));
@@ -960,10 +906,12 @@ async function main() {
960
906
  .sort()
961
907
  .forEach((f) => console.log(`${tab_a()}${DEL} ${f}`));
962
908
  } else {
963
- log("\nNo changes.");
909
+ log("");
910
+ log("No changes.");
964
911
  }
965
912
 
966
- log("\n" + pc.bold(pc.green("✅ Sync complete.")));
913
+ log("");
914
+ log(pc.bold(pc.green("✅ Sync complete.")));
967
915
  } catch (err) {
968
916
  const hint = describeSftpError(err);
969
917
  elog(pc.red("❌ Synchronisation error:"), err.message || err);
@@ -993,7 +941,10 @@ async function main() {
993
941
  );
994
942
  }
995
943
  }
996
- log(`${hr2()}\n\n`);
944
+
945
+ // Abschlusslinie + eine Leerzeile dahinter
946
+ log(hr2());
947
+ log("");
997
948
  }
998
949
 
999
950
  main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sftp-push-sync",
3
- "version": "1.0.14",
3
+ "version": "1.0.16",
4
4
  "description": "SFTP sync tool for Hugo projects (local to remote, with hash cache)",
5
5
  "type": "module",
6
6
  "bin": {