Entwicklerdokumentation

Was DiPAgE speichert, was es von Ihnen annimmt und womit es spricht. Geschrieben für alle, die Importer, Exporter und Integrationen bauen oder die App selbst betreiben.

Angular 22 · zoneless IndexedDB · kein Backend MIT PWA · Offline-First

/ drücken für Suche · Esc zum Löschen

Wenn Sie Hilfe bei der Nutzung der App suchen, dann gehen Sie zur Benutzerdokumentation

Sie suchen den Datenstandard oder Beispiel-Exporte? Zu den Downloads

Zentrale Entwicklerressourcen

Feldreferenz des Datensatzes

Jedes Feld des PSM-Anwendungsdatensatzes — insgesamt 69 — mit Typ, Einschränkungen, Beispiel und dem Verhalten, das im Schema nicht sichtbar ist.

Zur Feldreferenz
Datenstandard & Beispieldateien

Das JSON-Schema, eine XSD-Variante sowie Beispiel-Exporte in CSV, JSON und XML — alles an einem Ort.

Zu den Downloads
Sie suchen die Anwenderhilfe?

App installieren, Anwendung erfassen, für eine Prüfung exportieren — die aufgabenorientierte Dokumentation hat ihre eigene Seite.

Zur Anwenderdokumentation
Hier anfangen

Fragen von Entwicklern.

Die Fragen, die beim ersten Integrationsversuch aufkommen — ungefähr in der Reihenfolge, in der sie aufkommen.

10 Fragen

Nein. DiPAgE hat kein eigenes Backend und keine serverseitige Datenbank — es gibt nichts, wogegen man sich authentifiziert, und keinen Endpunkt, der Datensätze zurückgibt. Jeder Datensatz liegt in der IndexedDB des Browsers, in dem er erfasst wurde. Integration läuft über Dateien: aus der App exportieren, in die App importieren. Die Formate sind unter „Import, Export und Austausch“ beschrieben, die Datensatzstruktur hat eine eigene Referenzseite. Code, der innerhalb der Seite läuft, ist ein anderer Fall — siehe die JavaScript-API im nächsten Eintrag.

Ja — genau dafür gibt es window.dipageApi. Die App friert beim Start ein kleines Objekt auf window fest (derzeit Version 1.1.0), damit Code in der Seite Datensätze über Methoden lesen und schreiben kann, statt sich durch die Oberfläche zu klicken; das ist der vorgesehene Weg für einen Agenten, der Daten erhebt oder erfasst. Bereitgestellt werden createRecord, openNewRecord, updateRecord, deleteRecord, getRecord, getRecords, searchPsm, searchCrops, getProfile, exportRecords und getTemplates. Die Aufrufe laufen über dieselbe Speicherschicht wie die Oberfläche und damit über denselben Schema-Sanitizer — die API kann also keine Datensatzform schreiben, die das Formular nicht erzeugen könnte. Es ist ausschließlich eine Oberfläche innerhalb der Seite: dahinter steht kein Netzwerk-Endpunkt, sie existiert nur in einem Browser-Tab mit geöffnetem DiPAgE und verschwindet mit ihm.

Das ist Absicht, kein Tippfehler. Die Breite ist auf ±100, die Länge auf ±50 begrenzt — eine Produktentscheidung, die nebenbei Koordinaten weit außerhalb des vorgesehenen Einsatzgebiets abweist. Die Grenzen sind an zwei Stellen dupliziert, im JSON-Schema und in einer TypeScript-Konstantendatei, weil JSON kein TypeScript importieren kann. Wer die App forkt und eine der beiden weitet, muss die andere im selben Schritt mitziehen, sonst widerspricht sich die Validierung selbst.

Nicht als Schemafeld. Das Datensatzformat ist durch den PSM-Standard festgelegt und sieht dafür keinen Platz vor. Die App schreibt stattdessen sprachunabhängige Markercodes in notizen (PPS_EA, PPS_NZ, ŚOR_ZN) und liest sie beim Bearbeiten und Importieren zurück. Dieser Marker ist das einzige dauerhafte Signal, das Export und Reimport übersteht. Zwei Konsequenzen für eigene Werkzeuge: notizen nicht pauschal überschreiben, sonst löschen Sie ihn; und bei einem solchen Mittel keine zulassungsnummer erwarten, denn ein notfallzugelassenes Mittel hat keine.

Gar nicht — sie wird abgeleitet. Mehrere Mittel in einem Spritzgang werden zu mehreren Datensätzen, einer je Mittel, wie es die deutsche Aufzeichnungspflicht vorsieht. Es gibt kein tankmischung-Flag. Die App erkennt die Gruppe nachträglich an drei gemeinsamen Merkmalen: Behandlungsort, Anwendungsdatum und Erstellungszeitpunkt des Datensatzes — deshalb stempelt ein Sammelspeichern die Datensätze im Abstand von 10 ms ab einem gemeinsamen Basiswert, statt je Datensatz die echte Uhr zu lesen. Die Aufwandmenge ist aus diesem Gruppierungsschlüssel bewusst ausgenommen, denn jedes Mittel der Mischung behält seine eigene Dosis.

Zwei verschiedene Dinge, beide kein Schemafeld. Ein Entwurf ist ein Datensatz, den die Nutzerin mit weicher Validierung gespeichert hat; er trägt in der IndexedDB status: „draft“, doch das Schema schließt status bewusst aus — der Sanitizer entfernt ihn bei jedem Lesen und Schreiben, und die Speicherschicht hängt ihn danach wieder an, damit ein Entwurf ein Neuladen übersteht. In einem Export erscheint status nie. „Unvollständig“ wird überhaupt nicht gespeichert, sondern berechnet, indem ein gespeicherter Datensatz erneut auf fehlende Pflichtfelder geprüft wird. Unvollständige Datensätze und Entwürfe weist die Exportprüfung mit einer Meldung ab; in vollständigen Backups bleiben beide erhalten. Die reinen UI-Felder indikation, anwendungszeitpunkt und notfallzulassung werden vom Sanitizer ebenfalls entfernt.

Für JSON ist der Vertrag das Schema — dagegen validieren, fertig. Für die tabellarischen Formate kommt eine Regel hinzu: CSV und die Excel-Arbeitsmappe tragen beide einen zweizeiligen Kopf. Zeile 1 enthält lokalisierte Beschriftungen für Menschen, Zeile 2 die maschinellen Feldpfade (etwa anwendung_zeitpunkt.datum), die Daten beginnen in Zeile 3. Der Importer sucht die Pfadzeile, statt eine Position anzunehmen — deshalb lässt sich eine auf Deutsch exportierte Datei korrekt in eine englische Oberfläche importieren. Schreiben Sie Zeile 2 korrekt, dann darf in Zeile 1 stehen, was Sie möchten.

Der Client steht unter der MIT-Lizenz. Das Repository auf dem Git-Server des JKI ist noch nicht öffentlich — schreiben Sie dem Team für den Zugang; Forks für institutsspezifische Abläufe sind danach ausdrücklich willkommen. Wer zurück beitragen statt abzweigen möchte, sollte zwei Konventionen kennen: Commits folgen den Conventional Commits (durch commitlint erzwungen), und das Projekt führt ein Entscheidungsprotokoll über Entscheidungen, die falsch aussehen, aber keine sind. Es liegt derzeit nicht im Repository; fragen Sie das Team danach, bevor Sie eine dieser Stellen „reparieren“ — mehrere Überraschungen auf dieser Seite sind Einträge darin.

Nur Nachschlagevorgänge, und nur zu in der Content-Security-Policy festgelegten Origins: synops.julius-kuehn.de für Mittel, Indikationen, Kulturen und Notfallzulassungen; nominatim.openstreetmap.org für die Postleitzahlensuche; sowie der JKI-GeoServer und OSM-/Esri-Kachelserver für die Karten- und Geometrieauswahl. Diese Origins sind in der Content-Security-Policy verankert, sodass nichts anderes kontaktiert werden kann, selbst wenn eine Abhängigkeit es versuchte. Es gibt keine Telemetrie und keine Analyse. Ihre Datensätze verlassen das Gerät nur, wenn Sie sie selbst exportieren.

Nein, und die Benennung führt tatsächlich in die Irre: der .xml-Export ist eine SpreadsheetML-2003-Arbeitsmappe, kein fachliches XML-Dokument. Excel öffnet sie nativ, mit gestalteten Kopfzeilen und einem Referenzblatt aller zulässigen Einheitencodes, und die App importiert ihren eigenen Export unverändert zurück — dadurch taugt sie als von Hand ausfüllbare Vorlage. Ein separates „Excel“-Format gibt es nicht; das hier ist es. Ein Vorbehalt, den kein automatischer Test abdeckt: die Trust-Center-Dateiblockierung einer verwalteten Windows-Installation kann SpreadsheetML 2003 rundweg ablehnen.

Technische Themen.

Jedes Thema steht für sich. Öffnen Sie das, was Sie brauchen; der Rest bleibt eingeklappt.

Der Zuschnitt

DiPAgE ist eine Single-Page-Angular-Anwendung, die vollständig im Browser läuft. Es gibt keinen Anwendungsserver, keine bereitzustellende Datenbank und kein Kontosystem. Der einzige beteiligte Server liefert das statische Bundle aus, und damit endet seine Aufgabe. Alles, was die App weiß, liegt in der IndexedDB des Geräts, auf dem es erfasst wurde.

Technologieentscheidungen, die man kennen sollte

  • Angular 22 mit Standalone-Komponenten und durchgängig Signals. Change Detection ist zoneless, jede Komponente OnPush.
  • TypeScript im Strict-Modus einschließlich strictTemplates. Beachten Sie die Lücke: tsc prüft keine Templates, eine veraltete Bindung fällt erst beim AOT-Build auf.
  • Tailwind CSS 4 mit DaisyUI 5. Eigenes CSS nur dort, wo eine Utility-Klasse die Regel nicht ausdrücken kann.
  • UUIDv7 für jede erzeugte ID, damit Bezeichner nach Erstellungszeit sortieren.

Was zoneless kostet

Wer Tests gegen diese Codebasis schreibt, sollte eine Falle vorab kennen: unter zoneless Change Detection lässt sich eine im Konstruktor angelegte debounceTime- oder delay-Subscription in fakeAsync nicht mit tick() durchspülen. Die Zusicherung läuft dann durch, ohne die Entprellung überhaupt auszuführen. Legen Sie die Subscription stattdessen im Testkörper an.

Was lokale Datenhaltung kostet

Keine Synchronisation, kein Mehrgeräte-Abgleich, kein serverseitiges Backup. Daten zwischen Geräten zu bewegen ist ein ausdrücklicher Export und Import, und die zugehörige Konfliktlösung ist ein sichtbarer Ablauf statt eines Algorithmus im Hintergrund. Das ist der bewusste Preis dafür, ohne Verbindung zu arbeiten und keine personenbezogenen Daten zentral zu halten.

Die Speichereinheit

Gespeichert wird der PSM-Anwendungsdatensatz, gekennzeichnet als urn:psm:anwendung-datensatz:1.0.0 und definiert in record.schema.json (JSON Schema Draft 2020-12). Ein Datensatz beschreibt ein einmal angewandtes Mittel. Mehrere Mittel in einem Spritzgang oder mehrere behandelte Teile werden zu mehreren Datensätzen — das Formular multipliziert sie beim Speichern aus.

Wo die Komplexität wirklich sitzt

Der größte Teil des Schemas ist flach und unspektakulär. Die Ausnahme ist behandlungsort, ein Array, dessen standort_kennung_wert eine siebenfache Union ist, diskriminiert über standort_kennung_art: Koordinaten, Flurstückskennzeichen, InVeKoS-Referenz, Flächengeometrie, Gleisabschnitt, Forstadresse oder Weg. Eine davon — Wege — enthält ihrerseits eine weitere Union. Das Schema bildet das als if/then-Ketten unter allOf ab statt als benannte Varianten; ein generischer Validator kommt damit zurecht, eine handgeschriebene Prüfung muss die Verzweigung nachbauen.

Zwei Regeln, die nicht im Schema stehen

  1. Der Sanitizer ist die Formkontrolle. Jeder Lese- und Schreibvorgang wird gegen das Schema gefiltert. Ein Feld, das das Schema nicht kennt, wird stillschweigend verworfen — nicht laut abgelehnt. Wenn ein von Ihnen geschriebenes Feld danach fehlt, ist das der Grund.
  2. Manche Felder existieren im Modell, erscheinen aber in keinem Export. Beim Mittel indikation, anwendungszeitpunkt und notfallzulassung — reine UI-Felder, die auch nicht gespeichert werden. Die Ausnahme ist status am Datensatz: Der Sanitizer entfernt ihn zwar, die Speicherschicht hängt draft/saved danach aber wieder an, damit ein Entwurf ein Neuladen übersteht.

Feld für Feld

Jedes Feld mit Typ, Einschränkungen, einem einfügefertigen Beispiel und dem im Schema unsichtbaren Verhalten steht auf einer eigenen Seite — samt herunterladbarem Schema und einem vollständigen Beispieldatensatz, der alle sieben Ortsvarianten abdeckt.

Aufbau

Alles liegt in einer einzigen IndexedDB-Datenbank, psm-application-db, derzeit auf DB_VERSION 13. Sechs Object Stores: records (Anwendungsdatensätze), partial_data (wiederverwendbare Stammdatenvorlagen, nach Typ indiziert), settings, crops (die synchronisierte Kulturliste), notfallzulassungen (synchronisierte Notfallzulassungen) und logs.

Der Sanitizer liegt auf jedem Pfad

Lesen und Schreiben laufen beide durch den Schema-Filter. Das hält fremde oder veraltete Felder aus dem Speicher heraus — und ist zugleich der Grund, warum direktes Schreiben in die IndexedDB kein unterstützter Integrationsweg ist: Ihre zusätzlichen Schlüssel überleben das nächste Lesen nicht. Zum Debuggen können Sie die Datenbank dennoch in jedem Browser-Devtools-Panel öffnen (Store records) — als reine Lesediagnose.

Persistenz wird erbeten, nicht garantiert

Die App ruft beim Start navigator.storage.persist() auf. Lehnt die Nutzerin ab oder ignoriert der Browser die Bitte, werden gespeicherte Daten unter Speicherdruck verdrängbar — mobiles Safari ist der übliche Kandidat. Für eine App zur Felderfassung heißt das echter Datenverlust. Wer sie an Nutzer ausrollt, sollte den Backup-Export daher zum Bestandteil der Einführung machen, statt ihn als Funktion zum Selbstentdecken zu behandeln.

Wenn Sie das Schema ändern

Es gibt drei unabhängige Versionsnummern, die man nicht verwechseln darf: datensatz_format_version ist im Schema als const auf „1.0.0“ eingefroren und beschreibt das Datensatzformat; ein informeller Zähler („v12“, „v13“) in Codekommentaren und Changelog zählt Schema-Überarbeitungen und steht in keinem Datensatz; DB_VERSION ist die IndexedDB-Upgradeversion und taucht in keiner Exportdatei auf. Eine Schemaänderung bewegt DB_VERSION nicht zwangsläufig. Die Versionsnummern sind an Stellen gekoppelt, die Sie durch Ausführen der Testsuite nicht finden: die End-to-End-Helfer und der Schema-Integritätstest halten DB_VERSION beide fest verdrahtet. Ändert man sie an einer Stelle und nicht an den anderen, entstehen einige hundert Fehlschläge, die nach Flakiness aussehen statt nach Versionskonflikt.

Exportformate

  • JSON — formatierte Datensätze, exakt in Schemaform. Das Format, gegen das man baut.
  • XMLkein fachliches XML-Dokument, sondern eine SpreadsheetML-2003-Arbeitsmappe, die Excel nativ öffnet: gestaltete Kopfzeilen, fixierte Kopfzeile und ein Referenzblatt mit allen 284 zulässigen Einheitencodes. Ein separates „Excel“-Format gibt es nicht; das hier ist es.
  • CSV — flach, mit BOM, formelartige Werte gegen CSV-Injection entschärft.
  • PDF — ein abschnittsweiser, prüfungsorientierter Aufbau mit dem Betriebsimpressum. Flächengeometrie erscheint hier als Typ, Eckpunktzahl und Zentroid statt als Rohkoordinaten; jedes andere Format behält das vollständige Array.

Der zweizeilige Kopf

CSV und Arbeitsmappe teilen sich einen Vertrag, den man vor dem Schreiben eines Parsers verstehen sollte. Zeile 1 trägt lokalisierte Beschriftungen für Menschen. Zeile 2 trägt maschinelle Feldpfade — anwendung_zeitpunkt.datum, behandlungsort[0].bezeichnung. Die Daten beginnen in Zeile 3. Der Importer sucht die Pfadzeile, statt ihre Position anzunehmen, und kommt auch mit einer älteren einzeiligen Kopfzeile zurecht. Genau das lässt eine aus der deutschen Oberfläche exportierte Datei korrekt in der englischen importieren.

Import

Angenommen werden .json, .csv, .xml und .zip mit ebendiesen. Importe werden normalisiert (Punktpfade entflacht, Typen je Feld angeglichen, deutsche Dezimalkommata und TT.MM.JJJJ-Daten erkannt) und anschließend gegen das Schema geprüft. Datensätze, denen Pflichtfelder fehlen, landen in einem Prüfschritt, in dem die Nutzerin entscheidet, ob sie trotzdem übernommen werden; strukturell unbrauchbare Einträge werden gezählt und gemeldet. Die drei Systemfelder werden bei der Normalisierung ergänzt, wenn sie fehlen — eine handausgefüllte Vorlage mit leerer ID-Spalte importiert also trotzdem.

Konflikte

Ein Import mit gleicher ID und abweichendem Inhalt öffnet einen Versionsvergleich je Datensatz — importierte gegen aktuelle Fassung, mit der Option, beide zu behalten. Ein Treffer über den Inhalts-Fingerprint bei abweichenden IDs bildet eine eigene Konfliktklasse; das ist der Fall der zweimal importierten Vorlage. Der Fingerprint ist unabhängig von der Schlüsselreihenfolge, ignoriert ID, Erstellungszeitpunkt, Notizen und Status und reagiert auf Mittel, Aufwandmenge, Datum, Kultur, Ort und Anwender.

Backups

Ein vollständiges Backup ist ein ZIP mit records.json, master_data.json, profiles.json, settings.json und metadata.json. Die Wiederherstellung ist bewusst gestuft: erst eine ausdrückliche Bestätigung, dann Einlesen ohne Schreibvorgang, dann Konfliktklassifikation, dann Übernahme. Datensätze werden dabei so wiederhergestellt, wie sie gespeichert waren — auch Entwürfe und unvollständige —, denn die Schema-Konformität wird erst beim Export durchgesetzt. Schlagen Schreibvorgänge auf halbem Weg fehl, meldet die App eine Teilwiederherstellung, statt zu einem erneuten Versuch einzuladen, der doppelt anwenden würde.

Zwei Ebenen und die Lücke dazwischen

Die erste Ebene ist das JSON-Schema: Pflichtfelder, Typen, Enums, Formate, überall additionalProperties: false und die bedingten Einschränkungen als if/then-Ketten. Die zweite Ebene ist Anwendungscode und setzt Regeln durch, die das Schema nicht ausdrücken kann. Wer eigene Werkzeuge baut, sollte mit dieser Lücke rechnen: ein Datensatz kann dem Schema genügen und von der App dennoch abgelehnt werden.

Regeln, die nur im Anwendungscode stehen

  • Die Verschachtelungstiefe der Geometrie muss zum Geometrietyp passen. Das Schema verlangt nur, dass koordinaten ein Array ist.
  • Die Aufwandmenge muss größer als null sein. Das Schema sagt number.
  • Die Uhrzeit wird zur Pflicht, wenn das gewählte Mittel eine Bienenschutzauflage trägt.
  • Feldprüfungen je Ortsart, über das hinaus, was die if/then-Kette abdeckt.

Zwei Fallstricke im Schema selbst

Gut zu wissen, bevor Sie einen Validator-Widerspruch debuggen. Erstens: anwendung_zeitpunkt.uhrzeit hat format: time, gemeint ist die RFC-3339-Form full-time — ein Offset ist Pflicht. „06:15:00“ fällt durch, „06:15:00Z“ nicht. Ausgerechnet der examples-Eintrag des Schemas zeigt die Form ohne Offset; übernehmen Sie ihn nicht. Zweitens: die Streckenkilometer nutzen multipleOf: 0.1, was sich in binärer Gleitkommaarithmetik schlecht verhält. Ein typischer Validator rechnet wert / 0.1 und prüft auf eine ganze Zahl, und 0.3 / 0.1 ergibt 2,9999999999999996. Der Wert 0,3 — den das Schema ebenfalls als Beispiel führt — verletzt damit seine eigene Einschränkung.

Der Validator von DiPAgE selbst ist in beiden Punkten bewusst großzügiger: Der Offset ist optional, und multipleOf wird mit einer Toleranz verglichen. Aus der App exportierte Dateien können also Werte enthalten, die ein strenger Validator ablehnt — konfigurieren Sie Ihren Validator entsprechend.

Hart und weich

Nicht jede Regel blockiert. Stammdatenvorlagen nutzen durchgehend weiche Validierung: Warnungen verhindern nie ein Speichern. Im Datensatzformular sind mehrere feldübergreifende Prüfungen reine Hinweise — ein Erntejahr, das nicht zum Anwendungsdatum passt, eine behandelte Größe über der Standortgröße, ein umgekehrter BBCH-Bereich. Sie erscheinen als Warnung und lassen das Speichern zu.

Unvollständige Datensätze

„Unvollständig“ wird berechnet, nicht gespeichert: ein gespeicherter Datensatz wird erneut auf fehlende Pflichtfelder geprüft und mit einer Liste des Fehlenden markiert. Unvollständige Datensätze stehen oben in der Liste und werden von der Exportprüfung mit einer Meldung abgewiesen, bleiben aber in vollständigen Backups erhalten, damit eine Wiederherstellung verlustfrei ist.

SynOPS — der inhaltliche Kern

synops.julius-kuehn.de liefert vier Dinge: die Mittelsuche nach Handelsname oder Zulassungsnummer, eingegrenzt auf die gewählte Kultur und nach Zulassungsstatus gruppiert; die Indikationen zu einer Mittel-Kultur-Kombination samt Gefahrencodes und Dosierungsvorschlägen, die die Aufwandmenge automatisch füllen; die Kulturliste, je Sprache in die IndexedDB synchronisiert, damit die Suche offline funktioniert; und die Notfallzulassungen, ebenfalls lokal synchronisiert. Mittel- und Indikationsabfragen laufen live und entprellt, veraltete Antworten werden verworfen. Kulturen und Notfallzulassungen sind synchronisierte Datenbestände mit Altersprüfung statt Abfragen pro Anfrage.

Geokodierung und Kartendaten

nominatim.openstreetmap.org löst Postleitzahlen für die Kartenauswahl auf. Der JKI-GeoServer bedient die Geometrieauswahl über WFS: amtliche Feldblöcke samt FLIK- und Schlagnummern für Nordrhein-Westfalen (inv:NRW_FB_<Jahr>) und Umrisse aus der Kulturartenerkennung ohne jede Feldidentität für das übrige Bundesgebiet (cora:CORA_<Jahr>); OSM- und Esri-Hosts liefern die Kacheln. Nach der Auswahl eines Polygons legt die Nutzerin per Häkchen fest, welche zusätzlichen Identitätseinträge (InVeKoS-Referenz und/oder Zentroid-Koordinaten) angelegt werden — vorausgewählt ist nichts. Mit dem Polygon verbunden sind sie nur über eine gemeinsame bezeichnung, weil das Schema kein Verknüpfungsfeld kennt.

Alles davon ist festgelegt

Jeder dieser Origins steht in der Content-Security-Policy. Nichts anderes kann kontaktiert werden; das ist der Mechanismus hinter der Aussage „keine Telemetrie“, kein bloßes Versprechen. Wer die App forkt und auf eigene Hosts richtet, muss jeden dieser Origins an allen Stellen ergänzen, die im Thema Eigenbetrieb genannt sind, sonst schlagen die Anfragen still fehl.

Degradation

Fehler und Offline-Zustand schalten ein globales Banner zwischen offline und eingeschränkt. Lokales bleibt unberührt: jedes Feld lässt sich weiterhin von Hand füllen, Datensätze speichern, Exporte laufen. Nur die Nachschlagevorgänge verstummen.

Wenn Sie integrieren

Dies sind die Vorsysteme der App, keine API-Oberfläche, die DiPAgE Ihnen anbietet. Es gibt keinen DiPAgE-Endpunkt zum Aufrufen — siehe den ersten FAQ-Eintrag.

Caching-Modell

Die Offline-Fähigkeit stammt vom Angular Service Worker, konfiguriert in ngsw-config.json. App-Shell und Assets werden vorab zwischengespeichert; die externen API-Origins bekommen eigene Data Groups. Nach dem ersten erfolgreichen Laden ist die App ohne Netz vollständig bedienbar — Erfassung, Validierung, Speicherung, Export und PDF-Erzeugung laufen lokal.

Synchronisierte Bestände gegen Live-Abfragen

Zwei verschiedene Mechanismen, leicht zu verwechseln. Kulturen, Notfallzulassungen und Bienenschutz-Gefahrencodes werden in die IndexedDB synchronisiert und von dort gelesen, mit einer Altersprüfung beim Start statt einer Abfrage je Suche — deshalb funktioniert die Kultur-Autovervollständigung ohne Empfang. Mittelsuche und Indikationen sind live und fallen offline aus.

Kartenkacheln umgehen den Service Worker

Jede Kachel-URL trägt einen konstanten ngsw-bypass-Parameter, Kacheln laufen also gar nicht über den Service Worker, und fehlgeschlagene Ladevorgänge werden über den Wiedereinstiegspunkt von OpenLayers wiederholt statt über die Cache-Schicht. Gut zu wissen, wenn Sie untersuchen, warum sich Kacheln anders verhalten als jede andere Anfrage.

Installation und Erkennung

Die App ist als PWA installierbar und erkennt den Standalone-Modus. Die Installierbarkeit unterscheidet sich je Plattform auf eine Weise, die eher eine Support- als eine Codefrage ist — die Anwenderdokumentation führt auf, welche Browser und Betriebssysteme sie erlauben.

Wo das Risiko tatsächlich liegt

Es gibt keinen Server, keine Sitzung und keine Mandantenfähigkeit, damit entfällt die übliche Web-Angriffsfläche zum größten Teil. Was bleibt: die App verarbeitet Dateien, die ihr die Nutzerin übergibt — JSON, CSV, XML, ZIP — aus Quellen, für die sie nicht bürgen kann. Um diese Grenze herum ist die Härtung gebaut.

Härtung beim Import

  • ZIP: Positivliste für Eintragsnamen, Größen- und Anzahlgrenzen, Obergrenze für die entpackte Größe je Eintrag. Vollständige Backup-Archive weist der Datensatz-Import strukturell ab, bevor ein einziger Eintrag geparst wird.
  • XML: <!DOCTYPE> wird rundweg abgelehnt, dazu eine Obergrenze von 2 MB.
  • Prototype Pollution: jede Iteration über nicht vertrauenswürdige Schlüssel ist abgesichert. Das wiegt hier schwerer als sonst, weil das Entflachen von Punktpfaden schon konstruktionsbedingt über angreiferseitig gelieferte Schlüsselpfade läuft.
  • Backup-Wiederherstellung: Datensätze werden so wiederhergestellt, wie sie gespeichert waren (übersprungen werden nur Einträge, die keine Objekte sind), und durchlaufen bei jedem Lesen den Sanitizer; Vorlagen werden gegen ihre Form, Profile über eine Feld-Positivliste geprüft. Abgewiesenes wird gezählt und gemeldet statt still verworfen.

Härtung beim Export

CSV-Injection wird beim Export entschärft: ein formelartiger Wert wird so maskiert, dass er beim Öffnen in einer Tabellenkalkulation nicht ausgeführt wird. Im Arbeitsmappen-Pfad bleiben formelartige Werte dennoch wortgetreu erhalten, damit der Rundlauf keine Daten beschädigt.

Content-Security-Policy

Die CSP verankert die unter „Externe APIs“ genannten Origins. Sie steht im Meta-Tag der index.html und im Antwort-Header des ausliefernden Servers — beide werden getrennt durchgesetzt und müssen übereinstimmen; frame-ancestors gibt es nur im Header, weil eine Meta-CSP das nicht ausdrücken kann. Daneben: X-Frame-Options, nosniff, eine Referrer-Policy und ein Path-Traversal-Schutz im statischen Server.

Einwilligung vor Speicherung

Der Router-Outlet rendert erst nach erteilter Speichereinwilligung, es wird also nichts geschrieben, bevor zugestimmt wurde. Wer Tests schreibt, die die Einwilligung direkt in die IndexedDB setzen, muss danach neu laden — die App liest die Einwilligung nicht reaktiv, und das Auslassen dieses Neuladens hat schon ganze Suiten zerlegt.

Wo CSP und visuelle Tests auseinandergehen

Eine Falle sei benannt: eine per CSP blockierte Ressource rendert gleichbleibend kaputt, ein visueller Schnappschuss davon passt also weiterhin zu einer gleichbleibend kaputten Referenz. Ein neuer externer Origin muss in einem ausgelieferten Build geprüft werden; grüne End-to-End-Tests belegen nicht, dass er funktioniert.

Übersetzung

Drei Sprachen — de (Standard), en und pl — bei vollständiger Schlüsselparität, mit Punktnotation und {param}-Interpolation. Lokalisierte Formatierung umfasst Datum, Uhrzeit und erzeugte Dateinamen. Die Spracheinstellung liegt in der IndexedDB und ist von der Gerätesprache unabhängig.

Zwei Regeln, die den Code prägen

  1. Keine rohen Zeichenketten in der Oberfläche. Selbst Fehlermeldungen erscheinen als übersetzte Schlüssel; das Detail geht stattdessen in den Log-Store.
  2. translate() gibt bei einem Fehltreffer den Schlüssel zurück, nie einen leeren Wert. Eine fehlende Übersetzung zeigt sich also als sichtbarer Schlüssel statt als leeres Element — Absicht, denn eine leere Beschriftung übersieht man in der Durchsicht viel leichter.

Barrierefreiheit

Ziel ist WCAG 2.2, abgesichert aus drei Richtungen: ESLint-Regeln für Barrierefreiheit auf Templates, axe-core-Durchläufe end-to-end in beiden Themes und ein Tastatur-Durchlauf. Screenreader-Ansagen laufen über zwei ARIA-Live-Regionen, eine höfliche und eine bestimmte, verwaltet von einem einzigen Dienst. Routenwechsel, Theme-, Sprach- und Schriftgrößenwechsel sowie erscheinende Dialoge werden angesagt.

Themes und Skalierung

Zwei Themes, jkiLight und jkiDark, über data-theme gesetzt und gespeichert. Die Schriftskalierung beträgt 100, 150 oder 200 Prozent, angewandt auf die Wurzel-Schriftgröße, ebenfalls gespeichert. Wer eine Komponente ergänzt, muss sie bei 200 Prozent in beiden Themes tragfähig halten — an dieser Kombination bricht das Layout zuerst.

Die Stufen

  • Lint — Stil und Barrierefreiheitsregeln auf Templates.
  • Typprüfung — TypeScript in .ts-Dateien. Nicht in Templates.
  • Invarianten — ein dateiübergreifendes Konsistenzskript: das DB_VERSION-Tripel, das CSP-Paar aus Meta und Header, die Data Groups des Service Workers, die Geo-Grenzen in TypeScript gegen das Schema, die Maximallänge von notizen, der Gleichstand des generierten Schema-Validators, additionalProperties: false an jedem Objekt, i18n-Parität und verwaiste Schlüssel sowie eine Mindestwartezeit für neue pnpm-Pakete.
  • Unit — über 3.400 Karma/Jasmine-Tests, etwa 92 % Statement- und 85 % Branch-Abdeckung. Ein Schwellwert ist nicht konfiguriert — das ist also eine Messung, keine Schranke.
  • Build (AOT) — die einzige Stufe, die eine veraltete Template-Bindung abfängt.
  • End-to-End — echte Abläufe plus visuelle Schnappschüsse über Desktop, mobiles Chrome und mobiles Safari.
  • Barrierefreiheit — axe-core in beiden Themes plus ein Tastatur-Durchlauf.
  • Serversicherheit — Abweisung von Path Traversal und die Sicherheits-Header.

Wo ein grüner Lauf lügt

Wert, verinnerlicht zu werden, bevor Sie einer bestandenen Pipeline vertrauen:

  1. Eine grüne Typprüfung heißt nicht, dass eine Umbenennung angekommen ist — Templates werden erst beim AOT-Build geprüft.
  2. Vom Compiler stillgelegte Fixtures (as unknown as-Casts auf Datensatzformen) laufen nach einer Feldänderung gegen ein veraltetes Schema weiter durch.
  3. Eine Spy-Liste ohne eine neu ergänzte Methode liefert undefined und erzeugt ein irreführendes „nie aufgerufen“.
  4. Eine per CSP blockierte Ressource passt weiterhin zu ihrer kaputten Referenz; visuelle Tests belegen also nicht, dass ein neuer Origin funktioniert.

Schnappschuss-Politik

Visuelle Referenzbilder werden von Menschen aktualisiert, nie automatisch und nie von der CI. Die Abweichung ist das Prüfsignal; sie bei einem Fehlschlag neu zu erzeugen wirft genau das weg, was der Test gemessen hat.

Voraussetzungen

Node.js 22 oder neuer und pnpm 12 oder neuer zum Bauen. Sonst nichts — keine Datenbank, kein Message Broker, kein externer Dienst zum Registrieren. Laufzeitziel ist jeder moderne Browser mit ES2022, IndexedDB und Service-Worker-Unterstützung.

Auslieferung

Der Build erzeugt ein statisches Bundle. Für den eigenständigen Betrieb liegt ein kleiner Node-Server bei, etwa unter PM2 zu betreiben; die Referenzinstallation des JKI nutzt ihn allerdings nicht, sondern liefert das Bundle als statische Dateien unter /app/ einer übergeordneten Website aus, die ihre eigenen Sicherheits-Header setzt. Der mitgelieferte Server beherrscht einen konfigurierbaren Basispfad, der Betrieb in einem Unterverzeichnis funktioniert also ohne Umschreiben der Asset-URLs. Er erzwingt einen Path-Traversal-Schutz, setzt die Sicherheits-Header und liefert gehashte Assets als unveränderlich, die App-Shell als nicht zwischenspeicherbar aus.

HTTPS ist nicht optional

Service Worker verlangen einen sicheren Kontext. Ohne HTTPS bekommen Sie keine eingeschränkte Installationserfahrung, sondern gar keine Offline-Fähigkeit — womit die zentrale Prämisse der App entfällt.

Die eigentliche Aufgabe ist die Content-Security-Policy

Ein neuer externer Origin muss an drei Stellen eingetragen werden: im Meta-Tag der index.html, im Antwort-Header des ausliefernden Servers (des mitgelieferten Servers oder der übergeordneten Website) und — für API-Hosts — in den dataGroups der ngsw-config.json; fehlt er dort, beantwortet der Service Worker einen fehlgeschlagenen Abruf mit einem 504. Meta- und Header-CSP werden getrennt durchgesetzt; frame-ancestors existiert nur im Header, weil eine CSP im Meta-Tag das nicht ausdrücken kann. Wer die App auf einen eigenen SynOPS-Spiegel, einen eigenen Geokodierer oder einen eigenen Kachelserver richtet, muss jeden dieser Origins an allen drei Stellen ergänzen. Ein vergessener Origin scheitert zur Laufzeit, und zwar leise; eine bestandene Testsuite verrät es Ihnen nicht — prüfen Sie gegen einen ausgelieferten Build.

Pipeline

In der Referenz-CI sind Lint und Unit-Tests die Voraussetzung für den AOT-Build, und der Build ist die Voraussetzung für die Auslieferung per SSH. Die End-to-End-Tests laufen parallel mit, blockieren eine Auslieferung aber nicht; Invarianten-Skript und Server-Sicherheitstest laufen in der CI nicht. Übernehmenswert ist eher die Reihenfolge als das Werkzeug: der AOT-Build steht bewusst nach den Unit-Tests, weil er die Stufe ist, die Template-Brüche abfängt, welche die früheren Stufen nicht sehen.

Lizenz und Repository

Der Client steht unter der MIT-Lizenz. Das Repository auf dem Git-Server des JKI ist noch nicht öffentlich — schreiben Sie dem Team für den Zugang. Forks für institutsspezifische Abläufe sind danach ausdrücklich willkommen, Issues und Patches ebenso.

Konventionen

Commits folgen den Conventional Commits, erzwungen durch commitlint über einen Husky-Hook. Gelintet wird mit ESLint samt Angular- und TypeScript-Regelsätzen. Beides ist in der CI nicht verhandelbar, richten Sie es also vor dem ersten Push lokal ein statt nach der ersten abgelehnten Pipeline.

Fragen Sie zuerst nach dem Entscheidungsprotokoll

Das Projekt führt ein Protokoll über Entscheidungen, die falsch aussehen, aber keine sind, jeweils mit Begründung und einem ausdrücklichen „nicht tun“-Hinweis. Mehrere Überraschungen auf dieser Seite sind Einträge darin: die nicht standardkonformen Koordinatengrenzen, der Notfallzulassungs-Marker in einem Freitextfeld, die abgeleitete statt gespeicherte Tankmischung, das bewusst gekürzte Einheitenvokabular. Das Protokoll liegt derzeit nicht im Repository; fragen Sie das Team danach. Wenn im Code etwas nach einem offensichtlichen Fehler aussieht, klären Sie das, bevor Sie eine Korrektur öffnen — es könnte tragend sein.

Wenn Sie die Datensatzform ändern

Eine Feldänderung hat Spiegelstellen, die Ihnen kein einzelner Testfehlschlag aufzählt: das Schema, die TypeScript-Schnittstellen, die Formularbauer samt Validatoren, die Flach-zu-verschachtelt-Mapper, die Import-Typangleichung, die Export-Spaltensätze, die End-to-End-Seed-Helfer und die lokalisierten Spaltenbeschriftungen in allen drei Sprachen. Das Projekt pflegt eine Karte dieser Stellen, die ebenfalls noch nicht im Repository liegt — fragen Sie danach und arbeiten Sie sie durch, statt roten Pipelines einzeln hinterherzulaufen.

Sie suchen ein bestimmtes Feld? Zur Feldreferenz des Datensatzes

Direkt starten

DiPAgE direkt im Browser nutzen. Ohne Anmeldung und kostenlos.

Öffnen Sie die Anwendung und beginnen Sie direkt mit der Dokumentation Ihrer Pflanzenschutzmittelanwendungen. Eine Installation ist nicht erforderlich, auf unterstützten Geräten kann DiPAgE dennoch zusätzlich installiert und offline genutzt werden.

Direkt im Browser
Keine Installation erforderlich. Anwendung öffnen und direkt beginnen.
Keine Anmeldung
DiPAgE kann ohne Benutzerkonto oder Registrierung genutzt werden.
Daten lokal gespeichert
Ihre Dokumentationsdaten verbleiben auf dem verwendeten Gerät.
Offline nutzbar
Auf unterstützten Geräten kann DiPAgE installiert und ohne Internetverbindung verwendet werden.