hammerkit 1.5.0 → 1.6.0-alpha.1

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
@@ -1,90 +1,53 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const process_manager_1 = require("./process-manager");
4
- const environment_mock_1 = require("./environment-mock");
5
- const abort_event_1 = require("../utils/abort-event");
4
+ const work_item_status_1 = require("../planner/work-item-status");
5
+ const empty_writable_1 = require("../utils/empty-writable");
6
6
  describe('process-manager', () => {
7
+ function fakeWorkItem(id) {
8
+ return {
9
+ id: () => '',
10
+ status: (0, work_item_status_1.statusConsole)((0, empty_writable_1.emptyWritable)()).context((0, work_item_status_1.logContext)('task', { name: id })),
11
+ deps: [],
12
+ aliases: [id],
13
+ needs: [],
14
+ requiredBy: [],
15
+ name: id,
16
+ data: null,
17
+ state: null,
18
+ runtime: null,
19
+ };
20
+ }
7
21
  it('should complete on success', async () => {
8
- const manager = new process_manager_1.ProcessManager((0, environment_mock_1.environmentMock)(process.cwd()), 0);
9
- let currentIndex = 0;
10
- const expectedEvents = ['started', 'ended'];
11
- manager.on((evt) => {
12
- expect(evt.type).toBe(expectedEvents[currentIndex++]);
13
- });
14
- manager.task({ type: 'task', name: 'test-success', id: 'a' }, () => new Promise((resolve) => setTimeout(() => resolve(), 100)));
15
- await manager.onComplete();
16
- expect(currentIndex).toBe(2);
22
+ const manager = new process_manager_1.ProcessManager(0);
23
+ let called = false;
24
+ await manager.task(fakeWorkItem('a'), () => new Promise((resolve) => setTimeout(() => {
25
+ called = true;
26
+ resolve();
27
+ }, 100)));
28
+ expect(called).toBeTruthy();
17
29
  });
18
- it('should complete on error', async () => {
19
- const manager = new process_manager_1.ProcessManager((0, environment_mock_1.environmentMock)(process.cwd()), 0);
20
- let currentIndex = 0;
21
- const expectedEvents = ['started', 'ended'];
22
- manager.on((evt) => {
23
- expect(evt.type).toBe(expectedEvents[currentIndex++]);
24
- });
25
- manager.task({ type: 'task', name: 'test-error', id: 'a' }, () => new Promise((resolve, reject) => setTimeout(() => reject(new Error('3')), 100)));
26
- await manager.onComplete();
27
- expect(currentIndex).toBe(2);
30
+ it('should throw error', async () => {
31
+ const manager = new process_manager_1.ProcessManager(0);
32
+ await expect(manager.task(fakeWorkItem('a'), () => new Promise((resolve, reject) => setTimeout(() => reject(new Error('3')), 100)))).rejects.toThrow('3');
28
33
  });
29
34
  it('should not start more than worker count', async () => {
30
- const manager = new process_manager_1.ProcessManager((0, environment_mock_1.environmentMock)(process.cwd()), 1);
31
- const processes = [];
32
- let concurrencyCount = 0;
33
- let hasOversteppedLimits = false;
34
- manager.on((evt) => {
35
- if (evt.type === 'started') {
36
- concurrencyCount++;
37
- }
38
- else if (evt.type === 'ended') {
39
- concurrencyCount--;
40
- processes.push(evt.context.id);
41
- }
42
- if (concurrencyCount > 1) {
43
- hasOversteppedLimits = true;
44
- }
45
- });
46
- manager.task({ type: 'task', name: 'test-success', id: 'a' }, () => new Promise((resolve) => setTimeout(() => resolve(), 100)));
47
- manager.task({ type: 'task', name: 'test-success', id: 'b' }, () => new Promise((resolve) => setTimeout(() => resolve(), 100)));
48
- manager.task({ type: 'task', name: 'test-success', id: 'c' }, () => new Promise((resolve) => setTimeout(() => resolve(), 100)));
49
- await manager.onComplete();
50
- expect(processes).toEqual(['a', 'b', 'c']);
51
- expect(hasOversteppedLimits).toBeFalsy();
52
- expect(concurrencyCount).toBe(0);
53
- });
54
- it('should terminate on abort', async () => {
55
- const env = (0, environment_mock_1.environmentMock)(process.cwd());
56
- const manager = new process_manager_1.ProcessManager(env, 0);
57
- manager.task({ type: 'task', name: 'test-success', id: 'a' }, (abort) => new Promise((resolve) => {
58
- (0, abort_event_1.listenOnAbort)(abort.signal, resolve);
59
- }));
60
- setTimeout(() => {
61
- env.abortCtrl.abort();
62
- }, 200);
63
- await manager.onComplete();
64
- });
65
- it('should abort if task with same id is already started', async () => {
66
- const manager = new process_manager_1.ProcessManager((0, environment_mock_1.environmentMock)(process.cwd()), 0);
67
- const processes = [];
35
+ const manager = new process_manager_1.ProcessManager(1);
68
36
  let concurrencyCount = 0;
69
- let hasOversteppedLimits = false;
70
- manager.on((evt) => {
71
- if (evt.type === 'started') {
72
- concurrencyCount++;
73
- }
74
- else if (evt.type === 'ended') {
37
+ const fakeAction = () => new Promise((resolve) => {
38
+ expect(concurrencyCount).toBe(0);
39
+ concurrencyCount++;
40
+ setTimeout(() => {
41
+ expect(concurrencyCount).toBe(1);
75
42
  concurrencyCount--;
76
- processes.push(evt.context.id);
77
- }
78
- if (concurrencyCount > 1) {
79
- hasOversteppedLimits = true;
80
- }
43
+ resolve();
44
+ }, 100);
81
45
  });
82
- const abortController = manager.task({ type: 'task', name: 'test-success', id: 'a' }, () => new Promise((resolve) => setTimeout(() => resolve(), 200)));
83
- manager.task({ type: 'task', name: 'test-success', id: 'a' }, () => new Promise((resolve) => setTimeout(() => resolve(), 100)));
84
- expect(abortController.signal.aborted).toBeTruthy();
85
- await manager.onComplete();
86
- expect(processes).toEqual(['a', 'a']);
87
- expect(hasOversteppedLimits).toBeFalsy();
46
+ await Promise.all([
47
+ manager.task(fakeWorkItem('a'), fakeAction),
48
+ manager.task(fakeWorkItem('b'), fakeAction),
49
+ manager.task(fakeWorkItem('c'), fakeAction),
50
+ ]);
88
51
  expect(concurrencyCount).toBe(0);
89
52
  });
90
53
  });
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resetWorkTree = void 0;
4
+ const state_1 = require("./state");
5
+ function resetWorkTree(workTree, type) {
6
+ return {
7
+ tasks: Object.entries(workTree.tasks).reduce((tasks, [key, task]) => {
8
+ task.state = new state_1.State({
9
+ type: 'pending',
10
+ stateKey: null,
11
+ });
12
+ tasks[key] = task;
13
+ return tasks;
14
+ }, {}),
15
+ services: Object.entries(workTree.services).reduce((services, [key, service]) => {
16
+ if (type === 'execute' && service.requiredBy.length === 0) {
17
+ return services;
18
+ }
19
+ service.state = new state_1.State({
20
+ type: 'pending',
21
+ stateKey: null,
22
+ });
23
+ services[key] = service;
24
+ return services;
25
+ }, {}),
26
+ environment: workTree.environment,
27
+ };
28
+ }
29
+ exports.resetWorkTree = resetWorkTree;
30
+ //# sourceMappingURL=reset-work-tree.js.map
@@ -1,19 +1,29 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.checkForLoop = void 0;
4
- const plan_work_nodes_1 = require("../../planner/utils/plan-work-nodes");
4
+ const plan_work_tasks_1 = require("../../planner/utils/plan-work-tasks");
5
5
  const validate_1 = require("../../planner/validate");
6
- function checkForLoop(state) {
7
- for (const nodeState of (0, plan_work_nodes_1.iterateWorkNodes)(state.node)) {
8
- const cyclePath = (0, validate_1.hasCycle)(nodeState.node, []);
6
+ function checkForLoop(workTree) {
7
+ for (const task of (0, plan_work_tasks_1.iterateWorkTasks)(workTree)) {
8
+ const cyclePath = (0, validate_1.hasDependencyCycle)(task, []) ?? (0, validate_1.hasMixedCycle)(task, []);
9
9
  if (cyclePath && cyclePath.length > 0) {
10
10
  const errorMessage = `task cycle detected ${cyclePath.map((n) => n.name).join(' -> ')}`;
11
- state.node[nodeState.node.id] = {
11
+ task.state.set({
12
12
  type: 'error',
13
- node: nodeState.node,
14
13
  stateKey: null,
15
14
  errorMessage,
16
- };
15
+ });
16
+ }
17
+ }
18
+ for (const service of (0, plan_work_tasks_1.iterateWorkServices)(workTree)) {
19
+ const cyclePath = (0, validate_1.hasNeedCycle)(service, []) ?? (0, validate_1.hasMixedCycle)(service, []);
20
+ if (cyclePath && cyclePath.length > 0) {
21
+ const errorMessage = `service cycle detected ${cyclePath.map((n) => n.name).join(' -> ')}`;
22
+ service.state.set({
23
+ type: 'error',
24
+ stateKey: null,
25
+ errorMessage,
26
+ });
17
27
  }
18
28
  }
19
29
  }
@@ -1,3 +1,3 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- //# sourceMappingURL=scheduler-state.js.map
3
+ //# sourceMappingURL=dependency-state.js.map
@@ -1,27 +1,91 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.checkCacheState = void 0;
4
- const read_work_node_cache_1 = require("../../optimizer/read-work-node-cache");
5
- const get_work_node_cache_stats_1 = require("../../optimizer/get-work-node-cache-stats");
6
- async function checkCacheState(node, defaultCacheMethod, environment) {
7
- const status = environment.status.task(node);
8
- const caching = node.caching ?? defaultCacheMethod;
9
- const currentStats = await (0, get_work_node_cache_stats_1.getWorkNodeCacheStats)(node, environment);
10
- const stateKey = (0, get_work_node_cache_stats_1.getStateKey)(currentStats, caching);
11
- if (caching === 'none') {
12
- status.write('debug', `${node.name} is skipping cache check, because caching is disabled`);
13
- return { cached: false, stateKey };
4
+ const get_work_cache_stats_1 = require("../../optimizer/get-work-cache-stats");
5
+ const work_item_1 = require("../../planner/work-item");
6
+ const get_cache_directory_1 = require("../../optimizer/get-cache-directory");
7
+ const cache_metadata_1 = require("../cache-metadata");
8
+ const work_task_cache_description_1 = require("../../optimizer/work-task-cache-description");
9
+ const log_1 = require("../../log");
10
+ const crypto_1 = require("crypto");
11
+ function resolveEffective(item, fallbackMethod) {
12
+ const declared = item.data.caching;
13
+ if (declared.implicit) {
14
+ return { ...declared, method: fallbackMethod };
14
15
  }
15
- const cache = await (0, read_work_node_cache_1.readCache)(node, environment);
16
- if (cache === null) {
17
- status.write('debug', `${node.name} is skipping cache check, because there was none found`);
18
- return { cached: false, stateKey };
16
+ return declared;
17
+ }
18
+ // Fold a task's own state key together with the state keys of its dependencies.
19
+ // A change anywhere in the dependency subtree must change the resulting key, so
20
+ // that a downstream task is invalidated when an upstream source changes — even
21
+ // if the downstream task's own sources are untouched.
22
+ function combineStateKeys(ownKey, depKeys) {
23
+ if (depKeys.length === 0) {
24
+ return ownKey;
25
+ }
26
+ // Sort so the key is independent of dependency declaration order.
27
+ const sorted = [...depKeys].sort();
28
+ return (0, crypto_1.createHash)('md5')
29
+ .update([ownKey, ...sorted].join(','))
30
+ .digest('hex');
31
+ }
32
+ // Compute an item's effective state key from its source files plus the effective
33
+ // state keys of its dependencies, recursively. This is derived purely from the
34
+ // work tree and the filesystem, so it does not depend on scheduler/run state —
35
+ // important because checkCacheState runs before dependencies are awaited.
36
+ async function resolveEffectiveStateKey(item, defaultCacheMethod, environment, memo) {
37
+ const existing = memo.get(item.id());
38
+ if (existing !== undefined) {
39
+ return existing;
40
+ }
41
+ const resolved = resolveEffective(item, defaultCacheMethod);
42
+ // Always hash with a concrete method; `none` only disables the cache *check*
43
+ // for that task, its sources still influence what depends on it.
44
+ const method = resolved.method === 'none' ? defaultCacheMethod : resolved.method;
45
+ const stats = await (0, get_work_cache_stats_1.getWorkCacheStats)(item.data, environment);
46
+ const ownKey = (0, get_work_cache_stats_1.getStateKey)(stats, method);
47
+ const depKeys = [];
48
+ for (const dep of item.deps) {
49
+ depKeys.push(await resolveEffectiveStateKey(dep, defaultCacheMethod, environment, memo));
50
+ }
51
+ const combined = combineStateKeys(ownKey, depKeys);
52
+ memo.set(item.id(), combined);
53
+ return combined;
54
+ }
55
+ async function checkCacheState(item, defaultCacheMethod, environment) {
56
+ const resolved = resolveEffective(item, defaultCacheMethod);
57
+ const currentStats = await (0, get_work_cache_stats_1.getWorkCacheStats)(item.data, environment);
58
+ const ownKey = (0, get_work_cache_stats_1.getStateKey)(currentStats, resolved.method === 'none' ? defaultCacheMethod : resolved.method);
59
+ const memo = new Map();
60
+ const depKeys = [];
61
+ for (const dep of item.deps) {
62
+ depKeys.push(await resolveEffectiveStateKey(dep, defaultCacheMethod, environment, memo));
63
+ }
64
+ const stateKey = combineStateKeys(ownKey, depKeys);
65
+ if (resolved.method === 'none') {
66
+ item.status.write('debug', `${item.name} is skipping cache check, because caching is disabled`);
67
+ return { cached: false, stateKey, resolved };
68
+ }
69
+ if ((0, work_item_1.isWorkTaskItem)(item)) {
70
+ const runtimeStateKey = await item.runtime.currentStateKey(environment);
71
+ if (runtimeStateKey === stateKey) {
72
+ return { cached: true, stateKey, resolved };
73
+ }
74
+ try {
75
+ const cacheDir = (0, get_cache_directory_1.getCacheDirectory)(item.id());
76
+ const pulled = await resolved.backend.pull(item.id(), stateKey, cacheDir, environment);
77
+ if (pulled) {
78
+ item.status.write('info', `${item.name} pulled from cache "${resolved.name}" (${resolved.backend.type})`);
79
+ await item.runtime.restore(environment, cacheDir);
80
+ await (0, cache_metadata_1.writeCacheMetadata)(environment, item.id(), currentStats, (0, work_task_cache_description_1.getWorkTaskCacheDescription)(item.data));
81
+ return { cached: true, stateKey, resolved };
82
+ }
83
+ }
84
+ catch (e) {
85
+ item.status.write('warn', `${item.name} failed to pull from cache "${resolved.name}": ${(0, log_1.getErrorMessage)(e)} — continuing with local execution`);
86
+ }
19
87
  }
20
- const cacheKey = (0, get_work_node_cache_stats_1.getStateKey)(cache, caching);
21
- return {
22
- cached: cacheKey === stateKey,
23
- stateKey,
24
- };
88
+ return { cached: false, stateKey, resolved };
25
89
  }
26
90
  exports.checkCacheState = checkCacheState;
27
91
  //# sourceMappingURL=enqueue-next.js.map
@@ -0,0 +1,136 @@
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 environment_mock_1 = require("../environment-mock");
7
+ const enqueue_next_1 = require("./enqueue-next");
8
+ const local_cache_backend_1 = require("../../cache/backends/local-cache-backend");
9
+ const state_1 = require("../state");
10
+ describe('checkCacheState', () => {
11
+ let scratch;
12
+ let remoteDir;
13
+ let machineHome;
14
+ let originalHome;
15
+ beforeEach(() => {
16
+ scratch = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), 'hammerkit-enqueue-'));
17
+ remoteDir = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), 'hammerkit-remote-'));
18
+ machineHome = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), 'hammerkit-home-'));
19
+ originalHome = process.env.HOME;
20
+ if (process.platform === 'win32') {
21
+ process.env.APPDATA = machineHome;
22
+ }
23
+ else {
24
+ process.env.HOME = machineHome;
25
+ }
26
+ });
27
+ afterEach(() => {
28
+ (0, fs_1.rmSync)(scratch, { recursive: true, force: true });
29
+ (0, fs_1.rmSync)(remoteDir, { recursive: true, force: true });
30
+ (0, fs_1.rmSync)(machineHome, { recursive: true, force: true });
31
+ if (originalHome !== undefined) {
32
+ process.env.HOME = originalHome;
33
+ }
34
+ else {
35
+ delete process.env.HOME;
36
+ }
37
+ });
38
+ // empty remote → backend.pull always misses, so a key mismatch yields cached:false
39
+ function makeResolved(method) {
40
+ return {
41
+ name: 'remote',
42
+ method,
43
+ backend: (0, local_cache_backend_1.createLocalCacheBackend)({ type: 'local', path: remoteDir }),
44
+ implicit: false,
45
+ };
46
+ }
47
+ function makeTask(id, options) {
48
+ const storedStateKey = options.storedStateKey ?? null;
49
+ return {
50
+ id: () => id,
51
+ name: id,
52
+ status: { write: vi.fn() },
53
+ data: {
54
+ type: 'local-task',
55
+ name: id,
56
+ cwd: scratch,
57
+ cmds: [],
58
+ generates: [],
59
+ src: (options.srcFiles ?? []).map((absolutePath) => ({
60
+ absolutePath,
61
+ source: absolutePath,
62
+ matcher: () => true,
63
+ inherited: null,
64
+ isFile: true,
65
+ })),
66
+ envs: { variables: {}, replacements: [] },
67
+ labels: {},
68
+ shell: '/bin/sh',
69
+ caching: options.resolved,
70
+ description: null,
71
+ scope: {},
72
+ },
73
+ needs: [],
74
+ deps: options.deps ?? [],
75
+ requiredBy: [],
76
+ state: new state_1.State({ type: 'pending', stateKey: null }),
77
+ runtime: {
78
+ async initialize() { },
79
+ async remove() { },
80
+ async execute() { },
81
+ async stop() { },
82
+ async archive() { },
83
+ async restore() { },
84
+ async currentStateKey() {
85
+ return storedStateKey;
86
+ },
87
+ },
88
+ };
89
+ }
90
+ it('reports cached:false when caching method is none', async () => {
91
+ const env = (0, environment_mock_1.environmentMock)(scratch);
92
+ const task = makeTask('none-task', { resolved: makeResolved('none') });
93
+ const result = await (0, enqueue_next_1.checkCacheState)(task, 'checksum', env);
94
+ expect(result.cached).toBe(false);
95
+ });
96
+ it('reports cached:true when the stored key matches the effective key', async () => {
97
+ const env = (0, environment_mock_1.environmentMock)(scratch);
98
+ const probe = makeTask('hit-task', { resolved: makeResolved('checksum') });
99
+ const { stateKey } = await (0, enqueue_next_1.checkCacheState)(probe, 'checksum', env);
100
+ const task = makeTask('hit-task', { resolved: makeResolved('checksum'), storedStateKey: stateKey });
101
+ const result = await (0, enqueue_next_1.checkCacheState)(task, 'checksum', env);
102
+ expect(result.cached).toBe(true);
103
+ expect(result.stateKey).toBe(stateKey);
104
+ });
105
+ it('invalidates a downstream task when an upstream dependency source changes', async () => {
106
+ const env = (0, environment_mock_1.environmentMock)(scratch);
107
+ const upstreamSrc = (0, path_1.join)(scratch, 'upstream.txt');
108
+ (0, fs_1.writeFileSync)(upstreamSrc, 'v1');
109
+ const makeUpstream = () => makeTask('upstream', { resolved: makeResolved('checksum'), srcFiles: [upstreamSrc] });
110
+ const makeDownstream = (storedStateKey) => makeTask('downstream', { resolved: makeResolved('checksum'), deps: [makeUpstream()], storedStateKey });
111
+ // First pass: capture the downstream effective key and pretend it was stored.
112
+ const keyBefore = (await (0, enqueue_next_1.checkCacheState)(makeDownstream(null), 'checksum', env)).stateKey;
113
+ const cachedHit = await (0, enqueue_next_1.checkCacheState)(makeDownstream(keyBefore), 'checksum', env);
114
+ expect(cachedHit.cached).toBe(true);
115
+ // Change ONLY the upstream source — downstream's own sources are untouched.
116
+ (0, fs_1.writeFileSync)(upstreamSrc, 'v2-changed');
117
+ const afterChange = await (0, enqueue_next_1.checkCacheState)(makeDownstream(keyBefore), 'checksum', env);
118
+ expect(afterChange.stateKey).not.toBe(keyBefore);
119
+ expect(afterChange.cached).toBe(false);
120
+ });
121
+ it('produces a stable key regardless of dependency declaration order', async () => {
122
+ const env = (0, environment_mock_1.environmentMock)(scratch);
123
+ const aSrc = (0, path_1.join)(scratch, 'a.txt');
124
+ const bSrc = (0, path_1.join)(scratch, 'b.txt');
125
+ (0, fs_1.writeFileSync)(aSrc, 'a');
126
+ (0, fs_1.writeFileSync)(bSrc, 'b');
127
+ const depA = () => makeTask('dep-a', { resolved: makeResolved('checksum'), srcFiles: [aSrc] });
128
+ const depB = () => makeTask('dep-b', { resolved: makeResolved('checksum'), srcFiles: [bSrc] });
129
+ const forward = makeTask('root', { resolved: makeResolved('checksum'), deps: [depA(), depB()] });
130
+ const reversed = makeTask('root', { resolved: makeResolved('checksum'), deps: [depB(), depA()] });
131
+ const keyForward = (await (0, enqueue_next_1.checkCacheState)(forward, 'checksum', env)).stateKey;
132
+ const keyReversed = (await (0, enqueue_next_1.checkCacheState)(reversed, 'checksum', env)).stateKey;
133
+ expect(keyForward).toBe(keyReversed);
134
+ });
135
+ });
136
+ //# sourceMappingURL=enqueue-next.spec.js.map
@@ -1,3 +1,3 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- //# sourceMappingURL=node-state.js.map
3
+ //# sourceMappingURL=need-state.js.map
@@ -1,3 +1,3 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- //# sourceMappingURL=build-file.js.map
3
+ //# sourceMappingURL=task-state.js.map
@@ -4,7 +4,7 @@ exports.setUserPermission = void 0;
4
4
  const execute_docker_1 = require("./execute-docker");
5
5
  async function setUserPermission(directory, status, environment, container, user) {
6
6
  status.write('debug', `set permission on ${directory}`);
7
- const result = await (0, execute_docker_1.execCommand)(status, environment, container, '/', ['chown', user, directory], null, undefined, undefined);
7
+ const result = await (0, execute_docker_1.execCommand)(status, environment, container, '/', ['chown', user, directory], null, undefined, new AbortController().signal);
8
8
  if (result.type === 'canceled') {
9
9
  return;
10
10
  }
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.awaitState = exports.isState = exports.untilChanged = void 0;
4
+ const abort_event_1 = require("../utils/abort-event");
5
+ const abort_1 = require("./abort");
6
+ function untilChanged(key, state, abort) {
7
+ return new Promise((resolve, reject) => {
8
+ const handle = state.on(key, (value) => {
9
+ handle.close();
10
+ abortListener.close();
11
+ resolve(value);
12
+ });
13
+ const abortListener = (0, abort_event_1.listenOnAbort)(abort, () => {
14
+ handle.close();
15
+ reject(new abort_1.AbortError());
16
+ });
17
+ });
18
+ }
19
+ exports.untilChanged = untilChanged;
20
+ function isState(key, state, condition, abort) {
21
+ if (condition(state.current)) {
22
+ return Promise.resolve(state.current);
23
+ }
24
+ return new Promise((resolve, reject) => {
25
+ const listener = state.on(key, (value) => {
26
+ if (!condition(value)) {
27
+ return;
28
+ }
29
+ abortListener.close();
30
+ resolve(value);
31
+ listener.close();
32
+ });
33
+ const abortListener = (0, abort_event_1.listenOnAbort)(abort, () => {
34
+ listener.close();
35
+ reject(new abort_1.AbortError());
36
+ });
37
+ });
38
+ }
39
+ exports.isState = isState;
40
+ function awaitState(key, state, condition, abort) {
41
+ return new Promise((resolve, reject) => {
42
+ if (condition(state.current)) {
43
+ resolve();
44
+ return;
45
+ }
46
+ const listener = state.on(key, (value) => {
47
+ if (!condition(value)) {
48
+ return;
49
+ }
50
+ listener.close();
51
+ abortListener.close();
52
+ resolve();
53
+ });
54
+ const abortListener = (0, abort_event_1.listenOnAbort)(abort, () => {
55
+ listener.close();
56
+ reject(new abort_1.AbortError());
57
+ });
58
+ });
59
+ }
60
+ exports.awaitState = awaitState;
61
+ //# sourceMappingURL=state-resolver.js.map
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const state_resolver_1 = require("./state-resolver");
4
+ const state_1 = require("./state");
5
+ const abort_1 = require("./abort");
6
+ describe('state-resolver', () => {
7
+ it('should resolve state', async () => {
8
+ const state = new state_1.State('a');
9
+ const isB = (val) => val === 'b';
10
+ const result = (0, state_resolver_1.isState)('test-state', state, isB, new AbortController().signal);
11
+ state.set('c');
12
+ state.set('a');
13
+ state.set('b');
14
+ await result;
15
+ });
16
+ it('should resolve state if already in', async () => {
17
+ const state = new state_1.State('b');
18
+ const isB = (val) => val === 'b';
19
+ await (0, state_resolver_1.isState)('test-state', state, isB, new AbortController().signal);
20
+ });
21
+ it('isState rejects with AbortError when aborted before the condition is met', async () => {
22
+ const state = new state_1.State('a');
23
+ const isB = (val) => val === 'b';
24
+ const abort = new AbortController();
25
+ const result = (0, state_resolver_1.isState)('test-state', state, isB, abort.signal);
26
+ abort.abort();
27
+ await expect(result).rejects.toBeInstanceOf(abort_1.AbortError);
28
+ });
29
+ it('awaitState rejects with AbortError when aborted before the condition is met', async () => {
30
+ const state = new state_1.State('a');
31
+ const abort = new AbortController();
32
+ const result = (0, state_resolver_1.awaitState)('test-state', state, (val) => val === 'b', abort.signal);
33
+ abort.abort();
34
+ await expect(result).rejects.toBeInstanceOf(abort_1.AbortError);
35
+ });
36
+ });
37
+ //# sourceMappingURL=state-resolver.spec.js.map
@@ -2,46 +2,52 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.State = void 0;
4
4
  class State {
5
- constructor(current) {
5
+ constructor(current, options) {
6
6
  this.current = current;
7
+ this.options = options;
7
8
  this.listeners = [];
8
- }
9
- assignState(state, newValue) {
10
- const currentKeys = Object.keys(state);
11
- const newKeys = Object.keys(newValue);
12
- const keysToRemove = currentKeys.filter((k) => newKeys.indexOf(k) === -1);
13
- for (const key of keysToRemove) {
14
- delete state[key];
15
- }
16
- for (const key of newKeys) {
17
- state[key] = newValue[key];
9
+ this.handles = [];
10
+ if (this.options?.subStates) {
11
+ this.attachToStores(this.options.subStates);
18
12
  }
19
13
  }
20
- resetNode(newState) {
21
- const state = this.current.node[newState.node.id];
22
- this.assignState(state, newState);
23
- this.notifyListeners(this.current);
24
- return state;
25
- }
26
- patchNode(newState, stateKey) {
27
- const state = this.current.node[newState.node.id];
28
- if (state.stateKey === stateKey) {
29
- this.assignState(state, newState);
30
- this.notifyListeners(this.current);
14
+ attachToStores(subStates) {
15
+ for (const state of subStates) {
16
+ this.handles.push(state.on('sub-state-forward', () => {
17
+ this.notifyListeners(this.current);
18
+ }));
31
19
  }
32
- return state;
33
20
  }
34
- patchService(newState) {
35
- this.assignState(this.current.service[newState.service.id], newState);
36
- this.notifyListeners(this.current);
21
+ set(newState) {
22
+ this.current = newState;
23
+ this.notifyListeners(newState);
24
+ return this.current;
37
25
  }
38
- on(listener) {
39
- this.listeners.push(listener);
40
- listener(this.current);
26
+ on(key, listener) {
27
+ this.listeners.push({ key, listener });
28
+ return {
29
+ close: () => {
30
+ const index = this.listeners.findIndex((l) => l.key === key && l.listener === listener);
31
+ if (index >= 0) {
32
+ this.listeners.splice(index, 1);
33
+ }
34
+ else {
35
+ // TODO warn
36
+ }
37
+ },
38
+ };
41
39
  }
42
40
  notifyListeners(evt) {
43
- for (const listener of this.listeners) {
44
- listener(evt);
41
+ for (const listener of [...this.listeners]) {
42
+ listener.listener(evt);
43
+ }
44
+ }
45
+ close() {
46
+ for (const handle of this.handles) {
47
+ handle.close();
48
+ }
49
+ if (this.options?.onDestroy) {
50
+ this.options?.onDestroy();
45
51
  }
46
52
  }
47
53
  }