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,28 @@
1
+ import path from "node:path";
2
+ /** `;` on Windows and `:` elsewhere, whichever OS kiriya runs on. */
3
+ export function listSeparator(os) {
4
+ return os === "windows" ? ";" : ":";
5
+ }
6
+ function flavour(os) {
7
+ return os === "windows" ? path.win32 : path.posix;
8
+ }
9
+ /** The folder an entry names: on Windows without surrounding quotes and with `%NAME%` expanded; unknown names stay. */
10
+ export function entryFolder(entry, os, variable) {
11
+ const trimmed = entry.trim();
12
+ if (os !== "windows")
13
+ return trimmed;
14
+ return trimmed.replace(/^"(.*)"$/, "$1").replace(/%([^%]+)%/g, (whole, name) => variable(name) ?? whole);
15
+ }
16
+ export function isAbsoluteFolder(folder, os) {
17
+ return flavour(os).isAbsolute(folder);
18
+ }
19
+ /** The form two folders share when they are the same: normalised, without a trailing separator, and on Windows in lower case. */
20
+ export function comparableFolder(folder, os) {
21
+ const paths = flavour(os);
22
+ let normal = paths.normalize(folder);
23
+ const root = paths.parse(normal).root;
24
+ while (normal.length > root.length && normal.endsWith(paths.sep))
25
+ normal = normal.slice(0, -1);
26
+ return os === "windows" ? normal.toLowerCase() : normal;
27
+ }
28
+ //# sourceMappingURL=path-entries.js.map
@@ -0,0 +1,47 @@
1
+ import { done } from "../../../core/domain/command.js";
2
+ import { RawReader } from "../../../core/domain/input-schema.js";
3
+ import { message } from "../../../core/domain/message.js";
4
+ import { byCodePoint } from "../../../core/domain/names.js";
5
+ import { isSecretName, looksSecret } from "../../../core/domain/secrets.js";
6
+ export const showSpec = {
7
+ id: "env.show",
8
+ summary: "env.show.summary",
9
+ examples: ["kiriya env show", "kiriya env show java", "kiriya env show token --reveal"],
10
+ safety: "read",
11
+ idempotent: true,
12
+ usesNetwork: false,
13
+ runsUserCommands: false,
14
+ input: {
15
+ positionals: [{ name: "filter", description: "env.show.arg.filter", required: false, variadic: false }],
16
+ options: { reveal: { type: "boolean", description: "env.show.option.reveal", terminalOnly: true } },
17
+ parse(raw) {
18
+ const reader = new RawReader(raw);
19
+ return { filter: reader.positional(0), reveal: reader.flag("reveal") };
20
+ },
21
+ },
22
+ };
23
+ /** Values hide when a name or value looks secret, in text and in JSON alike, because both end up in logs. */
24
+ export class ShowVariables {
25
+ environment;
26
+ spec = showSpec;
27
+ constructor(environment) {
28
+ this.environment = environment;
29
+ }
30
+ execute(input) {
31
+ const filter = input.filter?.toLowerCase();
32
+ const variables = Object.entries(this.environment.variables())
33
+ .filter(([name]) => filter === undefined || name.toLowerCase().includes(filter))
34
+ .sort(([first], [second]) => byCodePoint(first.toLowerCase(), second.toLowerCase()) || byCodePoint(first, second))
35
+ .map(([name, value]) => {
36
+ const secret = isSecretName(name) || looksSecret(value);
37
+ return { name, value: secret && !input.reveal ? null : value, secret };
38
+ });
39
+ const hidden = variables.filter((variable) => variable.value === null).length;
40
+ const warnings = input.reveal && variables.some((variable) => variable.secret) ? [message("env.show.revealed")] : [];
41
+ const failures = input.filter !== undefined && variables.length === 0
42
+ ? [message("env.show.no-match", { filter: input.filter })]
43
+ : [];
44
+ return Promise.resolve(done({ variables, hidden }, { warnings, failures }));
45
+ }
46
+ }
47
+ //# sourceMappingURL=show-variables.use-case.js.map
@@ -0,0 +1,62 @@
1
+ const BYTE_ORDER_MARK = String.fromCharCode(0xfeff);
2
+ const ENTRY = /^(?:export\s+)?([A-Za-z_][A-Za-z0-9_.-]*)\s*=\s*(.*)$/;
3
+ const QUOTES = ['"', "'", "`"];
4
+ /** Where the closing quote is from `start`, skipping escaped ones inside double quotes; -1 when there is none. */
5
+ function closingQuote(text, quote, start) {
6
+ for (let index = start; index < text.length; index += 1) {
7
+ const char = text.charAt(index);
8
+ if (char === "\\" && quote === '"') {
9
+ index += 1;
10
+ }
11
+ else if (char === quote) {
12
+ return index;
13
+ }
14
+ }
15
+ return -1;
16
+ }
17
+ /**
18
+ * The keys of a .env file as dotenv reads it: `KEY=value`, an optional `export`,
19
+ * `#` comments, and values in single, double or backtick quotes that may span lines.
20
+ * A value is only checked for being empty; it never leaves this function.
21
+ */
22
+ export function parseDotenv(content) {
23
+ const text = content.startsWith(BYTE_ORDER_MARK) ? content.slice(1) : content;
24
+ const lines = text.split(/\r?\n/);
25
+ const entries = [];
26
+ const malformed = [];
27
+ for (let index = 0; index < lines.length; index += 1) {
28
+ const trimmed = (lines[index] ?? "").trim();
29
+ if (trimmed === "" || trimmed.startsWith("#"))
30
+ continue;
31
+ const match = ENTRY.exec(trimmed);
32
+ if (match === null) {
33
+ malformed.push(index + 1);
34
+ continue;
35
+ }
36
+ const key = match[1] ?? "";
37
+ const rest = match[2] ?? "";
38
+ const quote = rest.charAt(0);
39
+ if (!QUOTES.includes(quote)) {
40
+ // Unquoted, a # starts a comment.
41
+ entries.push({ key, line: index + 1, empty: (rest.split("#")[0] ?? "").trim() === "" });
42
+ continue;
43
+ }
44
+ const close = closingQuote(rest, quote, 1);
45
+ if (close >= 0) {
46
+ entries.push({ key, line: index + 1, empty: close === 1 });
47
+ continue;
48
+ }
49
+ // The value goes on to the line with the closing quote.
50
+ let end = index + 1;
51
+ while (end < lines.length && closingQuote(lines[end] ?? "", quote, 0) < 0)
52
+ end += 1;
53
+ if (end === lines.length) {
54
+ malformed.push(index + 1);
55
+ continue;
56
+ }
57
+ entries.push({ key, line: index + 1, empty: false });
58
+ index = end;
59
+ }
60
+ return { entries, malformed };
61
+ }
62
+ //# sourceMappingURL=dotenv.js.map
@@ -0,0 +1,17 @@
1
+ import { CheckDotenv } from "./application/check-dotenv.use-case.js";
2
+ import { InspectPath } from "./application/inspect-path.use-case.js";
3
+ import { ShowVariables } from "./application/show-variables.use-case.js";
4
+ import { checkView, pathView, showView } from "./presentation/env.views.js";
5
+ export const envModule = {
6
+ id: "env",
7
+ summary: "env.summary",
8
+ about: "env.about",
9
+ examples: ["kiriya env show node", "kiriya env path", "kiriya env check"],
10
+ guide: "https://github.com/SatPaingOo/kiriya/blob/main/docs/modules/env.md",
11
+ register(registrar, ports) {
12
+ registrar.add(new ShowVariables(ports.environment), showView);
13
+ registrar.add(new InspectPath(ports.environment, ports.fileSystem), pathView);
14
+ registrar.add(new CheckDotenv(ports.fileSystem, ports.fileContent), checkView);
15
+ },
16
+ };
17
+ //# sourceMappingURL=env.module.js.map
@@ -0,0 +1,43 @@
1
+ import { message } from "../../../core/domain/message.js";
2
+ /** NAME=value, one per line, as shells print them. */
3
+ export const showView = (output, format) => {
4
+ const lines = output.variables.map((variable) => variable.value === null
5
+ ? `${variable.name}=${format.dim(format.text(message("env.show.hidden-value")))}`
6
+ : `${variable.name}=${variable.value}`);
7
+ if (output.hidden > 0)
8
+ lines.push(format.dim(format.text(message("env.show.hidden", { count: output.hidden }))));
9
+ return lines;
10
+ };
11
+ const STATUS_KEYS = {
12
+ ok: "env.path.status.ok",
13
+ missing: "env.path.status.missing",
14
+ "not-a-folder": "env.path.status.not-a-folder",
15
+ duplicate: "env.path.status.duplicate",
16
+ empty: "env.path.status.empty",
17
+ relative: "env.path.status.relative",
18
+ };
19
+ function paintStatus(status, text, format) {
20
+ if (status === "ok")
21
+ return format.green(text);
22
+ return status === "missing" || status === "not-a-folder" ? format.red(text) : format.yellow(text);
23
+ }
24
+ export const pathView = (output, format) => {
25
+ const labels = output.entries.map((entry) => format.text(message(STATUS_KEYS[entry.status])));
26
+ const statusWidth = Math.max(0, ...labels.map((label) => label.length));
27
+ const indexWidth = String(output.entries.length).length;
28
+ return output.entries.map((entry, position) => {
29
+ const status = paintStatus(entry.status, (labels[position] ?? "").padEnd(statusWidth), format);
30
+ const expanded = entry.folder !== entry.entry.trim() ? format.dim(` → ${entry.folder}`) : "";
31
+ const repeat = entry.duplicateOf === null
32
+ ? ""
33
+ : format.dim(` ${format.text(message("env.path.same-as", { index: entry.duplicateOf }))}`);
34
+ return ` ${String(entry.index).padStart(indexWidth)} ${status} ${entry.entry}${expanded}${repeat}`;
35
+ });
36
+ };
37
+ /** Problems arrive as failures and warnings; a file with every variable set gets one line. */
38
+ export const checkView = (output, format) => output.fileExists && output.missing.length === 0
39
+ ? [
40
+ format.green(format.text(message("env.check.ok", { count: output.expected, example: output.example, file: output.file }))),
41
+ ]
42
+ : [];
43
+ //# sourceMappingURL=env.views.js.map
@@ -0,0 +1,149 @@
1
+ import path from "node:path";
2
+ import { CLEAN_RULES } from "../../../config/clean-rules.js";
3
+ import { measure } from "../../../core/application/paths.js";
4
+ import { refuseProtected, requireTypedConfirmation } from "../../../core/application/safety.js";
5
+ import { done, preview } from "../../../core/domain/command.js";
6
+ import { KiriyaError, NotFoundError } from "../../../core/domain/errors.js";
7
+ import { RawReader } from "../../../core/domain/input-schema.js";
8
+ import { message } from "../../../core/domain/message.js";
9
+ import { byCodePoint } from "../../../core/domain/names.js";
10
+ import { formatBytes } from "../../../core/domain/values/bytes.js";
11
+ function ruleFor(name, siblings) {
12
+ return CLEAN_RULES.find((rule) => {
13
+ const sibling = rule.sibling;
14
+ return rule.directories.includes(name) && (sibling === null || siblings.some((item) => sibling.test(item)));
15
+ });
16
+ }
17
+ export const cleanSpec = {
18
+ id: "files.clean",
19
+ summary: "files.clean.summary",
20
+ examples: ["kiriya files clean ~/projects", "kiriya files clean --apply", "kiriya files clean . --depth 3 --json"],
21
+ safety: "destroy",
22
+ idempotent: true,
23
+ usesNetwork: false,
24
+ runsUserCommands: false,
25
+ input: {
26
+ positionals: [
27
+ { name: "folder", description: "files.clean.arg.folder", required: false, variadic: false, path: true },
28
+ ],
29
+ options: {
30
+ depth: { type: "string", description: "files.clean.option.depth", valueName: "<n>" },
31
+ apply: { type: "boolean", description: "files.clean.option.apply" },
32
+ confirm: { type: "string", description: "files.clean.option.confirm", valueName: "<count>", terminalOnly: true },
33
+ },
34
+ parse(raw) {
35
+ const reader = new RawReader(raw);
36
+ return {
37
+ folder: reader.positional(0) ?? ".",
38
+ depth: reader.positiveInteger("depth", 8),
39
+ apply: reader.flag("apply"),
40
+ confirm: reader.string("confirm"),
41
+ };
42
+ },
43
+ },
44
+ };
45
+ export class CleanBuilds {
46
+ fileSystem;
47
+ processRunner;
48
+ protectedPaths;
49
+ spec = cleanSpec;
50
+ constructor(fileSystem, processRunner, protectedPaths) {
51
+ this.fileSystem = fileSystem;
52
+ this.processRunner = processRunner;
53
+ this.protectedPaths = protectedPaths;
54
+ }
55
+ async execute(input, context) {
56
+ const folder = path.resolve(context.cwd, input.folder);
57
+ const stat = await this.fileSystem.stat(folder);
58
+ if (stat === null)
59
+ throw new NotFoundError("core.fs.not-found", { path: folder });
60
+ if (stat.kind !== "directory")
61
+ throw new NotFoundError("core.path.not-a-folder", { path: folder });
62
+ const git = await this.processRunner.find("git");
63
+ const candidates = [];
64
+ let tracked = 0;
65
+ const visit = async (dir, depth) => {
66
+ let entries;
67
+ try {
68
+ entries = await this.fileSystem.readDirectory(dir);
69
+ }
70
+ catch (error) {
71
+ if (error instanceof KiriyaError)
72
+ return;
73
+ throw error;
74
+ }
75
+ const siblings = entries.map((entry) => entry.name);
76
+ for (const entry of entries) {
77
+ if (context.signal.aborted)
78
+ return;
79
+ if (entry.kind !== "directory" || entry.name === ".git")
80
+ continue;
81
+ const entryPath = path.join(dir, entry.name);
82
+ const rule = ruleFor(entry.name, siblings);
83
+ if (rule === undefined) {
84
+ // An unrecognised node_modules is still not worth walking into.
85
+ if (entry.name !== "node_modules" && depth < input.depth)
86
+ await visit(entryPath, depth + 1);
87
+ continue;
88
+ }
89
+ if (git !== null && (await this.insideRepository(dir))) {
90
+ const listed = await this.processRunner.run(git, ["-C", dir, "ls-files", "--", entry.name], {
91
+ timeoutMs: 60_000,
92
+ signal: context.signal,
93
+ });
94
+ if (listed.code === 0 && listed.stdout.trim() !== "") {
95
+ tracked += 1;
96
+ continue;
97
+ }
98
+ }
99
+ const { bytes } = await measure(this.fileSystem, entryPath);
100
+ candidates.push({ path: entryPath, reason: message(rule.reason), bytes });
101
+ }
102
+ };
103
+ await visit(folder, 1);
104
+ candidates.sort((a, b) => b.bytes - a.bytes || byCodePoint(a.path, b.path));
105
+ const bytes = candidates.reduce((sum, candidate) => sum + candidate.bytes, 0);
106
+ const warnings = git === null ? [message("files.clean.no-git")] : [];
107
+ const data = { folder, candidates, bytes, tracked, removed: 0 };
108
+ if (candidates.length === 0)
109
+ return done(data, { warnings });
110
+ if (!input.apply)
111
+ return preview(data, "--apply", warnings);
112
+ refuseProtected(this.protectedPaths, candidates.map((candidate) => candidate.path), context.cwd);
113
+ // Everything here is rebuildable, so it is removed for good rather than filling the trash.
114
+ const warning = message("files.clean.warn", { count: candidates.length, size: formatBytes(bytes) });
115
+ await requireTypedConfirmation(context, warning, String(candidates.length), input.confirm);
116
+ const failures = [];
117
+ let removed = 0;
118
+ for (const candidate of candidates) {
119
+ try {
120
+ await this.fileSystem.remove(candidate.path);
121
+ removed += 1;
122
+ }
123
+ catch (error) {
124
+ if (!(error instanceof KiriyaError))
125
+ throw error;
126
+ failures.push(error.detail);
127
+ }
128
+ }
129
+ return done({ ...data, removed }, { warnings, failures });
130
+ }
131
+ async insideRepository(folder) {
132
+ for (let current = folder;;) {
133
+ try {
134
+ if ((await this.fileSystem.lstat(path.join(current, ".git"))) !== null)
135
+ return true;
136
+ }
137
+ catch (error) {
138
+ if (error instanceof KiriyaError)
139
+ return false;
140
+ throw error;
141
+ }
142
+ const parent = path.dirname(current);
143
+ if (parent === current)
144
+ return false;
145
+ current = parent;
146
+ }
147
+ }
148
+ }
149
+ //# sourceMappingURL=clean-builds.use-case.js.map
@@ -0,0 +1,127 @@
1
+ import path from "node:path";
2
+ import { walk } from "../../../core/application/walk.js";
3
+ import { done } from "../../../core/domain/command.js";
4
+ import { NotFoundError, UsageError } from "../../../core/domain/errors.js";
5
+ import { RawReader } from "../../../core/domain/input-schema.js";
6
+ import { message } from "../../../core/domain/message.js";
7
+ import { byCodePoint } from "../../../core/domain/names.js";
8
+ import { readTextFile } from "./text-files.js";
9
+ export const compareSpec = {
10
+ id: "files.compare",
11
+ summary: "files.compare.summary",
12
+ examples: [
13
+ "kiriya files compare appsettings.json appsettings.Production.json",
14
+ "kiriya files compare dist backup/dist",
15
+ "kiriya files compare a b --all --json",
16
+ ],
17
+ safety: "read",
18
+ idempotent: true,
19
+ usesNetwork: false,
20
+ runsUserCommands: false,
21
+ input: {
22
+ positionals: [
23
+ { name: "a", description: "files.compare.arg.a", required: true, variadic: false, path: true },
24
+ { name: "b", description: "files.compare.arg.b", required: true, variadic: false, path: true },
25
+ ],
26
+ options: { all: { type: "boolean", description: "files.compare.option.all" } },
27
+ parse(raw) {
28
+ const reader = new RawReader(raw);
29
+ return { a: reader.positional(0) ?? "", b: reader.positional(1) ?? "", all: reader.flag("all") };
30
+ },
31
+ },
32
+ };
33
+ export class ComparePaths {
34
+ fileSystem;
35
+ content;
36
+ hasher;
37
+ spec = compareSpec;
38
+ constructor(fileSystem, content, hasher) {
39
+ this.fileSystem = fileSystem;
40
+ this.content = content;
41
+ this.hasher = hasher;
42
+ }
43
+ async execute(input, context) {
44
+ const a = path.resolve(context.cwd, input.a);
45
+ const b = path.resolve(context.cwd, input.b);
46
+ const statA = await this.fileSystem.lstat(a);
47
+ if (statA === null)
48
+ throw new NotFoundError("core.fs.not-found", { path: a });
49
+ const statB = await this.fileSystem.lstat(b);
50
+ if (statB === null)
51
+ throw new NotFoundError("core.fs.not-found", { path: b });
52
+ if ((statA.kind === "directory") !== (statB.kind === "directory"))
53
+ throw new UsageError("files.compare.mixed");
54
+ const output = statA.kind === "directory"
55
+ ? await this.compareFolders(a, b, input.all)
56
+ : await this.compareFiles(a, b, statA.size, statB.size);
57
+ const same = output.kind === "files"
58
+ ? output.identical
59
+ : output.onlyInA.length + output.onlyInB.length + output.different.length === 0;
60
+ // Like diff and cmp, a difference is exit code 1.
61
+ return done(output, { failures: same ? [] : [message("files.compare.different")] });
62
+ }
63
+ async sameContent(a, b, sizeA, sizeB) {
64
+ if (sizeA !== sizeB)
65
+ return false;
66
+ return (await this.hasher.hashFile(a, "sha256")) === (await this.hasher.hashFile(b, "sha256"));
67
+ }
68
+ async compareFiles(a, b, sizeA, sizeB) {
69
+ const base = { kind: "files", a, b, sizeA, sizeB };
70
+ if (await this.sameContent(a, b, sizeA, sizeB))
71
+ return { ...base, identical: true, text: null };
72
+ const textA = await readTextFile(this.fileSystem, this.content, a);
73
+ const textB = await readTextFile(this.fileSystem, this.content, b);
74
+ if (textA === null || textB === null)
75
+ return { ...base, identical: false, text: null };
76
+ const linesA = textA.text.split(/\r?\n/);
77
+ const linesB = textB.text.split(/\r?\n/);
78
+ const index = linesA.findIndex((line, position) => line !== linesB[position]);
79
+ const at = index < 0 ? linesA.length : index;
80
+ return {
81
+ ...base,
82
+ identical: false,
83
+ text: {
84
+ sameText: textA.text.replace(/\r\n/g, "\n") === textB.text.replace(/\r\n/g, "\n"),
85
+ encodingA: textA.encoding,
86
+ encodingB: textB.encoding,
87
+ line: at + 1,
88
+ linesA: linesA.length,
89
+ linesB: linesB.length,
90
+ lineA: linesA[at] ?? null,
91
+ lineB: linesB[at] ?? null,
92
+ },
93
+ };
94
+ }
95
+ async scan(root, all) {
96
+ const entries = new Map();
97
+ for await (const entry of walk(this.fileSystem, root, { all })) {
98
+ const isDirectory = entry.kind === "directory";
99
+ const stat = isDirectory ? null : await this.fileSystem.lstat(entry.path);
100
+ if (!isDirectory && stat === null)
101
+ continue;
102
+ entries.set(entry.rel, { path: entry.path, isDirectory, size: stat?.size ?? 0 });
103
+ }
104
+ return entries;
105
+ }
106
+ async compareFolders(a, b, all) {
107
+ const left = await this.scan(a, all);
108
+ const right = await this.scan(b, all);
109
+ const onlyInA = [...left.keys()].filter((rel) => !right.has(rel)).sort(byCodePoint);
110
+ const onlyInB = [...right.keys()].filter((rel) => !left.has(rel)).sort(byCodePoint);
111
+ const different = [];
112
+ for (const [rel, entry] of left) {
113
+ const other = right.get(rel);
114
+ if (other === undefined)
115
+ continue;
116
+ if (entry.isDirectory !== other.isDirectory) {
117
+ different.push({ path: rel, reason: "kind" });
118
+ }
119
+ else if (!entry.isDirectory && !(await this.sameContent(entry.path, other.path, entry.size, other.size))) {
120
+ different.push({ path: rel, reason: "content" });
121
+ }
122
+ }
123
+ different.sort((x, y) => byCodePoint(x.path, y.path));
124
+ return { kind: "folders", a, b, entries: left.size, onlyInA, onlyInB, different };
125
+ }
126
+ }
127
+ //# sourceMappingURL=compare-paths.use-case.js.map
@@ -0,0 +1,101 @@
1
+ import path from "node:path";
2
+ import { measure } from "../../../core/application/paths.js";
3
+ import { refuseProtected, requireTypedConfirmation } from "../../../core/application/safety.js";
4
+ import { done, preview } from "../../../core/domain/command.js";
5
+ import { KiriyaError } from "../../../core/domain/errors.js";
6
+ import { RawReader } from "../../../core/domain/input-schema.js";
7
+ import { message } from "../../../core/domain/message.js";
8
+ import { planTransfers } from "./transfers.js";
9
+ /** The arguments and flags copy and move share. */
10
+ export const transferInput = {
11
+ positionals: [
12
+ { name: "sources", description: "files.transfer.arg.sources", required: true, variadic: true, path: true },
13
+ { name: "target", description: "files.transfer.arg.target", required: true, variadic: false, path: true },
14
+ ],
15
+ options: {
16
+ overwrite: { type: "boolean", description: "files.transfer.option.overwrite" },
17
+ "dry-run": { type: "boolean", description: "files.transfer.option.dry-run" },
18
+ confirm: { type: "string", description: "files.transfer.option.confirm", valueName: "<count>", terminalOnly: true },
19
+ },
20
+ parse(raw) {
21
+ const reader = new RawReader(raw);
22
+ const positionals = reader.positionalsFrom(0);
23
+ return {
24
+ sources: positionals.slice(0, -1),
25
+ target: positionals.at(-1) ?? "",
26
+ overwrite: reader.flag("overwrite"),
27
+ dryRun: reader.flag("dry-run"),
28
+ confirm: reader.string("confirm"),
29
+ };
30
+ },
31
+ };
32
+ export const copySpec = {
33
+ id: "files.copy",
34
+ summary: "files.copy.summary",
35
+ examples: [
36
+ "kiriya files copy report.txt backup/",
37
+ 'kiriya files copy "src/**/*.ts" snapshot/ --dry-run',
38
+ "kiriya files copy config.json config.old.json",
39
+ ],
40
+ safety: "destroy",
41
+ idempotent: false,
42
+ usesNetwork: false,
43
+ runsUserCommands: false,
44
+ input: transferInput,
45
+ };
46
+ export class CopyPaths {
47
+ fileSystem;
48
+ protectedPaths;
49
+ environment;
50
+ spec = copySpec;
51
+ constructor(fileSystem, protectedPaths, environment) {
52
+ this.fileSystem = fileSystem;
53
+ this.protectedPaths = protectedPaths;
54
+ this.environment = environment;
55
+ }
56
+ async execute(input, context) {
57
+ const windows = this.environment.os === "windows";
58
+ const transfers = await planTransfers(this.fileSystem, input.sources, input.target, context.cwd, windows);
59
+ const planned = [];
60
+ for (const transfer of transfers) {
61
+ const { bytes } = await measure(this.fileSystem, transfer.source);
62
+ planned.push({ ...transfer, bytes, outcome: "planned", reason: null });
63
+ }
64
+ const conflicts = planned.filter((item) => item.conflict);
65
+ const data = {
66
+ items: planned,
67
+ bytes: planned.reduce((sum, item) => sum + item.bytes, 0),
68
+ conflicts: conflicts.length,
69
+ };
70
+ if (input.dryRun)
71
+ return preview(data, "");
72
+ if (conflicts.length > 0) {
73
+ if (!input.overwrite) {
74
+ return done(data, {
75
+ failures: conflicts.map((item) => message("core.fs.exists", { path: item.destination })),
76
+ warnings: [message("files.transfer.nothing-changed")],
77
+ });
78
+ }
79
+ refuseProtected(this.protectedPaths, conflicts.map((item) => item.destination), context.cwd);
80
+ const warning = message("files.copy.warn-overwrite", { count: conflicts.length });
81
+ await requireTypedConfirmation(context, warning, String(conflicts.length), input.confirm);
82
+ }
83
+ const items = [];
84
+ const failures = [];
85
+ for (const item of planned) {
86
+ try {
87
+ await this.fileSystem.createDirectory(path.dirname(item.destination));
88
+ await this.fileSystem.copy(item.source, item.destination, input.overwrite);
89
+ items.push({ ...item, outcome: "copied" });
90
+ }
91
+ catch (error) {
92
+ if (!(error instanceof KiriyaError))
93
+ throw error;
94
+ items.push({ ...item, outcome: "failed", reason: error.detail });
95
+ failures.push(error.detail);
96
+ }
97
+ }
98
+ return done({ ...data, items }, { failures });
99
+ }
100
+ }
101
+ //# sourceMappingURL=copy-paths.use-case.js.map
@@ -0,0 +1,85 @@
1
+ import path from "node:path";
2
+ import { done } from "../../../core/domain/command.js";
3
+ import { KiriyaError, 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 { invalidNameReason } from "../../../core/domain/names.js";
7
+ export const createSpec = {
8
+ id: "files.new",
9
+ summary: "files.new.summary",
10
+ examples: [
11
+ "kiriya files new notes.md",
12
+ "kiriya files new src/app/ docs/",
13
+ 'kiriya files new .env.example --content "PORT=3000"',
14
+ ],
15
+ safety: "write",
16
+ idempotent: false,
17
+ usesNetwork: false,
18
+ runsUserCommands: false,
19
+ input: {
20
+ positionals: [{ name: "paths", description: "files.new.arg.paths", required: true, variadic: true, path: true }],
21
+ options: {
22
+ dir: { type: "boolean", description: "files.new.option.dir" },
23
+ content: { type: "string", description: "files.new.option.content", valueName: "<text>" },
24
+ },
25
+ parse(raw) {
26
+ const reader = new RawReader(raw);
27
+ const directory = reader.flag("dir");
28
+ const content = reader.string("content");
29
+ if (directory && content !== undefined)
30
+ throw new UsageError("files.new.content-with-dir");
31
+ return { paths: reader.positionalsFrom(0), directory, content };
32
+ },
33
+ },
34
+ };
35
+ export class CreatePaths {
36
+ fileSystem;
37
+ environment;
38
+ spec = createSpec;
39
+ constructor(fileSystem, environment) {
40
+ this.fileSystem = fileSystem;
41
+ this.environment = environment;
42
+ }
43
+ async execute(input, context) {
44
+ const items = [];
45
+ const failures = [];
46
+ for (const arg of input.paths) {
47
+ const folder = input.directory || arg.endsWith("/") || (this.environment.os === "windows" && arg.endsWith("\\"));
48
+ const target = path.resolve(context.cwd, arg);
49
+ const kind = folder ? "directory" : "file";
50
+ const reason = await this.problem(target);
51
+ if (reason !== null) {
52
+ items.push({ path: target, kind, created: false, reason });
53
+ failures.push(reason);
54
+ continue;
55
+ }
56
+ try {
57
+ if (folder) {
58
+ await this.fileSystem.createDirectory(target);
59
+ }
60
+ else {
61
+ await this.fileSystem.createDirectory(path.dirname(target));
62
+ await this.fileSystem.createFile(target, input.content ?? "");
63
+ }
64
+ items.push({ path: target, kind, created: true, reason: null });
65
+ }
66
+ catch (error) {
67
+ if (!(error instanceof KiriyaError))
68
+ throw error;
69
+ items.push({ path: target, kind, created: false, reason: error.detail });
70
+ failures.push(error.detail);
71
+ }
72
+ }
73
+ return done({ items }, { failures });
74
+ }
75
+ async problem(target) {
76
+ const name = path.basename(target);
77
+ const invalid = invalidNameReason(name);
78
+ if (invalid !== null)
79
+ return message(invalid, { name });
80
+ if ((await this.fileSystem.lstat(target)) !== null)
81
+ return message("core.fs.exists", { path: target });
82
+ return null;
83
+ }
84
+ }
85
+ //# sourceMappingURL=create-paths.use-case.js.map