@workflow-manager/runner
Version:
CLI runner for in-memory and markdown workflow orchestration using ATEP-like envelopes
79 lines (78 loc) • 2.63 kB
JavaScript
import { splitLines } from "../runFormat.js";
const DEFAULT_MAX_LINES_PER_STEP = 2000;
// Sentinel bucket key for chunks/meta with no stepKey (workflow-level output).
// A symbol can never collide with a real (string) step key.
const WORKFLOW_BUCKET_KEY = Symbol("workflow");
function bucketKey(stepKey) {
return stepKey ?? WORKFLOW_BUCKET_KEY;
}
export class TuiLogStore {
maxLinesPerStep;
buckets = new Map();
// Per-bucket, per-stream partial line remainders awaiting a terminating newline.
partials = new Map();
constructor(options) {
this.maxLinesPerStep = options?.maxLinesPerStep ?? DEFAULT_MAX_LINES_PER_STEP;
}
appendChunk(log) {
const key = bucketKey(log.stepKey);
const streamMap = this.partials.get(key) ?? new Map();
if (!this.partials.has(key)) {
this.partials.set(key, streamMap);
}
const existing = streamMap.get(log.stream) ?? "";
const { lines, remainder } = splitLines(existing + log.text);
streamMap.set(log.stream, remainder);
if (lines.length === 0) {
return;
}
const bucket = this.bucketFor(key);
for (const line of lines) {
this.pushLine(bucket, { kind: log.stream, text: line });
}
}
appendMeta(stepKey, text) {
const bucket = this.bucketFor(bucketKey(stepKey));
this.pushLine(bucket, { kind: "meta", text });
}
flushPartialLines() {
for (const [key, streamMap] of this.partials.entries()) {
for (const [stream, text] of streamMap.entries()) {
if (!text) {
continue;
}
const bucket = this.bucketFor(key);
this.pushLine(bucket, { kind: stream, text });
}
}
this.partials.clear();
}
tail(stepKey, count) {
const bucket = this.buckets.get(bucketKey(stepKey));
if (!bucket || count <= 0) {
return [];
}
if (count >= bucket.length) {
return bucket.slice();
}
return bucket.slice(bucket.length - count);
}
lineCount(stepKey) {
return this.buckets.get(bucketKey(stepKey))?.length ?? 0;
}
bucketFor(key) {
const existing = this.buckets.get(key);
if (existing) {
return existing;
}
const bucket = [];
this.buckets.set(key, bucket);
return bucket;
}
pushLine(bucket, line) {
bucket.push(line);
while (bucket.length > this.maxLinesPerStep) {
bucket.shift();
}
}
}