{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "rate-limiting-config",
  "title": "Rate Limiting Config",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/rate-limiting-config/rate-limiting-config.component.ts",
      "content": "// Cloudflare/Upstash style API rate limiting rules builder and token bucket traffic monitor\n// with 4 health metrics cards, an interactive token bucket vs sliding window simulation\n// slider, and a configured mitigation rules table. Port of the React block 1:1.\nimport { ChangeDetectionStrategy, Component, Input, computed, signal } from '@angular/core'\nimport {\n  Activity,\n  AlertTriangle,\n  CheckCircle2,\n  Copy,\n  Database,\n  Edit3,\n  LucideAngularModule,\n  MoreHorizontal,\n  Plus,\n  RefreshCw,\n  Search,\n  Shield,\n  ShieldAlert,\n  ShieldCheck,\n  Sliders,\n  Timer,\n  Trash2,\n  TrendingUp,\n  Zap,\n  ZapOff,\n} from 'lucide-angular'\nimport { cn } from '@/lib/utils'\nimport { UiBadgeComponent } from '@/ui/badge/badge.component'\nimport { UiButtonComponent } from '@/ui/button/button.component'\nimport {\n  UiCardComponent,\n  UiCardContentComponent,\n  UiCardDescriptionComponent,\n  UiCardHeaderComponent,\n  UiCardTitleComponent,\n} from '@/ui/card/card.component'\nimport {\n  UiDropdownMenuComponent,\n  UiDropdownMenuContentComponent,\n  UiDropdownMenuItemComponent,\n  UiDropdownMenuLabelComponent,\n  UiDropdownMenuSeparatorComponent,\n  UiDropdownMenuTriggerComponent,\n} from '@/ui/dropdown-menu/dropdown-menu.component'\nimport { UiProgressComponent } from '@/ui/progress/progress.component'\nimport {\n  UiSelectComponent,\n  UiSelectContentComponent,\n  UiSelectItemComponent,\n  UiSelectTriggerComponent,\n  UiSelectValueComponent,\n} from '@/ui/select/select.component'\nimport { UiSliderComponent } from '@/ui/slider/slider.component'\nimport { UiSwitchComponent } from '@/ui/switch/switch.component'\nimport {\n  UiTableBodyComponent,\n  UiTableCellComponent,\n  UiTableComponent,\n  UiTableHeadComponent,\n  UiTableHeaderComponent,\n  UiTableRowComponent,\n} from '@/ui/table/table.component'\n\nexport interface RateLimitRule {\n  id: string\n  name: string\n  scope: string\n  path: string\n  method: string\n  limitThreshold: string\n  mitigationAction: 'HTTP 429 Too Many Requests' | 'CAPTCHA Challenge' | 'Block IP'\n  actionVariant: 'warning' | 'secondary' | 'destructive'\n  enabled: boolean\n  lastTriggered: string\n  blocked24h: string\n}\n\nexport type RateLimitAlgorithm = 'token-bucket' | 'sliding-window'\n\nexport interface SimStats {\n  allowed: number\n  dropped: number\n  allowedPct: number\n  droppedPct: number\n  bucketFill: number\n  status: 'optimal' | 'burst' | 'throttling'\n  statusLabel: string\n  latency: string\n  description: string\n}\n\nexport const INITIAL_RULES: RateLimitRule[] = [\n  {\n    id: 'rule-auth-bruteforce',\n    name: 'auth-endpoint-bruteforce',\n    scope: 'Login & SSO token issuance',\n    path: '/api/v1/auth/*',\n    method: 'POST',\n    limitThreshold: '100 req / 60s per IP',\n    mitigationAction: 'HTTP 429 Too Many Requests',\n    actionVariant: 'warning',\n    enabled: true,\n    lastTriggered: '2 mins ago',\n    blocked24h: '842 blocks',\n  },\n  {\n    id: 'rule-public-unauth',\n    name: 'public-api-v1-unauthenticated',\n    scope: 'Anonymous REST endpoints',\n    path: '/api/v1/public/*',\n    method: 'GET, POST',\n    limitThreshold: '300 req / 60s per IP',\n    mitigationAction: 'CAPTCHA Challenge',\n    actionVariant: 'secondary',\n    enabled: true,\n    lastTriggered: '14 mins ago',\n    blocked24h: '412 challenges',\n  },\n  {\n    id: 'rule-ai-inference',\n    name: 'ai-inference-tier-standard',\n    scope: 'LLM completions & embeddings',\n    path: '/api/v1/models/generate',\n    method: 'POST',\n    limitThreshold: '20 req / 60s per API Key',\n    mitigationAction: 'HTTP 429 Too Many Requests',\n    actionVariant: 'warning',\n    enabled: true,\n    lastTriggered: '1 hour ago',\n    blocked24h: '148 throttled',\n  },\n  {\n    id: 'rule-search-cluster',\n    name: 'search-endpoints',\n    scope: 'Vector & fuzzy full-text query',\n    path: '/api/v1/search/*',\n    method: 'GET',\n    limitThreshold: '60 req / 60s per IP',\n    mitigationAction: 'Block IP',\n    actionVariant: 'destructive',\n    enabled: false,\n    lastTriggered: '3 days ago',\n    blocked24h: '18 blocks',\n  },\n]\n\nexport function computeSimStats(algorithm: RateLimitAlgorithm, vol: number): SimStats {\n  if (algorithm === 'token-bucket') {\n    const refillRate = 50\n    const burstCapacity = 100\n    if (vol <= refillRate) {\n      return {\n        allowed: vol,\n        dropped: 0,\n        allowedPct: 100,\n        droppedPct: 0,\n        bucketFill: Math.round(100 - (vol / refillRate) * 20),\n        status: 'optimal',\n        statusLabel: 'Optimal Throughput · No Throttling',\n        latency: '0.35ms',\n        description: `Traffic volume (${vol} req/s) is well within the 50 req/s continuous refill capacity. 100% of requests pass with zero drop rate.`,\n      }\n    } else if (vol <= burstCapacity) {\n      return {\n        allowed: vol,\n        dropped: 0,\n        allowedPct: 100,\n        droppedPct: 0,\n        bucketFill: Math.max(10, Math.round(100 - ((vol - refillRate) / (burstCapacity - refillRate)) * 80)),\n        status: 'burst',\n        statusLabel: 'Consuming Burst Pool · 0% Dropped',\n        latency: '0.42ms',\n        description: `Traffic exceeds the 50 req/s replenishment rate. Tokens are being drawn from the 100-token burst reservoir. Zero requests dropped so far.`,\n      }\n    } else {\n      const allowed = burstCapacity\n      const dropped = vol - allowed\n      const allowedPct = Math.round((allowed / vol) * 100)\n      return {\n        allowed,\n        dropped,\n        allowedPct,\n        droppedPct: 100 - allowedPct,\n        bucketFill: 0,\n        status: 'throttling',\n        statusLabel: `Rate Limiting Active · ${100 - allowedPct}% Dropped`,\n        latency: '0.51ms',\n        description: `Burst reservoir exhausted. Token bucket limiter is actively enforcing HTTP 429 throttling on ${dropped} req/s excess traffic.`,\n      }\n    }\n  }\n  const windowLimit = 120\n  if (vol <= windowLimit) {\n    return {\n      allowed: vol,\n      dropped: 0,\n      allowedPct: 100,\n      droppedPct: 0,\n      bucketFill: Math.round((vol / windowLimit) * 100),\n      status: 'optimal',\n      statusLabel: 'Within Sliding Window Quota',\n      latency: '0.38ms',\n      description: `Current 60-second rolling window is ${Math.round((vol / windowLimit) * 100)}% utilized. No mitigation actions triggered.`,\n    }\n  }\n  const allowed = windowLimit\n  const dropped = vol - allowed\n  const allowedPct = Math.round((allowed / vol) * 100)\n  return {\n    allowed,\n    dropped,\n    allowedPct,\n    droppedPct: 100 - allowedPct,\n    bucketFill: 100,\n    status: 'throttling',\n    statusLabel: `Window Saturated · ${100 - allowedPct}% Shed`,\n    latency: '0.48ms',\n    description: `Rolling time-window threshold exceeded (${vol} req/s > ${windowLimit} limit). Excess requests are rejected at edge proxy.`,\n  }\n}\n\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-rate-limiting-config, [ui-rate-limiting-config]',\n  standalone: true,\n  host: {\n    '[attr.data-slot]': '\"rate-limiting-config\"',\n    '[class]': 'hostClass',\n  },\n  imports: [\n    LucideAngularModule,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiCardComponent,\n    UiCardContentComponent,\n    UiCardDescriptionComponent,\n    UiCardHeaderComponent,\n    UiCardTitleComponent,\n    UiDropdownMenuComponent,\n    UiDropdownMenuContentComponent,\n    UiDropdownMenuItemComponent,\n    UiDropdownMenuLabelComponent,\n    UiDropdownMenuSeparatorComponent,\n    UiDropdownMenuTriggerComponent,\n    UiProgressComponent,\n    UiSelectComponent,\n    UiSelectContentComponent,\n    UiSelectItemComponent,\n    UiSelectTriggerComponent,\n    UiSelectValueComponent,\n    UiSliderComponent,\n    UiSwitchComponent,\n    UiTableBodyComponent,\n    UiTableCellComponent,\n    UiTableComponent,\n    UiTableHeadComponent,\n    UiTableHeaderComponent,\n    UiTableRowComponent,\n  ],\n  template: `\n    <!-- Header Section -->\n    <div class=\"flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between\">\n      <div class=\"space-y-1.5\">\n        <div class=\"flex flex-wrap items-center gap-2\">\n          <span ui-badge variant=\"outline\" class=\"text-xs font-medium tracking-wide uppercase\">\n            <lucide-icon [img]=\"ShieldCheck\" class=\"text-primary size-3.5\" />\n            Edge Protection\n          </span>\n          <span class=\"text-muted-foreground text-xs\">Cloudflare & Upstash compatible</span>\n        </div>\n        <h1 class=\"text-foreground text-2xl font-bold tracking-tight sm:text-3xl\">\n          Rate Limiting & Traffic Protection\n        </h1>\n        <p class=\"text-muted-foreground text-sm\">\n          Configure DDoS mitigation, IP throttling, and per-tier request quotas.\n        </p>\n      </div>\n      <div class=\"flex flex-wrap items-center gap-3\">\n        <div class=\"bg-card border-border flex items-center gap-3 rounded-lg border px-3 py-2 shadow-xs\">\n          <div class=\"flex flex-col\">\n            <span class=\"text-foreground text-xs font-semibold\">Global Protection</span>\n            <span class=\"text-muted-foreground text-xs tabular-nums\">\n              {{ globalProtection() ? activeRulesCount() + ' Active Rules' : 'Disabled (Bypassed)' }}\n            </span>\n          </div>\n          <ui-switch [checked]=\"globalProtection()\" (checkedChange)=\"globalProtection.set($event)\" />\n        </div>\n        <button ui-button class=\"gap-1.5 shadow-xs\">\n          <lucide-icon [img]=\"Plus\" class=\"size-4\" />\n          <span>Add Rate Limit Rule</span>\n        </button>\n      </div>\n    </div>\n\n    <!-- 4 Health Cards -->\n    <div class=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n      <div ui-card class=\"shadow-xs\">\n        <div ui-card-header class=\"flex flex-row items-center justify-between pb-2\">\n          <h3 ui-card-title class=\"text-muted-foreground text-xs font-medium\">Total Requests / min</h3>\n          <div class=\"bg-primary/10 text-primary flex size-8 items-center justify-center rounded-md\">\n            <lucide-icon [img]=\"Activity\" class=\"size-4\" />\n          </div>\n        </div>\n        <div ui-card-content class=\"space-y-1\">\n          <div class=\"text-foreground text-2xl font-bold tracking-tight tabular-nums\">4,250 req/s</div>\n          <div class=\"flex items-center gap-1.5 text-xs\">\n            <span ui-badge variant=\"success\" class=\"h-4.5 px-1 py-0 text-xs\">\n              <lucide-icon [img]=\"TrendingUp\" class=\"size-3\" />\n              +12.4%\n            </span>\n            <span class=\"text-muted-foreground tabular-nums\">255.0k / min · 99.98% ok</span>\n          </div>\n        </div>\n      </div>\n      <div ui-card class=\"shadow-xs\">\n        <div ui-card-header class=\"flex flex-row items-center justify-between pb-2\">\n          <h3 ui-card-title class=\"text-muted-foreground text-xs font-medium\">Blocked Attacks</h3>\n          <div class=\"bg-destructive/10 text-destructive flex size-8 items-center justify-center rounded-md\">\n            <lucide-icon [img]=\"ShieldAlert\" class=\"size-4\" />\n          </div>\n        </div>\n        <div ui-card-content class=\"space-y-1\">\n          <div class=\"text-foreground text-2xl font-bold tracking-tight tabular-nums\">1,420 in 24h</div>\n          <div class=\"flex items-center gap-1.5 text-xs\">\n            <span ui-badge variant=\"outline\" class=\"h-4.5 px-1.5 py-0 text-xs font-normal\">Tier-1 Edge</span>\n            <span class=\"text-muted-foreground truncate tabular-nums\">18 brute-force bursts</span>\n          </div>\n        </div>\n      </div>\n      <div ui-card class=\"shadow-xs\">\n        <div ui-card-header class=\"flex flex-row items-center justify-between pb-2\">\n          <h3 ui-card-title class=\"text-muted-foreground text-xs font-medium\">Active Throttles</h3>\n          <div class=\"bg-warning/10 text-warning flex size-8 items-center justify-center rounded-md\">\n            <lucide-icon [img]=\"ZapOff\" class=\"size-4\" />\n          </div>\n        </div>\n        <div ui-card-content class=\"space-y-1\">\n          <div class=\"text-foreground text-2xl font-bold tracking-tight tabular-nums\">18 IPs</div>\n          <div class=\"flex items-center gap-1.5 text-xs\">\n            <span class=\"bg-warning inline-block size-1.5 rounded-full\"></span>\n            <span class=\"text-muted-foreground truncate tabular-nums\">4 edge clusters · Exp 15m</span>\n          </div>\n        </div>\n      </div>\n      <div ui-card class=\"shadow-xs\">\n        <div ui-card-header class=\"flex flex-row items-center justify-between pb-2\">\n          <h3 ui-card-title class=\"text-muted-foreground text-xs font-medium\">Median Latency Overhead</h3>\n          <div class=\"bg-info/10 text-info flex size-8 items-center justify-center rounded-md\">\n            <lucide-icon [img]=\"Timer\" class=\"size-4\" />\n          </div>\n        </div>\n        <div ui-card-content class=\"space-y-1\">\n          <div class=\"text-foreground text-2xl font-bold tracking-tight tabular-nums\">0.4ms</div>\n          <div class=\"flex items-center gap-1.5 text-xs\">\n            <span class=\"text-muted-foreground tabular-nums\">p99 &lt; 1.2ms · Multi-Region Redis</span>\n          </div>\n        </div>\n      </div>\n    </div>\n\n    <!-- Interactive Token Bucket Visualizer -->\n    <div ui-card class=\"shadow-xs\">\n      <div ui-card-header class=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n        <div class=\"space-y-1\">\n          <div class=\"flex flex-wrap items-center gap-2\">\n            <h3 ui-card-title class=\"text-lg font-semibold\">Algorithm Simulation & Token Dynamics</h3>\n            <span ui-badge variant=\"outline\" class=\"text-xs font-normal\">Interactive Sandbox</span>\n          </div>\n          <p ui-card-description class=\"text-xs\">\n            Test traffic volume against token reservoir dynamics and rolling time windows.\n          </p>\n        </div>\n        <div class=\"flex flex-wrap items-center gap-2\">\n          <span class=\"text-muted-foreground text-xs font-medium\">Algorithm:</span>\n          <ui-select [value]=\"algorithm()\" (valueChange)=\"setAlgorithm($event)\">\n            <button ui-select-trigger class=\"w-52 text-xs\">\n              <ui-select-value placeholder=\"Select algorithm\" />\n            </button>\n            <ui-select-content>\n              <ui-select-item value=\"token-bucket\">Token Bucket (Burst Tolerant)</ui-select-item>\n              <ui-select-item value=\"sliding-window\">Sliding Window Counter</ui-select-item>\n            </ui-select-content>\n          </ui-select>\n        </div>\n      </div>\n\n      <div ui-card-content class=\"space-y-6\">\n        <div class=\"grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4\">\n          <div class=\"bg-muted/30 border-border relative flex flex-col justify-between rounded-lg border p-3.5\">\n            <div class=\"space-y-1.5\">\n              <div class=\"flex items-center justify-between\">\n                <span class=\"text-muted-foreground text-xs font-medium tracking-wider uppercase\">\n                  {{ algorithm() === 'token-bucket' ? '1. Refill Velocity' : '1. Time Window' }}\n                </span>\n                <lucide-icon [img]=\"RefreshCw\" class=\"text-muted-foreground size-3.5\" />\n              </div>\n              <div class=\"text-foreground text-lg font-semibold tabular-nums\">\n                {{ algorithm() === 'token-bucket' ? '50 tokens / s' : '60s Window' }}\n              </div>\n              <p class=\"text-muted-foreground text-xs\">\n                {{\n                  algorithm() === 'token-bucket'\n                    ? 'Deterministic continuous refill into pool'\n                    : 'Rolling granular sub-window slices'\n                }}\n              </p>\n            </div>\n            <div class=\"mt-3\"><ui-progress [value]=\"100\" class=\"h-1.5\" /></div>\n          </div>\n          <div class=\"bg-muted/30 border-border relative flex flex-col justify-between rounded-lg border p-3.5\">\n            <div class=\"space-y-1.5\">\n              <div class=\"flex items-center justify-between\">\n                <span class=\"text-muted-foreground text-xs font-medium tracking-wider uppercase\">\n                  {{ algorithm() === 'token-bucket' ? '2. Reservoir Capacity' : '2. Window Ceiling' }}\n                </span>\n                <lucide-icon [img]=\"Database\" class=\"text-muted-foreground size-3.5\" />\n              </div>\n              <div class=\"text-foreground text-lg font-semibold tabular-nums\">\n                {{ algorithm() === 'token-bucket' ? '100 tokens max' : '120 req / window' }}\n              </div>\n              <p class=\"text-muted-foreground text-xs\">\n                {{\n                  algorithm() === 'token-bucket'\n                    ? 'Pool status: ' + simStats().bucketFill + '% available'\n                    : 'Window utilized: ' + simStats().bucketFill + '%'\n                }}\n              </p>\n            </div>\n            <div class=\"mt-3\"><ui-progress [value]=\"simStats().bucketFill\" class=\"h-1.5\" /></div>\n          </div>\n          <div class=\"bg-muted/30 border-border relative flex flex-col justify-between rounded-lg border p-3.5\">\n            <div class=\"space-y-1.5\">\n              <div class=\"flex items-center justify-between\">\n                <span class=\"text-muted-foreground text-xs font-medium tracking-wider uppercase\"\n                  >3. Test Traffic Load</span\n                >\n                <lucide-icon [img]=\"Zap\" class=\"text-primary size-3.5\" />\n              </div>\n              <div class=\"text-foreground text-lg font-semibold tabular-nums\">{{ simVolume() }} req/s</div>\n              <p class=\"text-muted-foreground text-xs\">Simulated rate generated from slider</p>\n            </div>\n            <div class=\"mt-3\">\n              <ui-progress [value]=\"sliderFillPct()\" class=\"h-1.5\" />\n            </div>\n          </div>\n          <div class=\"bg-muted/30 border-border relative flex flex-col justify-between rounded-lg border p-3.5\">\n            <div class=\"space-y-1.5\">\n              <div class=\"flex items-center justify-between\">\n                <span class=\"text-muted-foreground text-xs font-medium tracking-wider uppercase\"\n                  >4. Edge Gate Verdict</span\n                >\n                @if (simStats().status === 'optimal') {\n                  <lucide-icon [img]=\"CheckCircle2\" class=\"text-success size-3.5\" />\n                } @else if (simStats().status === 'burst') {\n                  <lucide-icon [img]=\"AlertTriangle\" class=\"text-warning size-3.5\" />\n                } @else {\n                  <lucide-icon [img]=\"ShieldAlert\" class=\"text-destructive size-3.5\" />\n                }\n              </div>\n              <div class=\"text-foreground text-lg font-semibold tabular-nums\">\n                {{ simStats().allowed }} Pass · {{ simStats().dropped }} Drop\n              </div>\n              <p class=\"text-muted-foreground text-xs\">Overhead: {{ simStats().latency }}</p>\n            </div>\n            <div class=\"mt-3\">\n              <div class=\"bg-secondary relative h-1.5 w-full overflow-hidden rounded-full\">\n                <div\n                  class=\"bg-success absolute top-0 bottom-0 left-0 transition-colors duration-150\"\n                  [style.width.%]=\"simStats().allowedPct\"\n                ></div>\n                <div\n                  class=\"bg-destructive absolute top-0 right-0 bottom-0 transition-colors duration-150\"\n                  [style.width.%]=\"simStats().droppedPct\"\n                ></div>\n              </div>\n            </div>\n          </div>\n        </div>\n\n        <!-- Interactive Simulator Slider Box -->\n        <div class=\"bg-muted/20 border-border space-y-4 rounded-lg border p-4\">\n          <div class=\"flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between\">\n            <div class=\"space-y-0.5\">\n              <div class=\"flex flex-wrap items-center gap-2\">\n                <lucide-icon [img]=\"Sliders\" class=\"text-primary size-4\" />\n                <span class=\"text-foreground text-sm font-semibold\">Simulate Incoming Request Volume</span>\n              </div>\n              <p class=\"text-muted-foreground text-xs\">\n                Drag the slider to stress-test the algorithm against burst spikes and DDoS loads.\n              </p>\n            </div>\n            <div class=\"flex flex-wrap items-center gap-2\">\n              <span\n                ui-badge\n                [variant]=\"\n                  simStats().status === 'optimal'\n                    ? 'success'\n                    : simStats().status === 'burst'\n                      ? 'warning'\n                      : 'destructive'\n                \"\n                class=\"text-xs font-medium\"\n              >\n                {{ simStats().statusLabel }}\n              </span>\n              <div class=\"bg-card border-border rounded border px-2.5 py-1 font-mono text-sm font-bold tabular-nums\">\n                {{ simVolume() }} req/s\n              </div>\n            </div>\n          </div>\n          <div class=\"space-y-2 pt-2\">\n            <ui-slider\n              [value]=\"[simVolume()]\"\n              (valueChange)=\"onSimVolumeChange($event)\"\n              [min]=\"10\"\n              [max]=\"500\"\n              [step]=\"10\"\n              class=\"w-full\"\n            />\n            <div class=\"text-muted-foreground flex justify-between text-xs tabular-nums\">\n              <span>10 req/s (Low)</span>\n              <span>50 req/s (Refill Rate)</span>\n              <span>100 req/s (Burst Limit)</span>\n              <span>250 req/s (Heavy Spike)</span>\n              <span>500 req/s (DDoS Attack)</span>\n            </div>\n          </div>\n          <div class=\"space-y-3 pt-2\">\n            <div class=\"space-y-1.5\">\n              <div class=\"flex justify-between text-xs font-medium\">\n                <span class=\"text-success flex items-center gap-1\">\n                  <span class=\"bg-success size-2 rounded-full\"></span>\n                  Allowed: {{ simStats().allowed }} req/s ({{ simStats().allowedPct }}%)\n                </span>\n                <span class=\"text-destructive flex items-center gap-1\">\n                  <span class=\"bg-destructive size-2 rounded-full\"></span>\n                  Rate Limited / 429: {{ simStats().dropped }} req/s ({{ simStats().droppedPct }}%)\n                </span>\n              </div>\n              <div class=\"bg-muted border-border/50 relative h-2.5 w-full overflow-hidden rounded-full border\">\n                <div\n                  class=\"bg-success absolute top-0 bottom-0 left-0 transition-colors duration-150\"\n                  [style.width.%]=\"simStats().allowedPct\"\n                ></div>\n                <div\n                  class=\"bg-destructive absolute top-0 right-0 bottom-0 transition-colors duration-150\"\n                  [style.width.%]=\"simStats().droppedPct\"\n                ></div>\n              </div>\n            </div>\n            <div class=\"bg-card border-border/80 flex items-start gap-2.5 rounded-md border p-3 text-xs\">\n              <lucide-icon [img]=\"Shield\" class=\"text-primary mt-0.5 size-4 shrink-0\" />\n              <p class=\"text-muted-foreground leading-relaxed\">{{ simStats().description }}</p>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n\n    <!-- Configured Rules Table Card -->\n    <div ui-card class=\"shadow-xs\">\n      <div ui-card-header class=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n        <div class=\"space-y-1\">\n          <div class=\"flex flex-wrap items-center gap-2\">\n            <h3 ui-card-title class=\"text-lg font-semibold\">Configured Rate Limiting Rules</h3>\n            <span ui-badge variant=\"secondary\" class=\"text-xs font-normal tabular-nums\">\n              {{ filteredRules().length }} Policies\n            </span>\n          </div>\n          <p ui-card-description class=\"text-xs\">\n            Priority-ordered edge firewall rules evaluated sequentially for incoming traffic.\n          </p>\n        </div>\n        <div class=\"relative w-full sm:w-64\">\n          <lucide-icon\n            [img]=\"Search\"\n            class=\"text-muted-foreground pointer-events-none absolute top-1/2 left-2.5 size-3.5 -translate-y-1/2\"\n          />\n          <input\n            type=\"text\"\n            placeholder=\"Search rules, paths, actions…\"\n            [value]=\"searchQuery()\"\n            (input)=\"searchQuery.set($any($event.target).value)\"\n            class=\"border-input bg-background placeholder:text-muted-foreground focus-visible:ring-ring/50 focus-visible:border-ring h-8.5 w-full rounded-md border pr-3 pl-8 text-xs shadow-xs outline-none focus-visible:ring-[3px]\"\n          />\n        </div>\n      </div>\n\n      <div ui-card-content class=\"p-0\">\n        <div class=\"overflow-x-auto\">\n          <table ui-table>\n            <thead ui-table-header>\n              <tr ui-table-row>\n                <th ui-table-head class=\"w-[280px]\">Rule Name & Scope</th>\n                <th ui-table-head class=\"w-[220px]\">Path Matcher</th>\n                <th ui-table-head class=\"w-[180px]\">Limit Threshold</th>\n                <th ui-table-head class=\"w-[200px]\">Mitigation Action</th>\n                <th ui-table-head class=\"w-[90px] text-center\">Status</th>\n                <th ui-table-head class=\"w-[60px] text-right\">Actions</th>\n              </tr>\n            </thead>\n            <tbody ui-table-body>\n              @for (rule of filteredRules(); track rule.id) {\n                <tr ui-table-row [class]=\"rule.enabled ? '' : 'bg-muted/20 opacity-60'\">\n                  <td ui-table-cell>\n                    <div class=\"space-y-0.5\">\n                      <div class=\"flex flex-wrap items-center gap-2\">\n                        <span class=\"text-foreground font-mono text-xs font-semibold\">{{ rule.name }}</span>\n                      </div>\n                      <div class=\"text-muted-foreground flex items-center gap-1.5 text-xs\">\n                        <span>{{ rule.scope }}</span>\n                        <span>·</span>\n                        <span class=\"tabular-nums\">{{ rule.blocked24h }} in 24h</span>\n                      </div>\n                    </div>\n                  </td>\n                  <td ui-table-cell>\n                    <div class=\"flex items-center gap-1.5\">\n                      <span ui-badge variant=\"outline\" class=\"font-mono text-xs uppercase\">{{ rule.method }}</span>\n                      <code\n                        class=\"bg-muted/60 border-border text-foreground rounded border px-1.5 py-0.5 font-mono text-xs\"\n                      >\n                        {{ rule.path }}\n                      </code>\n                    </div>\n                  </td>\n                  <td ui-table-cell>\n                    <div class=\"space-y-0.5\">\n                      <div class=\"text-foreground font-mono text-xs font-medium tabular-nums\">\n                        {{ rule.limitThreshold }}\n                      </div>\n                      <div class=\"text-muted-foreground text-xs\">Triggered {{ rule.lastTriggered }}</div>\n                    </div>\n                  </td>\n                  <td ui-table-cell>\n                    <span ui-badge [variant]=\"rule.actionVariant\" class=\"text-xs font-medium\">\n                      <span\n                        [class]=\"\n                          'mr-1 size-1.5 rounded-full ' +\n                          (rule.actionVariant === 'warning'\n                            ? 'bg-warning'\n                            : rule.actionVariant === 'destructive'\n                              ? 'bg-destructive'\n                              : 'bg-secondary-foreground')\n                        \"\n                      ></span>\n                      {{ rule.mitigationAction }}\n                    </span>\n                  </td>\n                  <td ui-table-cell class=\"text-center\">\n                    <ui-switch\n                      [checked]=\"rule.enabled\"\n                      [attr.aria-label]=\"'Toggle rule ' + rule.name\"\n                      (checkedChange)=\"toggleRule(rule.id, $event)\"\n                    />\n                  </td>\n                  <td ui-table-cell class=\"text-right\">\n                    <ui-dropdown-menu>\n                      <button ui-button ui-dropdown-menu-trigger variant=\"ghost\" size=\"icon\" class=\"size-8\">\n                        <lucide-icon [img]=\"MoreHorizontal\" class=\"size-4\" />\n                        <span class=\"sr-only\">Open menu</span>\n                      </button>\n                      <ui-dropdown-menu-content align=\"end\" class=\"w-48\">\n                        <ui-dropdown-menu-label class=\"text-xs\">Rule Configuration</ui-dropdown-menu-label>\n                        <ui-dropdown-menu-item class=\"gap-2 text-xs\">\n                          <lucide-icon [img]=\"Edit3\" class=\"size-3.5\" />\n                          <span>Edit Rule</span>\n                        </ui-dropdown-menu-item>\n                        <ui-dropdown-menu-item class=\"gap-2 text-xs\">\n                          <lucide-icon [img]=\"Activity\" class=\"size-3.5\" />\n                          <span>View Metrics</span>\n                        </ui-dropdown-menu-item>\n                        <ui-dropdown-menu-item class=\"gap-2 text-xs\">\n                          <lucide-icon [img]=\"Copy\" class=\"size-3.5\" />\n                          <span>Duplicate Rule</span>\n                        </ui-dropdown-menu-item>\n                        <ui-dropdown-menu-separator />\n                        <ui-dropdown-menu-item class=\"text-destructive focus:text-destructive gap-2 text-xs\">\n                          <lucide-icon [img]=\"Trash2\" class=\"size-3.5\" />\n                          <span>Delete Rule</span>\n                        </ui-dropdown-menu-item>\n                      </ui-dropdown-menu-content>\n                    </ui-dropdown-menu>\n                  </td>\n                </tr>\n              }\n            </tbody>\n          </table>\n        </div>\n      </div>\n    </div>\n  `,\n})\nexport class UiRateLimitingConfigComponent {\n  protected readonly Activity = Activity\n  protected readonly AlertTriangle = AlertTriangle\n  protected readonly CheckCircle2 = CheckCircle2\n  protected readonly Copy = Copy\n  protected readonly Database = Database\n  protected readonly Edit3 = Edit3\n  protected readonly MoreHorizontal = MoreHorizontal\n  protected readonly Plus = Plus\n  protected readonly RefreshCw = RefreshCw\n  protected readonly Search = Search\n  protected readonly Shield = Shield\n  protected readonly ShieldAlert = ShieldAlert\n  protected readonly ShieldCheck = ShieldCheck\n  protected readonly Sliders = Sliders\n  protected readonly Timer = Timer\n  protected readonly Trash2 = Trash2\n  protected readonly TrendingUp = TrendingUp\n  protected readonly Zap = Zap\n  protected readonly ZapOff = ZapOff\n\n  @Input('class') className?: string\n\n  readonly globalProtection = signal(true)\n  readonly algorithm = signal<RateLimitAlgorithm>('token-bucket')\n  readonly simVolume = signal(120)\n  readonly searchQuery = signal('')\n  readonly rules = signal<RateLimitRule[]>(INITIAL_RULES.map((r) => ({ ...r })))\n\n  readonly activeRulesCount = computed(() => this.rules().filter((r) => r.enabled).length)\n\n  readonly filteredRules = computed(() => {\n    const q = this.searchQuery().trim().toLowerCase()\n    if (!q) return this.rules()\n    return this.rules().filter(\n      (r) =>\n        r.name.toLowerCase().includes(q) ||\n        r.path.toLowerCase().includes(q) ||\n        r.scope.toLowerCase().includes(q) ||\n        r.mitigationAction.toLowerCase().includes(q),\n    )\n  })\n\n  readonly simStats = computed<SimStats>(() => computeSimStats(this.algorithm(), this.simVolume()))\n  readonly sliderFillPct = computed(() => Math.min(100, Math.round((this.simVolume() / 500) * 100)))\n\n  get hostClass(): string {\n    return cn('mx-auto w-full max-w-6xl space-y-6', this.className)\n  }\n\n  toggleRule(id: string, enabled?: boolean): void {\n    this.rules.update((prev) => prev.map((r) => (r.id === id ? { ...r, enabled: enabled ?? !r.enabled } : r)))\n  }\n\n  setAlgorithm(value: string): void {\n    this.algorithm.set(value as RateLimitAlgorithm)\n  }\n\n  onSimVolumeChange(value: number[]): void {\n    if (value[0] !== undefined) this.simVolume.set(value[0])\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/rate-limiting-config/rate-limiting-config.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/rate-limiting-config/index.ts",
      "content": "export {\n  UiRateLimitingConfigComponent,\n  INITIAL_RULES,\n  computeSimStats,\n  type RateLimitRule,\n  type RateLimitAlgorithm,\n  type SimStats,\n} from './rate-limiting-config.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/rate-limiting-config/index.ts"
    }
  ],
  "dependencies": [
    "lucide-angular"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/angular/badge.json",
    "https://uipkge.dev/r/angular/button.json",
    "https://uipkge.dev/r/angular/card.json",
    "https://uipkge.dev/r/angular/dropdown-menu.json",
    "https://uipkge.dev/r/angular/progress.json",
    "https://uipkge.dev/r/angular/select.json",
    "https://uipkge.dev/r/angular/slider.json",
    "https://uipkge.dev/r/angular/switch.json",
    "https://uipkge.dev/r/angular/table.json"
  ],
  "description": "Cloudflare/Upstash style API rate limiting rules builder and token bucket traffic monitor with 4 health metrics cards, interactive token bucket vs sliding window simulation slider, and configured mitigation rules table with status controls and contextual action menus.",
  "categories": [
    "devops",
    "app",
    "dashboard"
  ]
}