# core/reporters/journal.reporter.ts

> 129 lines of code and 25 definitions.

Tree: Bane's Lab Deploy
Language: typescript
Layer: operations
Canonical: https://banes-lab.com/anatomy/deploy#file-deploy-core-reporters-journal-reporter-ts
Source text: https://banes-lab.com/source/deploy/core/reporters/journal.reporter.ts.txt

Listed in [core/reporters](https://banes-lab.com/api/source/deploy/core/reporters.md), after [core/reporters/deployment.reporter.ts](https://banes-lab.com/source/deploy/core/reporters/deployment.reporter.ts.md) and before [core/reporters/nginx.reporter.ts](https://banes-lab.com/source/deploy/core/reporters/nginx.reporter.ts.md).

## Definitions

- `now` (lexical_declaration, line 28, exported)
- `stepOf` (lexical_declaration, line 69)
- `draw` (lexical_declaration, line 84, exported)
- `report` (lexical_declaration, line 97, exported)
- `createProgress` (lexical_declaration, line 73, exported)
- `log` (lexical_declaration, line 23, exported)
- `note` (lexical_declaration, line 88, exported)
- `write` (lexical_declaration, line 16)
- `mark` (lexical_declaration, line 27, exported)
- `createByteProgress` (lexical_declaration, line 47, exported)
- `createJournal` (lexical_declaration, line 20, exported)
- `steps` (lexical_declaration, line 21, exported)
- `lastStepAt` (lexical_declaration, line 22, exported)
- `duration` (lexical_declaration, line 29, exported)
- `summary` (lexical_declaration, line 33, exported)
- `joined` (lexical_declaration, line 34, exported)
- `progress` (lexical_declaration, line 54, exported)
- `reached` (lexical_declaration, line 55, exported)
- `crossed` (lexical_declaration, line 62, exported)
- `live` (lexical_declaration, line 79, exported)
- `state` (lexical_declaration, line 80, exported)
- `drawnAt` (lexical_declaration, line 81, exported)
- `logged` (lexical_declaration, line 82, exported)
- `journaled` (lexical_declaration, line 83, exported)
- `percent` (lexical_declaration, line 98, exported)

## Contained in

- [core/reporters](https://banes-lab.com/anatomy/deploy/folder-deploy-core-reporters.md)

## Uses

- [core/formatters/journal.formatter.ts](https://banes-lab.com/source/deploy/core/formatters/journal.formatter.ts.md)

## Linked from

- [core/formatters](https://banes-lab.com/anatomy/deploy/folder-deploy-core-formatters.md)
- [core/reporters](https://banes-lab.com/anatomy/deploy/folder-deploy-core-reporters.md)

## Source

```typescript
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,
    };
};
```
