


Eine Full-Stack-TypeScript-App mit API-Schemas bauen, die Server und Client teilen
Für den Bau oder Umbau einer Full-Stack-TypeScript-App. Die KI legt jede API-Anfrage und -Antwort einmal für Routen und Client-Hooks fest, prüft die Anmeldung vor dem Lesen von Eingaben, hält Abhängigkeiten, Cache und Stile einheitlich und schreibt schnelle Tests.
Diese KI-Regeln verwenden
## Paketabhängigkeiten in eine Richtung halten und festlegen, wo welcher Zustand liegt
Strukturiere die Codebasis so, dass Zuständigkeiten, Abhängigkeitsrichtung und Platzierung von Zuständen auch bei wachsendem System vorhersehbar bleiben.
### Nach Zuständigkeit strukturieren
- Gib jeder Komponente, jedem Hook, Store, Dienst und Paket genau einen klaren Zweck und bevorzuge die Komposition kleiner Einheiten, statt eine bereits komplexe Einheit um weitere Verzweigungen und Modi zu erweitern.
- Ordne Anwendungscode im Abhängigkeitsgraphen oberhalb wiederverwendbarer Pakete an; Pakete niedrigerer Ebene dürfen keine Anwendungsmodule importieren.
- Halte isolierte Dienste frei von nicht zugehörigen UI-Frameworks, Registraturen, Anbieter-SDKs und Ausführungs-Engines.
- Teile die Authentifizierung über eine einzige kompatible Sitzung und ein gemeinsames Datenmodell, statt sie für jede Anwendung neu zu implementieren.
### Typ- und Zustandsverantwortung bewahren
- Definiere explizite Schnittstellen für Komponenteneigenschaften, Zustände, Funktionseingaben und Rückgabewerte.
- Verwende nicht `any`; nutze `unknown` mit Typwächtern, wenn der Typ tatsächlich ungewiss ist.
- Halte Serverzustand in der Bibliothek für Serverzustände, globalen Clientzustand in einem Store und lokalen Darstellungszustand in der Komponente.
- Teile einen komplexen Store in Implementierungs- und Typmodule auf; persistiere nur Zustände, die Neuladevorgänge überstehen müssen, und wähle persistierte Felder ausdrücklich aus.
## Gemeinsame Helfer und Werkzeuge des Repos statt lokaler Kopien verwenden
Nutze eine einheitliche, maßgebliche Werkzeugkette und gemeinsame Implementierungsmuster, statt lokale Ersatzlösungen zu schaffen.
### Einheitliche Codekonventionen anwenden
- Verwende den im Repository festgelegten Paketmanager, Formatierer, Linter, Typprüfer und die vorgesehenen Prüfkommandos.
- Nutze strukturierte Protokollierung statt Konsolenausgaben; anfragebezogene Routen-Wrapper sollten Anfragen automatisch Kennungen zuweisen.
- Halte Benennung und Importreihenfolge einheitlich; verwende reine Typimporte, Konstantenzusicherungen, explizite Referenzelementtypen und TSDoc statt dekorativer oder unstrukturierter Kommentare.
- Bevorzuge absolute Importe. Erstelle eine Sammeldatei nur, wenn ein Ordner mindestens 3 Exporte hat, und reexportiere nicht aus Dateien, die keine Sammeldateien sind.
### Bewährte Hilfsfunktionen wiederverwenden
- Nutze gemeinsame Hilfsfunktionen für ID-Erzeugung, Verzögerungen, Fehlernormalisierung, Nachrichtenextraktion, tiefe Kopien, Objektfilterung, Kürzung und Wiederholungswartezeiten mit Zufallsstreuung; bilde sie weder durch lokale Pakete noch durch einmalige Sonderlogik nach.
- Bette eine Hilfsfunktion direkt ein, wenn sie von 1 Verbraucher genutzt wird; verschiebe sie in ein Hilfsmodul, sobald mindestens 2 Dateien sie benötigen.
- Lagere eine Komponente aus, wenn sie ungefähr 50 Zeilen überschreitet, mindestens 2 Aufrufern dient oder eigenen Zustand beziehungsweise eigene Logik besitzt; belasse ein rein darstellendes, einmal verwendetes Fragment mit weniger als 10 Zeilen direkt an Ort und Stelle.
## Jede API-Anfrage und -Antwort einmal festlegen und jedes direkte fetch begründen
Definiere jede HTTP-Anfrage und -Antwort genau einmal, damit Routen und Clients nicht in inkompatible Übertragungsformate auseinanderlaufen.
### Verträge zur maßgeblichen Quelle an Grenzen machen
- Verwende ein Vertragsmodul pro Ressourcenfamilie, mit getrennten Schemas für Pfadparameter, Abfragewerte, Inhalt, Header und Antwort, die in einem Routenvertrag zusammengeführt werden.
- Exportiere benannte Schemas sowie benannte Eingabe- und Ausgabetypaliase für Clients und verwende gemeinsame Kennungsprimitive wieder, statt spontane Übertragungstypen abzuleiten oder zeichenkettenbasierte Kennungen neu zu definieren.
- Domänenvalidatoren dürfen die Schemabibliothek direkt verwenden, doch Schemas für Routengrenzen gehören weder in Routen-Handler noch in Clients.
### Ausnahmen ausdrücklich steuern
- Verlange unmittelbar über jedem ausnahmsweise direkten `fetch`, jeder doppelten Typumwandlung, jedem direkten JSON-Lesevorgang und jeder bewusst untypisierten Antwort eine begründete Anmerkung.
- Erlaube direkte `fetch`-Aufrufe nur für Fälle wie Streaming, Binärübertragung, mehrteilige Uploads, signierte URLs, OAuth-Weiterleitungen oder externe Ursprünge.
- Erlaube direkte JSON-Lesevorgänge nur für Umschläge oder tolerantes Parsen, das den Standardparser nicht verwenden kann, und untypisierte Antworten nur für wirklich undurchsichtige Daten.
- Führe berechtigte Ausnahmen für ganze Dateien in einer geprüften Positivliste, statt zeilenweise Umgehungen zu verstreuen.
- Eine strenge Grenzprüfung muss bei fehlenden Begründungen, routenlokalen Schemas, direkten Lesevorgängen, nicht genehmigten `fetch`-Aufrufen und unsicheren doppelten Typumwandlungen fehlschlagen.
## Eine API-Route und ihren Client-Hook hinzufügen, beginnend beim Schema
Füge eine Funktion an einer Systemgrenze in fester Reihenfolge hinzu, damit Authentifizierung, Validierung, Antworttypisierung und Zwischenspeicherung durchgängig übereinstimmen.
### Implementierungsreihenfolge einhalten
1. Erstelle den Vertrag und seine benannten Eingabe- und Ausgabetypen.
2. Umschließe die Route mit dem maßgeblichen Wrapper für Anfragekontext und Fehlerbehandlung und kombiniere gemeinsame Middleware für Authentifizierung, Ratenbegrenzung und Zugriffskontrolle, statt sie je Route neu zu implementieren.
3. Authentifiziere und autorisiere vor dem Parsen nicht vertrauenswürdiger Eingaben und parse anschließend alle Teile der Anfrage über den Vertrag.
4. Gib genau die JSON-Struktur zurück, die das Antwortschema festlegt.
5. Füge einen typisierten Client-Hook hinzu, der Daten über denselben Vertrag sendet und validiert.
6. Verwende den Hook in der Komponente und zeige seine typisierte Fehlermeldung an.
### Schemas wie Migrationen prüfen
- Unterscheide das Weglassen von `null`; die Kombination einer optionalen mit einer `null`-fähigen Deklaration erzeugt einen dritten Zustand und sollte beabsichtigt sein.
- Vergleiche jede Aufrufstelle einer Antwort mit dem deklarierten Antwortschema.
- Lege explizite Grenzen für Zeichenketten, Arrays, Grenzwerte und Größen fest und verwende aussagekräftige feldspezifische Fehler.
- Behandle unbekannte Schemas als Ausnahme und dokumentiere, warum die Nutzlast undurchsichtig ist; bevorzuge diskriminierte Vereinigungen, wenn das Übertragungsformat einen Diskriminator enthält.
- Nutze menschliche Prüfung für die semantische Qualität; strukturelle Prüfungen können nicht jede zu schwache Grenze oder ungenaue Antwortstruktur erkennen.
## Den Abfrage-Cache bei Abbrüchen und optimistischen Aktualisierungen korrekt halten
Mache Abbruch, Zwischenspeicherung, Invalidierung und optimistische Aktualisierungen ausdrücklich sichtbar, statt Serverzustand im Komponentenzustand zu verbergen.
### Stabile Hooks und Abfrageschlüssel erstellen
- Verwende eine Referenz, wenn ein stabiler Rückruf die neuesten Eingaben lesen muss, und aktualisiere diese Referenz, sobald sich die Eingabe ändert.
- Leite das Abbruchsignal aus jeder Abfragefunktion weiter und gib jeder Abfrage ein ausdrückliches Aktualitätsintervall.
- Bilde hierarchische Schlüssel von einem Wurzelschlüssel über Listen- und Detailpräfixe für gezielte Invalidierung und behalte vorherige Daten nur bei Abfragen mit veränderlichen Schlüsseln, nicht bei statischen Abfragen.
### Mutationen sicher abgleichen
- Brich vor einer optimistischen Aktualisierung betroffene Abfragen ab und speichere den vorherigen Cachewert, damit er bei einem Fehler wiederhergestellt werden kann.
- Gleiche ab und invalidiere beim Abschluss, nicht nur bei Erfolg, weil sowohl Erfolg als auch Fehlschlag einen konsistenten Cache erfordern.
- Bevorzuge gezielte Invalidierung gegenüber der Invalidierung des gesamten Ressourcenbaums.
- Füge stabile Mutationsmethoden oder -objekte nicht unnötig zu den Abhängigkeiten eines Rückrufs hinzu.
- Leite JSON desselben Ursprungs über die typisierte Anfragehilfsfunktion; Ausnahmen mit direktem `fetch` müssen der dokumentierten Richtlinie für Systemgrenzen folgen.
## Das Aussehen gemeinsamer Komponenten in der Komponente festlegen und über Props anpassen
Behandle das gemeinsame Erscheinungsbild einer Komponente als eine einzige, von der Komponente verantwortete maßgebliche Quelle statt als Aufgabe jedes Verbrauchers.
### Darstellung lokal und kombinierbar halten
- Ändere für eine lokale Funktion keine globalen Stile; begrenze die Gestaltung auf Komponenten.
- Verwende die projektweite Hilfsfunktion für bedingte Klassen und die Dimensionskurzschreibweise, statt Logik zum Aufbau von Klassen zu duplizieren.
- Nutze Variantenwerkzeuge nur, wenn eine Komponente mindestens 2 echte Varianten hat; verwende für das Umschalten eines einzelnen Zustands eine einfache bedingte Klasse.
- Importiere gemeinsame Komponenten über den unterstützten öffentlichen Einstiegspunkt statt über interne Unterpfade.
- Prüfe, ob referenzierte CSS-Variablen vorhanden sind, denn eine undefinierte Variable kann unbemerkt auf eine unbeabsichtigte Farbe zurückfallen.
### Absicht durch Eigenschaften ausdrücken
- Stelle Eigenschaften für Fehler, Zusatzelemente, Beschriftungen, Hinweise, Pflichtzustand und Verhaltensmodi bereit; Klassenüberschreibungen durch Verbraucher bleiben Layout und Größen vorbehalten, nicht maßgeblichen Rahmen, Füllungen, Radien, Höhen, Farben oder Fokusdarstellungen.
- Verwende Eigenschaften als diskriminierte Vereinigung für Modi, statt nahezu identische Komponenten parallel zu pflegen.
- Nutze in Modalinhalten den maßgeblichen Wrapper für beschriftete Felder, damit die Feldabstände mit Kopf- und Fußbereichen ausgerichtet sind.
- Entferne veraltete Varianten nach der Migration, statt parallele Pfade im Designsystem zurückzulassen.
## Schnelle Vitest-Tests mit gemeinsamen Mocks und Fake-Timern schreiben
Nutze den Standard-Test-Runner des Repositorys, globale Testvorrichtungen und deterministische Zeitmuster.
### Verhalten isolieren
- Benenne jeden Test neben seiner Quelldatei mit dem Standard-Testsuffix des Repositorys.
- Verwende globale Attrappen und gemeinsame Fabriken wieder und ersetze sie nur, wenn ein Test anderes Verhalten benötigt; leere den Zustand der Attrappen vor jedem Test und lege Rückgabewerte pro Test ausdrücklich fest.
- Simuliere umfangreiche Registraturen und Anbieterabhängigkeiten, wenn der Test sie nicht prüft.
- Verwende eine Server-Laufzeitumgebung, sofern keine Browser-APIs erforderlich sind.
- Vermeide echte Zeitgeber; nutze simulierte Zeitgeber oder die kleinste deterministische Verzögerung, die das Verhalten erfordert.
### Teure Modulwechsel vermeiden
- Bevorzuge in Vitest vorgezogene Attrappen und statische Importe.
- Kombiniere keine Modulrücksetzungen, späten Attrappen und dynamischen Importe und importiere reale Implementierungen nicht über `importActual`, wenn ausdrückliche Attrappen genügen.
- Halte Attrappen für Authentifizierung und Protokollierung ausdrücklich, statt Hilfsfunktionen aufzurufen, die spätes Simulieren verbergen.
- Führe vor der Prüfung die strenge Prüfung der API-Grenzen zusammen mit den relevanten Tests aus.
## Integrationen hinzufügen, ohne Variablen vor ihrer Auflösung umzuwandeln
Baue Integrationen in Abhängigkeitsreihenfolge auf und bewahre nicht aufgelöste Referenzen bis zur Ausführung.
### Integrationsreihenfolge einhalten
1. Definiere und registriere Werkzeuge mit stabilen `snake_case`-Kennungen.
2. Füge den verbrauchenden Block hinzu und registriere ihn in der maßgeblichen Reihenfolge.
3. Füge das Symbol und die erforderlichen Metadaten hinzu.
4. Füge nur dann einen Auslöser hinzu, wenn die Integration einen benötigt.
5. Überprüfe das Dienstverhalten anhand maßgeblicher API-Dokumentation.
### Lebenszykluszeitpunkte beachten
- Unterscheide Konfiguration zur Serialisierungszeit von der Parameterauflösung zur Ausführungszeit.
- Erzwinge während der Serialisierung nicht die Typumwandlung nicht aufgelöster Werte; dadurch können dynamische Referenzen zerstört werden.
- Führe numerische und andere Typumwandlungen erst aus, nachdem Variablen während der Ausführung aufgelöst wurden.
- Halte Metadaten von Werkzeug, Block, Auslöser, Authentifizierung und Dateieingabe synchron.
- Führe Linting, Typprüfungen, Tests und strenge Grenzprüfungen aus und prüfe anschließend den endgültigen Unterschied auf Konsistenz von Verträgen, Cache, Gestaltung und Registratur.
