hammerkit 1.6.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (277) hide show
  1. package/README.md +7 -7
  2. package/build.schema.json +59 -15
  3. package/dist/cache/backends/local-cache-backend.js +58 -1
  4. package/dist/cache/backends/registry/registry-client.js +264 -0
  5. package/dist/cache/backends/registry/registry-credentials.js +81 -0
  6. package/dist/cache/backends/registry/registry-reference.js +36 -0
  7. package/dist/cache/backends/registry-cache-backend.js +145 -0
  8. package/dist/cache/backends/s3-cache-backend.js +99 -46
  9. package/dist/cache/cache-backend-registry.js +2 -0
  10. package/dist/cache/cache-inventory.js +84 -0
  11. package/dist/cache/cache-sync.js +77 -0
  12. package/dist/cache/explain.js +214 -0
  13. package/dist/cache/last-resolved.js +48 -0
  14. package/dist/cache/read-only.js +15 -0
  15. package/dist/cache/retention.js +61 -0
  16. package/dist/cli.js +43 -0
  17. package/dist/docker/using-container.js +17 -1
  18. package/dist/executer/archive-task-entry.js +21 -0
  19. package/dist/executer/await-completed-dependencies.js +32 -14
  20. package/dist/executer/cache-metadata.js +9 -2
  21. package/dist/executer/docker-service.js +2 -1
  22. package/dist/executer/docker-task.js +18 -3
  23. package/dist/executer/dry-run.js +73 -0
  24. package/dist/executer/event-cache.js +6 -5
  25. package/dist/executer/execute-command.js +21 -3
  26. package/dist/executer/execute-docker.js +18 -2
  27. package/dist/executer/execute-work-service.js +10 -1
  28. package/dist/executer/execute-work-task.js +111 -26
  29. package/dist/executer/execute-work-tree.js +26 -1
  30. package/dist/executer/execution-steps.js +3 -8
  31. package/dist/executer/get-container-binds.js +13 -1
  32. package/dist/executer/get-scheduler-execute-result.js +4 -1
  33. package/dist/executer/lazy-resolver.js +15 -3
  34. package/dist/executer/reset-work-tree.js +2 -0
  35. package/dist/executer/run-summary.js +102 -0
  36. package/dist/executer/scheduler/enqueue-next.js +15 -60
  37. package/dist/executer/scheduler/state-key.js +78 -0
  38. package/dist/index.js +14 -1
  39. package/dist/kubernetes/apply.js +6 -7
  40. package/dist/kubernetes/await-running-state.js +100 -60
  41. package/dist/kubernetes/ensure-http-route.js +7 -10
  42. package/dist/kubernetes/ensure-namespace.js +9 -10
  43. package/dist/kubernetes/ensure-persistent-data.js +6 -5
  44. package/dist/kubernetes/get-service-ip.js +2 -2
  45. package/dist/kubernetes/kubernetes-instance.js +0 -4
  46. package/dist/kubernetes/remove-persistent-data.js +16 -6
  47. package/dist/kubernetes/resolve-pod-name.js +7 -7
  48. package/dist/log.js +16 -6
  49. package/dist/logging/live-logger.js +2 -1
  50. package/dist/optimizer/calculate-checksum.js +8 -2
  51. package/dist/optimizer/get-work-cache-stats.js +66 -16
  52. package/dist/optimizer/work-task-cache-description.js +34 -5
  53. package/dist/planner/project-root.js +26 -0
  54. package/dist/planner/utils/append-work-environment.js +1 -1
  55. package/dist/planner/utils/append-work-service.js +2 -1
  56. package/dist/planner/utils/append-work-task.js +7 -1
  57. package/dist/planner/utils/get-outputs-to-reset.js +20 -0
  58. package/dist/planner/utils/parse-work-generate.js +2 -2
  59. package/dist/planner/utils/parse-work-source.js +35 -30
  60. package/dist/planner/utils/portable-path.js +30 -0
  61. package/dist/planner/work-cache-id.js +2 -2
  62. package/dist/planner/work-graph-serializer.js +103 -0
  63. package/dist/planner/work-instance-id.js +15 -0
  64. package/dist/planner/work-runtime-docker.js +58 -15
  65. package/dist/planner/work-runtime-kubernetes.js +95 -45
  66. package/dist/planner/work-runtime-local.js +31 -8
  67. package/dist/planner/work-service-id.js +16 -4
  68. package/dist/program.js +235 -1
  69. package/dist/schema/build-file-base-task-schema.js +4 -0
  70. package/dist/schema/build-file-environment-schema-http-route.js +15 -0
  71. package/dist/schema/build-file-environment-schema.js +8 -2
  72. package/dist/schema/build-file-schema.js +1 -1
  73. package/dist/schema/cache-schema.js +21 -2
  74. package/dist/schema/duration-schema.js +18 -0
  75. package/dist/schema/envs-schema.js +1 -1
  76. package/dist/schema/reference-parser.js +3 -0
  77. package/dist/schema/size-schema.js +18 -0
  78. package/dist/schema/work-scope-parser.js +15 -2
  79. package/dist/utils/units.js +87 -0
  80. package/package.json +20 -9
  81. package/.agents/FEEDBACK.md +0 -74
  82. package/.agents/skills/before-commit/SKILL.md +0 -101
  83. package/.agents/skills/do-the-work/SKILL.md +0 -64
  84. package/.agents/skills/fix-ci/SKILL.md +0 -98
  85. package/.agents/skills/speckit-git-commit/SKILL.md +0 -53
  86. package/.agents/skills/speckit-git-feature/SKILL.md +0 -72
  87. package/.agents/skills/speckit-git-initialize/SKILL.md +0 -54
  88. package/.agents/skills/speckit-git-remote/SKILL.md +0 -50
  89. package/.agents/skills/speckit-git-validate/SKILL.md +0 -54
  90. package/.devcontainer/Dockerfile +0 -5
  91. package/.devcontainer/devcontainer.json +0 -40
  92. package/.gitbook.yaml +0 -5
  93. package/.specify/extensions/.registry +0 -37
  94. package/.specify/extensions/git/README.md +0 -100
  95. package/.specify/extensions/git/commands/speckit.git.commit.md +0 -48
  96. package/.specify/extensions/git/commands/speckit.git.feature.md +0 -67
  97. package/.specify/extensions/git/commands/speckit.git.initialize.md +0 -49
  98. package/.specify/extensions/git/commands/speckit.git.remote.md +0 -45
  99. package/.specify/extensions/git/commands/speckit.git.validate.md +0 -49
  100. package/.specify/extensions/git/config-template.yml +0 -62
  101. package/.specify/extensions/git/extension.yml +0 -140
  102. package/.specify/extensions/git/git-config.yml +0 -62
  103. package/.specify/extensions.yml +0 -149
  104. package/.specify/feature.json +0 -3
  105. package/.specify/init-options.json +0 -10
  106. package/.specify/integration.json +0 -15
  107. package/.specify/integrations/claude.manifest.json +0 -16
  108. package/.specify/integrations/speckit.manifest.json +0 -17
  109. package/.specify/memory/constitution.md +0 -162
  110. package/.specify/templates/checklist-template.md +0 -40
  111. package/.specify/templates/constitution-template.md +0 -50
  112. package/.specify/templates/plan-template.md +0 -113
  113. package/.specify/templates/spec-template.md +0 -131
  114. package/.specify/templates/tasks-template.md +0 -252
  115. package/.specify/workflows/speckit/workflow.yml +0 -77
  116. package/.specify/workflows/workflow-registry.json +0 -13
  117. package/AGENTS.md +0 -94
  118. package/Dockerfile +0 -32
  119. package/FOLLOWUPS.md +0 -34
  120. package/SUMMARY.md +0 -68
  121. package/VALUES.md +0 -53
  122. package/dist/cache/auto-pull.spec.js +0 -123
  123. package/dist/cache/backends/local-cache-backend.spec.js +0 -72
  124. package/dist/cache/backends/s3-cache-backend.spec.js +0 -118
  125. package/dist/cache/cache-backend-registry.spec.js +0 -22
  126. package/dist/cache/resolve-cache.spec.js +0 -52
  127. package/dist/cli.spec.js +0 -23
  128. package/dist/console/get-logger.spec.js +0 -23
  129. package/dist/docker/get-default-platform.spec.js +0 -29
  130. package/dist/docker/longest-common-prefix.spec.js +0 -26
  131. package/dist/docker/package.spec.js +0 -145
  132. package/dist/docker/remove-container.spec.js +0 -32
  133. package/dist/docker/using-container.spec.js +0 -90
  134. package/dist/environment/merge-environment-variables.spec.js +0 -24
  135. package/dist/environment/replace-env-variables.spec.js +0 -47
  136. package/dist/executer/abort.spec.js +0 -18
  137. package/dist/executer/environment-mock.spec.js +0 -22
  138. package/dist/executer/execute.spec.js +0 -51
  139. package/dist/executer/get-service-containers.spec.js +0 -59
  140. package/dist/executer/get-service-env-hints.spec.js +0 -65
  141. package/dist/executer/label-values.spec.js +0 -61
  142. package/dist/executer/lazy-resolver.spec.js +0 -20
  143. package/dist/executer/process-manager.spec.js +0 -54
  144. package/dist/executer/scheduler/enqueue-next.spec.js +0 -136
  145. package/dist/executer/state-resolver.spec.js +0 -37
  146. package/dist/executer/state.spec.js +0 -66
  147. package/dist/executer/states.spec.js +0 -14
  148. package/dist/file/get-file-context.spec.js +0 -178
  149. package/dist/file/move-files.spec.js +0 -48
  150. package/dist/kubernetes/ensure-http-route.spec.js +0 -70
  151. package/dist/kubernetes/ensure-ingress.js +0 -54
  152. package/dist/kubernetes/ensure-namespace.spec.js +0 -39
  153. package/dist/kubernetes/resolve-pod-name.spec.js +0 -81
  154. package/dist/log.spec.js +0 -131
  155. package/dist/logging/grouped-logger.spec.js +0 -84
  156. package/dist/logging/interactive-logger.spec.js +0 -57
  157. package/dist/logging/live-logger.spec.js +0 -37
  158. package/dist/logging/loggers.spec.js +0 -154
  159. package/dist/optimizer/calculate-checksum.spec.js +0 -34
  160. package/dist/optimizer/get-cache-directory.spec.js +0 -59
  161. package/dist/optimizer/get-work-cache-stats.spec.js +0 -79
  162. package/dist/optimizer/work-task-cache-description.spec.js +0 -45
  163. package/dist/parser/default-build-file.spec.js +0 -48
  164. package/dist/parser/parse-label-arguments.spec.js +0 -27
  165. package/dist/parser/read-build-file.spec.js +0 -89
  166. package/dist/parser/read-env-file.spec.js +0 -42
  167. package/dist/planner/utils/append-work-service.spec.js +0 -71
  168. package/dist/planner/utils/get-container-user.spec.js +0 -30
  169. package/dist/planner/utils/get-default-kube-config.spec.js +0 -11
  170. package/dist/planner/utils/normalize-path.spec.js +0 -24
  171. package/dist/planner/utils/parse-work-command.spec.js +0 -21
  172. package/dist/planner/utils/parse-work-generate.spec.js +0 -45
  173. package/dist/planner/utils/parse-work-mount.spec.js +0 -62
  174. package/dist/planner/utils/parse-work-mounts.spec.js +0 -19
  175. package/dist/planner/utils/parse-work-port.spec.js +0 -24
  176. package/dist/planner/utils/parse-work-ports.spec.js +0 -24
  177. package/dist/planner/utils/parse-work-source.spec.js +0 -58
  178. package/dist/planner/utils/parse-work-volume.spec.js +0 -18
  179. package/dist/planner/utils/plan-work-tasks.spec.js +0 -30
  180. package/dist/planner/utils/plan-work-volume.spec.js +0 -33
  181. package/dist/planner/utils/split-name.spec.js +0 -16
  182. package/dist/planner/utils/template-value.spec.js +0 -14
  183. package/dist/planner/validate.spec.js +0 -251
  184. package/dist/planner/work-cache-id.spec.js +0 -173
  185. package/dist/planner/work-runtime-local.spec.js +0 -193
  186. package/dist/program-commands.spec.js +0 -84
  187. package/dist/program.spec.js +0 -43
  188. package/dist/schema/build-file-environment-schema-ingress.js +0 -14
  189. package/dist/schema/build-file-schema.spec.js +0 -61
  190. package/dist/schema/doc-build-files.spec.js +0 -223
  191. package/dist/schema/parse-error.spec.js +0 -18
  192. package/dist/schema/reference-parser.spec.js +0 -83
  193. package/dist/schema/schema-parser.spec.js +0 -80
  194. package/dist/schema/type-guards.spec.js +0 -23
  195. package/dist/schema/work-scope-parser.spec.js +0 -66
  196. package/dist/service/update-service-status.spec.js +0 -27
  197. package/dist/testing/ensure-local-registry.js +0 -134
  198. package/dist/testing/example-test-suite.js +0 -63
  199. package/dist/testing/expect.js +0 -82
  200. package/dist/testing/get-test-suite.js +0 -9
  201. package/dist/testing/http-get.js +0 -21
  202. package/dist/testing/integration/cache.spec.js +0 -49
  203. package/dist/testing/integration/cancellation.spec.js +0 -45
  204. package/dist/testing/integration/clean.spec.js +0 -43
  205. package/dist/testing/integration/cmd.spec.js +0 -15
  206. package/dist/testing/integration/concurrency.spec.js +0 -19
  207. package/dist/testing/integration/docker-package-registry.spec.js +0 -44
  208. package/dist/testing/integration/docker-service-crash.spec.js +0 -36
  209. package/dist/testing/integration/docker.spec.js +0 -18
  210. package/dist/testing/integration/env.spec.js +0 -48
  211. package/dist/testing/integration/environment.spec.js +0 -97
  212. package/dist/testing/integration/error.spec.js +0 -19
  213. package/dist/testing/integration/export.spec.js +0 -22
  214. package/dist/testing/integration/extend.spec.js +0 -29
  215. package/dist/testing/integration/glob.spec.js +0 -40
  216. package/dist/testing/integration/include.spec.js +0 -31
  217. package/dist/testing/integration/invalid.spec.js +0 -17
  218. package/dist/testing/integration/invalid_loop.spec.js +0 -56
  219. package/dist/testing/integration/k8s-state-cache.spec.js +0 -41
  220. package/dist/testing/integration/kubernetes-service.spec.js +0 -148
  221. package/dist/testing/integration/kubernetes.spec.js +0 -99
  222. package/dist/testing/integration/labels.spec.js +0 -42
  223. package/dist/testing/integration/local.spec.js +0 -14
  224. package/dist/testing/integration/monorepo.spec.js +0 -15
  225. package/dist/testing/integration/multi-line.spec.js +0 -17
  226. package/dist/testing/integration/reference.spec.js +0 -21
  227. package/dist/testing/integration/services.spec.js +0 -38
  228. package/dist/testing/integration/store.spec.js +0 -52
  229. package/dist/testing/integration/unknown.spec.js +0 -28
  230. package/dist/testing/integration/validate.spec.js +0 -160
  231. package/dist/testing/integration/watch-dependency.spec.js +0 -33
  232. package/dist/testing/integration/watch.spec.js +0 -45
  233. package/dist/testing/requires-kubernetes.js +0 -18
  234. package/dist/testing/requires-linux-containers.js +0 -18
  235. package/dist/testing/requires-windows-containers.js +0 -18
  236. package/dist/testing/test-case.js +0 -75
  237. package/dist/testing/test-streams.js +0 -28
  238. package/dist/testing/test-suite-setup.js +0 -3
  239. package/dist/testing/test-suite.js +0 -3
  240. package/dist/testing/testing-timeout.js +0 -32
  241. package/dist/utils/abort-event.spec.js +0 -46
  242. package/dist/utils/buffer-context.spec.js +0 -38
  243. package/dist/utils/ci.spec.js +0 -53
  244. package/dist/utils/debouncer.spec.js +0 -58
  245. package/dist/utils/emitter.spec.js +0 -34
  246. package/dist/utils/empty-writable.spec.js +0 -18
  247. package/dist/utils/environment-config.spec.js +0 -26
  248. package/dist/utils/fail-never.spec.js +0 -9
  249. package/dist/utils/sleep.spec.js +0 -22
  250. package/dist/version.spec.js +0 -12
  251. package/specs/build-file/caches/spec.md +0 -114
  252. package/specs/build-file/environment-variables/spec.md +0 -82
  253. package/specs/build-file/includes/spec.md +0 -60
  254. package/specs/build-file/references/spec.md +0 -56
  255. package/specs/build-file/spec.md +0 -62
  256. package/specs/cli/clean/spec.md +0 -85
  257. package/specs/cli/down/spec.md +0 -85
  258. package/specs/cli/execute/spec.md +0 -101
  259. package/specs/cli/init/spec.md +0 -64
  260. package/specs/cli/ls/spec.md +0 -66
  261. package/specs/cli/package/spec.md +0 -110
  262. package/specs/cli/spec.md +0 -73
  263. package/specs/cli/store-restore/spec.md +0 -74
  264. package/specs/cli/up/spec.md +0 -80
  265. package/specs/cli/validate/spec.md +0 -70
  266. package/specs/labels/spec.md +0 -91
  267. package/specs/service/container/spec.md +0 -120
  268. package/specs/service/kubernetes/spec.md +0 -97
  269. package/specs/service/spec.md +0 -105
  270. package/specs/task/caching/spec.md +0 -107
  271. package/specs/task/container/spec.md +0 -118
  272. package/specs/task/dependencies/spec.md +0 -70
  273. package/specs/task/extending/spec.md +0 -74
  274. package/specs/task/kubernetes/spec.md +0 -125
  275. package/specs/task/needs/spec.md +0 -59
  276. package/specs/task/spec.md +0 -87
  277. package/specs/task/watching/spec.md +0 -69
@@ -1,61 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const label_values_1 = require("./label-values");
4
- describe('label-values', () => {
5
- describe('hasLabels', () => {
6
- it('is false for an empty object', () => {
7
- expect((0, label_values_1.hasLabels)({})).toBe(false);
8
- });
9
- it('is true when at least one key is present', () => {
10
- expect((0, label_values_1.hasLabels)({ app: ['web'] })).toBe(true);
11
- });
12
- });
13
- describe('matchesAnyLabel', () => {
14
- it('matches when a filter value is present in the labels', () => {
15
- expect((0, label_values_1.matchesAnyLabel)({ app: ['web'] }, { app: ['web', 'api'] })).toBe(true);
16
- });
17
- it('does not match when the value is absent', () => {
18
- expect((0, label_values_1.matchesAnyLabel)({ app: ['web'] }, { app: ['api'] })).toBe(false);
19
- });
20
- it('skips label keys that are missing or empty on the value', () => {
21
- expect((0, label_values_1.matchesAnyLabel)({ app: ['web'] }, {})).toBe(false);
22
- expect((0, label_values_1.matchesAnyLabel)({ app: ['web'] }, { app: [] })).toBe(false);
23
- });
24
- it('returns false for an empty filter', () => {
25
- expect((0, label_values_1.matchesAnyLabel)({}, { app: ['web'] })).toBe(false);
26
- });
27
- });
28
- describe('appliesToLabels', () => {
29
- it('applies when there is no filter and no exclude', () => {
30
- expect((0, label_values_1.appliesToLabels)({ app: ['web'] }, {})).toBe(true);
31
- });
32
- it('applies only when the filter matches', () => {
33
- expect((0, label_values_1.appliesToLabels)({ app: ['web'] }, { filterLabels: { app: ['web'] } })).toBe(true);
34
- expect((0, label_values_1.appliesToLabels)({ app: ['web'] }, { filterLabels: { app: ['api'] } })).toBe(false);
35
- });
36
- it('does not apply when an exclude matches', () => {
37
- expect((0, label_values_1.appliesToLabels)({ app: ['web'] }, { excludeLabels: { app: ['web'] } })).toBe(false);
38
- });
39
- it('exclude wins over a matching filter', () => {
40
- expect((0, label_values_1.appliesToLabels)({ app: ['web'], tier: ['frontend'] }, { filterLabels: { app: ['web'] }, excludeLabels: { tier: ['frontend'] } })).toBe(false);
41
- });
42
- });
43
- describe('mergeLabels', () => {
44
- it('coerces scalar values into string arrays', () => {
45
- expect((0, label_values_1.mergeLabels)({ app: 'web', port: 8080 })).toEqual({ app: ['web'], port: ['8080'] });
46
- });
47
- it('keeps array values as-is', () => {
48
- expect((0, label_values_1.mergeLabels)({ app: ['web', 'api'] })).toEqual({ app: ['web', 'api'] });
49
- });
50
- it('merges multiple sources and dedupes values', () => {
51
- expect((0, label_values_1.mergeLabels)({ app: 'web' }, { app: ['web', 'api'] }, { tier: 'frontend' })).toEqual({
52
- app: ['web', 'api'],
53
- tier: ['frontend'],
54
- });
55
- });
56
- it('ignores null and undefined sources', () => {
57
- expect((0, label_values_1.mergeLabels)(null, undefined, { app: 'web' })).toEqual({ app: ['web'] });
58
- });
59
- });
60
- });
61
- //# sourceMappingURL=label-values.spec.js.map
@@ -1,20 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const lazy_resolver_1 = require("./lazy-resolver");
4
- describe('lazyResolver', () => {
5
- it('calls the factory once and memoizes the result', () => {
6
- const factory = vi.fn(() => 'value');
7
- const resolve = (0, lazy_resolver_1.lazyResolver)(factory);
8
- expect(resolve()).toBe('value');
9
- expect(resolve()).toBe('value');
10
- expect(factory).toHaveBeenCalledTimes(1);
11
- });
12
- it('memoizes a falsy result so the factory still runs only once', () => {
13
- const factory = vi.fn(() => 0);
14
- const resolve = (0, lazy_resolver_1.lazyResolver)(factory);
15
- expect(resolve()).toBe(0);
16
- expect(resolve()).toBe(0);
17
- expect(factory).toHaveBeenCalledTimes(1);
18
- });
19
- });
20
- //# sourceMappingURL=lazy-resolver.spec.js.map
@@ -1,54 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const process_manager_1 = require("./process-manager");
4
- const work_item_status_1 = require("../planner/work-item-status");
5
- const empty_writable_1 = require("../utils/empty-writable");
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
- }
21
- it('should complete on success', async () => {
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();
29
- });
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');
33
- });
34
- it('should not start more than worker count', async () => {
35
- const manager = new process_manager_1.ProcessManager(1);
36
- let concurrencyCount = 0;
37
- const fakeAction = () => new Promise((resolve) => {
38
- expect(concurrencyCount).toBe(0);
39
- concurrencyCount++;
40
- setTimeout(() => {
41
- expect(concurrencyCount).toBe(1);
42
- concurrencyCount--;
43
- resolve();
44
- }, 100);
45
- });
46
- await Promise.all([
47
- manager.task(fakeWorkItem('a'), fakeAction),
48
- manager.task(fakeWorkItem('b'), fakeAction),
49
- manager.task(fakeWorkItem('c'), fakeAction),
50
- ]);
51
- expect(concurrencyCount).toBe(0);
52
- });
53
- });
54
- //# sourceMappingURL=process-manager.spec.js.map
@@ -1,136 +0,0 @@
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,37 +0,0 @@
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
@@ -1,66 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const state_1 = require("./state");
4
- describe('State', () => {
5
- it('exposes the initial value via `current`', () => {
6
- const s = new state_1.State(7);
7
- expect(s.current).toBe(7);
8
- });
9
- it('set() updates current and returns the new value', () => {
10
- const s = new state_1.State(0);
11
- expect(s.set(3)).toBe(3);
12
- expect(s.current).toBe(3);
13
- });
14
- it('notifies every registered listener on set()', () => {
15
- const s = new state_1.State(0);
16
- const a = vi.fn();
17
- const b = vi.fn();
18
- s.on('a', a);
19
- s.on('b', b);
20
- s.set(1);
21
- expect(a).toHaveBeenCalledWith(1);
22
- expect(b).toHaveBeenCalledWith(1);
23
- });
24
- it('close() on a listener handle stops further notifications', () => {
25
- const s = new state_1.State(0);
26
- const cb = vi.fn();
27
- const handle = s.on('k', cb);
28
- handle.close();
29
- s.set(1);
30
- expect(cb).not.toHaveBeenCalled();
31
- });
32
- it('close() on an already-removed listener is a no-op', () => {
33
- const s = new state_1.State(0);
34
- const handle = s.on('k', vi.fn());
35
- handle.close();
36
- expect(() => handle.close()).not.toThrow();
37
- });
38
- it('forwards sub-state changes to its own listeners', () => {
39
- const sub = new state_1.State(0);
40
- const parent = new state_1.State('p', { subStates: [sub] });
41
- const cb = vi.fn();
42
- parent.on('any', cb);
43
- // emit a 'sub-state-forward' on the sub-state to trigger parent notification
44
- sub.set(1); // sub.set notifies its listeners under whatever keys are registered
45
- // The parent registered for 'sub-state-forward' on the sub; sub.set calls
46
- // notifyListeners which dispatches to all listeners regardless of key, so
47
- // the parent's forwarding listener fires and notifies parent listeners.
48
- expect(cb).toHaveBeenCalledWith('p');
49
- });
50
- it('close() runs the onDestroy callback', () => {
51
- const onDestroy = vi.fn();
52
- const s = new state_1.State(0, { onDestroy });
53
- s.close();
54
- expect(onDestroy).toHaveBeenCalledTimes(1);
55
- });
56
- it('close() detaches sub-state forwarders', () => {
57
- const sub = new state_1.State(0);
58
- const parent = new state_1.State('p', { subStates: [sub] });
59
- const cb = vi.fn();
60
- parent.on('any', cb);
61
- parent.close();
62
- sub.set(1);
63
- expect(cb).not.toHaveBeenCalled();
64
- });
65
- });
66
- //# sourceMappingURL=state.spec.js.map
@@ -1,14 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const states_1 = require("./states");
4
- describe('getDuration', () => {
5
- it('returns the milliseconds elapsed since the given start date', () => {
6
- const started = new Date(Date.now() - 250);
7
- const d = (0, states_1.getDuration)(started);
8
- expect(d).toBeGreaterThanOrEqual(250);
9
- });
10
- it('returns 0 (or very small) for a start date that is essentially now', () => {
11
- expect((0, states_1.getDuration)(new Date())).toBeGreaterThanOrEqual(0);
12
- });
13
- });
14
- //# sourceMappingURL=states.spec.js.map
@@ -1,178 +0,0 @@
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 stream_1 = require("stream");
7
- const get_file_context_1 = require("./get-file-context");
8
- describe('getFileContext', () => {
9
- let cwd;
10
- beforeEach(() => {
11
- cwd = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), 'hammerkit-fc-'));
12
- });
13
- afterEach(() => {
14
- // maxRetries handles Windows' transient ENOTEMPTY/EBUSY when a handle is
15
- // released a beat after the test ends (mirrors getFileContext.remove). The
16
- // stream tests below also await 'close' so this is only a safety net.
17
- (0, fs_1.rmSync)(cwd, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 });
18
- });
19
- describe('stats', () => {
20
- it('reports type "directory" for a directory', async () => {
21
- const fc = (0, get_file_context_1.getFileContext)(cwd);
22
- expect((await fc.stats('.')).type).toBe('directory');
23
- });
24
- it('reports type "file" with lastModified for a regular file', async () => {
25
- (0, fs_1.writeFileSync)((0, path_1.join)(cwd, 'a.txt'), 'x');
26
- const fc = (0, get_file_context_1.getFileContext)(cwd);
27
- const s = await fc.stats('a.txt');
28
- expect(s.type).toBe('file');
29
- if (s.type === 'file') {
30
- expect(s.lastModified).toBeGreaterThan(0);
31
- }
32
- });
33
- it('rejects when the path does not exist', async () => {
34
- const fc = (0, get_file_context_1.getFileContext)(cwd);
35
- await expect(fc.stats('missing')).rejects.toThrow();
36
- });
37
- it('reports type "other" for non-file non-directory entries (symlink to missing)', async () => {
38
- const link = (0, path_1.join)(cwd, 'link');
39
- try {
40
- (0, fs_1.symlinkSync)('/no/such/target', link);
41
- }
42
- catch {
43
- // symlink may not be permitted on this platform — accept either outcome
44
- return;
45
- }
46
- // statSync (used by stats()) follows symlinks; if the target is missing it errors.
47
- // That is the existing behavior; we cover the rejection path here.
48
- const fc = (0, get_file_context_1.getFileContext)(cwd);
49
- await expect(fc.stats('link')).rejects.toThrow();
50
- });
51
- });
52
- describe('exists', () => {
53
- it('resolves true for an existing path and false otherwise', async () => {
54
- const fc = (0, get_file_context_1.getFileContext)(cwd);
55
- expect(await fc.exists('.')).toBe(true);
56
- expect(await fc.exists('nope')).toBe(false);
57
- });
58
- });
59
- describe('writeFile / read / appendFile', () => {
60
- it('writes, reads, and appends file content', async () => {
61
- const fc = (0, get_file_context_1.getFileContext)(cwd);
62
- await fc.writeFile('f.txt', 'hello');
63
- expect(await fc.read('f.txt')).toBe('hello');
64
- await fc.appendFile('f.txt', ' world');
65
- expect(await fc.read('f.txt')).toBe('hello world');
66
- });
67
- it('read rejects on missing file', async () => {
68
- const fc = (0, get_file_context_1.getFileContext)(cwd);
69
- await expect(fc.read('missing.txt')).rejects.toThrow();
70
- });
71
- });
72
- describe('listFiles', () => {
73
- it('returns the entries in a directory', async () => {
74
- (0, fs_1.writeFileSync)((0, path_1.join)(cwd, 'a'), '');
75
- (0, fs_1.writeFileSync)((0, path_1.join)(cwd, 'b'), '');
76
- const fc = (0, get_file_context_1.getFileContext)(cwd);
77
- expect((await fc.listFiles('.')).sort()).toEqual(['a', 'b']);
78
- });
79
- });
80
- describe('createDirectory / remove', () => {
81
- it('creates a directory recursively and removes it', async () => {
82
- const fc = (0, get_file_context_1.getFileContext)(cwd);
83
- await fc.createDirectory((0, path_1.join)('deep', 'inner'));
84
- expect(await fc.exists((0, path_1.join)('deep', 'inner'))).toBe(true);
85
- await fc.remove('deep');
86
- expect(await fc.exists('deep')).toBe(false);
87
- });
88
- it('remove on a missing path is a no-op', async () => {
89
- const fc = (0, get_file_context_1.getFileContext)(cwd);
90
- await expect(fc.remove('nope')).resolves.toBeUndefined();
91
- });
92
- });
93
- describe('copy', () => {
94
- it('copies a file, creating the destination directory if needed', async () => {
95
- const fc = (0, get_file_context_1.getFileContext)(cwd);
96
- await fc.writeFile('src.txt', 'data');
97
- await fc.copy('src.txt', (0, path_1.join)('dest', 'src.txt'));
98
- expect((0, fs_1.readFileSync)((0, path_1.join)(cwd, 'dest', 'src.txt'), 'utf8')).toBe('data');
99
- });
100
- it('copies a directory recursively', async () => {
101
- const fc = (0, get_file_context_1.getFileContext)(cwd);
102
- (0, fs_1.mkdirSync)((0, path_1.join)(cwd, 'src', 'sub'), { recursive: true });
103
- (0, fs_1.writeFileSync)((0, path_1.join)(cwd, 'src', 'a.txt'), 'a');
104
- (0, fs_1.writeFileSync)((0, path_1.join)(cwd, 'src', 'sub', 'b.txt'), 'b');
105
- await fc.copy('src', 'dest');
106
- expect((0, fs_1.readFileSync)((0, path_1.join)(cwd, 'dest', 'a.txt'), 'utf8')).toBe('a');
107
- expect((0, fs_1.readFileSync)((0, path_1.join)(cwd, 'dest', 'sub', 'b.txt'), 'utf8')).toBe('b');
108
- });
109
- it('copy of a missing source is a no-op', async () => {
110
- const fc = (0, get_file_context_1.getFileContext)(cwd);
111
- await expect(fc.copy('nope', 'dest')).resolves.toBeUndefined();
112
- expect((0, fs_1.statSync)((0, path_1.join)(cwd))).toBeDefined(); // cwd still exists
113
- });
114
- it('copy preserves the destination directory when it already exists', async () => {
115
- const fc = (0, get_file_context_1.getFileContext)(cwd);
116
- await fc.writeFile('src.txt', 'x');
117
- (0, fs_1.mkdirSync)((0, path_1.join)(cwd, 'dest'));
118
- await fc.copy('src.txt', (0, path_1.join)('dest', 'src.txt'));
119
- expect((0, fs_1.readFileSync)((0, path_1.join)(cwd, 'dest', 'src.txt'), 'utf8')).toBe('x');
120
- });
121
- });
122
- describe('streams', () => {
123
- it('writeStream pipes a Readable into a file', async () => {
124
- const fc = (0, get_file_context_1.getFileContext)(cwd);
125
- const target = (0, path_1.join)(cwd, 'out.bin');
126
- await fc.writeStream(target, stream_1.Readable.from(['part-a', 'part-b']));
127
- expect((0, fs_1.readFileSync)(target, 'utf8')).toBe('part-apart-b');
128
- });
129
- it('createWriteStream returns a writable stream', async () => {
130
- const fc = (0, get_file_context_1.getFileContext)(cwd);
131
- const target = (0, path_1.join)(cwd, 'wstream.txt');
132
- const ws = fc.createWriteStream(target);
133
- // Wait for 'close' (fd released), not just the end/'finish' callback (data
134
- // flushed): on Windows an open write handle locks the file, so the afterEach
135
- // rmSync would race it and fail with ENOTEMPTY.
136
- await new Promise((resolve, reject) => {
137
- ws.on('error', reject);
138
- ws.on('close', () => resolve());
139
- ws.end('payload');
140
- });
141
- expect((0, fs_1.readFileSync)(target, 'utf8')).toBe('payload');
142
- });
143
- it('readStream reads back what writeStream wrote', async () => {
144
- const fc = (0, get_file_context_1.getFileContext)(cwd);
145
- const target = (0, path_1.join)(cwd, 'r.txt');
146
- (0, fs_1.writeFileSync)(target, 'abc');
147
- const chunks = [];
148
- await new Promise((resolve, reject) => {
149
- const rs = fc.readStream(target);
150
- rs.on('data', (c) => chunks.push(c));
151
- rs.on('error', reject);
152
- // 'close' (fd released) fires after 'end'; wait for it so an open read
153
- // handle can't block the afterEach cleanup on Windows.
154
- rs.on('close', resolve);
155
- });
156
- expect(Buffer.concat(chunks).toString()).toBe('abc');
157
- });
158
- });
159
- describe('watch', () => {
160
- it('returns a handle whose close() is callable; the polling override is honored', async () => {
161
- const previous = process.env.HAMMERKIT_WATCH_POLLING;
162
- process.env.HAMMERKIT_WATCH_POLLING = 'false';
163
- try {
164
- const fc = (0, get_file_context_1.getFileContext)(cwd);
165
- const handle = fc.watch('.', () => undefined);
166
- // close() returns a Promise on chokidar; await it
167
- await Promise.resolve(handle.close());
168
- }
169
- finally {
170
- if (previous === undefined)
171
- delete process.env.HAMMERKIT_WATCH_POLLING;
172
- else
173
- process.env.HAMMERKIT_WATCH_POLLING = previous;
174
- }
175
- });
176
- });
177
- });
178
- //# sourceMappingURL=get-file-context.spec.js.map
@@ -1,48 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const move_files_1 = require("./move-files");
4
- function context(existing) {
5
- const remove = vi.fn(async () => undefined);
6
- const copy = vi.fn(async () => undefined);
7
- const env = {
8
- file: {
9
- exists: vi.fn(async (p) => existing.includes(p)),
10
- remove,
11
- copy,
12
- },
13
- };
14
- return { env, remove, copy };
15
- }
16
- const item = { status: { write: vi.fn() } };
17
- function* once(from, to) {
18
- yield { from, to };
19
- }
20
- describe('move-files', () => {
21
- it('copies an existing folder and removes a pre-existing target first', async () => {
22
- const { env, remove, copy } = context(['/a', '/out/a']);
23
- await (0, move_files_1.moveFiles)(item, env, () => once('/a', '/out/a'));
24
- expect(remove).toHaveBeenCalledWith('/out/a');
25
- expect(copy).toHaveBeenCalledWith('/a', '/out/a');
26
- });
27
- it('does not remove the target when it does not exist yet', async () => {
28
- const { env, remove, copy } = context(['/a']);
29
- await (0, move_files_1.moveFiles)(item, env, () => once('/a', '/out/a'));
30
- expect(remove).not.toHaveBeenCalled();
31
- expect(copy).toHaveBeenCalledWith('/a', '/out/a');
32
- });
33
- it('skips folders whose source does not exist', async () => {
34
- const { env, copy } = context([]);
35
- await (0, move_files_1.moveFiles)(item, env, () => once('/missing', '/out/m'));
36
- expect(copy).not.toHaveBeenCalled();
37
- });
38
- it('dedupes identical from/to pairs', async () => {
39
- const { env, copy } = context(['/a']);
40
- function* twice() {
41
- yield { from: '/a', to: '/out/a' };
42
- yield { from: '/a', to: '/out/a' };
43
- }
44
- await (0, move_files_1.moveFiles)(item, env, twice);
45
- expect(copy).toHaveBeenCalledTimes(1);
46
- });
47
- });
48
- //# sourceMappingURL=move-files.spec.js.map