@elaraai/e3-types 0.0.1-beta.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 +31 -0
- package/README.md +33 -0
- package/dist/src/dataset.d.ts +109 -0
- package/dist/src/dataset.d.ts.map +1 -0
- package/dist/src/dataset.js +96 -0
- package/dist/src/dataset.js.map +1 -0
- package/dist/src/execution.d.ts +80 -0
- package/dist/src/execution.d.ts.map +1 -0
- package/dist/src/execution.js +79 -0
- package/dist/src/execution.js.map +1 -0
- package/dist/src/index.d.ts +29 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +39 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/package.d.ts +210 -0
- package/dist/src/package.d.ts.map +1 -0
- package/dist/src/package.js +86 -0
- package/dist/src/package.js.map +1 -0
- package/dist/src/structure.d.ts +254 -0
- package/dist/src/structure.d.ts.map +1 -0
- package/dist/src/structure.js +236 -0
- package/dist/src/structure.js.map +1 -0
- package/dist/src/task.d.ts +66 -0
- package/dist/src/task.d.ts.map +1 -0
- package/dist/src/task.js +61 -0
- package/dist/src/task.js.map +1 -0
- package/dist/src/workspace.d.ts +49 -0
- package/dist/src/workspace.d.ts.map +1 -0
- package/dist/src/workspace.js +48 -0
- package/dist/src/workspace.js.map +1 -0
- package/package.json +30 -0
- package/src/dataset.ts +126 -0
- package/src/execution.ts +91 -0
- package/src/index.ts +78 -0
- package/src/package.ts +100 -0
- package/src/structure.ts +270 -0
- package/src/task.ts +66 -0
- package/src/workspace.ts +52 -0
- package/tsconfig.json +21 -0
package/src/execution.ts
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Elara AI Pty Ltd
|
|
3
|
+
* Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Execution status type definitions.
|
|
8
|
+
*
|
|
9
|
+
* An execution represents a single run of a task with specific inputs.
|
|
10
|
+
* Executions are stored at: executions/<taskHash>/<inputsHash>/
|
|
11
|
+
*
|
|
12
|
+
* The status file tracks:
|
|
13
|
+
* - For running: process identification for crash detection
|
|
14
|
+
* - For success: output hash and timing
|
|
15
|
+
* - For failed: exit code and timing
|
|
16
|
+
* - For error: internal error message and timing
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
VariantType,
|
|
21
|
+
StructType,
|
|
22
|
+
ArrayType,
|
|
23
|
+
StringType,
|
|
24
|
+
IntegerType,
|
|
25
|
+
DateTimeType,
|
|
26
|
+
ValueTypeOf,
|
|
27
|
+
} from '@elaraai/east';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Execution status stored in executions/<taskHash>/<inputsHash>/status.beast2
|
|
31
|
+
*
|
|
32
|
+
* A variant type representing the four possible states of an execution:
|
|
33
|
+
*
|
|
34
|
+
* - `running`: Task has been launched but not yet completed
|
|
35
|
+
* - `success`: Task ran and returned exit code 0
|
|
36
|
+
* - `failed`: Task ran and returned non-zero exit code
|
|
37
|
+
* - `error`: e3 execution engine had an internal error (runner not found, output missing, etc.)
|
|
38
|
+
*
|
|
39
|
+
* The `running` state includes process identification fields (pid, pidStartTime, bootId)
|
|
40
|
+
* to enable detection of crashed executions. See design/e3-execution.md for details.
|
|
41
|
+
*/
|
|
42
|
+
export const ExecutionStatusType = VariantType({
|
|
43
|
+
/** Task has been launched but not yet completed */
|
|
44
|
+
running: StructType({
|
|
45
|
+
/** Input dataset hashes */
|
|
46
|
+
inputHashes: ArrayType(StringType),
|
|
47
|
+
/** When execution started */
|
|
48
|
+
startedAt: DateTimeType,
|
|
49
|
+
/** Process ID of the runner */
|
|
50
|
+
pid: IntegerType,
|
|
51
|
+
/** Process start time in jiffies since boot (from /proc/<pid>/stat field 22) */
|
|
52
|
+
pidStartTime: IntegerType,
|
|
53
|
+
/** System boot ID (from /proc/sys/kernel/random/boot_id) */
|
|
54
|
+
bootId: StringType,
|
|
55
|
+
}),
|
|
56
|
+
/** Task ran and returned exit code 0 */
|
|
57
|
+
success: StructType({
|
|
58
|
+
/** Input dataset hashes */
|
|
59
|
+
inputHashes: ArrayType(StringType),
|
|
60
|
+
/** Hash of the output dataset */
|
|
61
|
+
outputHash: StringType,
|
|
62
|
+
/** When execution started */
|
|
63
|
+
startedAt: DateTimeType,
|
|
64
|
+
/** When execution completed */
|
|
65
|
+
completedAt: DateTimeType,
|
|
66
|
+
}),
|
|
67
|
+
/** Task ran and returned non-zero exit code */
|
|
68
|
+
failed: StructType({
|
|
69
|
+
/** Input dataset hashes */
|
|
70
|
+
inputHashes: ArrayType(StringType),
|
|
71
|
+
/** When execution started */
|
|
72
|
+
startedAt: DateTimeType,
|
|
73
|
+
/** When execution completed */
|
|
74
|
+
completedAt: DateTimeType,
|
|
75
|
+
/** Process exit code */
|
|
76
|
+
exitCode: IntegerType,
|
|
77
|
+
}),
|
|
78
|
+
/** e3 execution engine had an internal error */
|
|
79
|
+
error: StructType({
|
|
80
|
+
/** Input dataset hashes */
|
|
81
|
+
inputHashes: ArrayType(StringType),
|
|
82
|
+
/** When execution started */
|
|
83
|
+
startedAt: DateTimeType,
|
|
84
|
+
/** When execution completed */
|
|
85
|
+
completedAt: DateTimeType,
|
|
86
|
+
/** Error message describing what went wrong */
|
|
87
|
+
message: StringType,
|
|
88
|
+
}),
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
export type ExecutionStatus = ValueTypeOf<typeof ExecutionStatusType>;
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Elara AI Pty Ltd
|
|
3
|
+
* Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* e3-types: Shared type definitions for e3 (East Execution Engine)
|
|
8
|
+
*
|
|
9
|
+
* This package defines the East types used for serializing e3 objects:
|
|
10
|
+
* - Data references and tree structures
|
|
11
|
+
* - Task definitions (command IR, input/output paths)
|
|
12
|
+
* - Package objects
|
|
13
|
+
* - Data structure and paths
|
|
14
|
+
* - Workspace state
|
|
15
|
+
* - Execution status
|
|
16
|
+
*
|
|
17
|
+
* Terminology:
|
|
18
|
+
* - **Dataset**: A location holding a value (leaf node)
|
|
19
|
+
* - **Tree**: A location containing other locations (branch node)
|
|
20
|
+
* - **Structure**: The shape of the data tree
|
|
21
|
+
* - **Task**: A computation with command IR and input/output paths
|
|
22
|
+
* - **Path**: An address in the data tree
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
// Data references and trees
|
|
26
|
+
export {
|
|
27
|
+
DataRefType,
|
|
28
|
+
type DataRef,
|
|
29
|
+
unassignedRef,
|
|
30
|
+
nullRef,
|
|
31
|
+
DataTreeType,
|
|
32
|
+
} from './dataset.js';
|
|
33
|
+
|
|
34
|
+
// Task definitions
|
|
35
|
+
export {
|
|
36
|
+
TaskObjectType,
|
|
37
|
+
type TaskObject,
|
|
38
|
+
} from './task.js';
|
|
39
|
+
|
|
40
|
+
// Data structure and paths
|
|
41
|
+
export {
|
|
42
|
+
StructureType,
|
|
43
|
+
type Structure,
|
|
44
|
+
PathSegmentType,
|
|
45
|
+
type PathSegment,
|
|
46
|
+
TreePathType,
|
|
47
|
+
type TreePath,
|
|
48
|
+
type ParsePathResult,
|
|
49
|
+
treePath,
|
|
50
|
+
pathToString,
|
|
51
|
+
parsePath,
|
|
52
|
+
// Backwards compatibility
|
|
53
|
+
DatasetSchemaType,
|
|
54
|
+
type DatasetSchema,
|
|
55
|
+
} from './structure.js';
|
|
56
|
+
|
|
57
|
+
// Package objects
|
|
58
|
+
export {
|
|
59
|
+
PackageDataType,
|
|
60
|
+
type PackageData,
|
|
61
|
+
PackageObjectType,
|
|
62
|
+
type PackageObject,
|
|
63
|
+
// Backwards compatibility
|
|
64
|
+
PackageDatasetsType,
|
|
65
|
+
type PackageDatasets,
|
|
66
|
+
} from './package.js';
|
|
67
|
+
|
|
68
|
+
// Workspace state
|
|
69
|
+
export {
|
|
70
|
+
WorkspaceStateType,
|
|
71
|
+
type WorkspaceState,
|
|
72
|
+
} from './workspace.js';
|
|
73
|
+
|
|
74
|
+
// Execution status
|
|
75
|
+
export {
|
|
76
|
+
ExecutionStatusType,
|
|
77
|
+
type ExecutionStatus,
|
|
78
|
+
} from './execution.js';
|
package/src/package.ts
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Elara AI Pty Ltd
|
|
3
|
+
* Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Package object types for e3.
|
|
8
|
+
*
|
|
9
|
+
* A package bundles everything needed to run computations:
|
|
10
|
+
* tasks and data structure with initial values.
|
|
11
|
+
*
|
|
12
|
+
* Terminology:
|
|
13
|
+
* - **Package**: A deployable bundle of tasks and data structure
|
|
14
|
+
* - **Structure**: The shape of the data tree
|
|
15
|
+
* - **Task**: A computation with input/output paths (stored separately)
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { StructType, StringType, DictType, ValueTypeOf } from '@elaraai/east';
|
|
19
|
+
import { StructureType } from './structure.js';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Data configuration in a package.
|
|
23
|
+
*
|
|
24
|
+
* Defines the structure (which paths are datasets vs trees)
|
|
25
|
+
* and initial values (root tree hash).
|
|
26
|
+
*
|
|
27
|
+
* @remarks
|
|
28
|
+
* - `structure`: Defines which paths are datasets vs trees (recursive)
|
|
29
|
+
* - `value`: Hash of the root tree object in the object store
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```ts
|
|
33
|
+
* const data: PackageData = {
|
|
34
|
+
* structure: variant('struct', new Map([
|
|
35
|
+
* ['inputs', variant('struct', new Map([
|
|
36
|
+
* ['sales', variant('value', variant('Array', variant('Integer', null)))],
|
|
37
|
+
* ]))],
|
|
38
|
+
* ['tasks', variant('struct', new Map([
|
|
39
|
+
* ['process', variant('struct', new Map([
|
|
40
|
+
* ['function_ir', variant('value', ...)],
|
|
41
|
+
* ['output', variant('value', variant('Integer', null))],
|
|
42
|
+
* ]))],
|
|
43
|
+
* ]))],
|
|
44
|
+
* ])),
|
|
45
|
+
* value: 'abc123...', // Hash of initial tree
|
|
46
|
+
* };
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export const PackageDataType = StructType({
|
|
50
|
+
/** Structure defining tree shape (what's a group vs dataset) */
|
|
51
|
+
structure: StructureType,
|
|
52
|
+
/** Hash of the root tree object containing initial/default values */
|
|
53
|
+
value: StringType,
|
|
54
|
+
});
|
|
55
|
+
export type PackageDataType = typeof PackageDataType;
|
|
56
|
+
|
|
57
|
+
export type PackageData = ValueTypeOf<typeof PackageDataType>;
|
|
58
|
+
|
|
59
|
+
// Backwards compatibility alias
|
|
60
|
+
/** @deprecated Use PackageDataType instead */
|
|
61
|
+
export const PackageDatasetsType = PackageDataType;
|
|
62
|
+
/** @deprecated Use PackageData instead */
|
|
63
|
+
export type PackageDatasetsType = PackageDataType;
|
|
64
|
+
/** @deprecated Use PackageData instead */
|
|
65
|
+
export type PackageDatasets = PackageData;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Package object stored in the object store.
|
|
69
|
+
*
|
|
70
|
+
* Packages are the unit of distribution and deployment in e3.
|
|
71
|
+
* They are immutable and content-addressed by their hash.
|
|
72
|
+
*
|
|
73
|
+
* @remarks
|
|
74
|
+
* - `tasks`: Maps task names to task object hashes. Each task object
|
|
75
|
+
* contains runner, input paths, and output path.
|
|
76
|
+
* - `data`: The structure and initial values for the data tree.
|
|
77
|
+
*
|
|
78
|
+
* Package identity (name/version) is determined by the path in the
|
|
79
|
+
* bundle's `packages/<name>/<version>` directory structure.
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* ```ts
|
|
83
|
+
* const pkg: PackageObject = {
|
|
84
|
+
* tasks: new Map([['process', 'abc123...']]), // hash of TaskObject
|
|
85
|
+
* data: {
|
|
86
|
+
* structure: variant('struct', new Map([...])),
|
|
87
|
+
* value: 'def456...', // hash of root tree
|
|
88
|
+
* },
|
|
89
|
+
* };
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
export const PackageObjectType = StructType({
|
|
93
|
+
/** Tasks defined in this package: name -> task object hash */
|
|
94
|
+
tasks: DictType(StringType, StringType),
|
|
95
|
+
/** Data structure and initial values */
|
|
96
|
+
data: PackageDataType,
|
|
97
|
+
});
|
|
98
|
+
export type PackageObjectType = typeof PackageObjectType;
|
|
99
|
+
|
|
100
|
+
export type PackageObject = ValueTypeOf<typeof PackageObjectType>;
|
package/src/structure.ts
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Elara AI Pty Ltd
|
|
3
|
+
* Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Data structure and path types for e3.
|
|
8
|
+
*
|
|
9
|
+
* Terminology:
|
|
10
|
+
* - **Dataset**: A location holding a value (leaf node in the data tree)
|
|
11
|
+
* - **Tree**: A location containing datasets or nested trees (branch node)
|
|
12
|
+
* - **Structure**: The shape of the data tree (what trees/datasets exist and their types)
|
|
13
|
+
* - **Path**: An address pointing to a dataset or tree
|
|
14
|
+
*
|
|
15
|
+
* Paths use East's keypath syntax:
|
|
16
|
+
* - `.field` for struct field access (backtick-quoted for special chars)
|
|
17
|
+
* - `[N]` for array index access (future)
|
|
18
|
+
* - `[key]` for dict key lookup (future)
|
|
19
|
+
*
|
|
20
|
+
* @see East serialization docs for keypath syntax details
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { VariantType, StringType, ArrayType, DictType, RecursiveType, ValueTypeOf, printIdentifier, variant, EastTypeType } from '@elaraai/east';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Structure definition for a data tree node.
|
|
27
|
+
*
|
|
28
|
+
* Defines the shape of the data tree - which paths are datasets (hold values)
|
|
29
|
+
* and which are trees (hold other nodes).
|
|
30
|
+
*
|
|
31
|
+
* @remarks
|
|
32
|
+
* - `value`: A dataset - holds a typed value. The type is an `EastTypeValue`.
|
|
33
|
+
* - `struct`: A tree - has named children, each with its own structure.
|
|
34
|
+
*
|
|
35
|
+
* MVP only supports struct trees. Future: array, dict, variant trees.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* // A dataset holding an Integer value
|
|
40
|
+
* const dataset: Structure = variant('value', variant('Integer', null));
|
|
41
|
+
*
|
|
42
|
+
* // A tree with named children
|
|
43
|
+
* const tree: Structure = variant('struct', new Map([
|
|
44
|
+
* ['count', variant('value', variant('Integer', null))],
|
|
45
|
+
* ['items', variant('value', variant('Array', variant('String', null)))],
|
|
46
|
+
* ]));
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export const StructureType = RecursiveType(self => VariantType({
|
|
50
|
+
/** Dataset: East type of the value (homoiconic EastTypeValue) */
|
|
51
|
+
value: EastTypeType,
|
|
52
|
+
/** Struct tree: named children mapping to child structures */
|
|
53
|
+
struct: DictType(StringType, self),
|
|
54
|
+
}));
|
|
55
|
+
export type StructureType = typeof StructureType;
|
|
56
|
+
|
|
57
|
+
export type Structure = ValueTypeOf<typeof StructureType>;
|
|
58
|
+
|
|
59
|
+
// Backwards compatibility alias
|
|
60
|
+
/** @deprecated Use StructureType instead */
|
|
61
|
+
export const DatasetSchemaType = StructureType;
|
|
62
|
+
/** @deprecated Use Structure instead */
|
|
63
|
+
export type DatasetSchemaType = StructureType;
|
|
64
|
+
/** @deprecated Use Structure instead */
|
|
65
|
+
export type DatasetSchema = Structure;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Path segment for navigating data trees.
|
|
69
|
+
*
|
|
70
|
+
* Uses East keypath syntax for consistency:
|
|
71
|
+
* - `field`: Struct field access (rendered as `.field` or `` .`field` `` if quoted)
|
|
72
|
+
* - `index`: Array element access (rendered as `[N]`) - future
|
|
73
|
+
* - `key`: Dict key lookup (rendered as `[key]`) - future
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```ts
|
|
77
|
+
* // Struct field access
|
|
78
|
+
* const segment: PathSegment = variant('field', 'sales');
|
|
79
|
+
*
|
|
80
|
+
* // Array index (future)
|
|
81
|
+
* const segment: PathSegment = variant('index', 0n);
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
export const PathSegmentType = VariantType({
|
|
85
|
+
/** Struct field access by name */
|
|
86
|
+
field: StringType,
|
|
87
|
+
// Future: case: StringType for variant case identifiers
|
|
88
|
+
// Future: index: IntegerType for array access
|
|
89
|
+
// Future: key: StringType (or polymorphic) for dict access
|
|
90
|
+
});
|
|
91
|
+
export type PathSegmentType = typeof PathSegmentType;
|
|
92
|
+
|
|
93
|
+
export type PathSegment = ValueTypeOf<typeof PathSegmentType>;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Path: sequence of segments identifying a location in a data tree.
|
|
97
|
+
*
|
|
98
|
+
* Paths point to either a dataset (leaf) or a tree (branch).
|
|
99
|
+
* Used by tasks to specify where inputs come from and where outputs go.
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```ts
|
|
103
|
+
* // Path to .inputs.sales.data
|
|
104
|
+
* const path: TreePath = [
|
|
105
|
+
* variant('field', 'inputs'),
|
|
106
|
+
* variant('field', 'sales'),
|
|
107
|
+
* variant('field', 'data'),
|
|
108
|
+
* ];
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
export const TreePathType = ArrayType(PathSegmentType);
|
|
112
|
+
export type TreePathType = typeof TreePathType;
|
|
113
|
+
|
|
114
|
+
export type TreePath = ValueTypeOf<typeof TreePathType>;
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Converts a path to East keypath string representation.
|
|
118
|
+
*
|
|
119
|
+
* Uses East's keypath syntax: `.field` for simple identifiers,
|
|
120
|
+
* `` .`field` `` for identifiers needing quoting.
|
|
121
|
+
*
|
|
122
|
+
* @param path - The path to convert
|
|
123
|
+
* @returns A keypath string (e.g., ".inputs.sales" or ".inputs.`my/field`")
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* ```ts
|
|
127
|
+
* const path = treePath('inputs', 'sales');
|
|
128
|
+
* pathToString(path); // ".inputs.sales"
|
|
129
|
+
*
|
|
130
|
+
* const path2 = treePath('inputs', 'my/field');
|
|
131
|
+
* pathToString(path2); // ".inputs.`my/field`"
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
134
|
+
export function pathToString(path: TreePath): string {
|
|
135
|
+
return path.map(segment => {
|
|
136
|
+
if (segment.type === 'field') {
|
|
137
|
+
return '.' + printIdentifier(segment.value);
|
|
138
|
+
} else {
|
|
139
|
+
throw new Error(`pathToString: unsupported path segment type: ${segment.type}`);
|
|
140
|
+
}
|
|
141
|
+
}).join('');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Result of parsing a path with structure validation.
|
|
146
|
+
*/
|
|
147
|
+
export interface ParsePathResult {
|
|
148
|
+
/** The parsed path segments */
|
|
149
|
+
path: TreePath;
|
|
150
|
+
/** The structure at the path location */
|
|
151
|
+
structure: Structure;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Parses an East keypath string into a path, validating against the structure.
|
|
156
|
+
*
|
|
157
|
+
* Supports `.field` syntax for struct field access.
|
|
158
|
+
* Backtick-quoted identifiers (`` .`field` ``) are supported for special chars.
|
|
159
|
+
*
|
|
160
|
+
* @param pathStr - A keypath string (e.g., ".inputs.sales")
|
|
161
|
+
* @param structure - The root structure to validate against
|
|
162
|
+
* @returns The parsed path and the structure at that location
|
|
163
|
+
*
|
|
164
|
+
* @throws {Error} If a field doesn't exist in the structure or path descends into a dataset
|
|
165
|
+
*
|
|
166
|
+
* @remarks
|
|
167
|
+
* - Empty string returns empty path (root) with the root structure
|
|
168
|
+
* - Path must start with `.` (no leading slash)
|
|
169
|
+
* - Backtick escaping: `` \` `` for literal backtick, `\\` for backslash
|
|
170
|
+
* - Future: will disambiguate variant cases from struct fields using structure
|
|
171
|
+
* - Future: will use structure to parse dict keys with correct type
|
|
172
|
+
*
|
|
173
|
+
* @example
|
|
174
|
+
* ```ts
|
|
175
|
+
* const structure = variant('struct', new Map([
|
|
176
|
+
* ['inputs', variant('struct', new Map([
|
|
177
|
+
* ['sales', variant('value', variant('Integer', null))],
|
|
178
|
+
* ]))],
|
|
179
|
+
* ]));
|
|
180
|
+
*
|
|
181
|
+
* const { path, structure: leafStructure } = parsePath('.inputs.sales', structure);
|
|
182
|
+
* // path = [field('inputs'), field('sales')]
|
|
183
|
+
* // leafStructure = variant('value', variant('Integer', null))
|
|
184
|
+
* ```
|
|
185
|
+
*/
|
|
186
|
+
export function parsePath(pathStr: string, structure: Structure): ParsePathResult {
|
|
187
|
+
if (pathStr === '') return { path: [], structure };
|
|
188
|
+
|
|
189
|
+
const segments: TreePath = [];
|
|
190
|
+
let currentStructure = structure;
|
|
191
|
+
let pos = 0;
|
|
192
|
+
|
|
193
|
+
while (pos < pathStr.length) {
|
|
194
|
+
if (pathStr[pos] === '.') {
|
|
195
|
+
pos++;
|
|
196
|
+
|
|
197
|
+
// Parse identifier (TODO: export parseIdentifier from east package)
|
|
198
|
+
let fieldName: string;
|
|
199
|
+
|
|
200
|
+
// Check for backtick-quoted identifier
|
|
201
|
+
if (pos < pathStr.length && pathStr[pos] === '`') {
|
|
202
|
+
pos++;
|
|
203
|
+
fieldName = '';
|
|
204
|
+
while (pos < pathStr.length && pathStr[pos] !== '`') {
|
|
205
|
+
if (pathStr[pos] === '\\' && pos + 1 < pathStr.length) {
|
|
206
|
+
// Escape sequence
|
|
207
|
+
pos++;
|
|
208
|
+
fieldName += pathStr[pos];
|
|
209
|
+
} else {
|
|
210
|
+
fieldName += pathStr[pos];
|
|
211
|
+
}
|
|
212
|
+
pos++;
|
|
213
|
+
}
|
|
214
|
+
if (pos < pathStr.length && pathStr[pos] === '`') {
|
|
215
|
+
pos++; // consume closing backtick
|
|
216
|
+
}
|
|
217
|
+
} else {
|
|
218
|
+
// Simple identifier: [a-zA-Z_][a-zA-Z0-9_]*
|
|
219
|
+
fieldName = '';
|
|
220
|
+
while (pos < pathStr.length && /[a-zA-Z0-9_]/.test(pathStr[pos]!)) {
|
|
221
|
+
fieldName += pathStr[pos];
|
|
222
|
+
pos++;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (fieldName.length === 0) {
|
|
227
|
+
throw new Error(`parsePath: expected identifier after '.' at position ${pos}`);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Validate against structure
|
|
231
|
+
if (currentStructure.type === 'value') {
|
|
232
|
+
throw new Error(`parsePath: cannot descend into dataset at '${pathToString(segments)}'`);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// currentStructure.type === 'struct' (only other option after 'value' check)
|
|
236
|
+
const fields = currentStructure.value;
|
|
237
|
+
const childStructure = fields.get(fieldName);
|
|
238
|
+
if (childStructure === undefined) {
|
|
239
|
+
const available = [...fields.keys()].map(k => printIdentifier(k)).join(', ');
|
|
240
|
+
throw new Error(`parsePath: field '${fieldName}' not found at '${pathToString(segments)}'. Available: ${available}`);
|
|
241
|
+
}
|
|
242
|
+
segments.push(variant('field', fieldName));
|
|
243
|
+
currentStructure = childStructure;
|
|
244
|
+
} else {
|
|
245
|
+
throw new Error(`parsePath: unexpected character at position ${pos}: '${pathStr[pos]}'`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return { path: segments, structure: currentStructure };
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Creates a path from field names, validating against the structure.
|
|
254
|
+
*
|
|
255
|
+
* Convenience function for the common case of navigating through struct fields.
|
|
256
|
+
*
|
|
257
|
+
* @param structure - The root structure to validate against
|
|
258
|
+
* @param fields - Field names to include in the path
|
|
259
|
+
* @returns The parsed path and the structure at that location
|
|
260
|
+
*
|
|
261
|
+
* @throws {Error} If a field doesn't exist in the structure
|
|
262
|
+
*
|
|
263
|
+
* @example
|
|
264
|
+
* ```ts
|
|
265
|
+
* const { path, structure: leafStructure } = treePath(rootStructure, 'inputs', 'sales');
|
|
266
|
+
* ```
|
|
267
|
+
*/
|
|
268
|
+
export function treePath(structure: Structure, ...fields: string[]): ParsePathResult {
|
|
269
|
+
return parsePath('.' + fields.map(printIdentifier).join('.'), structure);
|
|
270
|
+
}
|
package/src/task.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Elara AI Pty Ltd
|
|
3
|
+
* Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Task object types for e3.
|
|
8
|
+
*
|
|
9
|
+
* A task object defines a complete executable unit: the command IR that
|
|
10
|
+
* generates the exec args, where to read inputs from, and where to write output.
|
|
11
|
+
*
|
|
12
|
+
* Task objects are stored in the object store and referenced by packages.
|
|
13
|
+
* They are content-addressed, enabling deduplication and memoization.
|
|
14
|
+
*
|
|
15
|
+
* Input and output types are inferred from the package's structure at the
|
|
16
|
+
* specified paths - the task just references locations, not types.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { StructType, StringType, ArrayType, ValueTypeOf } from '@elaraai/east';
|
|
20
|
+
import { TreePathType } from './structure.js';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Task object stored in the object store.
|
|
24
|
+
*
|
|
25
|
+
* A task is a complete executable unit that reads from input dataset paths
|
|
26
|
+
* and writes to an output dataset path. The commandIr is evaluated at runtime
|
|
27
|
+
* to produce the exec args.
|
|
28
|
+
*
|
|
29
|
+
* @remarks
|
|
30
|
+
* - `commandIr`: Hash of East IR object that produces exec args
|
|
31
|
+
* - IR signature: (inputs: Array<String>, output: String) -> Array<String>
|
|
32
|
+
* - `inputs` are paths to staged input .beast2 files
|
|
33
|
+
* - `output` is the path where output should be written
|
|
34
|
+
* - Returns array of strings to exec (e.g., ["sh", "-c", "python ..."])
|
|
35
|
+
* - `inputs`: Paths to input datasets in the data tree
|
|
36
|
+
* - `output`: Path to the output dataset in the data tree
|
|
37
|
+
*
|
|
38
|
+
* Types are not stored in the task - they are inferred from the package's
|
|
39
|
+
* structure at the specified paths. This keeps tasks simple and avoids
|
|
40
|
+
* redundant type information.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* import { variant } from '@elaraai/east';
|
|
45
|
+
*
|
|
46
|
+
* // Task with command IR that generates: ["sh", "-c", "python script.py <input> <output>"]
|
|
47
|
+
* const task: TaskObject = {
|
|
48
|
+
* commandIr: '5e7a3b...', // hash of compiled IR
|
|
49
|
+
* inputs: [
|
|
50
|
+
* [variant('field', 'inputs'), variant('field', 'sales')],
|
|
51
|
+
* ],
|
|
52
|
+
* output: [variant('field', 'tasks'), variant('field', 'train'), variant('field', 'output')],
|
|
53
|
+
* };
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export const TaskObjectType = StructType({
|
|
57
|
+
/** Hash of East IR that generates exec args: (inputs, output) -> Array<String> */
|
|
58
|
+
commandIr: StringType,
|
|
59
|
+
/** Input paths: where to read each input dataset from the data tree */
|
|
60
|
+
inputs: ArrayType(TreePathType),
|
|
61
|
+
/** Output path: where to write the output dataset in the data tree */
|
|
62
|
+
output: TreePathType,
|
|
63
|
+
});
|
|
64
|
+
export type TaskObjectType = typeof TaskObjectType;
|
|
65
|
+
|
|
66
|
+
export type TaskObject = ValueTypeOf<typeof TaskObjectType>;
|
package/src/workspace.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Elara AI Pty Ltd
|
|
3
|
+
* Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Workspace state type definitions.
|
|
8
|
+
*
|
|
9
|
+
* A workspace is a mutable working copy of a package. The state tracks:
|
|
10
|
+
* - Which package was deployed (immutable reference via hash)
|
|
11
|
+
* - When the deployment occurred
|
|
12
|
+
* - Current root data tree hash
|
|
13
|
+
* - When the root was last updated
|
|
14
|
+
*
|
|
15
|
+
* State file location: workspaces/<name>/state.beast2
|
|
16
|
+
* No state file = workspace exists but not yet deployed.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { StructType, StringType, DateTimeType, ValueTypeOf } from '@elaraai/east';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Workspace state stored in workspaces/<name>/state.beast2
|
|
23
|
+
*
|
|
24
|
+
* Contains both deployment info and current data root in a single
|
|
25
|
+
* atomic unit to ensure consistency.
|
|
26
|
+
*
|
|
27
|
+
* Future audit trail support:
|
|
28
|
+
* When we implement full audit trail, this state will move to the object
|
|
29
|
+
* store (content-addressed) with a ref file pointing to current state hash.
|
|
30
|
+
* Additional fields for the Merkle chain:
|
|
31
|
+
*
|
|
32
|
+
* previousStateHash: NullableType(StringType), // null for initial deploy
|
|
33
|
+
* message: StringType, // "deployed package X", "user Y wrote to dataset Z"
|
|
34
|
+
*
|
|
35
|
+
* This gives a complete history of workspace changes, similar to git commits.
|
|
36
|
+
*/
|
|
37
|
+
export const WorkspaceStateType = StructType({
|
|
38
|
+
/** Name of the deployed package */
|
|
39
|
+
packageName: StringType,
|
|
40
|
+
/** Version of the deployed package */
|
|
41
|
+
packageVersion: StringType,
|
|
42
|
+
/** Hash of the package object at deploy time (immutable reference) */
|
|
43
|
+
packageHash: StringType,
|
|
44
|
+
/** UTC datetime when the package was deployed */
|
|
45
|
+
deployedAt: DateTimeType,
|
|
46
|
+
/** Current root data tree hash */
|
|
47
|
+
rootHash: StringType,
|
|
48
|
+
/** UTC datetime when root was last updated */
|
|
49
|
+
rootUpdatedAt: DateTimeType,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
export type WorkspaceState = ValueTypeOf<typeof WorkspaceStateType>;
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"lib": ["ES2022"],
|
|
6
|
+
"moduleResolution": "NodeNext",
|
|
7
|
+
"outDir": "./dist",
|
|
8
|
+
"rootDir": "./",
|
|
9
|
+
"declaration": true,
|
|
10
|
+
"declarationMap": true,
|
|
11
|
+
"sourceMap": true,
|
|
12
|
+
"strict": true,
|
|
13
|
+
"esModuleInterop": true,
|
|
14
|
+
"skipLibCheck": true,
|
|
15
|
+
"forceConsistentCasingInFileNames": true,
|
|
16
|
+
"resolveJsonModule": true,
|
|
17
|
+
"composite": true
|
|
18
|
+
},
|
|
19
|
+
"include": ["src/**/*"],
|
|
20
|
+
"exclude": ["node_modules", "dist"]
|
|
21
|
+
}
|