diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 524bd99..4187ccf 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -145,3 +145,135 @@ jobs:
env:
DATABASE_URL: postgres://partizip:partizip@localhost:5433/partizip_upgrade_test
run: npm run test:upgrade
+
+ # Eigenständiger Job (Issue #60): prüft das GERENDERTE DOM von vier
+ # unangemeldet erreichbaren Seiten mit axe — Kontraste, Landmarks,
+ # Formular-Labels, Heading-Reihenfolge. Das ergänzt das statische
+ # jsx-a11y-Lint-Gate im ci-Job, das genau diese Klasse von Verstößen
+ # prinzipbedingt nicht sehen kann (sie entsteht erst beim Rendern).
+ #
+ # REICHWEITE — bitte nicht überdehnen: geprüft werden ausschließlich die
+ # anonymen Sichten /, /umfragen, /anliegen und /anmelden. Die eigentlichen
+ # Beteiligungsflüsse (/umfrage/[id], /mitmachen, /konto, /verifizieren,
+ # Admin) sind NICHT abgedeckt, ebenso wenig Tastaturbedienung, Fokusführung
+ # und alles, was erst nach einer Interaktion entsteht. Gegenüber Kommunen
+ # trägt dieser Job also genau diese vier Seiten und die von axe automatisiert
+ # entscheidbaren Regeln — keine Aussage über die Anwendung als Ganzes.
+ # Ausweitung auf die Kernflüsse ist ein eigener Schritt.
+ #
+ # Getrennter Job + eigene Service-DB, dem Muster von upgrade-path folgend →
+ # bricht die bestehenden Jobs nicht und läuft parallel.
+ #
+ # ZENTRAL: der Server MUSS mit PILOT_TENANT_SLUG=taunusstein starten. Die
+ # Tenant-Auflösung ist host-basiert (app/src/middleware.ts); localhost ist eine
+ # HAUPT-Domain und liefert ohne diese Variable die neutrale Landing-Page statt
+ # der Tenant-App. Ein a11y-Lauf dagegen wäre grün und wertlos. Der Schritt
+ # "a11y-Lauf" ruft deshalb scripts/a11y-preflight.ts VOR pa11y auf: HTTP-Poll
+ # als Readiness (kein sleep), 200-Prüfung aller vier Seiten, eine harte
+ # Assertion, dass /umfragen den Wortlaut einer geseedeten Umfrage aus
+ # db/seeds/polls.json enthält, und eine ebenso harte Assertion, dass die
+ # Stylesheets der Seiten wirklich ausgeliefert werden (ohne CSS findet axe
+ # keine visuellen Verstöße mehr — der Lauf wäre erneut grün und wertlos).
+ # Schlägt eine davon fehl, bricht der Job ab.
+ #
+ # `npm run a11y` startet danach ZWEI pa11y-ci-Läufe mit unterschiedlichen
+ # Configs. Warum zwei nötig sind — ein einzelner Lauf kann die unentscheidbaren
+ # Kontrastfälle des Verlaufs-Heros nicht dämpfen, ohne gleichzeitig belegte
+ # Verstöße wie duplicate-id-aria stumm zu schalten — steht ausführlich im
+ # Kopfkommentar von app/.pa11yci.js.
+ a11y:
+ name: a11y (axe) — Kernseiten
+ runs-on: ubuntu-latest
+
+ services:
+ postgres:
+ image: postgres:16-alpine
+ env:
+ POSTGRES_USER: partizip
+ POSTGRES_PASSWORD: partizip
+ POSTGRES_DB: partizip_a11y_test
+ ports:
+ - 5433:5432
+ options: >-
+ --health-cmd "pg_isready -U partizip -d partizip_a11y_test"
+ --health-interval 5s
+ --health-timeout 5s
+ --health-retries 10
+
+ env:
+ DATABASE_URL: postgres://partizip:partizip@localhost:5433/partizip_a11y_test
+ PILOT_TENANT_SLUG: taunusstein
+ # A11Y_BASE_URL ist die EINE Quelle: app/.pa11yci.js baut daraus die
+ # pa11y-Ziele, scripts/a11y-preflight.ts importiert dieselbe Konstante.
+ # PORT muss dazu passen — der Standalone-Server liest ihn.
+ PORT: "3000"
+ HOSTNAME: 127.0.0.1
+ A11Y_BASE_URL: http://127.0.0.1:3000
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v5
+
+ - name: Setup Node 22
+ uses: actions/setup-node@v5
+ with:
+ node-version: "22"
+ cache: "npm"
+ cache-dependency-path: app/package-lock.json
+
+ # Lädt zugleich das Chromium, das pa11y über Puppeteer startet.
+ - name: Install dependencies
+ working-directory: app
+ run: npm ci
+
+ # Die Seeds sind die Datengrundlage der geprüften Seiten UND der
+ # Tenant-Assertion (db/seeds/polls.json).
+ - name: Migrate + Seed
+ working-directory: app
+ run: |
+ npm run db:migrate
+ npm run db:seed
+
+ - name: Build
+ working-directory: app
+ run: npm run build
+
+ # `npm run start:standalone` statt `npm run start`: app/next.config.ts setzt
+ # `output: "standalone"`, und Next 16 warnt ausdrücklich, dass `next start`
+ # damit nicht unterstützt ist. Produktion fährt .next/standalone/server.js
+ # (siehe Dockerfile, Stage `runner`) — der a11y-Job prüft damit denselben
+ # Server wie die Kommunen ihn sehen. Das Skript kopiert vorher .next/static
+ # und public neben server.js, genau wie das Dockerfile.
+ #
+ # Der Server läuft in einer EIGENEN Prozessgruppe (setsid) und meldet deren
+ # PGID selbst. So beendet der Cleanup die ganze Gruppe (npm-Wrapper +
+ # node-Server) statt nur des npm-Prozesses; ein `$!` des aufrufenden Shells
+ # wäre je nach Job-Control nicht zuverlässig die PGID.
+ # Auf Readiness wird hier NICHT gewartet — das macht der Preflight per
+ # HTTP-Poll im nächsten Schritt (kein sleep als Synchronisation).
+ - name: Server starten (Pilot-Tenant, Standalone wie in Produktion)
+ working-directory: app
+ run: |
+ setsid bash -c 'echo $$ > /tmp/a11y-server.pgid; exec npm run start:standalone' \
+ > /tmp/a11y-server.log 2>&1 < /dev/null &
+ for _ in $(seq 1 100); do
+ [ -s /tmp/a11y-server.pgid ] && break
+ sleep 0.1
+ done
+ [ -s /tmp/a11y-server.pgid ] || { echo "Server-PGID wurde nicht geschrieben"; exit 1; }
+ echo "Server-Prozessgruppe: $(cat /tmp/a11y-server.pgid)"
+
+ - name: a11y-Lauf (Preflight + beide pa11y-ci-Läufe)
+ working-directory: app
+ run: npm run a11y
+
+ - name: Server-Log (nur bei Fehlschlag)
+ if: failure()
+ run: tail -n 100 /tmp/a11y-server.log || true
+
+ - name: Server beenden
+ if: always()
+ run: |
+ if [ -f /tmp/a11y-server.pgid ]; then
+ kill -TERM -"$(cat /tmp/a11y-server.pgid)" 2>/dev/null || true
+ fi
diff --git a/app/.pa11yci.js b/app/.pa11yci.js
new file mode 100644
index 0000000..9cbf34a
--- /dev/null
+++ b/app/.pa11yci.js
@@ -0,0 +1,141 @@
+/**
+ * .pa11yci.js — LAUF 1 des a11y-Gates (Issue #60).
+ *
+ * Warum eine .js- und keine .json-Datei: die Basis-URL und die Liste der
+ * geprüften Seiten sind sonst zweimal im Repo hinterlegt (hier und in
+ * scripts/a11y-preflight.ts). Driftet eine der beiden, prüft der Preflight
+ * einen anderen Server als pa11y — das Gate wäre still grün. pa11y-ci lädt
+ * `.cjs`/`.js`-Configs über `loadConfigModule` (bin/pa11y-ci.js), deshalb sind
+ * BASIS_URL und PFADE hier definiert und werden von .pa11yci.streng.js sowie
+ * von scripts/a11y-preflight.ts importiert. EINE Quelle für beide Läufe und
+ * den Preflight.
+ *
+ * ─── Warum ZWEI Läufe ───────────────────────────────────────────────────────
+ * axe unterscheidet `violations` (belegter Verstoß) und `incomplete` (axe konnte
+ * es nicht entscheiden). pa11y bildet beides auf Meldungen ab und deckelt die
+ * incomplete-Klasse über `levelCapWhenNeedsReview` — aber PAUSCHAL, nie
+ * regelbezogen (pa11y/lib/runners/axe.js, `choosePa11yLevel` prüft nur das Flag
+ * `issueNeedsReview`, nie die Regel-ID). Zusätzlich bildet pa11y die gesamte
+ * axe-Impact-Klasse `moderate` auf `warning` ab, und pa11y zählt Warnungen
+ * per Default nicht (`includeWarnings: false`).
+ *
+ * Ein einziger Lauf kann deshalb nicht gleichzeitig
+ * (a) die unberechenbaren Kontrast-`incomplete`s des Verlaufs-Heros dämpfen und
+ * (b) belegte Befunde wie `duplicate-id-aria` (das axe wegen `reviewOnFail`
+ * ebenfalls nach `incomplete` schreibt) hart blocken.
+ * Genau daran ist die erste Fassung dieses Gates gescheitert: sie meldete 0
+ * Befunde, während 3 belegte Verstöße bestanden.
+ *
+ * Deshalb:
+ * LAUF 1 (diese Datei) — `levelCapWhenNeedsReview: "warning"`, Kontrast-
+ * regel auf ALLEN Seiten aktiv. Blockt belegte
+ * `violations` mit Impact critical/serious,
+ * insbesondere echte `color-contrast`-Verstöße.
+ * LAUF 2 (.pa11yci.streng.js) — KEINE Deckelung, `includeWarnings`/
+ * `includeNotices` an, dafür `color-contrast` auf
+ * der Startseite regel- und seitengenau aus.
+ * Blockt alles Übrige: incomplete-Befunde jeder
+ * Impact-Klasse und moderate/minor-Verstöße.
+ * Beide zusammen laufen als `npm run a11y`; jeder Lauf beendet die Kette bei
+ * Exit != 0.
+ *
+ * ─── Tenant-Vorbedingung ────────────────────────────────────────────────────
+ * Der Server MUSS mit PILOT_TENANT_SLUG=taunusstein laufen, sonst liefert die
+ * Haupt-Domain die neutrale Landing-Page statt der Tenant-App (src/middleware.ts)
+ * und der Lauf wäre grün und wertlos. `npm run a11y` erzwingt das über
+ * scripts/a11y-preflight.ts (Status-200-Prüfung, Seed-Inhalts-Assertion,
+ * CSS-Assertion).
+ *
+ * ─── Zur Notation `"ignore": []` ────────────────────────────────────────────
+ * Das leere Array liest sich wie ein Opt-out aus globalen Ignores, ist aber
+ * keines: pa11y-ci merged URL-Optionen über lodash `defaultsDeep`, und das
+ * merged Arrays INDEXWEISE statt sie zu ersetzen. Solange `defaults` hier kein
+ * `ignore` setzt (tut es nicht), ist das folgenlos — dokumentiert, damit es
+ * niemanden beißt, der später ein globales `defaults.ignore` ergänzt.
+ */
+
+/** Basis-URL des laufenden Servers. Identisch zu scripts/a11y-preflight.ts. */
+const BASIS_URL = (
+ process.env.A11Y_BASE_URL || "http://127.0.0.1:3000"
+).replace(/\/+$/, "");
+
+/** Die geprüften Kernseiten. Einzige Quelle für beide Läufe und den Preflight. */
+const PFADE = ["/", "/umfragen", "/anliegen", "/anmelden"];
+
+/** Pfad → absolute URL des laufenden Servers. */
+const url = (pfad) => `${BASIS_URL}${pfad}`;
+
+/**
+ * Von beiden Läufen geteilte Grundeinstellungen.
+ *
+ * `concurrency: 1`: pa11y startet je Ziel einen eigenen Inkognito-Browser-
+ * Kontext; parallel dazu kam es reproduzierbar zu `Protocol error
+ * (Target.closeTarget)`-Abbrüchen. Die Richtung wäre zwar sicher (rot statt
+ * grün), aber ein flackerndes Pflicht-Gate wird weggeklickt. Bei vier URLs
+ * bringt Parallelität ohnehin fast nichts.
+ */
+const BASIS_DEFAULTS = {
+ runners: ["axe"],
+ standard: "WCAG2AA",
+ timeout: 60000,
+ wait: 1000,
+ concurrency: 1,
+ chromeLaunchConfig: {
+ args: ["--no-sandbox", "--disable-dev-shm-usage"],
+ },
+};
+
+module.exports = {
+ defaults: {
+ ...BASIS_DEFAULTS,
+
+ // Deckelt die axe-`incomplete`-Klasse auf `warning`; pa11y zählt Warnungen
+ // hier nicht mit (`includeWarnings` bleibt aus). Grund: `.pz-hero` in
+ // globals.css hat einen Verlauf als Hintergrund, und bei Verlaufs-
+ // hintergründen kann axe den Kontrast prinzipiell nicht berechnen — 21
+ // Textknoten der Startseite landen deshalb als `color-contrast`/incomplete.
+ // Wichtig: die Deckelung wirkt pauschal auf ALLE incomplete-Befunde, nicht
+ // nur auf Kontrast. Was sie hier durchlässt, fängt LAUF 2 wieder ein.
+ levelCapWhenNeedsReview: "warning",
+ },
+
+ urls: [
+ {
+ url: url("/"),
+ ignore: [],
+ },
+ {
+ url: url("/umfragen"),
+ ignore: [],
+ },
+ {
+ _kommentar_ignore: [
+ "ALTLAST, eingefroren am 2026-07-26 — abzubauen, nicht auszuweiten.",
+ "Regel: link-in-text-block · Seite: /anliegen · Element:",
+ '#main-content > main > p > a → Konto unter',
+ "„Meine Anliegen“ . Der Link im Fließtext ist nur farblich (--pz-brand-strong)",
+ "vom umgebenden Text unterschieden; 'hover:underline' greift erst beim Hover.",
+ "Grund für die Ausnahme: dieser PR friert den Bestand ein und ändert",
+ "bewusst KEINE UI (Issue #60). Fix gehört in einen eigenen PR (Unterstreichung",
+ "im Ruhezustand oder ausreichender Nicht-Farb-Unterschied); danach diese",
+ "Ausnahme in BEIDEN Configs ersatzlos entfernen.",
+ "Die Ausnahme gilt regel- UND seitengenau: auf allen anderen URLs blockt",
+ "link-in-text-block weiterhin hart.",
+ ],
+ url: url("/anliegen"),
+ ignore: ["link-in-text-block"],
+ },
+ {
+ url: url("/anmelden"),
+ ignore: [],
+ },
+ ],
+};
+
+// Geteilte Konstanten für .pa11yci.streng.js und scripts/a11y-preflight.ts.
+// pa11y-ci liest nur `defaults` und `urls` (bin/pa11y-ci.js, `defaultConfig`),
+// zusätzliche Exporte stören es nicht.
+module.exports.BASIS_URL = BASIS_URL;
+module.exports.PFADE = PFADE;
+module.exports.BASIS_DEFAULTS = BASIS_DEFAULTS;
+module.exports.url = url;
diff --git a/app/.pa11yci.streng.js b/app/.pa11yci.streng.js
new file mode 100644
index 0000000..3a3c3b7
--- /dev/null
+++ b/app/.pa11yci.streng.js
@@ -0,0 +1,122 @@
+/**
+ * .pa11yci.streng.js — LAUF 2 des a11y-Gates (Issue #60).
+ *
+ * Ergänzt LAUF 1 (.pa11yci.js) um genau die Befundklassen, die dort
+ * konstruktionsbedingt stumm bleiben. Die Aufteilung und ihre Begründung
+ * stehen im Kopfkommentar von .pa11yci.js — hier nur, was DIESER Lauf tut:
+ *
+ * 1. `levelCapWhenNeedsReview` wird NICHT gesetzt (pa11y-Default: "error").
+ * Damit behalten axe-`incomplete`-Befunde ihre vom Impact abgeleitete
+ * Stufe. Nötig, weil axe belegte Verstöße wie `duplicate-id-aria` wegen
+ * `reviewOnFail: true` (axe-core) in die incomplete-Klasse schreibt — die
+ * Deckelung in LAUF 1 macht daraus eine Warnung, die niemand zählt.
+ *
+ * 2. `includeWarnings: true` + `includeNotices: true`. pa11y bildet die
+ * gesamte axe-Impact-Klasse `moderate` auf `warning` und `minor` auf
+ * `notice` ab (pa11y/lib/runners/axe.js, `axeImpactToPa11yLevel`) und
+ * zählt beides per Default NICHT (pa11y/lib/option.js schiebt sonst
+ * 'warning'/'notice' in die Ignore-Liste). Ohne diese zwei Schalter wären
+ * z. B. `heading-order` und `form-field-multiple-labels` dauerhaft stumm —
+ * beides reale Befunde auf den geprüften Seiten. pa11y-ci zählt jede
+ * verbliebene Meldung als Fehler (lib/pa11y-ci.js: `results.issues.length`),
+ * also blocken sie hier.
+ *
+ * 3. `color-contrast` ist auf `/` regel- und seitengenau abgeschaltet.
+ * Der Verlaufshintergrund von `.pz-hero` macht die Kontrastberechnung dort
+ * prinzipiell unentscheidbar (21 incomplete-Meldungen). ES ENTSTEHT KEINE
+ * LÜCKE: LAUF 1 lässt `color-contrast` auf allen Seiten aktiv, und ein
+ * belegter Kontrast-*Verstoß* hat Impact `serious` → Stufe `error` → blockt
+ * dort weiterhin hart. Abgeschaltet ist hier nur das Unentscheidbare.
+ *
+ * ─── Reichweite dieses Gates (ehrlich) ──────────────────────────────────────
+ * Beide Läufe zusammen decken auf den vier geprüften Seiten alle axe-Regeln des
+ * Standards WCAG2AA ab, in jeder Impact-Klasse, sowohl `violations` als auch
+ * `incomplete` — mit der einen dokumentierten Ausnahme `color-contrast`/`/` aus
+ * Punkt 3 und den unten eingefrorenen Altlasten. Was das Gate NICHT sieht:
+ * andere Seiten (siehe .pa11yci.js: nur vier anonyme Sichten), alles was
+ * Interaktion braucht (Fokus-Reihenfolge, Tastaturbedienung, Live-Regionen nach
+ * einer Aktion), und alles, was automatisierte Prüfung grundsätzlich nicht
+ * entscheiden kann (Verständlichkeit, sinnvolle Alternativtexte).
+ *
+ * ─── Zur Notation `"ignore": []` ────────────────────────────────────────────
+ * Siehe .pa11yci.js: kein Opt-out aus globalen Ignores, nur Dokumentation.
+ * `defaults` setzt hier bewusst kein `ignore`.
+ */
+
+const { PFADE, BASIS_DEFAULTS, url } = require("./.pa11yci.js");
+
+// Absicherung gegen stilles Auseinanderlaufen der beiden Configs: wenn jemand
+// PFADE in .pa11yci.js ändert, ohne die Ausnahmen hier nachzuziehen, prüft
+// dieser Lauf sonst eine Seite ohne die für sie gedachten Ausnahmen (oder
+// umgekehrt eine Ausnahme ohne Seite). Beides fällt hier sofort auf.
+const AUSNAHMEN = {
+ /**
+ * `color-contrast` — technisch bedingt, KEINE Altlast, bleibt dauerhaft.
+ * Regel: color-contrast · Seite: / · Element: alle Textknoten in `.pz-hero`
+ * Grund: Verlaufshintergrund (globals.css), axe kann den Kontrast dort
+ * prinzipiell nicht berechnen → 21x incomplete.
+ * Abbau: nur mit einem einfarbigen Hero-Hintergrund. Kein Ziel für sich —
+ * echte Kontrast-VERSTÖSSE blockt LAUF 1 auf dieser Seite weiterhin.
+ *
+ * `duplicate-id-aria` — ALTLAST, eingefroren am 2026-07-26.
+ * Regel: duplicate-id-aria · Seite: / · Elemente: id="plz" und
+ * id="plz-funktion" existieren auf der Startseite je zweimal
+ * (Hero-Formular und zweites Formular weiter unten).
+ * Grund: 2 belegte Verstöße (Impact critical). Dieser PR baut das GATE und
+ * ändert bewusst keine UI-Datei; der Fix ist ein eigener PR.
+ * Abbau: IDs eindeutig machen (z. B. Suffix je Formular-Instanz) und diese
+ * Ausnahme dann ERSATZLOS entfernen — der Folge-PR nimmt sie mit raus.
+ *
+ * `form-field-multiple-labels` — ALTLAST, eingefroren am 2026-07-26.
+ * Regel: form-field-multiple-labels · Seite: / · Elemente: dieselben zwei
+ * PLZ-Felder, die durch die doppelten IDs je zwei einsammeln.
+ * Grund: Impact moderate, Folgefehler der doppelten IDs. Gleiche Begründung
+ * wie oben: keine UI-Änderung in diesem PR.
+ * Abbau: verschwindet mit dem duplicate-id-aria-Fix von selbst; Ausnahme im
+ * selben Folge-PR ersatzlos entfernen.
+ */
+ "/": ["color-contrast", "duplicate-id-aria", "form-field-multiple-labels"],
+
+ /**
+ * `heading-order` — ALTLAST, eingefroren am 2026-07-26.
+ * Regel: heading-order · Seite: /umfragen · Element: eine Überschrift
+ * springt eine Ebene (h2 → h4) statt lückenlos zu staffeln.
+ * Grund: 1 belegter Verstoß (Impact moderate). Erst durch `includeWarnings`
+ * in diesem Lauf überhaupt sichtbar geworden. Keine UI-Änderung in
+ * diesem PR.
+ * Abbau: Überschriftenebene korrigieren, danach diese Ausnahme ersatzlos
+ * entfernen.
+ */
+ "/umfragen": ["heading-order"],
+
+ /**
+ * `link-in-text-block` — ALTLAST, eingefroren am 2026-07-26.
+ * Identisch zur Ausnahme in .pa11yci.js (dort steht die volle Begründung).
+ * Muss in BEIDEN Configs stehen, weil beide Läufe die Seite prüfen, und im
+ * selben Folge-PR aus beiden verschwinden.
+ */
+ "/anliegen": ["link-in-text-block"],
+
+ "/anmelden": [],
+};
+
+const unbekannt = Object.keys(AUSNAHMEN).filter((p) => !PFADE.includes(p));
+if (unbekannt.length > 0) {
+ throw new Error(
+ `.pa11yci.streng.js: Ausnahmen für Seiten, die .pa11yci.js gar nicht prüft: ${unbekannt.join(", ")}`
+ );
+}
+
+module.exports = {
+ defaults: {
+ ...BASIS_DEFAULTS,
+ includeWarnings: true,
+ includeNotices: true,
+ // levelCapWhenNeedsReview bewusst NICHT gesetzt — siehe Kopfkommentar.
+ },
+
+ urls: PFADE.map((pfad) => ({
+ url: url(pfad),
+ ignore: AUSNAHMEN[pfad] ?? [],
+ })),
+};
diff --git a/app/package-lock.json b/app/package-lock.json
index 098a227..1cbac06 100644
--- a/app/package-lock.json
+++ b/app/package-lock.json
@@ -39,6 +39,7 @@
"drizzle-kit": "^0.31.1",
"eslint": "^9",
"eslint-config-next": "16.2.9",
+ "pa11y-ci": "^4.1.1",
"tailwindcss": "^4",
"tsx": "^4.19.4",
"typescript": "^5",
@@ -2732,6 +2733,145 @@
"node": ">=12.4.0"
}
},
+ "node_modules/@pa11y/html_codesniffer": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/@pa11y/html_codesniffer/-/html_codesniffer-2.6.0.tgz",
+ "integrity": "sha512-BKA7qG8NyaIBdCBDep0hYuYoF/bEyWJprE6EEVJOPiwj80sSiIKDT8LUVd19qKhVqNZZD3QvJIdFZ35p+vAFPg==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@puppeteer/browsers": {
+ "version": "2.13.2",
+ "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.13.2.tgz",
+ "integrity": "sha512-5EUZSUIc37H6aIXyWO0Z4y8NlF8NnjgmqeQgOGiswAU7pY0HOo16ho4+alIWmSfdZnjqBRawMsP3I5YqLSn6kw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "debug": "^4.4.3",
+ "extract-zip": "^2.0.1",
+ "progress": "^2.0.3",
+ "proxy-agent": "^6.5.0",
+ "semver": "^7.7.4",
+ "tar-fs": "^3.1.1",
+ "yargs": "^17.7.2"
+ },
+ "bin": {
+ "browsers": "lib/cjs/main-cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@puppeteer/browsers/node_modules/cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@puppeteer/browsers/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@puppeteer/browsers/node_modules/semver": {
+ "version": "7.8.5",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+ "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@puppeteer/browsers/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@puppeteer/browsers/node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/@puppeteer/browsers/node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@puppeteer/browsers/node_modules/yargs": {
+ "version": "17.7.3",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz",
+ "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@puppeteer/browsers/node_modules/yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/@radix-ui/number": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.2.tgz",
@@ -4881,6 +5021,13 @@
"tailwindcss": "4.3.0"
}
},
+ "node_modules/@tootallnate/quickjs-emscripten": {
+ "version": "0.23.0",
+ "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz",
+ "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@ts-morph/common": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.27.0.tgz",
@@ -5069,6 +5216,17 @@
"integrity": "sha512-lrpDziQipxCEeK5kWxvljWYhUvOiB2A9izZd9B2AFarYAkqZshb4lPbRs7zKEic6eGtH8V/2qJW+dPp9OtF6bw==",
"license": "MIT"
},
+ "node_modules/@types/yauzl": {
+ "version": "2.10.3",
+ "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz",
+ "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.61.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.61.0.tgz",
@@ -6039,6 +6197,29 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/array-union": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
+ "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-uniq": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/array-uniq": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
+ "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/array.prototype.findlast": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz",
@@ -6188,6 +6369,13 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/async": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
+ "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/async-function": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz",
@@ -6234,6 +6422,21 @@
"node": ">= 0.4"
}
},
+ "node_modules/b4a": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz",
+ "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "react-native-b4a": "*"
+ },
+ "peerDependenciesMeta": {
+ "react-native-b4a": {
+ "optional": true
+ }
+ }
+ },
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -6241,6 +6444,91 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/bare-events": {
+ "version": "2.9.1",
+ "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.1.tgz",
+ "integrity": "sha512-Z0oHEHAFDZkffN8Qc39zNZjQlMDkPJRyyyZieU1VH7u8c5S+qHZ2S8ixdKIAxEjfHO7FJxXmJWgteOghVanIsg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "bare-abort-controller": "*"
+ },
+ "peerDependenciesMeta": {
+ "bare-abort-controller": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/bare-fs": {
+ "version": "4.7.4",
+ "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.7.4.tgz",
+ "integrity": "sha512-y1kC+ffIx/tPLdTE693uNjHfzTfr+ravR5tvWlMXe25nELbkqV400S71qHDwbkAQ1FVEZobB1NFRzFbCCcyBCQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "bare-events": "^2.5.4",
+ "bare-path": "^3.0.0",
+ "bare-stream": "^2.6.4",
+ "bare-url": "^2.2.2",
+ "fast-fifo": "^1.3.2"
+ },
+ "engines": {
+ "bare": ">=1.16.0"
+ },
+ "peerDependencies": {
+ "bare-buffer": "*"
+ },
+ "peerDependenciesMeta": {
+ "bare-buffer": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/bare-path": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.1.1.tgz",
+ "integrity": "sha512-JprUlveX3QjApC1cTpsUOiscADftCGVWkzitbHsRqv84hzYwYHw2mbluddsq5TvI8mH/8Ov1f4BiMAdcB0oYnQ==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/bare-stream": {
+ "version": "2.13.3",
+ "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.13.3.tgz",
+ "integrity": "sha512-Kc+brLqvEqGkjyfiwJmImAOqLZL7OsoLKuavx+hJjgVV3nLTOjloJyPMFxjUPerGGHrNH0fLU06jjykMLWrERQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "b4a": "^1.8.1",
+ "streamx": "^2.25.0",
+ "teex": "^1.0.1"
+ },
+ "peerDependencies": {
+ "bare-abort-controller": "*",
+ "bare-buffer": "*",
+ "bare-events": "*"
+ },
+ "peerDependenciesMeta": {
+ "bare-abort-controller": {
+ "optional": true
+ },
+ "bare-buffer": {
+ "optional": true
+ },
+ "bare-events": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/bare-url": {
+ "version": "2.4.6",
+ "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.4.6.tgz",
+ "integrity": "sha512-iQxPClE07hETVpbRoX7JXX3v/ZQViCxe/SYCxylRLzdEx1xJAufPptfiOqR8tqiCtmbtMDANKWszzjLu1PMAZQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "bare-path": "^3.0.0"
+ }
+ },
"node_modules/baseline-browser-mapping": {
"version": "2.10.35",
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.35.tgz",
@@ -6253,6 +6541,31 @@
"node": ">=6.0.0"
}
},
+ "node_modules/basic-ftp": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.3.1.tgz",
+ "integrity": "sha512-bopVNp6ugyA150DDuZfPFdt1KZ5a94ZDiwX4hMgZDzF+GttD80lEy8kj98kbyhLXnPvhtIo93mdnLIjpCAeeOw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/bfj": {
+ "version": "9.1.3",
+ "resolved": "https://registry.npmjs.org/bfj/-/bfj-9.1.3.tgz",
+ "integrity": "sha512-1ythbcNNAd2UjTYW6M+MAHd9KM/m3g4mQ+3a4Vom16WgmUa4GsisdmXAYfpAjkObY5zdpgzaBh1ctZOEcJipuQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "check-types": "^11.2.3",
+ "hoopy": "^0.1.4",
+ "tryer": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 18.0.0"
+ }
+ },
"node_modules/body-parser": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz",
@@ -6277,6 +6590,13 @@
"url": "https://opencollective.com/express"
}
},
+ "node_modules/boolbase": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
+ "dev": true,
+ "license": "ISC"
+ },
"node_modules/brace-expansion": {
"version": "1.1.15",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
@@ -6333,6 +6653,16 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
+ "node_modules/buffer-crc32": {
+ "version": "0.2.13",
+ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
+ "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
@@ -6504,6 +6834,71 @@
"node": ">= 16"
}
},
+ "node_modules/check-types": {
+ "version": "11.2.3",
+ "resolved": "https://registry.npmjs.org/check-types/-/check-types-11.2.3.tgz",
+ "integrity": "sha512-+67P1GkJRaxQD6PKK0Et9DhwQB+vGg3PM5+aavopCpZT1lj9jeqfvpgTLAWErNj8qApkkmXlu/Ug74kmhagkXg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cheerio": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0.tgz",
+ "integrity": "sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cheerio-select": "^2.1.0",
+ "dom-serializer": "^2.0.0",
+ "domhandler": "^5.0.3",
+ "domutils": "^3.1.0",
+ "encoding-sniffer": "^0.2.0",
+ "htmlparser2": "^9.1.0",
+ "parse5": "^7.1.2",
+ "parse5-htmlparser2-tree-adapter": "^7.0.0",
+ "parse5-parser-stream": "^7.1.2",
+ "undici": "^6.19.5",
+ "whatwg-mimetype": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=18.17"
+ },
+ "funding": {
+ "url": "https://github.com/cheeriojs/cheerio?sponsor=1"
+ }
+ },
+ "node_modules/cheerio-select": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz",
+ "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "boolbase": "^1.0.0",
+ "css-select": "^5.1.0",
+ "css-what": "^6.1.0",
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3",
+ "domutils": "^3.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/chromium-bidi": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-14.0.0.tgz",
+ "integrity": "sha512-9gYlLtS6tStdRWzrtXaTMnqcM4dudNegMXJxkR0I/CXObHalYeYcAMPrL19eroNZHtJ8DQmu1E+ZNOYu/IXMXw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "mitt": "^3.0.1",
+ "zod": "^3.24.1"
+ },
+ "peerDependencies": {
+ "devtools-protocol": "*"
+ }
+ },
"node_modules/class-variance-authority": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz",
@@ -6732,6 +7127,36 @@
"node": ">= 8"
}
},
+ "node_modules/css-select": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz",
+ "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "boolbase": "^1.0.0",
+ "css-what": "^6.1.0",
+ "domhandler": "^5.0.2",
+ "domutils": "^3.0.1",
+ "nth-check": "^2.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/css-what": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz",
+ "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">= 6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
"node_modules/cssesc": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
@@ -6963,13 +7388,41 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/depd": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
- "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "node_modules/degenerator": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz",
+ "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==",
+ "dev": true,
"license": "MIT",
+ "dependencies": {
+ "ast-types": "^0.13.4",
+ "escodegen": "^2.1.0",
+ "esprima": "^4.0.1"
+ },
"engines": {
- "node": ">= 0.8"
+ "node": ">= 14"
+ }
+ },
+ "node_modules/degenerator/node_modules/ast-types": {
+ "version": "0.13.4",
+ "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz",
+ "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
}
},
"node_modules/detect-libc": {
@@ -6988,6 +7441,13 @@
"integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==",
"license": "MIT"
},
+ "node_modules/devtools-protocol": {
+ "version": "0.0.1608973",
+ "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1608973.tgz",
+ "integrity": "sha512-Tpm17fxYzt+J7VrGdc1k8YdRqS3YV7se/M6KeemEqvUbq/n7At1rWVuXMxQgpWkdwSdIEKYbU//Bve+Shm4YNQ==",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
"node_modules/diff": {
"version": "8.0.4",
"resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz",
@@ -7016,6 +7476,65 @@
"node": ">=0.10.0"
}
},
+ "node_modules/dom-serializer": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
+ "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.2",
+ "entities": "^4.2.0"
+ },
+ "funding": {
+ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+ }
+ },
+ "node_modules/domelementtype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+ "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ],
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/domhandler": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
+ "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "domelementtype": "^2.3.0"
+ },
+ "engines": {
+ "node": ">= 4"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domhandler?sponsor=1"
+ }
+ },
+ "node_modules/domutils": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz",
+ "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "dom-serializer": "^2.0.0",
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domutils?sponsor=1"
+ }
+ },
"node_modules/dotenv": {
"version": "17.4.2",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.2.tgz",
@@ -7228,6 +7747,43 @@
"node": ">= 0.8"
}
},
+ "node_modules/encoding-sniffer": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz",
+ "integrity": "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "iconv-lite": "^0.6.3",
+ "whatwg-encoding": "^3.1.1"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/encoding-sniffer?sponsor=1"
+ }
+ },
+ "node_modules/encoding-sniffer/node_modules/iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/end-of-stream": {
+ "version": "1.4.5",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
+ "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "once": "^1.4.0"
+ }
+ },
"node_modules/enhanced-resolve": {
"version": "5.23.0",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.23.0.tgz",
@@ -7255,6 +7811,19 @@
"node": ">=8.6"
}
},
+ "node_modules/entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
"node_modules/env-paths": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
@@ -7264,6 +7833,19 @@
"node": ">=6"
}
},
+ "node_modules/envinfo": {
+ "version": "7.21.0",
+ "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.21.0.tgz",
+ "integrity": "sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "envinfo": "dist/cli.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/error-ex": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz",
@@ -7524,6 +8106,28 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/escodegen": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
+ "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esprima": "^4.0.1",
+ "estraverse": "^5.2.0",
+ "esutils": "^2.0.2"
+ },
+ "bin": {
+ "escodegen": "bin/escodegen.js",
+ "esgenerate": "bin/esgenerate.js"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "optionalDependencies": {
+ "source-map": "~0.6.1"
+ }
+ },
"node_modules/eslint": {
"version": "9.39.4",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz",
@@ -7962,6 +8566,16 @@
"node": ">= 0.6"
}
},
+ "node_modules/events-universal": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz",
+ "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "bare-events": "^2.7.0"
+ }
+ },
"node_modules/eventsource": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz",
@@ -8080,12 +8694,56 @@
"express": ">= 4.11"
}
},
+ "node_modules/extract-zip": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
+ "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "debug": "^4.1.1",
+ "get-stream": "^5.1.0",
+ "yauzl": "^2.10.0"
+ },
+ "bin": {
+ "extract-zip": "cli.js"
+ },
+ "engines": {
+ "node": ">= 10.17.0"
+ },
+ "optionalDependencies": {
+ "@types/yauzl": "^2.9.1"
+ }
+ },
+ "node_modules/extract-zip/node_modules/get-stream": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
+ "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pump": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"license": "MIT"
},
+ "node_modules/fast-fifo": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
+ "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/fast-glob": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz",
@@ -8155,6 +8813,16 @@
"reusify": "^1.0.4"
}
},
+ "node_modules/fd-slicer": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
+ "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pend": "~1.2.0"
+ }
+ },
"node_modules/fetch-blob": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
@@ -8206,6 +8874,16 @@
"node": ">=16.0.0"
}
},
+ "node_modules/file-url": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/file-url/-/file-url-3.0.0.tgz",
+ "integrity": "sha512-g872QGsHexznxkIAdK8UiZRe7SkE6kvylShU4Nsj8NvfvZag7S0QuQ4IgvPDkk75HxgjIVDwycFTDAgIiO4nDA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
@@ -8337,6 +9015,13 @@
"node": ">=14.14"
}
},
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true,
+ "license": "ISC"
+ },
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@@ -8543,6 +9228,53 @@
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
}
},
+ "node_modules/get-uri": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz",
+ "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "basic-ftp": "^5.0.2",
+ "data-uri-to-buffer": "^6.0.2",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/get-uri/node_modules/data-uri-to-buffer": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz",
+ "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/glob-parent": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
@@ -8586,6 +9318,23 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/globby": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
+ "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-union": "^1.0.1",
+ "glob": "^7.0.3",
+ "object-assign": "^4.0.1",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
@@ -8722,6 +9471,36 @@
"node": ">=16.9.0"
}
},
+ "node_modules/hoopy": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz",
+ "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/htmlparser2": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz",
+ "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==",
+ "dev": true,
+ "funding": [
+ "https://github.com/fb55/htmlparser2?sponsor=1",
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3",
+ "domutils": "^3.1.0",
+ "entities": "^4.5.0"
+ }
+ },
"node_modules/http-errors": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
@@ -8742,6 +9521,20 @@
"url": "https://opencollective.com/express"
}
},
+ "node_modules/http-proxy-agent": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
+ "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
"node_modules/https-proxy-agent": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
@@ -8815,6 +9608,18 @@
"node": ">=0.8.19"
}
},
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
@@ -8854,6 +9659,16 @@
"node": ">= 0.10"
}
},
+ "node_modules/is": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/is/-/is-3.3.2.tgz",
+ "integrity": "sha512-a2xr4E3s1PjDS8ORcGgXpWx6V+liNs+O3JRD2mb9aeugD7rtkkZ0zgLdYgw0tWsKhsdiezGYptSiMlVazCBTuQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/is-array-buffer": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
@@ -9918,6 +10733,13 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/lodash": {
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
+ "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@@ -10139,12 +10961,29 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/mitt": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
+ "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
+ "node_modules/mustache": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz",
+ "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "mustache": "bin/mustache"
+ }
+ },
"node_modules/nanoid": {
"version": "3.3.12",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
@@ -10195,6 +11034,16 @@
"node": ">= 0.6"
}
},
+ "node_modules/netmask": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.1.1.tgz",
+ "integrity": "sha512-eonl3sLUha+S1GzTPxychyhnUzKyeQkZ7jLjKrBagJgPla13F+uQ71HgpFefyHgqrjEbCPkDArxYsjY8/+gLKA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
"node_modules/next": {
"version": "16.2.9",
"resolved": "https://registry.npmjs.org/next/-/next-16.2.9.tgz",
@@ -10348,6 +11197,20 @@
"node": ">=18"
}
},
+ "node_modules/node.extend": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/node.extend/-/node.extend-2.0.3.tgz",
+ "integrity": "sha512-xwADg/okH48PvBmRZyoX8i8GJaKuJ1CqlqotlZOhUio8egD1P5trJupHKBzcPjSF9ifK2gPcEICRBnkfPqQXZw==",
+ "dev": true,
+ "license": "(MIT OR GPL-2.0)",
+ "dependencies": {
+ "hasown": "^2.0.0",
+ "is": "^3.3.0"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
"node_modules/nodemailer": {
"version": "8.0.11",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-8.0.11.tgz",
@@ -10385,6 +11248,19 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/nth-check": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
+ "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "boolbase": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/nth-check?sponsor=1"
+ }
+ },
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -10710,6 +11586,135 @@
"node": ">=6"
}
},
+ "node_modules/pa11y": {
+ "version": "9.1.1",
+ "resolved": "https://registry.npmjs.org/pa11y/-/pa11y-9.1.1.tgz",
+ "integrity": "sha512-kHuEgMcoH7YZjcf/G/GEFi2XELsLOv5R+ctaus4EDlLaTU+Cd9GjPbHc/wsKpl87Rmk3lHL2eJA+mZ0XXd0Eew==",
+ "dev": true,
+ "license": "LGPL-3.0-only",
+ "dependencies": {
+ "@pa11y/html_codesniffer": "^2.6.0",
+ "axe-core": "~4.11.1",
+ "bfj": "~9.1.3",
+ "commander": "~14.0.3",
+ "envinfo": "~7.21.0",
+ "kleur": "~4.1.5",
+ "mustache": "~4.2.0",
+ "node.extend": "~2.0.3",
+ "puppeteer": "^24.37.5",
+ "semver": "~7.7.4"
+ },
+ "bin": {
+ "pa11y": "bin/pa11y.js"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/pa11y-ci": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/pa11y-ci/-/pa11y-ci-4.1.1.tgz",
+ "integrity": "sha512-urSrJTTDtXypYxpjhYSzVsbHlFblmbPgMfCxJI6WyTF3oSxNHfQ77mFIv5PKGiN/k46Qc/tVc8+4Ny+y13pGow==",
+ "dev": true,
+ "license": "LGPL-3.0-only",
+ "dependencies": {
+ "async": "~3.2.6",
+ "cheerio": "~1.0.0",
+ "commander": "~14.0.3",
+ "globby": "~6.1.0",
+ "kleur": "~4.1.5",
+ "lodash": "~4.18.1",
+ "node-fetch": "~2.7.0",
+ "pa11y": "^9.1.1",
+ "protocolify": "~3.0.0",
+ "puppeteer": "^24.37.5",
+ "wordwrap": "~1.0.0"
+ },
+ "bin": {
+ "pa11y-ci": "bin/pa11y-ci.js"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/pa11y-ci/node_modules/node-fetch": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
+ "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "whatwg-url": "^5.0.0"
+ },
+ "engines": {
+ "node": "4.x || >=6.0.0"
+ },
+ "peerDependencies": {
+ "encoding": "^0.1.0"
+ },
+ "peerDependenciesMeta": {
+ "encoding": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/pa11y/node_modules/axe-core": {
+ "version": "4.11.4",
+ "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.4.tgz",
+ "integrity": "sha512-KunSNx+TVpkAw/6ULfhnx+HWRecjqZGTOyquAoWHYLRSdK1tB5Ihce1ZW+UY3fj33bYAFWPu7W/GRSmmrCGuxA==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/pa11y/node_modules/semver": {
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/pac-proxy-agent": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz",
+ "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@tootallnate/quickjs-emscripten": "^0.23.0",
+ "agent-base": "^7.1.2",
+ "debug": "^4.3.4",
+ "get-uri": "^6.0.1",
+ "http-proxy-agent": "^7.0.0",
+ "https-proxy-agent": "^7.0.6",
+ "pac-resolver": "^7.0.1",
+ "socks-proxy-agent": "^8.0.5"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/pac-resolver": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz",
+ "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "degenerator": "^5.0.0",
+ "netmask": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
"node_modules/parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -10728,28 +11733,81 @@
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
"license": "MIT",
"dependencies": {
- "@babel/code-frame": "^7.0.0",
- "error-ex": "^1.3.1",
- "json-parse-even-better-errors": "^2.3.0",
- "lines-and-columns": "^1.1.6"
- },
- "engines": {
- "node": ">=8"
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-even-better-errors": "^2.3.0",
+ "lines-and-columns": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parse-ms": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz",
+ "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parse5": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
+ "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "entities": "^6.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/inikulin/parse5?sponsor=1"
+ }
+ },
+ "node_modules/parse5-htmlparser2-tree-adapter": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz",
+ "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "domhandler": "^5.0.3",
+ "parse5": "^7.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/inikulin/parse5?sponsor=1"
+ }
+ },
+ "node_modules/parse5-parser-stream": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz",
+ "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parse5": "^7.0.0"
},
"funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "url": "https://github.com/inikulin/parse5?sponsor=1"
}
},
- "node_modules/parse-ms": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz",
- "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==",
- "license": "MIT",
+ "node_modules/parse5/node_modules/entities": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
+ "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
"engines": {
- "node": ">=18"
+ "node": ">=0.12"
},
"funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/parseurl": {
@@ -10776,6 +11834,16 @@
"node": ">=8"
}
},
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
@@ -10841,6 +11909,13 @@
"ms": "^2.1.1"
}
},
+ "node_modules/pend": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
+ "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -10859,6 +11934,39 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
+ "node_modules/pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/pinkie": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+ "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/pinkie-promise": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+ "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pinkie": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/pkce-challenge": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz",
@@ -10963,6 +12071,16 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/prepend-http": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-3.0.1.tgz",
+ "integrity": "sha512-BLxfZh+m6UiAiCPZFJ4+vYoL7NrRs5XgCTRrjseATAggXhdZKKxn+JUNmuVYWY23bDHgaEHodxw8mnmtVEDtHw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/pretty-ms": {
"version": "9.3.0",
"resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.0.tgz",
@@ -10978,6 +12096,16 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/progress": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
"node_modules/prompts": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
@@ -11012,6 +12140,20 @@
"react-is": "^16.13.1"
}
},
+ "node_modules/protocolify": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/protocolify/-/protocolify-3.0.0.tgz",
+ "integrity": "sha512-PuvDJOkKJMVQx8jSNf8E5g0bJw/UTKm30mTjFHg4N30c8sefgA5Qr/f8INKqYBKfvP/MUSJrj+z1Smjbq4/3rQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "file-url": "^3.0.0",
+ "prepend-http": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@@ -11025,6 +12167,54 @@
"node": ">= 0.10"
}
},
+ "node_modules/proxy-agent": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz",
+ "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.2",
+ "debug": "^4.3.4",
+ "http-proxy-agent": "^7.0.1",
+ "https-proxy-agent": "^7.0.6",
+ "lru-cache": "^7.14.1",
+ "pac-proxy-agent": "^7.1.0",
+ "proxy-from-env": "^1.1.0",
+ "socks-proxy-agent": "^8.0.5"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/proxy-agent/node_modules/lru-cache": {
+ "version": "7.18.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
+ "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/proxy-from-env": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/pump": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz",
+ "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@@ -11035,6 +12225,47 @@
"node": ">=6"
}
},
+ "node_modules/puppeteer": {
+ "version": "24.43.1",
+ "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.43.1.tgz",
+ "integrity": "sha512-/FSOViCrqRdb1HDocpsM9Z1giA71gTQPUt3SpHGVRALKAy/rJr1fLFYZW9F23qPxqVxTHQnbh/5B5opJST3kAw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@puppeteer/browsers": "2.13.2",
+ "chromium-bidi": "14.0.0",
+ "cosmiconfig": "^9.0.0",
+ "devtools-protocol": "0.0.1608973",
+ "puppeteer-core": "24.43.1",
+ "typed-query-selector": "^2.12.2"
+ },
+ "bin": {
+ "puppeteer": "lib/cjs/puppeteer/node/cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/puppeteer-core": {
+ "version": "24.43.1",
+ "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.43.1.tgz",
+ "integrity": "sha512-T5ScUMAsmhdNbgDR41AGESYeS6V9MSgetkSnVhhW+gXvzC42VesKCn5ld87gAZDJ6vLHL9GkRvY9WtQWSnwFbw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@puppeteer/browsers": "2.13.2",
+ "chromium-bidi": "14.0.0",
+ "debug": "^4.4.3",
+ "devtools-protocol": "0.0.1608973",
+ "typed-query-selector": "^2.12.2",
+ "webdriver-bidi-protocol": "0.4.1",
+ "ws": "^8.20.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/qrcode": {
"version": "1.5.4",
"resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz",
@@ -11984,6 +13215,47 @@
"integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
"license": "MIT"
},
+ "node_modules/smart-buffer": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
+ "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6.0.0",
+ "npm": ">= 3.0.0"
+ }
+ },
+ "node_modules/socks": {
+ "version": "2.8.9",
+ "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.9.tgz",
+ "integrity": "sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ip-address": "^10.1.1",
+ "smart-buffer": "^4.2.0"
+ },
+ "engines": {
+ "node": ">= 10.0.0",
+ "npm": ">= 3.0.0"
+ }
+ },
+ "node_modules/socks-proxy-agent": {
+ "version": "8.0.5",
+ "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz",
+ "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.2",
+ "debug": "^4.3.4",
+ "socks": "^2.8.3"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -12069,6 +13341,18 @@
"node": ">= 0.4"
}
},
+ "node_modules/streamx": {
+ "version": "2.28.0",
+ "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.0.tgz",
+ "integrity": "sha512-1Yowhzjf0ivGMrTIkY9hav5TxobO9qIVqUE41fiCGMGgc3CLlf4MY+9AHmZqBWgDTue0fY9zWjYFVyf6Diuobw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "events-universal": "^1.0.0",
+ "fast-fifo": "^1.3.2",
+ "text-decoder": "^1.1.0"
+ }
+ },
"node_modules/string-width": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
@@ -12396,6 +13680,54 @@
"url": "https://opencollective.com/webpack"
}
},
+ "node_modules/tar-fs": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.3.tgz",
+ "integrity": "sha512-/hU4AXnIdZu+Gvl1pk0oI5f5HxWsCJRtY2aFaJdk9VvyL48DWU6iU5WAIPG+wIi1YvWA6eTJvIviP/tMAZZNwQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pump": "^3.0.0",
+ "tar-stream": "^3.1.5"
+ },
+ "optionalDependencies": {
+ "bare-fs": "^4.0.1",
+ "bare-path": "^3.0.0"
+ }
+ },
+ "node_modules/tar-stream": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.2.0.tgz",
+ "integrity": "sha512-ojzvCvVaNp6aOTFmG7jaRD0meowIAuPc3cMMhSgKiVWws1GyHbGd/xvnyuRKcKlMpt3qvxx6r0hreCNITP9hIg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "b4a": "^1.6.4",
+ "bare-fs": "^4.5.5",
+ "fast-fifo": "^1.2.0",
+ "streamx": "^2.15.0"
+ }
+ },
+ "node_modules/teex": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz",
+ "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "streamx": "^2.12.5"
+ }
+ },
+ "node_modules/text-decoder": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz",
+ "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "b4a": "^1.6.4"
+ }
+ },
"node_modules/tiny-invariant": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
@@ -12515,6 +13847,20 @@
"node": ">=0.6"
}
},
+ "node_modules/tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tryer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz",
+ "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/ts-api-utils": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz",
@@ -13213,6 +14559,13 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/typed-query-selector": {
+ "version": "2.12.2",
+ "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.2.tgz",
+ "integrity": "sha512-EOPFbyIub4ngnEdqi2yOcNeDLaX/0jcE1JoAXQDDMIthap7FoN795lc/SHfIq2d416VufXpM8z/lD+WRm2gfOQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/typescript": {
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
@@ -13270,6 +14623,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/undici": {
+ "version": "6.28.0",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-6.28.0.tgz",
+ "integrity": "sha512-LIY910g9TI13YS95lrMFrs8Rm/u/irgHeTWoKCoteeJ04CUJ92eEfj0rVn+7VKMPBpUPiUoBKfhNyLI23EE/KA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.17"
+ }
+ },
"node_modules/undici-types": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
@@ -14168,6 +15531,68 @@
"node": ">= 8"
}
},
+ "node_modules/webdriver-bidi-protocol": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.4.1.tgz",
+ "integrity": "sha512-ARrjNjtWRRs2w4Tk7nqrf2gBI0QXWuOmMCx2hU+1jUt6d00MjMxURrhxhGbrsoiZKJrhTSTzbIrc554iKI10qw==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
+ "dev": true,
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/whatwg-encoding": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",
+ "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
+ "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "iconv-lite": "0.6.3"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/whatwg-encoding/node_modules/iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/whatwg-mimetype": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
+ "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ },
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -14305,6 +15730,13 @@
"node": ">=0.10.0"
}
},
+ "node_modules/wordwrap": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+ "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/wrap-ansi": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
@@ -14345,6 +15777,28 @@
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"license": "ISC"
},
+ "node_modules/ws": {
+ "version": "8.21.1",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.1.tgz",
+ "integrity": "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": ">=5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
"node_modules/wsl-utils": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.3.1.tgz",
@@ -14480,6 +15934,17 @@
"node": ">=8"
}
},
+ "node_modules/yauzl": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
+ "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer-crc32": "~0.2.3",
+ "fd-slicer": "~1.1.0"
+ }
+ },
"node_modules/yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
diff --git a/app/package.json b/app/package.json
index b2231d4..c103852 100644
--- a/app/package.json
+++ b/app/package.json
@@ -9,6 +9,7 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
+ "start:standalone": "rm -rf .next/standalone/.next/static .next/standalone/public && cp -r .next/static .next/standalone/.next/static && cp -r public .next/standalone/public && node .next/standalone/server.js",
"lint": "eslint src scripts --max-warnings 0",
"typecheck": "tsc --noEmit",
"test": "vitest run",
@@ -21,6 +22,7 @@
"db:seed:regions": "tsx scripts/seed-regions.ts",
"db:upgrade": "npm run db:seed:regions && npm run db:migrate",
"test:upgrade": "tsx scripts/test-upgrade-path.ts",
+ "a11y": "tsx scripts/a11y-preflight.ts && pa11y-ci --config .pa11yci.js && pa11y-ci --config .pa11yci.streng.js",
"demo:reset": "tsx scripts/demo-reset.ts",
"db:setup": "tsx scripts/migrate.ts && tsx scripts/seed.ts",
"db:cleanup": "tsx scripts/cleanup-auth.ts",
@@ -69,6 +71,7 @@
"drizzle-kit": "^0.31.1",
"eslint": "^9",
"eslint-config-next": "16.2.9",
+ "pa11y-ci": "^4.1.1",
"tailwindcss": "^4",
"tsx": "^4.19.4",
"typescript": "^5",
diff --git a/app/scripts/a11y-preflight.ts b/app/scripts/a11y-preflight.ts
new file mode 100644
index 0000000..27af1a5
--- /dev/null
+++ b/app/scripts/a11y-preflight.ts
@@ -0,0 +1,265 @@
+/**
+ * a11y-preflight.ts — Torwächter vor den pa11y-Läufen (Issue #60).
+ *
+ * WARUM DIESES SKRIPT ÜBERHAUPT EXISTIERT:
+ * Die Tenant-Auflösung ist host-basiert (src/middleware.ts). `localhost` und
+ * `127.0.0.1` sind HAUPT-Domains — ein Server, der ohne `PILOT_TENANT_SLUG`
+ * startet, liefert dort die NEUTRALE Landing-Page, nicht die Tenant-App. Ein
+ * a11y-Job, der dagegen läuft, ist grün und wertlos: er prüft eine Seite, die
+ * mit den Kernflüssen nichts zu tun hat.
+ *
+ * Dieses Skript macht daraus einen harten Fehler statt einer stillen Lüge:
+ *
+ * 1. READINESS: HTTP-Poll auf die Basis-URL bis Timeout — KEIN `sleep` als
+ * Synchronisation (nicht deterministisch, verdeckt Boot-Fehler).
+ * 2. STATUS: jede der geprüften Seiten muss unauthentifiziert exakt 200
+ * liefern. Ein 3xx/404 hieße, dass pa11y eine Weiterleitungs- oder
+ * Fehlerseite prüft statt der Kernseite.
+ * 3. TENANT-BEWEIS: /umfragen MUSS den Wortlaut mindestens einer geseedeten
+ * Umfrage aus db/seeds/polls.json enthalten. Die neutrale Landing-Page
+ * kann das prinzipiell nicht — damit ist bewiesen, dass der Lauf die
+ * echten Tenant-Seiten sieht. (Dieselbe Fehlerklasse wie ein erfundenes
+ * Fixture: grün gegen eine Struktur, die es so nicht gibt.)
+ * 4. STYLING-BEWEIS: jede Seite muss mindestens ein
+ * führen, und jedes davon muss mit 200 und nicht-leerem Rumpf ausliefern.
+ * Punkt 2 und 3 beweisen nur INHALT, nicht STYLING — und viele
+ * axe-Regeln sind rein visuell (color-contrast, link-in-text-block).
+ * Real nachgestellt: mit blockiertem Stylesheet verschwindet die bekannte
+ * link-in-text-block-Violation auf /anliegen spurlos, ohne dass irgendein
+ * anderer Schritt bricht. Ein CSS-loser Lauf wäre also wieder grün und
+ * wertlos — hier wird er zum harten Fehler.
+ *
+ * Exit 0 = Preflight bestanden, pa11y darf laufen. Exit 1 = Abbruch.
+ *
+ * Basis-URL und Seitenliste kommen aus ../.pa11yci.js — derselben Datei, aus
+ * der pa11y-ci seine Ziele liest. Vorher lagen beide doppelt vor (hier und in
+ * der Config); driftet eines, prüft der Preflight einen anderen Server oder
+ * andere Seiten als pa11y, ohne dass es auffällt.
+ *
+ * Env:
+ * A11Y_BASE_URL Basis-URL des laufenden Servers (Default http://127.0.0.1:3000)
+ * PILOT_TENANT_SLUG Tenant, dessen Seeds erwartet werden (Default taunusstein)
+ * A11Y_READY_TIMEOUT Readiness-Timeout in ms (Default 120000)
+ */
+
+import { readFileSync } from "node:fs";
+import { createRequire } from "node:module";
+import { join } from "node:path";
+
+interface Pa11yZiele {
+ BASIS_URL: string;
+ PFADE: string[];
+}
+
+/**
+ * `.pa11yci.js` ist eine CommonJS-Config (pa11y-ci lädt sie über `require`).
+ * `createRequire` holt sie hier mit demselben Auflösungsverhalten — ein
+ * statischer `import` würde TypeScript zwingen, die JS-Datei mitzutypen.
+ */
+const requireCjs = createRequire(join(process.cwd(), "package.json"));
+const ziele = requireCjs("./.pa11yci.js") as Pa11yZiele;
+
+const BASE_URL = (process.env.A11Y_BASE_URL ?? ziele.BASIS_URL).replace(
+ /\/+$/,
+ ""
+);
+const TENANT_SLUG = process.env.PILOT_TENANT_SLUG?.trim() || "taunusstein";
+const READY_TIMEOUT_MS = Number(process.env.A11Y_READY_TIMEOUT ?? 120_000);
+const READY_INTERVAL_MS = 500;
+
+/** Die Kernseiten, die pa11y prüft — direkt aus der pa11y-Config. */
+const PAGES: readonly string[] = ziele.PFADE;
+
+interface PollSeed {
+ tenantSlug: string;
+ frage: string;
+}
+
+function fail(message: string): never {
+ console.error(`\na11y-preflight FEHLGESCHLAGEN: ${message}\n`);
+ process.exit(1);
+}
+
+/**
+ * Readiness über HTTP-Poll statt sleep: fragt die Basis-URL, bis sie irgendeine
+ * HTTP-Antwort liefert (auch 4xx/5xx zählt als „Server nimmt Verbindungen an").
+ */
+async function waitForServer(): Promise {
+ const deadline = Date.now() + READY_TIMEOUT_MS;
+ let lastError = "keine Antwort";
+
+ while (Date.now() < deadline) {
+ try {
+ const res = await fetch(`${BASE_URL}/`, { redirect: "manual" });
+ console.log(`readiness: ${BASE_URL}/ antwortet mit ${res.status}`);
+ return;
+ } catch (err) {
+ lastError = err instanceof Error ? err.message : String(err);
+ }
+ await new Promise((resolve) => setTimeout(resolve, READY_INTERVAL_MS));
+ }
+
+ fail(
+ `Server unter ${BASE_URL} war nach ${READY_TIMEOUT_MS} ms nicht erreichbar (${lastError}).`
+ );
+}
+
+/**
+ * Holt jede Kernseite genau einmal. Alle folgenden Assertions arbeiten auf
+ * diesen Antworten — ein Fetch pro Seite, keine Doppelabfragen.
+ */
+async function ladeSeiten(): Promise> {
+ const html = new Map();
+ const abweichungen: string[] = [];
+
+ for (const path of PAGES) {
+ const res = await fetch(`${BASE_URL}${path}`, { redirect: "manual" });
+ console.log(`status: ${path} → ${res.status}`);
+ if (res.status !== 200) {
+ abweichungen.push(`${path} → ${res.status}`);
+ continue;
+ }
+ html.set(path, await res.text());
+ }
+
+ if (abweichungen.length > 0) {
+ fail(
+ `Nicht alle Kernseiten liefern unauthentifiziert 200: ${abweichungen.join(", ")}. ` +
+ `pa11y würde Weiterleitungs- oder Fehlerseiten prüfen statt der Kernseiten.`
+ );
+ }
+
+ return html;
+}
+
+/**
+ * Minimale Entity-Dekodierung: React escaped im HTML-Output u. a. `&`, `<`, `>`,
+ * `"` und `'`. Ohne diese Rücknahme scheitert ein Substring-Vergleich an
+ * Fragen, die solche Zeichen enthalten — ein falsch-negativer Abbruch.
+ */
+function decodeEntities(html: string): string {
+ return html
+ .replace(/'|'|'/g, "'")
+ .replace(/"|"/g, '"')
+ .replace(/</g, "<")
+ .replace(/>/g, ">")
+ .replace(/ /g, " ")
+ .replace(/&/g, "&");
+}
+
+/**
+ * Der eigentliche Inhalts-Beweis: /umfragen zeigt echte Seed-Inhalte des
+ * Pilot-Tenants.
+ */
+function assertTenantPagesVisible(seiten: Map): void {
+ const seedPath = join(process.cwd(), "..", "db", "seeds", "polls.json");
+ const polls = JSON.parse(readFileSync(seedPath, "utf8")) as PollSeed[];
+ const fragen = polls
+ .filter((p) => p.tenantSlug === TENANT_SLUG)
+ .map((p) => p.frage);
+
+ if (fragen.length === 0) {
+ fail(
+ `db/seeds/polls.json enthält keine Umfrage für Tenant "${TENANT_SLUG}" — ` +
+ `die Tenant-Assertion wäre inhaltsleer.`
+ );
+ }
+
+ const html = decodeEntities(seiten.get("/umfragen") ?? "");
+ const gefunden = fragen.filter((frage) => html.includes(frage));
+
+ if (gefunden.length === 0) {
+ fail(
+ `/umfragen enthält KEINE der ${fragen.length} geseedeten Umfragen des Tenants ` +
+ `"${TENANT_SLUG}".\nErwartet (mindestens eine):\n` +
+ fragen.map((f) => ` - ${f}`).join("\n") +
+ `\n\nWahrscheinlichste Ursache: der Server läuft ohne PILOT_TENANT_SLUG, ` +
+ `also liefert die Haupt-Domain die NEUTRALE Landing-Page statt der Tenant-App ` +
+ `(src/middleware.ts). Alternativ fehlt der Seed (npm run db:seed).`
+ );
+ }
+
+ console.log(
+ `tenant-assertion: ${gefunden.length}/${fragen.length} geseedete Umfragen auf /umfragen gefunden`
+ );
+ for (const frage of gefunden) console.log(` ✓ ${frage}`);
+}
+
+/** Alle ` ` einer Seite, in Dokumentreihenfolge. */
+function stylesheetHrefs(html: string): string[] {
+ const hrefs: string[] = [];
+ for (const [tag] of html.matchAll(/ ]*>/gi)) {
+ if (!/\brel\s*=\s*["']?stylesheet\b/i.test(tag)) continue;
+ const href = /\bhref\s*=\s*["']([^"']+)["']/i.exec(tag)?.[1];
+ if (href) hrefs.push(href);
+ }
+ return hrefs;
+}
+
+/**
+ * Der Styling-Beweis. Ohne ihn kann der Lauf gegen ein ungestyltes Dokument
+ * grün werden: axe prüft berechnete Stile, und ohne CSS gibt es weder
+ * Kontrastprobleme noch nur-farblich markierte Links.
+ */
+async function assertStylesheetsLoaded(
+ seiten: Map
+): Promise {
+ const probleme: string[] = [];
+
+ for (const path of PAGES) {
+ const hrefs = stylesheetHrefs(seiten.get(path) ?? "");
+ if (hrefs.length === 0) {
+ probleme.push(`${path}: kein im HTML`);
+ continue;
+ }
+
+ for (const href of hrefs) {
+ const cssUrl = new URL(href, `${BASE_URL}/`).toString();
+ let status = 0;
+ let laenge = 0;
+ try {
+ const res = await fetch(cssUrl, { redirect: "manual" });
+ status = res.status;
+ laenge = (await res.text()).length;
+ } catch (err) {
+ probleme.push(
+ `${path}: ${cssUrl} nicht abrufbar (${err instanceof Error ? err.message : String(err)})`
+ );
+ continue;
+ }
+ if (status !== 200 || laenge === 0) {
+ probleme.push(`${path}: ${cssUrl} → ${status}, ${laenge} Bytes`);
+ continue;
+ }
+ console.log(`css: ${path} → ${href} (200, ${laenge} Bytes)`);
+ }
+ }
+
+ if (probleme.length > 0) {
+ fail(
+ `Stylesheets nicht ausgeliefert:\n` +
+ probleme.map((p) => ` - ${p}`).join("\n") +
+ `\n\nDer Lauf würde ein ungestyltes Dokument prüfen. Rein visuelle Regeln ` +
+ `(color-contrast, link-in-text-block) hätten dann nichts zu finden und das ` +
+ `Gate wäre grün und wertlos. Wahrscheinlichste Ursache: der Server läuft ` +
+ `ohne die statischen Assets (bei .next/standalone müssen .next/static und ` +
+ `public danebenkopiert werden — siehe npm run start:standalone).`
+ );
+ }
+}
+
+async function main(): Promise {
+ console.log(`a11y-preflight gegen ${BASE_URL} (Tenant: ${TENANT_SLUG})`);
+ console.log(`geprüfte Seiten (aus .pa11yci.js): ${PAGES.join(", ")}`);
+ await waitForServer();
+ const seiten = await ladeSeiten();
+ assertTenantPagesVisible(seiten);
+ await assertStylesheetsLoaded(seiten);
+ console.log(
+ "\na11y-preflight bestanden — pa11y prüft echte, gestylte Tenant-Seiten.\n"
+ );
+}
+
+main().catch((err: unknown) => {
+ fail(err instanceof Error ? (err.stack ?? err.message) : String(err));
+});