core/converters/route.converter.ts

core/converters/route.converter.ts is a file in Bane's Lab Build Scripts. 70 lines of code and 20 definitions.

import type { DiscoveredRoute, Discovery } from "#types/site.types";
import type { HeldStamp, RouteDates, RouteLedger, RouteStamp, StampedRoutes } from "#types/route.types";
import { isoDate, renderRoutePayload } from "#core/formatters/site.formatter";
import { ledgerNotObject, malformedStamps, undatedRoute } from "#configuration/strings/route.strings";
import type { PageRoute } from "#types/page.types";
import { fingerprintOf } from "@govlab/content-fingerprint";
import { isRecord } from "#core/selectors/base.selector";

const SAMPLE_LENGTH = 80;

const hasText = function hasText(value: object, key: string): boolean {
    return key in value && typeof Reflect.get(value, key) === "string";
};

const hasOptionalText = function hasOptionalText(value: object, key: string): boolean {
    return !(key in value) || hasText(value, key);
};

const isStamp = function isStamp(value: unknown): value is HeldStamp {
    return (
        isRecord(value) &&
        hasText(value, "fingerprint") &&
        hasText(value, "lastmod") &&
        hasOptionalText(value, "published")
    );
};

export const parseLedger = function parseLedger(text: string): RouteLedger {
    const parsed: unknown = JSON.parse(text);
    if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
        throw new TypeError(ledgerNotObject(text.slice(0, SAMPLE_LENGTH)));
    }
    const malformed = Object.entries(parsed).filter((entry) => !isStamp(entry[1]));
    if (malformed.length > 0) {
        throw new TypeError(malformedStamps(malformed.map(([path]) => path)));
    }
    return Object.fromEntries(
        Object.entries(parsed).filter((entry): entry is [string, HeldStamp] => isStamp(entry[1])),
    );
};

export const latestStamp = function latestStamp(ledger: RouteLedger): string | null {
    const dates = Object.values(ledger).flatMap((stamp) => (stamp === undefined ? [] : [stamp.lastmod]));
    return dates.toSorted((left, right) => left.localeCompare(right)).at(-1) ?? null;
};

export const routeFingerprint = function routeFingerprint(discovery: Discovery, route: DiscoveredRoute): string {
    return fingerprintOf([route.title, route.description, route.markdown, renderRoutePayload(discovery, route)]);
};

export const datedRoutes = function datedRoutes(
    routes: readonly PageRoute[],
    dates: StampedRoutes["dates"],
): readonly PageRoute[] {
    return routes.map((route) => {
        const stamp = dates.get(route.path);
        if (stamp === undefined) {
            throw new Error(undatedRoute(route.path));
        }
        return { ...route, dates: { modified: stamp.lastmod, published: stamp.published } };
    });
};

export const stampRoutes = function stampRoutes(ledger: RouteLedger, discovery: Discovery, moment: Date): StampedRoutes {
    const today = isoDate(moment);
    const next: Record<string, RouteStamp> = {};
    const stamps = new Map<string, string>();
    const dates = new Map<string, RouteDates>();
    for (const route of discovery.routes) {
        const fingerprint = routeFingerprint(discovery, route);
        const held = ledger[route.path];
        const lastmod = held?.fingerprint === fingerprint ? held.lastmod : today;
        const published = held?.published ?? held?.lastmod ?? today;
        next[route.path] = { fingerprint, lastmod, published };
        stamps.set(route.path, lastmod);
        dates.set(route.path, { lastmod, published });
    }
    return { dates, ledger: next, stamps };
};