kiriya 0.1.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 (282) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/LICENSE +21 -0
  3. package/README.md +87 -0
  4. package/dist/src/config/clean-rules.js +39 -0
  5. package/dist/src/config/config-keys.js +24 -0
  6. package/dist/src/config/dependency-directories.js +16 -0
  7. package/dist/src/config/modules.js +38 -0
  8. package/dist/src/config/protected-paths.js +32 -0
  9. package/dist/src/config/rebuildable-directories.js +20 -0
  10. package/dist/src/core/application/command-registry.js +57 -0
  11. package/dist/src/core/application/config-values.js +33 -0
  12. package/dist/src/core/application/path-guard.js +69 -0
  13. package/dist/src/core/application/paths.js +77 -0
  14. package/dist/src/core/application/plugin-loader.js +100 -0
  15. package/dist/src/core/application/process-ending.js +28 -0
  16. package/dist/src/core/application/root-scope.js +123 -0
  17. package/dist/src/core/application/safety.js +26 -0
  18. package/dist/src/core/application/text-input.js +44 -0
  19. package/dist/src/core/application/walk.js +34 -0
  20. package/dist/src/core/domain/command.js +8 -0
  21. package/dist/src/core/domain/encodings.js +70 -0
  22. package/dist/src/core/domain/errors.js +49 -0
  23. package/dist/src/core/domain/glob.js +57 -0
  24. package/dist/src/core/domain/global-options.js +10 -0
  25. package/dist/src/core/domain/input-schema.js +54 -0
  26. package/dist/src/core/domain/message.js +7 -0
  27. package/dist/src/core/domain/module.js +2 -0
  28. package/dist/src/core/domain/names.js +22 -0
  29. package/dist/src/core/domain/ports/clipboard.js +2 -0
  30. package/dist/src/core/domain/ports/clock.js +2 -0
  31. package/dist/src/core/domain/ports/command-catalog.js +2 -0
  32. package/dist/src/core/domain/ports/compression.js +2 -0
  33. package/dist/src/core/domain/ports/config-store.js +2 -0
  34. package/dist/src/core/domain/ports/confirmation.js +2 -0
  35. package/dist/src/core/domain/ports/environment.js +2 -0
  36. package/dist/src/core/domain/ports/file-content.js +2 -0
  37. package/dist/src/core/domain/ports/file-system.js +2 -0
  38. package/dist/src/core/domain/ports/hasher.js +2 -0
  39. package/dist/src/core/domain/ports/network.js +2 -0
  40. package/dist/src/core/domain/ports/opener.js +2 -0
  41. package/dist/src/core/domain/ports/passthrough.js +2 -0
  42. package/dist/src/core/domain/ports/plugin-inventory.js +2 -0
  43. package/dist/src/core/domain/ports/plugin-source.js +2 -0
  44. package/dist/src/core/domain/ports/port-table.js +2 -0
  45. package/dist/src/core/domain/ports/process-runner.js +2 -0
  46. package/dist/src/core/domain/ports/process-table.js +2 -0
  47. package/dist/src/core/domain/ports/protected-paths.js +2 -0
  48. package/dist/src/core/domain/ports/random-source.js +2 -0
  49. package/dist/src/core/domain/ports/standard-input.js +2 -0
  50. package/dist/src/core/domain/ports/system-info.js +2 -0
  51. package/dist/src/core/domain/ports/trash.js +2 -0
  52. package/dist/src/core/domain/secrets.js +59 -0
  53. package/dist/src/core/domain/targets.js +41 -0
  54. package/dist/src/core/domain/text-case.js +39 -0
  55. package/dist/src/core/domain/values/bytes.js +13 -0
  56. package/dist/src/core/domain/view.js +2 -0
  57. package/dist/src/core/infrastructure/node/can-listen.js +10 -0
  58. package/dist/src/core/infrastructure/node/closed-standard-input.adapter.js +12 -0
  59. package/dist/src/core/infrastructure/node/config-location.js +27 -0
  60. package/dist/src/core/infrastructure/node/fs-errors.js +22 -0
  61. package/dist/src/core/infrastructure/node/json-config-store.adapter.js +69 -0
  62. package/dist/src/core/infrastructure/node/node-compression.adapter.js +82 -0
  63. package/dist/src/core/infrastructure/node/node-environment.adapter.js +21 -0
  64. package/dist/src/core/infrastructure/node/node-file-content.adapter.js +99 -0
  65. package/dist/src/core/infrastructure/node/node-file-system.adapter.js +169 -0
  66. package/dist/src/core/infrastructure/node/node-hasher.adapter.js +32 -0
  67. package/dist/src/core/infrastructure/node/node-network.adapter.js +168 -0
  68. package/dist/src/core/infrastructure/node/node-plugin-source.adapter.js +89 -0
  69. package/dist/src/core/infrastructure/node/node-process-runner.adapter.js +48 -0
  70. package/dist/src/core/infrastructure/node/node-random-source.adapter.js +7 -0
  71. package/dist/src/core/infrastructure/node/node-standard-input.adapter.js +24 -0
  72. package/dist/src/core/infrastructure/node/node-system-info.adapter.js +63 -0
  73. package/dist/src/core/infrastructure/node/process-signals.js +51 -0
  74. package/dist/src/core/infrastructure/node/run-program.js +65 -0
  75. package/dist/src/core/infrastructure/node/system-clock.adapter.js +22 -0
  76. package/dist/src/core/infrastructure/node/typed-programs.js +45 -0
  77. package/dist/src/core/infrastructure/platform/linux/freedesktop-trash.adapter.js +115 -0
  78. package/dist/src/core/infrastructure/platform/linux/linux-clipboard.adapter.js +70 -0
  79. package/dist/src/core/infrastructure/platform/linux/linux-opener.adapter.js +21 -0
  80. package/dist/src/core/infrastructure/platform/linux/linux-port-table.adapter.js +97 -0
  81. package/dist/src/core/infrastructure/platform/linux/linux-process-table.adapter.js +61 -0
  82. package/dist/src/core/infrastructure/platform/macos/macos-clipboard.adapter.js +29 -0
  83. package/dist/src/core/infrastructure/platform/macos/macos-opener.adapter.js +13 -0
  84. package/dist/src/core/infrastructure/platform/macos/macos-port-table.adapter.js +86 -0
  85. package/dist/src/core/infrastructure/platform/macos/macos-process-table.adapter.js +62 -0
  86. package/dist/src/core/infrastructure/platform/macos/macos-trash.adapter.js +64 -0
  87. package/dist/src/core/infrastructure/platform/windows/system32.js +6 -0
  88. package/dist/src/core/infrastructure/platform/windows/windows-clipboard.adapter.js +50 -0
  89. package/dist/src/core/infrastructure/platform/windows/windows-opener.adapter.js +11 -0
  90. package/dist/src/core/infrastructure/platform/windows/windows-port-table.adapter.js +38 -0
  91. package/dist/src/core/infrastructure/platform/windows/windows-process-table.adapter.js +134 -0
  92. package/dist/src/core/infrastructure/platform/windows/windows-trash.adapter.js +86 -0
  93. package/dist/src/core/presentation/cli/argv.js +83 -0
  94. package/dist/src/core/presentation/cli/cli-application.js +201 -0
  95. package/dist/src/core/presentation/cli/cli-view-format.js +46 -0
  96. package/dist/src/core/presentation/cli/help.js +202 -0
  97. package/dist/src/core/presentation/cli/json-output.js +28 -0
  98. package/dist/src/core/presentation/cli/style.js +21 -0
  99. package/dist/src/core/presentation/cli/suggest.js +25 -0
  100. package/dist/src/core/presentation/cli/terminal-confirmation.js +63 -0
  101. package/dist/src/core/presentation/ended-processes.js +18 -0
  102. package/dist/src/core/presentation/i18n/translator.js +23 -0
  103. package/dist/src/core/presentation/list-preview.js +10 -0
  104. package/dist/src/core/presentation/mcp/elicitation.js +55 -0
  105. package/dist/src/core/presentation/mcp/mcp-application.js +60 -0
  106. package/dist/src/core/presentation/mcp/mcp-confirmation.js +59 -0
  107. package/dist/src/core/presentation/mcp/mcp-server.js +355 -0
  108. package/dist/src/core/presentation/mcp/protocol.js +32 -0
  109. package/dist/src/core/presentation/mcp/request-state.js +66 -0
  110. package/dist/src/core/presentation/mcp/tool-definitions.js +149 -0
  111. package/dist/src/core/presentation/mcp/tool-results.js +56 -0
  112. package/dist/src/i18n/locales/en.js +827 -0
  113. package/dist/src/main.js +154 -0
  114. package/dist/src/modules/archive/application/archive-sources.js +55 -0
  115. package/dist/src/modules/archive/application/create-tar.use-case.js +123 -0
  116. package/dist/src/modules/archive/application/create-zip.use-case.js +106 -0
  117. package/dist/src/modules/archive/application/extract-tar.use-case.js +240 -0
  118. package/dist/src/modules/archive/application/extract-zip.use-case.js +181 -0
  119. package/dist/src/modules/archive/application/tar-reader.js +45 -0
  120. package/dist/src/modules/archive/application/zip-reader.js +57 -0
  121. package/dist/src/modules/archive/archive.module.js +27 -0
  122. package/dist/src/modules/archive/domain/crc32.js +18 -0
  123. package/dist/src/modules/archive/domain/tar-format.js +97 -0
  124. package/dist/src/modules/archive/domain/tar-stream.js +223 -0
  125. package/dist/src/modules/archive/domain/zip-format.js +158 -0
  126. package/dist/src/modules/archive/presentation/tar.view.js +17 -0
  127. package/dist/src/modules/archive/presentation/untar.view.js +30 -0
  128. package/dist/src/modules/archive/presentation/unzip.view.js +24 -0
  129. package/dist/src/modules/archive/presentation/zip.view.js +17 -0
  130. package/dist/src/modules/clip/application/copy-text.use-case.js +43 -0
  131. package/dist/src/modules/clip/application/paste-text.use-case.js +25 -0
  132. package/dist/src/modules/clip/clip.module.js +16 -0
  133. package/dist/src/modules/clip/presentation/clip.views.js +7 -0
  134. package/dist/src/modules/completion/application/print-script.use-case.js +38 -0
  135. package/dist/src/modules/completion/application/suggest-words.use-case.js +37 -0
  136. package/dist/src/modules/completion/completion.module.js +14 -0
  137. package/dist/src/modules/completion/domain/scripts.js +91 -0
  138. package/dist/src/modules/completion/domain/suggest.js +134 -0
  139. package/dist/src/modules/completion/presentation/completion.views.js +6 -0
  140. package/dist/src/modules/config/application/config-commands.js +14 -0
  141. package/dist/src/modules/config/application/get-setting.use-case.js +25 -0
  142. package/dist/src/modules/config/application/list-config-keys.use-case.js +26 -0
  143. package/dist/src/modules/config/application/list-settings.use-case.js +28 -0
  144. package/dist/src/modules/config/application/set-setting.use-case.js +53 -0
  145. package/dist/src/modules/config/application/show-config-path.use-case.js +21 -0
  146. package/dist/src/modules/config/application/unset-setting.use-case.js +28 -0
  147. package/dist/src/modules/config/config.module.js +28 -0
  148. package/dist/src/modules/config/presentation/config.views.js +33 -0
  149. package/dist/src/modules/convert/application/convert-case.use-case.js +38 -0
  150. package/dist/src/modules/convert/application/convert-codecs.use-case.js +92 -0
  151. package/dist/src/modules/convert/application/convert-input.js +16 -0
  152. package/dist/src/modules/convert/application/convert-json.use-case.js +51 -0
  153. package/dist/src/modules/convert/application/convert-time.use-case.js +39 -0
  154. package/dist/src/modules/convert/application/decode-jwt.use-case.js +52 -0
  155. package/dist/src/modules/convert/convert.module.js +29 -0
  156. package/dist/src/modules/convert/domain/json-syntax.js +135 -0
  157. package/dist/src/modules/convert/domain/jwt.js +29 -0
  158. package/dist/src/modules/convert/domain/moments.js +20 -0
  159. package/dist/src/modules/convert/presentation/convert.views.js +44 -0
  160. package/dist/src/modules/docker/application/clean-engine.use-case.js +77 -0
  161. package/dist/src/modules/docker/application/compose-project.js +45 -0
  162. package/dist/src/modules/docker/application/docker-engine.js +40 -0
  163. package/dist/src/modules/docker/application/list-containers.use-case.js +69 -0
  164. package/dist/src/modules/docker/application/run-compose.use-case.js +83 -0
  165. package/dist/src/modules/docker/application/show-logs.use-case.js +51 -0
  166. package/dist/src/modules/docker/application/stop-project.use-case.js +56 -0
  167. package/dist/src/modules/docker/docker.module.js +23 -0
  168. package/dist/src/modules/docker/domain/compose-file.js +105 -0
  169. package/dist/src/modules/docker/domain/engine-output.js +65 -0
  170. package/dist/src/modules/docker/presentation/docker.views.js +63 -0
  171. package/dist/src/modules/doctor/application/run-checks.use-case.js +128 -0
  172. package/dist/src/modules/doctor/doctor.module.js +13 -0
  173. package/dist/src/modules/doctor/domain/versions.js +22 -0
  174. package/dist/src/modules/doctor/presentation/doctor.view.js +30 -0
  175. package/dist/src/modules/env/application/check-dotenv.use-case.js +116 -0
  176. package/dist/src/modules/env/application/inspect-path.use-case.js +71 -0
  177. package/dist/src/modules/env/application/path-entries.js +28 -0
  178. package/dist/src/modules/env/application/show-variables.use-case.js +47 -0
  179. package/dist/src/modules/env/domain/dotenv.js +62 -0
  180. package/dist/src/modules/env/env.module.js +17 -0
  181. package/dist/src/modules/env/presentation/env.views.js +43 -0
  182. package/dist/src/modules/files/application/clean-builds.use-case.js +149 -0
  183. package/dist/src/modules/files/application/compare-paths.use-case.js +127 -0
  184. package/dist/src/modules/files/application/copy-paths.use-case.js +101 -0
  185. package/dist/src/modules/files/application/create-paths.use-case.js +85 -0
  186. package/dist/src/modules/files/application/delete-paths.use-case.js +127 -0
  187. package/dist/src/modules/files/application/find-duplicates.use-case.js +105 -0
  188. package/dist/src/modules/files/application/find-files.use-case.js +131 -0
  189. package/dist/src/modules/files/application/hash-files.use-case.js +73 -0
  190. package/dist/src/modules/files/application/list-entries.use-case.js +87 -0
  191. package/dist/src/modules/files/application/measure-folders.use-case.js +102 -0
  192. package/dist/src/modules/files/application/move-entry.js +19 -0
  193. package/dist/src/modules/files/application/move-paths.use-case.js +82 -0
  194. package/dist/src/modules/files/application/read-text.use-case.js +92 -0
  195. package/dist/src/modules/files/application/rename-paths.use-case.js +184 -0
  196. package/dist/src/modules/files/application/replace-text.use-case.js +146 -0
  197. package/dist/src/modules/files/application/search-text.use-case.js +115 -0
  198. package/dist/src/modules/files/application/show-info.use-case.js +80 -0
  199. package/dist/src/modules/files/application/show-tree.use-case.js +75 -0
  200. package/dist/src/modules/files/application/sync-folders.use-case.js +162 -0
  201. package/dist/src/modules/files/application/text-files.js +46 -0
  202. package/dist/src/modules/files/application/transfers.js +35 -0
  203. package/dist/src/modules/files/domain/case-style.js +18 -0
  204. package/dist/src/modules/files/domain/filters.js +9 -0
  205. package/dist/src/modules/files/domain/sync-plan.js +35 -0
  206. package/dist/src/modules/files/domain/text-encoding.js +75 -0
  207. package/dist/src/modules/files/domain/values.js +28 -0
  208. package/dist/src/modules/files/files.module.js +70 -0
  209. package/dist/src/modules/files/presentation/clean.view.js +15 -0
  210. package/dist/src/modules/files/presentation/compare.view.js +50 -0
  211. package/dist/src/modules/files/presentation/copy.view.js +17 -0
  212. package/dist/src/modules/files/presentation/delete.view.js +16 -0
  213. package/dist/src/modules/files/presentation/dupes.view.js +20 -0
  214. package/dist/src/modules/files/presentation/find.view.js +16 -0
  215. package/dist/src/modules/files/presentation/grep.view.js +27 -0
  216. package/dist/src/modules/files/presentation/hash.view.js +15 -0
  217. package/dist/src/modules/files/presentation/info.view.js +36 -0
  218. package/dist/src/modules/files/presentation/list.view.js +17 -0
  219. package/dist/src/modules/files/presentation/move.view.js +14 -0
  220. package/dist/src/modules/files/presentation/new.view.js +8 -0
  221. package/dist/src/modules/files/presentation/read.view.js +6 -0
  222. package/dist/src/modules/files/presentation/rename.view.js +16 -0
  223. package/dist/src/modules/files/presentation/replace.view.js +26 -0
  224. package/dist/src/modules/files/presentation/size.view.js +24 -0
  225. package/dist/src/modules/files/presentation/sync.view.js +37 -0
  226. package/dist/src/modules/files/presentation/tree.view.js +22 -0
  227. package/dist/src/modules/gen/application/gen-options.js +18 -0
  228. package/dist/src/modules/gen/application/generate-passwords.use-case.js +39 -0
  229. package/dist/src/modules/gen/application/generate-tokens.use-case.js +38 -0
  230. package/dist/src/modules/gen/application/generate-ulids.use-case.js +40 -0
  231. package/dist/src/modules/gen/application/generate-uuids.use-case.js +32 -0
  232. package/dist/src/modules/gen/domain/identifiers.js +65 -0
  233. package/dist/src/modules/gen/domain/secrets.js +43 -0
  234. package/dist/src/modules/gen/gen.module.js +20 -0
  235. package/dist/src/modules/git/application/fetch-repositories.use-case.js +50 -0
  236. package/dist/src/modules/git/application/git-client.js +62 -0
  237. package/dist/src/modules/git/application/pull-repositories.use-case.js +67 -0
  238. package/dist/src/modules/git/application/repositories.js +67 -0
  239. package/dist/src/modules/git/application/show-status.use-case.js +49 -0
  240. package/dist/src/modules/git/application/switch-branch.use-case.js +118 -0
  241. package/dist/src/modules/git/domain/repository-status.js +37 -0
  242. package/dist/src/modules/git/git.module.js +25 -0
  243. package/dist/src/modules/git/presentation/git.views.js +100 -0
  244. package/dist/src/modules/net/application/check-connection.use-case.js +71 -0
  245. package/dist/src/modules/net/application/list-addresses.use-case.js +29 -0
  246. package/dist/src/modules/net/application/lookup-name.use-case.js +55 -0
  247. package/dist/src/modules/net/net.module.js +17 -0
  248. package/dist/src/modules/net/presentation/net.views.js +39 -0
  249. package/dist/src/modules/open/application/open-target.use-case.js +53 -0
  250. package/dist/src/modules/open/domain/targets.js +45 -0
  251. package/dist/src/modules/open/open.module.js +12 -0
  252. package/dist/src/modules/open/presentation/open.view.js +6 -0
  253. package/dist/src/modules/port/application/end-port-owners.use-case.js +68 -0
  254. package/dist/src/modules/port/application/find-free-port.use-case.js +40 -0
  255. package/dist/src/modules/port/application/find-listeners.use-case.js +34 -0
  256. package/dist/src/modules/port/application/port-owners.js +25 -0
  257. package/dist/src/modules/port/domain/port-number.js +8 -0
  258. package/dist/src/modules/port/port.module.js +17 -0
  259. package/dist/src/modules/port/presentation/port.views.js +27 -0
  260. package/dist/src/modules/proc/application/end-processes.use-case.js +77 -0
  261. package/dist/src/modules/proc/application/find-processes.use-case.js +50 -0
  262. package/dist/src/modules/proc/application/list-processes.use-case.js +41 -0
  263. package/dist/src/modules/proc/application/show-tree.use-case.js +40 -0
  264. package/dist/src/modules/proc/domain/process-tree.js +55 -0
  265. package/dist/src/modules/proc/presentation/proc.views.js +28 -0
  266. package/dist/src/modules/proc/proc.module.js +19 -0
  267. package/dist/src/modules/sys/application/create-report.use-case.js +30 -0
  268. package/dist/src/modules/sys/application/find-tools.use-case.js +20 -0
  269. package/dist/src/modules/sys/application/machine-facts.js +44 -0
  270. package/dist/src/modules/sys/application/show-info.use-case.js +29 -0
  271. package/dist/src/modules/sys/domain/tools.js +17 -0
  272. package/dist/src/modules/sys/presentation/sys.views.js +67 -0
  273. package/dist/src/modules/sys/sys.module.js +17 -0
  274. package/dist/src/modules/wait/application/poll.js +26 -0
  275. package/dist/src/modules/wait/application/wait-file.use-case.js +55 -0
  276. package/dist/src/modules/wait/application/wait-port.use-case.js +76 -0
  277. package/dist/src/modules/wait/application/wait-url.use-case.js +71 -0
  278. package/dist/src/modules/wait/domain/reasons.js +14 -0
  279. package/dist/src/modules/wait/domain/wait-input.js +50 -0
  280. package/dist/src/modules/wait/presentation/wait.views.js +31 -0
  281. package/dist/src/modules/wait/wait.module.js +21 -0
  282. package/package.json +57 -0
@@ -0,0 +1,43 @@
1
+ import { toBase64, toHex } from "../../../core/domain/encodings.js";
2
+ export const LOWER = "abcdefghijklmnopqrstuvwxyz";
3
+ export const UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
4
+ export const DIGITS = "0123456789";
5
+ /** Symbols that need no quoting in most shells, URLs and configuration formats. */
6
+ export const SYMBOLS = "!#%*+-=?@^_~";
7
+ export const TOKEN_FORMATS = ["hex", "base64", "base64url"];
8
+ /**
9
+ * Characters drawn from an alphabet without bias: a random byte at or past the last
10
+ * whole multiple of the alphabet's length is thrown away and another is drawn.
11
+ */
12
+ export function pickCharacters(alphabet, count, randomBytes) {
13
+ const limit = 256 - (256 % alphabet.length);
14
+ let text = "";
15
+ while (text.length < count) {
16
+ for (const byte of randomBytes(count - text.length + 8)) {
17
+ if (byte >= limit)
18
+ continue;
19
+ text += alphabet.charAt(byte % alphabet.length);
20
+ if (text.length === count)
21
+ break;
22
+ }
23
+ }
24
+ return text;
25
+ }
26
+ /**
27
+ * A password with at least one character of every class. Candidates are drawn whole
28
+ * and kept only when they have every class, so each allowed password is equally likely.
29
+ */
30
+ export function password(length, classes, randomBytes) {
31
+ const alphabet = classes.join("");
32
+ for (;;) {
33
+ const candidate = pickCharacters(alphabet, length, randomBytes);
34
+ if (classes.every((characters) => [...candidate].some((char) => characters.includes(char))))
35
+ return candidate;
36
+ }
37
+ }
38
+ export function formatToken(bytes, format) {
39
+ if (format === "hex")
40
+ return toHex(bytes);
41
+ return toBase64(bytes, format === "base64url");
42
+ }
43
+ //# sourceMappingURL=secrets.js.map
@@ -0,0 +1,20 @@
1
+ import { GeneratePasswords } from "./application/generate-passwords.use-case.js";
2
+ import { GenerateTokens } from "./application/generate-tokens.use-case.js";
3
+ import { GenerateUlids } from "./application/generate-ulids.use-case.js";
4
+ import { GenerateUuids } from "./application/generate-uuids.use-case.js";
5
+ /** One value per line, so the output can be piped or captured as it is. */
6
+ const valuesView = (output) => output.values;
7
+ export const genModule = {
8
+ id: "gen",
9
+ summary: "gen.summary",
10
+ about: "gen.about",
11
+ examples: ["kiriya gen uuid --v7", "kiriya gen password --length 32", "kiriya gen token --format hex"],
12
+ guide: "https://github.com/SatPaingOo/kiriya/blob/main/docs/modules/gen.md",
13
+ register(registrar, ports) {
14
+ registrar.add(new GenerateUuids(ports.random, ports.clock), valuesView);
15
+ registrar.add(new GenerateUlids(ports.random, ports.clock), valuesView);
16
+ registrar.add(new GeneratePasswords(ports.random), valuesView);
17
+ registrar.add(new GenerateTokens(ports.random), valuesView);
18
+ },
19
+ };
20
+ //# sourceMappingURL=gen.module.js.map
@@ -0,0 +1,50 @@
1
+ import { done } from "../../../core/domain/command.js";
2
+ import { message } from "../../../core/domain/message.js";
3
+ import { firstLine } from "../domain/repository-status.js";
4
+ import { GitClient, NETWORK_TIMEOUT_MS } from "./git-client.js";
5
+ import { folderInput, repositoriesIn } from "./repositories.js";
6
+ export const fetchSpec = {
7
+ id: "git.fetch",
8
+ summary: "git.fetch.summary",
9
+ examples: ["kiriya git fetch", "kiriya git fetch ~/work --depth 2"],
10
+ safety: "write",
11
+ idempotent: true,
12
+ usesNetwork: true,
13
+ runsUserCommands: false,
14
+ input: folderInput,
15
+ };
16
+ export class FetchRepositories {
17
+ fileSystem;
18
+ processRunner;
19
+ spec = fetchSpec;
20
+ constructor(fileSystem, processRunner) {
21
+ this.fileSystem = fileSystem;
22
+ this.processRunner = processRunner;
23
+ }
24
+ async execute(input, context) {
25
+ const git = await GitClient.locate(this.processRunner, context.signal);
26
+ const { folder, repositories } = await repositoriesIn(this.fileSystem, context.cwd, input);
27
+ const items = [];
28
+ const warnings = repositories.length === 0 ? [message("git.none-found", { depth: input.depth })] : [];
29
+ const failures = [];
30
+ for (const repository of repositories) {
31
+ if (context.signal.aborted)
32
+ break;
33
+ const before = await git.status(folder, repository);
34
+ if (!before.hasRemote) {
35
+ items.push({ status: before, outcome: "no-remote" });
36
+ continue;
37
+ }
38
+ const result = await git.run(repository, ["fetch", "--all", "--prune", "--quiet"], NETWORK_TIMEOUT_MS);
39
+ if (result.code !== 0) {
40
+ const detail = firstLine(result.stderr) || firstLine(result.stdout);
41
+ failures.push(message("git.fetch.failed", { name: before.name, detail }));
42
+ items.push({ status: before, outcome: "failed" });
43
+ continue;
44
+ }
45
+ items.push({ status: await git.status(folder, repository), outcome: "fetched" });
46
+ }
47
+ return done({ folder, items }, { warnings, failures });
48
+ }
49
+ }
50
+ //# sourceMappingURL=fetch-repositories.use-case.js.map
@@ -0,0 +1,62 @@
1
+ import path from "node:path";
2
+ import { CapabilityUnavailableError } from "../../../core/domain/errors.js";
3
+ import { parseLeftRight } from "../domain/repository-status.js";
4
+ /** Git never stops to ask for a password in the middle of a run over many repositories. */
5
+ const NON_INTERACTIVE = { GIT_TERMINAL_PROMPT: "0", GCM_INTERACTIVE: "never" };
6
+ /** Fetch and pull talk to remotes, which may be slow. */
7
+ export const NETWORK_TIMEOUT_MS = 180_000;
8
+ /** Git run in one repository at a time, without a shell and without prompts. */
9
+ export class GitClient {
10
+ runner;
11
+ git;
12
+ signal;
13
+ constructor(runner, git, signal) {
14
+ this.runner = runner;
15
+ this.git = git;
16
+ this.signal = signal;
17
+ }
18
+ static async locate(runner, signal) {
19
+ const git = await runner.find("git");
20
+ if (git === null)
21
+ throw new CapabilityUnavailableError("git.not-installed");
22
+ return new GitClient(runner, git, signal);
23
+ }
24
+ run(repository, args, timeoutMs = 60_000) {
25
+ return this.runner.run(this.git, ["-C", repository, ...args], {
26
+ env: NON_INTERACTIVE,
27
+ timeoutMs,
28
+ signal: this.signal,
29
+ });
30
+ }
31
+ /** stdout without trailing whitespace, or null when git fails. */
32
+ async output(repository, args) {
33
+ const result = await this.run(repository, args);
34
+ return result.code === 0 ? result.stdout.replace(/\s+$/, "") : null;
35
+ }
36
+ async succeeds(repository, args) {
37
+ return (await this.run(repository, args)).code === 0;
38
+ }
39
+ async lines(repository, args) {
40
+ const text = await this.output(repository, args);
41
+ return text === null ? [] : text.split(/\r?\n/).filter((line) => line !== "");
42
+ }
43
+ async status(folder, repository) {
44
+ // symbolic-ref works on a branch with no commits yet; rev-parse covers a detached HEAD.
45
+ const branch = (await this.output(repository, ["symbolic-ref", "--short", "HEAD"])) ??
46
+ (await this.output(repository, ["rev-parse", "--short", "HEAD"])) ??
47
+ "?";
48
+ const counts = parseLeftRight(await this.output(repository, ["rev-list", "--left-right", "--count", "@{upstream}...HEAD"]));
49
+ const relative = path.relative(folder, repository).split(path.sep).join("/");
50
+ return {
51
+ name: relative === "" ? path.basename(repository) : relative,
52
+ path: repository,
53
+ branch,
54
+ commits: Number((await this.output(repository, ["rev-list", "--count", "HEAD"])) ?? 0),
55
+ dirty: (await this.lines(repository, ["status", "--porcelain"])).length,
56
+ ahead: counts?.ahead ?? null,
57
+ behind: counts?.behind ?? null,
58
+ hasRemote: (await this.lines(repository, ["remote"])).length > 0,
59
+ };
60
+ }
61
+ }
62
+ //# sourceMappingURL=git-client.js.map
@@ -0,0 +1,67 @@
1
+ import { done } from "../../../core/domain/command.js";
2
+ import { message } from "../../../core/domain/message.js";
3
+ import { firstLine } from "../domain/repository-status.js";
4
+ import { GitClient, NETWORK_TIMEOUT_MS } from "./git-client.js";
5
+ import { folderInput, repositoriesIn } from "./repositories.js";
6
+ export const pullSpec = {
7
+ id: "git.pull",
8
+ summary: "git.pull.summary",
9
+ examples: ["kiriya git pull", "kiriya git pull ~/work --json"],
10
+ safety: "write",
11
+ idempotent: true,
12
+ usesNetwork: true,
13
+ runsUserCommands: false,
14
+ input: folderInput,
15
+ };
16
+ /** Fast-forward only: a repository with uncommitted changes, no upstream, or diverged history is left as it is. */
17
+ export class PullRepositories {
18
+ fileSystem;
19
+ processRunner;
20
+ spec = pullSpec;
21
+ constructor(fileSystem, processRunner) {
22
+ this.fileSystem = fileSystem;
23
+ this.processRunner = processRunner;
24
+ }
25
+ async execute(input, context) {
26
+ const git = await GitClient.locate(this.processRunner, context.signal);
27
+ const { folder, repositories } = await repositoriesIn(this.fileSystem, context.cwd, input);
28
+ const items = [];
29
+ const warnings = repositories.length === 0 ? [message("git.none-found", { depth: input.depth })] : [];
30
+ const failures = [];
31
+ for (const repository of repositories) {
32
+ if (context.signal.aborted)
33
+ break;
34
+ const status = await git.status(folder, repository);
35
+ const skip = (outcome) => void items.push({ status, outcome, newCommits: 0 });
36
+ if (!status.hasRemote) {
37
+ skip("no-remote");
38
+ continue;
39
+ }
40
+ if (status.ahead === null) {
41
+ skip("no-upstream");
42
+ continue;
43
+ }
44
+ if (status.dirty > 0) {
45
+ warnings.push(message("git.pull.dirty", { name: status.name, count: status.dirty }));
46
+ skip("dirty");
47
+ continue;
48
+ }
49
+ const before = await git.output(repository, ["rev-parse", "HEAD"]);
50
+ const result = await git.run(repository, ["pull", "--ff-only", "--quiet"], NETWORK_TIMEOUT_MS);
51
+ if (result.code !== 0) {
52
+ const detail = firstLine(result.stderr) || firstLine(result.stdout);
53
+ failures.push(message("git.pull.failed", { name: status.name, detail }));
54
+ skip("failed");
55
+ continue;
56
+ }
57
+ const after = await git.output(repository, ["rev-parse", "HEAD"]);
58
+ const moved = before !== null && after !== null && before !== after;
59
+ const count = moved
60
+ ? Number((await git.output(repository, ["rev-list", "--count", `${before}..${after}`])) ?? 0)
61
+ : 0;
62
+ items.push({ status, outcome: count > 0 ? "pulled" : "up-to-date", newCommits: count });
63
+ }
64
+ return done({ folder, items }, { warnings, failures });
65
+ }
66
+ }
67
+ //# sourceMappingURL=pull-repositories.use-case.js.map
@@ -0,0 +1,67 @@
1
+ import path from "node:path";
2
+ import { DEPENDENCY_DIRECTORIES } from "../../../config/dependency-directories.js";
3
+ import { KiriyaError, NotFoundError, UsageError } from "../../../core/domain/errors.js";
4
+ import { RawReader } from "../../../core/domain/input-schema.js";
5
+ import { byCodePoint, isHiddenName } from "../../../core/domain/names.js";
6
+ import { DEFAULT_DEPTH, MAX_DEPTH } from "../domain/repository-status.js";
7
+ export function readDepth(reader) {
8
+ const value = reader.string("depth");
9
+ if (value === undefined)
10
+ return DEFAULT_DEPTH;
11
+ const depth = Number(value);
12
+ if (!Number.isInteger(depth) || depth < 1 || depth > MAX_DEPTH) {
13
+ throw new UsageError("git.option.depth-invalid", { value, max: MAX_DEPTH });
14
+ }
15
+ return depth;
16
+ }
17
+ export const folderInput = {
18
+ positionals: [{ name: "folder", description: "git.arg.folder", required: false, variadic: false, path: true }],
19
+ options: { depth: { type: "string", description: "git.option.depth", valueName: "<1-10>" } },
20
+ parse(raw) {
21
+ const reader = new RawReader(raw);
22
+ return { folder: reader.positional(0) ?? ".", depth: readDepth(reader) };
23
+ },
24
+ };
25
+ /**
26
+ * The folder itself when it is a repository; otherwise every repository below it, down
27
+ * to `depth` folder levels. Hidden and dependency folders, and the inside of a
28
+ * repository, are not searched.
29
+ */
30
+ export async function repositoriesUnder(fileSystem, folder, depth) {
31
+ if ((await fileSystem.lstat(path.join(folder, ".git"))) !== null)
32
+ return [folder];
33
+ const found = [];
34
+ const visit = async (current, level) => {
35
+ let entries;
36
+ try {
37
+ entries = await fileSystem.readDirectory(current);
38
+ }
39
+ catch (error) {
40
+ if (error instanceof KiriyaError)
41
+ return;
42
+ throw error;
43
+ }
44
+ for (const entry of entries) {
45
+ if (entry.kind !== "directory" || isHiddenName(entry.name) || DEPENDENCY_DIRECTORIES.has(entry.name))
46
+ continue;
47
+ const child = path.join(current, entry.name);
48
+ if ((await fileSystem.lstat(path.join(child, ".git"))) !== null)
49
+ found.push(child);
50
+ else if (level < depth)
51
+ await visit(child, level + 1);
52
+ }
53
+ };
54
+ await visit(folder, 1);
55
+ return found.sort(byCodePoint);
56
+ }
57
+ /** The folder a git command was given, checked, with the repositories in it. */
58
+ export async function repositoriesIn(fileSystem, cwd, input) {
59
+ const folder = path.resolve(cwd, input.folder);
60
+ const stat = await fileSystem.stat(folder);
61
+ if (stat === null)
62
+ throw new NotFoundError("core.fs.not-found", { path: folder });
63
+ if (stat.kind !== "directory")
64
+ throw new NotFoundError("core.path.not-a-folder", { path: folder });
65
+ return { folder, repositories: await repositoriesUnder(fileSystem, folder, input.depth) };
66
+ }
67
+ //# sourceMappingURL=repositories.js.map
@@ -0,0 +1,49 @@
1
+ import { done } from "../../../core/domain/command.js";
2
+ import { message } from "../../../core/domain/message.js";
3
+ import { majorityBranch } from "../domain/repository-status.js";
4
+ import { GitClient } from "./git-client.js";
5
+ import { folderInput, repositoriesIn } from "./repositories.js";
6
+ export const statusSpec = {
7
+ id: "git.status",
8
+ summary: "git.status.summary",
9
+ examples: ["kiriya git status", "kiriya git status ~/work --depth 2", "kiriya git status --json"],
10
+ safety: "read",
11
+ idempotent: true,
12
+ usesNetwork: false,
13
+ runsUserCommands: false,
14
+ input: folderInput,
15
+ };
16
+ export class ShowStatus {
17
+ fileSystem;
18
+ processRunner;
19
+ spec = statusSpec;
20
+ constructor(fileSystem, processRunner) {
21
+ this.fileSystem = fileSystem;
22
+ this.processRunner = processRunner;
23
+ }
24
+ async execute(input, context) {
25
+ const git = await GitClient.locate(this.processRunner, context.signal);
26
+ const { folder, repositories } = await repositoriesIn(this.fileSystem, context.cwd, input);
27
+ const statuses = [];
28
+ for (const repository of repositories)
29
+ statuses.push(await git.status(folder, repository));
30
+ const isRepository = repositories.length === 1 && repositories[0] === folder;
31
+ const failures = statuses
32
+ .filter((status) => !status.hasRemote)
33
+ .map((status) => message("git.status.no-remote", { name: status.name }));
34
+ const warnings = statuses
35
+ .filter((status) => (status.ahead ?? 0) > 0)
36
+ .map((status) => message("git.status.unpushed", { name: status.name, count: status.ahead ?? 0 }));
37
+ const common = isRepository ? null : majorityBranch(statuses.map((status) => status.branch));
38
+ if (common !== null) {
39
+ for (const status of statuses.filter((item) => item.branch !== common)) {
40
+ warnings.push(message("git.status.other-branch", { name: status.name, branch: status.branch, common }));
41
+ }
42
+ }
43
+ if (statuses.length === 0)
44
+ warnings.push(message("git.none-found", { depth: input.depth }));
45
+ const allGood = statuses.length > 0 && failures.length === 0 && warnings.length === 0;
46
+ return done({ folder, isRepository, repositories: statuses, allGood }, { warnings, failures });
47
+ }
48
+ }
49
+ //# sourceMappingURL=show-status.use-case.js.map
@@ -0,0 +1,118 @@
1
+ import { requireApproval } from "../../../core/application/safety.js";
2
+ import { done } from "../../../core/domain/command.js";
3
+ import { UsageError } from "../../../core/domain/errors.js";
4
+ import { RawReader } from "../../../core/domain/input-schema.js";
5
+ import { message } from "../../../core/domain/message.js";
6
+ import { firstLine } from "../domain/repository-status.js";
7
+ import { GitClient } from "./git-client.js";
8
+ import { readDepth, repositoriesIn } from "./repositories.js";
9
+ function gitArgs(action, branch) {
10
+ if (action === "local")
11
+ return ["switch", branch];
12
+ if (action === "track")
13
+ return ["switch", "-c", branch, "--track", `origin/${branch}`];
14
+ return ["switch", "-c", branch];
15
+ }
16
+ export const switchSpec = {
17
+ id: "git.switch",
18
+ summary: "git.switch.summary",
19
+ examples: [
20
+ "kiriya git switch main",
21
+ "kiriya git switch feature/login ~/work --create",
22
+ "kiriya git switch develop --yes",
23
+ ],
24
+ safety: "write",
25
+ idempotent: true,
26
+ usesNetwork: false,
27
+ runsUserCommands: false,
28
+ input: {
29
+ positionals: [
30
+ { name: "branch", description: "git.switch.arg.branch", required: true, variadic: false },
31
+ { name: "folder", description: "git.arg.folder", required: false, variadic: false, path: true },
32
+ ],
33
+ options: {
34
+ create: { type: "boolean", description: "git.switch.option.create" },
35
+ depth: { type: "string", description: "git.option.depth", valueName: "<1-10>" },
36
+ yes: { type: "boolean", description: "git.switch.option.yes", short: "y", terminalOnly: true },
37
+ },
38
+ parse(raw) {
39
+ const reader = new RawReader(raw);
40
+ return {
41
+ branch: reader.positional(0) ?? "",
42
+ folder: reader.positional(1) ?? ".",
43
+ depth: readDepth(reader),
44
+ create: reader.flag("create"),
45
+ yes: reader.flag("yes"),
46
+ };
47
+ },
48
+ },
49
+ };
50
+ /** Every repository under a folder onto one branch, or none of them when any is not ready. */
51
+ export class SwitchBranch {
52
+ fileSystem;
53
+ processRunner;
54
+ spec = switchSpec;
55
+ constructor(fileSystem, processRunner) {
56
+ this.fileSystem = fileSystem;
57
+ this.processRunner = processRunner;
58
+ }
59
+ async execute(input, context) {
60
+ const git = await GitClient.locate(this.processRunner, context.signal);
61
+ const { folder, repositories } = await repositoriesIn(this.fileSystem, context.cwd, input);
62
+ if (!(await git.succeeds(folder, ["check-ref-format", "--branch", input.branch]))) {
63
+ throw new UsageError("git.switch.bad-branch", { branch: input.branch });
64
+ }
65
+ const steps = [];
66
+ for (const repository of repositories) {
67
+ const status = await git.status(folder, repository);
68
+ let action;
69
+ if (status.branch === input.branch)
70
+ action = "already";
71
+ else if (await git.succeeds(repository, ["rev-parse", "--verify", "--quiet", `refs/heads/${input.branch}`])) {
72
+ action = "local";
73
+ }
74
+ else if (await git.succeeds(repository, ["rev-parse", "--verify", "--quiet", `refs/remotes/origin/${input.branch}`])) {
75
+ action = "track";
76
+ }
77
+ else
78
+ action = input.create ? "create" : "missing";
79
+ const outcome = action === "already" ? "unchanged" : "planned";
80
+ steps.push({ name: status.name, path: repository, from: status.branch, dirty: status.dirty, action, outcome });
81
+ }
82
+ const data = { branch: input.branch, folder, steps };
83
+ const warnings = repositories.length === 0 ? [message("git.none-found", { depth: input.depth })] : [];
84
+ const problems = [
85
+ ...steps
86
+ .filter((step) => step.action !== "already" && step.dirty > 0)
87
+ .map((step) => message("git.switch.dirty", { name: step.name, count: step.dirty })),
88
+ ...steps
89
+ .filter((step) => step.action === "missing")
90
+ .map((step) => message("git.switch.missing", { name: step.name, branch: input.branch })),
91
+ ];
92
+ if (problems.length > 0)
93
+ return done(data, { warnings, failures: problems });
94
+ const pending = steps.filter((step) => step.action !== "already");
95
+ if (pending.length === 0)
96
+ return done(data, { warnings });
97
+ await requireApproval(context, message("git.switch.ask", { count: pending.length, branch: input.branch }), input.yes);
98
+ const failures = [];
99
+ const finished = [];
100
+ for (const step of steps) {
101
+ if (step.action === "already") {
102
+ finished.push(step);
103
+ continue;
104
+ }
105
+ const result = await git.run(step.path, gitArgs(step.action, input.branch));
106
+ if (result.code === 0) {
107
+ finished.push({ ...step, outcome: "switched" });
108
+ }
109
+ else {
110
+ const detail = firstLine(result.stderr) || firstLine(result.stdout);
111
+ failures.push(message("git.switch.failed", { name: step.name, detail }));
112
+ finished.push({ ...step, outcome: "failed" });
113
+ }
114
+ }
115
+ return done({ ...data, steps: finished }, { warnings, failures });
116
+ }
117
+ }
118
+ //# sourceMappingURL=switch-branch.use-case.js.map
@@ -0,0 +1,37 @@
1
+ import { byCodePoint } from "../../../core/domain/names.js";
2
+ export const DEFAULT_DEPTH = 3;
3
+ export const MAX_DEPTH = 10;
4
+ export function syncState(status) {
5
+ if (status.ahead === null || status.behind === null)
6
+ return { kind: "no-upstream" };
7
+ if (status.ahead === 0 && status.behind === 0)
8
+ return { kind: "in-sync" };
9
+ return { kind: "diverged", ahead: status.ahead, behind: status.behind };
10
+ }
11
+ /** `git rev-list --left-right --count @{upstream}...HEAD` prints the commits behind, then the commits ahead. */
12
+ export function parseLeftRight(text) {
13
+ const match = /^(\d+)\s+(\d+)$/.exec(text?.trim() ?? "");
14
+ return match === null ? null : { behind: Number(match[1]), ahead: Number(match[2]) };
15
+ }
16
+ /** The branch most repositories are on, when they are not all on one; ties go to the first in code-point order. */
17
+ export function majorityBranch(branches) {
18
+ const counts = new Map();
19
+ for (const branch of branches)
20
+ counts.set(branch, (counts.get(branch) ?? 0) + 1);
21
+ if (counts.size < 2)
22
+ return null;
23
+ let best = null;
24
+ for (const entry of [...counts.entries()].sort(([a], [b]) => byCodePoint(a, b))) {
25
+ if (best === null || entry[1] > best[1])
26
+ best = entry;
27
+ }
28
+ return best === null ? null : best[0];
29
+ }
30
+ /** The first line with text in a program's output, trimmed. */
31
+ export function firstLine(text) {
32
+ return (text
33
+ .split(/\r?\n/)
34
+ .map((line) => line.trim())
35
+ .find((line) => line !== "") ?? "");
36
+ }
37
+ //# sourceMappingURL=repository-status.js.map
@@ -0,0 +1,25 @@
1
+ import { FetchRepositories } from "./application/fetch-repositories.use-case.js";
2
+ import { PullRepositories } from "./application/pull-repositories.use-case.js";
3
+ import { ShowStatus } from "./application/show-status.use-case.js";
4
+ import { SwitchBranch } from "./application/switch-branch.use-case.js";
5
+ import { fetchView, pullView, statusView, switchView } from "./presentation/git.views.js";
6
+ export const gitModule = {
7
+ id: "git",
8
+ summary: "git.summary",
9
+ about: "git.about",
10
+ examples: [
11
+ "kiriya git status ~/code",
12
+ "kiriya git fetch ~/code",
13
+ "kiriya git pull ~/code",
14
+ "kiriya git switch main ~/code",
15
+ ],
16
+ guide: "https://github.com/SatPaingOo/kiriya/blob/main/docs/modules/git.md",
17
+ register(registrar, ports) {
18
+ const { fileSystem, processRunner } = ports;
19
+ registrar.add(new ShowStatus(fileSystem, processRunner), statusView);
20
+ registrar.add(new FetchRepositories(fileSystem, processRunner), fetchView);
21
+ registrar.add(new PullRepositories(fileSystem, processRunner), pullView);
22
+ registrar.add(new SwitchBranch(fileSystem, processRunner), switchView);
23
+ },
24
+ };
25
+ //# sourceMappingURL=git.module.js.map
@@ -0,0 +1,100 @@
1
+ import path from "node:path";
2
+ import { message } from "../../../core/domain/message.js";
3
+ import { syncState } from "../domain/repository-status.js";
4
+ function syncText(status, format) {
5
+ const state = syncState(status);
6
+ if (state.kind === "no-upstream")
7
+ return format.text(message("git.sync.no-upstream"));
8
+ if (state.kind === "in-sync")
9
+ return format.text(message("git.sync.in-sync"));
10
+ const parts = [];
11
+ if (state.ahead > 0)
12
+ parts.push(format.text(message("git.sync.ahead", { count: state.ahead })));
13
+ if (state.behind > 0)
14
+ parts.push(format.text(message("git.sync.behind", { count: state.behind })));
15
+ return parts.join(", ");
16
+ }
17
+ const widthOf = (names) => Math.max(0, ...names.map((name) => name.length));
18
+ export const statusView = (output, format) => {
19
+ const kind = output.isRepository
20
+ ? message("git.status.repository")
21
+ : message("git.status.workspace", { count: output.repositories.length });
22
+ const lines = [
23
+ `${format.bold(path.basename(output.folder))} ${format.dim(format.text(kind))}`,
24
+ format.dim(output.folder),
25
+ ];
26
+ if (output.repositories.length > 0)
27
+ lines.push("");
28
+ const nameWidth = widthOf(output.repositories.map((status) => status.name));
29
+ const branchWidth = widthOf(output.repositories.map((status) => status.branch));
30
+ for (const status of output.repositories) {
31
+ const parts = [
32
+ format.text(message("git.status.commits", { count: status.commits })),
33
+ format.text(status.dirty > 0 ? message("git.status.uncommitted", { count: status.dirty }) : message("git.status.clean")),
34
+ syncText(status, format),
35
+ status.hasRemote
36
+ ? format.text(message("git.status.has-remote"))
37
+ : format.red(format.text(message("git.status.no-remote-short"))),
38
+ ];
39
+ lines.push(` ${status.name.padEnd(nameWidth)} ${status.branch.padEnd(branchWidth)} ${parts.join(" · ")}`);
40
+ }
41
+ if (output.allGood)
42
+ lines.push("", format.green(format.text(message("git.status.all-good"))));
43
+ return lines;
44
+ };
45
+ export const fetchView = (output, format) => {
46
+ const width = widthOf(output.items.map((item) => item.status.name));
47
+ return output.items.map((item) => {
48
+ const name = item.status.name.padEnd(width);
49
+ if (item.outcome === "fetched")
50
+ return ` ${format.green(name)} ${syncText(item.status, format)}`;
51
+ if (item.outcome === "no-remote")
52
+ return ` ${format.dim(name)} ${format.dim(format.text(message("git.skip.no-remote")))}`;
53
+ return ` ${format.red(name)} ${format.red(format.text(message("git.fetch.failed-short")))}`;
54
+ });
55
+ };
56
+ export const pullView = (output, format) => {
57
+ const width = widthOf(output.items.map((item) => item.status.name));
58
+ return output.items.map((item) => {
59
+ const name = item.status.name.padEnd(width);
60
+ switch (item.outcome) {
61
+ case "pulled":
62
+ return ` ${format.green(name)} ${format.text(message("git.pull.new-commits", { count: item.newCommits }))}`;
63
+ case "up-to-date":
64
+ return ` ${format.green(name)} ${format.dim(format.text(message("git.pull.up-to-date")))}`;
65
+ case "no-remote":
66
+ return ` ${format.dim(name)} ${format.dim(format.text(message("git.skip.no-remote")))}`;
67
+ case "no-upstream":
68
+ return ` ${format.dim(name)} ${format.dim(format.text(message("git.skip.no-upstream")))}`;
69
+ case "dirty":
70
+ return ` ${format.yellow(name)} ${format.text(message("git.skip.dirty", { count: item.status.dirty }))}`;
71
+ case "failed":
72
+ return ` ${format.red(name)} ${format.red(format.text(message("git.pull.failed-short")))}`;
73
+ }
74
+ });
75
+ };
76
+ const ACTION_KEYS = {
77
+ already: "git.switch.action.already",
78
+ local: "git.switch.action.local",
79
+ track: "git.switch.action.track",
80
+ create: "git.switch.action.create",
81
+ missing: "git.switch.action.missing",
82
+ };
83
+ export const switchView = (output, format) => {
84
+ const width = widthOf(output.steps.map((step) => step.name));
85
+ const lines = [`${format.bold(output.branch)} ${format.dim(output.folder)}`, ""];
86
+ for (const step of output.steps) {
87
+ const action = format.text(message(ACTION_KEYS[step.action]));
88
+ const shown = step.outcome === "switched" ? format.green(output.branch) : action;
89
+ lines.push(` ${step.name.padEnd(width)} ${step.from} -> ${shown}`);
90
+ }
91
+ const switched = output.steps.filter((step) => step.outcome === "switched").length;
92
+ if (switched > 0) {
93
+ lines.push("", format.green(format.text(message("git.switch.done", { count: switched, branch: output.branch }))));
94
+ }
95
+ else if (output.steps.length > 0 && output.steps.every((step) => step.action === "already")) {
96
+ lines.push("", format.green(format.text(message("git.switch.all-on", { branch: output.branch }))));
97
+ }
98
+ return lines;
99
+ };
100
+ //# sourceMappingURL=git.views.js.map