core/stores/fingerprint.store.ts

core/stores/fingerprint.store.ts is a file in Content Fingerprint. 48 lines of code and 10 definitions.

import type { FingerprintIndex, FingerprintIndexOptions } from "#types/fingerprint.types";
import { existsSync, mkdirSync, readFileSync } from "node:fs";
import { INDEX_INDENT } from "#configuration/constants/fingerprint.constants";
import { dirname } from "node:path";
import { writeVerbatim } from "@govlab/canonical-write";

const isStringRecord = function isStringRecord(value: unknown): value is Record<string, string> {
    return (
        typeof value === "object" &&
        value !== null &&
        !Array.isArray(value) &&
        Object.values(value).every((held) => typeof held === "string")
    );
};

const parsedIndex = function parsedIndex(text: string): unknown {
    try {
        return JSON.parse(text);
    } catch (error) {
        if (error instanceof SyntaxError) {
            return null;
        }
        throw error;
    }
};

const readIndex = function readIndex(file: string): Record<string, string> {
    if (!existsSync(file)) {
        return {};
    }
    const parsed = parsedIndex(readFileSync(file, "utf8"));
    return isStringRecord(parsed) ? parsed : {};
};

export const createFingerprintIndex = function createFingerprintIndex(
    options: FingerprintIndexOptions,
): FingerprintIndex {
    const state = readIndex(options.file);
    const force = options.force ?? false;
    return {
        flush(): void {
            mkdirSync(dirname(options.file), { recursive: true });
            writeVerbatim(options.file, `${JSON.stringify(state, null, INDEX_INDENT)}\n`);
        },
        unchanged(key: string, hash: string): boolean {
            return !force && state[key] === hash;
        },
        update(key: string, hash: string): void {
            state[key] = hash;
        },
    };
};