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,23 @@
1
+ import { CleanEngine } from "./application/clean-engine.use-case.js";
2
+ import { ListContainers } from "./application/list-containers.use-case.js";
3
+ import { createRebuild, createUp } from "./application/run-compose.use-case.js";
4
+ import { ShowLogs } from "./application/show-logs.use-case.js";
5
+ import { StopProject } from "./application/stop-project.use-case.js";
6
+ import { cleanView, downView, logsView, psView, rebuildView, upView } from "./presentation/docker.views.js";
7
+ export const dockerModule = {
8
+ id: "docker",
9
+ summary: "docker.summary",
10
+ about: "docker.about",
11
+ examples: ["kiriya docker ps", "kiriya docker up --build", "kiriya docker logs api -f", "kiriya docker clean"],
12
+ guide: "https://github.com/SatPaingOo/kiriya/blob/main/docs/modules/docker.md",
13
+ register(registrar, ports) {
14
+ const { fileSystem, fileContent, processRunner } = ports;
15
+ registrar.add(new ListContainers(fileSystem, fileContent, processRunner), psView);
16
+ registrar.add(createUp(fileSystem, fileContent, processRunner), upView);
17
+ registrar.add(new StopProject(fileSystem, fileContent, processRunner), downView);
18
+ registrar.add(new ShowLogs(fileSystem, fileContent, processRunner), logsView);
19
+ registrar.add(createRebuild(fileSystem, fileContent, processRunner), rebuildView);
20
+ registrar.add(new CleanEngine(processRunner), cleanView);
21
+ },
22
+ };
23
+ //# sourceMappingURL=docker.module.js.map
@@ -0,0 +1,105 @@
1
+ /** The file names docker compose looks for in a folder, in the order it prefers them. */
2
+ export const COMPOSE_FILE_NAMES = ["compose.yaml", "compose.yml", "docker-compose.yaml", "docker-compose.yml"];
3
+ /** The project name docker compose derives from a folder name: lower case, letters, digits, `-` and `_`. */
4
+ export function projectNameFromFolder(folderName) {
5
+ return folderName.toLowerCase().replace(/[^a-z0-9_-]/g, "");
6
+ }
7
+ function unquote(value) {
8
+ const trimmed = value.trim();
9
+ const first = trimmed[0];
10
+ if ((first === '"' || first === "'") && trimmed.length >= 2 && trimmed.endsWith(first))
11
+ return trimmed.slice(1, -1);
12
+ return trimmed;
13
+ }
14
+ /**
15
+ * Just enough YAML to show a project: the top-level name, the services, and each
16
+ * service's container_name, ports and build context. It is not a YAML parser, and
17
+ * docker compose itself stays the authority on the file.
18
+ */
19
+ export function parseCompose(text, fallbackProject) {
20
+ let project = fallbackProject;
21
+ const services = [];
22
+ let inServices = false;
23
+ let serviceIndent = -1;
24
+ let current;
25
+ let portsIndent = -1;
26
+ let buildIndent = -1;
27
+ for (const raw of text.split(/\r?\n/)) {
28
+ const line = raw.replace(/\s+#.*$/, "");
29
+ const trimmed = line.trim();
30
+ if (trimmed === "" || trimmed.startsWith("#"))
31
+ continue;
32
+ const indent = line.length - line.trimStart().length;
33
+ if (indent === 0) {
34
+ inServices = trimmed === "services:";
35
+ current = undefined;
36
+ portsIndent = -1;
37
+ buildIndent = -1;
38
+ const name = /^name:\s*(.+)$/.exec(trimmed);
39
+ if (name?.[1] !== undefined)
40
+ project = unquote(name[1]);
41
+ continue;
42
+ }
43
+ if (!inServices)
44
+ continue;
45
+ if (serviceIndent < 0)
46
+ serviceIndent = indent;
47
+ if (indent === serviceIndent) {
48
+ const key = /^([A-Za-z0-9_.-]+):\s*$/.exec(trimmed);
49
+ if (key?.[1] !== undefined) {
50
+ current = { name: key[1], container: null, ports: [], buildContext: null };
51
+ services.push(current);
52
+ }
53
+ portsIndent = -1;
54
+ buildIndent = -1;
55
+ continue;
56
+ }
57
+ if (current === undefined)
58
+ continue;
59
+ if (portsIndent >= 0 && indent > portsIndent && trimmed.startsWith("-")) {
60
+ current.ports.push(unquote(trimmed.slice(1)));
61
+ continue;
62
+ }
63
+ portsIndent = -1;
64
+ if (buildIndent >= 0 && indent > buildIndent) {
65
+ const context = /^context:\s*(.+)$/.exec(trimmed);
66
+ if (context?.[1] !== undefined)
67
+ current.buildContext = unquote(context[1]);
68
+ continue;
69
+ }
70
+ buildIndent = -1;
71
+ const pair = /^([A-Za-z0-9_.-]+):\s*(.*)$/.exec(trimmed);
72
+ if (pair === null)
73
+ continue;
74
+ const key = pair[1];
75
+ const value = pair[2] ?? "";
76
+ if (key === "container_name" && value !== "")
77
+ current.container = unquote(value);
78
+ else if (key === "ports" && value === "")
79
+ portsIndent = indent;
80
+ else if (key === "build" && value === "")
81
+ buildIndent = indent;
82
+ else if (key === "build")
83
+ current.buildContext = unquote(value);
84
+ }
85
+ return { project, services };
86
+ }
87
+ /**
88
+ * Published ports as host and container port: "8080:80" and "127.0.0.1:8080:80/tcp" both
89
+ * give 8080 and 80. A port with no host side is published at a random port and left out.
90
+ */
91
+ export function portMappings(services) {
92
+ const mappings = [];
93
+ for (const service of services) {
94
+ for (const port of service.ports) {
95
+ const parts = port.replace(/\/(tcp|udp|sctp)$/i, "").split(":");
96
+ const target = parts[parts.length - 1];
97
+ const host = parts[parts.length - 2];
98
+ if (host === undefined || target === undefined || host === "")
99
+ continue;
100
+ mappings.push({ host, service: service.name, target });
101
+ }
102
+ }
103
+ return mappings;
104
+ }
105
+ //# sourceMappingURL=compose-file.js.map
@@ -0,0 +1,65 @@
1
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2
+ function parseLine(line) {
3
+ try {
4
+ const parsed = JSON.parse(line);
5
+ return isRecord(parsed) ? [parsed] : [];
6
+ }
7
+ catch {
8
+ return [];
9
+ }
10
+ }
11
+ /** Docker prints JSON as one array or as one object per line, depending on the command and version. */
12
+ export function parseJsonRecords(text) {
13
+ const trimmed = text.trim();
14
+ if (trimmed === "")
15
+ return [];
16
+ if (trimmed.startsWith("[")) {
17
+ try {
18
+ const parsed = JSON.parse(trimmed);
19
+ return Array.isArray(parsed) ? parsed.filter(isRecord) : [];
20
+ }
21
+ catch {
22
+ return [];
23
+ }
24
+ }
25
+ return trimmed.split(/\r?\n/).flatMap(parseLine);
26
+ }
27
+ function field(record, name) {
28
+ const value = record[name];
29
+ if (typeof value === "string")
30
+ return value;
31
+ return typeof value === "number" ? String(value) : "";
32
+ }
33
+ /** `docker compose ps --all --format json`. */
34
+ export function parseContainers(text) {
35
+ return parseJsonRecords(text).map((record) => ({
36
+ name: field(record, "Name"),
37
+ service: field(record, "Service"),
38
+ state: field(record, "State"),
39
+ status: field(record, "Status"),
40
+ ports: field(record, "Ports"),
41
+ }));
42
+ }
43
+ /** `docker compose ls --all --format json`. */
44
+ export function parseProjects(text) {
45
+ return parseJsonRecords(text).map((record) => ({
46
+ name: field(record, "Name"),
47
+ status: field(record, "Status"),
48
+ configFiles: field(record, "ConfigFiles"),
49
+ }));
50
+ }
51
+ /** `docker system df --format "{{json .}}"`. */
52
+ export function parseDiskUsage(text) {
53
+ return parseJsonRecords(text).map((record) => ({
54
+ type: field(record, "Type"),
55
+ total: field(record, "TotalCount"),
56
+ active: field(record, "Active"),
57
+ size: field(record, "Size"),
58
+ reclaimable: field(record, "Reclaimable"),
59
+ }));
60
+ }
61
+ /** "Total reclaimed space: 1.2GB" in a prune command's output, as "1.2GB"; null when absent. */
62
+ export function reclaimedSpace(text) {
63
+ return /Total reclaimed space:\s*(\S+)/i.exec(text)?.[1] ?? null;
64
+ }
65
+ //# sourceMappingURL=engine-output.js.map
@@ -0,0 +1,63 @@
1
+ import { message } from "../../../core/domain/message.js";
2
+ /** Rows of cells as columns padded to their widest cell. */
3
+ function columns(rows) {
4
+ const widths = [];
5
+ for (const row of rows)
6
+ row.forEach((cell, index) => (widths[index] = Math.max(widths[index] ?? 0, cell.length)));
7
+ return rows.map((row) => ` ${row
8
+ .map((cell, index) => cell.padEnd(widths[index] ?? 0))
9
+ .join(" ")
10
+ .trimEnd()}`);
11
+ }
12
+ function portLines(ports, format) {
13
+ return ports.map((port) => ` ${format.text(message("docker.port", { ...port }))}`);
14
+ }
15
+ export const psView = (output, format) => {
16
+ if (output.mode === "projects") {
17
+ if (output.projects.length === 0)
18
+ return [format.dim(format.text(message("docker.ps.no-projects")))];
19
+ return columns(output.projects.map((project) => [project.name, project.status, project.configFiles]));
20
+ }
21
+ const header = `${format.bold(output.project)} ${format.dim(format.path(output.file))}`;
22
+ if (output.containers.length === 0)
23
+ return [header, format.dim(format.text(message("docker.ps.no-containers")))];
24
+ return [
25
+ header,
26
+ ...columns(output.containers.map((item) => [item.name, item.service, item.state, item.status, item.ports])),
27
+ ];
28
+ };
29
+ function startView(done) {
30
+ return (output, format) => output.succeeded
31
+ ? [format.green(format.text(message(done, { project: output.project }))), ...portLines(output.ports, format)]
32
+ : [];
33
+ }
34
+ export const upView = startView("docker.up.done");
35
+ export const rebuildView = startView("docker.rebuild.done");
36
+ export const downView = (output, format) => {
37
+ if (!output.succeeded)
38
+ return [];
39
+ const key = output.volumes ? "docker.down.done-volumes" : "docker.down.done";
40
+ return [format.green(format.text(message(key, { project: output.project })))];
41
+ };
42
+ /** The logs themselves already went to the terminal as docker printed them. */
43
+ export const logsView = () => [];
44
+ export const cleanView = (output, format) => {
45
+ const label = (key) => format.text(message(key));
46
+ const lines = columns([
47
+ [
48
+ label("docker.clean.column.type"),
49
+ label("docker.clean.column.total"),
50
+ label("docker.clean.column.active"),
51
+ label("docker.clean.column.size"),
52
+ label("docker.clean.column.reclaimable"),
53
+ ],
54
+ ...output.usage.map((row) => [row.type, row.total, row.active, row.size, row.reclaimable]),
55
+ ]);
56
+ lines[0] = format.bold(lines[0] ?? "");
57
+ for (const result of output.results.filter((item) => item.succeeded)) {
58
+ const space = result.reclaimed ?? "0B";
59
+ lines.push(format.green(format.text(message("docker.clean.reclaimed", { target: result.target, space }))));
60
+ }
61
+ return lines;
62
+ };
63
+ //# sourceMappingURL=docker.views.js.map
@@ -0,0 +1,128 @@
1
+ import { checkConfigTypes } from "../../../core/application/config-values.js";
2
+ import { done } from "../../../core/domain/command.js";
3
+ import { KiriyaError } from "../../../core/domain/errors.js";
4
+ import { message } from "../../../core/domain/message.js";
5
+ import { isAtLeast } from "../domain/versions.js";
6
+ /** The oldest Node.js release kiriya supports, as package.json's engines field says. */
7
+ const MINIMUM_NODE = "22.13.0";
8
+ const PROBE_TIMEOUT_MS = 20_000;
9
+ export const doctorSpec = {
10
+ id: "doctor",
11
+ summary: "doctor.summary",
12
+ examples: ["kiriya doctor", "kiriya doctor --json"],
13
+ safety: "read",
14
+ idempotent: true,
15
+ usesNetwork: false,
16
+ runsUserCommands: false,
17
+ input: { positionals: [], options: {}, parse: () => ({}) },
18
+ };
19
+ export class RunChecks {
20
+ processRunner;
21
+ environment;
22
+ config;
23
+ trash;
24
+ clipboard;
25
+ plugins;
26
+ runtime;
27
+ spec = doctorSpec;
28
+ constructor(processRunner, environment, config, trash, clipboard, plugins, runtime) {
29
+ this.processRunner = processRunner;
30
+ this.environment = environment;
31
+ this.config = config;
32
+ this.trash = trash;
33
+ this.clipboard = clipboard;
34
+ this.plugins = plugins;
35
+ this.runtime = runtime;
36
+ }
37
+ async execute(_input, context) {
38
+ const loaded = this.plugins.loaded();
39
+ const problems = this.plugins.problems();
40
+ const node = this.runtime.nodeVersion;
41
+ const checks = [
42
+ { name: "kiriya", status: "ok", detail: message("doctor.check.kiriya", { version: this.runtime.kiriyaVersion }) },
43
+ isAtLeast(node, MINIMUM_NODE)
44
+ ? { name: "node", status: "ok", detail: message("doctor.check.node", { version: node }) }
45
+ : {
46
+ name: "node",
47
+ status: "warn",
48
+ detail: message("doctor.check.node-old", { version: node, minimum: MINIMUM_NODE }),
49
+ },
50
+ { name: "os", status: "ok", detail: message("doctor.check.os", { os: this.environment.os }) },
51
+ await this.configCheck(),
52
+ {
53
+ name: "trash",
54
+ status: "ok",
55
+ detail: message("doctor.check.trash", { location: message(this.trash.location) }),
56
+ },
57
+ await this.clipboardCheck(),
58
+ await this.gitCheck(context.signal),
59
+ await this.dockerCheck(context.signal),
60
+ {
61
+ name: "plugins",
62
+ status: problems.length > 0 ? "fail" : "ok",
63
+ detail: message("doctor.check.plugins", { loaded: loaded.length, failed: problems.length }),
64
+ },
65
+ ];
66
+ const failures = [
67
+ ...checks.filter((check) => check.status === "fail" && check.name !== "plugins").map((check) => check.detail),
68
+ ...problems.map((problem) => message("doctor.plugin-failed", { entry: problem.entry, reason: problem.reason })),
69
+ ];
70
+ return done({ checks, plugins: loaded, pluginProblems: problems }, { failures });
71
+ }
72
+ async configCheck() {
73
+ const path = this.config.path;
74
+ try {
75
+ if (!(await this.config.exists())) {
76
+ return { name: "config", status: "ok", detail: message("doctor.check.config-missing", { path }) };
77
+ }
78
+ checkConfigTypes(await this.config.read(), path);
79
+ return { name: "config", status: "ok", detail: message("doctor.check.config", { path }) };
80
+ }
81
+ catch (error) {
82
+ if (!(error instanceof KiriyaError))
83
+ throw error;
84
+ return { name: "config", status: "fail", detail: error.detail };
85
+ }
86
+ }
87
+ async clipboardCheck() {
88
+ try {
89
+ const backend = await this.clipboard.backend();
90
+ return { name: "clipboard", status: "ok", detail: message("doctor.check.clipboard", { backend }) };
91
+ }
92
+ catch (error) {
93
+ if (!(error instanceof KiriyaError) || error.kind === "interrupted")
94
+ throw error;
95
+ return { name: "clipboard", status: "warn", detail: error.detail };
96
+ }
97
+ }
98
+ /** The first line a program prints for `args`, or null when it is missing or fails. */
99
+ async probe(program, args, signal) {
100
+ try {
101
+ const result = await this.processRunner.run(program, args, { timeoutMs: PROBE_TIMEOUT_MS, signal });
102
+ const line = result.stdout.trim().split(/\r?\n/)[0] ?? "";
103
+ return result.code === 0 && line !== "" ? line : null;
104
+ }
105
+ catch (error) {
106
+ if (error instanceof KiriyaError && error.kind !== "interrupted")
107
+ return null;
108
+ throw error;
109
+ }
110
+ }
111
+ async gitCheck(signal) {
112
+ const git = await this.processRunner.find("git");
113
+ const version = git === null ? null : await this.probe(git, ["--version"], signal);
114
+ return version === null
115
+ ? { name: "git", status: "warn", detail: message("doctor.check.git-missing") }
116
+ : { name: "git", status: "ok", detail: message("doctor.check.git", { version }) };
117
+ }
118
+ async dockerCheck(signal) {
119
+ const docker = await this.processRunner.find("docker");
120
+ if (docker === null)
121
+ return { name: "docker", status: "warn", detail: message("doctor.check.docker-missing") };
122
+ const server = await this.probe(docker, ["version", "--format", "{{.Server.Version}}"], signal);
123
+ return server === null
124
+ ? { name: "docker", status: "warn", detail: message("doctor.check.docker-stopped") }
125
+ : { name: "docker", status: "ok", detail: message("doctor.check.docker", { version: server }) };
126
+ }
127
+ }
128
+ //# sourceMappingURL=run-checks.use-case.js.map
@@ -0,0 +1,13 @@
1
+ import { RunChecks } from "./application/run-checks.use-case.js";
2
+ import { doctorView } from "./presentation/doctor.view.js";
3
+ export const doctorModule = {
4
+ id: "doctor",
5
+ summary: "doctor.summary",
6
+ about: "doctor.about",
7
+ guide: "https://github.com/SatPaingOo/kiriya/blob/main/docs/modules/doctor.md",
8
+ register(registrar, ports) {
9
+ const { processRunner, environment, config, trash, clipboard, plugins, runtime } = ports;
10
+ registrar.add(new RunChecks(processRunner, environment, config, trash, clipboard, plugins, runtime), doctorView);
11
+ },
12
+ };
13
+ //# sourceMappingURL=doctor.module.js.map
@@ -0,0 +1,22 @@
1
+ /** "v24.14.0" or "22.13" -> [24, 14, 0] or [22, 13]; a part that is not a number counts as 0. */
2
+ export function versionParts(version) {
3
+ return version
4
+ .trim()
5
+ .replace(/^v/i, "")
6
+ .split(".")
7
+ .map((part) => Number.parseInt(part, 10))
8
+ .map((part) => (Number.isNaN(part) ? 0 : part));
9
+ }
10
+ /** Compares part by part; a missing part counts as 0, so 22.13 equals 22.13.0. */
11
+ export function isAtLeast(version, minimum) {
12
+ const actual = versionParts(version);
13
+ const required = versionParts(minimum);
14
+ for (let index = 0; index < Math.max(actual.length, required.length); index += 1) {
15
+ const a = actual[index] ?? 0;
16
+ const b = required[index] ?? 0;
17
+ if (a !== b)
18
+ return a > b;
19
+ }
20
+ return true;
21
+ }
22
+ //# sourceMappingURL=versions.js.map
@@ -0,0 +1,30 @@
1
+ import { message } from "../../../core/domain/message.js";
2
+ const STATUS_KEYS = {
3
+ ok: "doctor.status.ok",
4
+ warn: "doctor.status.warn",
5
+ fail: "doctor.status.fail",
6
+ };
7
+ function paint(status, text, format) {
8
+ if (status === "ok")
9
+ return format.green(text);
10
+ return status === "warn" ? format.yellow(text) : format.red(text);
11
+ }
12
+ export const doctorView = (output, format) => {
13
+ const statusWidth = Math.max(...output.checks.map((check) => format.text(message(STATUS_KEYS[check.status])).length));
14
+ const nameWidth = Math.max(...output.checks.map((check) => check.name.length));
15
+ const lines = output.checks.map((check) => {
16
+ const status = format.text(message(STATUS_KEYS[check.status])).padEnd(statusWidth);
17
+ return ` ${paint(check.status, status, format)} ${check.name.padEnd(nameWidth)} ${format.text(check.detail)}`;
18
+ });
19
+ if (output.plugins.length > 0) {
20
+ lines.push("", format.bold(format.text(message("doctor.plugins"))));
21
+ const idWidth = Math.max(...output.plugins.map((plugin) => plugin.id.length));
22
+ for (const plugin of output.plugins) {
23
+ const label = [plugin.name ?? plugin.entry, plugin.version].filter((part) => part !== null).join(" ");
24
+ const detail = message("doctor.plugin", { commands: plugin.commands, location: format.path(plugin.location) });
25
+ lines.push(` ${plugin.id.padEnd(idWidth)} ${label} ${format.dim(format.text(detail))}`);
26
+ }
27
+ }
28
+ return lines;
29
+ };
30
+ //# sourceMappingURL=doctor.view.js.map
@@ -0,0 +1,116 @@
1
+ import path from "node:path";
2
+ import { done } from "../../../core/domain/command.js";
3
+ import { utf8Text } from "../../../core/domain/encodings.js";
4
+ import { NotFoundError, OperationFailedError } from "../../../core/domain/errors.js";
5
+ import { RawReader } from "../../../core/domain/input-schema.js";
6
+ import { message } from "../../../core/domain/message.js";
7
+ import { formatBytes } from "../../../core/domain/values/bytes.js";
8
+ import { parseDotenv } from "../domain/dotenv.js";
9
+ const LIMIT_BYTES = 1024 * 1024;
10
+ export const checkSpec = {
11
+ id: "env.check",
12
+ summary: "env.check.summary",
13
+ examples: ["kiriya env check", "kiriya env check --file .env.local --example .env.example"],
14
+ safety: "read",
15
+ idempotent: true,
16
+ usesNetwork: false,
17
+ runsUserCommands: false,
18
+ input: {
19
+ positionals: [],
20
+ options: {
21
+ file: { type: "string", description: "env.check.option.file", valueName: "<path>", path: true },
22
+ example: { type: "string", description: "env.check.option.example", valueName: "<path>", path: true },
23
+ },
24
+ parse(raw) {
25
+ const reader = new RawReader(raw);
26
+ return { file: reader.string("file") ?? ".env", example: reader.string("example") ?? ".env.example" };
27
+ },
28
+ },
29
+ };
30
+ function names(keys) {
31
+ return keys.join(", ");
32
+ }
33
+ export class CheckDotenv {
34
+ fileSystem;
35
+ content;
36
+ spec = checkSpec;
37
+ constructor(fileSystem, content) {
38
+ this.fileSystem = fileSystem;
39
+ this.content = content;
40
+ }
41
+ async execute(input, context) {
42
+ const filePath = path.resolve(context.cwd, input.file);
43
+ const examplePath = path.resolve(context.cwd, input.example);
44
+ const exampleText = await this.readText(examplePath);
45
+ if (exampleText === null)
46
+ throw new NotFoundError("env.check.no-example", { path: examplePath });
47
+ const fileText = await this.readText(filePath);
48
+ const expected = parseDotenv(exampleText);
49
+ const actual = parseDotenv(fileText ?? "");
50
+ const expectedKeys = new Set(expected.entries.map((entry) => entry.key));
51
+ const actualKeys = new Set(actual.entries.map((entry) => entry.key));
52
+ // As in dotenv, a key set twice keeps its last value.
53
+ const lastEntries = new Map(actual.entries.map((entry) => [entry.key, entry]));
54
+ const counts = new Map();
55
+ for (const entry of actual.entries)
56
+ counts.set(entry.key, (counts.get(entry.key) ?? 0) + 1);
57
+ const output = {
58
+ file: input.file,
59
+ example: input.example,
60
+ fileExists: fileText !== null,
61
+ expected: expectedKeys.size,
62
+ missing: [...expectedKeys].filter((key) => !actualKeys.has(key)),
63
+ empty: [...lastEntries.values()]
64
+ .filter((entry) => entry.empty && expectedKeys.has(entry.key))
65
+ .map((entry) => entry.key),
66
+ extra: [...actualKeys].filter((key) => !expectedKeys.has(key)),
67
+ duplicates: [...counts].filter(([, count]) => count > 1).map(([key]) => key),
68
+ malformed: [
69
+ ...expected.malformed.map((line) => ({ path: input.example, line })),
70
+ ...actual.malformed.map((line) => ({ path: input.file, line })),
71
+ ],
72
+ };
73
+ return done(output, { failures: this.failures(output), warnings: this.warnings(output) });
74
+ }
75
+ failures(output) {
76
+ const failures = [];
77
+ if (!output.fileExists) {
78
+ failures.push(message("env.check.no-file", { path: output.file, example: output.example }));
79
+ }
80
+ if (output.missing.length > 0) {
81
+ const params = { count: output.missing.length, names: names(output.missing) };
82
+ failures.push(message("env.check.missing", { ...params, example: output.example, file: output.file }));
83
+ }
84
+ return failures;
85
+ }
86
+ warnings(output) {
87
+ const warnings = [];
88
+ if (output.empty.length > 0) {
89
+ warnings.push(message("env.check.empty", { count: output.empty.length, names: names(output.empty) }));
90
+ }
91
+ if (output.extra.length > 0) {
92
+ const params = { count: output.extra.length, names: names(output.extra) };
93
+ warnings.push(message("env.check.extra", { ...params, file: output.file, example: output.example }));
94
+ }
95
+ if (output.duplicates.length > 0) {
96
+ warnings.push(message("env.check.duplicates", { file: output.file, names: names(output.duplicates) }));
97
+ }
98
+ for (const line of output.malformed)
99
+ warnings.push(message("env.check.malformed", { ...line }));
100
+ return warnings;
101
+ }
102
+ /** The file's text, or null when there is no file there. */
103
+ async readText(target) {
104
+ const stat = await this.fileSystem.stat(target);
105
+ if (stat?.kind !== "file")
106
+ return null;
107
+ if (stat.size > LIMIT_BYTES) {
108
+ throw new OperationFailedError("env.check.too-large", { path: target, limit: formatBytes(LIMIT_BYTES) });
109
+ }
110
+ const text = utf8Text(await this.content.read(target));
111
+ if (text === null)
112
+ throw new OperationFailedError("env.check.not-text", { path: target });
113
+ return text;
114
+ }
115
+ }
116
+ //# sourceMappingURL=check-dotenv.use-case.js.map
@@ -0,0 +1,71 @@
1
+ import { done } from "../../../core/domain/command.js";
2
+ import { NotFoundError } from "../../../core/domain/errors.js";
3
+ import { RawReader } from "../../../core/domain/input-schema.js";
4
+ import { message } from "../../../core/domain/message.js";
5
+ import { comparableFolder, entryFolder, isAbsoluteFolder, listSeparator } from "./path-entries.js";
6
+ export const pathSpec = {
7
+ id: "env.path",
8
+ summary: "env.path.summary",
9
+ examples: ["kiriya env path", "kiriya env path PYTHONPATH --json"],
10
+ safety: "read",
11
+ idempotent: true,
12
+ usesNetwork: false,
13
+ runsUserCommands: false,
14
+ input: {
15
+ positionals: [{ name: "variable", description: "env.path.arg.variable", required: false, variadic: false }],
16
+ options: {},
17
+ parse: (raw) => ({ variable: new RawReader(raw).positional(0) ?? "PATH" }),
18
+ },
19
+ };
20
+ export class InspectPath {
21
+ environment;
22
+ fileSystem;
23
+ spec = pathSpec;
24
+ constructor(environment, fileSystem) {
25
+ this.environment = environment;
26
+ this.fileSystem = fileSystem;
27
+ }
28
+ async execute(input) {
29
+ const os = this.environment.os;
30
+ const variable = this.nameOnThisOs(input.variable);
31
+ const value = this.environment.variable(variable);
32
+ if (value === undefined)
33
+ throw new NotFoundError("env.path.not-set", { variable: input.variable });
34
+ const separator = listSeparator(os);
35
+ const firstSeen = new Map();
36
+ const entries = [];
37
+ for (const [position, entry] of value.split(separator).entries()) {
38
+ const index = position + 1;
39
+ const folder = entryFolder(entry, os, (name) => this.environment.variable(name));
40
+ entries.push({ index, entry, folder, ...(await this.check(folder, index, firstSeen)) });
41
+ }
42
+ const problems = entries.filter((entry) => entry.status !== "ok").length;
43
+ const warnings = problems > 0 ? [message("env.path.problems", { count: problems, total: entries.length })] : [];
44
+ return done({ variable, separator, entries }, { warnings });
45
+ }
46
+ /** Windows spells PATH as Path; show the name the OS uses. */
47
+ nameOnThisOs(name) {
48
+ if (this.environment.os !== "windows")
49
+ return name;
50
+ const wanted = name.toLowerCase();
51
+ return Object.keys(this.environment.variables()).find((key) => key.toLowerCase() === wanted) ?? name;
52
+ }
53
+ async check(folder, index, firstSeen) {
54
+ const os = this.environment.os;
55
+ if (folder === "")
56
+ return { status: "empty", duplicateOf: null };
57
+ // A relative entry is searched from whatever folder a program runs in.
58
+ if (!isAbsoluteFolder(folder, os))
59
+ return { status: "relative", duplicateOf: null };
60
+ const key = comparableFolder(folder, os);
61
+ const first = firstSeen.get(key);
62
+ if (first !== undefined)
63
+ return { status: "duplicate", duplicateOf: first };
64
+ firstSeen.set(key, index);
65
+ const stat = await this.fileSystem.stat(folder);
66
+ if (stat === null)
67
+ return { status: "missing", duplicateOf: null };
68
+ return { status: stat.kind === "directory" ? "ok" : "not-a-folder", duplicateOf: null };
69
+ }
70
+ }
71
+ //# sourceMappingURL=inspect-path.use-case.js.map