hammerkit 1.5.1 → 1.6.0-alpha.2

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 (480) hide show
  1. package/.agents/FEEDBACK.md +74 -0
  2. package/.agents/skills/before-commit/SKILL.md +101 -0
  3. package/.agents/skills/do-the-work/SKILL.md +64 -0
  4. package/.agents/skills/fix-ci/SKILL.md +98 -0
  5. package/.agents/skills/speckit-git-commit/SKILL.md +53 -0
  6. package/.agents/skills/speckit-git-feature/SKILL.md +72 -0
  7. package/.agents/skills/speckit-git-initialize/SKILL.md +54 -0
  8. package/.agents/skills/speckit-git-remote/SKILL.md +50 -0
  9. package/.agents/skills/speckit-git-validate/SKILL.md +54 -0
  10. package/.gitbook.yaml +5 -0
  11. package/.specify/extensions/.registry +37 -0
  12. package/.specify/extensions/git/README.md +100 -0
  13. package/.specify/extensions/git/commands/speckit.git.commit.md +48 -0
  14. package/.specify/extensions/git/commands/speckit.git.feature.md +67 -0
  15. package/.specify/extensions/git/commands/speckit.git.initialize.md +49 -0
  16. package/.specify/extensions/git/commands/speckit.git.remote.md +45 -0
  17. package/.specify/extensions/git/commands/speckit.git.validate.md +49 -0
  18. package/.specify/extensions/git/config-template.yml +62 -0
  19. package/.specify/extensions/git/extension.yml +140 -0
  20. package/.specify/extensions/git/git-config.yml +62 -0
  21. package/.specify/extensions.yml +149 -0
  22. package/.specify/feature.json +3 -0
  23. package/.specify/init-options.json +10 -0
  24. package/.specify/integration.json +15 -0
  25. package/.specify/integrations/claude.manifest.json +16 -0
  26. package/.specify/integrations/speckit.manifest.json +17 -0
  27. package/.specify/memory/constitution.md +162 -0
  28. package/.specify/templates/checklist-template.md +40 -0
  29. package/.specify/templates/constitution-template.md +50 -0
  30. package/.specify/templates/plan-template.md +113 -0
  31. package/.specify/templates/spec-template.md +131 -0
  32. package/.specify/templates/tasks-template.md +252 -0
  33. package/.specify/workflows/speckit/workflow.yml +77 -0
  34. package/.specify/workflows/workflow-registry.json +13 -0
  35. package/AGENTS.md +94 -0
  36. package/Dockerfile +3 -3
  37. package/FOLLOWUPS.md +34 -0
  38. package/README.md +9 -7
  39. package/SUMMARY.md +30 -5
  40. package/VALUES.md +53 -0
  41. package/build.schema.json +542 -408
  42. package/dist/cache/auto-pull.spec.js +123 -0
  43. package/dist/cache/backends/local-cache-backend.js +56 -0
  44. package/dist/cache/backends/local-cache-backend.spec.js +72 -0
  45. package/dist/cache/backends/s3-cache-backend.js +124 -0
  46. package/dist/cache/backends/s3-cache-backend.spec.js +118 -0
  47. package/dist/cache/cache-backend-registry.js +21 -0
  48. package/dist/cache/cache-backend-registry.spec.js +22 -0
  49. package/dist/cache/cache-backend.js +3 -0
  50. package/dist/cache/resolve-cache.js +60 -0
  51. package/dist/cache/resolve-cache.spec.js +52 -0
  52. package/dist/cli.js +112 -82
  53. package/dist/cli.spec.js +20 -2
  54. package/dist/console/get-logger.spec.js +23 -0
  55. package/dist/docker/get-default-platform.spec.js +29 -0
  56. package/dist/docker/longest-common-prefix.spec.js +26 -0
  57. package/dist/docker/package.js +283 -0
  58. package/dist/docker/package.spec.js +145 -0
  59. package/dist/docker/pull.js +4 -4
  60. package/dist/docker/remove-container.spec.js +32 -0
  61. package/dist/docker/stream.js +3 -3
  62. package/dist/docker/using-container.js +29 -11
  63. package/dist/docker/using-container.spec.js +90 -0
  64. package/dist/environment/merge-environment-variables.js +21 -0
  65. package/dist/environment/merge-environment-variables.spec.js +24 -0
  66. package/dist/environment/replace-env-variables.js +43 -10
  67. package/dist/environment/replace-env-variables.spec.js +47 -0
  68. package/dist/executer/abort.spec.js +18 -0
  69. package/dist/executer/await-completed-dependencies.js +56 -0
  70. package/dist/executer/cache-metadata.js +11 -0
  71. package/dist/executer/check-readiness.js +1 -1
  72. package/dist/{planner/work-node-port.js → executer/container-bind.js} +1 -1
  73. package/dist/executer/docker-service.js +110 -86
  74. package/dist/executer/docker-task.js +132 -0
  75. package/dist/executer/environment-mock.js +3 -4
  76. package/dist/executer/environment-mock.spec.js +22 -0
  77. package/dist/executer/event-cache.js +60 -127
  78. package/dist/executer/execute-command.js +4 -3
  79. package/dist/executer/execute-docker.js +32 -14
  80. package/dist/executer/execute-work-service.js +61 -0
  81. package/dist/executer/execute-work-task.js +107 -0
  82. package/dist/executer/execute-work-tree.js +27 -0
  83. package/dist/executer/execute.spec.js +14 -10
  84. package/dist/executer/execution-steps.js +47 -31
  85. package/dist/executer/get-container-binds.js +19 -0
  86. package/dist/executer/get-docker-executor.js +12 -12
  87. package/dist/executer/get-scheduler-down-result.js +12 -0
  88. package/dist/executer/get-scheduler-execute-result.js +28 -0
  89. package/dist/executer/get-scheduler-up-result.js +12 -0
  90. package/dist/executer/get-service-containers.js +14 -0
  91. package/dist/executer/get-service-containers.spec.js +59 -0
  92. package/dist/executer/get-service-env-hints.js +35 -0
  93. package/dist/executer/get-service-env-hints.spec.js +65 -0
  94. package/dist/executer/kubernetes-service.js +116 -48
  95. package/dist/executer/label-values.js +29 -46
  96. package/dist/executer/label-values.spec.js +61 -0
  97. package/dist/executer/lazy-resolver.js +16 -0
  98. package/dist/executer/lazy-resolver.spec.js +20 -0
  99. package/dist/executer/local-task.js +44 -0
  100. package/dist/executer/process-manager.js +16 -70
  101. package/dist/executer/process-manager.spec.js +39 -76
  102. package/dist/executer/reset-work-tree.js +30 -0
  103. package/dist/executer/scheduler/check-for-loop.js +17 -7
  104. package/dist/executer/scheduler/{scheduler-state.js → dependency-state.js} +1 -1
  105. package/dist/executer/scheduler/enqueue-next.js +83 -19
  106. package/dist/executer/scheduler/enqueue-next.spec.js +136 -0
  107. package/dist/executer/scheduler/{node-state.js → need-state.js} +1 -1
  108. package/dist/{parser/build-file.js → executer/scheduler/task-state.js} +1 -1
  109. package/dist/executer/set-user-permission.js +1 -1
  110. package/dist/executer/state-resolver.js +61 -0
  111. package/dist/executer/state-resolver.spec.js +37 -0
  112. package/dist/executer/state.js +37 -31
  113. package/dist/executer/state.spec.js +66 -0
  114. package/dist/executer/states.spec.js +14 -0
  115. package/dist/executer/watch-loop.js +68 -0
  116. package/dist/executer/watch-state-key.js +44 -0
  117. package/dist/executer/work-scope.js +1 -5
  118. package/dist/file/get-file-context.js +24 -3
  119. package/dist/file/get-file-context.spec.js +178 -0
  120. package/dist/file/move-files.js +2 -2
  121. package/dist/file/move-files.spec.js +48 -0
  122. package/dist/index.js +7 -8
  123. package/dist/json-schema.js +12 -0
  124. package/dist/kubernetes/apply.js +34 -0
  125. package/dist/kubernetes/await-running-state.js +71 -0
  126. package/dist/kubernetes/ensure-http-route.js +53 -0
  127. package/dist/kubernetes/ensure-http-route.spec.js +70 -0
  128. package/dist/kubernetes/ensure-ingress.js +54 -0
  129. package/dist/kubernetes/ensure-kubernetes-deployment-exists.js +105 -0
  130. package/dist/kubernetes/ensure-kubernetes-persistent-volume-claim-exists.js +40 -0
  131. package/dist/kubernetes/ensure-kubernetes-service-exists.js +36 -0
  132. package/dist/kubernetes/ensure-namespace.js +43 -0
  133. package/dist/kubernetes/ensure-namespace.spec.js +39 -0
  134. package/dist/kubernetes/ensure-persistent-data.js +158 -0
  135. package/dist/kubernetes/get-service-ip.js +11 -0
  136. package/dist/kubernetes/kubernetes-instance.js +48 -0
  137. package/dist/kubernetes/remove-persistent-data.js +19 -0
  138. package/dist/kubernetes/resolve-pod-name.js +42 -0
  139. package/dist/kubernetes/resolve-pod-name.spec.js +81 -0
  140. package/dist/kubernetes/resources.js +12 -0
  141. package/dist/kubernetes/restore-kubernetes-data.js +42 -0
  142. package/dist/kubernetes/store-kubernetes-data.js +39 -0
  143. package/dist/kubernetes/volumes.js +64 -0
  144. package/dist/log.js +69 -69
  145. package/dist/log.spec.js +131 -0
  146. package/dist/logging/grouped-logger.js +17 -15
  147. package/dist/logging/grouped-logger.spec.js +84 -0
  148. package/dist/logging/interactive-logger.js +1 -1
  149. package/dist/logging/interactive-logger.spec.js +57 -0
  150. package/dist/logging/live-logger.js +2 -2
  151. package/dist/logging/live-logger.spec.js +37 -0
  152. package/dist/logging/loggers.spec.js +154 -0
  153. package/dist/optimizer/calculate-checksum.spec.js +34 -0
  154. package/dist/optimizer/get-cache-directory.js +6 -6
  155. package/dist/optimizer/get-cache-directory.spec.js +59 -0
  156. package/dist/optimizer/get-work-cache-stats.js +53 -0
  157. package/dist/optimizer/get-work-cache-stats.spec.js +79 -0
  158. package/dist/{parser/build-file-task.js → optimizer/work-cache-stats.js} +1 -1
  159. package/dist/optimizer/work-task-cache-description.js +25 -0
  160. package/dist/optimizer/work-task-cache-description.spec.js +45 -0
  161. package/dist/parser/default-build-file.js +14 -7
  162. package/dist/parser/default-build-file.spec.js +48 -0
  163. package/dist/parser/parse-label-arguments.spec.js +27 -0
  164. package/dist/parser/read-build-file.js +2 -13
  165. package/dist/parser/read-build-file.spec.js +89 -0
  166. package/dist/parser/read-env-file.js +4 -5
  167. package/dist/parser/read-env-file.spec.js +42 -0
  168. package/dist/planner/utils/append-work-dependencies.js +55 -0
  169. package/dist/planner/utils/append-work-environment.js +22 -0
  170. package/dist/planner/utils/append-work-needs.js +18 -0
  171. package/dist/planner/utils/append-work-service.js +107 -0
  172. package/dist/planner/utils/append-work-service.spec.js +71 -0
  173. package/dist/planner/utils/append-work-task.js +81 -0
  174. package/dist/planner/utils/get-container-user.js +14 -0
  175. package/dist/planner/utils/get-container-user.spec.js +30 -0
  176. package/dist/planner/utils/get-default-kube-config.js +10 -0
  177. package/dist/planner/utils/get-default-kube-config.spec.js +11 -0
  178. package/dist/planner/utils/get-work-runtime.js +44 -0
  179. package/dist/planner/utils/normalize-path.js +1 -1
  180. package/dist/planner/utils/normalize-path.spec.js +24 -0
  181. package/dist/planner/utils/parse-work-command.js +80 -0
  182. package/dist/planner/utils/parse-work-command.spec.js +21 -0
  183. package/dist/planner/utils/parse-work-generate.js +38 -0
  184. package/dist/planner/utils/parse-work-generate.spec.js +45 -0
  185. package/dist/planner/utils/{parse-work-node-mount.js → parse-work-mount.js} +10 -7
  186. package/dist/planner/utils/{parse-work-node-mount.spec.js → parse-work-mount.spec.js} +14 -11
  187. package/dist/planner/utils/parse-work-mounts.js +13 -0
  188. package/dist/planner/utils/parse-work-mounts.spec.js +19 -0
  189. package/dist/planner/utils/{parse-work-node-port.js → parse-work-port.js} +11 -8
  190. package/dist/planner/utils/parse-work-port.spec.js +24 -0
  191. package/dist/planner/utils/parse-work-ports.js +10 -0
  192. package/dist/planner/utils/parse-work-ports.spec.js +24 -0
  193. package/dist/planner/utils/parse-work-source.js +72 -0
  194. package/dist/planner/utils/parse-work-source.spec.js +58 -0
  195. package/dist/planner/utils/parse-work-volume.js +45 -0
  196. package/dist/planner/utils/parse-work-volume.spec.js +18 -0
  197. package/dist/planner/utils/plan-work-tasks.js +41 -0
  198. package/dist/planner/utils/plan-work-tasks.spec.js +30 -0
  199. package/dist/planner/utils/plan-work-volume.js +1 -0
  200. package/dist/planner/utils/plan-work-volume.spec.js +33 -0
  201. package/dist/planner/utils/split-name.js +2 -2
  202. package/dist/planner/utils/split-name.spec.js +16 -0
  203. package/dist/planner/utils/template-value.js +1 -2
  204. package/dist/planner/utils/template-value.spec.js +14 -0
  205. package/dist/planner/validate.js +71 -38
  206. package/dist/planner/validate.spec.js +251 -0
  207. package/dist/planner/work-cache-id.js +12 -0
  208. package/dist/planner/work-cache-id.spec.js +173 -0
  209. package/dist/planner/work-command.js +3 -0
  210. package/dist/planner/work-environment.js +3 -0
  211. package/dist/planner/work-healthcheck.js +3 -0
  212. package/dist/planner/{work-node-status.js → work-item-status.js} +16 -62
  213. package/dist/{parser/build-file-service.js → planner/work-item-validation.js} +1 -1
  214. package/dist/planner/work-item.js +16 -0
  215. package/dist/{optimizer/work-node-cache-stats.js → planner/work-kubernetes-selector.js} +1 -1
  216. package/dist/planner/{work-nodes.js → work-port.js} +1 -1
  217. package/dist/planner/work-runtime-docker.js +224 -0
  218. package/dist/planner/work-runtime-kubernetes.js +264 -0
  219. package/dist/planner/work-runtime-local.js +109 -0
  220. package/dist/planner/work-runtime-local.spec.js +193 -0
  221. package/dist/planner/work-service-id.js +15 -5
  222. package/dist/planner/work-source.js +3 -0
  223. package/dist/planner/work-task.js +8 -0
  224. package/dist/program-commands.spec.js +84 -0
  225. package/dist/program.js +219 -73
  226. package/dist/program.spec.js +34 -11
  227. package/dist/run-program.js +28 -0
  228. package/dist/{executer/process.js → runtime/runtime.js} +1 -1
  229. package/dist/schema/build-file-base-task-schema.js +27 -0
  230. package/dist/schema/build-file-container-service-schema.js +28 -0
  231. package/dist/schema/build-file-container-task-schema.js +13 -0
  232. package/dist/schema/build-file-environment-schema-ingress.js +14 -0
  233. package/dist/schema/build-file-environment-schema.js +23 -0
  234. package/dist/schema/build-file-kubernetes-service-schema.js +17 -0
  235. package/dist/schema/build-file-kubernetes-service-selector-schema.js +9 -0
  236. package/dist/schema/build-file-local-platform-schema.js +9 -0
  237. package/dist/schema/build-file-local-task-schema.js +12 -0
  238. package/dist/schema/build-file-need-schema.js +12 -0
  239. package/dist/schema/build-file-schema.js +23 -0
  240. package/dist/schema/build-file-schema.spec.js +61 -0
  241. package/dist/schema/build-file-service-container-healthcheck.js +8 -0
  242. package/dist/schema/build-file-service-schema.js +15 -0
  243. package/dist/schema/build-file-task-command-schema.js +12 -0
  244. package/dist/schema/build-file-task-schema.js +12 -0
  245. package/dist/schema/build-file-volume-schema.js +14 -0
  246. package/dist/schema/cache-schema.js +31 -0
  247. package/dist/schema/doc-build-files.spec.js +223 -0
  248. package/dist/schema/envs-schema.js +6 -0
  249. package/dist/schema/labels-schema.js +6 -0
  250. package/dist/schema/parse-context.js +3 -0
  251. package/dist/schema/parse-error.js +12 -0
  252. package/dist/schema/parse-error.spec.js +18 -0
  253. package/dist/schema/reference-parser.js +209 -0
  254. package/dist/schema/reference-parser.spec.js +83 -0
  255. package/dist/schema/schema-parser.js +66 -0
  256. package/dist/schema/schema-parser.spec.js +80 -0
  257. package/dist/schema/type-guards.spec.js +23 -0
  258. package/dist/schema/work-scope-parser.js +77 -0
  259. package/dist/schema/work-scope-parser.spec.js +66 -0
  260. package/dist/service/update-service-status.js +11 -0
  261. package/dist/service/update-service-status.spec.js +27 -0
  262. package/dist/testing/ensure-local-registry.js +134 -0
  263. package/dist/testing/example-test-suite.js +16 -18
  264. package/dist/testing/expect.js +46 -22
  265. package/dist/testing/http-get.js +21 -0
  266. package/dist/testing/integration/cache.spec.js +13 -14
  267. package/dist/testing/integration/cancellation.spec.js +14 -9
  268. package/dist/testing/integration/clean.spec.js +14 -11
  269. package/dist/testing/integration/cmd.spec.js +1 -1
  270. package/dist/testing/integration/concurrency.spec.js +3 -3
  271. package/dist/testing/integration/docker-package-registry.spec.js +44 -0
  272. package/dist/testing/integration/docker-service-crash.spec.js +36 -0
  273. package/dist/testing/integration/docker.spec.js +4 -3
  274. package/dist/testing/integration/env.spec.js +9 -8
  275. package/dist/testing/integration/environment.spec.js +97 -0
  276. package/dist/testing/integration/error.spec.js +5 -4
  277. package/dist/testing/integration/export.spec.js +22 -0
  278. package/dist/testing/integration/extend.spec.js +9 -12
  279. package/dist/testing/integration/glob.spec.js +12 -20
  280. package/dist/testing/integration/include.spec.js +3 -3
  281. package/dist/testing/integration/invalid.spec.js +2 -3
  282. package/dist/testing/integration/invalid_loop.spec.js +49 -6
  283. package/dist/testing/integration/k8s-state-cache.spec.js +41 -0
  284. package/dist/testing/integration/kubernetes-service.spec.js +148 -0
  285. package/dist/testing/integration/kubernetes.spec.js +93 -9
  286. package/dist/testing/integration/labels.spec.js +6 -11
  287. package/dist/testing/integration/local.spec.js +1 -1
  288. package/dist/testing/integration/monorepo.spec.js +4 -4
  289. package/dist/testing/integration/multi-line.spec.js +3 -3
  290. package/dist/testing/integration/reference.spec.js +4 -5
  291. package/dist/testing/integration/services.spec.js +22 -4
  292. package/dist/testing/integration/store.spec.js +15 -16
  293. package/dist/testing/integration/unknown.spec.js +20 -18
  294. package/dist/testing/integration/validate.spec.js +143 -26
  295. package/dist/testing/integration/watch-dependency.spec.js +11 -8
  296. package/dist/testing/integration/watch.spec.js +15 -16
  297. package/dist/testing/requires-kubernetes.js +18 -0
  298. package/dist/testing/requires-linux-containers.js +18 -0
  299. package/dist/testing/requires-windows-containers.js +18 -0
  300. package/dist/testing/test-case.js +75 -0
  301. package/dist/testing/test-streams.js +28 -0
  302. package/dist/testing/testing-timeout.js +32 -0
  303. package/dist/utils/abort-event.js +10 -4
  304. package/dist/utils/abort-event.spec.js +46 -0
  305. package/dist/utils/buffer-context.js +44 -0
  306. package/dist/utils/buffer-context.spec.js +38 -0
  307. package/dist/utils/ci.spec.js +53 -0
  308. package/dist/utils/emitter.spec.js +34 -0
  309. package/dist/utils/empty-writable.spec.js +18 -0
  310. package/dist/utils/environment-config.js +4 -1
  311. package/dist/utils/environment-config.spec.js +26 -0
  312. package/dist/utils/fail-never.spec.js +9 -0
  313. package/dist/version.js +9 -0
  314. package/dist/version.spec.js +12 -0
  315. package/package.json +41 -23
  316. package/specs/build-file/caches/spec.md +114 -0
  317. package/specs/build-file/environment-variables/spec.md +82 -0
  318. package/specs/build-file/includes/spec.md +60 -0
  319. package/specs/build-file/references/spec.md +56 -0
  320. package/specs/build-file/spec.md +62 -0
  321. package/specs/cli/clean/spec.md +85 -0
  322. package/specs/cli/down/spec.md +85 -0
  323. package/specs/cli/execute/spec.md +101 -0
  324. package/specs/cli/init/spec.md +64 -0
  325. package/specs/cli/ls/spec.md +66 -0
  326. package/specs/cli/package/spec.md +110 -0
  327. package/specs/cli/spec.md +73 -0
  328. package/specs/cli/store-restore/spec.md +74 -0
  329. package/specs/cli/up/spec.md +80 -0
  330. package/specs/cli/validate/spec.md +70 -0
  331. package/specs/labels/spec.md +91 -0
  332. package/specs/service/container/spec.md +120 -0
  333. package/specs/service/kubernetes/spec.md +97 -0
  334. package/specs/service/spec.md +105 -0
  335. package/specs/task/caching/spec.md +107 -0
  336. package/specs/task/container/spec.md +118 -0
  337. package/specs/task/dependencies/spec.md +70 -0
  338. package/specs/task/extending/spec.md +74 -0
  339. package/specs/task/kubernetes/spec.md +125 -0
  340. package/specs/task/needs/spec.md +59 -0
  341. package/specs/task/spec.md +87 -0
  342. package/specs/task/watching/spec.md +69 -0
  343. package/dist/console/console-context-mock.js +0 -14
  344. package/dist/environment/get-process-env.js +0 -15
  345. package/dist/executer/create-scheduler-state.js +0 -32
  346. package/dist/executer/docker-node.js +0 -123
  347. package/dist/executer/hierarchy-scheduler.js +0 -94
  348. package/dist/executer/local-node.js +0 -57
  349. package/dist/executer/schedule.js +0 -137
  350. package/dist/executer/start-node.js +0 -67
  351. package/dist/executer/watch-node.js +0 -56
  352. package/dist/executer/watch-service.js +0 -54
  353. package/dist/file/get-file-context-mock.js +0 -187
  354. package/dist/file/get-file-context-mock.spec.js +0 -56
  355. package/dist/get-work-context.js +0 -29
  356. package/dist/optimizer/get-work-node-cache-stats.js +0 -104
  357. package/dist/optimizer/read-work-node-cache.js +0 -19
  358. package/dist/optimizer/work-node-cache-description.js +0 -32
  359. package/dist/optimizer/write-work-node-cache.js +0 -21
  360. package/dist/parser/build-file-task-command.js +0 -3
  361. package/dist/parser/build-file-task-platform.js +0 -3
  362. package/dist/parser/build-file-task-source.js +0 -3
  363. package/dist/parser/get-build-file.js +0 -9
  364. package/dist/parser/parse-boolean.js +0 -29
  365. package/dist/parser/parse-boolean.spec.js +0 -48
  366. package/dist/parser/parse-build-file-references.js +0 -35
  367. package/dist/parser/parse-build-file-services.js +0 -58
  368. package/dist/parser/parse-build-file-task-command.js +0 -36
  369. package/dist/parser/parse-build-file-task-source.js +0 -63
  370. package/dist/parser/parse-build-file.js +0 -125
  371. package/dist/parser/parse-context.js +0 -13
  372. package/dist/parser/parse-envs.js +0 -29
  373. package/dist/parser/parse-healthcheck.js +0 -28
  374. package/dist/parser/parse-service-selector.js +0 -39
  375. package/dist/parser/parse-string-array.js +0 -20
  376. package/dist/parser/parse-string-map.js +0 -30
  377. package/dist/parser/parse-string.js +0 -20
  378. package/dist/planner/utils/get-container-mounts.js +0 -24
  379. package/dist/planner/utils/parse-work-service-volume.js +0 -23
  380. package/dist/planner/utils/plan-work-command.js +0 -21
  381. package/dist/planner/utils/plan-work-dependency.js +0 -36
  382. package/dist/planner/utils/plan-work-node.js +0 -293
  383. package/dist/planner/utils/plan-work-nodes.js +0 -78
  384. package/dist/planner/utils/plan-work-tree.js +0 -15
  385. package/dist/planner/work-context.js +0 -23
  386. package/dist/planner/work-node-command.js +0 -3
  387. package/dist/planner/work-node-id.js +0 -12
  388. package/dist/planner/work-node-id.spec.js +0 -106
  389. package/dist/planner/work-node-path.js +0 -2
  390. package/dist/planner/work-node-source.js +0 -3
  391. package/dist/planner/work-node-validation.js +0 -3
  392. package/dist/planner/work-node.js +0 -8
  393. package/dist/planner/work-service-volume.js +0 -3
  394. package/dist/planner/work-services.js +0 -3
  395. package/dist/start-watch-processes.js +0 -28
  396. package/dist/testing/cli.js +0 -73
  397. package/dist/testing/create-build-file.js +0 -9
  398. package/dist/testing/get-logger.js +0 -23
  399. package/dist/testing/get-node.js +0 -12
  400. package/dist/testing/get-test-context.js +0 -19
  401. package/dist/testing/integration/program.spec.js +0 -25
  402. package/dist/testing/lazy.js +0 -17
  403. package/dist/testing/start-watch-processes.js +0 -29
  404. package/dist/testing/test-suite-options.js +0 -3
  405. package/dist/utils/abortable-function.js +0 -71
  406. package/logs/cache-console.log +0 -0
  407. package/logs/cache-status.log +0 -6
  408. package/logs/cache-stout.log +0 -0
  409. package/logs/cancellation-console.log +0 -0
  410. package/logs/cancellation-status.log +0 -3
  411. package/logs/cancellation-stout.log +0 -3
  412. package/logs/clean-console.log +0 -0
  413. package/logs/clean-status.log +0 -37
  414. package/logs/clean-stout.log +0 -95
  415. package/logs/cmd-console.log +0 -0
  416. package/logs/cmd-status.log +0 -8
  417. package/logs/cmd-stout.log +0 -9
  418. package/logs/concurrency-console.log +0 -0
  419. package/logs/concurrency-status.log +0 -14
  420. package/logs/concurrency-stout.log +0 -88
  421. package/logs/docker-console.log +0 -0
  422. package/logs/docker-status.log +0 -14
  423. package/logs/docker-stout.log +0 -39
  424. package/logs/env-console.log +0 -1
  425. package/logs/env-status.log +0 -1
  426. package/logs/env-stout.log +0 -0
  427. package/logs/error-console.log +0 -0
  428. package/logs/error-status.log +0 -10
  429. package/logs/error-stout.log +0 -36
  430. package/logs/extend-console.log +0 -0
  431. package/logs/extend-status.log +0 -1
  432. package/logs/extend-stout.log +0 -0
  433. package/logs/glob-console.log +0 -28
  434. package/logs/glob-status.log +0 -23
  435. package/logs/glob-stout.log +0 -52
  436. package/logs/hello-world-node-console.log +0 -0
  437. package/logs/hello-world-node-status.log +0 -37
  438. package/logs/hello-world-node-stout.log +0 -41
  439. package/logs/include-console.log +0 -0
  440. package/logs/include-status.log +0 -3
  441. package/logs/include-stout.log +0 -0
  442. package/logs/invalid-console.log +0 -0
  443. package/logs/invalid-status.log +0 -1
  444. package/logs/invalid-stout.log +0 -0
  445. package/logs/invalid_loop-console.log +0 -0
  446. package/logs/invalid_loop-status.log +0 -1
  447. package/logs/invalid_loop-stout.log +0 -11
  448. package/logs/labels-console.log +0 -0
  449. package/logs/labels-status.log +0 -1
  450. package/logs/labels-stout.log +0 -0
  451. package/logs/local-console.log +0 -0
  452. package/logs/local-status.log +0 -7
  453. package/logs/local-stout.log +0 -8
  454. package/logs/monorepo-console.log +0 -0
  455. package/logs/monorepo-status.log +0 -152
  456. package/logs/monorepo-stout.log +0 -2354
  457. package/logs/multi-line-console.log +0 -0
  458. package/logs/multi-line-status.log +0 -1
  459. package/logs/multi-line-stout.log +0 -0
  460. package/logs/reference-console.log +0 -0
  461. package/logs/reference-status.log +0 -5
  462. package/logs/reference-stout.log +0 -0
  463. package/logs/services-console.log +0 -0
  464. package/logs/services-status.log +0 -129
  465. package/logs/services-stout.log +0 -639
  466. package/logs/store-restore-console.log +0 -0
  467. package/logs/store-restore-status.log +0 -40
  468. package/logs/store-restore-stout.log +0 -97
  469. package/logs/unknown-console.log +0 -3
  470. package/logs/unknown-status.log +0 -1
  471. package/logs/unknown-stout.log +0 -0
  472. package/logs/validate-console.log +0 -0
  473. package/logs/validate-status.log +0 -2
  474. package/logs/validate-stout.log +0 -0
  475. package/logs/watch-console.log +0 -0
  476. package/logs/watch-dependency-console.log +0 -0
  477. package/logs/watch-dependency-status.log +0 -176
  478. package/logs/watch-dependency-stout.log +0 -779
  479. package/logs/watch-status.log +0 -100
  480. package/logs/watch-stout.log +0 -349
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const stream_1 = require("stream");
4
+ const log_1 = require("./log");
5
+ const environment_mock_1 = require("./executer/environment-mock");
6
+ function capture() {
7
+ const chunks = [];
8
+ const stream = new stream_1.Writable({
9
+ write(chunk, _enc, cb) {
10
+ chunks.push(chunk.toString());
11
+ cb();
12
+ },
13
+ });
14
+ return { stream, read: () => chunks.join('') };
15
+ }
16
+ describe('log helpers', () => {
17
+ describe('getLogs', () => {
18
+ it('splits on \\r?\\n and drops empty lines', () => {
19
+ expect((0, log_1.getLogs)('a\r\nb\nc\n\n')).toEqual(['a', 'b', 'c']);
20
+ });
21
+ it('accepts a Buffer', () => {
22
+ expect((0, log_1.getLogs)(Buffer.from('x\ny'))).toEqual(['x', 'y']);
23
+ });
24
+ });
25
+ describe('getLogLevel', () => {
26
+ it.each(['debug', 'info', 'warn', 'error'])('returns a string containing %s', (level) => {
27
+ expect((0, log_1.getLogLevel)(level)).toContain(level);
28
+ });
29
+ });
30
+ describe('getErrorMessage', () => {
31
+ it('uses Error.message for Error instances', () => {
32
+ expect((0, log_1.getErrorMessage)(new Error('boom'))).toBe('boom');
33
+ });
34
+ it('returns a string value unchanged', () => {
35
+ expect((0, log_1.getErrorMessage)('plain')).toBe('plain');
36
+ });
37
+ it('falls back to "unknown error <value>" for other values', () => {
38
+ expect((0, log_1.getErrorMessage)({ x: 1 })).toMatch(/^unknown error/);
39
+ });
40
+ });
41
+ describe('consoleContext', () => {
42
+ it('writes formatted lines for each level', () => {
43
+ const cap = capture();
44
+ const ctx = (0, log_1.consoleContext)(cap.stream);
45
+ ctx.debug('d-msg');
46
+ ctx.info('i-msg');
47
+ ctx.warn('w-msg');
48
+ ctx.error('e-msg');
49
+ const out = cap.read();
50
+ expect(out).toContain('d-msg');
51
+ expect(out).toContain('i-msg');
52
+ expect(out).toContain('w-msg');
53
+ expect(out).toContain('e-msg');
54
+ // 4 newlines, one per call
55
+ expect(out.split('\n').filter((l) => l.length > 0)).toHaveLength(4);
56
+ });
57
+ });
58
+ describe('getType / getNodeName / getMaxNameLength', () => {
59
+ it('formats the type with the configured padding', () => {
60
+ expect((0, log_1.getType)('cli')).toContain('cli');
61
+ });
62
+ it('pads name to a given length', () => {
63
+ expect((0, log_1.getNodeName)('a', 4)).toContain('a');
64
+ // pads with spaces to reach the requested length, then color codes
65
+ // surround it — assert by inspecting the visible characters
66
+ // ANSI color wrappers can only add chars, so the rendered string is at
67
+ // least the requested width even when colors are enabled.
68
+ expect((0, log_1.getNodeName)('a', 4).length).toBeGreaterThanOrEqual(4);
69
+ });
70
+ it('getMaxNameLength returns the longest name yielded', () => {
71
+ const names = function* () {
72
+ yield 'aa';
73
+ yield 'aaaaa';
74
+ yield 'a';
75
+ };
76
+ expect((0, log_1.getMaxNameLength)(names)).toBe(5);
77
+ });
78
+ it('getMaxNameLength returns 0 for an empty source', () => {
79
+ expect((0, log_1.getMaxNameLength)(() => [])).toBe(0);
80
+ });
81
+ });
82
+ describe('print helpers', () => {
83
+ it('printItem with and without description', () => {
84
+ const env = (0, environment_mock_1.environmentMock)('/tmp');
85
+ const cap = capture();
86
+ env.stdout = cap.stream;
87
+ (0, log_1.printItem)(env, { name: 'a', description: null });
88
+ (0, log_1.printItem)(env, { name: 'b', description: 'desc' });
89
+ const out = cap.read();
90
+ expect(out).toContain('• a\n');
91
+ expect(out).toContain('• b');
92
+ expect(out).toContain('desc');
93
+ });
94
+ it('printProperty writes "<name>: <value>"', () => {
95
+ const env = (0, environment_mock_1.environmentMock)('/tmp');
96
+ const cap = capture();
97
+ env.stdout = cap.stream;
98
+ (0, log_1.printProperty)(env, 'image', 'alpine');
99
+ expect(cap.read()).toContain('image:');
100
+ expect(cap.read()).toContain('alpine');
101
+ });
102
+ it('printTitle writes the title with a colon', () => {
103
+ const env = (0, environment_mock_1.environmentMock)('/tmp');
104
+ const cap = capture();
105
+ env.stdout = cap.stream;
106
+ (0, log_1.printTitle)(env, 'Tasks');
107
+ expect(cap.read()).toContain('Tasks:');
108
+ });
109
+ });
110
+ describe('formatDate', () => {
111
+ it('produces a [yyyy-MM-DD HH:mm:ss.SSS] formatted timestamp', () => {
112
+ // Use a fixed Date in UTC; the function uses local-time getters, so only
113
+ // pattern-check the output rather than the literal value.
114
+ const out = (0, log_1.formatDate)(new Date(2024, 0, 2, 3, 4, 5, 6));
115
+ expect(out).toMatch(/\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}\]/);
116
+ });
117
+ });
118
+ describe('cursor', () => {
119
+ it('hideCursor / showCursor write escape sequences', () => {
120
+ const env = (0, environment_mock_1.environmentMock)('/tmp');
121
+ const cap = capture();
122
+ env.stdout = cap.stream;
123
+ (0, log_1.hideCursor)(env);
124
+ (0, log_1.showCursor)(env);
125
+ const out = cap.read();
126
+ expect(out).toContain('\x1B[?25l');
127
+ expect(out).toContain('\x1B[?25h');
128
+ });
129
+ });
130
+ });
131
+ //# sourceMappingURL=log.spec.js.map
@@ -2,31 +2,33 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.groupedLogger = void 0;
4
4
  const log_1 = require("../log");
5
- const plan_work_nodes_1 = require("../planner/utils/plan-work-nodes");
5
+ const plan_work_tasks_1 = require("../planner/utils/plan-work-tasks");
6
6
  function groupedLogger(state, env) {
7
- const maxNodeNameLength = (0, log_1.getNodeNameLengthForWorkTree)(state.current.node, state.current.service);
7
+ const maxNodeNameLength = (0, log_1.getWorkItemMaxLength)(state.current);
8
8
  const completedNodes = [];
9
9
  const completedServices = [];
10
- state.on((currentState) => {
11
- for (const node of (0, plan_work_nodes_1.iterateWorkNodes)(currentState.node)) {
12
- if (completedNodes.indexOf(node.node.id) >= 0) {
10
+ state.on('log-status', (currentState) => {
11
+ for (const task of (0, plan_work_tasks_1.iterateWorkTasks)(currentState)) {
12
+ if (completedNodes.indexOf(task.name) >= 0) {
13
13
  continue;
14
14
  }
15
- if (node.type === 'crash' || node.type === 'error' || node.type === 'completed') {
16
- completedNodes.push(node.node.id);
17
- for (const log of env.status.task(node.node).read()) {
18
- (0, log_1.writeNodeLogToConsole)(env, log, maxNodeNameLength);
15
+ if (task.state.current.type === 'crash' ||
16
+ task.state.current.type === 'error' ||
17
+ task.state.current.type === 'completed') {
18
+ completedNodes.push(task.name);
19
+ for (const log of task.status.read()) {
20
+ (0, log_1.writeWorkItemLogToConsole)(env, log, maxNodeNameLength);
19
21
  }
20
22
  }
21
23
  }
22
- for (const service of (0, plan_work_nodes_1.iterateWorkServices)(currentState.service)) {
23
- if (completedServices.indexOf(service.service.id) >= 0) {
24
+ for (const service of (0, plan_work_tasks_1.iterateWorkServices)(currentState)) {
25
+ if (completedServices.indexOf(service.name) >= 0) {
24
26
  continue;
25
27
  }
26
- if (service.type === 'end') {
27
- completedServices.push(service.service.id);
28
- for (const log of env.status.service(service.service).read()) {
29
- (0, log_1.writeNodeLogToConsole)(env, log, maxNodeNameLength);
28
+ if (service.state.current.type === 'end') {
29
+ completedServices.push(service.name);
30
+ for (const log of service.status.read()) {
31
+ (0, log_1.writeWorkItemLogToConsole)(env, log, maxNodeNameLength);
30
32
  }
31
33
  }
32
34
  }
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ // Verifies that the grouped logger only flushes a task/service once it reaches
3
+ // a terminal state, by mocking the underlying log helpers and iterators.
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ vi.mock('../log', () => ({
6
+ writeWorkItemLogToConsole: vi.fn(),
7
+ printWorkTreeResult: vi.fn().mockResolvedValue(undefined),
8
+ getWorkItemMaxLength: vi.fn(() => 10),
9
+ }));
10
+ vi.mock('../planner/utils/plan-work-tasks', () => ({
11
+ iterateWorkTasks: vi.fn(),
12
+ iterateWorkServices: vi.fn(),
13
+ }));
14
+ const grouped_logger_1 = require("./grouped-logger");
15
+ const state_1 = require("../executer/state");
16
+ const plan_work_tasks_1 = require("../planner/utils/plan-work-tasks");
17
+ const log_1 = require("../log");
18
+ const mockedIterTasks = plan_work_tasks_1.iterateWorkTasks;
19
+ const mockedIterServices = plan_work_tasks_1.iterateWorkServices;
20
+ const mockedWrite = log_1.writeWorkItemLogToConsole;
21
+ const mockedPrint = log_1.printWorkTreeResult;
22
+ function fakeTask(name, type, logs = []) {
23
+ return { name, state: { current: { type } }, status: { read: () => logs } };
24
+ }
25
+ describe('groupedLogger', () => {
26
+ beforeEach(() => {
27
+ mockedWrite.mockClear();
28
+ mockedPrint.mockClear();
29
+ mockedIterTasks.mockReset();
30
+ mockedIterServices.mockReset();
31
+ });
32
+ it('flushes a task only once it reaches a terminal state and not again on re-emit', () => {
33
+ const t = fakeTask('t1', 'pending', [{ id: 1 }]);
34
+ mockedIterTasks.mockImplementation(function* () {
35
+ yield t;
36
+ });
37
+ mockedIterServices.mockImplementation(function* () { });
38
+ const state = new state_1.State({ services: {}, tasks: { t1: t } });
39
+ (0, grouped_logger_1.groupedLogger)(state, {});
40
+ state.set(state.current); // pending — should NOT flush
41
+ expect(mockedWrite).not.toHaveBeenCalled();
42
+ t.state.current.type = 'completed';
43
+ state.set(state.current); // completed — flush once
44
+ expect(mockedWrite).toHaveBeenCalledTimes(1);
45
+ state.set(state.current); // already-flushed task — must not flush again
46
+ expect(mockedWrite).toHaveBeenCalledTimes(1);
47
+ });
48
+ it.each(['crash', 'error', 'completed'])('flushes on terminal state %s', (terminal) => {
49
+ const t = fakeTask('t1', terminal, [{ id: 1 }]);
50
+ mockedIterTasks.mockImplementation(function* () {
51
+ yield t;
52
+ });
53
+ mockedIterServices.mockImplementation(function* () { });
54
+ const state = new state_1.State({ services: {}, tasks: { t1: t } });
55
+ (0, grouped_logger_1.groupedLogger)(state, {});
56
+ state.set(state.current);
57
+ expect(mockedWrite).toHaveBeenCalledTimes(1);
58
+ });
59
+ it('flushes a service on type "end" and not again', () => {
60
+ const s = fakeTask('svc1', 'starting', [{ id: 1 }]);
61
+ mockedIterTasks.mockImplementation(function* () { });
62
+ mockedIterServices.mockImplementation(function* () {
63
+ yield s;
64
+ });
65
+ const state = new state_1.State({ services: { svc1: s }, tasks: {} });
66
+ (0, grouped_logger_1.groupedLogger)(state, {});
67
+ state.set(state.current); // not yet ended
68
+ expect(mockedWrite).not.toHaveBeenCalled();
69
+ s.state.current.type = 'end';
70
+ state.set(state.current); // ended — flush
71
+ expect(mockedWrite).toHaveBeenCalledTimes(1);
72
+ state.set(state.current); // already flushed — no second call
73
+ expect(mockedWrite).toHaveBeenCalledTimes(1);
74
+ });
75
+ it('complete() delegates to printWorkTreeResult', async () => {
76
+ mockedIterTasks.mockImplementation(function* () { });
77
+ mockedIterServices.mockImplementation(function* () { });
78
+ const state = new state_1.State({ services: {}, tasks: {} });
79
+ const logger = (0, grouped_logger_1.groupedLogger)(state, {});
80
+ await logger.complete({ state: state.current, success: true }, {});
81
+ expect(mockedPrint).toHaveBeenCalledTimes(1);
82
+ });
83
+ });
84
+ //# sourceMappingURL=grouped-logger.spec.js.map
@@ -18,7 +18,7 @@ function interactiveLogger(state, env) {
18
18
  };
19
19
  (0, log_1.hideCursor)(env);
20
20
  tickerFn();
21
- state.on((currentState) => {
21
+ state.on('log-status', (currentState) => {
22
22
  (0, log_1.writeWorkTreeStatus)(currentState, env, count);
23
23
  });
24
24
  return {
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ // interactiveLogger hides the cursor, polls writeWorkTreeStatus on a 100ms
3
+ // ticker, and writes on every state change until complete() stops it.
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ vi.mock('../log', () => ({
6
+ writeWorkTreeStatus: vi.fn(),
7
+ printWorkTreeResult: vi.fn().mockResolvedValue(undefined),
8
+ hideCursor: vi.fn(),
9
+ showCursor: vi.fn(),
10
+ }));
11
+ vi.mock('readline', () => ({ clearScreenDown: vi.fn() }));
12
+ const interactive_logger_1 = require("./interactive-logger");
13
+ const state_1 = require("../executer/state");
14
+ const log_1 = require("../log");
15
+ const readline_1 = require("readline");
16
+ const mockedWriteStatus = log_1.writeWorkTreeStatus;
17
+ const mockedHide = log_1.hideCursor;
18
+ const mockedShow = log_1.showCursor;
19
+ const mockedClear = readline_1.clearScreenDown;
20
+ const mockedPrint = log_1.printWorkTreeResult;
21
+ describe('interactiveLogger', () => {
22
+ beforeEach(() => {
23
+ vi.useFakeTimers();
24
+ mockedWriteStatus.mockClear();
25
+ mockedHide.mockClear();
26
+ mockedShow.mockClear();
27
+ mockedClear.mockClear();
28
+ mockedPrint.mockClear();
29
+ });
30
+ afterEach(() => {
31
+ vi.useRealTimers();
32
+ });
33
+ it('hides the cursor, ticks every 100ms, and writes on state changes', async () => {
34
+ const state = new state_1.State({ services: {}, tasks: {} });
35
+ const env = { stdout: { write: vi.fn() } };
36
+ const logger = (0, interactive_logger_1.interactiveLogger)(state, env);
37
+ // hideCursor on construction; initial tick fired immediately
38
+ expect(mockedHide).toHaveBeenCalledTimes(1);
39
+ expect(mockedWriteStatus).toHaveBeenCalledTimes(1);
40
+ // advance one tick window → one additional writeWorkTreeStatus
41
+ vi.advanceTimersByTime(100);
42
+ expect(mockedWriteStatus).toHaveBeenCalledTimes(2);
43
+ // a state change writes status synchronously
44
+ state.set({ services: {}, tasks: {} });
45
+ expect(mockedWriteStatus).toHaveBeenCalledTimes(3);
46
+ // complete() stops the ticker and restores the cursor
47
+ await logger.complete({ state: state.current, success: true }, env);
48
+ expect(mockedClear).toHaveBeenCalledWith(env.stdout);
49
+ expect(mockedPrint).toHaveBeenCalledTimes(1);
50
+ expect(mockedShow).toHaveBeenCalledTimes(1);
51
+ // after complete, advancing time must NOT schedule further writes
52
+ const after = mockedWriteStatus.mock.calls.length;
53
+ vi.advanceTimersByTime(1000);
54
+ expect(mockedWriteStatus.mock.calls.length).toBe(after);
55
+ });
56
+ });
57
+ //# sourceMappingURL=interactive-logger.spec.js.map
@@ -3,9 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.liveLogger = void 0;
4
4
  const log_1 = require("../log");
5
5
  function liveLogger(state, env) {
6
- const maxNodeNameLength = (0, log_1.getNodeNameLengthForWorkTree)(state.current.node, state.current.service);
6
+ const maxNodeNameLength = (0, log_1.getWorkItemMaxLength)(state.current);
7
7
  env.status.on((log) => {
8
- (0, log_1.writeNodeLogToConsole)(env, log, maxNodeNameLength);
8
+ (0, log_1.writeWorkItemLogToConsole)(env, log, maxNodeNameLength);
9
9
  });
10
10
  return {
11
11
  async complete(evt, env) {
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ // liveLogger forwards every status message verbatim to writeWorkItemLogToConsole.
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ vi.mock('../log', () => ({
5
+ writeWorkItemLogToConsole: vi.fn(),
6
+ printWorkTreeResult: vi.fn().mockResolvedValue(undefined),
7
+ getWorkItemMaxLength: vi.fn(() => 10),
8
+ }));
9
+ const live_logger_1 = require("./live-logger");
10
+ const state_1 = require("../executer/state");
11
+ const log_1 = require("../log");
12
+ const emitter_1 = require("../utils/emitter");
13
+ const mockedWrite = log_1.writeWorkItemLogToConsole;
14
+ const mockedPrint = log_1.printWorkTreeResult;
15
+ describe('liveLogger', () => {
16
+ beforeEach(() => {
17
+ mockedWrite.mockClear();
18
+ mockedPrint.mockClear();
19
+ });
20
+ it('forwards each status message to writeWorkItemLogToConsole', () => {
21
+ const status = (0, emitter_1.emitter)();
22
+ const env = { status };
23
+ const state = new state_1.State({ services: {}, tasks: {} });
24
+ (0, live_logger_1.liveLogger)(state, env);
25
+ const msg = { type: 'status', message: 'hi' };
26
+ status.emit(msg);
27
+ expect(mockedWrite).toHaveBeenCalledWith(env, msg, 10);
28
+ });
29
+ it('complete() delegates to printWorkTreeResult', async () => {
30
+ const status = (0, emitter_1.emitter)();
31
+ const state = new state_1.State({ services: {}, tasks: {} });
32
+ const logger = (0, live_logger_1.liveLogger)(state, { status });
33
+ await logger.complete({ state: state.current, success: true }, {});
34
+ expect(mockedPrint).toHaveBeenCalledTimes(1);
35
+ });
36
+ });
37
+ //# sourceMappingURL=live-logger.spec.js.map
@@ -0,0 +1,154 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const stream_1 = require("stream");
4
+ const state_1 = require("../executer/state");
5
+ const work_item_status_1 = require("../planner/work-item-status");
6
+ const live_logger_1 = require("./live-logger");
7
+ const grouped_logger_1 = require("./grouped-logger");
8
+ const interactive_logger_1 = require("./interactive-logger");
9
+ function memStream() {
10
+ const chunks = [];
11
+ const stream = new stream_1.Writable({
12
+ write(c, _e, cb) {
13
+ chunks.push(c.toString());
14
+ cb();
15
+ },
16
+ });
17
+ return Object.assign(stream, { read: () => chunks.join('') });
18
+ }
19
+ function noopStream() {
20
+ return new stream_1.Writable({ write: (_c, _e, cb) => cb() });
21
+ }
22
+ function makeEnv(stdout = noopStream()) {
23
+ return {
24
+ cwd: '/tmp',
25
+ file: {},
26
+ processEnvs: {},
27
+ abortCtrl: new AbortController(),
28
+ console: { info: vi.fn(), error: vi.fn(), warn: vi.fn(), debug: vi.fn(), log: vi.fn() },
29
+ status: (0, work_item_status_1.statusConsole)(noopStream()),
30
+ stdout,
31
+ stderr: noopStream(),
32
+ stdoutColumns: 80,
33
+ };
34
+ }
35
+ function taskItem(env, name, state) {
36
+ const data = { type: 'local-task', name, description: null, scope: { fileName: 'x' } };
37
+ return {
38
+ id: () => name,
39
+ name,
40
+ data,
41
+ status: env.status.from(data),
42
+ state: new state_1.State(state),
43
+ needs: [],
44
+ deps: [],
45
+ requiredBy: [],
46
+ runtime: {},
47
+ };
48
+ }
49
+ function serviceItem(env, name, state) {
50
+ const data = { type: 'container-service', name, ports: [] };
51
+ return {
52
+ id: () => name,
53
+ name,
54
+ data,
55
+ status: env.status.from(data),
56
+ state: new state_1.State(state),
57
+ needs: [],
58
+ deps: [],
59
+ requiredBy: [],
60
+ runtime: {},
61
+ };
62
+ }
63
+ function workTree(items) {
64
+ return {
65
+ tasks: Object.fromEntries((items.tasks ?? []).map((t) => [t.name, t])),
66
+ services: Object.fromEntries((items.services ?? []).map((s) => [s.name, s])),
67
+ environment: { type: 'docker' },
68
+ };
69
+ }
70
+ describe('loggers', () => {
71
+ describe('liveLogger', () => {
72
+ it('writes each status message and renders results on complete', async () => {
73
+ const out = memStream();
74
+ const env = makeEnv(out);
75
+ const task = taskItem(env, 'build', { type: 'pending', stateKey: null });
76
+ const tree = workTree({ tasks: [task] });
77
+ const state = new state_1.State(tree);
78
+ const logger = (0, live_logger_1.liveLogger)(state, env);
79
+ // a write on the scoped console emits a message on env.status, which the
80
+ // logger's listener formats into env.stdout
81
+ task.status.write('info', 'compiling');
82
+ const result = { state: tree, success: true };
83
+ await logger.complete(result, env);
84
+ expect(out.read()).toContain('compiling');
85
+ });
86
+ });
87
+ describe('groupedLogger', () => {
88
+ it("writes a completed task's logs on the next status update", async () => {
89
+ const out = memStream();
90
+ const env = makeEnv(out);
91
+ const task = taskItem(env, 'build', { type: 'pending', stateKey: null });
92
+ const tree = workTree({ tasks: [task] });
93
+ const state = new state_1.State(tree);
94
+ const logger = (0, grouped_logger_1.groupedLogger)(state, env);
95
+ // buffer one info log, mark the task completed, then notify the parent state
96
+ task.status.write('info', 'compiling');
97
+ task.state.set({ type: 'completed', stateKey: 'k', duration: 5, cached: false });
98
+ state.set(tree);
99
+ await logger.complete({ state: tree, success: true }, env);
100
+ expect(out.read()).toContain('compiling');
101
+ });
102
+ it("writes an ended service's logs on the next status update", async () => {
103
+ const out = memStream();
104
+ const env = makeEnv(out);
105
+ const svc = serviceItem(env, 'db', { type: 'pending', stateKey: null });
106
+ const tree = workTree({ services: [svc] });
107
+ const state = new state_1.State(tree);
108
+ const logger = (0, grouped_logger_1.groupedLogger)(state, env);
109
+ svc.status.write('info', 'ready');
110
+ svc.state.set({ type: 'end', reason: 'terminated', stateKey: 'k' });
111
+ state.set(tree);
112
+ await logger.complete({ state: tree, success: true }, env);
113
+ expect(out.read()).toContain('ready');
114
+ });
115
+ it('does not duplicate a task that is already in the completed set', async () => {
116
+ const out = memStream();
117
+ const env = makeEnv(out);
118
+ const task = taskItem(env, 'build', { type: 'pending', stateKey: null });
119
+ const tree = workTree({ tasks: [task] });
120
+ const state = new state_1.State(tree);
121
+ const logger = (0, grouped_logger_1.groupedLogger)(state, env);
122
+ task.status.write('info', 'once');
123
+ task.state.set({ type: 'completed', stateKey: 'k', duration: 1, cached: false });
124
+ state.set(tree);
125
+ const after = out.read();
126
+ // a second notification with no further log message should not append another copy
127
+ state.set(tree);
128
+ expect(out.read()).toEqual(after);
129
+ await logger.complete({ state: tree, success: true }, env);
130
+ });
131
+ });
132
+ describe('interactiveLogger', () => {
133
+ beforeEach(() => vi.useFakeTimers());
134
+ afterEach(() => vi.useRealTimers());
135
+ it('hides the cursor, ticks, redraws on log-status, then cleans up on complete', async () => {
136
+ const out = memStream();
137
+ const env = makeEnv(out);
138
+ const task = taskItem(env, 'build', { type: 'pending', stateKey: null });
139
+ const tree = workTree({ tasks: [task] });
140
+ const state = new state_1.State(tree);
141
+ const logger = (0, interactive_logger_1.interactiveLogger)(state, env); // hideCursor + first tick + setTimeout
142
+ // drive a state change to re-render
143
+ task.state.set({ type: 'running', stateKey: 'k' });
144
+ state.set(tree);
145
+ // let one fake tick fire while running===true (covers the recursive branch)
146
+ vi.advanceTimersByTime(150);
147
+ await logger.complete({ state: tree, success: true }, env);
148
+ // and one more after complete — covers the `if (!running) return` early exit
149
+ vi.advanceTimersByTime(150);
150
+ expect(out.read().length).toBeGreaterThan(0);
151
+ });
152
+ });
153
+ });
154
+ //# sourceMappingURL=loggers.spec.js.map
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const path_1 = require("path");
4
+ const os_1 = require("os");
5
+ const fs_1 = require("fs");
6
+ const crypto_1 = require("crypto");
7
+ const environment_mock_1 = require("../executer/environment-mock");
8
+ const calculate_checksum_1 = require("./calculate-checksum");
9
+ describe('calculateChecksum', () => {
10
+ let scratch;
11
+ beforeEach(() => {
12
+ scratch = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), 'hammerkit-checksum-'));
13
+ });
14
+ afterEach(() => {
15
+ (0, fs_1.rmSync)(scratch, { recursive: true, force: true });
16
+ });
17
+ it('returns the sha1 of the file contents', async () => {
18
+ const env = (0, environment_mock_1.environmentMock)(scratch);
19
+ const file = (0, path_1.join)(scratch, 'a.txt');
20
+ (0, fs_1.writeFileSync)(file, 'hello world');
21
+ const expected = (0, crypto_1.createHash)('sha1').update('hello world').digest('hex');
22
+ expect(await (0, calculate_checksum_1.calculateChecksum)(env, file)).toBe(expected);
23
+ });
24
+ it('is deterministic and content-sensitive', async () => {
25
+ const env = (0, environment_mock_1.environmentMock)(scratch);
26
+ const file = (0, path_1.join)(scratch, 'a.txt');
27
+ (0, fs_1.writeFileSync)(file, 'one');
28
+ const first = await (0, calculate_checksum_1.calculateChecksum)(env, file);
29
+ expect(await (0, calculate_checksum_1.calculateChecksum)(env, file)).toBe(first);
30
+ (0, fs_1.writeFileSync)(file, 'two');
31
+ expect(await (0, calculate_checksum_1.calculateChecksum)(env, file)).not.toBe(first);
32
+ });
33
+ });
34
+ //# sourceMappingURL=calculate-checksum.spec.js.map
@@ -2,16 +2,16 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getHammerkitDirectory = exports.getCacheStatsFile = exports.getCacheDescriptionFile = exports.getCacheDirectory = void 0;
4
4
  const path_1 = require("path");
5
- function getCacheDirectory(nodeId) {
6
- return (0, path_1.join)(getHammerkitDirectory(), 'cache', nodeId);
5
+ function getCacheDirectory(cacheId) {
6
+ return (0, path_1.join)(getHammerkitDirectory(), 'cache', cacheId);
7
7
  }
8
8
  exports.getCacheDirectory = getCacheDirectory;
9
- function getCacheDescriptionFile(nodeId) {
10
- return (0, path_1.join)(getCacheDirectory(nodeId), 'description.json');
9
+ function getCacheDescriptionFile(cacheId) {
10
+ return (0, path_1.join)(getCacheDirectory(cacheId), 'description.json');
11
11
  }
12
12
  exports.getCacheDescriptionFile = getCacheDescriptionFile;
13
- function getCacheStatsFile(nodeId) {
14
- return (0, path_1.join)(getCacheDirectory(nodeId), 'stats.json');
13
+ function getCacheStatsFile(cacheId) {
14
+ return (0, path_1.join)(getCacheDirectory(cacheId), 'stats.json');
15
15
  }
16
16
  exports.getCacheStatsFile = getCacheStatsFile;
17
17
  function getHammerkitDirectory() {
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const path_1 = require("path");
4
+ const get_cache_directory_1 = require("./get-cache-directory");
5
+ function withPlatform(value, env, fn) {
6
+ const originalPlatform = process.platform;
7
+ const originalEnv = {};
8
+ for (const k of Object.keys(env))
9
+ originalEnv[k] = process.env[k];
10
+ Object.defineProperty(process, 'platform', { value, configurable: true });
11
+ for (const [k, v] of Object.entries(env)) {
12
+ if (v === undefined)
13
+ delete process.env[k];
14
+ else
15
+ process.env[k] = v;
16
+ }
17
+ try {
18
+ return fn();
19
+ }
20
+ finally {
21
+ Object.defineProperty(process, 'platform', { value: originalPlatform, configurable: true });
22
+ for (const [k, v] of Object.entries(originalEnv)) {
23
+ if (v === undefined)
24
+ delete process.env[k];
25
+ else
26
+ process.env[k] = v;
27
+ }
28
+ }
29
+ }
30
+ describe('cache directory helpers', () => {
31
+ it('builds the per-cache directory under the hammerkit directory', () => {
32
+ const dir = (0, get_cache_directory_1.getCacheDirectory)('abc123');
33
+ expect(dir.endsWith((0, path_1.join)('cache', 'abc123'))).toBe(true);
34
+ });
35
+ it('builds the description file path under the cache directory', () => {
36
+ expect((0, get_cache_directory_1.getCacheDescriptionFile)('id').endsWith((0, path_1.join)('cache', 'id', 'description.json'))).toBe(true);
37
+ });
38
+ it('builds the stats file path under the cache directory', () => {
39
+ expect((0, get_cache_directory_1.getCacheStatsFile)('id').endsWith((0, path_1.join)('cache', 'id', 'stats.json'))).toBe(true);
40
+ });
41
+ describe('getHammerkitDirectory', () => {
42
+ it('uses %APPDATA% on Windows', () => {
43
+ withPlatform('win32', { APPDATA: 'C:\\Users\\X\\AppData\\Roaming' }, () => {
44
+ expect((0, get_cache_directory_1.getHammerkitDirectory)()).toBe((0, path_1.join)('C:\\Users\\X\\AppData\\Roaming', 'hammerkit'));
45
+ });
46
+ });
47
+ it('uses ~/Library/Application Support on macOS', () => {
48
+ withPlatform('darwin', { HOME: '/Users/x' }, () => {
49
+ expect((0, get_cache_directory_1.getHammerkitDirectory)()).toBe((0, path_1.join)('/Users/x', 'Library', 'Application Support', 'hammerkit'));
50
+ });
51
+ });
52
+ it('uses ~/.hammerkit on Linux', () => {
53
+ withPlatform('linux', { HOME: '/home/x' }, () => {
54
+ expect((0, get_cache_directory_1.getHammerkitDirectory)()).toBe((0, path_1.join)('/home/x', '.hammerkit'));
55
+ });
56
+ });
57
+ });
58
+ });
59
+ //# sourceMappingURL=get-cache-directory.spec.js.map