# core/caches/readme.cache.ts

> 95 lines of code and 23 definitions.

Tree: GovLab Docs
Language: typescript
Layer: infrastructure
Canonical: https://banes-lab.com/anatomy/docs#file-docs-core-caches-readme-cache-ts
Source text: https://banes-lab.com/source/docs/core/caches/readme.cache.ts.txt

Listed in [core/caches](https://banes-lab.com/api/source/docs/core/caches.md).

## Definitions

- `hasDocsBlock` (lexical_declaration, line 94)
- `dependencyNamesOf` (lexical_declaration, line 52)
- `packageAt` (lexical_declaration, line 47)
- `dependencyClosure` (lexical_declaration, line 66)
- `selectStaleModules` (lexical_declaration, line 99, exported)
- `GENERATED_MARK` (lexical_declaration, line 18)
- `TEST_MARKERS` (lexical_declaration, line 19)
- `isExcluded` (lexical_declaration, line 20)
- `isSharedInput` (lexical_declaration, line 22)
- `inputsUnder` (lexical_declaration, line 28)
- `SHARED_HASH` (lexical_declaration, line 32)
- `cacheForced` (lexical_declaration, line 43, exported)
- `{ dependencies }` (lexical_declaration, line 53)
- `membersByName` (lexical_declaration, line 57)
- `{ name }` (lexical_declaration, line 60)
- `reached` (lexical_declaration, line 67)
- `pending` (lexical_declaration, line 68)
- `next` (lexical_declaration, line 69)
- `moduleKey` (lexical_declaration, line 80, exported)
- `workspaceMapKey` (lexical_declaration, line 85, exported)
- `inputs` (lexical_declaration, line 86, exported)
- `createDocIndex` (lexical_declaration, line 90, exported)
- `parsed` (lexical_declaration, line 95)

## Contained in

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

## Uses

- [core/predicates/record.predicate.ts](https://banes-lab.com/source/docs/core/predicates/record.predicate.ts.md)

## Linked from

- [core/caches](https://banes-lab.com/anatomy/docs/folder-docs-core-caches.md)
- [core/predicates](https://banes-lab.com/anatomy/docs/folder-docs-core-predicates.md)

## Source

```typescript
import { DOCS_CACHE_NAME, NO_CACHE_ENV, NO_CACHE_ON } from "#configuration/constants/invocation.constants";
import { MANIFEST_FILE, PACKAGE_FILE, README_FILE } from "#configuration/constants/document.constants";
import { ROOT, absolutePath } from "@ssot/paths";
import {
    cacheFile,
    collectFiles,
    createFingerprintIndex,
    fingerprint,
    fingerprintOf,
} from "@govlab/content-fingerprint";
import type { FingerprintIndex } from "@govlab/content-fingerprint";
import { excludeMatcher } from "@govlab/quality/config";
import { existsSync } from "node:fs";
import { isPlainRecord } from "#core/predicates/record.predicate";
import { join } from "node:path";
import { readJsonSafe } from "#core/loaders/base.loader";

const GENERATED_MARK = ".generated.";
const TEST_MARKERS: readonly string[] = [".test.", ".spec."];
const isExcluded = await excludeMatcher(ROOT);

const isSharedInput = function isSharedInput(name: string): boolean {
    return (
        name !== README_FILE && !name.includes(GENERATED_MARK) && !TEST_MARKERS.some((marker) => name.includes(marker))
    );
};

const inputsUnder = function inputsUnder(dir: string): string[] {
    return collectFiles(dir, { excluded: isExcluded, include: isSharedInput });
};

const SHARED_HASH = fingerprint([
    absolutePath("govlab.quality.generated.concepts"),
    absolutePath("govlab.quality.generated.rules"),
    absolutePath("govlabHost.config"),
    ...[
        absolutePath("govlab.context.principles"),
        absolutePath("govlab.docs"),
        absolutePath("govlab.utils.canonicalWrite"),
    ].flatMap(inputsUnder),
]);

export const cacheForced = function cacheForced(env: Readonly<Record<string, string | undefined>>): boolean {
    return env[NO_CACHE_ENV] === NO_CACHE_ON;
};

const packageAt = function packageAt(rel: string): Record<string, unknown> {
    const parsed = readJsonSafe(join(ROOT, rel, PACKAGE_FILE));
    return isPlainRecord(parsed) ? parsed : {};
};

const dependencyNamesOf = function dependencyNamesOf(rel: string): string[] {
    const { dependencies } = packageAt(rel);
    return isPlainRecord(dependencies) ? Object.keys(dependencies) : [];
};

const membersByName = function membersByName(modules: readonly string[]): ReadonlyMap<string, string> {
    return new Map(
        modules.flatMap((rel) => {
            const { name } = packageAt(rel);
            return typeof name === "string" ? [[name, rel] as const] : [];
        }),
    );
};

const dependencyClosure = function dependencyClosure(rel: string, byName: ReadonlyMap<string, string>): string[] {
    const reached = new Set([rel]);
    const pending = [rel];
    for (let next = pending.pop(); next !== undefined; next = pending.pop()) {
        for (const member of dependencyNamesOf(next).flatMap((name) => byName.get(name) ?? [])) {
            if (!reached.has(member)) {
                reached.add(member);
                pending.push(member);
            }
        }
    }
    return [...reached].toSorted((left, right) => left.localeCompare(right));
};

export const moduleKey = function moduleKey(rel: string, modules: readonly string[]): string {
    const inputs = dependencyClosure(rel, membersByName(modules)).flatMap((member) => inputsUnder(join(ROOT, member)));
    return fingerprintOf([fingerprint(inputs), SHARED_HASH]);
};

export const workspaceMapKey = function workspaceMapKey(modules: readonly string[]): string {
    const inputs = modules.flatMap((rel) => [join(ROOT, rel, MANIFEST_FILE), join(ROOT, rel, PACKAGE_FILE)]);
    return fingerprintOf([fingerprint(inputs), SHARED_HASH]);
};

export const createDocIndex = function createDocIndex(force: boolean): FingerprintIndex {
    return createFingerprintIndex({ file: cacheFile(DOCS_CACHE_NAME), force });
};

const hasDocsBlock = function hasDocsBlock(rel: string): boolean {
    const parsed = readJsonSafe(join(ROOT, rel, MANIFEST_FILE));
    return isPlainRecord(parsed) && isPlainRecord(parsed.docs);
};

export const selectStaleModules = function selectStaleModules(
    index: FingerprintIndex,
    modules: readonly string[],
    keyByModule: ReadonlyMap<string, string>,
): string[] {
    return modules.filter(
        (rel) =>
            !index.unchanged(rel, keyByModule.get(rel) ?? "") ||
            (hasDocsBlock(rel) && !existsSync(join(ROOT, rel, README_FILE))),
    );
};
```
