Programmatisch (Node)
@findsl/core ist der Sprachkern: Grammatik/AST, Validator, Type-Checker, Interpreter, Doc-Generator und Codegen. Es ist die Grundlage, auf der @findsl/cli und @findsl/web aufsetzen — und die richtige Wahl für eigenes Server-, Build- oder Tooling.
Installation
Abschnitt betitelt „Installation“npm install @findsl/core@findsl/core exportiert seine Bausteine über Subpfade (kein einzelner Top-Level-Export):
import { createFindslServices } from "@findsl/core/language/findsl-module.js";import { loadModuleGraph } from "@findsl/core/interpret/module-loader.js";import { runPruefe } from "@findsl/core/interpret/pruefe.js";Muster: laden und prüfen
Abschnitt betitelt „Muster: laden und prüfen“Der typische Ablauf — Module über ihren relativen verwende-Graph laden und die prüfe-Blöcke auswerten — entspricht dem, was die CLI intern tut:
const services = createFindslServices(NodeFileSystem).Findsl;
// parse: Dateipfad -> Program (über die Langium-Services)const module = await loadModuleGraph(entryPfad, parse, { allowedRoot: projektWurzel, // Path-Traversal-Schutz});const report = runPruefe(module);Für die Auswertung gegen einen Projektbaum setzt loadModuleGraph einen allowedRoot, damit verwende-Importe das Projektverzeichnis nicht verlassen.
Praxisbeispiel: Wirkungsanalyse aus dem AST
Abschnitt betitelt „Praxisbeispiel: Wirkungsanalyse aus dem AST“Prüfen und Generieren nimmt dir die CLI datei-basiert ab (findsl test, findsl codegen). Zu @findsl/core greifst du, wenn du FinDSL im eigenen Prozess auswerten und etwas Eigenes daraus bauen willst — etwas, das die CLI nicht kennt.
Ein typischer Fall im Steuerumfeld ist die Audit-Frage: Welche Gesetzesstellen setzt unser Regelwerk um — und wenn sich § 23 KStG ändert, welche Funktionen sind betroffen? Diese Information steckt bereits in den @Quelle-Annotationen. @findsl/core parst das Modul und legt den AST offen; ein einziger Lauf über die Knoten aggregiert sie zu einer Wirkungs-Matrix:
import { NodeFileSystem } from "langium/node";import { URI, AstUtils } from "langium";import * as fs from "node:fs/promises";import { createFindslServices } from "@findsl/core/language/findsl-module.js";import { isAnnotation, type Program,} from "@findsl/core/language/generated/ast.js";
const services = createFindslServices(NodeFileSystem).Findsl;
async function parse(datei: string): Promise<Program> { const text = await fs.readFile(datei, "utf-8"); const doc = services.shared.workspace.LangiumDocumentFactory.fromString( text, URI.file(datei), ); await services.shared.workspace.DocumentBuilder.build([doc], { validation: true, }); return doc.parseResult.value as Program;}
// Gesetzesstelle → Deklarationen, die sich darauf berufen (@Quelle).const program = await parse("src/steuerregeln/kst.findsl");const matrix = new Map<string, Set<string>>();
for (const node of AstUtils.streamAllContents(program)) { if (!isAnnotation(node) || node.name !== "Quelle") continue; const arg = node.args[0]; if (arg?.$type !== "StringLiteral") continue; const stelle = arg.value.replace(/^"|"$/g, ""); // ohne Anführungszeichen // Annotation → DeclPrefix → Deklaration (mit Namen): const decl = node.$container?.$container as { name?: string } | undefined; const liste = matrix.get(stelle) ?? new Set<string>(); liste.add(decl?.name ?? "(Modul)"); matrix.set(stelle, liste);}
for (const stelle of [...matrix.keys()].sort()) { console.log(`${stelle}\n ${[...matrix.get(stelle)!].join(", ")}`);}Ausgabe etwa:
§ 23 Absatz 1 KStG KstSatz, KörperschaftsteuerBetrag§ 23 Absatz 1 Nummer 1 KStG KST_SATZ_BIS_2027§ 24 KStG FreibetragNach24§ 24 Satz 1 KStG FREIBETRAG_24, _BegrenzterFreibetrag24§ 7 KStG KörperschaftsteuerFall, BerechneKörperschaftsteuerEin Lauf über AstUtils.streamAllContents genügt — kein Interpreter, kein Codegen. Genau dafür ist @findsl/core da: FinDSL im eigenen Prozess verarbeiten und daraus bauen, was die Anwendung braucht — eine Compliance-Matrix wie hier, einen Abhängigkeitsgraphen oder einen Export ins Redaktionssystem. Über mehrere Module ziehst du dasselbe einfach über ein Verzeichnis (fs.readdir(..., { recursive: true })).