@elaraai/east-node-cli 1.0.76 → 1.0.78

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.
@@ -0,0 +1,159 @@
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
+ * The segment writer behind `run --emit` and `merge` (issue #770).
7
+ *
8
+ * Entries arrive one at a time and go out as segments of a streaming beast2
9
+ * writer on the output file: header at open, terminator and index at the
10
+ * finish, so every finished file is a complete canonical blob. Batches are
11
+ * re-sized byte-adaptively toward the paged-encode segment target from what
12
+ * the writer has actually emitted — the refinement `encodeBeast2PagedFor`
13
+ * applies, so a file written here segments exactly as that encoder segments
14
+ * the same value. Both commands write through this one class, which is what
15
+ * makes a merge's output byte-identical to the sink's for the same entries.
16
+ * Memory is one open batch whatever the output's size.
17
+ */
18
+ import { closeSync, openSync, writeSync } from 'fs';
19
+ import { Beast2Writer, BEAST2_PAGED_BATCH_DEFAULT, BEAST2_PAGED_PROBE_BATCH, BEAST2_PAGED_TARGET_BYTES_DEFAULT } from '@elaraai/east';
20
+ /** Writes all of `bytes` to `fd` — `writeSync` may write fewer bytes than
21
+ * asked, and a silently short write would corrupt the file. */
22
+ function writeAll(fd, bytes) {
23
+ let written = 0;
24
+ while (written < bytes.length) {
25
+ written += writeSync(fd, bytes, written, bytes.length - written);
26
+ }
27
+ }
28
+ /**
29
+ * A streaming, byte-adaptively batched writer of one canonical collection
30
+ * blob. Entries are elements (Array and Set) or `[key, value]` pairs (Dict).
31
+ */
32
+ export class EmitFileWriter {
33
+ kind;
34
+ outType;
35
+ fd;
36
+ writer;
37
+ headerBytes = -1;
38
+ batch = [];
39
+ written = 0;
40
+ nextBatch = BEAST2_PAGED_BATCH_DEFAULT;
41
+ probed = false;
42
+ /**
43
+ * Opens `path` for writing and emits the blob's header.
44
+ *
45
+ * @param kind - the collection kind
46
+ * @param outType - the collection's wire type
47
+ * @param path - the output file
48
+ */
49
+ constructor(kind, outType, path) {
50
+ this.kind = kind;
51
+ this.outType = outType;
52
+ this.fd = openSync(path, 'w');
53
+ // Frames deflate on worker threads (#763); the refinement below reads
54
+ // the emitted bytes through the writer's bounds.
55
+ this.writer = new Beast2Writer(outType, (bytes) => {
56
+ writeAll(this.fd, bytes);
57
+ if (this.headerBytes < 0)
58
+ this.headerBytes = bytes.length;
59
+ }, { parallel: true });
60
+ }
61
+ /**
62
+ * Appends `entry` under the flush rule: a full batch goes out only now
63
+ * that an entry which will not fold into it has arrived, so the batch's
64
+ * last entry is always still open for a fold.
65
+ *
66
+ * @param entry - the element, or the `[key, value]` pair
67
+ */
68
+ push(entry) {
69
+ if (this.batch.length >= this.nextBatch)
70
+ this.flush();
71
+ this.batch.push(entry);
72
+ this.probe();
73
+ }
74
+ /**
75
+ * Seeds the batch size from a throwaway encode of the first entries, as
76
+ * `encodeBeast2PagedFor` does — the same probe, over the same count, so
77
+ * one value segments the same whether it was returned or emitted.
78
+ *
79
+ * Opening at the element cap instead made the first segment as many
80
+ * elements as the cap whatever they weighed: for rows above the target's
81
+ * share that is one oversized segment and every later boundary shifted,
82
+ * so the same Dict written both ways hashed differently. The probed
83
+ * entries are then drained through the refined size, exactly as the
84
+ * paged encoder pumps its own probe.
85
+ */
86
+ probe() {
87
+ if (this.probed || this.batch.length < BEAST2_PAGED_PROBE_BATCH)
88
+ return;
89
+ this.probed = true;
90
+ let scratchBytes = 0;
91
+ const scratch = new Beast2Writer(this.outType, (bytes) => { scratchBytes += bytes.length; });
92
+ const scratchHeader = scratchBytes;
93
+ scratch.write(this.toValue(this.batch));
94
+ const avg = Math.max(1, (scratchBytes - scratchHeader) / this.batch.length);
95
+ this.nextBatch = Math.max(1, Math.min(BEAST2_PAGED_BATCH_DEFAULT, Math.floor(BEAST2_PAGED_TARGET_BYTES_DEFAULT / avg)));
96
+ if (this.nextBatch >= this.batch.length)
97
+ return;
98
+ // Wider than one probe batch: the entries held so far go out in
99
+ // refined-size segments, which is what the paged encoder writes.
100
+ const buffered = this.batch;
101
+ this.batch = [];
102
+ for (const item of buffered) {
103
+ if (this.batch.length >= this.nextBatch)
104
+ this.flush();
105
+ this.batch.push(item);
106
+ }
107
+ }
108
+ /**
109
+ * Replaces the open batch's last entry — where an adjacent equal key's
110
+ * fold lands.
111
+ *
112
+ * @param update - maps the last entry to its folded form
113
+ */
114
+ foldLast(update) {
115
+ const at = this.batch.length - 1;
116
+ this.batch[at] = update(this.batch[at]);
117
+ }
118
+ /** Writes the open batch and finalizes the blob (terminator + index). */
119
+ finishClose() {
120
+ this.flush();
121
+ this.writer.finish();
122
+ closeSync(this.fd);
123
+ }
124
+ /** Closes the file without finalizing it — after an error, the partial
125
+ * output carries no terminator or index. */
126
+ closeAbandoned() {
127
+ closeSync(this.fd);
128
+ }
129
+ flush() {
130
+ if (this.batch.length === 0)
131
+ return;
132
+ this.writer.write(this.toValue(this.batch));
133
+ this.written += this.batch.length;
134
+ this.batch = [];
135
+ this.nextBatch = this.refineNext();
136
+ }
137
+ toValue(items) {
138
+ return this.kind === 'dict' ? new Map(items) : this.kind === 'set' ? new Set(items) : items;
139
+ }
140
+ /** The batch size the paged encoder would choose after `bytes` of body
141
+ * for `this.written` elements. */
142
+ refine(bytes) {
143
+ const avg = Math.max(1, Math.max(1, bytes - Math.max(0, this.headerBytes)) / this.written);
144
+ return Math.max(1, Math.min(BEAST2_PAGED_BATCH_DEFAULT, Math.floor(BEAST2_PAGED_TARGET_BYTES_DEFAULT / avg)));
145
+ }
146
+ /** The next batch size. With frames deflating on workers the bytes
147
+ * written are only known within bounds; the refinement is monotone in
148
+ * them, so agreeing bounds are the serial decision and disagreeing ones
149
+ * wait for the frames — the segmentation never depends on timing. */
150
+ refineNext() {
151
+ const { lo, hi } = this.writer.emittedBounds();
152
+ const next = this.refine(lo);
153
+ if (next === this.refine(hi))
154
+ return next;
155
+ this.writer.settle();
156
+ return this.refine(this.writer.emittedBounds().lo);
157
+ }
158
+ }
159
+ //# sourceMappingURL=emit-writer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emit-writer.js","sourceRoot":"","sources":["../src/emit-writer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
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 type { PlatformFunction } from '@elaraai/east/internal';
6
+ /** Options accepted by {@link mergeBlobs}. */
7
+ export interface MergeBlobsOptions {
8
+ /** Dict inputs: an IR file (any format the IR positional accepts) holding
9
+ * a `(K, V, V) -> V` East function over the inputs' key and value types,
10
+ * compiled with `platformFns`. Equal keys fold with it in input order. */
11
+ mergePath?: string;
12
+ /** Set inputs: the first of equal elements stands. */
13
+ union?: boolean;
14
+ /** A beast2 blob of `Struct{from: Option<K>, to: Option<K>}` over the
15
+ * inputs' key type: only the keys in `[from, to)` merge; an absent
16
+ * bound is open. */
17
+ rangePath?: string;
18
+ /** The platforms the merge function compiles with. */
19
+ platformFns?: PlatformFunction[];
20
+ /** Print the account and timing on stderr. */
21
+ verbose?: boolean;
22
+ }
23
+ /** What a merge came to. */
24
+ export interface MergeBlobsStats {
25
+ /** Inputs merged. */
26
+ inputs: number;
27
+ /** Entries written to the output. */
28
+ entries: number;
29
+ /** Equal keys folded (merge) or collapsed (union). */
30
+ folds: number;
31
+ }
32
+ /**
33
+ * Merges sorted Set or Dict blobs of one type into one canonical blob — the
34
+ * `merge` command.
35
+ *
36
+ * @param inputPaths - the inputs, in the order equal keys fold; at least one
37
+ * @param outputPath - the output blob
38
+ * @param options - the fold, its platforms, the key range, verbosity
39
+ * @returns the account: inputs merged, entries written, keys folded
40
+ * @throws {Error} With the merge's message — an input of another type than
41
+ * input 0's, an Array input, keys that do not ascend, a fold whose
42
+ * signature does not match the inputs, a range blob of another shape than
43
+ * the bounds over the inputs' key type, a file that cannot be opened, a
44
+ * shared key without a fold — leaving the output unfinalised (no
45
+ * terminator or index).
46
+ */
47
+ export declare function mergeBlobs(inputPaths: readonly string[], outputPath: string, options?: MergeBlobsOptions): MergeBlobsStats;
48
+ //# sourceMappingURL=merge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAmDH,OAAO,KAAK,EAAiB,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAK9E,8CAA8C;AAC9C,MAAM,WAAW,iBAAiB;IAC9B;;+EAE2E;IAC3E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;yBAEqB;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,WAAW,CAAC,EAAE,gBAAgB,EAAE,CAAC;IACjC,8CAA8C;IAC9C,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,4BAA4B;AAC5B,MAAM,WAAW,eAAe;IAC5B,qBAAqB;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,qCAAqC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,sDAAsD;IACtD,KAAK,EAAE,MAAM,CAAC;CACjB;AA0LD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,UAAU,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,GAAE,iBAAsB,GAAG,eAAe,CAoG9H"}
package/dist/merge.js ADDED
@@ -0,0 +1,342 @@
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
+ * The blob merge behind `east-node merge` (issue #770).
7
+ *
8
+ * Canonical Set or Dict blobs of one type in — sorted, indexed beast2 v5
9
+ * collections, as every runner writes them — and one canonical blob out, in
10
+ * a single pass: every input is read segment by segment through positioned
11
+ * reads on its descriptor, a heap over the inputs' current entries yields
12
+ * keys in East order, and the output is written through the same segment
13
+ * writer as `run --emit`, so the file is byte-identical to what that sink
14
+ * writes for the same entries emitted ascending. Memory is one decoded
15
+ * segment per input plus one open batch; no temporary file is ever written.
16
+ *
17
+ * Equal keys across inputs fold in input order: with a merge function (Dict
18
+ * inputs) `acc = merge(key, acc, value)`; in union mode (Set inputs) the
19
+ * first element stands. Without a fold, an equal key is the duplicate error
20
+ * the emit sink raises. An input whose keys do not ascend, an input whose
21
+ * type is not input 0's, and an Array input are refused.
22
+ *
23
+ * With a key range (`--range`, a blob of `Struct{from: Option<K>, to:
24
+ * Option<K>}` over the inputs' key type) only the keys in `[from, to)`
25
+ * merge: every input is sought to the segment owning `from` through its
26
+ * fences and read up to the first key at or past `to`, so a unit over a
27
+ * range of a large output reads that range's share of each input, plus at
28
+ * most one segment. An absent bound is open; both absent is the whole merge.
29
+ *
30
+ * This is the fan-in of a partitioned task's keyed partials: e3 runs it as an
31
+ * ordinary execution on the task's runner, one unit per key range of a group
32
+ * of partials, and never decodes a partial itself. east-c and east-py merge
33
+ * through the same contract, so the three runners write the same bytes.
34
+ */
35
+ import { closeSync, fstatSync, openSync, readFileSync, readSync } from 'fs';
36
+ import { OptionType, SortedMap, SortedSet, StructType, compareFor, decodeBeast2For, fromEastTypeValue, isTypeValueEqual, openBeast2LazyFor, printFor, readBeast2Extents, readBeast2HeaderType, readBeast2Type, toEastTypeValue, } from '@elaraai/east';
37
+ import { printTypeValue } from '@elaraai/east/internal';
38
+ import { EmitFileWriter } from './emit-writer.js';
39
+ import { duplicateMessage, formatFileSize, loadMergeFunction } from './runner.js';
40
+ /**
41
+ * Opens input `index` at `path`: reads its geometry and type, and prepares a
42
+ * segment-by-segment iteration of its entries — from the first key, or from
43
+ * a range's lower bound, whose owning segment the lazy value seeks through
44
+ * the fences.
45
+ *
46
+ * @param path - the blob
47
+ * @param index - the input's position, for messages
48
+ * @param expected - input 0's type, or `null` for input 0 itself
49
+ * @returns the opened input and its type
50
+ * @throws {Error} When the file cannot be opened (missing, unreadable, not
51
+ * a regular file), is not a blob (too short, or without the magic — the
52
+ * reader's own words), cannot be read as a canonical collection blob, or
53
+ * its type is not `expected`.
54
+ */
55
+ function openInput(path, index, expected) {
56
+ let fd;
57
+ let size;
58
+ try {
59
+ fd = openSync(path, 'r');
60
+ }
61
+ catch {
62
+ throw new Error(`merge: input ${index} (${path}): cannot open the file`);
63
+ }
64
+ try {
65
+ const stat = fstatSync(fd);
66
+ if (!stat.isFile())
67
+ throw new Error('not a regular file');
68
+ size = stat.size;
69
+ }
70
+ catch {
71
+ closeSync(fd);
72
+ throw new Error(`merge: input ${index} (${path}): cannot open the file`);
73
+ }
74
+ const reader = {
75
+ size,
76
+ read(offset, length) {
77
+ const out = new Uint8Array(length);
78
+ let done = 0;
79
+ while (done < length) {
80
+ const n = readSync(fd, out, done, length - done, offset + done);
81
+ if (n === 0)
82
+ throw new Error(`beast2: short read at offset ${offset + done}`);
83
+ done += n;
84
+ }
85
+ return out;
86
+ },
87
+ };
88
+ const prefixed = (err) => new Error(`merge: input ${index} (${path}): ${err.message ?? String(err)}`);
89
+ let type;
90
+ let lazy;
91
+ try {
92
+ // The header first: a file too short for a blob, or one without the
93
+ // magic, is refused in the reader's words — the sentence east-c and
94
+ // east-py give for the same bytes — before the index is looked for.
95
+ readBeast2HeaderType(reader);
96
+ const extents = readBeast2Extents(reader);
97
+ type = extents.typeValue;
98
+ if (expected !== null && !isTypeValueEqual(type, expected)) {
99
+ closeSync(fd);
100
+ throw new Error(`merge: input ${index} (${path}) has type ${printTypeValue(type)}, expected ${printTypeValue(expected)} (input 0)`);
101
+ }
102
+ if (type.type !== 'Set' && type.type !== 'Dict') {
103
+ closeSync(fd);
104
+ throw new Error(`merge: inputs must be Set or Dict blobs, got ${type.type}`);
105
+ }
106
+ if (!extents.selfContained)
107
+ throw new Error('the blob is not self-contained');
108
+ lazy = openBeast2LazyFor(type, { frozen: true })(reader);
109
+ }
110
+ catch (err) {
111
+ const message = err.message ?? '';
112
+ if (message.startsWith('merge: '))
113
+ throw err;
114
+ closeSync(fd);
115
+ throw prefixed(err);
116
+ }
117
+ const dict = type.type === 'Dict';
118
+ return {
119
+ type,
120
+ close: () => closeSync(fd),
121
+ start: (range, cmp) => {
122
+ // The lazy value streams one decoded segment at a time, checking
123
+ // the ascent within and across segments as the eager decoder does;
124
+ // from a lower bound it starts at the segment owning it.
125
+ let iterator;
126
+ try {
127
+ iterator = dict
128
+ ? lazy.entries(range.from)
129
+ : lazy.keys(range.from);
130
+ }
131
+ catch (err) {
132
+ throw prefixed(err);
133
+ }
134
+ let exhausted = false;
135
+ const input = {
136
+ head: null,
137
+ advance: () => {
138
+ if (exhausted)
139
+ return;
140
+ let next;
141
+ try {
142
+ next = iterator.next();
143
+ }
144
+ catch (err) {
145
+ throw prefixed(err);
146
+ }
147
+ if (next.done) {
148
+ exhausted = true;
149
+ input.head = null;
150
+ return;
151
+ }
152
+ const [key, value] = dict ? next.value : [next.value, undefined];
153
+ // The first key at or past the upper bound ends the input.
154
+ if (range.to !== undefined && cmp(key, range.to) >= 0) {
155
+ exhausted = true;
156
+ input.head = null;
157
+ return;
158
+ }
159
+ input.head = { key, value };
160
+ },
161
+ };
162
+ return input;
163
+ },
164
+ };
165
+ }
166
+ /**
167
+ * Reads a `--range` blob: `Struct{from: Option<K>, to: Option<K>}` over the
168
+ * inputs' key type, self-describing, checked against that type.
169
+ *
170
+ * @param path - the blob
171
+ * @param keyType - the inputs' key (Dict) or element (Set) type
172
+ * @returns the bounds, an absent one `undefined`
173
+ * @throws {Error} When the file cannot be opened or read as a beast2 blob,
174
+ * or its type is not the bounds struct over the inputs' key type.
175
+ */
176
+ function readRange(path, keyType) {
177
+ let bytes;
178
+ try {
179
+ bytes = new Uint8Array(readFileSync(path));
180
+ }
181
+ catch {
182
+ throw new Error(`merge: --range (${path}): cannot open the file`);
183
+ }
184
+ // The same shape east-c builds (merge.c) and e3-core writes
185
+ // (partitionExec.ts): the bounds struct over the key type.
186
+ const key = fromEastTypeValue(keyType);
187
+ const rangeType = toEastTypeValue(StructType({ from: OptionType(key), to: OptionType(key) }));
188
+ let type;
189
+ try {
190
+ type = readBeast2Type(bytes);
191
+ }
192
+ catch (err) {
193
+ throw new Error(`merge: --range (${path}): ${err.message ?? String(err)}`);
194
+ }
195
+ if (!isTypeValueEqual(type, rangeType)) {
196
+ throw new Error(`merge: --range (${path}) has type ${printTypeValue(type)}, expected ${printTypeValue(rangeType)} (bounds over the inputs' key type)`);
197
+ }
198
+ let bounds;
199
+ try {
200
+ bounds = decodeBeast2For(rangeType)(bytes);
201
+ }
202
+ catch (err) {
203
+ throw new Error(`merge: --range (${path}): ${err.message ?? String(err)}`);
204
+ }
205
+ return {
206
+ from: bounds.from.type === 'some' ? bounds.from.value : undefined,
207
+ to: bounds.to.type === 'some' ? bounds.to.value : undefined,
208
+ };
209
+ }
210
+ /**
211
+ * Merges sorted Set or Dict blobs of one type into one canonical blob — the
212
+ * `merge` command.
213
+ *
214
+ * @param inputPaths - the inputs, in the order equal keys fold; at least one
215
+ * @param outputPath - the output blob
216
+ * @param options - the fold, its platforms, the key range, verbosity
217
+ * @returns the account: inputs merged, entries written, keys folded
218
+ * @throws {Error} With the merge's message — an input of another type than
219
+ * input 0's, an Array input, keys that do not ascend, a fold whose
220
+ * signature does not match the inputs, a range blob of another shape than
221
+ * the bounds over the inputs' key type, a file that cannot be opened, a
222
+ * shared key without a fold — leaving the output unfinalised (no
223
+ * terminator or index).
224
+ */
225
+ export function mergeBlobs(inputPaths, outputPath, options = {}) {
226
+ const started = performance.now();
227
+ if (inputPaths.length === 0)
228
+ throw new Error('merge: at least one input is needed');
229
+ if (options.mergePath !== undefined && options.union) {
230
+ throw new Error('merge: --merge and --union are two folds — give one');
231
+ }
232
+ const opened = [];
233
+ const inputs = [];
234
+ let out = null;
235
+ let finished = false;
236
+ try {
237
+ const first = openInput(inputPaths[0], 0, null);
238
+ opened.push(first);
239
+ const type = first.type;
240
+ const kind = type.type === 'Dict' ? 'dict' : 'set';
241
+ if (options.mergePath !== undefined && kind !== 'dict')
242
+ throw new Error('--merge applies to Dict inputs only');
243
+ if (options.union && kind !== 'set')
244
+ throw new Error('--union applies to Set inputs only');
245
+ const keyType = (kind === 'dict' ? type.value.key : type.value);
246
+ const valueType = (kind === 'dict' ? type.value.value : null);
247
+ const merge = options.mergePath !== undefined
248
+ ? loadMergeFunction(options.mergePath, keyType, valueType, options.platformFns ?? [], 'the inputs')
249
+ : null;
250
+ const union = options.union ?? false;
251
+ const range = options.rangePath !== undefined ? readRange(options.rangePath, keyType) : { from: undefined, to: undefined };
252
+ for (let i = 1; i < inputPaths.length; i++)
253
+ opened.push(openInput(inputPaths[i], i, type));
254
+ const cmp = compareFor(keyType);
255
+ const printKey = printFor(keyType);
256
+ for (const input of opened)
257
+ inputs.push(input.start(range, cmp));
258
+ for (const input of inputs)
259
+ input.advance();
260
+ // A binary min-heap over the inputs' current keys, ordered by (key,
261
+ // input index), so equal keys leave in input order.
262
+ const heap = [];
263
+ for (let i = 0; i < inputs.length; i++)
264
+ if (inputs[i].head !== null)
265
+ heap.push(i);
266
+ const before = (a, b) => {
267
+ const order = cmp(inputs[a].head.key, inputs[b].head.key);
268
+ return order < 0 || (order === 0 && a < b);
269
+ };
270
+ const siftDown = (at) => {
271
+ for (;;) {
272
+ const l = 2 * at + 1;
273
+ const r = l + 1;
274
+ let least = at;
275
+ if (l < heap.length && before(heap[l], heap[least]))
276
+ least = l;
277
+ if (r < heap.length && before(heap[r], heap[least]))
278
+ least = r;
279
+ if (least === at)
280
+ return;
281
+ [heap[at], heap[least]] = [heap[least], heap[at]];
282
+ at = least;
283
+ }
284
+ };
285
+ for (let i = (heap.length >> 1) - 1; i >= 0; i--)
286
+ siftDown(i);
287
+ out = new EmitFileWriter(kind, type, outputPath);
288
+ let entries = 0;
289
+ let folds = 0;
290
+ // The current key's entry is held until a greater key arrives, so
291
+ // every equal key folds into it before it is written.
292
+ let held = null;
293
+ const put = (entry) => {
294
+ out.push(kind === 'dict' ? [entry.key, entry.value] : entry.key);
295
+ entries++;
296
+ };
297
+ while (heap.length > 0) {
298
+ const input = inputs[heap[0]];
299
+ const { key, value } = input.head;
300
+ if (held !== null && cmp(held.key, key) === 0) {
301
+ if (merge !== null) {
302
+ held.value = merge(key, held.value, value);
303
+ }
304
+ else if (!union) {
305
+ throw new Error(duplicateMessage(kind, printKey, key));
306
+ }
307
+ folds++;
308
+ }
309
+ else {
310
+ if (held !== null)
311
+ put(held);
312
+ held = { key, value };
313
+ }
314
+ input.advance();
315
+ if (input.head === null) {
316
+ heap[0] = heap[heap.length - 1];
317
+ heap.pop();
318
+ }
319
+ if (heap.length > 0)
320
+ siftDown(0);
321
+ }
322
+ if (held !== null)
323
+ put(held);
324
+ out.finishClose();
325
+ finished = true;
326
+ const stats = { inputs: inputs.length, entries, folds };
327
+ if (options.verbose) {
328
+ console.error(`merge: ${stats.inputs} input(s), ${stats.entries} entries, ${stats.folds} fold(s)`);
329
+ console.error(`Output: ${outputPath} (${formatFileSize(outputPath)})`);
330
+ console.error('\nTiming:');
331
+ console.error(` Total: ${(performance.now() - started).toFixed(1).padStart(8)} ms`);
332
+ }
333
+ return stats;
334
+ }
335
+ finally {
336
+ for (const input of opened)
337
+ input.close();
338
+ if (out !== null && !finished)
339
+ out.closeAbandoned();
340
+ }
341
+ }
342
+ //# sourceMappingURL=merge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge.js","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"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"}
package/dist/runner.d.ts CHANGED
@@ -2,8 +2,22 @@
2
2
  * Copyright (c) 2025 Elara AI Pty Ltd
3
3
  * Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
4
4
  */
5
- import type { PlatformFunction } from '@elaraai/east/internal';
5
+ import type { PlatformFunction, EastTypeValue } from '@elaraai/east/internal';
6
+ /** A file's size for the verbose account, or `?` when it cannot be read. @internal */
7
+ export declare function formatFileSize(path: string): string;
6
8
  /** Streaming-execution options accepted by {@link runProgram}. */
9
+ /**
10
+ * A refusal of the command line itself — a flag combination, an output
11
+ * destination, a function shape the flags do not fit.
12
+ *
13
+ * These are the user's errors, not the program's, and east-c and east-py
14
+ * answer them with one sentence on stderr. Marking them lets the CLI do the
15
+ * same instead of printing the JS stack it prints for an error thrown from
16
+ * East or from a platform function, where the frames are the diagnosis.
17
+ */
18
+ export declare class UsageError extends Error {
19
+ constructor(message: string);
20
+ }
7
21
  export interface RunProgramOptions {
8
22
  /** Enable verbose timing/memory output on stderr. */
9
23
  verbose?: boolean;
@@ -12,11 +26,19 @@ export interface RunProgramOptions {
12
26
  * collection kind; element/key/value types come from the emit
13
27
  * parameter's function type. */
14
28
  emit?: 'array' | 'set' | 'dict';
15
- /** Feed the given `-i` input (0-based) lazily — segment-by-segment
29
+ /** Feed these `-i` inputs (0-based) lazily — segment-by-segment
16
30
  * iteration with O(segment) decoded memory — regardless of size.
17
31
  * Element shapes the lazy contract excludes (nested mutable
18
32
  * containers, vectors/matrices, functions) decode whole instead. */
19
- streamInput?: number;
33
+ streamInputs?: number[];
34
+ /** With `emit: 'dict'`: an IR file (any format the IR positional
35
+ * accepts) holding a `(K, V, V) -> V` East function over the emit
36
+ * parameter's key and value types, compiled with the run's platforms.
37
+ * Adjacent emissions with an equal key fold left with it, in emission
38
+ * order. */
39
+ merge?: string;
40
+ /** With `emit: 'set'`: adjacent equal elements collapse to the first. */
41
+ union?: boolean;
20
42
  /** Open indexed beast2 collection inputs at or above this many bytes as
21
43
  * lazy pager-backed values (0 disables). Defaults to 64 MiB, or the
22
44
  * `EAST_LAZY_INPUT_BYTES` environment variable. Applies only to
@@ -34,4 +56,32 @@ export declare function lazyThreshold(options: RunProgramOptions): number;
34
56
  * Runs an East IR program.
35
57
  */
36
58
  export declare function runProgram(irPath: string, platformFns: PlatformFunction[], packages: string[], inputPaths: string[], outputPath?: string, options?: RunProgramOptions | boolean): Promise<unknown>;
59
+ /** A compiled `(K, V, V) -> V` fold of equal keys. */
60
+ export type MergeFunction = (key: unknown, acc: unknown, value: unknown) => unknown;
61
+ /**
62
+ * Loads a `--merge` function: an IR file holding a `(K, V, V) -> V` East
63
+ * function over the given key and value types, compiled with the run's
64
+ * platforms. Shared by the emit sink and the blob merge.
65
+ *
66
+ * @param path - the IR file (any format the IR positional accepts)
67
+ * @param keyType - the key type
68
+ * @param valueType - the value type
69
+ * @param platformFns - the run's platforms
70
+ * @param subject - what the function must match, for the message: `the emit
71
+ * parameter` or `the inputs`
72
+ * @returns the compiled merge function
73
+ * @throws {Error} When the IR is not a function of that shape, naming the
74
+ * expected and the actual types.
75
+ */
76
+ export declare function loadMergeFunction(path: string, keyType: EastTypeValue, valueType: EastTypeValue, platformFns: PlatformFunction[], subject: string): MergeFunction;
77
+ /**
78
+ * The canonical duplicate-key error, identical across runners: the emit
79
+ * sink's, and the blob merge's when a key is shared without a fold.
80
+ *
81
+ * @param kind - the collection kind
82
+ * @param printKey - the key type's printer
83
+ * @param key - the repeated key
84
+ * @returns the message
85
+ */
86
+ export declare function duplicateMessage(kind: 'set' | 'dict', printKey: (v: unknown) => string, key: unknown): string;
37
87
  //# sourceMappingURL=runner.d.ts.map