refactor(quick-260921-bi2): maschinelle Lint-Fixe und toten Code abbauen

- Aufgabe 2: vier sichere Biome-Regeln (useImportType pfadgebunden auf
  apps/web+packages, noUselessEscapeInRegex, useConst,
  useExponentiationOperator) sowie fuenf ungesicherte Regeln
  (useNodejsImportProtocol, useLiteralKeys, useOptionalChain, useTemplate,
  useParseIntRadix) angewendet und den gesamten Diff von Hand gelesen
  (ldap.service.ts zeichenweise gegen Gross-/Kleinschreibung der
  AD-Merkmale, auth.service.ts/jwt.strategy.ts gegen Durchwinken bei
  fehlender Sitzung geprueft)
- noUselessSwitchCase bleibt bewusst stehen (tender-normalizer.service.ts:60,
  die Fallmarke dokumentiert Absicht)
- Toter Code (D-03): fuenf folgenlose Auffangvariablen entfernt, eine
  nicht benutzte Funktion (forSystemQuery, Pruefskript) entfernt, ein
  positionsgebundener Dekoratorparameter umbenannt (current-user.decorator.ts),
  fuenf Symptomfunde entfernt und als Folgeaufgaben zu melden (siehe unten)
- Sechs weitere, im Plan nicht namentlich gelistete aber
  gleich-kategorische Dead-Code-Fundstellen in Testdateien zusaetzlich
  bereinigt (groups.service.spec.ts, cert-manager.test.tsx,
  ldap.service.spec.ts, prisma-tenant.extension.spec.ts x3) — noetig, um
  die vom Plan selbst verlangten Nullstaende bei noUnusedVariables/
  noUnusedImports/noUnusedFunctionParameters zu erreichen

Dekoratordaten aus apps/api unveraendert (593 Zeilen, sha256 6e1583f1...).
Endstand 620 Befunde (541 echt, 79 Test) statt der im Plan geschaetzten
621/542 — eine Differenz von 1, weil das Streichen des Namens aus
`catch (e: any)` in calendar.service.ts (Symptom-Fix) den dort ebenfalls
gemeldeten noExplicitAny-Befund miteliminiert; das ist eine erwuenschte
Nebenwirkung, keine Regression. Fehlerstufe 0, beide Testlaeufe
punktgleich gruen (69/1124, 66/459), pnpm type-check 4/4, pnpm lint
--force 5/5.

Folgeaufgaben aus D-03 (nicht in diesem Vorgang behoben):
- force-password-change.interceptor.ts: Freigabeliste prueft nur den Pfad,
  nicht die HTTP-Methode
- change-password/page.tsx: nach erzwungenem Wechsel bleibt die Person auf
  der Seite stehen (keine Weiterleitung, keine Aktualisierung der
  Benutzerablage)
- VehicleTable.tsx: Loeschschaltflaeche hat keinen Besetztzustand, laesst
  sich doppelt ausloesen
- SplitTab.tsx: downloadAllAsZip erhielt eine ungenutzte
  Uebersetzungsfunktion, Hinweis auf fest verdrahtete Texte im Zip-Pfad

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
This commit is contained in:
2026-09-21 08:58:32 +02:00
parent 8d1c8f320b
commit 636fe0df8f
56 changed files with 126 additions and 147 deletions
@@ -1,5 +1,5 @@
import { readFileSync } from 'fs';
import { join } from 'path';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { CosinexAdapter } from './cosinex.adapter';
@@ -1,6 +1,6 @@
import AdmZip from 'adm-zip';
import { readFileSync } from 'fs';
import { join } from 'path';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { buildDoeNoticeUrl, DoeOpenDataAdapter } from './doe-opendata.adapter';
@@ -1,5 +1,5 @@
import { readFileSync } from 'fs';
import { join } from 'path';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import { Logger } from '@nestjs/common';
import { describe, expect, it, vi } from 'vitest';
import {
@@ -1,7 +1,7 @@
import { Injectable, Logger } from '@nestjs/common';
import { CryptoService } from '../../crypto/crypto.service';
import * as cheerio from 'cheerio';
import { createHash } from 'crypto';
import { createHash } from 'node:crypto';
import { ExchangeInboxProvider } from '../../inbox/exchange-inbox.provider';
import { ImapProvider } from '../../inbox/imap.provider';
@@ -1,5 +1,5 @@
import { readFileSync } from 'fs';
import { join } from 'path';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { NetServerAdapter } from './netserver.adapter';
@@ -1,5 +1,5 @@
import { readFileSync } from 'fs';
import { join } from 'path';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { RssAdapter } from './rss.adapter';
+1 -1
View File
@@ -1,5 +1,5 @@
import { Injectable, Logger } from '@nestjs/common';
import { createHash } from 'crypto';
import { createHash } from 'node:crypto';
import { XMLParser } from 'fast-xml-parser';
import { PrismaService } from '../../prisma/prisma.service';
import type { RawTenderRecord, SourceType } from '../tender.types';
@@ -235,8 +235,8 @@ describe('TenderDigestScheduler — Multi-Tenant via findMany (Pitfall 1)', () =
});
it('never uses findFirst anywhere in the module source — findMany over all due users is mandatory (Pitfall 1)', async () => {
const { readFileSync } = await import('fs');
const { join } = await import('path');
const { readFileSync } = await import('node:fs');
const { join } = await import('node:path');
const source = readFileSync(join(__dirname, 'tender-digest.scheduler.ts'), 'utf8');
expect(source).not.toMatch(/findFirst/);
});
@@ -168,7 +168,7 @@ export class TenderDigestScheduler implements OnModuleInit {
// Kein Konto, oder ein Konto ohne Adresse (WINDOWS #15, kollidierte
// AD-Adresse) -- die Zugehoerigkeit funktioniert, nur der
// Mailversand wird uebersprungen (zugesagtes Verhalten).
if (!user || !user.email) continue;
if (!user?.email) continue;
const sections = groupMatchesByProfile(matches);
const sent = await this.mail.sendDigest({ email: user.email }, user.tenantId, sections);
+1 -1
View File
@@ -1,4 +1,4 @@
import { createHash } from 'crypto';
import { createHash } from 'node:crypto';
/**
* tenderFingerprint — pure, deterministic cross-source dedup key (SCHEMA-03,
@@ -1,5 +1,5 @@
import { readFileSync } from 'fs';
import { join } from 'path';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import { describe, expect, it, vi } from 'vitest';
import { TenderIngestionService } from './tender-ingestion.service';
@@ -150,7 +150,7 @@ export class TenderMatchingService {
// Kein Konto, oder ein Konto ohne Adresse (WINDOWS #15, kollidierte
// AD-Adresse) -- die Zugehoerigkeit funktioniert, nur der
// Mailversand wird uebersprungen (zugesagtes Verhalten).
if (!user || !user.email) continue;
if (!user?.email) continue;
const sent = await this.mail.sendInstant(
{ email: user.email },
@@ -1,7 +1,7 @@
import AdmZip from 'adm-zip';
import { XMLParser } from 'fast-xml-parser';
import { readFileSync } from 'fs';
import { join } from 'path';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import { describe, expect, it } from 'vitest';
import type { RawTenderRecord, SourceType } from './tender.types';
import { TenderNormalizerService } from './tender-normalizer.service';
@@ -1,5 +1,5 @@
import { Injectable } from '@nestjs/common';
import { createHash } from 'crypto';
import { createHash } from 'node:crypto';
import { divisionOf } from './cpv/cpv-catalog';
import { bundeslandFromRegion } from './geo/nuts-bundesland';
import type { NormalizedTenderFields, RawTenderRecord } from './tender.types';
@@ -64,7 +64,7 @@ function makeFakePrisma() {
const tenderRssFeedSource = {
findMany: async ({ where, orderBy }: any = {}) => {
let all = [...rows.values()].filter((row) => matchesWhere(row, where));
const all = [...rows.values()].filter((row) => matchesWhere(row, where));
if (orderBy?.createdAt === 'asc') {
all.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
}
@@ -1,5 +1,5 @@
import { readFileSync } from 'fs';
import { join } from 'path';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import { describe, expect, it, vi } from 'vitest';
import { ModuleRegistryService } from '../module-registry/module-registry.service';
import { TenderSchedulerService } from './tender-scheduler.service';