core/coordinators/export.coordinator.ts

core/coordinators/export.coordinator.ts is a file in Bane's Lab Social Share. 64 lines of code and 14 definitions.

import type { CaptureJob, ExportOptions, ExportResult } from "#types/stage.types";
import { SUBJECT_SEPARATOR, UNKNOWN_CARD } from "#configuration/strings/card.strings";
import {
    ledgerKeyOf,
    outdatedJobs,
    pruneOrphans,
    readLedger,
    shareEntriesOf,
    writeImages,
    writeLedger,
    writeShareMap,
} from "#core/persistence/image.persistence";
import type { CardSpec } from "#types/card.types";
import type { RenderedImage } from "#types/image.types";
import { absolutePath } from "@ssot/paths";
import { captureCards } from "#core/coordinators/card.coordinator";
import { profilesOf } from "#core/converters/filename.converter";
import { publishShares } from "#core/persistence/export.persistence";

const scopeOf = function scopeOf(specs: readonly CardSpec[], only: string | null): readonly CardSpec[] {
    if (only === null) {
        return specs;
    }
    const chosen = specs.filter((spec) => spec.id === only);
    if (chosen.length === 0) {
        throw new Error(UNKNOWN_CARD + SUBJECT_SEPARATOR + only);
    }
    return chosen;
};

const everyJob = function everyJob(specs: readonly CardSpec[]): readonly CaptureJob[] {
    return specs.flatMap((spec) => profilesOf(spec).map((profile) => ({ profile, spec })));
};

const keyOf = function keyOf(job: CaptureJob): string {
    return ledgerKeyOf(job.spec.id, job.profile.id);
};

export const exportCards = async function exportCards(
    options: ExportOptions,
    specs: readonly CardSpec[],
): Promise<ExportResult> {
    const renders = absolutePath("app.cardRenders");
    const location = absolutePath("app.shareLedger");
    const ledger = readLedger(location);
    const scoped = scopeOf(specs, options.only);
    const jobs = options.force ? everyJob(scoped) : outdatedJobs(scoped, ledger, renders);
    const persist = async (images: readonly RenderedImage[]): Promise<void> => {
        writeImages(images, renders);
        await writeLedger(location, readLedger(location), images, specs);
    };
    if (jobs.length > 0) {
        await captureCards({ gpu: options.gpu, onCaptured: persist, url: options.url }, jobs);
    }
    const pruned = pruneOrphans(specs, renders);
    await writeLedger(location, readLedger(location), [], specs);
    const entries = shareEntriesOf(specs, renders);
    publishShares(entries, renders, absolutePath("app.shares"));
    await writeShareMap(absolutePath("app.shareMap"), entries);
    const rendered = new Set(jobs.map(keyOf));
    return {
        current: everyJob(scoped)
            .map(keyOf)
            .filter((key) => !rendered.has(key)),
        pruned,
        rendered: [...rendered],
    };
};