feat(13-04): NetServer-Adapter fuer tender24/lhs-vpbw/vergabe.landbw (INGEST-02)

- Package-Legitimacy-Checkpoint bestaetigt: cheerio (sauberer Gate-Pass,
  27M Downloads/Woche) statt node-html-parser installiert
- EIN config-getriebener NetServerAdapter bedient alle 3 AI-AG-Portale
  via PublicationSearchControllerServlet-Trefferliste, parst <table> mit
  cheerio zu RawTenderRecord[], sourcePortal je Zeile korrekt
- sourceNoticeId = data-oid (stabil, per Zeile eindeutig); sourceUrl =
  Such-URL als dokumentierter Best-Effort-Fallback (kein Deep-Link ohne
  JS-Ausfuehrung ermittelbar, Open Question 2)
- Fehlertoleranz: try/catch pro Zeile + pro Portal, []-Fallback bei
  Totalausfall, AbortController 15s-Timeout, hardcodierte Portal-URLs
  (SSRF-Guard T-13-04-01)
- Live-gecapturte Fixture (tender24.de, 2026-07-23) mit Umlaut-/&amp;-
  Buyer, gefuellter und leerer Deadline; 14 Spec-Tests gruen

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-23 09:13:01 +02:00
parent 2c28605397
commit 043ada9438
5 changed files with 651 additions and 21 deletions
+26 -21
View File
@@ -62,6 +62,9 @@ importers:
argon2:
specifier: ^0.44.0
version: 0.44.0
cheerio:
specifier: ^1.2.0
version: 1.2.0
class-transformer:
specifier: ^0.5.1
version: 0.5.1
@@ -2569,6 +2572,10 @@ packages:
resolution: {integrity: sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==}
engines: {node: '>=18.17'}
cheerio@1.2.0:
resolution: {integrity: sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg==}
engines: {node: '>=20.18.1'}
chokidar@4.0.3:
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
engines: {node: '>= 14.16.0'}
@@ -7905,7 +7912,6 @@ snapshots:
domelementtype: 2.3.0
domhandler: 5.0.3
domutils: 3.2.2
optional: true
cheerio@1.0.0:
dependencies:
@@ -7922,6 +7928,20 @@ snapshots:
whatwg-mimetype: 4.0.0
optional: true
cheerio@1.2.0:
dependencies:
cheerio-select: 2.1.0
dom-serializer: 2.0.0
domhandler: 5.0.3
domutils: 3.2.2
encoding-sniffer: 0.2.1
htmlparser2: 10.1.0
parse5: 7.3.0
parse5-htmlparser2-tree-adapter: 7.1.0
parse5-parser-stream: 7.1.2
undici: 7.28.0
whatwg-mimetype: 4.0.0
chokidar@4.0.3:
dependencies:
readdirp: 4.1.2
@@ -8108,7 +8128,6 @@ snapshots:
domhandler: 5.0.3
domutils: 3.2.2
nth-check: 2.1.1
optional: true
css-selector-parser@3.3.0: {}
@@ -8123,8 +8142,7 @@ snapshots:
mdn-data: 2.27.1
source-map-js: 1.2.1
css-what@6.2.2:
optional: true
css-what@6.2.2: {}
css.escape@1.5.1: {}
@@ -8283,10 +8301,8 @@ snapshots:
domelementtype: 2.3.0
domhandler: 5.0.3
entities: 4.5.0
optional: true
domelementtype@2.3.0:
optional: true
domelementtype@2.3.0: {}
domhandler@4.3.1:
dependencies:
@@ -8296,7 +8312,6 @@ snapshots:
domhandler@5.0.3:
dependencies:
domelementtype: 2.3.0
optional: true
domutils@2.8.0:
dependencies:
@@ -8310,7 +8325,6 @@ snapshots:
dom-serializer: 2.0.0
domelementtype: 2.3.0
domhandler: 5.0.3
optional: true
dotenv-expand@12.0.3:
dependencies:
@@ -8368,7 +8382,6 @@ snapshots:
dependencies:
iconv-lite: 0.6.3
whatwg-encoding: 3.1.1
optional: true
enhanced-resolve@5.21.6:
dependencies:
@@ -8386,13 +8399,11 @@ snapshots:
entities@3.0.1:
optional: true
entities@4.5.0:
optional: true
entities@4.5.0: {}
entities@6.0.1: {}
entities@7.0.1:
optional: true
entities@7.0.1: {}
entities@8.0.0: {}
@@ -8951,7 +8962,6 @@ snapshots:
domhandler: 5.0.3
domutils: 3.2.2
entities: 7.0.1
optional: true
htmlparser2@7.2.0:
dependencies:
@@ -9004,7 +9014,6 @@ snapshots:
iconv-lite@0.6.3:
dependencies:
safer-buffer: 2.1.2
optional: true
iconv-lite@0.7.2:
dependencies:
@@ -10531,12 +10540,10 @@ snapshots:
dependencies:
domhandler: 5.0.3
parse5: 7.3.0
optional: true
parse5-parser-stream@7.1.2:
dependencies:
parse5: 7.3.0
optional: true
parse5@7.3.0:
dependencies:
@@ -12077,10 +12084,8 @@ snapshots:
whatwg-encoding@3.1.1:
dependencies:
iconv-lite: 0.6.3
optional: true
whatwg-mimetype@4.0.0:
optional: true
whatwg-mimetype@4.0.0: {}
whatwg-mimetype@5.0.0: {}