@elaraai/e3-core 0.0.2-beta.8 → 1.0.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 (285) hide show
  1. package/LICENSE.md +4 -0
  2. package/README.md +74 -35
  3. package/dist/src/dataflow/api-compat.d.ts +90 -0
  4. package/dist/src/dataflow/api-compat.d.ts.map +1 -0
  5. package/dist/src/dataflow/api-compat.js +139 -0
  6. package/dist/src/dataflow/api-compat.js.map +1 -0
  7. package/dist/src/dataflow/api-compat.spec.d.ts +6 -0
  8. package/dist/src/dataflow/api-compat.spec.d.ts.map +1 -0
  9. package/dist/src/dataflow/api-compat.spec.js +182 -0
  10. package/dist/src/dataflow/api-compat.spec.js.map +1 -0
  11. package/dist/src/dataflow/index.d.ts +18 -0
  12. package/dist/src/dataflow/index.d.ts.map +1 -0
  13. package/dist/src/dataflow/index.js +23 -0
  14. package/dist/src/dataflow/index.js.map +1 -0
  15. package/dist/src/dataflow/orchestrator/LocalOrchestrator.d.ts +76 -0
  16. package/dist/src/dataflow/orchestrator/LocalOrchestrator.d.ts.map +1 -0
  17. package/dist/src/dataflow/orchestrator/LocalOrchestrator.js +729 -0
  18. package/dist/src/dataflow/orchestrator/LocalOrchestrator.js.map +1 -0
  19. package/dist/src/dataflow/orchestrator/index.d.ts +12 -0
  20. package/dist/src/dataflow/orchestrator/index.d.ts.map +1 -0
  21. package/dist/src/dataflow/orchestrator/index.js +12 -0
  22. package/dist/src/dataflow/orchestrator/index.js.map +1 -0
  23. package/dist/src/dataflow/orchestrator/interfaces.d.ts +163 -0
  24. package/dist/src/dataflow/orchestrator/interfaces.d.ts.map +1 -0
  25. package/dist/src/dataflow/orchestrator/interfaces.js +52 -0
  26. package/dist/src/dataflow/orchestrator/interfaces.js.map +1 -0
  27. package/dist/src/dataflow/state-store/FileStateStore.d.ts +67 -0
  28. package/dist/src/dataflow/state-store/FileStateStore.d.ts.map +1 -0
  29. package/dist/src/dataflow/state-store/FileStateStore.js +300 -0
  30. package/dist/src/dataflow/state-store/FileStateStore.js.map +1 -0
  31. package/dist/src/dataflow/state-store/InMemoryStateStore.d.ts +42 -0
  32. package/dist/src/dataflow/state-store/InMemoryStateStore.d.ts.map +1 -0
  33. package/dist/src/dataflow/state-store/InMemoryStateStore.js +229 -0
  34. package/dist/src/dataflow/state-store/InMemoryStateStore.js.map +1 -0
  35. package/dist/src/dataflow/state-store/InMemoryStateStore.spec.d.ts +6 -0
  36. package/dist/src/dataflow/state-store/InMemoryStateStore.spec.d.ts.map +1 -0
  37. package/dist/src/dataflow/state-store/InMemoryStateStore.spec.js +114 -0
  38. package/dist/src/dataflow/state-store/InMemoryStateStore.spec.js.map +1 -0
  39. package/dist/src/dataflow/state-store/index.d.ts +13 -0
  40. package/dist/src/dataflow/state-store/index.d.ts.map +1 -0
  41. package/dist/src/dataflow/state-store/index.js +13 -0
  42. package/dist/src/dataflow/state-store/index.js.map +1 -0
  43. package/dist/src/dataflow/state-store/interfaces.d.ts +159 -0
  44. package/dist/src/dataflow/state-store/interfaces.d.ts.map +1 -0
  45. package/dist/src/dataflow/state-store/interfaces.js +6 -0
  46. package/dist/src/dataflow/state-store/interfaces.js.map +1 -0
  47. package/dist/src/dataflow/steps.d.ts +222 -0
  48. package/dist/src/dataflow/steps.d.ts.map +1 -0
  49. package/dist/src/dataflow/steps.js +707 -0
  50. package/dist/src/dataflow/steps.js.map +1 -0
  51. package/dist/src/dataflow/steps.spec.d.ts +6 -0
  52. package/dist/src/dataflow/steps.spec.d.ts.map +1 -0
  53. package/dist/src/dataflow/steps.spec.js +343 -0
  54. package/dist/src/dataflow/steps.spec.js.map +1 -0
  55. package/dist/src/dataflow/types.d.ts +127 -0
  56. package/dist/src/dataflow/types.d.ts.map +1 -0
  57. package/dist/src/dataflow/types.js +7 -0
  58. package/dist/src/dataflow/types.js.map +1 -0
  59. package/dist/src/dataflow-orchestration.spec.d.ts +6 -0
  60. package/dist/src/dataflow-orchestration.spec.d.ts.map +1 -0
  61. package/dist/src/dataflow-orchestration.spec.js +1025 -0
  62. package/dist/src/dataflow-orchestration.spec.js.map +1 -0
  63. package/dist/src/dataflow.d.ts +113 -38
  64. package/dist/src/dataflow.d.ts.map +1 -1
  65. package/dist/src/dataflow.js +269 -416
  66. package/dist/src/dataflow.js.map +1 -1
  67. package/dist/src/dataflow.spec.d.ts +6 -0
  68. package/dist/src/dataflow.spec.d.ts.map +1 -0
  69. package/dist/src/dataflow.spec.js +663 -0
  70. package/dist/src/dataflow.spec.js.map +1 -0
  71. package/dist/src/dataset-refs.d.ts +124 -0
  72. package/dist/src/dataset-refs.d.ts.map +1 -0
  73. package/dist/src/dataset-refs.js +319 -0
  74. package/dist/src/dataset-refs.js.map +1 -0
  75. package/dist/src/errors.d.ts +39 -9
  76. package/dist/src/errors.d.ts.map +1 -1
  77. package/dist/src/errors.js +51 -8
  78. package/dist/src/errors.js.map +1 -1
  79. package/dist/src/errors.spec.d.ts +6 -0
  80. package/dist/src/errors.spec.d.ts.map +1 -0
  81. package/dist/src/errors.spec.js +276 -0
  82. package/dist/src/errors.spec.js.map +1 -0
  83. package/dist/src/execution/LocalTaskRunner.d.ts +73 -0
  84. package/dist/src/execution/LocalTaskRunner.d.ts.map +1 -0
  85. package/dist/src/execution/LocalTaskRunner.js +399 -0
  86. package/dist/src/execution/LocalTaskRunner.js.map +1 -0
  87. package/dist/src/execution/MockTaskRunner.d.ts +49 -0
  88. package/dist/src/execution/MockTaskRunner.d.ts.map +1 -0
  89. package/dist/src/execution/MockTaskRunner.js +54 -0
  90. package/dist/src/execution/MockTaskRunner.js.map +1 -0
  91. package/dist/src/execution/index.d.ts +16 -0
  92. package/dist/src/execution/index.d.ts.map +1 -0
  93. package/dist/src/execution/index.js +8 -0
  94. package/dist/src/execution/index.js.map +1 -0
  95. package/dist/src/execution/interfaces.d.ts +246 -0
  96. package/dist/src/execution/interfaces.d.ts.map +1 -0
  97. package/dist/src/execution/interfaces.js +6 -0
  98. package/dist/src/execution/interfaces.js.map +1 -0
  99. package/dist/src/execution/processHelpers.d.ts +20 -0
  100. package/dist/src/execution/processHelpers.d.ts.map +1 -0
  101. package/dist/src/execution/processHelpers.js +62 -0
  102. package/dist/src/execution/processHelpers.js.map +1 -0
  103. package/dist/src/executions.d.ts +71 -104
  104. package/dist/src/executions.d.ts.map +1 -1
  105. package/dist/src/executions.js +113 -481
  106. package/dist/src/executions.js.map +1 -1
  107. package/dist/src/executions.spec.d.ts +6 -0
  108. package/dist/src/executions.spec.d.ts.map +1 -0
  109. package/dist/src/executions.spec.js +387 -0
  110. package/dist/src/executions.spec.js.map +1 -0
  111. package/dist/src/formats.d.ts +18 -2
  112. package/dist/src/formats.d.ts.map +1 -1
  113. package/dist/src/formats.js +34 -2
  114. package/dist/src/formats.js.map +1 -1
  115. package/dist/src/gc.spec.d.ts +6 -0
  116. package/dist/src/gc.spec.d.ts.map +1 -0
  117. package/dist/src/gc.spec.js +512 -0
  118. package/dist/src/gc.spec.js.map +1 -0
  119. package/dist/src/index.d.ts +20 -10
  120. package/dist/src/index.d.ts.map +1 -1
  121. package/dist/src/index.js +48 -18
  122. package/dist/src/index.js.map +1 -1
  123. package/dist/src/objects.d.ts +7 -53
  124. package/dist/src/objects.d.ts.map +1 -1
  125. package/dist/src/objects.js +13 -232
  126. package/dist/src/objects.js.map +1 -1
  127. package/dist/src/objects.spec.d.ts +6 -0
  128. package/dist/src/objects.spec.d.ts.map +1 -0
  129. package/dist/src/objects.spec.js +247 -0
  130. package/dist/src/objects.spec.js.map +1 -0
  131. package/dist/src/packages.d.ts +41 -14
  132. package/dist/src/packages.d.ts.map +1 -1
  133. package/dist/src/packages.js +151 -89
  134. package/dist/src/packages.js.map +1 -1
  135. package/dist/src/packages.spec.d.ts +6 -0
  136. package/dist/src/packages.spec.d.ts.map +1 -0
  137. package/dist/src/packages.spec.js +324 -0
  138. package/dist/src/packages.spec.js.map +1 -0
  139. package/dist/src/storage/in-memory/InMemoryRepoStore.d.ts +35 -0
  140. package/dist/src/storage/in-memory/InMemoryRepoStore.d.ts.map +1 -0
  141. package/dist/src/storage/in-memory/InMemoryRepoStore.js +107 -0
  142. package/dist/src/storage/in-memory/InMemoryRepoStore.js.map +1 -0
  143. package/dist/src/storage/in-memory/InMemoryRepoStore.spec.d.ts +6 -0
  144. package/dist/src/storage/in-memory/InMemoryRepoStore.spec.d.ts.map +1 -0
  145. package/dist/src/storage/in-memory/InMemoryRepoStore.spec.js +187 -0
  146. package/dist/src/storage/in-memory/InMemoryRepoStore.spec.js.map +1 -0
  147. package/dist/src/storage/in-memory/InMemoryStorage.d.ts +139 -0
  148. package/dist/src/storage/in-memory/InMemoryStorage.d.ts.map +1 -0
  149. package/dist/src/storage/in-memory/InMemoryStorage.js +439 -0
  150. package/dist/src/storage/in-memory/InMemoryStorage.js.map +1 -0
  151. package/dist/src/storage/in-memory/index.d.ts +12 -0
  152. package/dist/src/storage/in-memory/index.d.ts.map +1 -0
  153. package/dist/src/storage/in-memory/index.js +12 -0
  154. package/dist/src/storage/in-memory/index.js.map +1 -0
  155. package/dist/src/storage/index.d.ts +18 -0
  156. package/dist/src/storage/index.d.ts.map +1 -0
  157. package/dist/src/storage/index.js +10 -0
  158. package/dist/src/storage/index.js.map +1 -0
  159. package/dist/src/storage/interfaces.d.ts +581 -0
  160. package/dist/src/storage/interfaces.d.ts.map +1 -0
  161. package/dist/src/storage/interfaces.js +6 -0
  162. package/dist/src/storage/interfaces.js.map +1 -0
  163. package/dist/src/storage/local/LocalBackend.d.ts +56 -0
  164. package/dist/src/storage/local/LocalBackend.d.ts.map +1 -0
  165. package/dist/src/storage/local/LocalBackend.js +145 -0
  166. package/dist/src/storage/local/LocalBackend.js.map +1 -0
  167. package/dist/src/storage/local/LocalDatasetRefStore.d.ts +22 -0
  168. package/dist/src/storage/local/LocalDatasetRefStore.d.ts.map +1 -0
  169. package/dist/src/storage/local/LocalDatasetRefStore.js +118 -0
  170. package/dist/src/storage/local/LocalDatasetRefStore.js.map +1 -0
  171. package/dist/src/storage/local/LocalLockService.d.ts +111 -0
  172. package/dist/src/storage/local/LocalLockService.d.ts.map +1 -0
  173. package/dist/src/storage/local/LocalLockService.js +364 -0
  174. package/dist/src/storage/local/LocalLockService.js.map +1 -0
  175. package/dist/src/storage/local/LocalLockService.spec.d.ts +6 -0
  176. package/dist/src/storage/local/LocalLockService.spec.d.ts.map +1 -0
  177. package/dist/src/storage/local/LocalLockService.spec.js +148 -0
  178. package/dist/src/storage/local/LocalLockService.spec.js.map +1 -0
  179. package/dist/src/storage/local/LocalLogStore.d.ts +23 -0
  180. package/dist/src/storage/local/LocalLogStore.d.ts.map +1 -0
  181. package/dist/src/storage/local/LocalLogStore.js +66 -0
  182. package/dist/src/storage/local/LocalLogStore.js.map +1 -0
  183. package/dist/src/storage/local/LocalObjectStore.d.ts +55 -0
  184. package/dist/src/storage/local/LocalObjectStore.d.ts.map +1 -0
  185. package/dist/src/storage/local/LocalObjectStore.js +300 -0
  186. package/dist/src/storage/local/LocalObjectStore.js.map +1 -0
  187. package/dist/src/storage/local/LocalRefStore.d.ts +50 -0
  188. package/dist/src/storage/local/LocalRefStore.d.ts.map +1 -0
  189. package/dist/src/storage/local/LocalRefStore.js +337 -0
  190. package/dist/src/storage/local/LocalRefStore.js.map +1 -0
  191. package/dist/src/storage/local/LocalRepoStore.d.ts +55 -0
  192. package/dist/src/storage/local/LocalRepoStore.d.ts.map +1 -0
  193. package/dist/src/storage/local/LocalRepoStore.js +365 -0
  194. package/dist/src/storage/local/LocalRepoStore.js.map +1 -0
  195. package/dist/src/storage/local/LocalRepoStore.spec.d.ts +6 -0
  196. package/dist/src/storage/local/LocalRepoStore.spec.d.ts.map +1 -0
  197. package/dist/src/storage/local/LocalRepoStore.spec.js +255 -0
  198. package/dist/src/storage/local/LocalRepoStore.spec.js.map +1 -0
  199. package/dist/src/storage/local/gc.d.ts +92 -0
  200. package/dist/src/storage/local/gc.d.ts.map +1 -0
  201. package/dist/src/storage/local/gc.js +377 -0
  202. package/dist/src/storage/local/gc.js.map +1 -0
  203. package/dist/src/storage/local/index.d.ts +18 -0
  204. package/dist/src/storage/local/index.d.ts.map +1 -0
  205. package/dist/src/storage/local/index.js +18 -0
  206. package/dist/src/storage/local/index.js.map +1 -0
  207. package/dist/src/storage/local/localHelpers.d.ts +25 -0
  208. package/dist/src/storage/local/localHelpers.d.ts.map +1 -0
  209. package/dist/src/storage/local/localHelpers.js +69 -0
  210. package/dist/src/storage/local/localHelpers.js.map +1 -0
  211. package/dist/src/{repository.d.ts → storage/local/repository.d.ts} +8 -4
  212. package/dist/src/storage/local/repository.d.ts.map +1 -0
  213. package/dist/src/{repository.js → storage/local/repository.js} +31 -29
  214. package/dist/src/storage/local/repository.js.map +1 -0
  215. package/dist/src/storage/local/repository.spec.d.ts +6 -0
  216. package/dist/src/storage/local/repository.spec.d.ts.map +1 -0
  217. package/dist/src/storage/local/repository.spec.js +186 -0
  218. package/dist/src/storage/local/repository.spec.js.map +1 -0
  219. package/dist/src/tasks.d.ts +16 -10
  220. package/dist/src/tasks.d.ts.map +1 -1
  221. package/dist/src/tasks.js +35 -41
  222. package/dist/src/tasks.js.map +1 -1
  223. package/dist/src/tasks.spec.d.ts +6 -0
  224. package/dist/src/tasks.spec.d.ts.map +1 -0
  225. package/dist/src/tasks.spec.js +105 -0
  226. package/dist/src/tasks.spec.js.map +1 -0
  227. package/dist/src/test-helpers.d.ts +5 -4
  228. package/dist/src/test-helpers.d.ts.map +1 -1
  229. package/dist/src/test-helpers.js +9 -21
  230. package/dist/src/test-helpers.js.map +1 -1
  231. package/dist/src/transfer/InMemoryTransferBackend.d.ts +75 -0
  232. package/dist/src/transfer/InMemoryTransferBackend.d.ts.map +1 -0
  233. package/dist/src/transfer/InMemoryTransferBackend.js +211 -0
  234. package/dist/src/transfer/InMemoryTransferBackend.js.map +1 -0
  235. package/dist/src/transfer/index.d.ts +9 -0
  236. package/dist/src/transfer/index.d.ts.map +1 -0
  237. package/dist/src/transfer/index.js +11 -0
  238. package/dist/src/transfer/index.js.map +1 -0
  239. package/dist/src/transfer/interfaces.d.ts +103 -0
  240. package/dist/src/transfer/interfaces.d.ts.map +1 -0
  241. package/dist/src/transfer/interfaces.js +6 -0
  242. package/dist/src/transfer/interfaces.js.map +1 -0
  243. package/dist/src/transfer/process.d.ts +55 -0
  244. package/dist/src/transfer/process.d.ts.map +1 -0
  245. package/dist/src/transfer/process.js +144 -0
  246. package/dist/src/transfer/process.js.map +1 -0
  247. package/dist/src/transfer/types.d.ts +106 -0
  248. package/dist/src/transfer/types.d.ts.map +1 -0
  249. package/dist/src/transfer/types.js +61 -0
  250. package/dist/src/transfer/types.js.map +1 -0
  251. package/dist/src/trees.d.ts +102 -63
  252. package/dist/src/trees.d.ts.map +1 -1
  253. package/dist/src/trees.js +319 -479
  254. package/dist/src/trees.js.map +1 -1
  255. package/dist/src/trees.spec.d.ts +6 -0
  256. package/dist/src/trees.spec.d.ts.map +1 -0
  257. package/dist/src/trees.spec.js +635 -0
  258. package/dist/src/trees.spec.js.map +1 -0
  259. package/dist/src/uuid.d.ts +26 -0
  260. package/dist/src/uuid.d.ts.map +1 -0
  261. package/dist/src/uuid.js +80 -0
  262. package/dist/src/uuid.js.map +1 -0
  263. package/dist/src/workspaceStatus.d.ts +6 -4
  264. package/dist/src/workspaceStatus.d.ts.map +1 -1
  265. package/dist/src/workspaceStatus.js +46 -60
  266. package/dist/src/workspaceStatus.js.map +1 -1
  267. package/dist/src/workspaces.d.ts +46 -47
  268. package/dist/src/workspaces.d.ts.map +1 -1
  269. package/dist/src/workspaces.js +281 -221
  270. package/dist/src/workspaces.js.map +1 -1
  271. package/dist/src/workspaces.spec.d.ts +6 -0
  272. package/dist/src/workspaces.spec.d.ts.map +1 -0
  273. package/dist/src/workspaces.spec.js +273 -0
  274. package/dist/src/workspaces.spec.js.map +1 -0
  275. package/package.json +15 -15
  276. package/dist/src/gc.d.ts +0 -54
  277. package/dist/src/gc.d.ts.map +0 -1
  278. package/dist/src/gc.js +0 -233
  279. package/dist/src/gc.js.map +0 -1
  280. package/dist/src/repository.d.ts.map +0 -1
  281. package/dist/src/repository.js.map +0 -1
  282. package/dist/src/workspaceLock.d.ts +0 -67
  283. package/dist/src/workspaceLock.d.ts.map +0 -1
  284. package/dist/src/workspaceLock.js +0 -217
  285. package/dist/src/workspaceLock.js.map +0 -1
@@ -0,0 +1,365 @@
1
+ /**
2
+ * Copyright (c) 2025 Elara AI Pty Ltd
3
+ * Licensed under BSL 1.1. See LICENSE for details.
4
+ */
5
+ import * as fs from 'fs/promises';
6
+ import * as path from 'path';
7
+ import { RepoNotFoundError, RepoAlreadyExistsError, RepoStatusConflictError, } from '../../errors.js';
8
+ import { decodeBeast2For } from '@elaraai/east';
9
+ import { WorkspaceStateType } from '@elaraai/e3-types';
10
+ const METADATA_FILENAME = '.e3-metadata.json';
11
+ /**
12
+ * Local filesystem implementation of RepoStore.
13
+ *
14
+ * Manages repository lifecycle for local e3 repositories stored
15
+ * as subdirectories within a parent directory.
16
+ */
17
+ export class LocalRepoStore {
18
+ reposDir;
19
+ refs;
20
+ datasets;
21
+ /**
22
+ * Create a new LocalRepoStore.
23
+ * @param reposDir - Parent directory containing repositories
24
+ * @param refs - RefStore for reading package/workspace/execution refs
25
+ * @param datasets - DatasetRefStore for reading per-dataset refs (for GC scanning)
26
+ */
27
+ constructor(reposDir, refs, datasets) {
28
+ this.reposDir = reposDir;
29
+ this.refs = refs;
30
+ this.datasets = datasets;
31
+ }
32
+ /**
33
+ * Get the path to a repository directory.
34
+ */
35
+ getRepoPath(repo) {
36
+ return path.join(this.reposDir, repo);
37
+ }
38
+ /**
39
+ * Get the path to a repository's metadata file.
40
+ */
41
+ getMetadataPath(repo) {
42
+ return path.join(this.getRepoPath(repo), METADATA_FILENAME);
43
+ }
44
+ /**
45
+ * Check if a directory is a valid e3 repository.
46
+ */
47
+ async isValidRepository(repoPath) {
48
+ const requiredDirs = ['objects', 'packages', 'executions', 'workspaces'];
49
+ for (const dir of requiredDirs) {
50
+ try {
51
+ const stat = await fs.stat(path.join(repoPath, dir));
52
+ if (!stat.isDirectory()) {
53
+ return false;
54
+ }
55
+ }
56
+ catch {
57
+ return false;
58
+ }
59
+ }
60
+ return true;
61
+ }
62
+ // ===========================================================================
63
+ // Queries
64
+ // ===========================================================================
65
+ async list() {
66
+ const repos = [];
67
+ try {
68
+ const entries = await fs.readdir(this.reposDir, { withFileTypes: true });
69
+ for (const entry of entries) {
70
+ if (entry.isDirectory()) {
71
+ const repoPath = path.join(this.reposDir, entry.name);
72
+ if (await this.isValidRepository(repoPath)) {
73
+ repos.push(entry.name);
74
+ }
75
+ }
76
+ }
77
+ }
78
+ catch {
79
+ // reposDir doesn't exist or can't be read
80
+ }
81
+ return repos;
82
+ }
83
+ async exists(repo) {
84
+ const repoPath = this.getRepoPath(repo);
85
+ return this.isValidRepository(repoPath);
86
+ }
87
+ async getMetadata(repo) {
88
+ const repoPath = this.getRepoPath(repo);
89
+ // Check if repo exists
90
+ if (!(await this.isValidRepository(repoPath))) {
91
+ return null;
92
+ }
93
+ const metadataPath = this.getMetadataPath(repo);
94
+ try {
95
+ const content = await fs.readFile(metadataPath, 'utf-8');
96
+ const metadata = JSON.parse(content);
97
+ return {
98
+ name: metadata.name,
99
+ status: metadata.status,
100
+ createdAt: metadata.createdAt,
101
+ statusChangedAt: metadata.statusChangedAt,
102
+ };
103
+ }
104
+ catch {
105
+ // No metadata file - synthesize for legacy repos
106
+ // Get mtime from repo directory for createdAt
107
+ try {
108
+ const stat = await fs.stat(repoPath);
109
+ const createdAt = stat.birthtime.toISOString();
110
+ return {
111
+ name: repo,
112
+ status: 'active',
113
+ createdAt,
114
+ statusChangedAt: createdAt,
115
+ };
116
+ }
117
+ catch {
118
+ return null;
119
+ }
120
+ }
121
+ }
122
+ // ===========================================================================
123
+ // Lifecycle
124
+ // ===========================================================================
125
+ async create(repo) {
126
+ const repoPath = this.getRepoPath(repo);
127
+ // Check if already exists
128
+ if (await this.isValidRepository(repoPath)) {
129
+ throw new RepoAlreadyExistsError(repo);
130
+ }
131
+ // Create directory structure
132
+ await fs.mkdir(repoPath, { recursive: true });
133
+ await fs.mkdir(path.join(repoPath, 'objects'), { recursive: true });
134
+ await fs.mkdir(path.join(repoPath, 'packages'), { recursive: true });
135
+ await fs.mkdir(path.join(repoPath, 'executions'), { recursive: true });
136
+ await fs.mkdir(path.join(repoPath, 'workspaces'), { recursive: true });
137
+ // Write metadata file
138
+ const now = new Date().toISOString();
139
+ const metadata = {
140
+ name: repo,
141
+ status: 'active',
142
+ createdAt: now,
143
+ statusChangedAt: now,
144
+ };
145
+ await fs.writeFile(this.getMetadataPath(repo), JSON.stringify(metadata, null, 2));
146
+ }
147
+ async setStatus(repo, status, expected) {
148
+ const current = await this.getMetadata(repo);
149
+ if (!current) {
150
+ throw new RepoNotFoundError(repo);
151
+ }
152
+ // Check expected status (CAS)
153
+ if (expected !== undefined) {
154
+ const expectedArray = Array.isArray(expected) ? expected : [expected];
155
+ if (!expectedArray.includes(current.status)) {
156
+ throw new RepoStatusConflictError(repo, expected, current.status);
157
+ }
158
+ }
159
+ // Update metadata
160
+ const now = new Date().toISOString();
161
+ const metadata = {
162
+ name: current.name,
163
+ status,
164
+ createdAt: current.createdAt,
165
+ statusChangedAt: now,
166
+ };
167
+ // Atomic write using rename
168
+ const metadataPath = this.getMetadataPath(repo);
169
+ const tempPath = `${metadataPath}.tmp`;
170
+ await fs.writeFile(tempPath, JSON.stringify(metadata, null, 2));
171
+ await fs.rename(tempPath, metadataPath);
172
+ }
173
+ async remove(repo) {
174
+ const repoPath = this.getRepoPath(repo);
175
+ try {
176
+ // Remove the entire repository directory
177
+ await fs.rm(repoPath, { recursive: true, force: true });
178
+ }
179
+ catch {
180
+ // Ignore errors if directory doesn't exist
181
+ }
182
+ }
183
+ // ===========================================================================
184
+ // Batched Deletion
185
+ // ===========================================================================
186
+ async deleteRefsBatch(repo, _cursor) {
187
+ const repoPath = this.getRepoPath(repo);
188
+ let deleted = 0;
189
+ // For local storage, we delete all refs in one pass
190
+ // (packages/, workspaces/, executions/, locks/)
191
+ const refDirs = ['packages', 'workspaces', 'executions', 'locks'];
192
+ for (const dir of refDirs) {
193
+ const dirPath = path.join(repoPath, dir);
194
+ try {
195
+ deleted += await this.deleteDirectoryContents(dirPath);
196
+ }
197
+ catch {
198
+ // Directory doesn't exist
199
+ }
200
+ }
201
+ return { status: 'done', deleted };
202
+ }
203
+ async deleteObjectsBatch(repo, _cursor) {
204
+ const repoPath = this.getRepoPath(repo);
205
+ const objectsDir = path.join(repoPath, 'objects');
206
+ let deleted = 0;
207
+ try {
208
+ deleted = await this.deleteDirectoryContents(objectsDir);
209
+ }
210
+ catch {
211
+ // objects dir doesn't exist
212
+ }
213
+ return { status: 'done', deleted };
214
+ }
215
+ /**
216
+ * Recursively delete all contents of a directory.
217
+ * Returns count of files deleted.
218
+ */
219
+ async deleteDirectoryContents(dir) {
220
+ let count = 0;
221
+ try {
222
+ const entries = await fs.readdir(dir, { withFileTypes: true });
223
+ for (const entry of entries) {
224
+ const entryPath = path.join(dir, entry.name);
225
+ if (entry.isDirectory()) {
226
+ count += await this.deleteDirectoryContents(entryPath);
227
+ await fs.rmdir(entryPath);
228
+ }
229
+ else {
230
+ await fs.unlink(entryPath);
231
+ count++;
232
+ }
233
+ }
234
+ }
235
+ catch {
236
+ // Directory doesn't exist or can't be read
237
+ }
238
+ return count;
239
+ }
240
+ // ===========================================================================
241
+ // GC Primitives
242
+ // ===========================================================================
243
+ // Note: GC primitives receive `repo` as a full path (same as ObjectStore/RefStore),
244
+ // NOT as a repo name relative to reposDir. This is consistent with how all
245
+ // storage interfaces work in the local implementation.
246
+ async gcScanPackageRoots(repo, _cursor) {
247
+ const roots = [];
248
+ const packages = await this.refs.packageList(repo);
249
+ for (const { name, version } of packages) {
250
+ const hash = await this.refs.packageResolve(repo, name, version);
251
+ if (hash) {
252
+ roots.push(hash);
253
+ }
254
+ }
255
+ return { roots };
256
+ }
257
+ async gcScanWorkspaceRoots(repo, _cursor) {
258
+ const roots = [];
259
+ const decoder = decodeBeast2For(WorkspaceStateType);
260
+ const names = await this.refs.workspaceList(repo);
261
+ for (const name of names) {
262
+ const data = await this.refs.workspaceRead(repo, name);
263
+ if (!data || data.length === 0)
264
+ continue;
265
+ try {
266
+ const state = decoder(data);
267
+ roots.push(state.packageHash);
268
+ // Scan per-dataset ref files for value hashes
269
+ if (this.datasets) {
270
+ const refPaths = await this.datasets.list(repo, name);
271
+ for (const refPath of refPaths) {
272
+ const ref = await this.datasets.read(repo, name, refPath);
273
+ if (ref && ref.type === 'value') {
274
+ roots.push(ref.value.hash);
275
+ }
276
+ }
277
+ }
278
+ }
279
+ catch {
280
+ // Corrupt workspace state - skip
281
+ }
282
+ }
283
+ return { roots };
284
+ }
285
+ async gcScanExecutionRoots(repo, _cursor) {
286
+ const roots = [];
287
+ const entries = await this.refs.executionList(repo);
288
+ for (const { taskHash, inputsHash } of entries) {
289
+ const ids = await this.refs.executionListIds(repo, taskHash, inputsHash);
290
+ for (const executionId of ids) {
291
+ const status = await this.refs.executionGet(repo, taskHash, inputsHash, executionId);
292
+ if (!status)
293
+ continue;
294
+ // ExecutionStatus is a variant; extract outputHash from success
295
+ const raw = status;
296
+ if (raw.type === 'success' && raw.value.outputHash && /^[a-f0-9]{64}$/.test(raw.value.outputHash)) {
297
+ roots.push(raw.value.outputHash);
298
+ }
299
+ }
300
+ }
301
+ return { roots };
302
+ }
303
+ async gcScanObjects(repo, _cursor) {
304
+ const objectsDir = path.join(repo, 'objects');
305
+ const objects = [];
306
+ try {
307
+ const subdirs = await fs.readdir(objectsDir);
308
+ for (const subdir of subdirs) {
309
+ if (!/^[a-f0-9]{2}$/.test(subdir))
310
+ continue;
311
+ const subdirPath = path.join(objectsDir, subdir);
312
+ try {
313
+ const stat = await fs.stat(subdirPath);
314
+ if (!stat.isDirectory())
315
+ continue;
316
+ }
317
+ catch {
318
+ continue;
319
+ }
320
+ const files = await fs.readdir(subdirPath);
321
+ for (const file of files) {
322
+ if (file.endsWith('.partial'))
323
+ continue;
324
+ if (!file.endsWith('.beast2'))
325
+ continue;
326
+ const hash = subdir + file.slice(0, -7); // remove .beast2
327
+ try {
328
+ const fileStat = await fs.stat(path.join(subdirPath, file));
329
+ objects.push({ hash, lastModified: fileStat.mtimeMs, size: fileStat.size });
330
+ }
331
+ catch {
332
+ // Skip files we can't stat
333
+ }
334
+ }
335
+ }
336
+ }
337
+ catch {
338
+ // Objects directory doesn't exist
339
+ }
340
+ // Local returns all in one batch (no cursor)
341
+ return { objects };
342
+ }
343
+ async gcDeleteObjects(repo, hashes) {
344
+ const objectsDir = path.join(repo, 'objects');
345
+ for (const hash of hashes) {
346
+ const subdir = hash.slice(0, 2);
347
+ const rest = hash.slice(2);
348
+ const filePath = path.join(objectsDir, subdir, `${rest}.beast2`);
349
+ try {
350
+ await fs.unlink(filePath);
351
+ }
352
+ catch {
353
+ // File doesn't exist
354
+ }
355
+ // Try to remove empty subdirectory
356
+ try {
357
+ await fs.rmdir(path.join(objectsDir, subdir));
358
+ }
359
+ catch {
360
+ // Directory not empty or doesn't exist
361
+ }
362
+ }
363
+ }
364
+ }
365
+ //# sourceMappingURL=LocalRepoStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LocalRepoStore.js","sourceRoot":"","sources":["../../../../src/storage/local/LocalRepoStore.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,MAAM,aAAa,CAAC;AAClC,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAW7B,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAYvD,MAAM,iBAAiB,GAAG,mBAAmB,CAAC;AAE9C;;;;;GAKG;AACH,MAAM,OAAO,cAAc;IAQN;IACA;IACA;IATnB;;;;;OAKG;IACH,YACmB,QAAgB,EAChB,IAAc,EACd,QAA0B;QAF1B,aAAQ,GAAR,QAAQ,CAAQ;QAChB,SAAI,GAAJ,IAAI,CAAU;QACd,aAAQ,GAAR,QAAQ,CAAkB;IAC1C,CAAC;IAEJ;;OAEG;IACK,WAAW,CAAC,IAAY;QAC9B,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IACxC,CAAC;IAED;;OAEG;IACK,eAAe,CAAC,IAAY;QAClC,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,iBAAiB,CAAC,CAAC;IAC9D,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,iBAAiB,CAAC,QAAgB;QAC9C,MAAM,YAAY,GAAG,CAAC,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC;QACzE,KAAK,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;YAC/B,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC;gBACrD,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;oBACxB,OAAO,KAAK,CAAC;gBACf,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,KAAK,CAAC;YACf,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,8EAA8E;IAC9E,UAAU;IACV,8EAA8E;IAE9E,KAAK,CAAC,IAAI;QACR,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;YACzE,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;gBAC5B,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;oBACxB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;oBACtD,IAAI,MAAM,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,EAAE,CAAC;wBAC3C,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;oBACzB,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,0CAA0C;QAC5C,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,IAAY;QACvB,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QACxC,OAAO,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAC1C,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,IAAY;QAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QAExC,uBAAuB;QACvB,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;YAC9C,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,YAAY,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QAChD,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;YACzD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAiB,CAAC;YACrD,OAAO;gBACL,IAAI,EAAE,QAAQ,CAAC,IAAI;gBACnB,MAAM,EAAE,QAAQ,CAAC,MAAM;gBACvB,SAAS,EAAE,QAAQ,CAAC,SAAS;gBAC7B,eAAe,EAAE,QAAQ,CAAC,eAAe;aAC1C,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,iDAAiD;YACjD,8CAA8C;YAC9C,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBACrC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC;gBAC/C,OAAO;oBACL,IAAI,EAAE,IAAI;oBACV,MAAM,EAAE,QAAQ;oBAChB,SAAS;oBACT,eAAe,EAAE,SAAS;iBAC3B,CAAC;YACJ,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;IACH,CAAC;IAED,8EAA8E;IAC9E,YAAY;IACZ,8EAA8E;IAE9E,KAAK,CAAC,MAAM,CAAC,IAAY;QACvB,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QAExC,0BAA0B;QAC1B,IAAI,MAAM,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC3C,MAAM,IAAI,sBAAsB,CAAC,IAAI,CAAC,CAAC;QACzC,CAAC;QAED,6BAA6B;QAC7B,MAAM,EAAE,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9C,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACpE,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACrE,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACvE,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAEvE,sBAAsB;QACtB,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACrC,MAAM,QAAQ,GAAiB;YAC7B,IAAI,EAAE,IAAI;YACV,MAAM,EAAE,QAAQ;YAChB,SAAS,EAAE,GAAG;YACd,eAAe,EAAE,GAAG;SACrB,CAAC;QACF,MAAM,EAAE,CAAC,SAAS,CAChB,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,EAC1B,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAClC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,SAAS,CACb,IAAY,EACZ,MAAkB,EAClB,QAAoC;QAEpC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QAC7C,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,iBAAiB,CAAC,IAAI,CAAC,CAAC;QACpC,CAAC;QAED,8BAA8B;QAC9B,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,MAAM,aAAa,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;YACtE,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC5C,MAAM,IAAI,uBAAuB,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;YACpE,CAAC;QACH,CAAC;QAED,kBAAkB;QAClB,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACrC,MAAM,QAAQ,GAAiB;YAC7B,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,MAAM;YACN,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,eAAe,EAAE,GAAG;SACrB,CAAC;QAEF,4BAA4B;QAC5B,MAAM,YAAY,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QAChD,MAAM,QAAQ,GAAG,GAAG,YAAY,MAAM,CAAC;QACvC,MAAM,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAChE,MAAM,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IAC1C,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,IAAY;QACvB,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,CAAC;YACH,yCAAyC;YACzC,MAAM,EAAE,CAAC,EAAE,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1D,CAAC;QAAC,MAAM,CAAC;YACP,2CAA2C;QAC7C,CAAC;IACH,CAAC;IAED,8EAA8E;IAC9E,mBAAmB;IACnB,8EAA8E;IAE9E,KAAK,CAAC,eAAe,CAAC,IAAY,EAAE,OAAgB;QAClD,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,OAAO,GAAG,CAAC,CAAC;QAEhB,oDAAoD;QACpD,gDAAgD;QAChD,MAAM,OAAO,GAAG,CAAC,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC;QAElE,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;YAC1B,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;YACzC,IAAI,CAAC;gBACH,OAAO,IAAI,MAAM,IAAI,CAAC,uBAAuB,CAAC,OAAO,CAAC,CAAC;YACzD,CAAC;YAAC,MAAM,CAAC;gBACP,0BAA0B;YAC5B,CAAC;QACH,CAAC;QAED,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,kBAAkB,CAAC,IAAY,EAAE,OAAgB;QACrD,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QACxC,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;QAClD,IAAI,OAAO,GAAG,CAAC,CAAC;QAEhB,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,IAAI,CAAC,uBAAuB,CAAC,UAAU,CAAC,CAAC;QAC3D,CAAC;QAAC,MAAM,CAAC;YACP,4BAA4B;QAC9B,CAAC;QAED,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;IACrC,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,uBAAuB,CAAC,GAAW;QAC/C,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;YAC/D,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;gBAC5B,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;gBAC7C,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;oBACxB,KAAK,IAAI,MAAM,IAAI,CAAC,uBAAuB,CAAC,SAAS,CAAC,CAAC;oBACvD,MAAM,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;gBAC5B,CAAC;qBAAM,CAAC;oBACN,MAAM,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;oBAC3B,KAAK,EAAE,CAAC;gBACV,CAAC;YACH,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,2CAA2C;QAC7C,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,8EAA8E;IAC9E,gBAAgB;IAChB,8EAA8E;IAE9E,oFAAoF;IACpF,2EAA2E;IAC3E,uDAAuD;IAEvD,KAAK,CAAC,kBAAkB,CAAC,IAAY,EAAE,OAAiB;QACtD,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QACnD,KAAK,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,QAAQ,EAAE,CAAC;YACzC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;YACjE,IAAI,IAAI,EAAE,CAAC;gBACT,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACnB,CAAC;QACH,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,CAAC;IACnB,CAAC;IAED,KAAK,CAAC,oBAAoB,CAAC,IAAY,EAAE,OAAiB;QACxD,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,eAAe,CAAC,kBAAkB,CAAC,CAAC;QACpD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;QAClD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YACvD,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS;YACzC,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;gBAC5B,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;gBAC9B,8CAA8C;gBAC9C,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;oBAClB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;oBACtD,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;wBAC/B,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;wBAC1D,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;4BAChC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;wBAC7B,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,iCAAiC;YACnC,CAAC;QACH,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,CAAC;IACnB,CAAC;IAED,KAAK,CAAC,oBAAoB,CAAC,IAAY,EAAE,OAAiB;QACxD,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;QACpD,KAAK,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,IAAI,OAAO,EAAE,CAAC;YAC/C,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,IAAI,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC;YACzE,KAAK,MAAM,WAAW,IAAI,GAAG,EAAE,CAAC;gBAC9B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;gBACrF,IAAI,CAAC,MAAM;oBAAE,SAAS;gBACtB,gEAAgE;gBAChE,MAAM,GAAG,GAAG,MAAqE,CAAC;gBAClF,IAAI,GAAG,CAAC,IAAI,KAAK,SAAS,IAAI,GAAG,CAAC,KAAK,CAAC,UAAU,IAAI,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;oBAClG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;gBACnC,CAAC;YACH,CAAC;QACH,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,CAAC;IACnB,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,IAAY,EAAE,OAAiB;QACjD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QAC9C,MAAM,OAAO,GAAoB,EAAE,CAAC;QAEpC,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAC7C,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;gBAC7B,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC;oBAAE,SAAS;gBAC5C,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;gBACjD,IAAI,CAAC;oBACH,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;oBACvC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE;wBAAE,SAAS;gBACpC,CAAC;gBAAC,MAAM,CAAC;oBACP,SAAS;gBACX,CAAC;gBACD,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;gBAC3C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;oBACzB,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC;wBAAE,SAAS;oBACxC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC;wBAAE,SAAS;oBACxC,MAAM,IAAI,GAAG,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,iBAAiB;oBAC1D,IAAI,CAAC;wBACH,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,CAAC;wBAC5D,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,QAAQ,CAAC,OAAO,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;oBAC9E,CAAC;oBAAC,MAAM,CAAC;wBACP,2BAA2B;oBAC7B,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,kCAAkC;QACpC,CAAC;QAED,6CAA6C;QAC7C,OAAO,EAAE,OAAO,EAAE,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,IAAY,EAAE,MAAgB;QAClD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QAE9C,KAAK,MAAM,IAAI,IAAI,MAAM,EAAE,CAAC;YAC1B,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAChC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAC3B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,IAAI,SAAS,CAAC,CAAC;YACjE,IAAI,CAAC;gBACH,MAAM,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAC5B,CAAC;YAAC,MAAM,CAAC;gBACP,qBAAqB;YACvB,CAAC;YACD,mCAAmC;YACnC,IAAI,CAAC;gBACH,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;YAChD,CAAC;YAAC,MAAM,CAAC;gBACP,uCAAuC;YACzC,CAAC;QACH,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Copyright (c) 2025 Elara AI Pty Ltd
3
+ * Licensed under BSL 1.1. See LICENSE for details.
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=LocalRepoStore.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LocalRepoStore.spec.d.ts","sourceRoot":"","sources":["../../../../src/storage/local/LocalRepoStore.spec.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
@@ -0,0 +1,255 @@
1
+ /**
2
+ * Copyright (c) 2025 Elara AI Pty Ltd
3
+ * Licensed under BSL 1.1. See LICENSE for details.
4
+ */
5
+ /**
6
+ * Tests for LocalRepoStore.
7
+ */
8
+ import { describe, it, beforeEach, afterEach } from 'node:test';
9
+ import assert from 'node:assert';
10
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
11
+ import { join } from 'node:path';
12
+ import { LocalStorage } from './LocalBackend.js';
13
+ import { RepoNotFoundError, RepoAlreadyExistsError, RepoStatusConflictError, } from '../../errors.js';
14
+ import { createTempDir, removeTempDir } from '../../test-helpers.js';
15
+ describe('LocalRepoStore', () => {
16
+ let testDir;
17
+ let storage;
18
+ let store;
19
+ beforeEach(() => {
20
+ testDir = createTempDir();
21
+ storage = new LocalStorage(testDir);
22
+ store = storage.repos;
23
+ });
24
+ afterEach(() => {
25
+ removeTempDir(testDir);
26
+ });
27
+ describe('list', () => {
28
+ it('returns empty array initially', async () => {
29
+ const repos = await store.list();
30
+ assert.deepStrictEqual(repos, []);
31
+ });
32
+ it('returns all created repos', async () => {
33
+ await store.create('repo1');
34
+ await store.create('repo2');
35
+ await store.create('repo3');
36
+ const repos = await store.list();
37
+ assert.deepStrictEqual(repos.sort(), ['repo1', 'repo2', 'repo3']);
38
+ });
39
+ it('does not include invalid directories', async () => {
40
+ await store.create('valid-repo');
41
+ // Create an incomplete repo (missing workspaces dir)
42
+ const invalidDir = join(testDir, 'invalid-repo');
43
+ mkdirSync(invalidDir);
44
+ mkdirSync(join(invalidDir, 'objects'));
45
+ mkdirSync(join(invalidDir, 'packages'));
46
+ mkdirSync(join(invalidDir, 'executions'));
47
+ // Missing 'workspaces' directory
48
+ const repos = await store.list();
49
+ assert.deepStrictEqual(repos, ['valid-repo']);
50
+ });
51
+ });
52
+ describe('exists', () => {
53
+ it('returns false for non-existent repo', async () => {
54
+ const exists = await store.exists('nonexistent');
55
+ assert.strictEqual(exists, false);
56
+ });
57
+ it('returns true for existing repo', async () => {
58
+ await store.create('my-repo');
59
+ const exists = await store.exists('my-repo');
60
+ assert.strictEqual(exists, true);
61
+ });
62
+ });
63
+ describe('getMetadata', () => {
64
+ it('returns null for non-existent repo', async () => {
65
+ const metadata = await store.getMetadata('nonexistent');
66
+ assert.strictEqual(metadata, null);
67
+ });
68
+ it('returns metadata for existing repo', async () => {
69
+ await store.create('my-repo');
70
+ const metadata = await store.getMetadata('my-repo');
71
+ assert.ok(metadata);
72
+ assert.strictEqual(metadata.name, 'my-repo');
73
+ assert.strictEqual(metadata.status, 'active');
74
+ assert.ok(metadata.createdAt);
75
+ assert.ok(metadata.statusChangedAt);
76
+ });
77
+ it('synthesizes metadata for legacy repos without metadata file', async () => {
78
+ // Create a legacy repo structure without metadata file
79
+ const legacyDir = join(testDir, 'legacy-repo');
80
+ mkdirSync(legacyDir);
81
+ mkdirSync(join(legacyDir, 'objects'));
82
+ mkdirSync(join(legacyDir, 'packages'));
83
+ mkdirSync(join(legacyDir, 'executions'));
84
+ mkdirSync(join(legacyDir, 'workspaces'));
85
+ const metadata = await store.getMetadata('legacy-repo');
86
+ assert.ok(metadata);
87
+ assert.strictEqual(metadata.name, 'legacy-repo');
88
+ assert.strictEqual(metadata.status, 'active');
89
+ assert.ok(metadata.createdAt);
90
+ });
91
+ });
92
+ describe('create', () => {
93
+ it('creates a new repo with active status', async () => {
94
+ await store.create('my-repo');
95
+ const metadata = await store.getMetadata('my-repo');
96
+ assert.ok(metadata);
97
+ assert.strictEqual(metadata.status, 'active');
98
+ });
99
+ it('creates all required directories', async () => {
100
+ await store.create('my-repo');
101
+ const repoDir = join(testDir, 'my-repo');
102
+ assert.strictEqual(existsSync(join(repoDir, 'objects')), true);
103
+ assert.strictEqual(existsSync(join(repoDir, 'packages')), true);
104
+ assert.strictEqual(existsSync(join(repoDir, 'executions')), true);
105
+ assert.strictEqual(existsSync(join(repoDir, 'workspaces')), true);
106
+ });
107
+ it('creates metadata file', async () => {
108
+ await store.create('my-repo');
109
+ const metadataPath = join(testDir, 'my-repo', '.e3-metadata.json');
110
+ assert.strictEqual(existsSync(metadataPath), true);
111
+ const content = JSON.parse(readFileSync(metadataPath, 'utf-8'));
112
+ assert.strictEqual(content.name, 'my-repo');
113
+ assert.strictEqual(content.status, 'active');
114
+ });
115
+ it('throws RepoAlreadyExistsError if repo exists', async () => {
116
+ await store.create('my-repo');
117
+ await assert.rejects(() => store.create('my-repo'), RepoAlreadyExistsError);
118
+ });
119
+ });
120
+ describe('setStatus', () => {
121
+ it('updates status', async () => {
122
+ await store.create('my-repo');
123
+ await store.setStatus('my-repo', 'gc');
124
+ const metadata = await store.getMetadata('my-repo');
125
+ assert.ok(metadata);
126
+ assert.strictEqual(metadata.status, 'gc');
127
+ });
128
+ it('updates statusChangedAt', async () => {
129
+ await store.create('my-repo');
130
+ const before = await store.getMetadata('my-repo');
131
+ // Wait a tiny bit to ensure timestamps differ
132
+ await new Promise(resolve => setTimeout(resolve, 10));
133
+ await store.setStatus('my-repo', 'gc');
134
+ const after = await store.getMetadata('my-repo');
135
+ assert.ok(before && after);
136
+ assert.notStrictEqual(before.statusChangedAt, after.statusChangedAt);
137
+ });
138
+ it('throws RepoNotFoundError for non-existent repo', async () => {
139
+ await assert.rejects(() => store.setStatus('nonexistent', 'gc'), RepoNotFoundError);
140
+ });
141
+ it('succeeds with correct expected status', async () => {
142
+ await store.create('my-repo');
143
+ await store.setStatus('my-repo', 'gc', 'active');
144
+ const metadata = await store.getMetadata('my-repo');
145
+ assert.ok(metadata);
146
+ assert.strictEqual(metadata.status, 'gc');
147
+ });
148
+ it('throws RepoStatusConflictError with wrong expected status', async () => {
149
+ await store.create('my-repo');
150
+ await assert.rejects(() => store.setStatus('my-repo', 'gc', 'deleting'), RepoStatusConflictError);
151
+ });
152
+ it('succeeds with expected status array', async () => {
153
+ await store.create('my-repo');
154
+ await store.setStatus('my-repo', 'gc', ['active', 'creating']);
155
+ const metadata = await store.getMetadata('my-repo');
156
+ assert.ok(metadata);
157
+ assert.strictEqual(metadata.status, 'gc');
158
+ });
159
+ });
160
+ describe('remove', () => {
161
+ it('removes a repo and its directory', async () => {
162
+ await store.create('my-repo');
163
+ await store.remove('my-repo');
164
+ const exists = await store.exists('my-repo');
165
+ assert.strictEqual(exists, false);
166
+ const dirExists = existsSync(join(testDir, 'my-repo'));
167
+ assert.strictEqual(dirExists, false);
168
+ });
169
+ it('does not throw for non-existent repo', async () => {
170
+ await store.remove('nonexistent');
171
+ // Should not throw
172
+ });
173
+ });
174
+ describe('deleteRefsBatch', () => {
175
+ it('deletes packages, workspaces, executions, locks directories', async () => {
176
+ await store.create('my-repo');
177
+ const repoDir = join(testDir, 'my-repo');
178
+ // Create some refs
179
+ const packagesDir = join(repoDir, 'packages', 'test-pkg');
180
+ mkdirSync(packagesDir, { recursive: true });
181
+ writeFileSync(join(packagesDir, '1.0.0'), 'abc123');
182
+ const result = await store.deleteRefsBatch('my-repo');
183
+ assert.strictEqual(result.status, 'done');
184
+ assert.ok(result.deleted >= 1);
185
+ // Packages dir should be empty now
186
+ assert.strictEqual(existsSync(join(packagesDir, '1.0.0')), false);
187
+ });
188
+ });
189
+ describe('deleteObjectsBatch', () => {
190
+ it('deletes objects directory contents', async () => {
191
+ await store.create('my-repo');
192
+ const repoDir = join(testDir, 'my-repo');
193
+ // Create an object
194
+ const objectDir = join(repoDir, 'objects', 'ab');
195
+ mkdirSync(objectDir, { recursive: true });
196
+ writeFileSync(join(objectDir, 'cd1234.beast2'), 'test data');
197
+ const result = await store.deleteObjectsBatch('my-repo');
198
+ assert.strictEqual(result.status, 'done');
199
+ assert.ok(result.deleted >= 1);
200
+ });
201
+ });
202
+ describe('GC primitives', () => {
203
+ // GC primitives receive the full repo path (same as ObjectStore/RefStore),
204
+ // not a repo name relative to reposDir.
205
+ it('gcScanPackageRoots returns empty for empty repo', async () => {
206
+ await store.create('my-repo');
207
+ const repoPath = join(testDir, 'my-repo');
208
+ const result = await store.gcScanPackageRoots(repoPath);
209
+ assert.deepStrictEqual(result.roots, []);
210
+ assert.strictEqual(result.cursor, undefined);
211
+ });
212
+ it('gcScanWorkspaceRoots returns empty for empty repo', async () => {
213
+ await store.create('my-repo');
214
+ const repoPath = join(testDir, 'my-repo');
215
+ const result = await store.gcScanWorkspaceRoots(repoPath);
216
+ assert.deepStrictEqual(result.roots, []);
217
+ });
218
+ it('gcScanExecutionRoots returns empty for empty repo', async () => {
219
+ await store.create('my-repo');
220
+ const repoPath = join(testDir, 'my-repo');
221
+ const result = await store.gcScanExecutionRoots(repoPath);
222
+ assert.deepStrictEqual(result.roots, []);
223
+ });
224
+ it('gcScanObjects returns empty for empty repo', async () => {
225
+ await store.create('my-repo');
226
+ const repoPath = join(testDir, 'my-repo');
227
+ const result = await store.gcScanObjects(repoPath);
228
+ assert.deepStrictEqual(result.objects, []);
229
+ assert.strictEqual(result.cursor, undefined);
230
+ });
231
+ it('gcScanObjects enumerates objects', async () => {
232
+ await store.create('my-repo');
233
+ const repoPath = join(testDir, 'my-repo');
234
+ // Create a fake object file
235
+ const objDir = join(repoPath, 'objects', 'ab');
236
+ mkdirSync(objDir, { recursive: true });
237
+ writeFileSync(join(objDir, 'cd' + '0'.repeat(60) + '.beast2'), 'data');
238
+ const result = await store.gcScanObjects(repoPath);
239
+ assert.strictEqual(result.objects.length, 1);
240
+ assert.strictEqual(result.objects[0].hash, 'ab' + 'cd' + '0'.repeat(60));
241
+ });
242
+ it('gcDeleteObjects removes objects', async () => {
243
+ await store.create('my-repo');
244
+ const repoPath = join(testDir, 'my-repo');
245
+ const hash = 'ab' + 'cd' + '0'.repeat(60);
246
+ const objDir = join(repoPath, 'objects', 'ab');
247
+ mkdirSync(objDir, { recursive: true });
248
+ writeFileSync(join(objDir, 'cd' + '0'.repeat(60) + '.beast2'), 'data');
249
+ await store.gcDeleteObjects(repoPath, [hash]);
250
+ const result = await store.gcScanObjects(repoPath);
251
+ assert.strictEqual(result.objects.length, 0);
252
+ });
253
+ });
254
+ });
255
+ //# sourceMappingURL=LocalRepoStore.spec.js.map