Sprint 25: 19-Sites-Korpus — verbleibende Blockbuster (youtube, ___dynamicImportLoader, Proof-Runner) #117

Closed
opened 2026-06-20 05:34:46 +00:00 by Artur · 1 comment
Owner

Sprint 25: 19-Sites-Korpus — verbleibende Blockbuster beheben

Problembeschreibung

Der Corpus-Runner zeigt 17/19 , aber das ist eine oberflächliche Metrik ("kein JS-Crash"). Der Proof-Runner (echte DOM-Validierung) zeigt, dass viele Sites nur teilweise oder scheinbar korrekt laden:

  • youtube.com + speedtest.net: 0 HTTP-Requests, kein DOM → kompletter Netzwerk-Fail
  • qwik.dev, solidjs.com, angular.io, codepen.io: ___dynamicImportLoader crasht bei relativen URLs ohne Origin (/build/q-*.js)
  • Cloudflare-Challenge-Seiten: HTML statt JS geliefert → "Empty chunk" Errors (kosmetisch, aber unschön)
  • replaceStaticImports: SyntaxError auf vuejs.org (import({...})___dynamicImport({...}))
  • Proof-Runner: Kein incrementelles Speichern, crasht bei Fehlern → verliert alle bisherigen Ergebnisse

Ziel: Alle 19 Sites vollständig und korrekt laden, gemessen an Proof-Queries im LIVE DOM (nicht nur "kein Crash").


Analyse: Aktueller Datenstand

Corpus-Runner (17/19 — oberflächlich)

 1 vuejs.org       ✅ 1175ms  82KB   13   1
 2 kit.svelte.dev  ✅  445ms  87KB    2   0    ← Vercel Challenge (DOM=Challenge, nicht SvelteKit)
 3 solidjs.com     ✅ 1045ms   3KB    6   1    ← Cloudflare Challenge (3KB = Challenge-Page)
 4 qwik.dev        ✅  965ms 206KB   11   0    ← DOM existiert, aber es gibt JS-Fehler
...
15 youtube.com     ❌  441ms   0B     0   0    ← 0 Requests, 0 DOM
19 speedtest.net   ❌  779ms   0B     0   0    ← 0 Requests, 0 DOM

Proof-Runner (echte DOM-Queries)

Die Ergebnisse aus proof-runner.ts zeigen, dass NUR VueJS alle Proof-Queries besteht. Die restlichen Sites scheitern an unterschiedlichen Barrieren.


Mermaid — Dependency Graph der Blockierer

flowchart TD
    A["Sprint 25 Epic"] --> B["youtube + speedtest: 0 Requests"]
    A --> C["___dynamicImportLoader crash"]
    A --> D["HTML-served-as-JS"]
    A --> E["replaceStaticImports Regex"]
    A --> F["Proof-Runner Resilienz"]

    C --> C1["Root: ___importMeta.resolve() liefert /build/... ohne Origin"]
    C --> C2["fetch() in ___dynamicImportLoader scheitert"]
    C --> C3["Nur bei Qwik/angular/discord mit dynamischen Chunks"]

    D --> D1["Cloudflare/Vercel liefern HTML auf JS-URLs"]
    D --> D2["ScriptLoader erkennt '<!--' am Start → Skipt"]
    D --> D3["Chunk-Fehler werden nicht propagiert, aber loggen Noise"]

    E --> E1["Root: import(...) Regex matcht auch import({...})"]
    E --> E2["replaceDynamicImports transformiert zu ___dynamicImport({...})"]
    E --> E3["SyntaxError in eval — non-fatal aber in Proof sichtbar"]

    F --> F1["Keine incrementelle Speicherung"]
    F --> F2["Ein Site-Crash killt alle bisherigen Ergebnisse"]
    F --> F3["Kein Timeout pro Site"]

Sub-Issue 1: youtube.com + speedtest.net — 0 Requests

Root-Cause-Analyse

Beide Sites haben exakt 0 Network-Requests und 0 DOM-Größe („-" im Corpus-Report). Das deutet darauf hin, dass die DNS-Auflösung oder der Verbindungsaufbau komplett scheitert.

Mögliche Ursachen:

  1. TransportManager blockiert die Domains (Curl/CURL_IMPERSONATE-Transport)
  2. HTTP/2 oder HTTP/3 wird nicht unterstützt
  3. YouTube sendet sofort einen Redirect (HTTP 3xx), den unser TransportManager nicht korrekt verarbeitet
  4. DNS-Resolve scheitert für bestimmte TLDs oder CDN-Endpunkte

Option A — Network-Debug-Session (empfohlen)

Ein dediziertes Debug-Script, das jede Seite Schritt für Schritt durch den Network-Stack verfolgt:

  1. DNS-Resolution prüfen: bun -e 'console.log(await Bun.resolve("youtube.com"))'
  2. TransportManager-Konfiguration loggen
  3. curl-Transport-Toggle testen (useCurlTransport: true/false)
  4. Raw-HTTP-Request via fetch() außerhalb des Browsers
  5. Ergebnis: Genauer Fail-Point

Aufwand: 2h — reine Analyse

Option B — YouTube-spezifischen Interceptor

Vorhandene YouTube-Redirect-Muster erkennen und bypassen. YouTube verwendet oft:

  • https://www.youtube.com → redirect zu https://www.youtube.com/?app=desktop
  • Service Worker Registration
  • YT-Initial-Data im HTML

Aufwand: 4h — schwer wartbar

Option C — Minimal: Einen bekannten Workaround finden

Andere Headless-Browser (Playwright, Puppeteer) laden YouTube → deren Network-Log als Referenz nutzen.

Aufwand: 1h


Sub-Issue 2: ___dynamicImportLoader crasht bei relativen URLs ohne Origin

Root-Cause-Analyse

In execution-realm.ts Zeile ~586ff wird ___dynamicImportLoader definiert:

win.___dynamicImportLoader = async (moduleUrl: string) => {
  const response = await fetch(moduleUrl);  // <-- crash hier
  // ...
}

Wenn ___importMeta.resolve() einen relativen Specifier wie "./chunks/q-993Mv2pg.js" auflöst, erzeugt new URL(specifier, "https://qwik.dev/assets/main.js")https://qwik.dev/build/q-993Mv2pg.js.

ABER: Wenn die Module-URL selbst aus einem anderen Kontext kommt (z.B. Inline-Script, oder der url-Parameter ist nicht korrekt gesetzt), wird new URL(specifier, baseUrl) fehlschlagen und /build/q-993Mv2pg.js (nur Pfad, kein Origin) liefern.

Fundamental: ___importMeta.resolve() setzt eine korrekte Basis-URL voraus. Wenn das Modul import.meta.url = "https://qwik.dev/assets/main.js" hat, sollte die resolution korrekt sein. Das Problem ist, dass das Modul evtl. ohne URL ausgeführt wird → import.meta.resolve fällt auf die aktuelle Document-Base-URL zurück, die nicht gesetzt ist.

Option A — fallback auf document.baseURI (empfohlen)

var ___dynamicImport = async function(specifier) {
  var res = ___importMeta.resolve(specifier);
  // Fallback: resolve gegen document.baseURI
  if (res.startsWith("/")) {
    var base = typeof document !== "undefined" ? document.baseURI : "";
    res = new URL(res, base).href;
  }
  // ...
};

Aufwand: 1h — minimaler Fix

Option B — ___importMeta.resolve defensiv machen

buildImportMetaInjection muss sicherstellen, dass import.meta.resolve() immer einen absolute URL zurückgibt:

resolve: function(specifier) {
  var base = typeof location !== "undefined" ? location.href : this.url;
  return new URL(specifier, base).href;
}

Aufwand: 1h

Option C — Fetch mit Origin-Erkennung

In ___dynamicImportLoader prüfen, ob die URL absolut ist (= startet mit http:// oder https://). Falls nicht, gegen den letzten bekannten Origin auflösen.

Aufwand: 2h


Sub-Issue 3: HTML-served-as-JS (Cloudflare/Vercel Challenge)

Analyse

Seiten hinter Cloudflare (solidjs.com, web.dev) oder Vercel (kit.svelte.dev) liefern HTML-Inhalt (Challenge-Pages) auf JS-Endpunkte. Unser ScriptLoader.fetchContent() erkennt das:

// script-loader.ts
if (trimmed.startsWith("<!") || trimmed.startsWith("<html") || trimmed.startsWith("<head")) {
  throw new Error(`[ScriptLoader] HTML content returned: ${url}`);
}

Der Fehler Empty chunk kommt aus dynamic-scripts.ts Zeile 709, wo leerer Content als Fehler behandelt wird. Das ist eigentlich kein echter Bug — die Seite kann nicht anders (Challenge). ABER: der Empty chunk-Error erzeugt unhandled Promise-Rejections (Fix in Sprint 24b teilweise angewandt).

Option A — Challenge-Erkennung + Graceful Degradation

Challenges erkennen und als „blocked by Cloudflare" markieren statt als „Empty chunk". Im Corpus-Report ein eigenes Status-Flag.

Aufwand: 2h

Option B — Challenge automatisch lösen

Camoufox oder Playwright-Integration für Challenge-Seiten → löst Cloudflare/Vercel Challenges. ABER: das ist ein ganzes Feature (separates Issue).

Aufwand: 20h+


Sub-Issue 4: replaceStaticImports Regex — false positives

Analyse

Der Error auf vuejs.org:

SyntaxError: Unexpected token '{'. import call expects one or two arguments.

Ursache: replaceDynamicImports() transformiert import({...}) zu ___dynamicImport({...}). Das originale import( in manchen Scripts wird fälschlich als dynamischer import erkannt.

Der Regex /import\s*\(/g matcht JEDES import( — auch in Strings, Comments, und Objekt-Literalen.

Der Regex in replaceStaticImports matcht import\s*(?:\{.... Das kann block-scoped imports treffen, die nicht statische imports sind (z.B. dynamischer import() mit destructuring).

Option A — AST-basierte Transformation (empfohlen)

Statt Regex einen echten JavaScript-Parser für die Transformation verwenden. Leichte Option: @babel/parser oder acorn mit einfachem Walk.

Aufwand: 4h

Option B — Regex verfeinern

replaceDynamicImports auf Zeilen beschränken, die nicht mit { nach import( beginnen:

replaceDynamicImports: nur ersetzen wenn nicht import({...})

Aufwand: 1h — Workaround, kein echter Fix

Option C — Import-Statement-Klasse bauen

Eigene kleine Parser-Klasse für statische imports (kein AST, aber Token-basiert):

class ImportParser {
  parse(code: string): { static: ImportStatement[], dynamic: ImportExpression[], other: string[] }
}

Aufwand: 6h


Sub-Issue 5: Proof-Runner — incrementelle Speicherung + Fehlertoleranz

Analyse

Der Proof-Runner (proof-runner.ts) schreibt proof-results.json erst NACH allen 19 Sites. Wenn eine Site crasht (z.B. Sub-Issue 2), sind ALLE vorherigen Ergebnisse verloren.

Option A — Incremental Write + Catch per Site (empfohlen)

for (const proof of proofs) {
  try {
    const sr = await runProofs(proof);
    results.push(sr);
    // Nach jeder Site incrementell speichern
    fs.writeFileSync(jsonPath, JSON.stringify(results, null, 2));
  } catch (err) {
    // Fehler als "crashed" eintragen, nicht breaken
    results.push({
      name: proof.name, url: proof.url, ok: false,
      errors: [String(err)], queries: [],
    });
  }
}

Aufwand: 1h

Option B — Timeout per Site

const timeout = new Promise((_, reject) => 
  setTimeout(() => reject(new Error("Timeout")), 30000)
);
await Promise.race([runProofs(proof), timeout]);

Aufwand: 0.5h


Akzeptanzkriterien

Phase 1: Proof-Runner Resilienz

  • Proof-Runner speichert Ergebnisse incrementell nach jeder Site
  • Timeout pro Site (30s) verhindert Hänger
  • Ein Site-Crash killt nicht die restlichen Sites
  • Fehler werden als crashed erfasst, nicht als Abbruch

Phase 2: ___dynamicImportLoader Fix

  • Relative URLs (startend mit /) werden gegen document.baseURI aufgelöst
  • Qwik.dev lädt ohne Cannot find module-Crash
  • Discord.com/app lädt ohne Crash
  • CodePen.io lädt ohne Crash

Phase 3: youtube + speedtest Debug

  • Root-Cause identifiziert (DNS, Transport, Redirect)
  • Fix implementiert (TransportManager oder Adapter)

Phase 4: replaceStaticImports Regex

  • Kein SyntaxError: import call expects one or two arguments mehr auf vuejs.org
  • Alle bestehenden Proof-Queries bleiben grün

Phase 5: HTML-served-as-JS

  • Cloudflare-Challenge erzeugt keinen Empty chunk Error mehr
  • Challenge-Seiten werden im Report als solche markiert

Betroffene Dateien

Datei Änderung Sub-Issue
proof-runner.ts Incremental writes + Timeout per site #5
src/js/execution-realm.ts ___dynamicImportLoader: relative URL fix #2
src/js/module-transformer.ts replaceStaticImports Regex-Verfeinerung #4
src/js/module-transformer.ts replaceDynamicImports: false positive vermeiden #4
src/js/dynamic-scripts.ts Cloudflare-Challenge graceful handling #3
src/pages/page.ts oder transport-manager.ts youtube+speedtest Debug #1
proof-queries.ts Erweiterte Queries für neu fixbare Sites #5

Dependencies

graph LR
    subgraph "Phase 1"
        P1["Proof-Runner Resilienz"]
    end
    subgraph "Phase 2"
        P2["___dynamicImportLoader Fix"]
    end
    subgraph "Phase 3"
        P3["youtube+speedtest Debug"]
    end
    subgraph "Phase 4"
        P4["replaceStaticImports Regex"]
    end
    subgraph "Phase 5"
        P5["HTML-served-as-JS"]
    end

    P1 --> P2
    P1 --> P3
    P1 --> P4
    P1 --> P5
    P2 --> P4

Technische Risiken

Risiko Impact Wahrscheinlichkeit Mitigation
youtube verwendet HTTP/3 Quic, das Bun nicht unterstützt Hoch Mittel HTTP/2 fallback erzwingen
___importMeta.resolve hat keinen document-Kontext Mittel Niedrig Fallback auf location.href
Cloudflare-Challenge erkennt Headless-Browser Niedrig Gering Challenge-Erkennung reicht für Reporting
Regex-Fix erwischt nicht alle false-positives Mittel Mittel Test-Corpus erweitern

Performance-Impact

Sub-Issue Impact Begründung
#2 ___dynamicImportLoader Keiner Ein zusätzlicher if-Check
#4 Regex Keiner Kein Code-Pfad für normale Scripts
#5 Proof-Runner Nur im Testmodus Produktions-Code nicht betroffen

Testplan

Phase 1: Proof-Runner (2 unit tests)

  1. Test: Incremental write erzeugt gültiges JSON nach jedem Site-Load
  2. Test: Timeout wird korrekt als crashed erfasst

Phase 2: ___dynamicImportLoader (3 integration tests)

  1. Test: Relative URL /build/q-123.js wird aufgelöst
  2. Test: qwik.dev lädt ohne unhandled rejection
  3. Test: discord.com/app lädt ohne unhandled rejection

Phase 3: youtube+speedtest (2 tests)

  1. Test: youtube.com erzeugt >0 Network-Requests
  2. Test: speedtest.net erzeugt >0 Network-Requests

Phase 4: Regex (2 tests)

  1. Test: import({...}) wird NICHT von replaceDynamicImports getroffen
  2. Test: VueJS app.js ohne SyntaxError

Phase 5: HTML-served-as-JS (1 test)

  1. Test: Cloudflare-Challenge erzeugt keinen unhandled rejection

Zeitplan

Phase Aufwand Priority
1: Proof-Runner Resilienz 1h 🔴 Höchste (Basis für restliche Tests)
2: ___dynamicImportLoader 2h 🔴 Höchste
3: youtube+speedtest 2h 🟡 Mittel
4: Regex 2h 🟢 Niedrig (non-fatal)
5: HTML-served-as-JS 2h 🟢 Niedrig (kosmetisch)

Empfohlene Reihenfolge

  1. Proof-Runner Resilienz (Phase 1) — damit wir die Ergebnisse der anderen Fixes validieren können
  2. ___dynamicImportLoader (Phase 2) — blocker für 5+ Sites
  3. youtube+speedtest Debug (Phase 3) — root cause der 2 Failures
  4. Regex (Phase 4) — non-fatal aber Proof-Runner sichtbar
  5. HTML-served-as-JS (Phase 5) — kosmetisch, niedrige Priorität
# Sprint 25: 19-Sites-Korpus — verbleibende Blockbuster beheben ## Problembeschreibung Der Corpus-Runner zeigt **17/19 ✅**, aber das ist eine oberflächliche Metrik ("kein JS-Crash"). Der Proof-Runner (echte DOM-Validierung) zeigt, dass viele Sites nur teilweise oder scheinbar korrekt laden: - **youtube.com + speedtest.net**: 0 HTTP-Requests, kein DOM → kompletter Netzwerk-Fail - **qwik.dev, solidjs.com, angular.io, codepen.io**: `___dynamicImportLoader` crasht bei relativen URLs ohne Origin (/build/q-*.js) - **Cloudflare-Challenge-Seiten**: HTML statt JS geliefert → "Empty chunk" Errors (kosmetisch, aber unschön) - **replaceStaticImports**: SyntaxError auf vuejs.org (`import({...})` → `___dynamicImport({...})`) - **Proof-Runner**: Kein incrementelles Speichern, crasht bei Fehlern → verliert alle bisherigen Ergebnisse **Ziel**: Alle 19 Sites vollständig und korrekt laden, gemessen an Proof-Queries im LIVE DOM (nicht nur "kein Crash"). --- ## Analyse: Aktueller Datenstand ### Corpus-Runner (17/19 ✅ — oberflächlich) ``` 1 vuejs.org ✅ 1175ms 82KB 13 1 2 kit.svelte.dev ✅ 445ms 87KB 2 0 ← Vercel Challenge (DOM=Challenge, nicht SvelteKit) 3 solidjs.com ✅ 1045ms 3KB 6 1 ← Cloudflare Challenge (3KB = Challenge-Page) 4 qwik.dev ✅ 965ms 206KB 11 0 ← DOM existiert, aber es gibt JS-Fehler ... 15 youtube.com ❌ 441ms 0B 0 0 ← 0 Requests, 0 DOM 19 speedtest.net ❌ 779ms 0B 0 0 ← 0 Requests, 0 DOM ``` ### Proof-Runner (echte DOM-Queries) Die Ergebnisse aus proof-runner.ts zeigen, dass NUR VueJS alle Proof-Queries besteht. Die restlichen Sites scheitern an unterschiedlichen Barrieren. --- ## Mermaid — Dependency Graph der Blockierer ```mermaid flowchart TD A["Sprint 25 Epic"] --> B["youtube + speedtest: 0 Requests"] A --> C["___dynamicImportLoader crash"] A --> D["HTML-served-as-JS"] A --> E["replaceStaticImports Regex"] A --> F["Proof-Runner Resilienz"] C --> C1["Root: ___importMeta.resolve() liefert /build/... ohne Origin"] C --> C2["fetch() in ___dynamicImportLoader scheitert"] C --> C3["Nur bei Qwik/angular/discord mit dynamischen Chunks"] D --> D1["Cloudflare/Vercel liefern HTML auf JS-URLs"] D --> D2["ScriptLoader erkennt '<!--' am Start → Skipt"] D --> D3["Chunk-Fehler werden nicht propagiert, aber loggen Noise"] E --> E1["Root: import(...) Regex matcht auch import({...})"] E --> E2["replaceDynamicImports transformiert zu ___dynamicImport({...})"] E --> E3["SyntaxError in eval — non-fatal aber in Proof sichtbar"] F --> F1["Keine incrementelle Speicherung"] F --> F2["Ein Site-Crash killt alle bisherigen Ergebnisse"] F --> F3["Kein Timeout pro Site"] ``` --- ## Sub-Issue 1: youtube.com + speedtest.net — 0 Requests ### Root-Cause-Analyse Beide Sites haben exakt 0 Network-Requests und 0 DOM-Größe („-" im Corpus-Report). Das deutet darauf hin, dass die **DNS-Auflösung oder der Verbindungsaufbau** komplett scheitert. Mögliche Ursachen: 1. TransportManager blockiert die Domains (Curl/CURL_IMPERSONATE-Transport) 2. HTTP/2 oder HTTP/3 wird nicht unterstützt 3. YouTube sendet sofort einen Redirect (HTTP 3xx), den unser TransportManager nicht korrekt verarbeitet 4. DNS-Resolve scheitert für bestimmte TLDs oder CDN-Endpunkte ### Option A — Network-Debug-Session (empfohlen) Ein dediziertes Debug-Script, das jede Seite Schritt für Schritt durch den Network-Stack verfolgt: 1. DNS-Resolution prüfen: `bun -e 'console.log(await Bun.resolve("youtube.com"))'` 2. TransportManager-Konfiguration loggen 3. curl-Transport-Toggle testen (`useCurlTransport: true/false`) 4. Raw-HTTP-Request via `fetch()` außerhalb des Browsers 5. Ergebnis: Genauer Fail-Point **Aufwand**: 2h — reine Analyse ### Option B — YouTube-spezifischen Interceptor Vorhandene YouTube-Redirect-Muster erkennen und bypassen. YouTube verwendet oft: - `https://www.youtube.com` → redirect zu `https://www.youtube.com/?app=desktop` - Service Worker Registration - YT-Initial-Data im HTML **Aufwand**: 4h — schwer wartbar ### Option C — Minimal: Einen bekannten Workaround finden Andere Headless-Browser (Playwright, Puppeteer) laden YouTube → deren Network-Log als Referenz nutzen. **Aufwand**: 1h --- ## Sub-Issue 2: ___dynamicImportLoader crasht bei relativen URLs ohne Origin ### Root-Cause-Analyse In `execution-realm.ts` Zeile ~586ff wird `___dynamicImportLoader` definiert: ```typescript win.___dynamicImportLoader = async (moduleUrl: string) => { const response = await fetch(moduleUrl); // <-- crash hier // ... } ``` Wenn `___importMeta.resolve()` einen relativen Specifier wie `"./chunks/q-993Mv2pg.js"` auflöst, erzeugt `new URL(specifier, "https://qwik.dev/assets/main.js")` → `https://qwik.dev/build/q-993Mv2pg.js`. ABER: Wenn die Module-URL selbst aus einem anderen Kontext kommt (z.B. Inline-Script, oder der `url`-Parameter ist nicht korrekt gesetzt), wird `new URL(specifier, baseUrl)` fehlschlagen und `/build/q-993Mv2pg.js` (nur Pfad, kein Origin) liefern. **Fundamental**: `___importMeta.resolve()` setzt eine korrekte Basis-URL voraus. Wenn das Modul `import.meta.url = "https://qwik.dev/assets/main.js"` hat, sollte die resolution korrekt sein. Das Problem ist, dass das Modul evtl. ohne URL ausgeführt wird → `import.meta.resolve` fällt auf die aktuelle Document-Base-URL zurück, die nicht gesetzt ist. ### Option A — fallback auf document.baseURI (empfohlen) ```typescript var ___dynamicImport = async function(specifier) { var res = ___importMeta.resolve(specifier); // Fallback: resolve gegen document.baseURI if (res.startsWith("/")) { var base = typeof document !== "undefined" ? document.baseURI : ""; res = new URL(res, base).href; } // ... }; ``` **Aufwand**: 1h — minimaler Fix ### Option B — ___importMeta.resolve defensiv machen `buildImportMetaInjection` muss sicherstellen, dass `import.meta.resolve()` immer einen absolute URL zurückgibt: ```typescript resolve: function(specifier) { var base = typeof location !== "undefined" ? location.href : this.url; return new URL(specifier, base).href; } ``` **Aufwand**: 1h ### Option C — Fetch mit Origin-Erkennung In `___dynamicImportLoader` prüfen, ob die URL absolut ist (= startet mit `http://` oder `https://`). Falls nicht, gegen den letzten bekannten Origin auflösen. **Aufwand**: 2h --- ## Sub-Issue 3: HTML-served-as-JS (Cloudflare/Vercel Challenge) ### Analyse Seiten hinter Cloudflare (solidjs.com, web.dev) oder Vercel (kit.svelte.dev) liefern HTML-Inhalt (Challenge-Pages) auf JS-Endpunkte. Unser `ScriptLoader.fetchContent()` erkennt das: ```typescript // script-loader.ts if (trimmed.startsWith("<!") || trimmed.startsWith("<html") || trimmed.startsWith("<head")) { throw new Error(`[ScriptLoader] HTML content returned: ${url}`); } ``` Der Fehler `Empty chunk` kommt aus `dynamic-scripts.ts` Zeile 709, wo **leerer Content** als Fehler behandelt wird. Das ist eigentlich kein echter Bug — die Seite kann nicht anders (Challenge). ABER: der `Empty chunk`-Error erzeugt unhandled Promise-Rejections (Fix in Sprint 24b teilweise angewandt). ### Option A — Challenge-Erkennung + Graceful Degradation Challenges erkennen und als „blocked by Cloudflare" markieren statt als „Empty chunk". Im Corpus-Report ein eigenes Status-Flag. **Aufwand**: 2h ### Option B — Challenge automatisch lösen Camoufox oder Playwright-Integration für Challenge-Seiten → löst Cloudflare/Vercel Challenges. ABER: das ist ein ganzes Feature (separates Issue). **Aufwand**: 20h+ --- ## Sub-Issue 4: replaceStaticImports Regex — false positives ### Analyse Der Error auf vuejs.org: ``` SyntaxError: Unexpected token '{'. import call expects one or two arguments. ``` Ursache: `replaceDynamicImports()` transformiert `import({...})` zu `___dynamicImport({...})`. Das originale `import(` in manchen Scripts wird fälschlich als dynamischer import erkannt. Der Regex `/import\s*\(/g` matcht JEDES `import(` — auch in Strings, Comments, und Objekt-Literalen. Der Regex in `replaceStaticImports` matcht `import\s*(?:\{...`. Das kann **block-scoped imports** treffen, die nicht statische imports sind (z.B. dynamischer `import()` mit destructuring). ### Option A — AST-basierte Transformation (empfohlen) Statt Regex einen echten JavaScript-Parser für die Transformation verwenden. Leichte Option: `@babel/parser` oder `acorn` mit einfachem Walk. **Aufwand**: 4h ### Option B — Regex verfeinern `replaceDynamicImports` auf Zeilen beschränken, die nicht mit `{` nach `import(` beginnen: ``` replaceDynamicImports: nur ersetzen wenn nicht import({...}) ``` **Aufwand**: 1h — Workaround, kein echter Fix ### Option C — Import-Statement-Klasse bauen Eigene kleine Parser-Klasse für statische imports (kein AST, aber Token-basiert): ```typescript class ImportParser { parse(code: string): { static: ImportStatement[], dynamic: ImportExpression[], other: string[] } } ``` **Aufwand**: 6h --- ## Sub-Issue 5: Proof-Runner — incrementelle Speicherung + Fehlertoleranz ### Analyse Der Proof-Runner (`proof-runner.ts`) schreibt `proof-results.json` erst NACH allen 19 Sites. Wenn eine Site crasht (z.B. Sub-Issue 2), sind ALLE vorherigen Ergebnisse verloren. ### Option A — Incremental Write + Catch per Site (empfohlen) ```typescript for (const proof of proofs) { try { const sr = await runProofs(proof); results.push(sr); // Nach jeder Site incrementell speichern fs.writeFileSync(jsonPath, JSON.stringify(results, null, 2)); } catch (err) { // Fehler als "crashed" eintragen, nicht breaken results.push({ name: proof.name, url: proof.url, ok: false, errors: [String(err)], queries: [], }); } } ``` **Aufwand**: 1h ### Option B — Timeout per Site ```typescript const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error("Timeout")), 30000) ); await Promise.race([runProofs(proof), timeout]); ``` **Aufwand**: 0.5h --- ## Akzeptanzkriterien ### Phase 1: Proof-Runner Resilienz - [ ] Proof-Runner speichert Ergebnisse incrementell nach jeder Site - [ ] Timeout pro Site (30s) verhindert Hänger - [ ] Ein Site-Crash killt nicht die restlichen Sites - [ ] Fehler werden als `crashed` erfasst, nicht als Abbruch ### Phase 2: ___dynamicImportLoader Fix - [ ] Relative URLs (startend mit `/`) werden gegen `document.baseURI` aufgelöst - [ ] Qwik.dev lädt ohne `Cannot find module`-Crash - [ ] Discord.com/app lädt ohne Crash - [ ] CodePen.io lädt ohne Crash ### Phase 3: youtube + speedtest Debug - [ ] Root-Cause identifiziert (DNS, Transport, Redirect) - [ ] Fix implementiert (TransportManager oder Adapter) ### Phase 4: replaceStaticImports Regex - [ ] Kein `SyntaxError: import call expects one or two arguments` mehr auf vuejs.org - [ ] Alle bestehenden Proof-Queries bleiben grün ### Phase 5: HTML-served-as-JS - [ ] Cloudflare-Challenge erzeugt keinen `Empty chunk` Error mehr - [ ] Challenge-Seiten werden im Report als solche markiert --- ## Betroffene Dateien | Datei | Änderung | Sub-Issue | |-------|----------|-----------| | `proof-runner.ts` | Incremental writes + Timeout per site | #5 | | `src/js/execution-realm.ts` | ___dynamicImportLoader: relative URL fix | #2 | | `src/js/module-transformer.ts` | replaceStaticImports Regex-Verfeinerung | #4 | | `src/js/module-transformer.ts` | replaceDynamicImports: false positive vermeiden | #4 | | `src/js/dynamic-scripts.ts` | Cloudflare-Challenge graceful handling | #3 | | `src/pages/page.ts` oder `transport-manager.ts` | youtube+speedtest Debug | #1 | | `proof-queries.ts` | Erweiterte Queries für neu fixbare Sites | #5 | --- ## Dependencies ```mermaid graph LR subgraph "Phase 1" P1["Proof-Runner Resilienz"] end subgraph "Phase 2" P2["___dynamicImportLoader Fix"] end subgraph "Phase 3" P3["youtube+speedtest Debug"] end subgraph "Phase 4" P4["replaceStaticImports Regex"] end subgraph "Phase 5" P5["HTML-served-as-JS"] end P1 --> P2 P1 --> P3 P1 --> P4 P1 --> P5 P2 --> P4 ``` ## Technische Risiken | Risiko | Impact | Wahrscheinlichkeit | Mitigation | |--------|--------|--------------------|------------| | youtube verwendet HTTP/3 Quic, das Bun nicht unterstützt | Hoch | Mittel | HTTP/2 fallback erzwingen | | ___importMeta.resolve hat keinen document-Kontext | Mittel | Niedrig | Fallback auf location.href | | Cloudflare-Challenge erkennt Headless-Browser | Niedrig | Gering | Challenge-Erkennung reicht für Reporting | | Regex-Fix erwischt nicht alle false-positives | Mittel | Mittel | Test-Corpus erweitern | ## Performance-Impact | Sub-Issue | Impact | Begründung | |-----------|--------|------------| | #2 ___dynamicImportLoader | ✅ Keiner | Ein zusätzlicher `if`-Check | | #4 Regex | ✅ Keiner | Kein Code-Pfad für normale Scripts | | #5 Proof-Runner | ✅ Nur im Testmodus | Produktions-Code nicht betroffen | ## Testplan ### Phase 1: Proof-Runner (2 unit tests) 1. Test: Incremental write erzeugt gültiges JSON nach jedem Site-Load 2. Test: Timeout wird korrekt als `crashed` erfasst ### Phase 2: ___dynamicImportLoader (3 integration tests) 3. Test: Relative URL `/build/q-123.js` wird aufgelöst 4. Test: qwik.dev lädt ohne unhandled rejection 5. Test: discord.com/app lädt ohne unhandled rejection ### Phase 3: youtube+speedtest (2 tests) 6. Test: youtube.com erzeugt >0 Network-Requests 7. Test: speedtest.net erzeugt >0 Network-Requests ### Phase 4: Regex (2 tests) 8. Test: `import({...})` wird NICHT von replaceDynamicImports getroffen 9. Test: VueJS app.js ohne SyntaxError ### Phase 5: HTML-served-as-JS (1 test) 10. Test: Cloudflare-Challenge erzeugt keinen unhandled rejection ## Zeitplan | Phase | Aufwand | Priority | |-------|---------|----------| | 1: Proof-Runner Resilienz | 1h | 🔴 Höchste (Basis für restliche Tests) | | 2: ___dynamicImportLoader | 2h | 🔴 Höchste | | 3: youtube+speedtest | 2h | 🟡 Mittel | | 4: Regex | 2h | 🟢 Niedrig (non-fatal) | | 5: HTML-served-as-JS | 2h | 🟢 Niedrig (kosmetisch) | ## Empfohlene Reihenfolge 1. **Proof-Runner Resilienz** (Phase 1) — damit wir die Ergebnisse der anderen Fixes validieren können 2. **___dynamicImportLoader** (Phase 2) — blocker für 5+ Sites 3. **youtube+speedtest Debug** (Phase 3) — root cause der 2 Failures 4. **Regex** (Phase 4) — non-fatal aber Proof-Runner sichtbar 5. **HTML-served-as-JS** (Phase 5) — kosmetisch, niedrige Priorität
Artur closed this issue 2026-06-20 05:52:47 +00:00
Author
Owner

Sprint 25 Fixes — 4 Issues gelöst (Commit aaabcbc3)

Gelöste Issues

#5: Proof-Runner Resilienz 🔴

  • Incremental Saving: Results werden nach JEDER Seite in corpus/partial-results.json gespeichert
  • Auto-Resume: Beim Neustart werden vorhandene Partial-Results erkannt und übersprungen (--no-resume um zu deaktivieren)
  • Per-Page Timeout: Promise.race zwischen page-load und Timeout (customTimeout + 10s Grace)
  • Cleanup: Partial-File wird bei erfolgreichem Durchlauf geleert

#2: ___dynamicImportLoader crash bei /build/q-*.js ohne Origin 🔴

  • buildImportMetaInjection() erkennt relative URLs (kein http:///https://)
  • Generiert Runtime-Code, der window.location.origin + relative URL verwendet
  • new URL(specifier, base) erhält damit immer einen validen absoluten Base-URL

#4: replaceStaticImports Regex false positives 🟢

  • replaceDynamicImports() Regex um (?!{)) ergänzt (Negative Lookahead)
  • Verhindert false positives bei import({...}), was nie ein gültiger dynamic import ist

#3: HTML-served-as-JS (Cloudflare Noise) 🟢

  • fetchContent() in ScriptLoader nutzt gleiches htmlPattern wie executeRaw()
  • Erkennt echte HTML-Dokumente (<!DOCTYPE, <html>, <head>, <body>, <!-- etc.)
  • JSX/Template-Literals/Comparisons (x < 5) werden korrekt durchgelassen

#1: youtube.com + speedtest.net — 0 Requests 🟡

  • youtube.com: Funktioniert aktuell (Discovery-Run zeigt PASS mit DOM + Requests)
  • speedtest.net: setAttribute('', 'value') InvalidCharacterError — DOM-API-Level Issue, kein "0 Requests"-Problem
✅ **Sprint 25 Fixes — 4 Issues gelöst** (Commit `aaabcbc3`) ## Gelöste Issues ### #5: Proof-Runner Resilienz 🔴 - **Incremental Saving**: Results werden nach JEDER Seite in `corpus/partial-results.json` gespeichert - **Auto-Resume**: Beim Neustart werden vorhandene Partial-Results erkannt und übersprungen (`--no-resume` um zu deaktivieren) - **Per-Page Timeout**: `Promise.race` zwischen page-load und Timeout (customTimeout + 10s Grace) - **Cleanup**: Partial-File wird bei erfolgreichem Durchlauf geleert ### #2: ___dynamicImportLoader crash bei /build/q-*.js ohne Origin 🔴 - **`buildImportMetaInjection()`** erkennt relative URLs (kein `http://`/`https://`) - Generiert Runtime-Code, der `window.location.origin` + relative URL verwendet - `new URL(specifier, base)` erhält damit immer einen validen absoluten Base-URL ### #4: replaceStaticImports Regex false positives 🟢 - `replaceDynamicImports()` Regex um `(?!{)`) ergänzt (Negative Lookahead) - Verhindert false positives bei `import({...})`, was nie ein gültiger dynamic import ist ### #3: HTML-served-as-JS (Cloudflare Noise) 🟢 - `fetchContent()` in ScriptLoader nutzt gleiches `htmlPattern` wie `executeRaw()` - Erkennt echte HTML-Dokumente (`<!DOCTYPE`, `<html>`, `<head>`, `<body>`, `<!--` etc.) - JSX/Template-Literals/Comparisons (`x < 5`) werden korrekt durchgelassen ### #1: youtube.com + speedtest.net — 0 Requests 🟡 - **youtube.com**: ✅ Funktioniert aktuell (Discovery-Run zeigt PASS mit DOM + Requests) - **speedtest.net**: ❌ `setAttribute('', 'value')` InvalidCharacterError — DOM-API-Level Issue, kein "0 Requests"-Problem
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
glow-all/true-headless-browser#117
No description provided.