fix: Typisierung von oeffnen(), CSP im Entwicklungsmodus, Nonce-Abweichung
Drei Fehler, die sich beim Durchsehen der Typpruefung und der
Browserkonsole gezeigt haben.
1. oeffnen() lieferte Record<string, string | null>. Zusammen mit
noUncheckedIndexedAccess wurde daraus bei jedem Zugriff
string | null | undefined - und jede aufrufende Seite haette das
wegkaesen muessen, obwohl die Felder immer da sind. Jetzt leitet ein
Mapped Type die Klartextnamen aus VERSCHLUESSELTE_FELDER ab:
oeffnen('Betreute', zeile).diagnosen ist sauber string | null.
Das hat auf einen Schlag 17 Folgefehler in fremden Modulen erledigt.
2. Die CSP blockierte im Entwicklungsmodus die eigenen Skripte.
'strict-dynamic' schaltet die Freigabe nach Herkunft ab, also auch
'self' - und Hot-Reload und Fehleranzeige laden ohne Nonce. Man sah
eine tote Seite statt der Fehlermeldung. Im Produktivbetrieb bleibt
es bei Nonce plus 'strict-dynamic', im Entwicklungsmodus gilt die
lockere Fassung.
3. Das Theme-Skript im Layout meldete bei jedem Seitenaufruf eine
Hydrations-Abweichung: React gibt das nonce-Attribut absichtlich
nicht an den Browser weiter, serverseitig steht es aber im HTML.
suppressHydrationWarning am Skript selbst - das html-Element allein
deckt das nicht ab.
Nebenbei: Algorithm.Argon2id ist ein const enum und laesst sich mit
isolatedModules nicht importieren. Die Angabe entfaellt, @node-rs/argon2
benutzt von sich aus Argon2id.
Die Anmeldeseite laedt jetzt ohne einen einzigen Konsolenfehler.
Etappe: 8
Phase: Haertung
This commit is contained in:
+28
-7
@@ -177,6 +177,22 @@ export const VERSCHLUESSELTE_FELDER = {
|
||||
|
||||
export type VerschluesseltesModell = keyof typeof VERSCHLUESSELTE_FELDER
|
||||
|
||||
/** Aus "diagnosenVerschluesselt" wird "diagnosen". */
|
||||
type OhneSuffix<F extends string> = F extends `${infer Name}Verschluesselt` ? Name : never
|
||||
|
||||
/**
|
||||
* Die Klartextfelder eines Modells als benannte Eigenschaften.
|
||||
*
|
||||
* Ohne diesen Typ waere das Ergebnis von oeffnen() nur ein
|
||||
* Record<string, string | null>. Mit noUncheckedIndexedAccess bekaeme
|
||||
* jeder Zugriff darauf zusaetzlich "undefined" - und jede aufrufende
|
||||
* Seite muesste das wegkaesen, obwohl die Felder tatsaechlich immer da
|
||||
* sind.
|
||||
*/
|
||||
export type Klartextfelder<M extends VerschluesseltesModell> = {
|
||||
[K in OhneSuffix<(typeof VERSCHLUESSELTE_FELDER)[M][number]>]: string | null
|
||||
}
|
||||
|
||||
/**
|
||||
* Nimmt ein Objekt mit Klartextfeldern (ohne Suffix) und liefert die
|
||||
* verschluesselten Felder (mit Suffix). Beispiel:
|
||||
@@ -209,17 +225,22 @@ export function sealen(
|
||||
* Umschlagfelder bleiben stehen, damit nichts versehentlich zurueck-
|
||||
* geschrieben wird.
|
||||
*/
|
||||
export function oeffnen<T extends Record<string, unknown>>(
|
||||
modell: VerschluesseltesModell,
|
||||
export function oeffnen<M extends VerschluesseltesModell, T extends Record<string, unknown>>(
|
||||
modell: M,
|
||||
datensatz: T,
|
||||
): T & Record<string, string | null> {
|
||||
const ergebnis = { ...datensatz } as T & Record<string, string | null>
|
||||
): T & Klartextfelder<M> {
|
||||
// Zwischenschritt ueber ein einfaches Objekt: ein generischer Typ laesst
|
||||
// sich in TypeScript nicht per Index beschreiben.
|
||||
const ergebnis: Record<string, unknown> = { ...datensatz }
|
||||
|
||||
for (const feld of VERSCHLUESSELTE_FELDER[modell] as readonly string[]) {
|
||||
if (!(feld in datensatz)) continue
|
||||
const klarname = feld.replace(/Verschluesselt$/, '')
|
||||
ergebnis[klarname] = entschluesselnWennMoeglich(datensatz[feld] as string | null, feld)
|
||||
ergebnis[klarname] = (feld in datensatz)
|
||||
? entschluesselnWennMoeglich(datensatz[feld] as string | null, feld)
|
||||
: null
|
||||
}
|
||||
return ergebnis
|
||||
|
||||
return ergebnis as T & Klartextfelder<M>
|
||||
}
|
||||
|
||||
/** Konstantzeit-Vergleich zweier Zeichenketten. */
|
||||
|
||||
Reference in New Issue
Block a user