# lib-core.api-discovery Pure-Lua-Library für Surface-Discovery (extrahiert `M.*` aus Lua-Quelltext) und README-Parsing (extrahiert die dokumentierte API aus Markdown). Außerdem ein README-Struktur-Linter, der die API-Doc-Convention (ADR-0038) gegen die MUST-Sections eines Tiers prüft. Sandbox-safe — kein FS-Zugriff, keine Seiteneffekte. Verwendet vom Sporel-`--lint`-CLI-Modus. **Version:** 0.1.0 **Lib-ID:** lib-core.api-discovery **Requires:** (none) **Tags:** api-discovery, lint, readme, surface, topology, tooling ## Topology ```mermaid graph LR this["lib-core.api-discovery"] ``` Reine Lua-Lib ohne Dependencies und ohne `engine.*`-Aufrufe — daher kein `engine`-Knoten im Topology-Graphen (sandbox-safe per Design, damit der Linter im `--lint`-CLI-Modus ohne Engine-Kontext laufen kann). ## API ### `api-discovery.parse_lua_surface(source_string)` **Syntax:** `parse_lua_surface(source_string: string) -> {public: string[], private: string[]}` **Example:** ```lua local ad = require("lib-core.api-discovery") local src = io.read_file("init.lua") local s = ad.parse_lua_surface(src) -- s.public == { "load", "create", ... } -- s.private == { "_normalize", ... } ``` **Description:** Extrahiert die öffentliche und private Surface aus Lua-Quelltext. Konvention: `M.` ist öffentlich, `M._` privat. Erkennt beide Schreibweisen — explizite Zuweisung (`M.foo = function`) und Syntax-Sugar (`function M.foo`). Lua-Zeilenkommentare (`--` bis Zeilenende) werden vor dem Pattern-Matching entfernt, damit auskommentierte Forward-Compat-Stubs (z. B. `DEPRECATED-MVP`-Platzhalter) keine False-Positives erzeugen. Block-Kommentare (`--[[ ... ]]`) werden nicht behandelt (in Sporel-Lib-Code nicht verwendet). Dedupliziert über alle Namen. Liefert `{ public = [...], private = [...] }`. ### `api-discovery.parse_readme_api(markdown_string)` **Syntax:** `parse_readme_api(markdown_string: string) -> {documented: string[]}` **Example:** ```lua local r = ad.parse_readme_api(readme_text) -- r.documented == { "bind", "unbind", ... } ``` **Description:** Extrahiert die dokumentierten Funktionsnamen aus dem `## API`-Abschnitt eines READMEs. Sucht den Abschnitt zwischen `## API` und der nächsten H2-Überschrift und parst die H3-Header darin. Konvention: ein H3-Header öffnet mit einem Backtick, der Funktionsname folgt nach einem optionalen Namespace-Punkt — sowohl die namespaced-Form `ns.func(...)` (Capture `func`) als auch die namespace-lose Form `func(...)` werden erkannt. Dedupliziert; Pattern 2 matcht keine Namen, die Pattern 1 bereits erfasst hat. Fehlt der `## API`-Abschnitt, ist `documented` leer. ### `api-discovery.diff_surface(surface, readme)` **Syntax:** `diff_surface(surface: {public: string[]}, readme: {documented: string[]}) -> {missing_docs: string[], stale_docs: string[]}` **Example:** ```lua local s = ad.parse_lua_surface(src) local r = ad.parse_readme_api(readme_text) local diff = ad.diff_surface(s, r) -- diff.missing_docs == öffentliche Funktionen ohne README-Eintrag -- diff.stale_docs == README-Einträge ohne öffentliche Funktion ``` **Description:** Vergleicht die Code-Surface (`surface.public`) gegen die README-Dokumentation (`readme.documented`). `missing_docs` sind öffentliche Funktionen ohne dokumentierten Eintrag; `stale_docs` sind dokumentierte Einträge ohne korrespondierende öffentliche Funktion. Beide Listen sind alphabetisch sortiert. ### `api-discovery.grep_engine_calls(source_string)` **Syntax:** `grep_engine_calls(source_string: string) -> string[]` **Example:** ```lua local calls = ad.grep_engine_calls(src) -- calls == { "engine.render.draw_rect", "engine.window.size", ... } ``` **Description:** Extrahiert alle eindeutigen `engine..`-Aufrufe aus Lua-Quelltext. Liefert ein dedupliziertes, alphabetisch sortiertes Array. Wird von `generate_topology_block` genutzt, um zu entscheiden, ob ein `engine.*`-Knoten in den Topology-Graphen aufgenommen wird. ### `api-discovery.generate_topology_block(manifest, engine_calls)` **Syntax:** `generate_topology_block(manifest: {id: string, deps?: {id: string}[]}, engine_calls: string[]) -> string` **Example:** ```lua local manifest = { id = "lib-core.maps", deps = {} } local calls = ad.grep_engine_calls(src) local mermaid = ad.generate_topology_block(manifest, calls) -- mermaid == "graph LR\n this[\"lib-core.maps\"]\n engine[\"engine.*\"]\n this --> engine" ``` **Description:** Generiert den Mermaid-Topology-Block aus Manifest + `engine_calls`. Der `this`-Knoten trägt die `manifest.id`. Für jede Dependency in `manifest.deps` wird ein Knoten plus `this --> dep`-Kante ausgegeben (die Dep-ID wird für den Mermaid-Variablennamen entschärft: `-` und `.` → `_`). Enthält `engine_calls` mindestens einen Eintrag, kommt ein `engine["engine.*"]`-Knoten mit `this --> engine`-Kante hinzu. Liefert den reinen Mermaid-Quelltext ohne Marker — der Aufrufer wrappt ihn in die `` / ``-Marker. ### `api-discovery.validate_readme_structure(markdown_string, tier)` **Syntax:** `validate_readme_structure(markdown_string: string, tier: "core"|"engine"|"module"|"community") -> {missing_sections: string[], section_order_ok: boolean}` **Example:** ```lua local res = ad.validate_readme_structure(readme_text, "core") if #res.missing_sections > 0 or not res.section_order_ok then -- README verletzt die API-Doc-Convention end ``` **Description:** Prüft die README-Struktur gegen die MUST-Sections des angegebenen Tiers. - **`core`** verlangt: `H1`, `Badges` (ein `**Lib-ID:** lib-…`-Block), `Topology` (`## Topology`), `Topology-Block` (`