@elaraai/e3-types 1.0.83 → 1.0.85
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/dist/src/api.d.ts +583 -17
- package/dist/src/api.d.ts.map +1 -1
- package/dist/src/api.js +208 -22
- package/dist/src/api.js.map +1 -1
- package/dist/src/dataflow.d.ts +115 -12
- package/dist/src/dataflow.d.ts.map +1 -1
- package/dist/src/dataflow.js +151 -15
- package/dist/src/dataflow.js.map +1 -1
- package/dist/src/dataflow.spec.js +41 -7
- package/dist/src/dataflow.spec.js.map +1 -1
- package/dist/src/dataset-blob.d.ts +19 -2
- package/dist/src/dataset-blob.d.ts.map +1 -1
- package/dist/src/dataset-blob.js +100 -21
- package/dist/src/dataset-blob.js.map +1 -1
- package/dist/src/dataset-blob.spec.js +132 -0
- package/dist/src/dataset-blob.spec.js.map +1 -1
- package/dist/src/execution.d.ts +68 -8
- package/dist/src/execution.d.ts.map +1 -1
- package/dist/src/execution.js +49 -8
- package/dist/src/execution.js.map +1 -1
- package/dist/src/execution.spec.js +31 -9
- package/dist/src/execution.spec.js.map +1 -1
- package/dist/src/function.d.ts +12 -0
- package/dist/src/function.d.ts.map +1 -1
- package/dist/src/index.d.ts +9 -8
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +15 -12
- package/dist/src/index.js.map +1 -1
- package/dist/src/names.d.ts +9 -0
- package/dist/src/names.d.ts.map +1 -1
- package/dist/src/names.js.map +1 -1
- package/dist/src/pieces.d.ts +47 -0
- package/dist/src/pieces.d.ts.map +1 -0
- package/dist/src/pieces.js +17 -0
- package/dist/src/pieces.js.map +1 -0
- package/dist/src/pieces.spec.d.ts +6 -0
- package/dist/src/pieces.spec.d.ts.map +1 -0
- package/dist/src/pieces.spec.js +20 -0
- package/dist/src/pieces.spec.js.map +1 -0
- package/dist/src/record.d.ts +52 -0
- package/dist/src/record.d.ts.map +1 -1
- package/dist/src/record.js +25 -0
- package/dist/src/record.js.map +1 -1
- package/dist/src/record.spec.js +43 -6
- package/dist/src/record.spec.js.map +1 -1
- package/dist/src/release.d.ts +1 -1
- package/dist/src/release.js +1 -1
- package/dist/src/repository.d.ts +16 -0
- package/dist/src/repository.d.ts.map +1 -1
- package/dist/src/repository.js +15 -0
- package/dist/src/repository.js.map +1 -1
- package/dist/src/runner.d.ts +22 -7
- package/dist/src/runner.d.ts.map +1 -1
- package/dist/src/runner.js +14 -11
- package/dist/src/runner.js.map +1 -1
- package/dist/src/runner.spec.js +4 -4
- package/dist/src/runner.spec.js.map +1 -1
- package/dist/src/structure.d.ts +27 -0
- package/dist/src/structure.d.ts.map +1 -1
- package/dist/src/structure.js +59 -18
- package/dist/src/structure.js.map +1 -1
- package/dist/src/structure.spec.d.ts +6 -0
- package/dist/src/structure.spec.d.ts.map +1 -0
- package/dist/src/structure.spec.js +57 -0
- package/dist/src/structure.spec.js.map +1 -0
- package/dist/src/task.d.ts +13 -1
- package/dist/src/task.d.ts.map +1 -1
- package/dist/src/task.js +1 -1
- package/dist/src/task.spec.js +25 -4
- package/dist/src/task.spec.js.map +1 -1
- package/package.json +3 -2
- package/src/api.ts +225 -21
- package/src/dataflow.spec.ts +49 -7
- package/src/dataflow.ts +160 -14
- package/src/dataset-blob.spec.ts +147 -0
- package/src/dataset-blob.ts +97 -20
- package/src/execution.spec.ts +31 -9
- package/src/execution.ts +52 -7
- package/src/index.ts +39 -2
- package/src/names.ts +12 -0
- package/src/pieces.spec.ts +23 -0
- package/src/pieces.ts +49 -0
- package/src/record.spec.ts +51 -5
- package/src/record.ts +28 -0
- package/src/release.ts +1 -1
- package/src/repository.ts +18 -0
- package/src/runner.spec.ts +4 -4
- package/src/runner.ts +14 -11
- package/src/structure.spec.ts +65 -0
- package/src/structure.ts +64 -21
- package/src/task.spec.ts +29 -3
- package/src/task.ts +1 -1
package/src/execution.spec.ts
CHANGED
|
@@ -5,15 +5,16 @@
|
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Execution status wire: the typed outcomes with their peaks, a split task's
|
|
8
|
-
* plan
|
|
9
|
-
* refusal of a record an older e3 wrote before
|
|
10
|
-
* were cases of it, before a runner's peak was
|
|
11
|
-
* plan was,
|
|
8
|
+
* plan, whether the execution is a unit and why a stopped one stopped, what
|
|
9
|
+
* each keeps from gc, and the refusal of a record an older e3 wrote before
|
|
10
|
+
* `cancelled` and `interrupted` were cases of it, before a runner's peak was
|
|
11
|
+
* recorded, before a split task's plan was, before a record said whether it
|
|
12
|
+
* is a unit, or before a stopped one said why.
|
|
12
13
|
*/
|
|
13
14
|
|
|
14
15
|
import { describe, it } from 'node:test';
|
|
15
16
|
import assert from 'node:assert/strict';
|
|
16
|
-
import { ArrayType, DateTimeType, IntegerType, OptionType, StringType, StructType, VariantType, encodeBeast2For, none, some, variant } from '@elaraai/east';
|
|
17
|
+
import { ArrayType, BooleanType, DateTimeType, IntegerType, OptionType, StringType, StructType, VariantType, encodeBeast2For, none, some, variant } from '@elaraai/east';
|
|
17
18
|
import { ExecutionStatusType, decodeExecutionStatus, executionStatusRoots, type ExecutionStatus } from './execution.js';
|
|
18
19
|
|
|
19
20
|
/** The four-case wire every record had before the typed outcomes. */
|
|
@@ -45,7 +46,7 @@ describe('ExecutionStatusType', () => {
|
|
|
45
46
|
};
|
|
46
47
|
const encodeOlder = encodeBeast2For(PreOutcomeExecutionStatusType);
|
|
47
48
|
|
|
48
|
-
it('round-trips the typed outcomes, with a runner\'s peak or without one, a task\'s own or a unit\'s', () => {
|
|
49
|
+
it('round-trips the typed outcomes, with a runner\'s peak or without one, a task\'s own or a unit\'s, and why a stopped one stopped', () => {
|
|
49
50
|
const own = { ...stopped, unit: false };
|
|
50
51
|
const statuses: ExecutionStatus[] = [
|
|
51
52
|
variant('success', { ...own, outputHash: 'b'.repeat(64), peakBytes: some(96n * 1024n ** 2n), plan: none }),
|
|
@@ -54,8 +55,11 @@ describe('ExecutionStatusType', () => {
|
|
|
54
55
|
variant('running', { executionId: '0199-b', inputHashes: ['a'.repeat(64)], startedAt: new Date(1000), pid: 4242n, pidStartTime: 7n, bootId: 'boot', unit: true }),
|
|
55
56
|
variant('failed', { ...own, exitCode: 1n, peakBytes: some(412n * 1024n ** 2n) }),
|
|
56
57
|
variant('failed', { ...own, exitCode: -1n, peakBytes: none }),
|
|
57
|
-
variant('cancelled', own),
|
|
58
|
-
variant('interrupted', { ...own, pid: 4242n }),
|
|
58
|
+
variant('cancelled', { ...own, reason: { kind: variant('aborted', null), message: 'cancelled: e3 stopped the runner because the run was aborted' } }),
|
|
59
|
+
variant('interrupted', { ...own, pid: 4242n, reason: { kind: variant('owner_gone', null), message: 'its runner and its owner are gone' } }),
|
|
60
|
+
variant('interrupted', { ...own, pid: 0n, unit: true, reason: { kind: variant('yielded', null), message: 'its run yielded' } }),
|
|
61
|
+
variant('interrupted', { ...own, pid: 0n, reason: { kind: variant('host', 'OutOfMemoryError'), message: 'its container ran out of memory' } }),
|
|
62
|
+
variant('interrupted', { ...own, pid: 0n, reason: { kind: variant('unrecorded', null), message: '' } }),
|
|
59
63
|
variant('error', { ...own, message: 'Failed to read output: no such file' }),
|
|
60
64
|
];
|
|
61
65
|
const encode = encodeBeast2For(ExecutionStatusType);
|
|
@@ -67,7 +71,7 @@ describe('ExecutionStatusType', () => {
|
|
|
67
71
|
assert.deepEqual(executionStatusRoots(variant('success', { ...success, plan: none })), ['b'.repeat(64)]);
|
|
68
72
|
assert.deepEqual(executionStatusRoots(variant('success', { ...success, plan: some('c'.repeat(64)) })), ['b'.repeat(64), 'c'.repeat(64)]);
|
|
69
73
|
assert.deepEqual(executionStatusRoots(variant('failed', { ...stopped, exitCode: 1n, peakBytes: none, unit: false })), []);
|
|
70
|
-
assert.deepEqual(executionStatusRoots(variant('cancelled', { ...stopped, unit: false })), []);
|
|
74
|
+
assert.deepEqual(executionStatusRoots(variant('cancelled', { ...stopped, unit: false, reason: { kind: variant('aborted', null), message: '' } })), []);
|
|
71
75
|
});
|
|
72
76
|
|
|
73
77
|
it('keeps from gc the inputs a running attempt reads, passing over a merge\'s tag', () => {
|
|
@@ -113,6 +117,24 @@ describe('ExecutionStatusType', () => {
|
|
|
113
117
|
}),
|
|
114
118
|
});
|
|
115
119
|
assert.throws(() => decodeExecutionStatus(encodeBeast2For(withoutUnits)(variant('success', { ...stopped, outputHash: 'b'.repeat(64), peakBytes: none, plan: none }))), refusal);
|
|
120
|
+
// With units, before a stopped record said why it stopped: only the
|
|
121
|
+
// repository upgrade that carries such a record forward reads it, and every
|
|
122
|
+
// case of it is refused, since its type names the cases that changed.
|
|
123
|
+
const withoutReasons = VariantType({
|
|
124
|
+
...ExecutionStatusType.cases,
|
|
125
|
+
cancelled: StructType({ executionId: StringType, inputHashes: ArrayType(StringType), startedAt: DateTimeType, completedAt: DateTimeType, unit: BooleanType }),
|
|
126
|
+
interrupted: StructType({
|
|
127
|
+
executionId: StringType, inputHashes: ArrayType(StringType), startedAt: DateTimeType, completedAt: DateTimeType, pid: IntegerType, unit: BooleanType,
|
|
128
|
+
}),
|
|
129
|
+
});
|
|
130
|
+
const own = { ...stopped, unit: false };
|
|
131
|
+
for (const record of [
|
|
132
|
+
variant('cancelled', own),
|
|
133
|
+
variant('interrupted', { ...own, pid: 4242n }),
|
|
134
|
+
variant('success', { ...own, outputHash: 'b'.repeat(64), peakBytes: none, plan: none }),
|
|
135
|
+
] as const) {
|
|
136
|
+
assert.throws(() => decodeExecutionStatus(encodeBeast2For(withoutReasons)(record)), refusal);
|
|
137
|
+
}
|
|
116
138
|
});
|
|
117
139
|
|
|
118
140
|
it('throws for bytes of no status shape', () => {
|
package/src/execution.ts
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* - For success: output hash, timing, peak memory and a split task's last plan
|
|
16
16
|
* - For failed: exit code, timing and peak memory
|
|
17
17
|
* - For error: internal error message and timing
|
|
18
|
-
* - For cancelled and interrupted: how e3, not the task, ended it
|
|
18
|
+
* - For cancelled and interrupted: how e3, not the task, ended it, and why
|
|
19
19
|
*
|
|
20
20
|
* Every case says whether the execution is a unit of a split task — a piece,
|
|
21
21
|
* or a merge of the pieces' outputs — which is recorded under its task's hash
|
|
@@ -31,10 +31,42 @@ import {
|
|
|
31
31
|
IntegerType,
|
|
32
32
|
BooleanType,
|
|
33
33
|
DateTimeType,
|
|
34
|
+
NullType,
|
|
34
35
|
ValueTypeOf,
|
|
35
36
|
decodeBeast2For,
|
|
36
37
|
} from '@elaraai/east';
|
|
37
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Why an execution stopped without finishing: what a `cancelled` or an
|
|
41
|
+
* `interrupted` record says, written in the same write as the record.
|
|
42
|
+
*
|
|
43
|
+
* - `aborted`: the signal it ran under was aborted — its run was cancelled,
|
|
44
|
+
* or the call it ran for ended
|
|
45
|
+
* - `yielded`: its run yielded mid-stage, and a run resumed takes the stage up
|
|
46
|
+
* again
|
|
47
|
+
* - `owner_gone`: its runner and the process, or the browser tab, that owned
|
|
48
|
+
* it are gone
|
|
49
|
+
* - `host`: a host's own reason, by its code — e3-cloud's for a container
|
|
50
|
+
* that stopped, say
|
|
51
|
+
* - `unrecorded`: it stopped before e3 recorded reasons; only the repository
|
|
52
|
+
* upgrade that carried its record forward writes it
|
|
53
|
+
*
|
|
54
|
+
* `message` is what e3 wrote as the attempt's log's last line, after its
|
|
55
|
+
* `e3: `, or the host's words; empty for `unrecorded`.
|
|
56
|
+
*/
|
|
57
|
+
export const StopReasonType = StructType({
|
|
58
|
+
kind: VariantType({
|
|
59
|
+
aborted: NullType,
|
|
60
|
+
yielded: NullType,
|
|
61
|
+
owner_gone: NullType,
|
|
62
|
+
host: StringType,
|
|
63
|
+
unrecorded: NullType,
|
|
64
|
+
}),
|
|
65
|
+
message: StringType,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
export type StopReason = ValueTypeOf<typeof StopReasonType>;
|
|
69
|
+
|
|
38
70
|
/** A running execution's process identification. */
|
|
39
71
|
const RunningStatusType = StructType({
|
|
40
72
|
/** Unique execution ID (UUIDv7) */
|
|
@@ -126,10 +158,14 @@ const ErrorStatusType = StructType({
|
|
|
126
158
|
* - `success`: Task ran and returned exit code 0
|
|
127
159
|
* - `failed`: Task ran and returned non-zero exit code
|
|
128
160
|
* - `error`: e3 execution engine had an internal error (runner not found, output missing, etc.)
|
|
129
|
-
* - `cancelled`: e3 stopped the execution because the
|
|
130
|
-
* its runner started or while it ran — not the task's own
|
|
131
|
-
*
|
|
132
|
-
*
|
|
161
|
+
* - `cancelled`: e3 stopped the execution because the signal it ran under was
|
|
162
|
+
* aborted, before its runner started or while it ran — not the task's own
|
|
163
|
+
* failure
|
|
164
|
+
* - `interrupted`: the execution can no longer finish, and nothing will write
|
|
165
|
+
* its outcome: its runner and its owner are gone, its host says it cannot,
|
|
166
|
+
* or its run yielded mid-stage
|
|
167
|
+
*
|
|
168
|
+
* Each of the two says why it stopped ({@link StopReasonType}).
|
|
133
169
|
*
|
|
134
170
|
* The `running` state includes process identification fields (pid, pidStartTime, bootId)
|
|
135
171
|
* to enable detection of crashed executions. See design/e3-execution.md for details.
|
|
@@ -154,6 +190,8 @@ export const ExecutionStatusType = VariantType({
|
|
|
154
190
|
* the pieces' outputs — rather than a task's own execution, which the one
|
|
155
191
|
* unit of a task whose input closes no piece is */
|
|
156
192
|
unit: BooleanType,
|
|
193
|
+
/** Why it stopped: `aborted` */
|
|
194
|
+
reason: StopReasonType,
|
|
157
195
|
}),
|
|
158
196
|
interrupted: StructType({
|
|
159
197
|
/** Unique execution ID (UUIDv7) */
|
|
@@ -170,6 +208,9 @@ export const ExecutionStatusType = VariantType({
|
|
|
170
208
|
* the pieces' outputs — rather than a task's own execution, which the one
|
|
171
209
|
* unit of a task whose input closes no piece is */
|
|
172
210
|
unit: BooleanType,
|
|
211
|
+
/** Why it can no longer finish: `owner_gone`, `yielded`, or its host's
|
|
212
|
+
* reason */
|
|
213
|
+
reason: StopReasonType,
|
|
173
214
|
}),
|
|
174
215
|
});
|
|
175
216
|
|
|
@@ -209,8 +250,12 @@ const decodeCurrentStatus = decodeBeast2For(ExecutionStatusType);
|
|
|
209
250
|
* Decode an execution status.
|
|
210
251
|
*
|
|
211
252
|
* @remarks
|
|
212
|
-
* Stored state,
|
|
213
|
-
*
|
|
253
|
+
* Stored state, which changes as `docs/conventions/WIRE_MIGRATION.md` says:
|
|
254
|
+
* this reads the current form alone. A repository's upgrade steps carry its
|
|
255
|
+
* records into it when an e3 first opens the repository — the stop reasons'
|
|
256
|
+
* step those from before a stopped execution recorded why — so a record in an
|
|
257
|
+
* earlier form here is one from before repositories recorded their upgrades,
|
|
258
|
+
* whose repository is re-created, and this says so.
|
|
214
259
|
*
|
|
215
260
|
* @param data - the stored bytes
|
|
216
261
|
* @returns the status
|
package/src/index.ts
CHANGED
|
@@ -92,6 +92,7 @@ export {
|
|
|
92
92
|
treePath,
|
|
93
93
|
pathToString,
|
|
94
94
|
parsePath,
|
|
95
|
+
parseKeypath,
|
|
95
96
|
parseDatasetPath,
|
|
96
97
|
parsePackageRef,
|
|
97
98
|
urlPathToTreePath,
|
|
@@ -122,6 +123,7 @@ export {
|
|
|
122
123
|
decodeRecordObject,
|
|
123
124
|
RecordIndexObjectType,
|
|
124
125
|
type RecordIndexObject,
|
|
126
|
+
decodeRecordIndexObject,
|
|
125
127
|
RECORD_STATE_KIND,
|
|
126
128
|
RecordStateType,
|
|
127
129
|
type RecordState,
|
|
@@ -184,6 +186,8 @@ export {
|
|
|
184
186
|
|
|
185
187
|
// Execution status
|
|
186
188
|
export {
|
|
189
|
+
StopReasonType,
|
|
190
|
+
type StopReason,
|
|
187
191
|
ExecutionStatusType,
|
|
188
192
|
type ExecutionStatus,
|
|
189
193
|
decodeExecutionStatus,
|
|
@@ -223,6 +227,8 @@ export {
|
|
|
223
227
|
export {
|
|
224
228
|
RepositoryRecordType,
|
|
225
229
|
type RepositoryRecord,
|
|
230
|
+
RepositoryUpgradeProgressType,
|
|
231
|
+
type RepositoryUpgradeProgress,
|
|
226
232
|
RepoStatusType,
|
|
227
233
|
type RepoStatus,
|
|
228
234
|
RepoMetadataType,
|
|
@@ -232,8 +238,8 @@ export {
|
|
|
232
238
|
// The release of e3 this build is, which what it keeps and ships records
|
|
233
239
|
export { E3_RELEASE, compareReleases } from './release.js';
|
|
234
240
|
|
|
235
|
-
// The names e3 makes paths of
|
|
236
|
-
export { type NamedKind, nameProblem } from './names.js';
|
|
241
|
+
// The names e3 makes paths of, and the hashes and ids
|
|
242
|
+
export { type NamedKind, type HashKind, type IdKind, type IdentifierKind, nameProblem } from './names.js';
|
|
237
243
|
|
|
238
244
|
// Dataset transfer types
|
|
239
245
|
export {
|
|
@@ -256,6 +262,7 @@ export {
|
|
|
256
262
|
isCollectionRoot,
|
|
257
263
|
encodeDatasetBlob,
|
|
258
264
|
writeCollectionManifest,
|
|
265
|
+
SEGMENT_WRITES_IN_FLIGHT,
|
|
259
266
|
type CollectionSegmentRef,
|
|
260
267
|
type CollectionPiece,
|
|
261
268
|
type SegmentSink,
|
|
@@ -355,6 +362,7 @@ export {
|
|
|
355
362
|
// Execution
|
|
356
363
|
DataflowRequestType,
|
|
357
364
|
LogChunkType,
|
|
365
|
+
TaskLogChunkType,
|
|
358
366
|
TaskExecutionResultType,
|
|
359
367
|
DataflowResultType,
|
|
360
368
|
// Dataflow API polling
|
|
@@ -365,9 +373,12 @@ export {
|
|
|
365
373
|
UnitWaitType,
|
|
366
374
|
SplitProgressType,
|
|
367
375
|
ApiDataflowExecutionStateType,
|
|
376
|
+
DATAFLOW_POLL_EVENTS_MAX,
|
|
368
377
|
// Task Execution History
|
|
369
378
|
ExecutionHistoryStatusType,
|
|
370
379
|
ExecutionListItemType,
|
|
380
|
+
TASK_EXECUTIONS_PAGE_DEFAULT,
|
|
381
|
+
TASK_EXECUTIONS_PAGE_MAX,
|
|
371
382
|
// Dataset List
|
|
372
383
|
TreeKindType,
|
|
373
384
|
ListEntryType,
|
|
@@ -377,6 +388,7 @@ export {
|
|
|
377
388
|
ExecuteLimitsType,
|
|
378
389
|
DiagnosticType,
|
|
379
390
|
ExecuteResultType,
|
|
391
|
+
CallArgType,
|
|
380
392
|
FunctionCallRequestType,
|
|
381
393
|
FunctionSignatureType,
|
|
382
394
|
OneShotRequestType,
|
|
@@ -388,9 +400,16 @@ export {
|
|
|
388
400
|
SplitCallPlanType,
|
|
389
401
|
MutationCallRequestType,
|
|
390
402
|
MutationResultType,
|
|
403
|
+
RecordMigrationAppliedType,
|
|
391
404
|
RecordSignatureType,
|
|
392
405
|
RecordCommitInfoType,
|
|
406
|
+
RecordArgPreviewType,
|
|
407
|
+
RecordCommitArgsType,
|
|
408
|
+
RecordHistoryCommitType,
|
|
393
409
|
RecordHistoryResultType,
|
|
410
|
+
RECORD_ARG_TEXT_CHARS,
|
|
411
|
+
RECORD_ARG_TEXT_BYTES,
|
|
412
|
+
RECORD_ARGS_READ_BYTES,
|
|
394
413
|
// Type aliases
|
|
395
414
|
type Error,
|
|
396
415
|
type RepositoryStatus,
|
|
@@ -424,6 +443,7 @@ export {
|
|
|
424
443
|
type TaskDetails,
|
|
425
444
|
type DataflowRequest,
|
|
426
445
|
type LogChunk,
|
|
446
|
+
type TaskLogChunk,
|
|
427
447
|
type TaskExecutionResult,
|
|
428
448
|
type DataflowResult,
|
|
429
449
|
type DataflowEvent,
|
|
@@ -441,6 +461,7 @@ export {
|
|
|
441
461
|
type ExecuteLimits,
|
|
442
462
|
type Diagnostic,
|
|
443
463
|
type ExecuteResult,
|
|
464
|
+
type CallArg,
|
|
444
465
|
type FunctionCallRequest,
|
|
445
466
|
type FunctionSignature,
|
|
446
467
|
type OneShotRequest,
|
|
@@ -451,8 +472,12 @@ export {
|
|
|
451
472
|
type SplitCallPlan,
|
|
452
473
|
type MutationCallRequest,
|
|
453
474
|
type MutationResult,
|
|
475
|
+
type RecordMigrationApplied,
|
|
454
476
|
type RecordSignature,
|
|
455
477
|
type RecordCommitInfo,
|
|
478
|
+
type RecordArgPreview,
|
|
479
|
+
type RecordCommitArgs,
|
|
480
|
+
type RecordHistoryCommit,
|
|
456
481
|
type RecordHistoryResult,
|
|
457
482
|
} from './api.js';
|
|
458
483
|
|
|
@@ -473,9 +498,17 @@ export {
|
|
|
473
498
|
ExecutionEventType,
|
|
474
499
|
type ExecutionEvent,
|
|
475
500
|
type PartitionProgress,
|
|
501
|
+
DataflowForceType,
|
|
502
|
+
type DataflowForce,
|
|
503
|
+
dataflowForce,
|
|
504
|
+
dataflowForceOption,
|
|
476
505
|
DataflowExecutionStateType,
|
|
477
506
|
type DataflowExecutionState,
|
|
478
507
|
decodeDataflowExecutionState,
|
|
508
|
+
ExecutionStateSummaryType,
|
|
509
|
+
type ExecutionStateSummary,
|
|
510
|
+
executionStateSummary,
|
|
511
|
+
lastEventSeq,
|
|
479
512
|
// Dataflow run history
|
|
480
513
|
DataflowRunStatusType,
|
|
481
514
|
type DataflowRunStatus,
|
|
@@ -487,6 +520,10 @@ export {
|
|
|
487
520
|
type DataflowRun,
|
|
488
521
|
} from './dataflow.js';
|
|
489
522
|
|
|
523
|
+
// The sizes e3 cuts a split task's input into pieces at, which a caller
|
|
524
|
+
// planning a split call weighs a dataset against
|
|
525
|
+
export { PIECE_SIZES, type PieceSizes } from './pieces.js';
|
|
526
|
+
|
|
490
527
|
// A split task's unit graph, a stage at a time
|
|
491
528
|
export {
|
|
492
529
|
UNIT_PLAN_KIND,
|
package/src/names.ts
CHANGED
|
@@ -13,6 +13,18 @@
|
|
|
13
13
|
/** What a name names. */
|
|
14
14
|
export type NamedKind = 'repository' | 'workspace' | 'package' | 'package version' | 'lock';
|
|
15
15
|
|
|
16
|
+
/** What a hash e3 makes a path or a key of names: an object, or an
|
|
17
|
+
* execution's task or inputs. */
|
|
18
|
+
export type HashKind = 'object hash' | 'task hash' | 'inputs hash';
|
|
19
|
+
|
|
20
|
+
/** What an id e3 makes a path or a key of names: an execution attempt, a
|
|
21
|
+
* dataflow run, or a gc run in steps. */
|
|
22
|
+
export type IdKind = 'execution id' | 'run id' | 'gc run id';
|
|
23
|
+
|
|
24
|
+
/** What a hash or an id e3 makes a path or a key of names: not a name, so
|
|
25
|
+
* checked for its form rather than by {@link nameProblem}. */
|
|
26
|
+
export type IdentifierKind = HashKind | IdKind;
|
|
27
|
+
|
|
16
28
|
/** What no name may hold: a path separator, a character a Windows file name
|
|
17
29
|
* refuses, or a control character. */
|
|
18
30
|
const REFUSED = /[/\\:*?"<>|\u0000-\u001f]/;
|
|
@@ -0,0 +1,23 @@
|
|
|
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
|
+
* The platform's piece sizes, which e3-core's piece rule closes pieces at and
|
|
8
|
+
* a caller planning a split call weighs a dataset against.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { describe, it } from 'node:test';
|
|
12
|
+
import assert from 'node:assert/strict';
|
|
13
|
+
import { PIECE_SIZES } from './index.js';
|
|
14
|
+
|
|
15
|
+
describe('PIECE_SIZES', () => {
|
|
16
|
+
it('is 16, 64 and 256 MiB, exported from the package', () => {
|
|
17
|
+
assert.deepEqual(PIECE_SIZES, { min: 16 * 2 ** 20, target: 64 * 2 ** 20, max: 256 * 2 ** 20 });
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('orders its sizes: the least a piece holds, its middle, and the most', () => {
|
|
21
|
+
assert.ok(PIECE_SIZES.min < PIECE_SIZES.target && PIECE_SIZES.target < PIECE_SIZES.max);
|
|
22
|
+
});
|
|
23
|
+
});
|
package/src/pieces.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
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
|
+
* The sizes e3 cuts a split task's input into pieces at.
|
|
8
|
+
*
|
|
9
|
+
* They live here, beside the wire types, so a caller that plans a split call
|
|
10
|
+
* in a browser — the query builder — weighs a dataset against the smallest
|
|
11
|
+
* piece e3 cuts without importing e3-core. e3-core's piece rule closes pieces
|
|
12
|
+
* at these sizes, and its `pieceSizes()` gives the sizes a process plans with:
|
|
13
|
+
* these, or the sizes a test sets.
|
|
14
|
+
*
|
|
15
|
+
* @packageDocumentation
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The sizes the piece rule closes pieces at, in stored bytes: a piece closes
|
|
20
|
+
* only once it holds `min`, weighs its segments against `max` until it holds
|
|
21
|
+
* `target` and against `min` after, and always closes once it holds `max`.
|
|
22
|
+
*
|
|
23
|
+
* @remarks
|
|
24
|
+
* A dataset that weighs no more than `min` is one piece, which is why a
|
|
25
|
+
* caller compares a dataset's stored bytes with `min` to tell whether a split
|
|
26
|
+
* call would split it.
|
|
27
|
+
*/
|
|
28
|
+
export interface PieceSizes {
|
|
29
|
+
/** The least a piece holds, unless the input ends first. */
|
|
30
|
+
readonly min: number;
|
|
31
|
+
/** Where a piece starts closing readily: the pieces' middle size, which a
|
|
32
|
+
* merge range aims for too. */
|
|
33
|
+
readonly target: number;
|
|
34
|
+
/** The most a piece holds, but for the rest of a `by` group. */
|
|
35
|
+
readonly max: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The platform's piece sizes: 16, 64 and 256 MiB.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```ts
|
|
43
|
+
* import { PIECE_SIZES } from '@elaraai/e3-types';
|
|
44
|
+
*
|
|
45
|
+
* // A dataset within one piece is read in one call.
|
|
46
|
+
* const split = storedBytes > PIECE_SIZES.min;
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export const PIECE_SIZES: PieceSizes = { min: 16 * 2 ** 20, target: 64 * 2 ** 20, max: 256 * 2 ** 20 };
|
package/src/record.spec.ts
CHANGED
|
@@ -6,26 +6,29 @@
|
|
|
6
6
|
/**
|
|
7
7
|
* The record wire types' decoders: each reads the current shape, and refuses
|
|
8
8
|
* an older one naming the fix — a commit an older e3 wrote, whose repository
|
|
9
|
-
* is re-created, and a mutation, migration or record object an older
|
|
10
|
-
* exported, whose package is re-exported.
|
|
9
|
+
* is re-created, and a mutation, migration, index or record object an older
|
|
10
|
+
* SDK exported, whose package is re-exported.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { describe, it } from 'node:test';
|
|
14
14
|
import assert from 'node:assert/strict';
|
|
15
|
-
import { DateTimeType, DictType, IntegerType, OptionType, StringType, StructType, ArrayType, EastTypeType, encodeBeast2For, isTypeValueEqual, some, toEastTypeValue, variant } from '@elaraai/east';
|
|
15
|
+
import { BooleanType, DateTimeType, DictType, IntegerType, OptionType, StringType, StructType, ArrayType, EastTypeType, VariantType, encodeBeast2For, isTypeValueEqual, some, toEastTypeValue, variant } from '@elaraai/east';
|
|
16
16
|
import { RunnerType } from './runner.js';
|
|
17
17
|
import {
|
|
18
18
|
MigrationObjectType,
|
|
19
19
|
MutationObjectType,
|
|
20
20
|
RecordCommitType,
|
|
21
|
+
RecordIndexObjectType,
|
|
21
22
|
RecordObjectType,
|
|
22
23
|
decodeMigrationObject,
|
|
23
24
|
decodeMutationObject,
|
|
24
25
|
decodeRecordCommit,
|
|
26
|
+
decodeRecordIndexObject,
|
|
25
27
|
decodeRecordObject,
|
|
26
28
|
type MigrationObject,
|
|
27
29
|
type MutationObject,
|
|
28
30
|
type RecordCommit,
|
|
31
|
+
type RecordIndexObject,
|
|
29
32
|
type RecordObject,
|
|
30
33
|
} from './record.js';
|
|
31
34
|
|
|
@@ -65,7 +68,7 @@ describe('decodeMutationObject', () => {
|
|
|
65
68
|
const mutation: MutationObject = {
|
|
66
69
|
bodyIr: 'a'.repeat(64),
|
|
67
70
|
argTypes: [toEastTypeValue(IntegerType)],
|
|
68
|
-
runner: variant('east_node', { platforms: ['@elaraai/east-node-std'] }),
|
|
71
|
+
runner: variant('east_node', { platforms: ['@elaraai/east-node-std'], decode: variant('lazy', null) }),
|
|
69
72
|
form: variant('edit', null),
|
|
70
73
|
programIr: 'b'.repeat(64),
|
|
71
74
|
};
|
|
@@ -113,6 +116,49 @@ describe('decodeMutationObject', () => {
|
|
|
113
116
|
});
|
|
114
117
|
});
|
|
115
118
|
|
|
119
|
+
describe('decodeRecordIndexObject', () => {
|
|
120
|
+
const index: RecordIndexObject = {
|
|
121
|
+
keyIr: 'a'.repeat(64),
|
|
122
|
+
multi: false,
|
|
123
|
+
valueIr: some('b'.repeat(64)),
|
|
124
|
+
keyType: toEastTypeValue(StringType),
|
|
125
|
+
valueType: toEastTypeValue(IntegerType),
|
|
126
|
+
buildIr: 'c'.repeat(64),
|
|
127
|
+
runner: variant('east_node', { platforms: ['@elaraai/east-node-std'], decode: variant('whole', null) }),
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
it('round-trips a current index object, its types among it', () => {
|
|
131
|
+
const decoded = decodeRecordIndexObject(encodeBeast2For(RecordIndexObjectType)(index));
|
|
132
|
+
// A decoded type value carries no memoized identity, so only the East
|
|
133
|
+
// comparison says whether two of them are the same type.
|
|
134
|
+
assert.ok(isTypeValueEqual(decoded.keyType, index.keyType));
|
|
135
|
+
assert.ok(isTypeValueEqual(decoded.valueType, index.valueType));
|
|
136
|
+
const { keyType: _keyType, valueType: _valueType, ...rest } = decoded;
|
|
137
|
+
const { keyType: _k, valueType: _v, ...expected } = index;
|
|
138
|
+
assert.deepEqual(rest, expected);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('refuses an index object whose runner predates its decode, naming the fix', () => {
|
|
142
|
+
// A stock runner as a package exported before runners said how they read
|
|
143
|
+
// a program's inputs: its platforms alone.
|
|
144
|
+
const PreDecodeRunnerType = VariantType({
|
|
145
|
+
east_node: StructType({ platforms: ArrayType(StringType) }),
|
|
146
|
+
east_py: StructType({ platforms: ArrayType(StringType) }),
|
|
147
|
+
east_c: StructType({ platforms: ArrayType(StringType) }),
|
|
148
|
+
custom: StructType({ command: ArrayType(StringType) }),
|
|
149
|
+
});
|
|
150
|
+
const PreDecodeIndexType = StructType({
|
|
151
|
+
keyIr: StringType, multi: BooleanType, valueIr: OptionType(StringType), keyType: EastTypeType,
|
|
152
|
+
valueType: EastTypeType, buildIr: StringType, runner: PreDecodeRunnerType,
|
|
153
|
+
});
|
|
154
|
+
const older = encodeBeast2For(PreDecodeIndexType)({ ...index, runner: variant('east_node', { platforms: ['@elaraai/east-node-std'] }) });
|
|
155
|
+
assert.throws(
|
|
156
|
+
() => decodeRecordIndexObject(older),
|
|
157
|
+
/^Error: the index object does not decode: the package was exported by an older e3 SDK — re-export it with the current one \(/,
|
|
158
|
+
);
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
|
|
116
162
|
describe('decodeMigrationObject', () => {
|
|
117
163
|
const RowV1Type = StructType({ name: StringType });
|
|
118
164
|
const RowV2Type = StructType({ name: StringType, shift: IntegerType });
|
|
@@ -122,7 +168,7 @@ describe('decodeMigrationObject', () => {
|
|
|
122
168
|
to: toEastTypeValue(DictType(StringType, RowV2Type)),
|
|
123
169
|
bodyIr: 'a'.repeat(64),
|
|
124
170
|
programIr: 'b'.repeat(64),
|
|
125
|
-
runner: variant('east_node', { platforms: ['@elaraai/east-node-std'] }),
|
|
171
|
+
runner: variant('east_node', { platforms: ['@elaraai/east-node-std'], decode: variant('lazy', null) }),
|
|
126
172
|
};
|
|
127
173
|
|
|
128
174
|
it('round-trips a current migration, its types among it', () => {
|
package/src/record.ts
CHANGED
|
@@ -200,6 +200,34 @@ export const RecordIndexObjectType = StructType({
|
|
|
200
200
|
export type RecordIndexObjectType = typeof RecordIndexObjectType;
|
|
201
201
|
export type RecordIndexObject = ValueTypeOf<typeof RecordIndexObjectType>;
|
|
202
202
|
|
|
203
|
+
const decodeCurrentRecordIndex = decodeBeast2For(RecordIndexObjectType);
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Decode a `RecordIndexObject`.
|
|
207
|
+
*
|
|
208
|
+
* @remarks
|
|
209
|
+
* A package-borne wire, so it changes by hard cutover: a package exported by
|
|
210
|
+
* an older SDK is re-exported with the current one, and this says so. A
|
|
211
|
+
* record's state names the index object each index was built under, so one an
|
|
212
|
+
* older SDK exported is read until a deploy of the re-exported package
|
|
213
|
+
* rebuilds the index under the current one.
|
|
214
|
+
*
|
|
215
|
+
* @param data - the stored bytes
|
|
216
|
+
* @returns the index object
|
|
217
|
+
* @throws {Error} When the bytes are not a current index object — a package
|
|
218
|
+
* exported by an older SDK, which is re-exported with the current one.
|
|
219
|
+
*/
|
|
220
|
+
export function decodeRecordIndexObject(data: Uint8Array): RecordIndexObject {
|
|
221
|
+
try {
|
|
222
|
+
return decodeCurrentRecordIndex(data);
|
|
223
|
+
} catch (err) {
|
|
224
|
+
throw new Error(
|
|
225
|
+
`the index object does not decode: the package was exported by an older e3 SDK — re-export it with the current one ` +
|
|
226
|
+
`(${err instanceof Error ? err.message : String(err)})`,
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
203
231
|
/**
|
|
204
232
|
* How a migration says what it changes.
|
|
205
233
|
*
|
package/src/release.ts
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
/** The release of e3 this build is. */
|
|
20
|
-
export const E3_RELEASE = '1.0.
|
|
20
|
+
export const E3_RELEASE = '1.0.85';
|
|
21
21
|
|
|
22
22
|
/** A semantic version: its core, a pre-release, and build metadata. */
|
|
23
23
|
const RELEASE = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
package/src/repository.ts
CHANGED
|
@@ -34,6 +34,24 @@ export const RepositoryRecordType = StructType({
|
|
|
34
34
|
|
|
35
35
|
export type RepositoryRecord = ValueTypeOf<typeof RepositoryRecordType>;
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
* A store upgrade under way in a repository, kept beside the repository
|
|
39
|
+
* record: the step, the release of e3 applying it, and where its last part
|
|
40
|
+
* stopped. A step applies in parts, and the next part takes it up from here,
|
|
41
|
+
* in this process or another; the record lists the step only once it is done.
|
|
42
|
+
*/
|
|
43
|
+
export const RepositoryUpgradeProgressType = StructType({
|
|
44
|
+
/** The step's name */
|
|
45
|
+
step: StringType,
|
|
46
|
+
/** The release of e3 applying it */
|
|
47
|
+
release: StringType,
|
|
48
|
+
/** Where its last part stopped: the step's own cursor, which the next part
|
|
49
|
+
* takes it up from */
|
|
50
|
+
cursor: StringType,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
export type RepositoryUpgradeProgress = ValueTypeOf<typeof RepositoryUpgradeProgressType>;
|
|
54
|
+
|
|
37
55
|
/**
|
|
38
56
|
* Where a repository is in its lifecycle.
|
|
39
57
|
*/
|
package/src/runner.spec.ts
CHANGED
|
@@ -18,9 +18,9 @@ import { variant } from '@elaraai/east';
|
|
|
18
18
|
import { withRunnerLifeline, type RunnerValue } from './runner.js';
|
|
19
19
|
|
|
20
20
|
const KNOWN: Array<[string, RunnerValue, string]> = [
|
|
21
|
-
['east_node', variant('east_node', { platforms: ['@elaraai/east-node-std'] }), 'east-node'],
|
|
22
|
-
['east_py', variant('east_py', { platforms: ['east-py-std'] }), 'east-py'],
|
|
23
|
-
['east_c', variant('east_c', { platforms: ['east-c-std'] }), 'east-c'],
|
|
21
|
+
['east_node', variant('east_node', { platforms: ['@elaraai/east-node-std'], decode: variant('lazy', null) }), 'east-node'],
|
|
22
|
+
['east_py', variant('east_py', { platforms: ['east-py-std'], decode: variant('lazy', null) }), 'east-py'],
|
|
23
|
+
['east_c', variant('east_c', { platforms: ['east-c-std'], decode: variant('lazy', null) }), 'east-c'],
|
|
24
24
|
];
|
|
25
25
|
|
|
26
26
|
describe('withRunnerLifeline', () => {
|
|
@@ -38,7 +38,7 @@ describe('withRunnerLifeline', () => {
|
|
|
38
38
|
});
|
|
39
39
|
|
|
40
40
|
it('is a no-op on a degenerate argv and does not mutate its input', () => {
|
|
41
|
-
const runner = variant('east_c', { platforms: [] });
|
|
41
|
+
const runner = variant('east_c', { platforms: [], decode: variant('lazy', null) });
|
|
42
42
|
assert.deepStrictEqual(withRunnerLifeline(runner, ['east-c']), ['east-c']);
|
|
43
43
|
const args = ['east-c', 'exec', 'unit.beast2'];
|
|
44
44
|
const before = [...args];
|
package/src/runner.ts
CHANGED
|
@@ -11,23 +11,26 @@
|
|
|
11
11
|
* its `exec` command; `custom` names a command of the author's own.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import { VariantType, StructType, ArrayType, StringType, ValueTypeOf } from '@elaraai/east';
|
|
14
|
+
import { VariantType, StructType, ArrayType, StringType, UnitDecodeType, ValueTypeOf } from '@elaraai/east';
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Wire representation of a runner — shared by tasks, functions and records.
|
|
18
18
|
*
|
|
19
|
-
* A known-runtime tag names a stock runner
|
|
20
|
-
* packages the units it runs load
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
19
|
+
* A known-runtime tag names a stock runner: `platforms` names the platform
|
|
20
|
+
* packages the units it runs load, and `decode` how it reads a run unit's
|
|
21
|
+
* inputs — each collection `lazy`, a segment decoded as the program reaches
|
|
22
|
+
* it, or every input decoded `whole` before the program runs — which e3 writes
|
|
23
|
+
* into each run unit it stages for the runner. `custom` carries a command,
|
|
24
|
+
* which e3 runs with the arguments of a stock runner's `run`: `-i` for each
|
|
25
|
+
* input, `-o` for the output, then the program's file. A custom task's body is
|
|
26
|
+
* its own command, and its runner's is empty. Package authors can already
|
|
27
|
+
* execute arbitrary commands via custom tasks, so `custom` grants no
|
|
28
|
+
* capability that tasks don't have.
|
|
26
29
|
*/
|
|
27
30
|
export const RunnerType = VariantType({
|
|
28
|
-
east_node: StructType({ platforms: ArrayType(StringType) }),
|
|
29
|
-
east_py: StructType({ platforms: ArrayType(StringType) }),
|
|
30
|
-
east_c: StructType({ platforms: ArrayType(StringType) }),
|
|
31
|
+
east_node: StructType({ platforms: ArrayType(StringType), decode: UnitDecodeType }),
|
|
32
|
+
east_py: StructType({ platforms: ArrayType(StringType), decode: UnitDecodeType }),
|
|
33
|
+
east_c: StructType({ platforms: ArrayType(StringType), decode: UnitDecodeType }),
|
|
31
34
|
custom: StructType({ command: ArrayType(StringType) }),
|
|
32
35
|
});
|
|
33
36
|
export type RunnerType = typeof RunnerType;
|