diff --git a/docs/architecture/SYSTEM_ARCHITECTURE.md b/docs/architecture/SYSTEM_ARCHITECTURE.md index 5adc39df9..70a33adfd 100644 --- a/docs/architecture/SYSTEM_ARCHITECTURE.md +++ b/docs/architecture/SYSTEM_ARCHITECTURE.md @@ -9,18 +9,18 @@ This document describes the current (monolithic) architecture of the adblock-com ```mermaid flowchart TD %% ── Clients ────────────────────────────────────────────────────────────── - Browser["🌐 Browser"] - CLIUser["💻 CLI User\n(Deno CLI)"] - CICD["⚙️ CI/CD Pipeline"] - MCPAgent["🤖 AI Agent / MCP Client"] + Browser["Browser"] + CLIUser["CLI User\n(Deno CLI)"] + CICD["CI/CD Pipeline"] + MCPAgent["AI Agent / MCP Client"] %% ── Edge / Zero Trust perimeter ───────────────────────────────────────── - CFAccess["☁️ Cloudflare Access\n(Zero Trust / WAF)"] - CFTurnstile["🛡️ Cloudflare Turnstile\n(Human Verification)"] + CFAccess["Cloudflare Access\n(Zero Trust / WAF)"] + CFTurnstile["Cloudflare Turnstile\n(Human Verification)"] %% ── Angular Frontend (separate SSR Worker — adblock-frontend) ────── subgraph FrontendWorker["adblock-frontend (separate SSR Worker)"] - Frontend["📱 Angular 21 SSR SPA\n(AngularAppEngine)"] + Frontend["Angular 21 SSR SPA\n(AngularAppEngine)"] FrontendAssets["ASSETS binding\n(JS/CSS/fonts — CDN)"] FrontendAPI["[[services]] API binding\n(wired in server.ts — routes /api/* internally)"] end @@ -35,7 +35,7 @@ flowchart TD Handlers["handlers/\ncompile · admin · auth · metrics\nqueue · websocket · proxy"] Workflows["workflows/\nCompilation · Batch\nCacheWarming · HealthMonitoring"] MCPAgentWorker["mcp-agent.ts\n(Playwright / CF Browser Rendering)"] - BetterAuth["🔐 Better Auth\n(in-Worker · Neon / Hyperdrive)"] + BetterAuth["Better Auth\n(in-Worker · Neon / Hyperdrive)"] subgraph CoreLib["src/ (Core Library — inlined in monolith)"] Compiler["compiler/\nFilterCompiler · SourceCompiler\nIncrementalCompiler · WorkerCompiler"] @@ -80,15 +80,15 @@ flowchart TD %% ── External Services ──────────────────────────────────────────────────── subgraph ExternalServices["External Services"] - Sentry["🔍 Sentry\n(Errors · Tracing)"] - OTel["📊 OpenTelemetry\n(Spans · Exporters)"] - PostgreSQL["🐘 PostgreSQL\n(via Hyperdrive)"] - FilterSources["📋 Filter List Sources\n(EasyList · uBlock etc.)"] + Sentry["Sentry\n(Errors · Tracing)"] + OTel["OpenTelemetry\n(Spans · Exporters)"] + PostgreSQL["PostgreSQL\n(via Hyperdrive)"] + FilterSources["Filter List Sources\n(EasyList · uBlock etc.)"] end %% ── Auth Stack ─────────────────────────────────────────────────────────── - LocalJWT["🔐 Local HS256 JWT\n(dev mode)"] - APIKeys["🗝️ API Keys\n(PostgreSQL / Hyperdrive)"] + LocalJWT["Local HS256 JWT\n(dev mode)"] + APIKeys["API Keys\n(PostgreSQL / Hyperdrive)"] %% ── Connections ────────────────────────────────────────────────────────── Browser --> CFAccess @@ -131,14 +131,14 @@ flowchart TD TailWorker --> OTel %% ── Class Definitions ──────────────────────────────────────────────────── - classDef client fill:#4A90D9,stroke:#2C5F8A,color:#fff - classDef edge fill:#7B68EE,stroke:#4B3FA0,color:#fff - classDef worker fill:#E8A838,stroke:#B07820,color:#fff - classDef corelib fill:#F0C040,stroke:#C09010,color:#333 - classDef storage fill:#5BA85A,stroke:#3A7039,color:#fff - classDef observability fill:#D9534F,stroke:#A02B28,color:#fff - classDef auth fill:#9B59B6,stroke:#6C3483,color:#fff - classDef external fill:#7F8C8D,stroke:#555F60,color:#fff + classDef client fill:#1d6fbd,stroke:#0d4a8a,color:#fff + classDef edge fill:#6a1fa0,stroke:#4a1570,color:#fff + classDef worker fill:#b05a10,stroke:#7a3d08,color:#fff + classDef corelib fill:#b8860b,stroke:#8a6208,color:#fff + classDef storage fill:#2e7d32,stroke:#1a5421,color:#fff + classDef observability fill:#c62828,stroke:#8e1c1c,color:#fff + classDef auth fill:#37474f,stroke:#1a2327,color:#fff + classDef external fill:#37474f,stroke:#1a2327,color:#fff class Browser,CLIUser,CICD,MCPAgent client class CFAccess,CFTurnstile edge @@ -163,14 +163,14 @@ The current system is a **monolith**: every concern — compilation, transformat ```mermaid flowchart TD %% ── Clients ────────────────────────────────────────────────────────────── - Browser["🌐 Browser"] - CLIUser["💻 CLI User\n(Deno CLI)"] - CICD["⚙️ CI/CD Pipeline"] - MCPAgent["🤖 AI Agent / MCP Client"] + Browser["Browser"] + CLIUser["CLI User\n(Deno CLI)"] + CICD["CI/CD Pipeline"] + MCPAgent["AI Agent / MCP Client"] %% ── Edge / Zero Trust perimeter ───────────────────────────────────────── - CFAccess["☁️ Cloudflare Access\n(Zero Trust / WAF)"] - CFTurnstile["🛡️ Cloudflare Turnstile\n(Human Verification)"] + CFAccess["Cloudflare Access\n(Zero Trust / WAF)"] + CFTurnstile["Cloudflare Turnstile\n(Human Verification)"] %% ── Angular Frontend (served via API Worker STATIC_ASSETS binding) ───────────── subgraph FrontendApp["adblock-frontend (Worker STATIC_ASSETS binding)"] @@ -186,7 +186,7 @@ flowchart TD subgraph APIWorker["adblock-compiler-api (Cloudflare Worker — thin routing layer)"] HonoRouter["hono-app.ts\n(OpenAPIHono Router)"] APIHandlers["handlers/\ncompile · admin · auth\nmetrics · queue · websocket"] - BetterAuth["🔐 Better Auth\n(in-Worker · Neon / Hyperdrive)"] + BetterAuth["Better Auth\n(in-Worker · Neon / Hyperdrive)"] end %% ── Worker Service Bindings ────────────────────────────────────────────── @@ -222,15 +222,15 @@ flowchart TD %% ── External Services ──────────────────────────────────────────────────── subgraph ExternalServices["External Services"] - Sentry["🔍 Sentry\n(Errors · Tracing)"] - OTel["📊 OpenTelemetry\n(Spans · Exporters)"] - PostgreSQL["🐘 PostgreSQL\n(via Hyperdrive)"] - FilterSources["📋 Filter List Sources\n(EasyList · uBlock etc.)"] + Sentry["Sentry\n(Errors · Tracing)"] + OTel["OpenTelemetry\n(Spans · Exporters)"] + PostgreSQL["PostgreSQL\n(via Hyperdrive)"] + FilterSources["Filter List Sources\n(EasyList · uBlock etc.)"] end %% ── Auth Stack ─────────────────────────────────────────────────────────── - LocalJWT["🔐 Local HS256 JWT\n(dev mode)"] - APIKeys["🗝️ API Keys\n(PostgreSQL / Hyperdrive)"] + LocalJWT["Local HS256 JWT\n(dev mode)"] + APIKeys["API Keys\n(PostgreSQL / Hyperdrive)"] %% ── Connections ────────────────────────────────────────────────────────── Browser --> CFAccess @@ -282,14 +282,14 @@ flowchart TD APIWorker --> FilterSources %% ── Class Definitions ──────────────────────────────────────────────────── - classDef client fill:#4A90D9,stroke:#2C5F8A,color:#fff - classDef edge fill:#7B68EE,stroke:#4B3FA0,color:#fff - classDef worker fill:#E8A838,stroke:#B07820,color:#fff - classDef jsrpkg fill:#F0C040,stroke:#C09010,color:#333 - classDef storage fill:#5BA85A,stroke:#3A7039,color:#fff - classDef observability fill:#D9534F,stroke:#A02B28,color:#fff - classDef auth fill:#9B59B6,stroke:#6C3483,color:#fff - classDef external fill:#7F8C8D,stroke:#555F60,color:#fff + classDef client fill:#1d6fbd,stroke:#0d4a8a,color:#fff + classDef edge fill:#6a1fa0,stroke:#4a1570,color:#fff + classDef worker fill:#b05a10,stroke:#7a3d08,color:#fff + classDef jsrpkg fill:#b8860b,stroke:#8a6208,color:#fff + classDef storage fill:#2e7d32,stroke:#1a5421,color:#fff + classDef observability fill:#c62828,stroke:#8e1c1c,color:#fff + classDef auth fill:#37474f,stroke:#1a2327,color:#fff + classDef external fill:#37474f,stroke:#1a2327,color:#fff class Browser,CLIUser,CICD,MCPAgent client class CFAccess,CFTurnstile edge diff --git a/docs/auth/auth-chain-reference.md b/docs/auth/auth-chain-reference.md index 6648ea270..7b66687df 100644 --- a/docs/auth/auth-chain-reference.md +++ b/docs/auth/auth-chain-reference.md @@ -51,32 +51,32 @@ flowchart TD APIKEY --> HASH["SHA-256 hash token"] HASH --> LOOKUP["Query api_keys table
via Hyperdrive"] LOOKUP -->|"Found + valid"| TIER["Resolve owner tier
from users table"] - TIER --> AUTH_OK["✅ Authenticated
(api-key method)"] - LOOKUP -->|"Not found / expired / revoked"| REJECT["❌ 401 Rejected"] + TIER --> AUTH_OK["Authenticated
(api-key method)"] + LOOKUP -->|"Not found / expired / revoked"| REJECT["401 Rejected"] BA -->|"Valid session
(cookie or bearer)"| ZTA["Run Token Validators
(ZTA checks)"] - ZTA -->|"Pass"| BA_OK["✅ Authenticated
(better-auth method)"] - ZTA -->|"Fail"| REJECT2["❌ 401 Rejected"] + ZTA -->|"Pass"| BA_OK["Authenticated
(better-auth method)"] + ZTA -->|"Fail"| REJECT2["401 Rejected"] BA -->|"No credentials
(no error)"| CLERK_CHECK{"Clerk fallback
enabled?"} - BA -->|"Error
(bad token)"| REJECT3["❌ 401 Rejected"] + BA -->|"Error
(bad token)"| REJECT3["401 Rejected"] CLERK_CHECK -->|"Yes + JWT token"| CLERK["Clerk JWT
(Fallback Provider)"] - CLERK_CHECK -->|"No or not JWT"| ANON["👤 Anonymous
(10 req/min)"] + CLERK_CHECK -->|"No or not JWT"| ANON["Anonymous
(10 req/min)"] - CLERK -->|"Valid JWT"| CLERK_OK["✅ Authenticated
(clerk-jwt method)
⚠️ Deprecation warning logged"] - CLERK -->|"Invalid JWT"| REJECT4["❌ 401 Rejected"] + CLERK -->|"Valid JWT"| CLERK_OK["Authenticated
(clerk-jwt method)
Deprecation warning logged"] + CLERK -->|"Invalid JWT"| REJECT4["401 Rejected"] CLERK -->|"No credentials"| ANON - style REQ fill:#e8f4f8,stroke:#2196F3 - style AUTH_OK fill:#e8f5e9,stroke:#4CAF50 - style BA_OK fill:#e8f5e9,stroke:#4CAF50 - style CLERK_OK fill:#fff3e0,stroke:#FF9800 - style ANON fill:#f5f5f5,stroke:#9E9E9E - style REJECT fill:#ffebee,stroke:#F44336 - style REJECT2 fill:#ffebee,stroke:#F44336 - style REJECT3 fill:#ffebee,stroke:#F44336 - style REJECT4 fill:#ffebee,stroke:#F44336 + style REQ fill:#37474f,stroke:#263238,color:#fff + style AUTH_OK fill:#1b5e20,stroke:#0a3010,color:#fff + style BA_OK fill:#1b5e20,stroke:#0a3010,color:#fff + style CLERK_OK fill:#b84000,stroke:#7a2900,color:#fff + style ANON fill:#37474f,stroke:#263238,color:#fff + style REJECT fill:#c62828,stroke:#8e1c1c,color:#fff + style REJECT2 fill:#c62828,stroke:#8e1c1c,color:#fff + style REJECT3 fill:#c62828,stroke:#8e1c1c,color:#fff + style REJECT4 fill:#c62828,stroke:#8e1c1c,color:#fff ``` --- @@ -343,7 +343,7 @@ The Clerk → Better Auth migration follows a phased approach: gantt title Clerk → Better Auth Migration dateFormat YYYY-MM-DD - axisFormat %b %Y + axisFormat %Y-%m-%d section Phase 1: Foundation Prisma adapter + Better Auth setup :done, p1a, 2025-03-01, 14d diff --git a/docs/cloudflare/CLOUDFLARE_WORKFLOWS.md b/docs/cloudflare/CLOUDFLARE_WORKFLOWS.md index ca43fc02a..e6e347f93 100644 --- a/docs/cloudflare/CLOUDFLARE_WORKFLOWS.md +++ b/docs/cloudflare/CLOUDFLARE_WORKFLOWS.md @@ -614,12 +614,12 @@ flowchart TD Error --> Complete - style Validate fill:#e1f5ff - style Compile fill:#fff9c4 - style Cache fill:#c8e6c9 - style Metrics fill:#e1f5ff - style Complete fill:#4caf50 - style Error fill:#ffcdd2 + style Validate fill:#1a237e,color:#fff + style Compile fill:#b8860b,color:#fff + style Cache fill:#1b5e20,color:#fff + style Metrics fill:#1a237e,color:#fff + style Complete fill:#1b5e20,color:#fff + style Error fill:#c62828,color:#fff ``` ### Batch Workflow with Chunking @@ -648,11 +648,11 @@ flowchart TD Chunk2Done[Chunk 2 Complete] --> Metrics[Step: update-batch-metrics] Metrics --> Complete[Return Batch Result] - style ValidateBatch fill:#e1f5ff - style Chunk1 fill:#fff9c4 - style Chunk2 fill:#fff9c4 - style Metrics fill:#e1f5ff - style Complete fill:#4caf50 + style ValidateBatch fill:#1a237e,color:#fff + style Chunk1 fill:#b8860b,color:#fff + style Chunk2 fill:#b8860b,color:#fff + style Metrics fill:#1a237e,color:#fff + style Complete fill:#1b5e20,color:#fff ``` ### Health Monitoring Workflow @@ -673,14 +673,14 @@ flowchart TD Store --> Complete[Return Health Result] - style LoadHistory fill:#e1f5ff - style CheckSource1 fill:#fff9c4 - style CheckSource2 fill:#fff9c4 - style CheckSourceN fill:#fff9c4 - style Analyze fill:#ffe0b2 - style SendAlerts fill:#ffcdd2 - style Store fill:#c8e6c9 - style Complete fill:#4caf50 + style LoadHistory fill:#1a237e,color:#fff + style CheckSource1 fill:#b8860b,color:#fff + style CheckSource2 fill:#b8860b,color:#fff + style CheckSourceN fill:#b8860b,color:#fff + style Analyze fill:#b84000,color:#fff + style SendAlerts fill:#c62828,color:#fff + style Store fill:#1b5e20,color:#fff + style Complete fill:#1b5e20,color:#fff ``` --- diff --git a/docs/development/ARCHITECTURE.md b/docs/development/ARCHITECTURE.md index 32a0eec7f..27c37449a 100644 --- a/docs/development/ARCHITECTURE.md +++ b/docs/development/ARCHITECTURE.md @@ -51,15 +51,15 @@ The **adblock-compiler** is a *compiler-as-a-service* for adblock filter lists. ## System Context Diagram ```mermaid -graph TD - classDef external fill:#f0f4ff,stroke:#4a6cf7,color:#1e293b - classDef client fill:#ecfdf5,stroke:#10b981,color:#1e293b - classDef system fill:#fef9c3,stroke:#ca8a04,color:#1e293b - classDef infra fill:#fdf4ff,stroke:#a855f7,color:#1e293b - classDef observability fill:#fff7ed,stroke:#f97316,color:#1e293b - classDef auth fill:#fef2f2,stroke:#ef4444,color:#1e293b - - subgraph EW["🌐 External World"] +flowchart TD + classDef external fill:#37474f,stroke:#263238,color:#fff + classDef client fill:#1b5e20,stroke:#0a3010,color:#fff + classDef system fill:#b8860b,stroke:#8a6208,color:#fff + classDef infra fill:#1a237e,stroke:#0d1257,color:#fff + classDef observability fill:#b84000,stroke:#7a2900,color:#fff + classDef auth fill:#880e4f,stroke:#560930,color:#fff + + subgraph EW["External World"] FLS["Filter List Sources\n(EasyList, uBlock, AdGuard URLs)"]:::external WB["Browser\n(Angular 21 SPA/SSR)"]:::client AC["API Consumers\n(CI/CD, curl, scripts)"]:::client @@ -67,7 +67,7 @@ graph TD AIAG["AI Agent / MCP Client\n(GitHub Copilot, etc.)"]:::client end - subgraph ACS["⚙️ adblock-compiler System"] + subgraph ACS["adblock-compiler System"] CLI["CLI App\n(src/cli/)"]:::system ANGULAR["Angular Frontend\n(frontend/ — SSR + PWA)"]:::system CFW["Cloudflare Worker\n(worker/worker.ts + router.ts)"]:::system @@ -81,7 +81,7 @@ graph TD TW["Tail Worker\n(worker/tail.ts)"]:::observability end - subgraph CF["☁️ Cloudflare Platform"] + subgraph CF["Cloudflare Platform"] KV["KV Store\n(Cache, Rate Limit, Metrics)"]:::infra D1["D1 (SQLite)\n(Metadata, History, Auth)"]:::infra QQ["Queues\n(Std + High Priority)"]:::infra @@ -91,7 +91,7 @@ graph TD CFAC["Cloudflare Access\n(Zero Trust WAF)"]:::auth end - subgraph EXT["🔌 External Services"] + subgraph EXT["External Services"] CLERK["Clerk\n(Auth / User Mgmt)"]:::auth SENTRY["Sentry\n(Error Tracking)"]:::observability PROM["Prometheus / OTel\n(Metrics Scraping)"]:::observability @@ -135,7 +135,7 @@ Every compilation—CLI, library, or API—follows this pipeline: ```mermaid flowchart LR - classDef step fill:#eff6ff,stroke:#3b82f6,color:#1e293b + classDef step fill:#1a237e,stroke:#0d1257,color:#fff A["1. Config
Loading"]:::step --> B["2. Validate
(Zod)"]:::step B --> C["3. Download
Sources"]:::step C --> D["4. Per-Source
Transforms"]:::step @@ -197,8 +197,8 @@ The orchestration layer that drives the entire compilation process. ```mermaid flowchart TD - classDef core fill:#fef9c3,stroke:#ca8a04,color:#1e293b - classDef module fill:#f0fdf4,stroke:#22c55e,color:#1e293b + classDef core fill:#b8860b,stroke:#8a6208,color:#fff + classDef module fill:#1b5e20,stroke:#0a3010,color:#fff FC["FilterCompiler\n← Main entry point (has FS access)"]:::core FC -->|uses| SC["SourceCompiler"]:::module FC -->|uses| HG["HeaderGenerator"]:::module @@ -219,8 +219,8 @@ Enables the compiler to run in environments **without file system access** (brow ```mermaid flowchart TD - classDef core fill:#fef9c3,stroke:#ca8a04,color:#1e293b - classDef module fill:#f0fdf4,stroke:#22c55e,color:#1e293b + classDef core fill:#b8860b,stroke:#8a6208,color:#fff + classDef module fill:#1b5e20,stroke:#0a3010,color:#fff WC["WorkerCompiler\n← No FS access"]:::core WC -->|uses| CF["CompositeFetcher\n← Chain of Responsibility"]:::module CF --> PFCF["PreFetchedContentFetcher"]:::module @@ -242,8 +242,8 @@ The transformation pipeline uses the **Strategy** and **Registry** patterns. ```mermaid flowchart TD - classDef core fill:#fef9c3,stroke:#ca8a04,color:#1e293b - classDef module fill:#f0fdf4,stroke:#22c55e,color:#1e293b + classDef core fill:#b8860b,stroke:#8a6208,color:#fff + classDef module fill:#1b5e20,stroke:#0a3010,color:#fff TP["TransformationPipeline\n← Applies ordered transforms"]:::core TP -->|delegates to| TR["TransformationRegistry\n← Maps type → instance"]:::module TR -->|contains| ST1["SyncTransformation\n(Deduplicate)"]:::module @@ -285,8 +285,8 @@ Handles fetching filter list content with preprocessor directive support. ```mermaid flowchart TD - classDef core fill:#fef9c3,stroke:#ca8a04,color:#1e293b - classDef module fill:#f0fdf4,stroke:#22c55e,color:#1e293b + classDef core fill:#b8860b,stroke:#8a6208,color:#fff + classDef module fill:#1b5e20,stroke:#0a3010,color:#fff FD["FilterDownloader\n← Static download() method"]:::core FD -->|uses| CF["ContentFetcher\n(FS + HTTP)"]:::module FD -->|uses| PE["PreprocessorEvaluator\n(!#if, !#include)"]:::module @@ -326,10 +326,10 @@ Pluggable persistence layer with multiple backends. ```mermaid flowchart TD - classDef interface fill:#eff6ff,stroke:#3b82f6,color:#1e293b - classDef adapter fill:#f0fdf4,stroke:#22c55e,color:#1e293b - classDef consumer fill:#fef9c3,stroke:#ca8a04,color:#1e293b - classDef infra fill:#f1f5f9,stroke:#64748b,color:#1e293b + classDef interface fill:#1a237e,stroke:#0d1257,color:#fff + classDef adapter fill:#1b5e20,stroke:#0a3010,color:#fff + classDef consumer fill:#b8860b,stroke:#8a6208,color:#fff + classDef infra fill:#1a237e,stroke:#0d1257,color:#fff ISA["IStorageAdapter\n← Abstract interface"]:::interface ISA --> PSA["PrismaStorageAdapter\n(SQLite · PostgreSQL · MySQL)"]:::adapter @@ -377,9 +377,9 @@ Asynchronous job processing abstraction. ```mermaid flowchart TD - classDef interface fill:#eff6ff,stroke:#3b82f6,color:#1e293b - classDef provider fill:#f0fdf4,stroke:#22c55e,color:#1e293b - classDef message fill:#fef9c3,stroke:#ca8a04,color:#1e293b + classDef interface fill:#1a237e,stroke:#0d1257,color:#fff + classDef provider fill:#1b5e20,stroke:#0a3010,color:#fff + classDef message fill:#b8860b,stroke:#8a6208,color:#fff IQP["IQueueProvider\n← Abstract interface"]:::interface IQP --> CQP["CloudflareQueueProvider\n← Cloudflare Workers Queue binding"]:::provider @@ -395,9 +395,9 @@ End-to-end observability through the compilation pipeline. ```mermaid flowchart LR - classDef core fill:#fef9c3,stroke:#ca8a04,color:#1e293b - classDef module fill:#f0fdf4,stroke:#22c55e,color:#1e293b - classDef observability fill:#fff7ed,stroke:#f97316,color:#1e293b + classDef core fill:#b8860b,stroke:#8a6208,color:#fff + classDef module fill:#1b5e20,stroke:#0a3010,color:#fff + classDef observability fill:#b84000,stroke:#7a2900,color:#fff TC["TracingContext\n(correlation ID, parent spans)"]:::core DC["DiagnosticsCollector\n(event aggregation)"]:::core OTE["OpenTelemetryExporter\n(Datadog, Honeycomb, Jaeger, etc.)"]:::observability @@ -435,8 +435,8 @@ Extensibility system for custom transformations and downloaders. ```mermaid flowchart TD - classDef core fill:#fef9c3,stroke:#ca8a04,color:#1e293b - classDef module fill:#f0fdf4,stroke:#22c55e,color:#1e293b + classDef core fill:#b8860b,stroke:#8a6208,color:#fff + classDef module fill:#1b5e20,stroke:#0a3010,color:#fff PR["PluginRegistry\n← Global singleton"]:::core PR -->|registers| P["Plugin\n{manifest, transforms, downloaders}"]:::module P --> TPLG["TransformationPlugin"]:::module @@ -498,15 +498,15 @@ The edge deployment target that exposes the compiler as an HTTP/WebSocket API. ```mermaid flowchart TD - classDef entry fill:#fef9c3,stroke:#ca8a04,color:#1e293b - classDef router fill:#eff6ff,stroke:#3b82f6,color:#1e293b - classDef handler fill:#f0fdf4,stroke:#22c55e,color:#1e293b - classDef middleware fill:#fdf4ff,stroke:#a855f7,color:#1e293b - classDef infra fill:#f1f5f9,stroke:#64748b,color:#1e293b - classDef observability fill:#fff7ed,stroke:#f97316,color:#1e293b - classDef auth fill:#fef2f2,stroke:#ef4444,color:#1e293b + classDef entry fill:#b8860b,stroke:#8a6208,color:#fff + classDef router fill:#1a237e,stroke:#0d1257,color:#fff + classDef handler fill:#1b5e20,stroke:#0a3010,color:#fff + classDef middleware fill:#6a1fa0,stroke:#4a1570,color:#fff + classDef infra fill:#37474f,stroke:#1a2327,color:#fff + classDef observability fill:#b84000,stroke:#7a2900,color:#fff + classDef auth fill:#880e4f,stroke:#560930,color:#fff - REQ["🌐 Incoming Request\n(HTTP / WebSocket / Queue / Cron)"]:::entry + REQ["Incoming Request\n(HTTP / WebSocket / Queue / Cron)"]:::entry subgraph MW["Middleware Stack"] CFAC["CF Access JWT\nverification"]:::auth @@ -619,20 +619,20 @@ flowchart TD ```mermaid flowchart LR - classDef auth fill:#fef2f2,stroke:#ef4444,color:#1e293b - classDef security fill:#fdf4ff,stroke:#a855f7,color:#1e293b - classDef infra fill:#f1f5f9,stroke:#64748b,color:#1e293b - classDef handler fill:#f0fdf4,stroke:#22c55e,color:#1e293b + classDef auth fill:#880e4f,stroke:#560930,color:#fff + classDef security fill:#6a1fa0,stroke:#4a1570,color:#fff + classDef infra fill:#37474f,stroke:#1a2327,color:#fff + classDef handler fill:#1b5e20,stroke:#0a3010,color:#fff - REQ["📥 Request"]:::infra + REQ["Request"]:::infra CFAC["CF Access JWT\n(Zero Trust)"]:::auth CJW["Clerk JWT\n(user auth)"]:::auth RL["Rate Limit\n(tiered: anon/free/pro/admin)"]:::security TS["Turnstile\n(CAPTCHA, public endpoints)"]:::security BS["Body Size\n(1MB max)"]:::infra AA["Admin Auth\n(X-Admin-Key, admin routes)"]:::auth - H["🎯 Handler"]:::handler - RESP["📤 Response"]:::infra + H["Handler"]:::handler + RESP["Response"]:::infra REQ --> CFAC CFAC --> CJW @@ -707,8 +707,8 @@ See [`frontend/ANGULAR_SIGNALS.md`](../../frontend/ANGULAR_SIGNALS.md) for Angul ```mermaid flowchart TD - classDef base fill:#eff6ff,stroke:#3b82f6,color:#1e293b - classDef error fill:#fef2f2,stroke:#ef4444,color:#1e293b + classDef base fill:#1a237e,stroke:#0d1257,color:#fff + classDef error fill:#c62828,stroke:#8e1c1c,color:#fff BE["BaseError (abstract)"]:::base BE --> CE["CompilationError\n— Compilation pipeline failures"]:::error BE --> NE["NetworkError\n— HTTP/connection failures"]:::error @@ -726,8 +726,8 @@ The `ICompilerEvents` interface provides lifecycle hooks: ```mermaid flowchart TD - classDef event fill:#fef9c3,stroke:#ca8a04,color:#1e293b - classDef hook fill:#f0fdf4,stroke:#22c55e,color:#1e293b + classDef event fill:#b8860b,stroke:#8a6208,color:#fff + classDef hook fill:#1b5e20,stroke:#0a3010,color:#fff CS["Compilation Start"]:::event CS --> OSS["onSourceStart\n(per source)"]:::hook CS --> OSC["onSourceComplete\n(per source, with rule count & duration)"]:::hook @@ -766,9 +766,9 @@ Both implement `ILogger` (extends `IDetailedLogger`): `info()`, `warn()`, `error ```mermaid flowchart LR - classDef input fill:#ecfdf5,stroke:#10b981,color:#1e293b - classDef core fill:#fef9c3,stroke:#ca8a04,color:#1e293b - classDef output fill:#eff6ff,stroke:#3b82f6,color:#1e293b + classDef input fill:#1b5e20,stroke:#0a3010,color:#fff + classDef core fill:#b8860b,stroke:#8a6208,color:#fff + classDef output fill:#1a237e,stroke:#0d1257,color:#fff CFG["config.json"]:::input --> CL["ConfigurationLoader"]:::core FS["Filter Sources\n(HTTP/FS)"]:::input --> FC CL --> FC["FilterCompiler"]:::core @@ -781,13 +781,13 @@ flowchart LR ```mermaid sequenceDiagram - box rgb(236,253,245) Client + box Client participant Client end - box rgb(254,249,195) Worker + box Worker participant Worker end - box rgb(240,244,255) Sources + box Sources participant Sources end @@ -807,16 +807,16 @@ sequenceDiagram ```mermaid sequenceDiagram - box rgb(236,253,245) Client + box Client participant Client end - box rgb(254,249,195) Worker + box Worker participant Worker end - box rgb(253,244,255) Queue + box Queue participant Queue end - box rgb(240,253,244) Consumer + box Consumer participant Consumer end @@ -836,23 +836,23 @@ sequenceDiagram ## Deployment Architecture ```mermaid -graph TD - classDef client fill:#ecfdf5,stroke:#10b981,color:#1e293b - classDef edge fill:#fef9c3,stroke:#ca8a04,color:#1e293b - classDef worker fill:#eff6ff,stroke:#3b82f6,color:#1e293b - classDef storage fill:#fdf4ff,stroke:#a855f7,color:#1e293b - classDef observability fill:#fff7ed,stroke:#f97316,color:#1e293b - classDef auth fill:#fef2f2,stroke:#ef4444,color:#1e293b - classDef external fill:#f1f5f9,stroke:#64748b,color:#1e293b - - subgraph CLIENTS["👥 Clients"] +flowchart TD + classDef client fill:#1b5e20,stroke:#0a3010,color:#fff + classDef edge fill:#b8860b,stroke:#8a6208,color:#fff + classDef worker fill:#1a237e,stroke:#0d1257,color:#fff + classDef storage fill:#6a1fa0,stroke:#4a1570,color:#fff + classDef observability fill:#b84000,stroke:#7a2900,color:#fff + classDef auth fill:#880e4f,stroke:#560930,color:#fff + classDef external fill:#37474f,stroke:#1a2327,color:#fff + + subgraph CLIENTS["Clients"] BROWSER["Browser\n(Angular 21 SSR/PWA)"]:::client CICD["CI/CD Systems\n(GitHub Actions)"]:::client CLIUSER["CLI User\n(Deno)"]:::client AICLIENT["AI Agent\n(MCP Client)"]:::client end - subgraph CFN["☁️ Cloudflare Edge Network"] + subgraph CFN["Cloudflare Edge Network"] CFAC["Cloudflare Access\n(Zero Trust WAF)"]:::auth CFPAGES["Cloudflare Pages\n(Angular SSR)"]:::edge @@ -876,7 +876,7 @@ graph TD BR["Browser Rendering\n(Playwright)"]:::edge end - subgraph EXTERNAL["🌍 External Services"] + subgraph EXTERNAL["External Services"] FLS["Filter List Sources\n(EasyList · uBlock · AdGuard)"]:::external CLERK["Clerk\n(Auth & User Mgmt)"]:::auth SENTRY["Sentry\n(Error Tracking)"]:::observability diff --git a/docs/workflows/WORKFLOW_DIAGRAMS.md b/docs/workflows/WORKFLOW_DIAGRAMS.md index 9f3231038..755aad40d 100644 --- a/docs/workflows/WORKFLOW_DIAGRAMS.md +++ b/docs/workflows/WORKFLOW_DIAGRAMS.md @@ -141,14 +141,14 @@ flowchart TB BCW --> KV_METRICS HMW --> D1 - style CW fill:#e1f5ff,stroke:#0288d1 - style BCW fill:#e1f5ff,stroke:#0288d1 - style CWW fill:#e1f5ff,stroke:#0288d1 - style HMW fill:#e1f5ff,stroke:#0288d1 - style STD_Q fill:#c8e6c9,stroke:#388e3c - style HIGH_Q fill:#fff9c4,stroke:#fbc02d - style DLQ fill:#ffcdd2,stroke:#d32f2f - style KV_CACHE fill:#e1bee7,stroke:#7b1fa2 + style CW fill:#1a237e,color:#fff,stroke:#0288d1 + style BCW fill:#1a237e,color:#fff,stroke:#0288d1 + style CWW fill:#1a237e,color:#fff,stroke:#0288d1 + style HMW fill:#1a237e,color:#fff,stroke:#0288d1 + style STD_Q fill:#1b5e20,color:#fff,stroke:#388e3c + style HIGH_Q fill:#b8860b,color:#fff,stroke:#fbc02d + style DLQ fill:#c62828,color:#fff,stroke:#d32f2f + style KV_CACHE fill:#6a1fa0,color:#fff,stroke:#7b1fa2 ``` ### Processing Path Comparison @@ -234,10 +234,10 @@ flowchart TB AE --> KV_METRICS KV_METRICS --> METRICS_API - style COMP_WF fill:#e3f2fd,stroke:#1976d2 - style BATCH_WF fill:#e8f5e9,stroke:#388e3c - style CACHE_WF fill:#fff8e1,stroke:#f57c00 - style HEALTH_WF fill:#fce4ec,stroke:#c2185b + style COMP_WF fill:#1a237e,color:#fff,stroke:#1976d2 + style BATCH_WF fill:#1b5e20,color:#fff,stroke:#388e3c + style CACHE_WF fill:#b8860b,color:#fff,stroke:#f57c00 + style HEALTH_WF fill:#880e4f,color:#fff,stroke:#c2185b ``` ### CompilationWorkflow @@ -300,12 +300,12 @@ flowchart TD RETURN_ERROR --> END([Workflow End]) RETURN_SUCCESS --> END - style V_START fill:#e3f2fd - style C_START fill:#fff8e1 - style CACHE_START fill:#e8f5e9 - style M_START fill:#f3e5f5 - style RETURN_SUCCESS fill:#c8e6c9 - style RETURN_ERROR fill:#ffcdd2 + style V_START fill:#1a237e,color:#fff + style C_START fill:#b8860b,color:#fff + style CACHE_START fill:#1b5e20,color:#fff + style M_START fill:#6a1fa0,color:#fff + style RETURN_SUCCESS fill:#1b5e20,color:#fff + style RETURN_ERROR fill:#c62828,color:#fff ``` **Retry Configuration:** @@ -375,8 +375,7 @@ flowchart TD C1_SETTLE --> C1_DONE[Emit: workflow:step:completed
chunkSuccess, chunkFailed] C1_DONE --> CHUNK2{More Chunks?} - CHUNK2 -->|Yes| NEXT_CHUNK[Process Next Chunk] - NEXT_CHUNK --> C1_START + CHUNK2 -->|Yes| C1_START CHUNK2 -->|No| METRICS_STEP end @@ -390,16 +389,16 @@ flowchart TD BATCH_ERROR --> END([Workflow End]) RETURN --> END - style CHUNK1 fill:#e3f2fd - style C1_P1 fill:#fff8e1 - style C1_P2 fill:#fff8e1 - style C1_P3 fill:#fff8e1 - style C1_S1 fill:#c8e6c9 - style C1_S2 fill:#c8e6c9 - style C1_S3 fill:#c8e6c9 - style C1_F1 fill:#ffcdd2 - style C1_F2 fill:#ffcdd2 - style C1_F3 fill:#ffcdd2 + style CHUNK1 fill:#1a237e,color:#fff + style C1_P1 fill:#b8860b,color:#fff + style C1_P2 fill:#b8860b,color:#fff + style C1_P3 fill:#b8860b,color:#fff + style C1_S1 fill:#1b5e20,color:#fff + style C1_S2 fill:#1b5e20,color:#fff + style C1_S3 fill:#1b5e20,color:#fff + style C1_F1 fill:#c62828,color:#fff + style C1_F2 fill:#c62828,color:#fff + style C1_F3 fill:#c62828,color:#fff ``` **Crash Recovery Scenario:** @@ -501,11 +500,11 @@ flowchart TD FINAL_EMIT --> RESULT[Return Warming Result] RESULT --> END([End]) - style CRON fill:#fff9c4,stroke:#f57c00 - style DEFAULT fill:#e8f5e9 - style CHUNK1 fill:#e3f2fd - style W1_WAIT1 fill:#f5f5f5 - style CHUNK_WAIT fill:#f5f5f5 + style CRON fill:#b8860b,color:#fff,stroke:#f57c00 + style DEFAULT fill:#1b5e20,color:#fff + style CHUNK1 fill:#1a237e,color:#fff + style W1_WAIT1 fill:#37474f,color:#fff + style CHUNK_WAIT fill:#37474f,color:#fff ``` **Warming Schedule:** @@ -626,12 +625,12 @@ flowchart TD EMIT_COMPLETE --> RETURN[Return Health Report] RETURN --> END([End]) - style CRON fill:#fff9c4 - style MARK_HEALTHY fill:#c8e6c9 - style MARK_UNHEALTHY fill:#ffcdd2 - style MARK_SLOW fill:#ffcdd2 - style MARK_LOW fill:#ffcdd2 - style NEED_ALERT fill:#ffcdd2 + style CRON fill:#b8860b,color:#fff + style MARK_HEALTHY fill:#1b5e20,color:#fff + style MARK_UNHEALTHY fill:#c62828,color:#fff + style MARK_SLOW fill:#c62828,color:#fff + style MARK_LOW fill:#c62828,color:#fff + style NEED_ALERT fill:#c62828,color:#fff ``` **Health Check Response Structure:** @@ -722,9 +721,9 @@ flowchart LR API --> CLIENT[Client Polling] end - style E6 fill:#c8e6c9 - style E7 fill:#ffcdd2 - style E4 fill:#ffcdd2 + style E6 fill:#1b5e20,color:#fff + style E7 fill:#c62828,color:#fff + style E4 fill:#c62828,color:#fff ``` **Event Polling Sequence:** @@ -901,16 +900,16 @@ flowchart TD LogBatchStats --> End[End Queue Processing] - style ProcessCompile fill:#e1f5ff - style ProcessBatch fill:#e1f5ff - style ProcessWarm fill:#e1f5ff - style AckCompile fill:#c8e6c9 - style AckBatch fill:#c8e6c9 - style AckWarm fill:#c8e6c9 - style AckUnknown fill:#fff9c4 - style RetryCompile fill:#ffcdd2 - style RetryBatch fill:#ffcdd2 - style RetryWarm fill:#ffcdd2 + style ProcessCompile fill:#1a237e,color:#fff + style ProcessBatch fill:#1a237e,color:#fff + style ProcessWarm fill:#1a237e,color:#fff + style AckCompile fill:#1b5e20,color:#fff + style AckBatch fill:#1b5e20,color:#fff + style AckWarm fill:#1b5e20,color:#fff + style AckUnknown fill:#b8860b,color:#fff + style RetryCompile fill:#c62828,color:#fff + style RetryBatch fill:#c62828,color:#fff + style RetryWarm fill:#c62828,color:#fff ``` ### Priority Queue Routing @@ -947,11 +946,11 @@ flowchart LR Process --> Result[Compilation Complete] - style HighQueue fill:#ff9800 - style StandardQueue fill:#4caf50 - style HighConsumer fill:#ffe0b2 - style StandardConsumer fill:#c8e6c9 - style Result fill:#e1f5ff + style HighQueue fill:#b84000,color:#fff + style StandardQueue fill:#1b5e20,color:#fff + style HighConsumer fill:#b84000,color:#fff + style StandardConsumer fill:#1b5e20,color:#fff + style Result fill:#1a237e,color:#fff ``` ### Batch Processing Flow @@ -1016,18 +1015,18 @@ flowchart TD RetryBatch --> End[End] AckBatch --> End - style ParallelExec fill:#bbdefb - style Compile1 fill:#e1f5ff - style Compile2 fill:#e1f5ff - style Compile3 fill:#e1f5ff - style Success1 fill:#c8e6c9 - style Success2 fill:#c8e6c9 - style Success3 fill:#c8e6c9 - style Fail1 fill:#ffcdd2 - style Fail2 fill:#ffcdd2 - style Fail3 fill:#ffcdd2 - style ThrowError fill:#f44336 - style AckBatch fill:#4caf50 + style ParallelExec fill:#1a237e,color:#fff + style Compile1 fill:#1a237e,color:#fff + style Compile2 fill:#1a237e,color:#fff + style Compile3 fill:#1a237e,color:#fff + style Success1 fill:#1b5e20,color:#fff + style Success2 fill:#1b5e20,color:#fff + style Success3 fill:#1b5e20,color:#fff + style Fail1 fill:#c62828,color:#fff + style Fail2 fill:#c62828,color:#fff + style Fail3 fill:#c62828,color:#fff + style ThrowError fill:#c62828,color:#fff + style AckBatch fill:#1b5e20,color:#fff ``` ### Cache Warming Flow @@ -1094,19 +1093,19 @@ flowchart TD Success --> End[End] Retry --> End - style Process1A fill:#e1f5ff - style Process1B fill:#e1f5ff - style Process1C fill:#e1f5ff - style Cache1A fill:#fff9c4 - style Cache1B fill:#fff9c4 - style Cache1C fill:#fff9c4 - style Inc1A fill:#c8e6c9 - style Inc1B fill:#c8e6c9 - style Inc1C fill:#c8e6c9 - style Fail1A fill:#ffcdd2 - style Fail1B fill:#ffcdd2 - style Fail1C fill:#ffcdd2 - style Success fill:#4caf50 + style Process1A fill:#1a237e,color:#fff + style Process1B fill:#1a237e,color:#fff + style Process1C fill:#1a237e,color:#fff + style Cache1A fill:#b8860b,color:#fff + style Cache1B fill:#b8860b,color:#fff + style Cache1C fill:#b8860b,color:#fff + style Inc1A fill:#1b5e20,color:#fff + style Inc1B fill:#1b5e20,color:#fff + style Inc1C fill:#1b5e20,color:#fff + style Fail1A fill:#c62828,color:#fff + style Fail1B fill:#c62828,color:#fff + style Fail1C fill:#c62828,color:#fff + style Success fill:#1b5e20,color:#fff ``` --- @@ -1179,13 +1178,13 @@ flowchart TD ReturnResult --> End[End] ThrowError --> End - style ParallelSources fill:#bbdefb - style Source1 fill:#e1f5ff - style Source2 fill:#e1f5ff - style Source3 fill:#e1f5ff - style ApplyTransforms fill:#fff9c4 - style ReturnResult fill:#c8e6c9 - style ThrowError fill:#ffcdd2 + style ParallelSources fill:#1a237e,color:#fff + style Source1 fill:#1a237e,color:#fff + style Source2 fill:#1a237e,color:#fff + style Source3 fill:#1a237e,color:#fff + style ApplyTransforms fill:#b8860b,color:#fff + style ReturnResult fill:#1b5e20,color:#fff + style ThrowError fill:#c62828,color:#fff ``` ### Source Compilation @@ -1270,17 +1269,17 @@ flowchart TD T11 --> OUTPUT[Transformed Rules Array] end - style T1 fill:#e3f2fd - style T2 fill:#e3f2fd - style T3 fill:#e3f2fd - style T4 fill:#fff8e1 - style T5 fill:#fff8e1 - style T6 fill:#fff8e1 - style T7 fill:#fce4ec - style T8 fill:#fce4ec - style T9 fill:#e8f5e9 - style T10 fill:#e8f5e9 - style T11 fill:#e8f5e9 + style T1 fill:#1a237e,color:#fff + style T2 fill:#1a237e,color:#fff + style T3 fill:#1a237e,color:#fff + style T4 fill:#b8860b,color:#fff + style T5 fill:#b8860b,color:#fff + style T6 fill:#b8860b,color:#fff + style T7 fill:#880e4f,color:#fff + style T8 fill:#880e4f,color:#fff + style T9 fill:#1b5e20,color:#fff + style T10 fill:#1b5e20,color:#fff + style T11 fill:#1b5e20,color:#fff ``` **Transformation Details:** @@ -1354,9 +1353,9 @@ flowchart TD SLOWER --> SET end - style PLAIN fill:#c8e6c9 - style REGEX fill:#fff9c4 - style SET fill:#e1f5ff + style PLAIN fill:#1b5e20,color:#fff + style REGEX fill:#b8860b,color:#fff + style SET fill:#1a237e,color:#fff ``` ### Request Deduplication @@ -1400,11 +1399,11 @@ flowchart TD ReturnResult --> End[End] ReturnCached --> End - style CheckPending fill:#fff9c4 - style WaitPending fill:#ffe0b2 - style AddPending fill:#e1f5ff - style ReturnCached fill:#c8e6c9 - style ReturnResult fill:#c8e6c9 + style CheckPending fill:#b8860b,color:#fff + style WaitPending fill:#b84000,color:#fff + style AddPending fill:#1a237e,color:#fff + style ReturnCached fill:#1b5e20,color:#fff + style ReturnResult fill:#1b5e20,color:#fff ``` --- @@ -1445,9 +1444,9 @@ flowchart TD AllowRequest --> End[End] DenyRequest --> End - style AllowRequest fill:#c8e6c9 - style DenyRequest fill:#ffcdd2 - style StartWindow fill:#e1f5ff + style AllowRequest fill:#1b5e20,color:#fff + style DenyRequest fill:#c62828,color:#fff + style StartWindow fill:#1a237e,color:#fff ``` ### Caching Strategy @@ -1480,10 +1479,10 @@ flowchart LR LogCache -.->|Later Request| Request - style Compress fill:#fff9c4 - style StoreKV fill:#e1f5ff - style ReturnCached fill:#c8e6c9 - style CacheMiss fill:#ffcdd2 + style Compress fill:#b8860b,color:#fff + style StoreKV fill:#1a237e,color:#fff + style ReturnCached fill:#1b5e20,color:#fff + style CacheMiss fill:#c62828,color:#fff ``` ### Error Handling & Retry @@ -1589,9 +1588,9 @@ flowchart TD MetricsKV --> Dashboard[Cloudflare Dashboard] TailWorker --> ExternalMonitoring[External Monitoring
Datadog, Splunk, etc.] - style MetricsKV fill:#e1f5ff - style Logs fill:#fff9c4 - style TailWorker fill:#ffe0b2 + style MetricsKV fill:#1a237e,color:#fff + style Logs fill:#b8860b,color:#fff + style TailWorker fill:#b84000,color:#fff ``` --- diff --git a/frontend/src/app/app.config.ts b/frontend/src/app/app.config.ts index 514695fcc..2c0b9eb78 100644 --- a/frontend/src/app/app.config.ts +++ b/frontend/src/app/app.config.ts @@ -21,10 +21,10 @@ */ import { ApplicationConfig, ErrorHandler, PLATFORM_ID, provideAppInitializer, provideZonelessChangeDetection, inject } from '@angular/core'; -import { provideRouter, withComponentInputBinding, withViewTransitions, withPreloading, PreloadAllModules, TitleStrategy } from '@angular/router'; +import { provideRouter, withComponentInputBinding, withViewTransitions, withPreloading, PreloadAllModules, TitleStrategy, withInMemoryScrolling } from '@angular/router'; import { HttpClient, provideHttpClient, withFetch, withInterceptors } from '@angular/common/http'; import { firstValueFrom, timeout } from 'rxjs'; -import { isPlatformBrowser } from '@angular/common'; +import { isPlatformBrowser, ViewportScroller } from '@angular/common'; import { authInterceptor } from './interceptors/auth.interceptor'; import { errorInterceptor } from './interceptors/error.interceptor'; import { provideAnimationsAsync } from '@angular/platform-browser/animations/async'; @@ -45,11 +45,18 @@ export const appConfig: ApplicationConfig = { // Router: map route params to component inputs, smooth View Transitions API, // preload all lazy routes after initial navigation completes. + // withInMemoryScrolling restores scroll position on back/forward navigation + // and enables anchor (#section) scrolling. The 72px scroll offset for the + // fixed header is applied via ViewportScroller.setOffset() in provideAppInitializer. provideRouter( routes, withComponentInputBinding(), withViewTransitions(), withPreloading(PreloadAllModules), + withInMemoryScrolling({ + scrollPositionRestoration: 'enabled', + anchorScrolling: 'enabled', + }), ), // Custom TitleStrategy: appends "| Adblock Compiler" to each route title @@ -89,6 +96,8 @@ export const appConfig: ApplicationConfig = { // still allows the app to boot with Turnstile simply disabled. provideAppInitializer(async () => { inject(MatIconRegistry).setDefaultFontSetClass('material-symbols-outlined'); + // Offset anchor/in-memory scroll restoration by 72px to clear the fixed header. + inject(ViewportScroller).setOffset([0, 72]); // isPlatformBrowser check MUST come before any call that touches browser-only // APIs (localStorage, fetch, Clerk, Sentry). ThemeService.loadPreferences() diff --git a/frontend/src/app/compiler/compiler.component.ts b/frontend/src/app/compiler/compiler.component.ts index 4659a6b40..69e788a44 100644 --- a/frontend/src/app/compiler/compiler.component.ts +++ b/frontend/src/app/compiler/compiler.component.ts @@ -929,11 +929,15 @@ export class CompilerComponent { selectedTransformations, ); if (urlEntries[0]?.source) { - this.router.navigate([], { - relativeTo: this.route, - queryParams: { url: urlEntries[0].source }, - queryParamsHandling: 'merge', - }); + const currentUrl = this.route.snapshot.queryParamMap.get('url'); + if (currentUrl !== urlEntries[0].source) { + this.router.navigate([], { + relativeTo: this.route, + queryParams: { url: urlEntries[0].source }, + queryParamsHandling: 'merge', + replaceUrl: true, + }); + } } return; } @@ -981,11 +985,15 @@ export class CompilerComponent { } if (urlEntries[0]?.source) { - this.router.navigate([], { - relativeTo: this.route, - queryParams: { url: urlEntries[0].source }, - queryParamsHandling: 'merge', - }); + const currentUrl = this.route.snapshot.queryParamMap.get('url'); + if (currentUrl !== urlEntries[0].source) { + this.router.navigate([], { + relativeTo: this.route, + queryParams: { url: urlEntries[0].source }, + queryParamsHandling: 'merge', + replaceUrl: true, + }); + } } }