core/reporters/journal.reporter.ts
core/reporters/journal.reporter.ts is a file in Bane's Lab Deploy. 129 lines of code and 25 definitions.
import type { ByteStep, Journal, Progress, ProgressSink, TransferPlan, TransferState } from "#types/deployment.types";
import {
CARRIAGE_RETURN,
CLEAR_LINE,
ELLIPSIS,
LINE_BREAK,
PROGRESS_JOURNAL_STEP,
PROGRESS_LOG_STEP,
PROGRESS_REDRAW_MS,
STEP_SUMMARY_LIMIT,
} from "#configuration/constants/deployment.constants";
import { percentOf, transferLineOf } from "#core/formatters/journal.formatter";
import { SECONDS_SUFFIX } from "#configuration/strings/deployment.strings";
import { formatDuration } from "#core/converters/text.converter";
const write = function write(message: string): void {
process.stdout.write(message + LINE_BREAK);
};
export const createJournal = function createJournal(): Journal {
const steps: string[] = [];
let lastStepAt = Date.now();
const log = function log(message: string): void {
steps.push(`[${new Date().toLocaleTimeString()}] ${message}`);
write(message);
};
const mark = function mark(message: string): void {
const now = Date.now();
const duration = formatDuration(now - lastStepAt);
lastStepAt = now;
log(`${message} (${duration}${SECONDS_SUFFIX})`);
};
const summary = function summary(): string {
const joined = steps.join(LINE_BREAK);
return joined.length > STEP_SUMMARY_LIMIT ? joined.slice(0, STEP_SUMMARY_LIMIT) + ELLIPSIS : joined;
};
return {
error: (message) => {
console.error(message);
},
log,
mark,
summary,
};
};
export const createByteProgress = function createByteProgress(
journal: Journal,
label: string,
unit: string,
sink: ProgressSink = process.stdout,
now: () => number = Date.now,
): { readonly finish: () => void; readonly step: ByteStep } {
let progress: Progress | null = null;
let reached = 0;
return {
finish: () => {
progress?.finish();
},
step: (transferred, size) => {
progress ??= createProgress(journal, { label, size, total: 1, unit }, sink, now);
const crossed = reached < size && transferred >= size ? 1 : 0;
progress.advance(crossed, transferred - reached, false);
reached = transferred;
},
};
};
const stepOf = function stepOf(percent: number, step: number): number {
return Math.floor(percent / step);
};
export const createProgress = function createProgress(
journal: Journal,
plan: TransferPlan,
sink: ProgressSink = process.stdout,
now: () => number = Date.now,
): Progress {
const live = sink.isTTY === true;
let state: TransferState = { ...plan, done: 0, failed: 0, startedAt: now(), transferred: 0 };
let drawnAt = 0;
let logged = 0;
let journaled = 0;
const draw = function draw(): void {
sink.write(CARRIAGE_RETURN + CLEAR_LINE + transferLineOf(state, now()));
drawnAt = now();
};
const note = function note(message: string): void {
if (live) {
sink.write(CARRIAGE_RETURN + CLEAR_LINE);
}
journal.log(message);
if (live) {
draw();
}
};
const report = function report(): void {
const percent = percentOf(state);
if (stepOf(percent, PROGRESS_JOURNAL_STEP) > journaled) {
journaled = stepOf(percent, PROGRESS_JOURNAL_STEP);
logged = stepOf(percent, PROGRESS_LOG_STEP);
note(transferLineOf(state, now()));
return;
}
if (live) {
if (now() - drawnAt >= PROGRESS_REDRAW_MS) {
draw();
}
return;
}
if (stepOf(percent, PROGRESS_LOG_STEP) > logged) {
logged = stepOf(percent, PROGRESS_LOG_STEP);
sink.write(transferLineOf(state, now()) + LINE_BREAK);
}
};
return {
advance: (items, bytes, failed) => {
state = {
...state,
done: state.done + (failed ? 0 : items),
failed: state.failed + (failed ? items : 0),
transferred: state.transferred + bytes,
};
report();
},
finish: () => {
if (live) {
draw();
sink.write(LINE_BREAK);
}
},
note,
};
};