# shared/loaders/graph.loader.ts

> 52 lines of code and 8 definitions.

Tree: Governance tree
Language: typescript
Layer: infrastructure
Canonical: https://banes-lab.com/anatomy/governance#file-governance-shared-loaders-graph-loader-ts
Source text: https://banes-lab.com/source/governance/shared/loaders/graph.loader.ts.txt

Listed in [shared/loaders](https://banes-lab.com/api/source/governance/shared/loaders.md), after [shared/loaders/document.loader.ts](https://banes-lab.com/source/governance/shared/loaders/document.loader.ts.md) and before [shared/loaders/index.loader.ts](https://banes-lab.com/source/governance/shared/loaders/index.loader.ts.md).

## Definitions

- `isClosureGraph` (lexical_declaration, line 44)
- `isRecord` (lexical_declaration, line 40)
- `loadClosureGraph` (lexical_declaration, line 48, exported)
- `normalizeImport` (lexical_declaration, line 6, exported)
- `parts` (lexical_declaration, line 10, exported)
- `resolved` (lexical_declaration, line 20, exported)
- `LIST_FIELDS` (lexical_declaration, line 24)
- `parsed` (lexical_declaration, line 52, exported)

## Uses

- [shared/strings/graph.strings.ts](https://banes-lab.com/source/governance/shared/strings/graph.strings.ts.md)

## Source

```typescript
import { existsSync, readFileSync } from "node:fs";
import type { ClosureGraph } from "../../types/closure.types.ts";
import { GRAPH_PATH } from "../resolvers/anchor.resolver.ts";
import { malformedClosureGraph } from "../strings/graph.strings.ts";

export const normalizeImport = function normalizeImport(fromFile: string, importPath: string): string | null {
    if (!importPath.startsWith(".")) {
        return null;
    }
    const parts = fromFile.split("/").slice(0, -1);
    for (const seg of importPath.split("/")) {
        if (seg === "..") {
            parts.pop();
            continue;
        }
        if (seg !== ".") {
            parts.push(seg);
        }
    }
    const resolved = parts.join("/");
    return resolved.endsWith(".ts") ? resolved : null;
};

const LIST_FIELDS = [
    "registers",
    "consumers",
    "emits",
    "subscribes",
    "eventActivity",
    "exports",
    "imports",
    "externalConsumers",
    "sideEffectImports",
    "interfaces",
    "idsExports",
    "stringsExports",
    "iconsExports",
] as const;

const isRecord = function isRecord(value: unknown): value is Record<string, unknown> {
    return typeof value === "object" && value !== null;
};

const isClosureGraph = function isClosureGraph(value: unknown): value is ClosureGraph {
    return isRecord(value) && LIST_FIELDS.every((field) => Array.isArray(value[field]));
};

export const loadClosureGraph = function loadClosureGraph(): ClosureGraph | null {
    if (!existsSync(GRAPH_PATH)) {
        return null;
    }
    const parsed: unknown = JSON.parse(readFileSync(GRAPH_PATH, "utf8"));
    if (!isClosureGraph(parsed)) {
        throw new TypeError(malformedClosureGraph(GRAPH_PATH, LIST_FIELDS));
    }
    return parsed;
};
```
