# core/stores/fingerprint.store.ts

> 48 lines of code and 10 definitions.

Tree: Content Fingerprint
Language: typescript
Layer: application
Canonical: https://banes-lab.com/anatomy/content-fingerprint#file-content-fingerprint-core-stores-fingerprint-store-ts
Source text: https://banes-lab.com/source/content-fingerprint/core/stores/fingerprint.store.ts.txt

Listed in [core/stores](https://banes-lab.com/api/source/content-fingerprint/core/stores.md).

## Definitions

- `isStringRecord` (lexical_declaration, line 7)
- `readIndex` (lexical_declaration, line 27)
- `parsedIndex` (lexical_declaration, line 16)
- `parsed` (lexical_declaration, line 31)
- `createFingerprintIndex` (lexical_declaration, line 35, exported)
- `state` (lexical_declaration, line 38, exported)
- `force` (lexical_declaration, line 39, exported)
- `flush` (method_definition, line 41, exported)
- `unchanged` (method_definition, line 45, exported)
- `update` (method_definition, line 48, exported)

## Contained in

- [core/stores](https://banes-lab.com/anatomy/content-fingerprint/folder-content-fingerprint-core-stores.md)

## Linked from

- [core/stores](https://banes-lab.com/anatomy/content-fingerprint/folder-content-fingerprint-core-stores.md)

## Source

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