@elaraai/e3 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 +73 -0
- package/dist/src/export.d.ts +31 -0
- package/dist/src/export.d.ts.map +1 -0
- package/dist/src/export.js +236 -0
- package/dist/src/export.js.map +1 -0
- package/dist/src/export.spec.d.ts +6 -0
- package/dist/src/export.spec.d.ts.map +1 -0
- package/dist/src/export.spec.js +176 -0
- package/dist/src/export.spec.js.map +1 -0
- package/dist/src/index.d.ts +69 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +30 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/input.d.ts +40 -0
- package/dist/src/input.d.ts.map +1 -0
- package/dist/src/input.js +49 -0
- package/dist/src/input.js.map +1 -0
- package/dist/src/package.d.ts +41 -0
- package/dist/src/package.d.ts.map +1 -0
- package/dist/src/package.js +111 -0
- package/dist/src/package.js.map +1 -0
- package/dist/src/sha256.d.ts +66 -0
- package/dist/src/sha256.d.ts.map +1 -0
- package/dist/src/sha256.js +106 -0
- package/dist/src/sha256.js.map +1 -0
- package/dist/src/sha256.spec.d.ts +6 -0
- package/dist/src/sha256.spec.d.ts.map +1 -0
- package/dist/src/sha256.spec.js +157 -0
- package/dist/src/sha256.spec.js.map +1 -0
- package/dist/src/task.d.ts +65 -0
- package/dist/src/task.d.ts.map +1 -0
- package/dist/src/task.js +192 -0
- package/dist/src/task.js.map +1 -0
- package/dist/src/types.d.ts +117 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +6 -0
- package/dist/src/types.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +36 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
Copyright (c) 2025 Elara AI Pty Ltd
|
|
2
|
+
|
|
3
|
+
# Dual License
|
|
4
|
+
|
|
5
|
+
This software is available under two licenses. You may choose which license applies to your use:
|
|
6
|
+
|
|
7
|
+
## Option 1: AGPL-3.0 (Open Source)
|
|
8
|
+
|
|
9
|
+
You may use, modify, and distribute this software under the terms of the GNU Affero General Public License v3.0.
|
|
10
|
+
|
|
11
|
+
This requires that if you use this software in a network service, you must make your complete source code available under AGPL-3.0.
|
|
12
|
+
|
|
13
|
+
Full text: https://www.gnu.org/licenses/agpl-3.0.html
|
|
14
|
+
|
|
15
|
+
## Option 2: Commercial License
|
|
16
|
+
|
|
17
|
+
If you wish to use this software without the source code disclosure requirements of AGPL-3.0, you must obtain a commercial license from Elara AI Pty Ltd.
|
|
18
|
+
|
|
19
|
+
Contact: support@elara.ai
|
|
20
|
+
|
|
21
|
+
## Contributions
|
|
22
|
+
|
|
23
|
+
Contributions are welcome. By submitting a pull request, you agree to license your contribution under both AGPL-3.0 and our commercial license terms.
|
|
24
|
+
|
|
25
|
+
## Governing Law
|
|
26
|
+
|
|
27
|
+
This license is governed by the laws of New South Wales, Australia.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
*Elara AI Pty Ltd*
|
package/README.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# @elaraai/e3
|
|
2
|
+
|
|
3
|
+
SDK for authoring e3 packages.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @elaraai/e3
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Example
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import e3 from '@elaraai/e3';
|
|
15
|
+
import { StringType, East } from '@elaraai/east';
|
|
16
|
+
|
|
17
|
+
// Define input datasets with default values
|
|
18
|
+
const input_name = e3.input('name', StringType, 'World');
|
|
19
|
+
const input_prefix = e3.input('prefix', StringType, 'Hello');
|
|
20
|
+
|
|
21
|
+
// Define a task that combines inputs
|
|
22
|
+
const greet = e3.task(
|
|
23
|
+
'greet',
|
|
24
|
+
[input_prefix, input_name],
|
|
25
|
+
East.function(
|
|
26
|
+
[StringType, StringType],
|
|
27
|
+
StringType,
|
|
28
|
+
($, prefix, name) => East.str`${prefix}, ${name}!`
|
|
29
|
+
)
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
// Chain tasks - output of one feeds into the next
|
|
33
|
+
const shout = e3.task(
|
|
34
|
+
'shout',
|
|
35
|
+
[greet.output], // reads from previous task's output
|
|
36
|
+
East.function(
|
|
37
|
+
[StringType],
|
|
38
|
+
StringType,
|
|
39
|
+
($, greeting) => greeting.upperCase()
|
|
40
|
+
)
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
// Create and export the package
|
|
44
|
+
const pkg = e3.package('greeting-pkg', '1.0.0', shout);
|
|
45
|
+
await e3.export(pkg, 'dist/greeting-pkg-1.0.0.zip');
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
This creates a package with:
|
|
49
|
+
- `.inputs.name` - String input (default: "World")
|
|
50
|
+
- `.inputs.prefix` - String input (default: "Hello")
|
|
51
|
+
- `.tasks.greet` - Combines inputs into greeting
|
|
52
|
+
- `.tasks.shout` - Transforms greeting to uppercase
|
|
53
|
+
|
|
54
|
+
## API
|
|
55
|
+
|
|
56
|
+
- `e3.input(name, type, default)` - Define an input dataset
|
|
57
|
+
- `e3.task(name, inputs, fn)` - Define a task with East function
|
|
58
|
+
- `e3.package(name, version, task)` - Create a package (dependencies collected automatically)
|
|
59
|
+
- `e3.export(pkg, path)` - Export package to zip file
|
|
60
|
+
|
|
61
|
+
## Related Repos
|
|
62
|
+
|
|
63
|
+
- **[east](https://github.com/elaraai/east)** - East language core
|
|
64
|
+
- **[east-node](https://github.com/elaraai/east-node)** - Node.js runtime and platform functions
|
|
65
|
+
- **[east-py](https://github.com/elaraai/east-py)** - Python runtime and data science
|
|
66
|
+
|
|
67
|
+
## About Elara
|
|
68
|
+
|
|
69
|
+
e3 is developed by [Elara AI](https://elaraai.com/), an AI-powered platform that creates economic digital twins of businesses. e3 powers the execution layer of Elara solutions, enabling durable and efficient execution of East programs across multiple runtimes.
|
|
70
|
+
|
|
71
|
+
## License
|
|
72
|
+
|
|
73
|
+
Dual AGPL-3.0 / Commercial. See [LICENSE.md](./LICENSE.md).
|
|
@@ -0,0 +1,31 @@
|
|
|
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
|
+
import yazl from 'yazl';
|
|
6
|
+
import type { PackageDef } from './types.js';
|
|
7
|
+
/**
|
|
8
|
+
* Exports a package to a .zip bundle.
|
|
9
|
+
*
|
|
10
|
+
* The bundle can be imported into an e3 repository using `e3 package import`.
|
|
11
|
+
* It contains all objects needed for the package, plus a ref at
|
|
12
|
+
* `packages/<name>/<version>` pointing to the package object.
|
|
13
|
+
*
|
|
14
|
+
* @param pkg - The package to export
|
|
15
|
+
* @param outputPath - Path to write the .zip file
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* await e3.export(pkg, './my-package-1.0.0.zip');
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export declare function export_(pkg: PackageDef<Record<string, unknown>>, outputPath: string): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Adds an object to the zip file at the content-addressed path.
|
|
25
|
+
*
|
|
26
|
+
* @param zipfile - The zip file to add to
|
|
27
|
+
* @param data - The serialized object data (.beast2 format)
|
|
28
|
+
* @returns The SHA256 hash of the data (used as the object ID)
|
|
29
|
+
*/
|
|
30
|
+
export declare function addObject(zipfile: yazl.ZipFile, data: Buffer): string;
|
|
31
|
+
//# sourceMappingURL=export.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../../src/export.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAaH,OAAO,IAAI,MAAM,MAAM,CAAC;AAIxB,OAAO,KAAK,EAAE,UAAU,EAAe,MAAM,YAAY,CAAC;AAE1D;;;;;;;;;;;;;;GAcG;AAEH,wBAAsB,OAAO,CAAC,GAAG,EAAE,UAAU,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAkNzG;AAOD;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAKrE"}
|
|
@@ -0,0 +1,236 @@
|
|
|
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
|
+
* Export functionality for e3 packages.
|
|
7
|
+
*
|
|
8
|
+
* Exports a package definition to a .zip bundle that can be imported
|
|
9
|
+
* into an e3 repository. The bundle is a valid subset of an e3 repository:
|
|
10
|
+
* - `packages/<name>/<version>` - ref to package object hash
|
|
11
|
+
* - `objects/<ab>/<cdef...>.beast2` - content-addressed objects
|
|
12
|
+
*/
|
|
13
|
+
import * as fs from 'node:fs';
|
|
14
|
+
import { createHash } from 'node:crypto';
|
|
15
|
+
import yazl from 'yazl';
|
|
16
|
+
import { variant, encodeBeast2For, StructType, printIdentifier, SortedMap, toEastTypeValue, IRType } from '@elaraai/east';
|
|
17
|
+
import { DataRefType, PackageObjectType, TaskObjectType } from '@elaraai/e3-types';
|
|
18
|
+
/**
|
|
19
|
+
* Exports a package to a .zip bundle.
|
|
20
|
+
*
|
|
21
|
+
* The bundle can be imported into an e3 repository using `e3 package import`.
|
|
22
|
+
* It contains all objects needed for the package, plus a ref at
|
|
23
|
+
* `packages/<name>/<version>` pointing to the package object.
|
|
24
|
+
*
|
|
25
|
+
* @param pkg - The package to export
|
|
26
|
+
* @param outputPath - Path to write the .zip file
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* await e3.export(pkg, './my-package-1.0.0.zip');
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
// Named export_ to avoid conflict with reserved word
|
|
34
|
+
export async function export_(pkg, outputPath) {
|
|
35
|
+
const partialPath = `${outputPath}.partial`;
|
|
36
|
+
// Create zip file
|
|
37
|
+
const zipfile = new yazl.ZipFile();
|
|
38
|
+
// Initialize empty package object that we'll populate as we iterate
|
|
39
|
+
const tasks = new SortedMap(); // name -> task object hash
|
|
40
|
+
const refs = new Map(); // path -> data ref (variant of dataset or tree object hash)
|
|
41
|
+
const trees = new Map(); // path -> struct of paths
|
|
42
|
+
const structures = new Map(); // path -> structure (parallel to trees)
|
|
43
|
+
// Create root tree and structure as first entries
|
|
44
|
+
trees.set('', variant('struct', {}));
|
|
45
|
+
structures.set('', variant('struct', new SortedMap()));
|
|
46
|
+
// Iterate over package contents and write each object
|
|
47
|
+
// Contents are topologically sorted, so dependencies come before dependents
|
|
48
|
+
for (const item of pkg.contents) {
|
|
49
|
+
if (item.kind === "datatree") {
|
|
50
|
+
// Trees are accumulated in memory and written after
|
|
51
|
+
// Get parent tree
|
|
52
|
+
const parentPath = item.path.slice(0, -1).map(segment => {
|
|
53
|
+
if (segment.type !== 'field') {
|
|
54
|
+
throw new Error(`Unsupported tree path segment type in path ${item.path}: ${segment.type}`);
|
|
55
|
+
}
|
|
56
|
+
return `.${printIdentifier(segment.value)}`;
|
|
57
|
+
}).join('');
|
|
58
|
+
const parentTree = trees.get(parentPath);
|
|
59
|
+
if (!parentTree) {
|
|
60
|
+
throw new Error(`Missing parent tree at path: ${parentPath}`);
|
|
61
|
+
}
|
|
62
|
+
if (parentTree.type !== 'struct') {
|
|
63
|
+
throw new Error(`Parent tree at path ${parentPath} is not a struct: ${parentTree.type}`);
|
|
64
|
+
}
|
|
65
|
+
// Add this tree as a child of the parent
|
|
66
|
+
const segment = item.path[item.path.length - 1];
|
|
67
|
+
if (segment.type !== 'field') {
|
|
68
|
+
throw new Error(`Unsupported tree path segment type in path ${item.path}: ${segment.type}`);
|
|
69
|
+
}
|
|
70
|
+
const name = segment.value;
|
|
71
|
+
const path = `${parentPath}.${printIdentifier(name)}`;
|
|
72
|
+
parentTree.value[name] = path;
|
|
73
|
+
trees.set(path, variant('struct', {}));
|
|
74
|
+
// Update structure: add nested struct to parent
|
|
75
|
+
const parentStructure = structures.get(parentPath);
|
|
76
|
+
if (!parentStructure || parentStructure.type !== 'struct') {
|
|
77
|
+
throw new Error(`Missing or invalid parent structure at path: ${parentPath}`);
|
|
78
|
+
}
|
|
79
|
+
const childStructure = variant('struct', new SortedMap());
|
|
80
|
+
parentStructure.value.set(name, childStructure);
|
|
81
|
+
structures.set(path, childStructure);
|
|
82
|
+
}
|
|
83
|
+
else if (item.kind === "dataset") {
|
|
84
|
+
// Datasets are serialized and written immediately
|
|
85
|
+
// Get parent tree
|
|
86
|
+
const parentPath = item.path.slice(0, -1).map(segment => {
|
|
87
|
+
if (segment.type !== 'field') {
|
|
88
|
+
throw new Error(`Unsupported tree path segment type in path ${item.path}: ${segment.type}`);
|
|
89
|
+
}
|
|
90
|
+
return `.${printIdentifier(segment.value)}`;
|
|
91
|
+
}).join('');
|
|
92
|
+
const parentTree = trees.get(parentPath);
|
|
93
|
+
if (!parentTree) {
|
|
94
|
+
throw new Error(`Missing parent tree at path: ${parentPath}`);
|
|
95
|
+
}
|
|
96
|
+
if (parentTree.type !== 'struct') {
|
|
97
|
+
throw new Error(`Parent tree at path ${parentPath} is not a struct: ${parentTree.type}`);
|
|
98
|
+
}
|
|
99
|
+
// Add this dataset as a child of the parent
|
|
100
|
+
const segment = item.path[item.path.length - 1];
|
|
101
|
+
if (segment.type !== 'field') {
|
|
102
|
+
throw new Error(`Unsupported tree path segment type in path ${item.path}: ${segment.type}`);
|
|
103
|
+
}
|
|
104
|
+
const name = segment.value;
|
|
105
|
+
const path = `${parentPath}.${printIdentifier(name)}`;
|
|
106
|
+
parentTree.value[name] = path;
|
|
107
|
+
// Serialize default value (if present) and record DataRef
|
|
108
|
+
let dataRef;
|
|
109
|
+
if (item.default !== undefined) {
|
|
110
|
+
const valueEncoder = encodeBeast2For(item.type);
|
|
111
|
+
const valueData = valueEncoder(item.default);
|
|
112
|
+
const valueHash = addObject(zipfile, Buffer.from(valueData));
|
|
113
|
+
dataRef = variant('value', valueHash);
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
dataRef = variant('unassigned', null);
|
|
117
|
+
}
|
|
118
|
+
refs.set(path, dataRef);
|
|
119
|
+
// Update structure: add value type to parent
|
|
120
|
+
const parentStructure = structures.get(parentPath);
|
|
121
|
+
if (!parentStructure || parentStructure.type !== 'struct') {
|
|
122
|
+
throw new Error(`Missing or invalid parent structure at path: ${parentPath}`);
|
|
123
|
+
}
|
|
124
|
+
const typeValue = toEastTypeValue(item.type);
|
|
125
|
+
parentStructure.value.set(name, variant('value', typeValue));
|
|
126
|
+
}
|
|
127
|
+
else if (item.kind === "task") {
|
|
128
|
+
// Tasks are serialized and written immediately
|
|
129
|
+
// Build input paths from the task definition
|
|
130
|
+
// Note: e3.task() includes function_ir in inputs, e3.customTask() does not
|
|
131
|
+
const inputPaths = item.inputs.map(input => input.path);
|
|
132
|
+
// Serialize command IR
|
|
133
|
+
const commandIrEncoder = encodeBeast2For(IRType);
|
|
134
|
+
const commandIrData = commandIrEncoder(item.command);
|
|
135
|
+
const commandIrHash = addObject(zipfile, Buffer.from(commandIrData));
|
|
136
|
+
// Build TaskObject
|
|
137
|
+
const taskObject = {
|
|
138
|
+
commandIr: commandIrHash,
|
|
139
|
+
inputs: inputPaths,
|
|
140
|
+
output: item.output.path,
|
|
141
|
+
};
|
|
142
|
+
// Serialize and add to zip
|
|
143
|
+
const taskEncoder = encodeBeast2For(TaskObjectType);
|
|
144
|
+
const taskData = taskEncoder(taskObject);
|
|
145
|
+
const taskHash = addObject(zipfile, Buffer.from(taskData));
|
|
146
|
+
// Add to package tasks map
|
|
147
|
+
tasks.set(item.name, taskHash);
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
throw new Error(`Unknown package item kind: ${item.kind}`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// Now we traverse the trees in reverse order to build and write tree objects, finishing with root.
|
|
154
|
+
const treePaths = Array.from(trees.keys()).reverse();
|
|
155
|
+
for (const treePath of treePaths) {
|
|
156
|
+
const tree = trees.get(treePath);
|
|
157
|
+
if (tree.type === 'struct') {
|
|
158
|
+
// Build tree object with DataRef fields
|
|
159
|
+
const treeObject = {};
|
|
160
|
+
for (const [fieldName, childPath] of Object.entries(tree.value).sort(([name1], [name2]) => name1 < name2 ? -1 : 1)) {
|
|
161
|
+
const childRef = refs.get(childPath);
|
|
162
|
+
if (!childRef) {
|
|
163
|
+
throw new Error(`Missing hash for tree child at path: ${childPath}`);
|
|
164
|
+
}
|
|
165
|
+
treeObject[fieldName] = childRef;
|
|
166
|
+
}
|
|
167
|
+
// Serialize and write tree object
|
|
168
|
+
// TODO: I wonder if this is better as a dictionary, like the structure?
|
|
169
|
+
const TreeType = StructType(Object.fromEntries(Object.keys(treeObject).map((fieldName) => [fieldName, DataRefType])));
|
|
170
|
+
const treeEncoder = encodeBeast2For(TreeType);
|
|
171
|
+
const treeData = treeEncoder(treeObject);
|
|
172
|
+
const treeHash = addObject(zipfile, Buffer.from(treeData));
|
|
173
|
+
// Record DataRef for this tree
|
|
174
|
+
const treeRef = variant('tree', treeHash);
|
|
175
|
+
refs.set(treePath, treeRef);
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
throw new Error(`Unsupported tree type at path ${treePath}: ${tree.type}`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// Get the root tree and structure
|
|
182
|
+
const rootTreeRef = refs.get('');
|
|
183
|
+
if (!rootTreeRef) {
|
|
184
|
+
throw new Error('Missing root tree object');
|
|
185
|
+
}
|
|
186
|
+
if (rootTreeRef.type !== 'tree') {
|
|
187
|
+
throw new Error(`Root tree ref is not a tree: ${rootTreeRef.type}`);
|
|
188
|
+
}
|
|
189
|
+
const rootStructure = structures.get('');
|
|
190
|
+
if (!rootStructure) {
|
|
191
|
+
throw new Error('Missing root structure');
|
|
192
|
+
}
|
|
193
|
+
// Build and write the package object
|
|
194
|
+
const packageObject = {
|
|
195
|
+
tasks,
|
|
196
|
+
data: {
|
|
197
|
+
structure: rootStructure,
|
|
198
|
+
value: rootTreeRef.value,
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
const packageObjectEncoder = encodeBeast2For(PackageObjectType);
|
|
202
|
+
const packageObjectData = packageObjectEncoder(packageObject);
|
|
203
|
+
const packageHash = addObject(zipfile, Buffer.from(packageObjectData));
|
|
204
|
+
// Write the package ref at packages/<name>/<version>
|
|
205
|
+
const refPath = `packages/${pkg.name}/${pkg.version}`;
|
|
206
|
+
zipfile.addBuffer(Buffer.from(packageHash + '\n'), refPath, { mtime: DETERMINISTIC_MTIME });
|
|
207
|
+
// Finalize and write zip to disk
|
|
208
|
+
await new Promise((resolve, reject) => {
|
|
209
|
+
const writeStream = fs.createWriteStream(partialPath);
|
|
210
|
+
zipfile.outputStream.pipe(writeStream);
|
|
211
|
+
zipfile.outputStream.on('error', reject);
|
|
212
|
+
writeStream.on('error', reject);
|
|
213
|
+
writeStream.on('close', resolve);
|
|
214
|
+
zipfile.end();
|
|
215
|
+
});
|
|
216
|
+
// Atomic rename to final path
|
|
217
|
+
await fs.promises.rename(partialPath, outputPath);
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Fixed mtime for deterministic zip output (Unix epoch)
|
|
221
|
+
*/
|
|
222
|
+
const DETERMINISTIC_MTIME = new Date(0);
|
|
223
|
+
/**
|
|
224
|
+
* Adds an object to the zip file at the content-addressed path.
|
|
225
|
+
*
|
|
226
|
+
* @param zipfile - The zip file to add to
|
|
227
|
+
* @param data - The serialized object data (.beast2 format)
|
|
228
|
+
* @returns The SHA256 hash of the data (used as the object ID)
|
|
229
|
+
*/
|
|
230
|
+
export function addObject(zipfile, data) {
|
|
231
|
+
const hash = createHash('sha256').update(data).digest('hex');
|
|
232
|
+
const path = `objects/${hash.slice(0, 2)}/${hash.slice(2)}.beast2`;
|
|
233
|
+
zipfile.addBuffer(data, path, { mtime: DETERMINISTIC_MTIME });
|
|
234
|
+
return hash;
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=export.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export.js","sourceRoot":"","sources":["../../src/export.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export.spec.d.ts","sourceRoot":"","sources":["../../src/export.spec.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
|
|
@@ -0,0 +1,176 @@
|
|
|
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
|
+
import { describe, it, before, after } from 'node:test';
|
|
6
|
+
import * as assert from 'node:assert';
|
|
7
|
+
import * as fs from 'node:fs';
|
|
8
|
+
import * as os from 'node:os';
|
|
9
|
+
import * as path from 'node:path';
|
|
10
|
+
import yazl from 'yazl';
|
|
11
|
+
import yauzl from 'yauzl';
|
|
12
|
+
import { StringType, decodeBeast2For } from '@elaraai/east';
|
|
13
|
+
import { PackageObjectType } from '@elaraai/e3-types';
|
|
14
|
+
import { addObject, export_ } from './export.js';
|
|
15
|
+
import { package_ } from './package.js';
|
|
16
|
+
import { input } from './input.js';
|
|
17
|
+
describe('addObject', () => {
|
|
18
|
+
it('returns correct SHA256 hash for empty buffer', () => {
|
|
19
|
+
const zipfile = new yazl.ZipFile();
|
|
20
|
+
const data = Buffer.from('');
|
|
21
|
+
const hash = addObject(zipfile, data);
|
|
22
|
+
// SHA256 of empty string
|
|
23
|
+
assert.strictEqual(hash, 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855');
|
|
24
|
+
});
|
|
25
|
+
it('returns correct SHA256 hash for known input', () => {
|
|
26
|
+
const zipfile = new yazl.ZipFile();
|
|
27
|
+
const data = Buffer.from('hello');
|
|
28
|
+
const hash = addObject(zipfile, data);
|
|
29
|
+
// SHA256 of "hello"
|
|
30
|
+
assert.strictEqual(hash, '2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824');
|
|
31
|
+
});
|
|
32
|
+
it('adds object at correct path in zip', () => {
|
|
33
|
+
const zipfile = new yazl.ZipFile();
|
|
34
|
+
const data = Buffer.from('hello');
|
|
35
|
+
const hash = addObject(zipfile, data);
|
|
36
|
+
// Verify the path format: objects/xx/yyyy...beast2
|
|
37
|
+
const expectedPath = `objects/${hash.slice(0, 2)}/${hash.slice(2)}.beast2`;
|
|
38
|
+
assert.strictEqual(expectedPath, 'objects/2c/f24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824.beast2');
|
|
39
|
+
});
|
|
40
|
+
it('returns consistent hash for same input', () => {
|
|
41
|
+
const zipfile1 = new yazl.ZipFile();
|
|
42
|
+
const zipfile2 = new yazl.ZipFile();
|
|
43
|
+
const data = Buffer.from('test data');
|
|
44
|
+
const hash1 = addObject(zipfile1, data);
|
|
45
|
+
const hash2 = addObject(zipfile2, data);
|
|
46
|
+
assert.strictEqual(hash1, hash2);
|
|
47
|
+
});
|
|
48
|
+
it('returns different hash for different input', () => {
|
|
49
|
+
const zipfile = new yazl.ZipFile();
|
|
50
|
+
const data1 = Buffer.from('hello');
|
|
51
|
+
const data2 = Buffer.from('world');
|
|
52
|
+
const hash1 = addObject(zipfile, data1);
|
|
53
|
+
const hash2 = addObject(zipfile, data2);
|
|
54
|
+
assert.notStrictEqual(hash1, hash2);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
/**
|
|
58
|
+
* Helper to read a zip file and return entries as a map of path -> buffer
|
|
59
|
+
*/
|
|
60
|
+
async function readZip(zipPath) {
|
|
61
|
+
return new Promise((resolve, reject) => {
|
|
62
|
+
yauzl.open(zipPath, { lazyEntries: true }, (err, zipfile) => {
|
|
63
|
+
if (err)
|
|
64
|
+
return reject(err);
|
|
65
|
+
if (!zipfile)
|
|
66
|
+
return reject(new Error('No zipfile'));
|
|
67
|
+
const entries = new Map();
|
|
68
|
+
zipfile.readEntry();
|
|
69
|
+
zipfile.on('entry', (entry) => {
|
|
70
|
+
if (/\/$/.test(entry.fileName)) {
|
|
71
|
+
// Directory entry, skip
|
|
72
|
+
zipfile.readEntry();
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
zipfile.openReadStream(entry, (err, readStream) => {
|
|
76
|
+
if (err)
|
|
77
|
+
return reject(err);
|
|
78
|
+
if (!readStream)
|
|
79
|
+
return reject(new Error('No read stream'));
|
|
80
|
+
const chunks = [];
|
|
81
|
+
readStream.on('data', (chunk) => chunks.push(chunk));
|
|
82
|
+
readStream.on('end', () => {
|
|
83
|
+
entries.set(entry.fileName, Buffer.concat(chunks));
|
|
84
|
+
zipfile.readEntry();
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
zipfile.on('end', () => resolve(entries));
|
|
90
|
+
zipfile.on('error', reject);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
describe('export_', () => {
|
|
95
|
+
let tempDir;
|
|
96
|
+
before(async () => {
|
|
97
|
+
tempDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'e3-export-test-'));
|
|
98
|
+
});
|
|
99
|
+
after(async () => {
|
|
100
|
+
await fs.promises.rm(tempDir, { recursive: true });
|
|
101
|
+
});
|
|
102
|
+
it('exports empty package', async () => {
|
|
103
|
+
const pkg = package_('empty-pkg', '1.0.0');
|
|
104
|
+
const zipPath = path.join(tempDir, 'empty.zip');
|
|
105
|
+
await export_(pkg, zipPath);
|
|
106
|
+
// Verify zip exists
|
|
107
|
+
const stat = await fs.promises.stat(zipPath);
|
|
108
|
+
assert.ok(stat.isFile());
|
|
109
|
+
// Read zip contents
|
|
110
|
+
const entries = await readZip(zipPath);
|
|
111
|
+
// Should have package ref
|
|
112
|
+
assert.ok(entries.has('packages/empty-pkg/1.0.0'));
|
|
113
|
+
// Package ref should contain a hash
|
|
114
|
+
const refContent = entries.get('packages/empty-pkg/1.0.0').toString().trim();
|
|
115
|
+
assert.match(refContent, /^[a-f0-9]{64}$/);
|
|
116
|
+
// Should have at least the package object and root tree
|
|
117
|
+
const objectEntries = Array.from(entries.keys()).filter(k => k.startsWith('objects/'));
|
|
118
|
+
assert.ok(objectEntries.length >= 2, `Expected at least 2 objects, got ${objectEntries.length}`);
|
|
119
|
+
// Decode and verify package object
|
|
120
|
+
const packageObjectPath = `objects/${refContent.slice(0, 2)}/${refContent.slice(2)}.beast2`;
|
|
121
|
+
assert.ok(entries.has(packageObjectPath), `Missing package object at ${packageObjectPath}`);
|
|
122
|
+
const packageObjectData = entries.get(packageObjectPath);
|
|
123
|
+
const decoder = decodeBeast2For(PackageObjectType);
|
|
124
|
+
const packageObject = decoder(packageObjectData);
|
|
125
|
+
// Empty package should have no tasks
|
|
126
|
+
assert.strictEqual(packageObject.tasks.size, 0);
|
|
127
|
+
// Structure should be an empty struct
|
|
128
|
+
assert.strictEqual(packageObject.data.structure.type, 'struct');
|
|
129
|
+
assert.strictEqual(packageObject.data.structure.value.size, 0);
|
|
130
|
+
});
|
|
131
|
+
it('exports package with input dataset', async () => {
|
|
132
|
+
const myInput = input('greeting', StringType, 'hello');
|
|
133
|
+
const pkg = package_('input-pkg', '1.0.0', myInput);
|
|
134
|
+
const zipPath = path.join(tempDir, 'input.zip');
|
|
135
|
+
await export_(pkg, zipPath);
|
|
136
|
+
// Read zip contents
|
|
137
|
+
const entries = await readZip(zipPath);
|
|
138
|
+
// Get package object
|
|
139
|
+
const refContent = entries.get('packages/input-pkg/1.0.0').toString().trim();
|
|
140
|
+
const packageObjectPath = `objects/${refContent.slice(0, 2)}/${refContent.slice(2)}.beast2`;
|
|
141
|
+
const packageObjectData = entries.get(packageObjectPath);
|
|
142
|
+
const decoder = decodeBeast2For(PackageObjectType);
|
|
143
|
+
const packageObject = decoder(packageObjectData);
|
|
144
|
+
// Should have no tasks (input only)
|
|
145
|
+
assert.strictEqual(packageObject.tasks.size, 0);
|
|
146
|
+
// Structure should have inputs.greeting as a value
|
|
147
|
+
assert.strictEqual(packageObject.data.structure.type, 'struct');
|
|
148
|
+
const inputs = packageObject.data.structure.value.get('inputs');
|
|
149
|
+
assert.ok(inputs, 'Missing inputs in structure');
|
|
150
|
+
assert.strictEqual(inputs.type, 'struct');
|
|
151
|
+
const greeting = inputs.value.get('greeting');
|
|
152
|
+
assert.ok(greeting, 'Missing greeting in inputs structure');
|
|
153
|
+
assert.strictEqual(greeting.type, 'value');
|
|
154
|
+
// The value should be a String type
|
|
155
|
+
assert.strictEqual(greeting.value.type, 'String');
|
|
156
|
+
});
|
|
157
|
+
it('produces identical output for same package', async () => {
|
|
158
|
+
const myInput = input('name', StringType, 'world');
|
|
159
|
+
const pkg = package_('deterministic', '1.0.0', myInput);
|
|
160
|
+
const zipPath1 = path.join(tempDir, 'deterministic1.zip');
|
|
161
|
+
const zipPath2 = path.join(tempDir, 'deterministic2.zip');
|
|
162
|
+
await export_(pkg, zipPath1);
|
|
163
|
+
await export_(pkg, zipPath2);
|
|
164
|
+
// Read both zips
|
|
165
|
+
const entries1 = await readZip(zipPath1);
|
|
166
|
+
const entries2 = await readZip(zipPath2);
|
|
167
|
+
// Should have same entries
|
|
168
|
+
assert.deepStrictEqual(Array.from(entries1.keys()).sort(), Array.from(entries2.keys()).sort());
|
|
169
|
+
// Each entry should have identical content
|
|
170
|
+
for (const [path, data1] of entries1) {
|
|
171
|
+
const data2 = entries2.get(path);
|
|
172
|
+
assert.ok(data1.equals(data2), `Content mismatch at ${path}`);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
//# sourceMappingURL=export.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export.spec.js","sourceRoot":"","sources":["../../src/export.spec.ts"],"names":[],"mappings":"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"}
|