@elaraai/e3-core 0.0.2-beta.3 → 0.0.2-beta.30

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 (184) hide show
  1. package/README.md +25 -22
  2. package/dist/src/dataflow/api-compat.d.ts +90 -0
  3. package/dist/src/dataflow/api-compat.d.ts.map +1 -0
  4. package/dist/src/dataflow/api-compat.js +134 -0
  5. package/dist/src/dataflow/api-compat.js.map +1 -0
  6. package/dist/src/dataflow/index.d.ts +18 -0
  7. package/dist/src/dataflow/index.d.ts.map +1 -0
  8. package/dist/src/dataflow/index.js +23 -0
  9. package/dist/src/dataflow/index.js.map +1 -0
  10. package/dist/src/dataflow/orchestrator/LocalOrchestrator.d.ts +53 -0
  11. package/dist/src/dataflow/orchestrator/LocalOrchestrator.d.ts.map +1 -0
  12. package/dist/src/dataflow/orchestrator/LocalOrchestrator.js +416 -0
  13. package/dist/src/dataflow/orchestrator/LocalOrchestrator.js.map +1 -0
  14. package/dist/src/dataflow/orchestrator/index.d.ts +12 -0
  15. package/dist/src/dataflow/orchestrator/index.d.ts.map +1 -0
  16. package/dist/src/dataflow/orchestrator/index.js +12 -0
  17. package/dist/src/dataflow/orchestrator/index.js.map +1 -0
  18. package/dist/src/dataflow/orchestrator/interfaces.d.ts +157 -0
  19. package/dist/src/dataflow/orchestrator/interfaces.d.ts.map +1 -0
  20. package/dist/src/dataflow/orchestrator/interfaces.js +51 -0
  21. package/dist/src/dataflow/orchestrator/interfaces.js.map +1 -0
  22. package/dist/src/dataflow/state-store/FileStateStore.d.ts +67 -0
  23. package/dist/src/dataflow/state-store/FileStateStore.d.ts.map +1 -0
  24. package/dist/src/dataflow/state-store/FileStateStore.js +286 -0
  25. package/dist/src/dataflow/state-store/FileStateStore.js.map +1 -0
  26. package/dist/src/dataflow/state-store/InMemoryStateStore.d.ts +42 -0
  27. package/dist/src/dataflow/state-store/InMemoryStateStore.d.ts.map +1 -0
  28. package/dist/src/dataflow/state-store/InMemoryStateStore.js +214 -0
  29. package/dist/src/dataflow/state-store/InMemoryStateStore.js.map +1 -0
  30. package/dist/src/dataflow/state-store/index.d.ts +13 -0
  31. package/dist/src/dataflow/state-store/index.d.ts.map +1 -0
  32. package/dist/src/dataflow/state-store/index.js +13 -0
  33. package/dist/src/dataflow/state-store/index.js.map +1 -0
  34. package/dist/src/dataflow/state-store/interfaces.d.ts +159 -0
  35. package/dist/src/dataflow/state-store/interfaces.d.ts.map +1 -0
  36. package/dist/src/dataflow/state-store/interfaces.js +6 -0
  37. package/dist/src/dataflow/state-store/interfaces.js.map +1 -0
  38. package/dist/src/dataflow/steps.d.ts +176 -0
  39. package/dist/src/dataflow/steps.d.ts.map +1 -0
  40. package/dist/src/dataflow/steps.js +528 -0
  41. package/dist/src/dataflow/steps.js.map +1 -0
  42. package/dist/src/dataflow/types.d.ts +116 -0
  43. package/dist/src/dataflow/types.d.ts.map +1 -0
  44. package/dist/src/dataflow/types.js +7 -0
  45. package/dist/src/dataflow/types.js.map +1 -0
  46. package/dist/src/dataflow.d.ts +142 -9
  47. package/dist/src/dataflow.d.ts.map +1 -1
  48. package/dist/src/dataflow.js +427 -64
  49. package/dist/src/dataflow.js.map +1 -1
  50. package/dist/src/errors.d.ts +39 -9
  51. package/dist/src/errors.d.ts.map +1 -1
  52. package/dist/src/errors.js +51 -8
  53. package/dist/src/errors.js.map +1 -1
  54. package/dist/src/execution/LocalTaskRunner.d.ts +73 -0
  55. package/dist/src/execution/LocalTaskRunner.d.ts.map +1 -0
  56. package/dist/src/execution/LocalTaskRunner.js +399 -0
  57. package/dist/src/execution/LocalTaskRunner.js.map +1 -0
  58. package/dist/src/execution/MockTaskRunner.d.ts +49 -0
  59. package/dist/src/execution/MockTaskRunner.d.ts.map +1 -0
  60. package/dist/src/execution/MockTaskRunner.js +55 -0
  61. package/dist/src/execution/MockTaskRunner.js.map +1 -0
  62. package/dist/src/execution/index.d.ts +16 -0
  63. package/dist/src/execution/index.d.ts.map +1 -0
  64. package/dist/src/execution/index.js +8 -0
  65. package/dist/src/execution/index.js.map +1 -0
  66. package/dist/src/execution/interfaces.d.ts +246 -0
  67. package/dist/src/execution/interfaces.d.ts.map +1 -0
  68. package/dist/src/execution/interfaces.js +6 -0
  69. package/dist/src/execution/interfaces.js.map +1 -0
  70. package/dist/src/execution/processHelpers.d.ts +20 -0
  71. package/dist/src/execution/processHelpers.d.ts.map +1 -0
  72. package/dist/src/execution/processHelpers.js +62 -0
  73. package/dist/src/execution/processHelpers.js.map +1 -0
  74. package/dist/src/executions.d.ts +71 -104
  75. package/dist/src/executions.d.ts.map +1 -1
  76. package/dist/src/executions.js +110 -476
  77. package/dist/src/executions.js.map +1 -1
  78. package/dist/src/index.d.ts +17 -9
  79. package/dist/src/index.d.ts.map +1 -1
  80. package/dist/src/index.js +44 -18
  81. package/dist/src/index.js.map +1 -1
  82. package/dist/src/objects.d.ts +6 -53
  83. package/dist/src/objects.d.ts.map +1 -1
  84. package/dist/src/objects.js +11 -232
  85. package/dist/src/objects.js.map +1 -1
  86. package/dist/src/packages.d.ts +22 -14
  87. package/dist/src/packages.d.ts.map +1 -1
  88. package/dist/src/packages.js +116 -83
  89. package/dist/src/packages.js.map +1 -1
  90. package/dist/src/storage/in-memory/InMemoryRepoStore.d.ts +35 -0
  91. package/dist/src/storage/in-memory/InMemoryRepoStore.d.ts.map +1 -0
  92. package/dist/src/storage/in-memory/InMemoryRepoStore.js +107 -0
  93. package/dist/src/storage/in-memory/InMemoryRepoStore.js.map +1 -0
  94. package/dist/src/storage/in-memory/InMemoryStorage.d.ts +114 -0
  95. package/dist/src/storage/in-memory/InMemoryStorage.d.ts.map +1 -0
  96. package/dist/src/storage/in-memory/InMemoryStorage.js +349 -0
  97. package/dist/src/storage/in-memory/InMemoryStorage.js.map +1 -0
  98. package/dist/src/storage/in-memory/index.d.ts +12 -0
  99. package/dist/src/storage/in-memory/index.d.ts.map +1 -0
  100. package/dist/src/storage/in-memory/index.js +12 -0
  101. package/dist/src/storage/in-memory/index.js.map +1 -0
  102. package/dist/src/storage/index.d.ts +18 -0
  103. package/dist/src/storage/index.d.ts.map +1 -0
  104. package/dist/src/storage/index.js +10 -0
  105. package/dist/src/storage/index.js.map +1 -0
  106. package/dist/src/storage/interfaces.d.ts +520 -0
  107. package/dist/src/storage/interfaces.d.ts.map +1 -0
  108. package/dist/src/storage/interfaces.js +6 -0
  109. package/dist/src/storage/interfaces.js.map +1 -0
  110. package/dist/src/storage/local/LocalBackend.d.ts +54 -0
  111. package/dist/src/storage/local/LocalBackend.d.ts.map +1 -0
  112. package/dist/src/storage/local/LocalBackend.js +141 -0
  113. package/dist/src/storage/local/LocalBackend.js.map +1 -0
  114. package/dist/src/storage/local/LocalLockService.d.ts +105 -0
  115. package/dist/src/storage/local/LocalLockService.d.ts.map +1 -0
  116. package/dist/src/storage/local/LocalLockService.js +342 -0
  117. package/dist/src/storage/local/LocalLockService.js.map +1 -0
  118. package/dist/src/storage/local/LocalLogStore.d.ts +23 -0
  119. package/dist/src/storage/local/LocalLogStore.d.ts.map +1 -0
  120. package/dist/src/storage/local/LocalLogStore.js +66 -0
  121. package/dist/src/storage/local/LocalLogStore.js.map +1 -0
  122. package/dist/src/storage/local/LocalObjectStore.d.ts +52 -0
  123. package/dist/src/storage/local/LocalObjectStore.d.ts.map +1 -0
  124. package/dist/src/storage/local/LocalObjectStore.js +287 -0
  125. package/dist/src/storage/local/LocalObjectStore.js.map +1 -0
  126. package/dist/src/storage/local/LocalRefStore.d.ts +50 -0
  127. package/dist/src/storage/local/LocalRefStore.d.ts.map +1 -0
  128. package/dist/src/storage/local/LocalRefStore.js +337 -0
  129. package/dist/src/storage/local/LocalRefStore.js.map +1 -0
  130. package/dist/src/storage/local/LocalRepoStore.d.ts +53 -0
  131. package/dist/src/storage/local/LocalRepoStore.d.ts.map +1 -0
  132. package/dist/src/storage/local/LocalRepoStore.js +353 -0
  133. package/dist/src/storage/local/LocalRepoStore.js.map +1 -0
  134. package/dist/src/storage/local/gc.d.ts +92 -0
  135. package/dist/src/storage/local/gc.d.ts.map +1 -0
  136. package/dist/src/storage/local/gc.js +322 -0
  137. package/dist/src/storage/local/gc.js.map +1 -0
  138. package/dist/src/storage/local/index.d.ts +17 -0
  139. package/dist/src/storage/local/index.d.ts.map +1 -0
  140. package/dist/src/storage/local/index.js +17 -0
  141. package/dist/src/storage/local/index.js.map +1 -0
  142. package/dist/src/storage/local/localHelpers.d.ts +25 -0
  143. package/dist/src/storage/local/localHelpers.d.ts.map +1 -0
  144. package/dist/src/storage/local/localHelpers.js +69 -0
  145. package/dist/src/storage/local/localHelpers.js.map +1 -0
  146. package/dist/src/{repository.d.ts → storage/local/repository.d.ts} +8 -4
  147. package/dist/src/storage/local/repository.d.ts.map +1 -0
  148. package/dist/src/{repository.js → storage/local/repository.js} +31 -29
  149. package/dist/src/storage/local/repository.js.map +1 -0
  150. package/dist/src/tasks.d.ts +16 -10
  151. package/dist/src/tasks.d.ts.map +1 -1
  152. package/dist/src/tasks.js +35 -41
  153. package/dist/src/tasks.js.map +1 -1
  154. package/dist/src/test-helpers.d.ts +4 -4
  155. package/dist/src/test-helpers.d.ts.map +1 -1
  156. package/dist/src/test-helpers.js +7 -21
  157. package/dist/src/test-helpers.js.map +1 -1
  158. package/dist/src/trees.d.ts +89 -27
  159. package/dist/src/trees.d.ts.map +1 -1
  160. package/dist/src/trees.js +218 -100
  161. package/dist/src/trees.js.map +1 -1
  162. package/dist/src/uuid.d.ts +26 -0
  163. package/dist/src/uuid.d.ts.map +1 -0
  164. package/dist/src/uuid.js +80 -0
  165. package/dist/src/uuid.js.map +1 -0
  166. package/dist/src/workspaceStatus.d.ts +6 -4
  167. package/dist/src/workspaceStatus.d.ts.map +1 -1
  168. package/dist/src/workspaceStatus.js +43 -49
  169. package/dist/src/workspaceStatus.js.map +1 -1
  170. package/dist/src/workspaces.d.ts +35 -26
  171. package/dist/src/workspaces.d.ts.map +1 -1
  172. package/dist/src/workspaces.js +169 -118
  173. package/dist/src/workspaces.js.map +1 -1
  174. package/package.json +4 -4
  175. package/dist/src/gc.d.ts +0 -54
  176. package/dist/src/gc.d.ts.map +0 -1
  177. package/dist/src/gc.js +0 -233
  178. package/dist/src/gc.js.map +0 -1
  179. package/dist/src/repository.d.ts.map +0 -1
  180. package/dist/src/repository.js.map +0 -1
  181. package/dist/src/workspaceLock.d.ts +0 -67
  182. package/dist/src/workspaceLock.d.ts.map +0 -1
  183. package/dist/src/workspaceLock.js +0 -217
  184. package/dist/src/workspaceLock.js.map +0 -1
@@ -0,0 +1,141 @@
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 { LocalObjectStore } from './LocalObjectStore.js';
8
+ import { LocalRefStore } from './LocalRefStore.js';
9
+ import { LocalLockService } from './LocalLockService.js';
10
+ import { LocalLogStore } from './LocalLogStore.js';
11
+ import { LocalRepoStore } from './LocalRepoStore.js';
12
+ import { RepoNotFoundError } from '../../errors.js';
13
+ /**
14
+ * Thrown when a local repository directory is not found or is missing required structure.
15
+ * This is an internal error for LocalStorage — external consumers see RepoNotFoundError.
16
+ */
17
+ class RepoDirNotFoundError extends RepoNotFoundError {
18
+ path;
19
+ constructor(path) {
20
+ super(path);
21
+ this.path = path;
22
+ }
23
+ }
24
+ /**
25
+ * Local filesystem implementation of StorageBackend.
26
+ *
27
+ * This combines the local implementations of all storage interfaces,
28
+ * providing a complete backend for local e3 repositories.
29
+ *
30
+ * The `repo` parameter passed to each method is the path to the e3 repository directory.
31
+ * This allows a single LocalStorage instance to be used for multiple repositories.
32
+ *
33
+ * @example
34
+ * ```typescript
35
+ * import { LocalStorage } from '@elaraai/e3-core';
36
+ *
37
+ * const storage = new LocalStorage();
38
+ * const repo = '/path/to/repo';
39
+ *
40
+ * // Use the backend with storage-agnostic functions
41
+ * const hash = await storage.objects.write(repo, data);
42
+ * const packages = await storage.refs.packageList(repo);
43
+ * ```
44
+ */
45
+ export class LocalStorage {
46
+ /** Content-addressed object storage */
47
+ objects;
48
+ /** Mutable reference storage */
49
+ refs;
50
+ /** Distributed locking service */
51
+ locks;
52
+ /** Execution log storage */
53
+ logs;
54
+ /** Repository lifecycle management */
55
+ repos;
56
+ /**
57
+ * Create a new LocalStorage instance.
58
+ *
59
+ * @param reposDir - Optional parent directory containing repositories.
60
+ * Required for repo lifecycle operations (repos.*).
61
+ * If not provided, repos.* methods will throw.
62
+ */
63
+ constructor(reposDir) {
64
+ this.objects = new LocalObjectStore();
65
+ this.refs = new LocalRefStore();
66
+ this.locks = new LocalLockService();
67
+ this.logs = new LocalLogStore();
68
+ // repos requires reposDir for multi-repo operations
69
+ // If not provided, create a RepoStore that throws on all operations
70
+ this.repos = reposDir
71
+ ? new LocalRepoStore(reposDir, this.refs)
72
+ : new NoOpRepoStore();
73
+ }
74
+ /**
75
+ * Validate that a repository exists and is properly structured.
76
+ * @param repo - Path to the e3 repository directory
77
+ * @throws {RepoNotFoundError} If repository doesn't exist or is invalid
78
+ */
79
+ async validateRepository(repo) {
80
+ const requiredDirs = ['objects', 'packages', 'workspaces', 'executions'];
81
+ for (const dir of requiredDirs) {
82
+ try {
83
+ await fs.access(path.join(repo, dir));
84
+ }
85
+ catch {
86
+ throw new RepoDirNotFoundError(repo);
87
+ }
88
+ }
89
+ }
90
+ }
91
+ // Re-export as LocalBackend for backwards compatibility during migration
92
+ export { LocalStorage as LocalBackend };
93
+ /**
94
+ * No-op implementation of RepoStore that throws on all operations.
95
+ * Used when LocalStorage is created without a reposDir.
96
+ */
97
+ class NoOpRepoStore {
98
+ error() {
99
+ throw new Error('RepoStore operations require reposDir to be configured');
100
+ }
101
+ list() {
102
+ return this.error();
103
+ }
104
+ exists(_repo) {
105
+ return this.error();
106
+ }
107
+ getMetadata(_repo) {
108
+ return this.error();
109
+ }
110
+ create(_repo) {
111
+ return this.error();
112
+ }
113
+ setStatus(_repo, _status, _expected) {
114
+ return this.error();
115
+ }
116
+ remove(_repo) {
117
+ return this.error();
118
+ }
119
+ deleteRefsBatch(_repo, _cursor) {
120
+ return this.error();
121
+ }
122
+ deleteObjectsBatch(_repo, _cursor) {
123
+ return this.error();
124
+ }
125
+ gcScanPackageRoots(_repo, _cursor) {
126
+ return this.error();
127
+ }
128
+ gcScanWorkspaceRoots(_repo, _cursor) {
129
+ return this.error();
130
+ }
131
+ gcScanExecutionRoots(_repo, _cursor) {
132
+ return this.error();
133
+ }
134
+ gcScanObjects(_repo, _cursor) {
135
+ return this.error();
136
+ }
137
+ gcDeleteObjects(_repo, _hashes) {
138
+ return this.error();
139
+ }
140
+ }
141
+ //# sourceMappingURL=LocalBackend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LocalBackend.js","sourceRoot":"","sources":["../../../../src/storage/local/LocalBackend.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Copyright (c) 2025 Elara AI Pty Ltd
3
+ * Licensed under BSL 1.1. See LICENSE for details.
4
+ */
5
+ import { type LockState, type LockOperation } from '@elaraai/e3-types';
6
+ import { type LockHolderInfo } from '../../errors.js';
7
+ import type { LockHandle, LockService } from '../interfaces.js';
8
+ /**
9
+ * Handle to a held workspace lock.
10
+ * Call release() when done to free the lock.
11
+ */
12
+ export interface WorkspaceLockHandle {
13
+ /** The resource (workspace name) this lock is for - compatible with LockHandle */
14
+ readonly resource: string;
15
+ /** The workspace name this lock is for */
16
+ readonly workspace: string;
17
+ /** Path to the lock file */
18
+ readonly lockPath: string;
19
+ /** Release the lock. Safe to call multiple times. */
20
+ release(): Promise<void>;
21
+ }
22
+ /**
23
+ * Options for acquiring a workspace lock.
24
+ */
25
+ export interface AcquireLockOptions {
26
+ /**
27
+ * If true, wait for the lock to become available instead of failing immediately.
28
+ * Default: false (fail fast if locked)
29
+ */
30
+ wait?: boolean;
31
+ /**
32
+ * Timeout in milliseconds when wait=true. Default: 30000 (30 seconds)
33
+ */
34
+ timeout?: number;
35
+ }
36
+ /**
37
+ * Get the lock file path for a workspace.
38
+ */
39
+ export declare function workspaceLockPath(repoPath: string, workspace: string): string;
40
+ /**
41
+ * Convert LockState to LockHolderInfo for error display.
42
+ */
43
+ export declare function lockStateToHolderInfo(state: LockState): LockHolderInfo;
44
+ /**
45
+ * Check if a lock holder is still alive.
46
+ * @param holderStr - East text-encoded holder string
47
+ */
48
+ export declare function isLockHolderAlive(holderStr: string): Promise<boolean>;
49
+ /**
50
+ * Acquire an exclusive lock on a workspace.
51
+ *
52
+ * Uses Linux flock() for kernel-managed locking. The lock is automatically
53
+ * released when the process exits (even on crash/kill).
54
+ *
55
+ * @param repoPath - Path to e3 repository
56
+ * @param workspace - Workspace name to lock
57
+ * @param operation - What operation is acquiring the lock
58
+ * @param options - Lock acquisition options
59
+ * @returns Lock handle - call release() when done
60
+ * @throws {WorkspaceLockError} If workspace is locked by another process
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * const lock = await acquireWorkspaceLock(repoPath, 'production', { type: 'dataflow', value: null });
65
+ * try {
66
+ * await dataflowExecute(repoPath, 'production', { lock });
67
+ * } finally {
68
+ * await lock.release();
69
+ * }
70
+ * ```
71
+ */
72
+ export declare function acquireWorkspaceLock(repoPath: string, workspace: string, operation: LockOperation, options?: AcquireLockOptions): Promise<WorkspaceLockHandle>;
73
+ /**
74
+ * Get the lock state for a workspace.
75
+ *
76
+ * @param repoPath - Path to e3 repository
77
+ * @param workspace - Workspace name to check
78
+ * @returns Lock state if locked, null if not locked
79
+ */
80
+ export declare function getWorkspaceLockState(repoPath: string, workspace: string): Promise<LockState | null>;
81
+ /**
82
+ * Get lock holder info for a workspace (for backwards compatibility).
83
+ *
84
+ * @param repoPath - Path to e3 repository
85
+ * @param workspace - Workspace name to check
86
+ * @returns Lock holder info if locked, null if not locked
87
+ * @deprecated Use getWorkspaceLockState for full lock information
88
+ */
89
+ export declare function getWorkspaceLockHolder(repoPath: string, workspace: string): Promise<LockHolderInfo | null>;
90
+ /**
91
+ * Local filesystem implementation of LockService.
92
+ *
93
+ * Uses flock() for kernel-managed locking with lock state
94
+ * stored in beast2 format using LockStateType.
95
+ * The `repo` parameter is the path to the e3 repository directory.
96
+ */
97
+ export declare class LocalLockService implements LockService {
98
+ acquire(repo: string, resource: string, operation: LockOperation, options?: {
99
+ wait?: boolean;
100
+ timeout?: number;
101
+ }): Promise<LockHandle | null>;
102
+ getState(repo: string, resource: string): Promise<LockState | null>;
103
+ isHolderAlive(holder: string): Promise<boolean>;
104
+ }
105
+ //# sourceMappingURL=LocalLockService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LocalLockService.d.ts","sourceRoot":"","sources":["../../../../src/storage/local/LocalLockService.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAqBH,OAAO,EAAoC,KAAK,SAAS,EAAE,KAAK,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACzG,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAE1E,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAkChE;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,kFAAkF;IAClF,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,0CAA0C;IAC1C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,4BAA4B;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,qDAAqD;IACrD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAMD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAE7E;AA0BD;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,SAAS,GAAG,cAAc,CAgBtE;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAc3E;AAMD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,aAAa,EACxB,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,mBAAmB,CAAC,CAgE9B;AAuFD;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAoB3B;AAED;;;;;;;GAOG;AACH,wBAAsB,sBAAsB,CAC1C,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAGhC;AAMD;;;;;;GAMG;AACH,qBAAa,gBAAiB,YAAW,WAAW;IAC5C,OAAO,CACX,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,aAAa,EACxB,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAC7C,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAkB7B,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAInE,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAGhD"}
@@ -0,0 +1,342 @@
1
+ /**
2
+ * Copyright (c) 2025 Elara AI Pty Ltd
3
+ * Licensed under BSL 1.1. See LICENSE for details.
4
+ */
5
+ /**
6
+ * Local filesystem implementation of workspace locking.
7
+ *
8
+ * Provides exclusive locks on workspaces to prevent concurrent dataflow
9
+ * executions or writes that could corrupt workspace state. Uses Linux
10
+ * flock() for automatic lock release on process death.
11
+ *
12
+ * Lock mechanism:
13
+ * - Uses flock(LOCK_EX | LOCK_NB) via the `flock` command for kernel-managed locking
14
+ * - Lock is automatically released when the process dies (kernel handles this)
15
+ * - Lock state stored in beast2 format using LockStateType from e3-types
16
+ * - Holder stored as East text string (e.g., `.process (pid=1234, ...)`)
17
+ * - Stale lock detection via bootId comparison (handles system restarts)
18
+ */
19
+ import * as fs from 'fs/promises';
20
+ import * as path from 'path';
21
+ import { spawn } from 'child_process';
22
+ import { encodeBeast2For, decodeBeast2For, printFor, parseInferred, variant, none, VariantType } from '@elaraai/east';
23
+ import { LockStateType, ProcessHolderType } from '@elaraai/e3-types';
24
+ import { WorkspaceLockError } from '../../errors.js';
25
+ import { getBootId, getPidStartTime, isProcessAlive } from '../../execution/processHelpers.js';
26
+ // =============================================================================
27
+ // Holder Encoding
28
+ // =============================================================================
29
+ /**
30
+ * Variant type for encoding holder as East text.
31
+ * The holder string stores `.process (...)` or other backend-specific variants.
32
+ */
33
+ const HolderVariantType = VariantType({
34
+ process: ProcessHolderType,
35
+ });
36
+ /** Print a process holder to East text format */
37
+ const printProcessHolder = printFor(HolderVariantType);
38
+ /**
39
+ * Parse an East text holder string.
40
+ * Returns the parsed variant or null if parsing fails.
41
+ */
42
+ function parseHolder(holderStr) {
43
+ try {
44
+ const [_type, value] = parseInferred(holderStr);
45
+ return value;
46
+ }
47
+ catch {
48
+ return null;
49
+ }
50
+ }
51
+ // =============================================================================
52
+ // Lock File Helpers
53
+ // =============================================================================
54
+ /**
55
+ * Get the lock file path for a workspace.
56
+ */
57
+ export function workspaceLockPath(repoPath, workspace) {
58
+ return path.join(repoPath, 'workspaces', `${workspace}.lock`);
59
+ }
60
+ /**
61
+ * Read lock state from a lock file.
62
+ * Returns null if file doesn't exist or is invalid.
63
+ */
64
+ async function readLockState(lockPath) {
65
+ try {
66
+ const data = await fs.readFile(lockPath);
67
+ if (data.length === 0)
68
+ return null;
69
+ const decoder = decodeBeast2For(LockStateType);
70
+ return decoder(data);
71
+ }
72
+ catch {
73
+ return null;
74
+ }
75
+ }
76
+ /**
77
+ * Write lock state to a lock file in beast2 format.
78
+ */
79
+ async function writeLockState(lockPath, state) {
80
+ const encoder = encodeBeast2For(LockStateType);
81
+ const data = encoder(state);
82
+ await fs.writeFile(lockPath, data);
83
+ }
84
+ /**
85
+ * Convert LockState to LockHolderInfo for error display.
86
+ */
87
+ export function lockStateToHolderInfo(state) {
88
+ const info = {
89
+ acquiredAt: state.acquiredAt.toISOString(),
90
+ operation: state.operation.type,
91
+ };
92
+ // Parse the holder string to extract process-specific fields
93
+ const holder = parseHolder(state.holder);
94
+ if (holder?.type === 'process') {
95
+ info.pid = Number(holder.value.pid);
96
+ info.bootId = holder.value.bootId;
97
+ info.startTime = Number(holder.value.startTime);
98
+ info.command = holder.value.command;
99
+ }
100
+ return info;
101
+ }
102
+ /**
103
+ * Check if a lock holder is still alive.
104
+ * @param holderStr - East text-encoded holder string
105
+ */
106
+ export async function isLockHolderAlive(holderStr) {
107
+ const holder = parseHolder(holderStr);
108
+ if (!holder)
109
+ return true; // Can't parse - assume alive (safer)
110
+ if (holder.type === 'process') {
111
+ return isProcessAlive(Number(holder.value.pid), Number(holder.value.startTime), holder.value.bootId);
112
+ }
113
+ // Unknown holder type - assume alive (safer default)
114
+ return true;
115
+ }
116
+ // =============================================================================
117
+ // Lock Acquisition
118
+ // =============================================================================
119
+ /**
120
+ * Acquire an exclusive lock on a workspace.
121
+ *
122
+ * Uses Linux flock() for kernel-managed locking. The lock is automatically
123
+ * released when the process exits (even on crash/kill).
124
+ *
125
+ * @param repoPath - Path to e3 repository
126
+ * @param workspace - Workspace name to lock
127
+ * @param operation - What operation is acquiring the lock
128
+ * @param options - Lock acquisition options
129
+ * @returns Lock handle - call release() when done
130
+ * @throws {WorkspaceLockError} If workspace is locked by another process
131
+ *
132
+ * @example
133
+ * ```typescript
134
+ * const lock = await acquireWorkspaceLock(repoPath, 'production', { type: 'dataflow', value: null });
135
+ * try {
136
+ * await dataflowExecute(repoPath, 'production', { lock });
137
+ * } finally {
138
+ * await lock.release();
139
+ * }
140
+ * ```
141
+ */
142
+ export async function acquireWorkspaceLock(repoPath, workspace, operation, options = {}) {
143
+ const lockPath = workspaceLockPath(repoPath, workspace);
144
+ // Ensure workspaces directory exists
145
+ await fs.mkdir(path.dirname(lockPath), { recursive: true });
146
+ // Gather our process identification
147
+ const pid = process.pid;
148
+ const bootId = await getBootId();
149
+ const startTime = await getPidStartTime(pid);
150
+ const command = process.argv.join(' ');
151
+ const acquiredAt = new Date();
152
+ // Encode holder as East text: .process (pid=..., bootId="...", ...)
153
+ const holderVariant = variant('process', {
154
+ pid: BigInt(pid),
155
+ bootId,
156
+ startTime: BigInt(startTime),
157
+ command,
158
+ });
159
+ const holder = printProcessHolder(holderVariant);
160
+ const lockState = {
161
+ operation,
162
+ holder,
163
+ acquiredAt,
164
+ expiresAt: none,
165
+ };
166
+ // Try to acquire flock via subprocess
167
+ // The subprocess holds the lock and we communicate with it via stdin/signals
168
+ const flockProcess = await tryAcquireFlock(lockPath, lockState, options);
169
+ if (!flockProcess) {
170
+ // Failed to acquire - read lock state to report who has it
171
+ const existingState = await readLockState(lockPath);
172
+ const holderInfo = existingState ? lockStateToHolderInfo(existingState) : undefined;
173
+ throw new WorkspaceLockError(workspace, holderInfo);
174
+ }
175
+ // Lock acquired! Create handle
176
+ let released = false;
177
+ const handle = {
178
+ resource: workspace,
179
+ workspace,
180
+ lockPath,
181
+ async release() {
182
+ if (released)
183
+ return;
184
+ released = true;
185
+ // Kill the flock subprocess to release the lock
186
+ flockProcess.kill('SIGTERM');
187
+ // Clean up lock file (best effort)
188
+ try {
189
+ await fs.unlink(lockPath);
190
+ }
191
+ catch {
192
+ // Ignore - file might already be gone
193
+ }
194
+ },
195
+ };
196
+ return handle;
197
+ }
198
+ /**
199
+ * Try to acquire flock using a subprocess.
200
+ *
201
+ * We spawn `flock --nonblock <lockfile> cat` which:
202
+ * 1. Tries to acquire exclusive lock (non-blocking)
203
+ * 2. If successful, runs `cat` which blocks reading stdin forever
204
+ * 3. We keep the subprocess alive to hold the lock
205
+ * 4. When we kill the subprocess, the lock is released
206
+ *
207
+ * Returns the subprocess if lock acquired, null if lock is held by another.
208
+ */
209
+ async function tryAcquireFlock(lockPath, lockState, options) {
210
+ // First, check if there's a stale lock we can clean up
211
+ await checkAndCleanStaleLock(lockPath);
212
+ const args = options.wait
213
+ ? ['--timeout', String((options.timeout ?? 30000) / 1000), lockPath, 'cat']
214
+ : ['--nonblock', lockPath, 'cat'];
215
+ const child = spawn('flock', args, {
216
+ stdio: ['pipe', 'pipe', 'pipe'],
217
+ detached: false,
218
+ });
219
+ return new Promise((resolve) => {
220
+ let resolved = false;
221
+ // If flock fails to acquire, it exits with code 1
222
+ child.on('error', () => {
223
+ if (!resolved) {
224
+ resolved = true;
225
+ resolve(null);
226
+ }
227
+ });
228
+ child.on('exit', () => {
229
+ if (!resolved) {
230
+ resolved = true;
231
+ // Exit code 1 means lock is held by another
232
+ resolve(null);
233
+ }
234
+ });
235
+ // Give flock a moment to either acquire or fail
236
+ // If it's still running after 100ms, we have the lock
237
+ setTimeout(() => {
238
+ if (!resolved && !child.killed && child.exitCode === null) {
239
+ resolved = true;
240
+ // Write lock state to lock file now that we have the lock
241
+ void writeLockState(lockPath, lockState).catch((err) => {
242
+ console.warn(`Failed to write lock state: ${err instanceof Error ? err.message : String(err)}`);
243
+ });
244
+ resolve(child);
245
+ }
246
+ }, 100);
247
+ });
248
+ }
249
+ /**
250
+ * Check if a lock file exists with stale lock state and clean it up.
251
+ * A lock is stale if the holder process no longer exists.
252
+ */
253
+ async function checkAndCleanStaleLock(lockPath) {
254
+ const state = await readLockState(lockPath);
255
+ if (!state)
256
+ return;
257
+ // Check if the holder is still alive
258
+ const alive = await isLockHolderAlive(state.holder);
259
+ if (!alive) {
260
+ // Stale lock - try to remove it
261
+ try {
262
+ await fs.unlink(lockPath);
263
+ }
264
+ catch {
265
+ // Ignore - another process might have cleaned it up
266
+ }
267
+ }
268
+ }
269
+ /**
270
+ * Get the lock state for a workspace.
271
+ *
272
+ * @param repoPath - Path to e3 repository
273
+ * @param workspace - Workspace name to check
274
+ * @returns Lock state if locked, null if not locked
275
+ */
276
+ export async function getWorkspaceLockState(repoPath, workspace) {
277
+ const lockPath = workspaceLockPath(repoPath, workspace);
278
+ const state = await readLockState(lockPath);
279
+ if (!state)
280
+ return null;
281
+ // Check if the holder is still alive
282
+ const alive = await isLockHolderAlive(state.holder);
283
+ if (!alive) {
284
+ // Stale lock - clean it up and report as not locked
285
+ try {
286
+ await fs.unlink(lockPath);
287
+ }
288
+ catch {
289
+ // Ignore
290
+ }
291
+ return null;
292
+ }
293
+ return state;
294
+ }
295
+ /**
296
+ * Get lock holder info for a workspace (for backwards compatibility).
297
+ *
298
+ * @param repoPath - Path to e3 repository
299
+ * @param workspace - Workspace name to check
300
+ * @returns Lock holder info if locked, null if not locked
301
+ * @deprecated Use getWorkspaceLockState for full lock information
302
+ */
303
+ export async function getWorkspaceLockHolder(repoPath, workspace) {
304
+ const state = await getWorkspaceLockState(repoPath, workspace);
305
+ return state ? lockStateToHolderInfo(state) : null;
306
+ }
307
+ // =============================================================================
308
+ // LockService Interface Implementation
309
+ // =============================================================================
310
+ /**
311
+ * Local filesystem implementation of LockService.
312
+ *
313
+ * Uses flock() for kernel-managed locking with lock state
314
+ * stored in beast2 format using LockStateType.
315
+ * The `repo` parameter is the path to the e3 repository directory.
316
+ */
317
+ export class LocalLockService {
318
+ async acquire(repo, resource, operation, options) {
319
+ const acquireOptions = {
320
+ wait: options?.wait ?? false,
321
+ timeout: options?.timeout,
322
+ };
323
+ try {
324
+ const handle = await acquireWorkspaceLock(repo, resource, operation, acquireOptions);
325
+ return {
326
+ resource,
327
+ release: () => handle.release(),
328
+ };
329
+ }
330
+ catch {
331
+ // Lock couldn't be acquired
332
+ return null;
333
+ }
334
+ }
335
+ getState(repo, resource) {
336
+ return getWorkspaceLockState(repo, resource);
337
+ }
338
+ isHolderAlive(holder) {
339
+ return isLockHolderAlive(holder);
340
+ }
341
+ }
342
+ //# sourceMappingURL=LocalLockService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LocalLockService.js","sourceRoot":"","sources":["../../../../src/storage/local/LocalLockService.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Copyright (c) 2025 Elara AI Pty Ltd
3
+ * Licensed under BSL 1.1. See LICENSE for details.
4
+ */
5
+ import type { LogChunk, LogStore } from '../interfaces.js';
6
+ /**
7
+ * Local filesystem implementation of LogStore.
8
+ *
9
+ * Logs are stored as text files in the execution directory:
10
+ * executions/<taskHash>/<inputsHash>/<executionId>/stdout.txt
11
+ * executions/<taskHash>/<inputsHash>/<executionId>/stderr.txt
12
+ *
13
+ * The `repo` parameter is the path to the e3 repository directory.
14
+ */
15
+ export declare class LocalLogStore implements LogStore {
16
+ private logPath;
17
+ append(repo: string, taskHash: string, inputsHash: string, executionId: string, stream: 'stdout' | 'stderr', data: string): Promise<void>;
18
+ read(repo: string, taskHash: string, inputsHash: string, executionId: string, stream: 'stdout' | 'stderr', options?: {
19
+ offset?: number;
20
+ limit?: number;
21
+ }): Promise<LogChunk>;
22
+ }
23
+ //# sourceMappingURL=LocalLogStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LocalLogStore.d.ts","sourceRoot":"","sources":["../../../../src/storage/local/LocalLogStore.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAG3D;;;;;;;;GAQG;AACH,qBAAa,aAAc,YAAW,QAAQ;IAC5C,OAAO,CAAC,OAAO;IAWT,MAAM,CACV,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,QAAQ,GAAG,QAAQ,EAC3B,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC;IAQV,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,QAAQ,GAAG,QAAQ,EAC3B,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAC5C,OAAO,CAAC,QAAQ,CAAC;CAwCrB"}