@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.
- package/LICENSE.md +4 -0
- package/README.md +74 -35
- package/dist/src/dataflow/api-compat.d.ts +90 -0
- package/dist/src/dataflow/api-compat.d.ts.map +1 -0
- package/dist/src/dataflow/api-compat.js +139 -0
- package/dist/src/dataflow/api-compat.js.map +1 -0
- package/dist/src/dataflow/api-compat.spec.d.ts +6 -0
- package/dist/src/dataflow/api-compat.spec.d.ts.map +1 -0
- package/dist/src/dataflow/api-compat.spec.js +182 -0
- package/dist/src/dataflow/api-compat.spec.js.map +1 -0
- package/dist/src/dataflow/index.d.ts +18 -0
- package/dist/src/dataflow/index.d.ts.map +1 -0
- package/dist/src/dataflow/index.js +23 -0
- package/dist/src/dataflow/index.js.map +1 -0
- package/dist/src/dataflow/orchestrator/LocalOrchestrator.d.ts +76 -0
- package/dist/src/dataflow/orchestrator/LocalOrchestrator.d.ts.map +1 -0
- package/dist/src/dataflow/orchestrator/LocalOrchestrator.js +729 -0
- package/dist/src/dataflow/orchestrator/LocalOrchestrator.js.map +1 -0
- package/dist/src/dataflow/orchestrator/index.d.ts +12 -0
- package/dist/src/dataflow/orchestrator/index.d.ts.map +1 -0
- package/dist/src/dataflow/orchestrator/index.js +12 -0
- package/dist/src/dataflow/orchestrator/index.js.map +1 -0
- package/dist/src/dataflow/orchestrator/interfaces.d.ts +163 -0
- package/dist/src/dataflow/orchestrator/interfaces.d.ts.map +1 -0
- package/dist/src/dataflow/orchestrator/interfaces.js +52 -0
- package/dist/src/dataflow/orchestrator/interfaces.js.map +1 -0
- package/dist/src/dataflow/state-store/FileStateStore.d.ts +67 -0
- package/dist/src/dataflow/state-store/FileStateStore.d.ts.map +1 -0
- package/dist/src/dataflow/state-store/FileStateStore.js +300 -0
- package/dist/src/dataflow/state-store/FileStateStore.js.map +1 -0
- package/dist/src/dataflow/state-store/InMemoryStateStore.d.ts +42 -0
- package/dist/src/dataflow/state-store/InMemoryStateStore.d.ts.map +1 -0
- package/dist/src/dataflow/state-store/InMemoryStateStore.js +229 -0
- package/dist/src/dataflow/state-store/InMemoryStateStore.js.map +1 -0
- package/dist/src/dataflow/state-store/InMemoryStateStore.spec.d.ts +6 -0
- package/dist/src/dataflow/state-store/InMemoryStateStore.spec.d.ts.map +1 -0
- package/dist/src/dataflow/state-store/InMemoryStateStore.spec.js +114 -0
- package/dist/src/dataflow/state-store/InMemoryStateStore.spec.js.map +1 -0
- package/dist/src/dataflow/state-store/index.d.ts +13 -0
- package/dist/src/dataflow/state-store/index.d.ts.map +1 -0
- package/dist/src/dataflow/state-store/index.js +13 -0
- package/dist/src/dataflow/state-store/index.js.map +1 -0
- package/dist/src/dataflow/state-store/interfaces.d.ts +159 -0
- package/dist/src/dataflow/state-store/interfaces.d.ts.map +1 -0
- package/dist/src/dataflow/state-store/interfaces.js +6 -0
- package/dist/src/dataflow/state-store/interfaces.js.map +1 -0
- package/dist/src/dataflow/steps.d.ts +222 -0
- package/dist/src/dataflow/steps.d.ts.map +1 -0
- package/dist/src/dataflow/steps.js +707 -0
- package/dist/src/dataflow/steps.js.map +1 -0
- package/dist/src/dataflow/steps.spec.d.ts +6 -0
- package/dist/src/dataflow/steps.spec.d.ts.map +1 -0
- package/dist/src/dataflow/steps.spec.js +343 -0
- package/dist/src/dataflow/steps.spec.js.map +1 -0
- package/dist/src/dataflow/types.d.ts +127 -0
- package/dist/src/dataflow/types.d.ts.map +1 -0
- package/dist/src/dataflow/types.js +7 -0
- package/dist/src/dataflow/types.js.map +1 -0
- package/dist/src/dataflow-orchestration.spec.d.ts +6 -0
- package/dist/src/dataflow-orchestration.spec.d.ts.map +1 -0
- package/dist/src/dataflow-orchestration.spec.js +1025 -0
- package/dist/src/dataflow-orchestration.spec.js.map +1 -0
- package/dist/src/dataflow.d.ts +113 -38
- package/dist/src/dataflow.d.ts.map +1 -1
- package/dist/src/dataflow.js +269 -416
- package/dist/src/dataflow.js.map +1 -1
- package/dist/src/dataflow.spec.d.ts +6 -0
- package/dist/src/dataflow.spec.d.ts.map +1 -0
- package/dist/src/dataflow.spec.js +663 -0
- package/dist/src/dataflow.spec.js.map +1 -0
- package/dist/src/dataset-refs.d.ts +124 -0
- package/dist/src/dataset-refs.d.ts.map +1 -0
- package/dist/src/dataset-refs.js +319 -0
- package/dist/src/dataset-refs.js.map +1 -0
- package/dist/src/errors.d.ts +39 -9
- package/dist/src/errors.d.ts.map +1 -1
- package/dist/src/errors.js +51 -8
- package/dist/src/errors.js.map +1 -1
- package/dist/src/errors.spec.d.ts +6 -0
- package/dist/src/errors.spec.d.ts.map +1 -0
- package/dist/src/errors.spec.js +276 -0
- package/dist/src/errors.spec.js.map +1 -0
- package/dist/src/execution/LocalTaskRunner.d.ts +73 -0
- package/dist/src/execution/LocalTaskRunner.d.ts.map +1 -0
- package/dist/src/execution/LocalTaskRunner.js +399 -0
- package/dist/src/execution/LocalTaskRunner.js.map +1 -0
- package/dist/src/execution/MockTaskRunner.d.ts +49 -0
- package/dist/src/execution/MockTaskRunner.d.ts.map +1 -0
- package/dist/src/execution/MockTaskRunner.js +54 -0
- package/dist/src/execution/MockTaskRunner.js.map +1 -0
- package/dist/src/execution/index.d.ts +16 -0
- package/dist/src/execution/index.d.ts.map +1 -0
- package/dist/src/execution/index.js +8 -0
- package/dist/src/execution/index.js.map +1 -0
- package/dist/src/execution/interfaces.d.ts +246 -0
- package/dist/src/execution/interfaces.d.ts.map +1 -0
- package/dist/src/execution/interfaces.js +6 -0
- package/dist/src/execution/interfaces.js.map +1 -0
- package/dist/src/execution/processHelpers.d.ts +20 -0
- package/dist/src/execution/processHelpers.d.ts.map +1 -0
- package/dist/src/execution/processHelpers.js +62 -0
- package/dist/src/execution/processHelpers.js.map +1 -0
- package/dist/src/executions.d.ts +71 -104
- package/dist/src/executions.d.ts.map +1 -1
- package/dist/src/executions.js +113 -481
- package/dist/src/executions.js.map +1 -1
- package/dist/src/executions.spec.d.ts +6 -0
- package/dist/src/executions.spec.d.ts.map +1 -0
- package/dist/src/executions.spec.js +387 -0
- package/dist/src/executions.spec.js.map +1 -0
- package/dist/src/formats.d.ts +18 -2
- package/dist/src/formats.d.ts.map +1 -1
- package/dist/src/formats.js +34 -2
- package/dist/src/formats.js.map +1 -1
- package/dist/src/gc.spec.d.ts +6 -0
- package/dist/src/gc.spec.d.ts.map +1 -0
- package/dist/src/gc.spec.js +512 -0
- package/dist/src/gc.spec.js.map +1 -0
- package/dist/src/index.d.ts +20 -10
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +48 -18
- package/dist/src/index.js.map +1 -1
- package/dist/src/objects.d.ts +7 -53
- package/dist/src/objects.d.ts.map +1 -1
- package/dist/src/objects.js +13 -232
- package/dist/src/objects.js.map +1 -1
- package/dist/src/objects.spec.d.ts +6 -0
- package/dist/src/objects.spec.d.ts.map +1 -0
- package/dist/src/objects.spec.js +247 -0
- package/dist/src/objects.spec.js.map +1 -0
- package/dist/src/packages.d.ts +41 -14
- package/dist/src/packages.d.ts.map +1 -1
- package/dist/src/packages.js +151 -89
- package/dist/src/packages.js.map +1 -1
- package/dist/src/packages.spec.d.ts +6 -0
- package/dist/src/packages.spec.d.ts.map +1 -0
- package/dist/src/packages.spec.js +324 -0
- package/dist/src/packages.spec.js.map +1 -0
- package/dist/src/storage/in-memory/InMemoryRepoStore.d.ts +35 -0
- package/dist/src/storage/in-memory/InMemoryRepoStore.d.ts.map +1 -0
- package/dist/src/storage/in-memory/InMemoryRepoStore.js +107 -0
- package/dist/src/storage/in-memory/InMemoryRepoStore.js.map +1 -0
- package/dist/src/storage/in-memory/InMemoryRepoStore.spec.d.ts +6 -0
- package/dist/src/storage/in-memory/InMemoryRepoStore.spec.d.ts.map +1 -0
- package/dist/src/storage/in-memory/InMemoryRepoStore.spec.js +187 -0
- package/dist/src/storage/in-memory/InMemoryRepoStore.spec.js.map +1 -0
- package/dist/src/storage/in-memory/InMemoryStorage.d.ts +139 -0
- package/dist/src/storage/in-memory/InMemoryStorage.d.ts.map +1 -0
- package/dist/src/storage/in-memory/InMemoryStorage.js +439 -0
- package/dist/src/storage/in-memory/InMemoryStorage.js.map +1 -0
- package/dist/src/storage/in-memory/index.d.ts +12 -0
- package/dist/src/storage/in-memory/index.d.ts.map +1 -0
- package/dist/src/storage/in-memory/index.js +12 -0
- package/dist/src/storage/in-memory/index.js.map +1 -0
- package/dist/src/storage/index.d.ts +18 -0
- package/dist/src/storage/index.d.ts.map +1 -0
- package/dist/src/storage/index.js +10 -0
- package/dist/src/storage/index.js.map +1 -0
- package/dist/src/storage/interfaces.d.ts +581 -0
- package/dist/src/storage/interfaces.d.ts.map +1 -0
- package/dist/src/storage/interfaces.js +6 -0
- package/dist/src/storage/interfaces.js.map +1 -0
- package/dist/src/storage/local/LocalBackend.d.ts +56 -0
- package/dist/src/storage/local/LocalBackend.d.ts.map +1 -0
- package/dist/src/storage/local/LocalBackend.js +145 -0
- package/dist/src/storage/local/LocalBackend.js.map +1 -0
- package/dist/src/storage/local/LocalDatasetRefStore.d.ts +22 -0
- package/dist/src/storage/local/LocalDatasetRefStore.d.ts.map +1 -0
- package/dist/src/storage/local/LocalDatasetRefStore.js +118 -0
- package/dist/src/storage/local/LocalDatasetRefStore.js.map +1 -0
- package/dist/src/storage/local/LocalLockService.d.ts +111 -0
- package/dist/src/storage/local/LocalLockService.d.ts.map +1 -0
- package/dist/src/storage/local/LocalLockService.js +364 -0
- package/dist/src/storage/local/LocalLockService.js.map +1 -0
- package/dist/src/storage/local/LocalLockService.spec.d.ts +6 -0
- package/dist/src/storage/local/LocalLockService.spec.d.ts.map +1 -0
- package/dist/src/storage/local/LocalLockService.spec.js +148 -0
- package/dist/src/storage/local/LocalLockService.spec.js.map +1 -0
- package/dist/src/storage/local/LocalLogStore.d.ts +23 -0
- package/dist/src/storage/local/LocalLogStore.d.ts.map +1 -0
- package/dist/src/storage/local/LocalLogStore.js +66 -0
- package/dist/src/storage/local/LocalLogStore.js.map +1 -0
- package/dist/src/storage/local/LocalObjectStore.d.ts +55 -0
- package/dist/src/storage/local/LocalObjectStore.d.ts.map +1 -0
- package/dist/src/storage/local/LocalObjectStore.js +300 -0
- package/dist/src/storage/local/LocalObjectStore.js.map +1 -0
- package/dist/src/storage/local/LocalRefStore.d.ts +50 -0
- package/dist/src/storage/local/LocalRefStore.d.ts.map +1 -0
- package/dist/src/storage/local/LocalRefStore.js +337 -0
- package/dist/src/storage/local/LocalRefStore.js.map +1 -0
- package/dist/src/storage/local/LocalRepoStore.d.ts +55 -0
- package/dist/src/storage/local/LocalRepoStore.d.ts.map +1 -0
- package/dist/src/storage/local/LocalRepoStore.js +365 -0
- package/dist/src/storage/local/LocalRepoStore.js.map +1 -0
- package/dist/src/storage/local/LocalRepoStore.spec.d.ts +6 -0
- package/dist/src/storage/local/LocalRepoStore.spec.d.ts.map +1 -0
- package/dist/src/storage/local/LocalRepoStore.spec.js +255 -0
- package/dist/src/storage/local/LocalRepoStore.spec.js.map +1 -0
- package/dist/src/storage/local/gc.d.ts +92 -0
- package/dist/src/storage/local/gc.d.ts.map +1 -0
- package/dist/src/storage/local/gc.js +377 -0
- package/dist/src/storage/local/gc.js.map +1 -0
- package/dist/src/storage/local/index.d.ts +18 -0
- package/dist/src/storage/local/index.d.ts.map +1 -0
- package/dist/src/storage/local/index.js +18 -0
- package/dist/src/storage/local/index.js.map +1 -0
- package/dist/src/storage/local/localHelpers.d.ts +25 -0
- package/dist/src/storage/local/localHelpers.d.ts.map +1 -0
- package/dist/src/storage/local/localHelpers.js +69 -0
- package/dist/src/storage/local/localHelpers.js.map +1 -0
- package/dist/src/{repository.d.ts → storage/local/repository.d.ts} +8 -4
- package/dist/src/storage/local/repository.d.ts.map +1 -0
- package/dist/src/{repository.js → storage/local/repository.js} +31 -29
- package/dist/src/storage/local/repository.js.map +1 -0
- package/dist/src/storage/local/repository.spec.d.ts +6 -0
- package/dist/src/storage/local/repository.spec.d.ts.map +1 -0
- package/dist/src/storage/local/repository.spec.js +186 -0
- package/dist/src/storage/local/repository.spec.js.map +1 -0
- package/dist/src/tasks.d.ts +16 -10
- package/dist/src/tasks.d.ts.map +1 -1
- package/dist/src/tasks.js +35 -41
- package/dist/src/tasks.js.map +1 -1
- package/dist/src/tasks.spec.d.ts +6 -0
- package/dist/src/tasks.spec.d.ts.map +1 -0
- package/dist/src/tasks.spec.js +105 -0
- package/dist/src/tasks.spec.js.map +1 -0
- package/dist/src/test-helpers.d.ts +5 -4
- package/dist/src/test-helpers.d.ts.map +1 -1
- package/dist/src/test-helpers.js +9 -21
- package/dist/src/test-helpers.js.map +1 -1
- package/dist/src/transfer/InMemoryTransferBackend.d.ts +75 -0
- package/dist/src/transfer/InMemoryTransferBackend.d.ts.map +1 -0
- package/dist/src/transfer/InMemoryTransferBackend.js +211 -0
- package/dist/src/transfer/InMemoryTransferBackend.js.map +1 -0
- package/dist/src/transfer/index.d.ts +9 -0
- package/dist/src/transfer/index.d.ts.map +1 -0
- package/dist/src/transfer/index.js +11 -0
- package/dist/src/transfer/index.js.map +1 -0
- package/dist/src/transfer/interfaces.d.ts +103 -0
- package/dist/src/transfer/interfaces.d.ts.map +1 -0
- package/dist/src/transfer/interfaces.js +6 -0
- package/dist/src/transfer/interfaces.js.map +1 -0
- package/dist/src/transfer/process.d.ts +55 -0
- package/dist/src/transfer/process.d.ts.map +1 -0
- package/dist/src/transfer/process.js +144 -0
- package/dist/src/transfer/process.js.map +1 -0
- package/dist/src/transfer/types.d.ts +106 -0
- package/dist/src/transfer/types.d.ts.map +1 -0
- package/dist/src/transfer/types.js +61 -0
- package/dist/src/transfer/types.js.map +1 -0
- package/dist/src/trees.d.ts +102 -63
- package/dist/src/trees.d.ts.map +1 -1
- package/dist/src/trees.js +319 -479
- package/dist/src/trees.js.map +1 -1
- package/dist/src/trees.spec.d.ts +6 -0
- package/dist/src/trees.spec.d.ts.map +1 -0
- package/dist/src/trees.spec.js +635 -0
- package/dist/src/trees.spec.js.map +1 -0
- package/dist/src/uuid.d.ts +26 -0
- package/dist/src/uuid.d.ts.map +1 -0
- package/dist/src/uuid.js +80 -0
- package/dist/src/uuid.js.map +1 -0
- package/dist/src/workspaceStatus.d.ts +6 -4
- package/dist/src/workspaceStatus.d.ts.map +1 -1
- package/dist/src/workspaceStatus.js +46 -60
- package/dist/src/workspaceStatus.js.map +1 -1
- package/dist/src/workspaces.d.ts +46 -47
- package/dist/src/workspaces.d.ts.map +1 -1
- package/dist/src/workspaces.js +281 -221
- package/dist/src/workspaces.js.map +1 -1
- package/dist/src/workspaces.spec.d.ts +6 -0
- package/dist/src/workspaces.spec.d.ts.map +1 -0
- package/dist/src/workspaces.spec.js +273 -0
- package/dist/src/workspaces.spec.js.map +1 -0
- package/package.json +15 -15
- package/dist/src/gc.d.ts +0 -54
- package/dist/src/gc.d.ts.map +0 -1
- package/dist/src/gc.js +0 -233
- package/dist/src/gc.js.map +0 -1
- package/dist/src/repository.d.ts.map +0 -1
- package/dist/src/repository.js.map +0 -1
- package/dist/src/workspaceLock.d.ts +0 -67
- package/dist/src/workspaceLock.d.ts.map +0 -1
- package/dist/src/workspaceLock.js +0 -217
- 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":"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"}
|
|
@@ -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
|