@narumitw/pi-sync 0.50.2 → 0.51.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 (218) hide show
  1. package/README.md +4 -1
  2. package/dist/chunks/{chunk-6LP3MCR2.ts → chunk-2JZFHNLM.ts} +3 -5
  3. package/dist/chunks/chunk-2JZFHNLM.ts.map +7 -0
  4. package/dist/chunks/{chunk-LCRI3ABW.ts → chunk-2RZSQWBH.ts} +8 -19
  5. package/dist/chunks/chunk-2RZSQWBH.ts.map +7 -0
  6. package/dist/chunks/{chunk-IJ6FJFA5.ts → chunk-4AIHXRKE.ts} +11 -17
  7. package/dist/chunks/chunk-4AIHXRKE.ts.map +7 -0
  8. package/dist/chunks/{chunk-C7XY5NF5.ts → chunk-4MPIH46N.ts} +29 -51
  9. package/dist/chunks/chunk-4MPIH46N.ts.map +7 -0
  10. package/dist/chunks/{chunk-5B3FXBLE.ts → chunk-6546TS54.ts} +16 -20
  11. package/dist/chunks/chunk-6546TS54.ts.map +7 -0
  12. package/dist/chunks/{chunk-Q2TUM5X6.ts → chunk-6NT3YFGY.ts} +2 -2
  13. package/dist/chunks/chunk-6NT3YFGY.ts.map +7 -0
  14. package/dist/chunks/{chunk-2B2ELUF5.ts → chunk-7D5SF7P3.ts} +1 -1
  15. package/dist/chunks/chunk-7D5SF7P3.ts.map +7 -0
  16. package/dist/chunks/{chunk-4CHTZMEO.ts → chunk-7KHKWL3Q.ts} +37 -55
  17. package/dist/chunks/chunk-7KHKWL3Q.ts.map +7 -0
  18. package/dist/chunks/{chunk-ZI5MXMEX.ts → chunk-BVVP33U5.ts} +2 -4
  19. package/dist/chunks/chunk-BVVP33U5.ts.map +7 -0
  20. package/dist/chunks/{chunk-SZWBGPID.ts → chunk-DM4X4FIA.ts} +8 -13
  21. package/dist/chunks/chunk-DM4X4FIA.ts.map +7 -0
  22. package/dist/chunks/{chunk-SL64RLXR.ts → chunk-DZUSTGQV.ts} +8 -16
  23. package/dist/chunks/chunk-DZUSTGQV.ts.map +7 -0
  24. package/dist/chunks/{chunk-G4RNQQZS.ts → chunk-E42GXD6D.ts} +13 -19
  25. package/dist/chunks/chunk-E42GXD6D.ts.map +7 -0
  26. package/dist/chunks/{chunk-Z4DQF6L3.ts → chunk-ECYCNZOE.ts} +1 -1
  27. package/dist/chunks/chunk-ECYCNZOE.ts.map +7 -0
  28. package/dist/chunks/{chunk-OEX4ABY6.ts → chunk-EEJPYTWE.ts} +11 -21
  29. package/dist/chunks/chunk-EEJPYTWE.ts.map +7 -0
  30. package/dist/chunks/{chunk-CPEHGXJV.ts → chunk-G4RUXRWZ.ts} +3 -5
  31. package/dist/chunks/chunk-G4RUXRWZ.ts.map +7 -0
  32. package/dist/chunks/{chunk-T2B5IU74.ts → chunk-HGJH56KR.ts} +4 -4
  33. package/dist/chunks/chunk-HGJH56KR.ts.map +7 -0
  34. package/dist/chunks/{chunk-JAAKLL6M.ts → chunk-IVXEDPAD.ts} +10 -34
  35. package/dist/chunks/chunk-IVXEDPAD.ts.map +7 -0
  36. package/dist/chunks/chunk-K3RIEL6J.ts +20 -0
  37. package/dist/chunks/chunk-K3RIEL6J.ts.map +7 -0
  38. package/dist/chunks/{chunk-LTBWGSBV.ts → chunk-MSSW6F3R.ts} +1 -1
  39. package/dist/chunks/chunk-MSSW6F3R.ts.map +7 -0
  40. package/dist/chunks/{chunk-T3HH3UEJ.ts → chunk-OVDTF5CH.ts} +4 -10
  41. package/dist/chunks/chunk-OVDTF5CH.ts.map +7 -0
  42. package/dist/chunks/{chunk-NJFUIVYL.ts → chunk-PWMWOJP4.ts} +1 -1
  43. package/dist/chunks/chunk-PWMWOJP4.ts.map +7 -0
  44. package/dist/chunks/{chunk-HUDQATCL.ts → chunk-Q32CQWPM.ts} +6 -8
  45. package/dist/chunks/chunk-Q32CQWPM.ts.map +7 -0
  46. package/dist/chunks/{chunk-ETTRJDNP.ts → chunk-QLSN7AGX.ts} +6 -13
  47. package/dist/chunks/chunk-QLSN7AGX.ts.map +7 -0
  48. package/dist/chunks/{chunk-THMF4BVL.ts → chunk-SWUXA3MH.ts} +1 -1
  49. package/dist/chunks/chunk-SWUXA3MH.ts.map +7 -0
  50. package/dist/chunks/{chunk-TEN2ZBJV.ts → chunk-T2SJD3N7.ts} +2 -4
  51. package/dist/chunks/chunk-T2SJD3N7.ts.map +7 -0
  52. package/dist/chunks/{chunk-AUSDLD6F.ts → chunk-T56RNRZJ.ts} +10 -26
  53. package/dist/chunks/chunk-T56RNRZJ.ts.map +7 -0
  54. package/dist/chunks/{chunk-KW7YQEZN.ts → chunk-UA7RXOIT.ts} +48 -100
  55. package/dist/chunks/chunk-UA7RXOIT.ts.map +7 -0
  56. package/dist/chunks/{chunk-D5X27LCA.ts → chunk-UETBKTDI.ts} +1 -1
  57. package/dist/chunks/chunk-UETBKTDI.ts.map +7 -0
  58. package/dist/chunks/{chunk-CHH26D4F.ts → chunk-YFADF5ZQ.ts} +7 -6
  59. package/dist/chunks/chunk-YFADF5ZQ.ts.map +7 -0
  60. package/dist/chunks/{chunk-CCSN3F47.ts → chunk-YPXGU5OR.ts} +4 -4
  61. package/dist/chunks/chunk-YPXGU5OR.ts.map +7 -0
  62. package/dist/chunks/{file-selection-FTTKAOYX.ts → file-selection-RAVHRYFG.ts} +14 -26
  63. package/dist/chunks/file-selection-RAVHRYFG.ts.map +7 -0
  64. package/dist/chunks/{git-backend-GMVS4PD4.ts → git-backend-IAANDL3K.ts} +41 -104
  65. package/dist/chunks/git-backend-IAANDL3K.ts.map +7 -0
  66. package/dist/chunks/manager-result-dispatcher-OTUMQM26.ts +23 -0
  67. package/dist/chunks/{manager-ui-5KLKVMRC.ts → manager-ui-763NEKJY.ts} +124 -251
  68. package/dist/chunks/manager-ui-763NEKJY.ts.map +7 -0
  69. package/dist/chunks/remote-selection-ui-TZTJF32Y.ts +22 -0
  70. package/dist/chunks/{s3-backend-66O4FGBH.ts → s3-backend-XGXUOPUN.ts} +23 -54
  71. package/dist/chunks/s3-backend-XGXUOPUN.ts.map +7 -0
  72. package/dist/chunks/{setup-switch-IS2PEZ3J.ts → setup-switch-5G2VNJHL.ts} +9 -9
  73. package/dist/chunks/{setup-wizard-IUB6ESEU.ts → setup-wizard-YIEWGX32.ts} +12 -12
  74. package/dist/chunks/setup-wizard-YIEWGX32.ts.map +7 -0
  75. package/dist/chunks/{snapshot-HKEWCVK6.ts → snapshot-JL5DFSXI.ts} +5 -5
  76. package/dist/chunks/sync-inspection-P3ENJLNF.ts +20 -0
  77. package/dist/chunks/{sync-operations-LBX3GKCM.ts → sync-operations-AR76EB6C.ts} +85 -201
  78. package/dist/chunks/sync-operations-AR76EB6C.ts.map +7 -0
  79. package/dist/chunks/{sync-state-Q2FTXTW6.ts → sync-state-VHJUZ4YG.ts} +6 -6
  80. package/dist/chunks/{webdav-backend-ECIKD7SO.ts → webdav-backend-3EGUONAT.ts} +20 -51
  81. package/dist/chunks/webdav-backend-3EGUONAT.ts.map +7 -0
  82. package/dist/index.ts +49 -74
  83. package/dist/index.ts.map +3 -3
  84. package/docs/settings.md +9 -0
  85. package/package.json +61 -62
  86. package/src/backends/backend-factory.ts +15 -15
  87. package/src/backends/backend-identity.ts +31 -31
  88. package/src/backends/backend-types.ts +42 -42
  89. package/src/backends/git/git-backend.ts +714 -788
  90. package/src/backends/git/git-config.ts +104 -111
  91. package/src/backends/git/git-runner.ts +230 -247
  92. package/src/backends/git/git-storage.ts +228 -245
  93. package/src/backends/s3/s3-backend.ts +408 -441
  94. package/src/backends/s3/s3-client.ts +288 -333
  95. package/src/backends/sync-backend.ts +65 -65
  96. package/src/backends/webdav/webdav-backend.ts +542 -589
  97. package/src/backends/webdav/webdav-client.ts +292 -316
  98. package/src/backends/webdav/webdav-config.ts +57 -68
  99. package/src/commands/command-execution.ts +270 -289
  100. package/src/commands/command-handler.ts +160 -163
  101. package/src/commands/command-types.ts +13 -13
  102. package/src/commands/command.ts +190 -196
  103. package/src/commands/setup-completions.ts +1 -1
  104. package/src/paths.ts +47 -47
  105. package/src/settings/config-errors.ts +4 -5
  106. package/src/settings/config-file.ts +473 -506
  107. package/src/settings/config.ts +171 -174
  108. package/src/settings/settings-management.ts +191 -216
  109. package/src/settings/settings-store.ts +63 -77
  110. package/src/settings/settings-types.ts +78 -75
  111. package/src/settings/settings-validation.ts +328 -364
  112. package/src/snapshot/session-paths.ts +41 -55
  113. package/src/snapshot/snapshot-apply.ts +197 -229
  114. package/src/snapshot/snapshot-codec.ts +29 -31
  115. package/src/snapshot/snapshot-paths.ts +4 -6
  116. package/src/snapshot/snapshot-transaction.ts +161 -170
  117. package/src/snapshot/snapshot-types.ts +24 -24
  118. package/src/snapshot/snapshot.ts +310 -343
  119. package/src/state/json-file.ts +11 -11
  120. package/src/state/lock.ts +226 -242
  121. package/src/state/lockfile-fs.ts +20 -20
  122. package/src/state/operation-availability.ts +36 -41
  123. package/src/state/state-directory.ts +171 -173
  124. package/src/state/state-types.ts +15 -15
  125. package/src/state/sync-state-store.ts +23 -23
  126. package/src/sync/automatic-sync.ts +75 -80
  127. package/src/sync/remote-snapshot.ts +52 -60
  128. package/src/sync/setup-switch.ts +99 -114
  129. package/src/sync/signals.ts +3 -5
  130. package/src/sync/startup-check.ts +97 -111
  131. package/src/sync/sync-decision.ts +76 -80
  132. package/src/sync/sync-error-guidance.ts +41 -48
  133. package/src/sync/sync-errors.ts +10 -10
  134. package/src/sync/sync-inspection.ts +29 -33
  135. package/src/sync/sync-loaders.ts +28 -36
  136. package/src/sync/sync-mutations.ts +510 -622
  137. package/src/sync/sync-operations.ts +7 -7
  138. package/src/sync/sync-policy.ts +296 -326
  139. package/src/sync/sync-queries.ts +175 -194
  140. package/src/sync/sync-state.ts +74 -100
  141. package/src/sync-extension.ts +89 -101
  142. package/src/sync.ts +26 -26
  143. package/src/types.ts +37 -37
  144. package/src/ui/automatic-sync-summary.ts +1 -3
  145. package/src/ui/cancellable-operation.ts +92 -112
  146. package/src/ui/file-selection.ts +250 -274
  147. package/src/ui/manager-attention.ts +51 -63
  148. package/src/ui/manager-recovery.ts +48 -57
  149. package/src/ui/manager-result-dispatcher.ts +62 -76
  150. package/src/ui/manager-state.ts +200 -232
  151. package/src/ui/manager-ui.ts +264 -362
  152. package/src/ui/remote-selection-ui.ts +547 -588
  153. package/src/ui/secret-input.ts +198 -214
  154. package/src/ui/settings-ui.ts +175 -172
  155. package/src/ui/setup/git-ui.ts +232 -261
  156. package/src/ui/setup/s3-credentials-ui.ts +58 -65
  157. package/src/ui/setup/s3-ui.ts +171 -195
  158. package/src/ui/setup/setup-actions.ts +91 -101
  159. package/src/ui/setup/setup-location-ui.ts +121 -137
  160. package/src/ui/setup/setup-name-ui.ts +27 -27
  161. package/src/ui/setup/setup-prompts.ts +65 -65
  162. package/src/ui/setup/setup-review.ts +58 -59
  163. package/src/ui/setup/setup-switcher.ts +156 -160
  164. package/src/ui/setup/setup-wizard.ts +28 -28
  165. package/src/ui/setup/text-input.ts +54 -58
  166. package/src/ui/setup/webdav-ui.ts +246 -293
  167. package/src/ui/storage-connections-ui.ts +317 -369
  168. package/src/ui/sync-attention.ts +148 -164
  169. package/src/ui/sync-format.ts +110 -121
  170. package/src/ui/sync-resolution-ui.ts +147 -153
  171. package/src/ui/sync-setups-ui.ts +218 -238
  172. package/src/ui/sync-status.ts +17 -0
  173. package/src/ui/terminal-text.ts +2 -2
  174. package/dist/chunks/chunk-2B2ELUF5.ts.map +0 -7
  175. package/dist/chunks/chunk-4CHTZMEO.ts.map +0 -7
  176. package/dist/chunks/chunk-5B3FXBLE.ts.map +0 -7
  177. package/dist/chunks/chunk-6LP3MCR2.ts.map +0 -7
  178. package/dist/chunks/chunk-AUSDLD6F.ts.map +0 -7
  179. package/dist/chunks/chunk-C7XY5NF5.ts.map +0 -7
  180. package/dist/chunks/chunk-CCSN3F47.ts.map +0 -7
  181. package/dist/chunks/chunk-CHH26D4F.ts.map +0 -7
  182. package/dist/chunks/chunk-CPEHGXJV.ts.map +0 -7
  183. package/dist/chunks/chunk-D5X27LCA.ts.map +0 -7
  184. package/dist/chunks/chunk-ETTRJDNP.ts.map +0 -7
  185. package/dist/chunks/chunk-G4RNQQZS.ts.map +0 -7
  186. package/dist/chunks/chunk-HUDQATCL.ts.map +0 -7
  187. package/dist/chunks/chunk-IJ6FJFA5.ts.map +0 -7
  188. package/dist/chunks/chunk-JAAKLL6M.ts.map +0 -7
  189. package/dist/chunks/chunk-KW7YQEZN.ts.map +0 -7
  190. package/dist/chunks/chunk-LCRI3ABW.ts.map +0 -7
  191. package/dist/chunks/chunk-LTBWGSBV.ts.map +0 -7
  192. package/dist/chunks/chunk-NJFUIVYL.ts.map +0 -7
  193. package/dist/chunks/chunk-OEX4ABY6.ts.map +0 -7
  194. package/dist/chunks/chunk-Q2TUM5X6.ts.map +0 -7
  195. package/dist/chunks/chunk-SL64RLXR.ts.map +0 -7
  196. package/dist/chunks/chunk-SZWBGPID.ts.map +0 -7
  197. package/dist/chunks/chunk-T2B5IU74.ts.map +0 -7
  198. package/dist/chunks/chunk-T3HH3UEJ.ts.map +0 -7
  199. package/dist/chunks/chunk-TEN2ZBJV.ts.map +0 -7
  200. package/dist/chunks/chunk-THMF4BVL.ts.map +0 -7
  201. package/dist/chunks/chunk-Z4DQF6L3.ts.map +0 -7
  202. package/dist/chunks/chunk-ZI5MXMEX.ts.map +0 -7
  203. package/dist/chunks/file-selection-FTTKAOYX.ts.map +0 -7
  204. package/dist/chunks/git-backend-GMVS4PD4.ts.map +0 -7
  205. package/dist/chunks/manager-result-dispatcher-FKIBM3GO.ts +0 -22
  206. package/dist/chunks/manager-ui-5KLKVMRC.ts.map +0 -7
  207. package/dist/chunks/remote-selection-ui-D7RBRJNL.ts +0 -21
  208. package/dist/chunks/s3-backend-66O4FGBH.ts.map +0 -7
  209. package/dist/chunks/setup-wizard-IUB6ESEU.ts.map +0 -7
  210. package/dist/chunks/sync-inspection-CST2KL65.ts +0 -20
  211. package/dist/chunks/sync-operations-LBX3GKCM.ts.map +0 -7
  212. package/dist/chunks/webdav-backend-ECIKD7SO.ts.map +0 -7
  213. /package/dist/chunks/{manager-result-dispatcher-FKIBM3GO.ts.map → manager-result-dispatcher-OTUMQM26.ts.map} +0 -0
  214. /package/dist/chunks/{remote-selection-ui-D7RBRJNL.ts.map → remote-selection-ui-TZTJF32Y.ts.map} +0 -0
  215. /package/dist/chunks/{setup-switch-IS2PEZ3J.ts.map → setup-switch-5G2VNJHL.ts.map} +0 -0
  216. /package/dist/chunks/{snapshot-HKEWCVK6.ts.map → snapshot-JL5DFSXI.ts.map} +0 -0
  217. /package/dist/chunks/{sync-inspection-CST2KL65.ts.map → sync-inspection-P3ENJLNF.ts.map} +0 -0
  218. /package/dist/chunks/{sync-state-Q2FTXTW6.ts.map → sync-state-VHJUZ4YG.ts.map} +0 -0
@@ -2,136 +2,129 @@ const DEFAULT_GIT_BRANCH = "pi-sync";
2
2
  const DEFAULT_GIT_DIRECTORY = "pi-sync";
3
3
 
4
4
  export function normalizeGitRemote(value: string | undefined) {
5
- const normalized = normalizeOptionalString(value);
6
- if (!normalized) return undefined;
7
- if (
8
- hasControlCharacter(normalized) ||
9
- /\s/u.test(normalized) ||
10
- normalized.startsWith("-") ||
11
- normalized.includes("\\")
12
- ) {
13
- throw new Error("Invalid pi-sync Git remote.");
14
- }
15
- if (
16
- !normalized.includes("://") &&
17
- /^(?:[A-Za-z0-9._-]+@)?(?:\[[0-9A-Fa-f:]+\]|[A-Za-z0-9.-]+):(?!-)[^:].+$/u.test(normalized)
18
- ) {
19
- return normalized;
20
- }
21
- if (/^[A-Za-z][A-Za-z0-9+.-]*:/u.test(normalized) && !normalized.includes("://")) {
22
- throw new Error("Invalid pi-sync Git remote: unsupported transport or remote-helper syntax.");
23
- }
24
- let url: URL;
25
- try {
26
- url = new URL(normalized);
27
- } catch {
28
- throw new Error("Invalid pi-sync Git remote: use an SSH or HTTPS remote.");
29
- }
30
- if (url.protocol !== "https:" && url.protocol !== "ssh:") {
31
- throw new Error("Invalid pi-sync Git remote: only SSH and HTTPS are supported.");
32
- }
33
- if (url.password || (url.protocol === "https:" && url.username)) {
34
- throw new Error("Invalid pi-sync Git remote: URL credentials or userinfo are not allowed.");
35
- }
36
- if (url.search || url.hash || !url.hostname || !url.pathname || url.pathname === "/") {
37
- throw new Error("Invalid pi-sync Git remote.");
38
- }
39
- return url.toString();
5
+ const normalized = normalizeOptionalString(value);
6
+ if (!normalized) return undefined;
7
+ if (
8
+ hasControlCharacter(normalized) ||
9
+ /\s/u.test(normalized) ||
10
+ normalized.startsWith("-") ||
11
+ normalized.includes("\\")
12
+ ) {
13
+ throw new Error("Invalid pi-sync Git remote.");
14
+ }
15
+ if (
16
+ !normalized.includes("://") &&
17
+ /^(?:[A-Za-z0-9._-]+@)?(?:\[[0-9A-Fa-f:]+\]|[A-Za-z0-9.-]+):(?!-)[^:].+$/u.test(normalized)
18
+ ) {
19
+ return normalized;
20
+ }
21
+ if (/^[A-Za-z][A-Za-z0-9+.-]*:/u.test(normalized) && !normalized.includes("://")) {
22
+ throw new Error("Invalid pi-sync Git remote: unsupported transport or remote-helper syntax.");
23
+ }
24
+ let url: URL;
25
+ try {
26
+ url = new URL(normalized);
27
+ } catch {
28
+ throw new Error("Invalid pi-sync Git remote: use an SSH or HTTPS remote.");
29
+ }
30
+ if (url.protocol !== "https:" && url.protocol !== "ssh:") {
31
+ throw new Error("Invalid pi-sync Git remote: only SSH and HTTPS are supported.");
32
+ }
33
+ if (url.password || (url.protocol === "https:" && url.username)) {
34
+ throw new Error("Invalid pi-sync Git remote: URL credentials or userinfo are not allowed.");
35
+ }
36
+ if (url.search || url.hash || !url.hostname || !url.pathname || url.pathname === "/") {
37
+ throw new Error("Invalid pi-sync Git remote.");
38
+ }
39
+ return url.toString();
40
40
  }
41
41
 
42
42
  export function normalizeGitRemoteIdentity(value: string) {
43
- const normalized = normalizeGitRemote(value);
44
- if (!normalized) return "";
45
- if (!normalized.includes("://")) {
46
- const match =
47
- /^(?<user>[A-Za-z0-9._-]+@)?(?<host>\[[0-9A-Fa-f:]+\]|[A-Za-z0-9.-]+):(?<path>.+)$/u.exec(
48
- normalized,
49
- );
50
- if (!match?.groups) return normalized;
51
- const userAndHost = `${match.groups.user ?? ""}${match.groups.host.toLowerCase()}`;
52
- const remotePath = match.groups.path.replace(/\/+$/gu, "");
53
- return remotePath.startsWith("/")
54
- ? `scp-absolute://${userAndHost}${remotePath}`
55
- : `ssh://${userAndHost}/${remotePath}`;
56
- }
57
- const url = new URL(normalized);
58
- url.hostname = url.hostname.toLowerCase();
59
- if (url.protocol === "ssh:" && url.port === "22") url.port = "";
60
- url.pathname = url.pathname.replace(/\/+$/gu, "");
61
- if (url.protocol === "ssh:") {
62
- return `ssh://${url.username ? `${url.username}@` : ""}${url.host}${url.pathname}`;
63
- }
64
- return url.toString();
43
+ const normalized = normalizeGitRemote(value);
44
+ if (!normalized) return "";
45
+ if (!normalized.includes("://")) {
46
+ const match = /^(?<user>[A-Za-z0-9._-]+@)?(?<host>\[[0-9A-Fa-f:]+\]|[A-Za-z0-9.-]+):(?<path>.+)$/u.exec(normalized);
47
+ if (!match?.groups) return normalized;
48
+ const userAndHost = `${match.groups.user ?? ""}${match.groups.host.toLowerCase()}`;
49
+ const remotePath = match.groups.path.replace(/\/+$/gu, "");
50
+ return remotePath.startsWith("/")
51
+ ? `scp-absolute://${userAndHost}${remotePath}`
52
+ : `ssh://${userAndHost}/${remotePath}`;
53
+ }
54
+ const url = new URL(normalized);
55
+ url.hostname = url.hostname.toLowerCase();
56
+ if (url.protocol === "ssh:" && url.port === "22") url.port = "";
57
+ url.pathname = url.pathname.replace(/\/+$/gu, "");
58
+ if (url.protocol === "ssh:") {
59
+ return `ssh://${url.username ? `${url.username}@` : ""}${url.host}${url.pathname}`;
60
+ }
61
+ return url.toString();
65
62
  }
66
63
 
67
64
  export function normalizeGitBranch(value: string | undefined) {
68
- const branch = normalizeOptionalString(value) ?? DEFAULT_GIT_BRANCH;
69
- if (
70
- branch === "@" ||
71
- branch.startsWith("-") ||
72
- branch.startsWith("refs/") ||
73
- branch.startsWith("/") ||
74
- branch.endsWith("/") ||
75
- branch.endsWith(".") ||
76
- branch.includes("..") ||
77
- branch.includes("@{") ||
78
- branch.includes("\\") ||
79
- hasControlCharacter(branch) ||
80
- /[ ~^:?*[\]]/u.test(branch) ||
81
- branch
82
- .split("/")
83
- .some((segment) => !segment || segment.startsWith(".") || segment.endsWith(".lock"))
84
- ) {
85
- throw new Error("Invalid pi-sync Git branch.");
86
- }
87
- return branch;
65
+ const branch = normalizeOptionalString(value) ?? DEFAULT_GIT_BRANCH;
66
+ if (
67
+ branch === "@" ||
68
+ branch.startsWith("-") ||
69
+ branch.startsWith("refs/") ||
70
+ branch.startsWith("/") ||
71
+ branch.endsWith("/") ||
72
+ branch.endsWith(".") ||
73
+ branch.includes("..") ||
74
+ branch.includes("@{") ||
75
+ branch.includes("\\") ||
76
+ hasControlCharacter(branch) ||
77
+ /[ ~^:?*[\]]/u.test(branch) ||
78
+ branch.split("/").some((segment) => !segment || segment.startsWith(".") || segment.endsWith(".lock"))
79
+ ) {
80
+ throw new Error("Invalid pi-sync Git branch.");
81
+ }
82
+ return branch;
88
83
  }
89
84
 
90
85
  export function normalizeGitDirectory(value: string | undefined) {
91
- if (value?.trim() === "." || value?.trim() === "./") return "./";
92
- const directory = trimSlashes(normalizeOptionalString(value) ?? DEFAULT_GIT_DIRECTORY);
93
- if (
94
- !directory ||
95
- directory.startsWith("-") ||
96
- directory.includes("\\") ||
97
- hasControlCharacter(directory) ||
98
- directory
99
- .split("/")
100
- .some((segment) => !segment || segment === "." || segment === ".." || segment === ".git")
101
- ) {
102
- throw new Error("Invalid pi-sync Git directory.");
103
- }
104
- return directory;
86
+ if (value?.trim() === "." || value?.trim() === "./") return "./";
87
+ const directory = trimSlashes(normalizeOptionalString(value) ?? DEFAULT_GIT_DIRECTORY);
88
+ if (
89
+ !directory ||
90
+ directory.startsWith("-") ||
91
+ directory.includes("\\") ||
92
+ hasControlCharacter(directory) ||
93
+ directory.split("/").some((segment) => !segment || segment === "." || segment === ".." || segment === ".git")
94
+ ) {
95
+ throw new Error("Invalid pi-sync Git directory.");
96
+ }
97
+ return directory;
105
98
  }
106
99
 
107
100
  export function validateGitNamespace(value: string) {
108
- if (
109
- value.length > 256 ||
110
- value === "." ||
111
- value === ".." ||
112
- value.includes("/") ||
113
- value.includes("\\") ||
114
- hasControlCharacter(value)
115
- ) {
116
- throw new Error("Invalid pi-sync Git namespace.");
117
- }
101
+ if (
102
+ value.length > 256 ||
103
+ value === "." ||
104
+ value === ".." ||
105
+ value.includes("/") ||
106
+ value.includes("\\") ||
107
+ hasControlCharacter(value)
108
+ ) {
109
+ throw new Error("Invalid pi-sync Git namespace.");
110
+ }
118
111
  }
119
112
 
120
113
  function normalizeOptionalString(value: unknown) {
121
- if (value !== undefined && typeof value !== "string") {
122
- throw new Error("Invalid pi-sync settings: expected a string.");
123
- }
124
- const normalized = value?.trim();
125
- return normalized || undefined;
114
+ if (value !== undefined && typeof value !== "string") {
115
+ throw new Error("Invalid pi-sync settings: expected a string.");
116
+ }
117
+ const normalized = value?.trim();
118
+ return normalized || undefined;
126
119
  }
127
120
 
128
121
  function trimSlashes(value: string) {
129
- return value.replace(/^\/+|\/+$/gu, "");
122
+ return value.replace(/^\/+|\/+$/gu, "");
130
123
  }
131
124
 
132
125
  function hasControlCharacter(value: string) {
133
- return [...value].some((character) => {
134
- const code = character.codePointAt(0) ?? 0;
135
- return code < 0x20 || (code >= 0x7f && code <= 0x9f);
136
- });
126
+ return [...value].some((character) => {
127
+ const code = character.codePointAt(0) ?? 0;
128
+ return code < 0x20 || (code >= 0x7f && code <= 0x9f);
129
+ });
137
130
  }