diff --git a/README.md b/README.md index 5eb39c5..6e10104 100644 --- a/README.md +++ b/README.md @@ -8,15 +8,20 @@ Angular Render Scan is a visual debugging overlay for Angular change detection. - **Automatic Angular Telemetry:** Out-of-the-box zero-setup component auto-instrumentation using Angular dev-mode profiler hooks. - **Heatmap & Outlines:** Highlights are colored dynamically based on DOM mutations: **green** for no-op wasted renders, **blue** for text/attribute mutations, and **prominent red borders** for expensive renders exceeding thresholds, making bottlenecks instantly recognizable. +- **CD Trigger Attribution:** Every change detection cycle is labeled with what triggered it — `zone:click`, `zone:setTimeout`, `zone:xhr`, `signal:write`, `router:navigation`, `manual:markForCheck`, and more. The toolbar shows the source of the last cycle at a glance. +- **OnPush Candidates Surface:** Automatically identifies `ChangeDetectionStrategy.Default` components with high wasted-render percentages and ranks them as OnPush conversion candidates with confidence scoring (high/medium/low). +- **Referential Input Stability Detection:** Tracks `@Input()` values across cycles using deep serialization and flags inputs where a new object reference carries the same value — the primary source of OnPush false positives. +- **Zone Pollution Detector:** Flags CD cycles that have no user interaction, no signal write, and no router navigation as suspected Zone pollution. Shows a live feed of polluted cycles in the toolbar. +- **Change Detection Graph:** Builds a session-level component graph of parent→child render relationships, including edge trigger counts and per-node CD strategy metadata. - **CD Waterfall View:** Click the SVG sparkline in the toolbar to expand a nested horizontal bar breakdown of component check execution stack offsets and children offsets. - **Non-Intrusive Budget Alerts Feed:** Standardized budget violations (warning/error millisecond limits and rate alerts) are elegantly grouped and logged in a collapsible alerts feed panel, handling concurrent violations cleanly. - **Live CPU & Main-Thread Telemetry:** Dotted CPU metric toggles a live popup showing detailed frame-lag latency and total main-thread blocking times. - **Memory Leak Detector Badge:** Automatically tracks zombie components whose DOM elements were disconnected but not properly destroyed. - **Click-to-Source IDE Integration:** Inspected details panel provides an "Open in Editor" link that deep links directly to Cursor, VS Code or WebStorm, and automatically copies the class query to your clipboard for instant search. -- **Session Export JSON:** Download a full profiling JSON bundle including CPU, cycle timelines, wasted statistics, and active budget violation logs. +- **Session Export JSON:** Download a full profiling JSON bundle including CPU, cycle timelines, wasted statistics, OnPush candidates, Zone pollution events, and referential instability reports. - **Dark Mode & Theme Presets:** Sleek dark mode styles that match `prefers-color-scheme`, customizable dynamically via options. - **Keyboard Shortcuts:** Keyboard hotkeys mapped to toggle scan, details panel, copy prompts, and clear stats instantly. -- **Production Guard:** Automatic safety guard shutting down package overhead entirely outside developer mode. +- **Production Guard:** Automatic safety guard shutting down package overhead entirely outside developer mode. Zone tracker lazy-loaded so it tree-shakes to zero in production bundles. ## Install @@ -26,7 +31,23 @@ npm install angular-render-scan Angular Render Scan expects Angular 9+ as a peer dependency. -## Quick Start +## Zero-Config Setup with the CLI + +The fastest way to add Angular Render Scan to any project: + +```sh +npx angular-render-scan-cli init +``` + +The CLI detects `angular.json`, finds your `main.ts` or `app.config.ts`, installs the npm package, and injects `provideAngularRenderScan()` into your providers — no manual editing required. + +```sh +npx angular-render-scan-cli init --dry-run # preview changes without writing +npx angular-render-scan-cli init --script-tag # use CDN script tag instead of npm +npx angular-render-scan-cli --help +``` + +## Quick Start (Manual) Add `provideAngularRenderScan()` to your Angular bootstrap providers. @@ -118,7 +139,6 @@ stop(); ``` ## Options - ```ts interface AngularRenderScanOptions { enabled?: boolean; @@ -138,6 +158,23 @@ interface AngularRenderScanOptions { theme?: Partial; editorProtocol?: 'vscode' | 'webstorm' | 'cursor' | string; darkMode?: 'auto' | 'dark' | 'light'; + + // CD Trigger Attribution + showCdGraph?: boolean; + + // Zone Pollution Detector + maxZonePollutionEvents?: number; // default: 50 + onZonePollution?: (event: ZonePollutionEvent) => void; + + // OnPush Candidates + onPushCandidateThreshold?: number; // wasted-render % threshold, default: 40 + trackComponents?: Array; // limit tracking to specific components + + // Referential Input Stability + trackReferentialStability?: boolean; // default: true + referentialStabilityDepth?: number; // deep-equal max depth, default: 4 + + // Callbacks onCycleStart?: () => void; onRender?: (entry: AngularRenderEntry) => void; onCycleFinish?: (cycle: AngularRenderCycle) => void; @@ -254,15 +291,87 @@ interface AngularRenderScanTheme { } ``` +## New API Functions + +```ts +import { + getOnPushCandidates, + getReferentialInstability, + getZonePollutionEvents, + getCdGraph +} from 'angular-render-scan'; + +// Components that are safe to switch to ChangeDetectionStrategy.OnPush +const candidates = getOnPushCandidates(40); // threshold: wasted-render % + +// Inputs where a new reference carried the same value +const unstable = getReferentialInstability(1); // minUnstableRefs + +// Cycles that fired with no user interaction / signal / router trigger +const pollution = getZonePollutionEvents(); + +// Session-level component dependency graph +const graph = getCdGraph(); +// graph.nodes: per-component stats (cdStrategy, renderCount, wastedChecks, isOnPushCandidate) +// graph.edges: parent→child trigger counts +``` + +### OnPush Candidate shape + +```ts +interface OnPushCandidate { + componentId: string; + selector: string; + wastedRenderPct: number; // percentage of renders that were no-ops + totalChecks: number; + confidence: 'high' | 'medium' | 'low'; + reason: string; +} +``` + +### Zone Pollution Event shape + +```ts +interface ZonePollutionEvent { + timestamp: number; + cycleId: string; + suspectedTrigger: string; // best-guess Zone task description + componentCount: number; +} +``` + +### Referential Instability Report shape + +```ts +interface ReferentialInstabilityReport { + componentId: string; + selector: string; + inputKey: string; + unstableRefCount: number; // how many times a new ref held the same value + totalInputChanges: number; +} +``` + +### Production no-op subpath + +For SSR, unit tests, or any context where you want to explicitly import stubs: + +```ts +import { getOnPushCandidates } from 'angular-render-scan/noop'; +// All functions return empty arrays / null — safe to call anywhere +``` + ## Toolbar The toolbar shows: - - scan on/off switch - FPS - latest cycle time +- CD trigger source (last cycle — e.g. `zone:click`, `signal:write`) - changed component count - slowest component +- OnPush candidates chip (count — click to open ranked list) +- Zone pollution chip (count — click to open event feed) - copy slow issues prompt - clear stats button diff --git a/demo/src/index.html b/demo/src/index.html index 11893ee..999cb3a 100644 --- a/demo/src/index.html +++ b/demo/src/index.html @@ -2,9 +2,17 @@ - Angular Render Scan Demo + Angular Render Scan · v22 Demo + + + + + diff --git a/demo/src/main.ts b/demo/src/main.ts index 662c16d..1f2ae66 100644 --- a/demo/src/main.ts +++ b/demo/src/main.ts @@ -1,527 +1,676 @@ -import { bootstrapApplication } from "@angular/platform-browser"; +import { bootstrapApplication } from '@angular/platform-browser'; import { ChangeDetectionStrategy, Component, computed, - signal, - WritableSignal, - effect, input, - output, OnDestroy, -} from "@angular/core"; + OnInit, + output, + signal, +} from '@angular/core'; import { AngularRenderScanMarkDirective, + getCdGraph, + getOnPushCandidates, + getReferentialInstability, + getZonePollutionEvents, provideAngularRenderScan, -} from "angular-render-scan"; - -interface Product { - id: number; - title: string; - price: number; - icon: string; - description: string; -} +} from 'angular-render-scan'; + +// ── Types ────────────────────────────────────────────────────── +interface Product { id: number; name: string; price: number; cat: string; icon: string } +interface LogEntry { time: string; msg: string; type: 'warn' | 'info' | 'success' } +interface PollutionEntry { time: string; trigger: string; comps: number } +interface TriggerEntry { time: string; source: string; kind: 'zone' | 'signal' | 'unknown' } const PRODUCTS: Product[] = [ - { - id: 1, - title: "Developer Coffee", - price: 29.99, - icon: "☕", - description: "Dark roast, high caffeine.", - }, - { - id: 2, - title: "Mechanical Keyboard", - price: 149.0, - icon: "⌨️", - description: "Clicky blue switches.", - }, + { id: 1, name: 'TypeScript Handbook', price: 29, cat: 'Books', icon: '📘' }, + { id: 2, name: 'Mechanical Keyboard', price: 149, cat: 'Hardware', icon: '⌨️' }, + { id: 3, name: 'Developer Mug', price: 19, cat: 'Merch', icon: '☕' }, + { id: 4, name: 'Rubber Duck', price: 9, cat: 'Debug', icon: '🦆' }, + { id: 5, name: 'Monitor Stand', price: 89, cat: 'Hardware', icon: '🖥️' }, + { id: 6, name: 'SSH Key Ring', price: 14, cat: 'Merch', icon: '🔑' }, ]; +// ── ProductCard (OnPush — scanner marks it green) ────────────── @Component({ - selector: "app-product-card", + selector: 'app-product', standalone: true, imports: [AngularRenderScanMarkDirective], template: ` -
-
{{ product().icon }}
-
-

{{ product().title }}

-

{{ product().description }}

-
-
+ \${{ p().price }} + + `, changeDetection: ChangeDetectionStrategy.OnPush, }) -class ProductCardComponent { - readonly product = input.required(); - readonly onAdd = output(); +class ProductComponent { + readonly p = input.required(); + readonly add = output(); } +// ── CartItem (Default CD — scanner shows it rerenders often) ─── @Component({ - selector: "app-cart-item", + selector: 'app-cart-item', standalone: true, imports: [AngularRenderScanMarkDirective], template: ` -
- {{ item().icon }} {{ item().title }} -
- x{{ quantity() }} - -
+
+ {{ item().icon }} + {{ item().name }} + ×{{ qty() }} +
`, + // intentionally Default CD to be surfaced as OnPush candidate }) class CartItemComponent { readonly item = input.required(); - readonly quantity = input.required(); - readonly onRemove = output(); + readonly qty = input.required(); + readonly remove = output(); } +// ── SlowComponent (Default CD — intentional OnPush candidate) ─ @Component({ - selector: "app-shopping-cart", + selector: 'app-slow', standalone: true, - imports: [AngularRenderScanMarkDirective, CartItemComponent], + imports: [AngularRenderScanMarkDirective], template: ` - +
`, + // no OnPush — will rerender on every parent CD cycle }) -class ShoppingCartComponent { - readonly cartMap = input.required< - WritableSignal< - Map< - number, - { - product: Product; - quantity: number; - } - > - > - >(); - readonly checkoutEvent = output(); - - readonly cartKeys = computed(() => Array.from(this.cartMap()().keys())); - - readonly totalItems = computed(() => { - let total = 0; - this.cartMap()().forEach((v: any) => (total += v.quantity)); - return total; - }); - - readonly totalPrice = computed(() => { - let total = 0; - this.cartMap()().forEach( - (v: any) => (total += v.product.price * v.quantity), - ); - return total; - }); - - removeFromCart(product: Product) { - const map = new Map(this.cartMap()()); - const existing: any = map.get(product.id); - if (existing) { - if (existing.quantity > 1) { - map.set(product.id, { ...existing, quantity: existing.quantity - 1 }); - } else { - map.delete(product.id); - } - this.cartMap().set(map); - } - } - - checkout() { - this.checkoutEvent.emit(); - this.cartMap().set(new Map()); +class SlowComponent { + readonly counter = input.required(); + readonly renders = signal(0); + + compute(): string { + this.renders.update(r => r + 1); + let n = 0; + for (let i = 0; i < 80_000; i++) n += Math.sqrt(i); + return n.toFixed(2); } } +// ── RefDemo (OnPush — but receives new object refs) ──────────── @Component({ - selector: "app-recommendations", + selector: 'app-ref-demo', standalone: true, imports: [AngularRenderScanMarkDirective], template: ` -
-
- Slow path (Default CD) -

Recommendation Engine

-
-

- Runs intentionally expensive computed work so the scanner can surface a - slow component. -

-
- Confidence - {{ expensiveScore() }} -
- -
+
+ config @Input + + {{ '{' }} theme: "{{ cfg().theme }}", size: {{ cfg().size }} {{ '}' }} + +
`, + changeDetection: ChangeDetectionStrategy.OnPush, }) -class RecommendationsComponent { - readonly seed = signal(2000); - readonly expensiveScore = computed(() => { - let total = 0; - for (let i = 0; i < this.seed() * 400; i += 1) { - total += Math.sqrt((i % 97) + (total % 13)); - } - return Math.round(total).toLocaleString(); - }); - - recalculate(): void { - this.seed.update((value) => value + 100); - } +class RefDemoComponent { + readonly cfg = input.required<{ theme: string; size: number }>(); } +// ── Root ─────────────────────────────────────────────────────── @Component({ - selector: "app-hero-banner", + selector: 'app-root', standalone: true, + imports: [ProductComponent, CartItemComponent, SlowComponent, RefDemoComponent, AngularRenderScanMarkDirective], template: ` -
-
-
- - - - - -
-
-

Developer Store

-
- Render Diagnostics Cockpit - - Angular v21.2 -
-
+
+ + +
+ +
+
+ Products + {{ products.length }} items
-
- Zone.js + Signals - SCANNER ACTIVE - - +
+ @for (p of products; track p.id) { + + }
-
- `, -}) -class HeroBannerComponent { - readonly showGrid = signal(true); - readonly darkMode = signal<"light" | "dark">("light"); - - toggleGrid() { - this.showGrid.update((v) => !v); - if (this.showGrid()) { - document.body.classList.remove("grid-lines-off"); - } else { - document.body.classList.add("grid-lines-off"); - } - } + - toggleDarkMode() { - const next = this.darkMode() === "light" ? "dark" : "light"; - this.darkMode.set(next); - if (next === "dark") { - document.documentElement.classList.add("dark"); - (window as any).AngularRenderScan?.setOptions({ darkMode: "dark" }); - } else { - document.documentElement.classList.remove("dark"); - (window as any).AngularRenderScan?.setOptions({ darkMode: "light" }); - } - } -} +
+
+ Cart + @if (cartTotal() > 0) { + \${{ cartTotal() }} + } +
+
+ @if (cartKeys().length === 0) { +
🛒
Empty
+ } @else { + @for (k of cartKeys(); track k) { + + } +
+ \${{ cartTotal() }} + +
+ } +
+
-@Component({ - selector: "app-root", - standalone: true, - imports: [ - ProductCardComponent, - ShoppingCartComponent, - RecommendationsComponent, - AngularRenderScanMarkDirective, - ], - template: ` -
-

Developer Store

- -
- -
-
- SYSTEM CONTROLLER -

Diagnostics Control

+
+
+ Render log + +
+
+ @if (log().length === 0) { +
📋
Nothing yet
+ } @else { +
+ @for (e of log(); track e.time + e.msg) { +
+ {{ e.time }} + {{ e.msg }} +
+ }
+ } +
+
+ +
+ + +
-
-
- - + +
+ + + + + +
+ + +
+ + + @if (activeTab() === 'trigger') { +
+
+
+
CD cycles
+
{{ totalCycles() }}
+
this session
+
+
+
Last trigger
+
{{ lastTrigger() }}
+
what caused the latest CD cycle
-
- -
- LIVE CYCLES: {{ reactiveCounter() }} - REAL-TIME PERFORMANCE LOG -
- @if (auditLogs().length === 0) { -
- [SYSTEM] - Listening for Angular render scan telemetry... +
+
Trigger types
+
+
+
+
Zone — click
+ +
+
+
Zone — setTimeout
+ +
+
+
Signal write
+
- } - @for (log of auditLogs(); track log.time + log.message) { -
- [{{ log.time }}] - - {{ log.message }} - +
+
Zone — setInterval
+
- } +
-
+ @if (triggers().length > 0) { +
+
+ History + +
+
+ + + + @for (t of triggers(); track t.time + t.source) { + + + + + + } + +
TimeSourceType
{{ t.time }}{{ t.source }}{{ t.kind }}
+
+
+ } +
+ } - -
-
- SANDBOX NODES -

Component Grid Matrix

+ + @if (activeTab() === 'onpush') { +
+ +
+ +
+ @if (onPushList().length === 0) { +
No candidates yet — interact with the shop, then Refresh
+ } @else { +
+
{{ onPushList().length }} component{{ onPushList().length > 1 ? 's' : '' }} to optimise
+
+ + + + @for (c of onPushList(); track c.name) { + + + + + + + } + +
ComponentWastedChecksConfidence
{{ c.selector }} +
+
+ {{ c.wastedPercentage.toFixed(0) }}% +
+
{{ c.totalChecks }}{{ c.confidence }}
+
+
+ } +
+
+
+ } -
- @for (product of products; track product.id) { - - } + + @if (activeTab() === 'zone') { +
+
+
+
Pollution events
+
{{ pollutionEvents().length }}
+
this session
+
+
+
Stream
+
{{ stream() ? '🔴 Live' : '⚪ Off' }}
+
setInterval source
+
+
+
+ + + +
+ @if (pollutionEvents().length === 0) { +
No pollution yet — use the buttons above
+ } @else { +
+
{{ pollutionEvents().length }} pollution events
+
+ + + + @for (e of pollutionEvents(); track e.time + e.trigger) { + + + + + + } + +
TimeTaskSource
{{ e.time }}{{ e.trigger }}{{ e.comps }} components
+
+
+ } +
+
+
+
+ } - + + @if (activeTab() === 'ref') { +
+ +
+
+ Fired {{ refFireCount() }} times with identical data but new object references +
-
- - -
- -
- -
+
+ + +
+ @if (refList().length === 0) { +
No instability yet — fire a few references, then Refresh
+ } @else { +
+
Instability report
+
+ + + + @for (r of refList(); track r.componentName + r.inputName) { + + + + + + + } + +
ComponentInputUnstable refsWaste
{{ r.selector }}{{ r.inputName }}{{ r.unstableRefCount }} +
+
+ {{ (r.unstableRefCount / (r.totalRenders || 1) * 100).toFixed(0) }}% +
+
+
+
+ } +
+
+
+ + } + + + @if (activeTab() === 'graph') { +
+
+ + Interact with the shop first +
+ @if (graphNodes().length === 0) { +
📊
No data yet — interact with the shop, then Refresh
+ } @else { +
+
+ {{ graphNodes().length }} components tracked +
+ {{ onPushNodeCount() }} OnPush + {{ defaultNodeCount() }} Default +
+
+
+ + + + @for (n of graphNodes(); track n.id) { + + + + + + + + } + +
ComponentStrategyRendersWastedLast trigger
{{ n.name }} + + {{ n.cdStrategy }} + + {{ n.renderCount }} + @if (n.wastedChecks > 0) { + {{ n.wastedChecks }} + } @else { + + } + + @if (n.lastTrigger) { + {{ n.lastTrigger }} + } @else { + + } +
+
+
+ } +
+ } + + + + + + `, }) -class AppComponent implements OnDestroy { - readonly products = PRODUCTS; - readonly cartMap = signal( - new Map(), - ); - - readonly reactiveCounter = signal(0); - readonly autoStreamActive = signal(false); - readonly auditLogs = signal< - { time: string; message: string; type: string }[] - >([]); - - private streamIntervalId: any = null; - - constructor() { - if (typeof window !== "undefined") { - window.addEventListener("angular-render-scan:render", this.onRenderEvent); - } - } +class AppComponent implements OnInit, OnDestroy { + + // ── Shared reactive counter ────────────────────────────────── + readonly activeTab = signal<'trigger'|'onpush'|'zone'|'ref'|'graph'>('trigger'); + + readonly counter = signal(0); + readonly signalTick = signal(0); + readonly stream = signal(false); + + + // ── Cart ───────────────────────────────────────────────────── + readonly products = PRODUCTS; + readonly cart = signal(new Map()); + readonly cartKeys = computed(() => Array.from(this.cart().keys())); + readonly cartTotal = computed(() => { let t = 0; this.cart().forEach(v => t += v.product.price * v.qty); return t; }); + + // ── Trigger attribution ────────────────────────────────────── + readonly totalCycles = signal(0); + readonly lastTrigger = signal('—'); + readonly triggers = signal([]); + readonly triggerKind = computed((): string => { + const t = this.lastTrigger(); + if (t.startsWith('zone:')) return 'zone'; + if (t.startsWith('signal:')) return 'signal'; + return 'unknown'; + }); - ngOnDestroy() { - if (typeof window !== "undefined") { - window.removeEventListener( - "angular-render-scan:render", - this.onRenderEvent, - ); - } - if (this.streamIntervalId) { - clearInterval(this.streamIntervalId); + // ── Log ────────────────────────────────────────────────────── + readonly log = signal([]); + + // ── Zone pollution ─────────────────────────────────────────── + readonly pollutionEvents = signal([]); + + // ── OnPush candidates ──────────────────────────────────────── + readonly onPushList = signal>([]); + + // ── Ref instability ────────────────────────────────────────── + readonly refCfg = signal({ theme: 'dark', size: 12 }); + readonly refFireCount = signal(0); + readonly refList = signal>([]); + + // ── CD graph ───────────────────────────────────────────────── + readonly graphNodes = signal['nodes']>([]); + readonly onPushNodeCount = computed(() => this.graphNodes().filter(n => n.cdStrategy === 'OnPush').length); + readonly defaultNodeCount = computed(() => this.graphNodes().filter(n => n.cdStrategy === 'Default').length); + + // ── Code snippets ──────────────────────────────────────────── + readonly codeOnPush = `// Before (Default — re-checks every cycle) +@Component({ changeDetection: ChangeDetectionStrategy.Default }) + +// After (OnPush — only re-checks when @Input reference changes or events fire) +@Component({ changeDetection: ChangeDetectionStrategy.OnPush })`; + + readonly codeZone = `// ❌ Problem — setInterval inside Angular triggers CD every second +constructor(private data: DataService) { + setInterval(() => this.data.refresh(), 1000); // Zone.js sees this! +} + +// ✅ Fix — run outside Angular's zone +constructor(private ngZone: NgZone, private data: DataService) { + ngZone.runOutsideAngular(() => { + setInterval(() => { + // only pull Angular back in when you need a real UI update + if (dataChanged) ngZone.run(() => this.data.refresh()); + }, 1000); + }); +}`; + + readonly codeRef = `// ❌ Problem — new object created each render (same values, new reference) +@Component({ template: '' }) +getConfig() { return { theme: 'dark', size: 12 }; } // called every cycle! + +// ✅ Fix 1 — lift to a class field (never re-created) +readonly config = { theme: 'dark', size: 12 }; + +// ✅ Fix 2 — use a signal +readonly config = signal({ theme: 'dark', size: 12 }); + +// ✅ Fix 3 — use computed() so it only changes when dependencies change +readonly config = computed(() => ({ theme: this.theme(), size: this.size() }));`; + + // ── Internals ──────────────────────────────────────────────── + private streamId: ReturnType | null = null; + + private readonly onRender = (e: Event) => { + const d = (e as CustomEvent<{ name: string; duration: number; trigger?: string }>).detail; + const t = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false }); + this.totalCycles.update(c => c + 1); + if (d.trigger) { + this.lastTrigger.set(d.trigger); + this.triggers.update(h => [{ + time: t, + source: d.trigger!, + kind: d.trigger!.startsWith('zone') ? 'zone' : d.trigger!.startsWith('signal') ? 'signal' : 'unknown', + }, ...h.slice(0, 49)]); } - } + this.log.update(l => [{ + time: t, + msg: `[${d.name}] ${d.duration.toFixed(2)}ms${d.trigger ? ` ← ${d.trigger}` : ''}`, + type: d.duration > 15 ? 'warn' : 'info', + }, ...l.slice(0, 24)]); + }; - private readonly onRenderEvent = (e: Event) => { - const detail = (e as CustomEvent<{ name: string; duration: number }>) - .detail; - const now = new Date().toLocaleTimeString([], { - hour: "2-digit", - minute: "2-digit", - second: "2-digit", - hour12: false, - }); - this.auditLogs.update((logs) => [ - { - time: now, - message: `Render: [${detail.name}] in ${detail.duration.toFixed(2)}ms`, - type: detail.duration > 15 ? "warn" : "info", - }, - ...logs.slice(0, 14), - ]); + private readonly onPollution = (e: Event) => { + const d = (e as CustomEvent).detail; + const t = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false }); + this.pollutionEvents.update(l => [{ + time: t, + trigger: d?.suspectedTrigger ?? 'unknown zone task', + comps: d?.componentCount ?? 0, + }, ...l.slice(0, 49)]); }; - addToCart(product: Product) { - const map = new Map(this.cartMap()); - const existing = map.get(product.id); - if (existing) { - map.set(product.id, { ...existing, quantity: existing.quantity + 1 }); - } else { - map.set(product.id, { product, quantity: 1 }); - } - this.cartMap.set(map); + ngOnInit() { + window.addEventListener('angular-render-scan:render', this.onRender); + window.addEventListener('angular-render-scan:zone-pollution', this.onPollution); } - onCheckout() { - alert("Thanks for your purchase!"); + ngOnDestroy() { + window.removeEventListener('angular-render-scan:render', this.onRender); + window.removeEventListener('angular-render-scan:zone-pollution', this.onPollution); + if (this.streamId) clearInterval(this.streamId); } - triggerSpike() { - for (let i = 0; i < 30; i++) { - setTimeout(() => { - this.reactiveCounter.update((c) => c + 1); - }, i * 15); - } + // ── Cart actions ───────────────────────────────────────────── + addToCart(product: Product) { + const m = new Map(this.cart()); + const e = m.get(product.id); + m.set(product.id, e ? { ...e, qty: e.qty + 1 } : { product, qty: 1 }); + this.cart.set(m); } + removeFromCart(id: number) { + const m = new Map(this.cart()); + const e = m.get(id); + if (!e) return; + if (e.qty > 1) m.set(id, { ...e, qty: e.qty - 1 }); else m.delete(id); + this.cart.set(m); + } + checkout() { + this.cart.set(new Map()); + const t = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false }); + this.log.update(l => [{ time: t, msg: 'Checkout complete — cart cleared', type: 'success' }, ...l.slice(0, 24)]); + } + + // ── Trigger generators ─────────────────────────────────────── + fireMacroTask() { setTimeout(() => this.counter.update(c => c + 1), 300); } - toggleAutoStream() { - this.autoStreamActive.update((v) => !v); - if (this.autoStreamActive()) { - this.streamIntervalId = setInterval(() => { - this.reactiveCounter.update((c) => c + 1); - }, 250); + toggleStream() { + this.stream.update(v => !v); + if (this.stream()) { + this.streamId = setInterval(() => this.counter.update(c => c + 1), 500); } else { - if (this.streamIntervalId) { - clearInterval(this.streamIntervalId); - this.streamIntervalId = null; - } + if (this.streamId) { clearInterval(this.streamId); this.streamId = null; } } } - simulateMemoryLeak() { - const card = document.querySelector("app-product-card"); - if (card) { - card.remove(); // Remove element dynamically to create a disconnected leak! - this.auditLogs.update((logs) => [ - { - time: new Date().toLocaleTimeString([], { - hour: "2-digit", - minute: "2-digit", - second: "2-digit", - hour12: false, - }), - message: - "SIMULATED LEAK: Removed product card DOM element without destroying component!", - type: "warn", - }, - ...logs.slice(0, 14), - ]); - } + // ── Zone pollution ─────────────────────────────────────────── + fireSinglePollution() { setTimeout(() => this.counter.update(c => c + 1), 100); } + + // ── Ref instability ────────────────────────────────────────── + fireRefInstab() { + // Always a new object reference, same values + this.refCfg.set({ theme: 'dark', size: 12 }); + this.refFireCount.update(c => c + 1); } + fireNewRef() { this.fireRefInstab(); } + refreshRefInstab() { this.refList.set(getReferentialInstability()); } + + // ── OnPush candidates ──────────────────────────────────────── + refreshOnPush() { this.onPushList.set(getOnPushCandidates()); } + + // ── CD graph ───────────────────────────────────────────────── + refreshGraph() { + const g = getCdGraph(); + this.graphNodes.set(g.nodes); + } + + } +// ── Bootstrap ───────────────────────────────────────────────── bootstrapApplication(AppComponent, { providers: [ provideAngularRenderScan({ enabled: true, showToolbar: true, - animationSpeed: "slow", + animationSpeed: 'fast', showFPS: true, - log: true, + log: false, + trackReferentialStability: true, + referentialStabilityDepth: 4, + onPushCandidateThreshold: 40, + maxZonePollutionEvents: 50, + showCdGraph: true, + maxRecordedCycles: 30, + showCopyPrompt: true, + promptContext: 'Angular 22 demo — showcase of all Angular Render Scan features', + onZonePollution: (ev) => { + window.dispatchEvent(new CustomEvent('angular-render-scan:zone-pollution', { detail: ev })); + }, }), ], -}).catch((error: unknown) => console.error(error)); +}); diff --git a/demo/src/styles.css b/demo/src/styles.css index 9dccba8..d37861e 100644 --- a/demo/src/styles.css +++ b/demo/src/styles.css @@ -1,710 +1,288 @@ +/* ── Fonts ─────────────────────────────────────────────────── */ +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap'); + +/* ── Tokens ────────────────────────────────────────────────── */ :root { - --font-cyber: "Courier New", Courier, monospace; - --font-sans: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; - - /* Premium SaaS Light Theme Palette */ - --bg-primary: #f8fafc; /* Slate 50 */ - --bg-panel: rgba(255, 255, 255, 0.75); - --bg-panel-inner: #f1f5f9; /* Slate 100 */ - --border-cyber: rgba(15, 23, 42, 0.08); - --border-glow: #3b82f6; /* Blue 500 */ - - --color-neon-cyan: #2563eb; /* Indigo 600 */ - --color-neon-purple: #7c3aed; /* Violet 600 */ - --color-neon-rose: #f43f5e; /* Rose 500 */ - - --text-primary: #0f172a; /* Slate 900 */ - --text-muted: #64748b; /* Slate 500 */ - --text-cyber: #2563eb; - - --success: #10b981; /* Emerald 500 */ - --warning: #f59e0b; /* Amber 500 */ - --danger: #ef4444; /* Red 500 */ - - --shadow-glow: 0 4px 6px -1px rgba(0, 0, 0, 0.02), 0 2px 4px -2px rgba(0, 0, 0, 0.02), 0 10px 15px -3px rgba(0, 0, 0, 0.03); - --shadow-glow-strong: 0 10px 30px rgba(37, 99, 235, 0.06); - --transition-fast: 0.15s ease; + --font: 'Inter', system-ui, sans-serif; + --mono: 'JetBrains Mono', monospace; + + --bg: #f8fafc; + --surface: #ffffff; + --border: #e2e8f0; + --text: #0f172a; + --muted: #64748b; + + --indigo: #6366f1; + --indigo-l: #eef2ff; + --green: #10b981; + --green-l: #d1fae5; + --amber: #f59e0b; + --amber-l: #fef3c7; + --red: #ef4444; + --red-l: #fee2e2; + --purple: #8b5cf6; + --purple-l: #ede9fe; + --cyan: #06b6d4; + --cyan-l: #cffafe; + + --r: 8px; + --r2: 12px; + --sh: 0 1px 3px rgba(0,0,0,.06), 0 1px 2px rgba(0,0,0,.04); + --sh2:0 4px 16px rgba(0,0,0,.08); } :root.dark { - --bg-primary: #0f172a; - --bg-panel: rgba(30, 41, 59, 0.75); - --bg-panel-inner: #1e293b; - --border-cyber: rgba(255, 255, 255, 0.08); - --border-glow: #60a5fa; - - --color-neon-cyan: #3b82f6; - --color-neon-purple: #a78bfa; - --color-neon-rose: #fb7185; - - --text-primary: #f8fafc; - --text-muted: #94a3b8; - --text-cyber: #3b82f6; - - --success: #34d399; - --warning: #fbbf24; - --danger: #f87171; - - --shadow-glow: 0 4px 6px -1px rgba(0, 0, 0, 0.5); - --shadow-glow-strong: 0 10px 30px rgba(0, 0, 0, 0.5); -} - -* { - box-sizing: border-box; - margin: 0; - padding: 0; -} - -html, body { - width: 100%; - height: 100vh; - overflow: hidden; - background-color: var(--bg-primary); - font-family: var(--font-sans); - color: var(--text-primary); - transition: background-color var(--transition-fast); -} - -/* Subtle architectural grid background */ -body { - position: relative; -} - -body::before { - content: ""; - position: absolute; - inset: 0; - z-index: -2; - background-image: - radial-gradient(circle at 10% 20%, rgba(37, 99, 235, 0.04) 0%, transparent 40%), - radial-gradient(circle at 90% 80%, rgba(124, 58, 237, 0.03) 0%, transparent 40%), - linear-gradient(rgba(15, 23, 42, 0.015) 1px, transparent 1px), - linear-gradient(90deg, rgba(15, 23, 42, 0.015) 1px, transparent 1px); - background-size: 100% 100%, 100% 100%, 24px 24px, 24px 24px; -} - -body.grid-lines-off::before { - background-image: - radial-gradient(circle at 10% 20%, rgba(37, 99, 235, 0.04) 0%, transparent 40%), - radial-gradient(circle at 90% 80%, rgba(124, 58, 237, 0.03) 0%, transparent 40%); -} - -/* Main Layout Framework: Fixed 100vh, No Scroll */ -main { - width: 100vw; - height: 100vh; - display: grid; - grid-template-rows: minmax(0, 1fr); - gap: 16px; - padding: 16px; -} - -.visually-hidden { - position: absolute; - top: -9999px; - left: -9999px; - width: 1px; - height: 1px; - margin: 0; - padding: 0; - overflow: hidden; - clip: rect(0, 0, 0, 0); - border: 0; - font-size: 0px; -} - -/* cyber header */ -.hero-banner { - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 24px; - background: var(--bg-panel); - border: 1px solid var(--border-cyber); - border-radius: 12px; - backdrop-filter: blur(16px); - box-shadow: var(--shadow-glow); - z-index: 10; -} - -.hero-brand { - display: flex; - align-items: center; - gap: 14px; -} - -.logo-container { - width: 40px; - height: 40px; - border-radius: 10px; - background: linear-gradient(135deg, rgba(37, 99, 235, 0.08), rgba(124, 58, 237, 0.08)); - border: 1px solid rgba(37, 99, 235, 0.18); - display: grid; - place-items: center; - color: var(--color-neon-cyan); -} - -.logo-svg { - width: 22px; - height: 22px; -} - -.brand-details { - display: flex; - flex-direction: column; -} - -.hero-brand h1 { - font-size: 18px; - font-weight: 800; - letter-spacing: -0.02em; - color: var(--text-primary); - line-height: 1.2; -} - -.brand-meta { - display: flex; - align-items: center; - gap: 8px; - margin-top: 2px; -} - -.hero-brand .subtitle { - font-size: 11px; - color: var(--text-muted); - font-weight: 500; - letter-spacing: 0.01em; -} - -.meta-divider { - width: 3px; - height: 3px; - border-radius: 50%; - background: var(--text-muted); - opacity: 0.6; -} - -.version-badge { - font-size: 9px; - font-family: var(--font-cyber); - color: var(--color-neon-purple); - background: rgba(124, 58, 237, 0.06); - border: 1px solid rgba(124, 58, 237, 0.15); - padding: 1px 6px; - border-radius: 4px; - font-weight: 600; -} - -.zone-badge { - font-size: 10px; - font-weight: 600; - color: var(--text-muted); - background: var(--bg-panel-inner); - border: 1px solid var(--border-cyber); - padding: 5px 10px; - border-radius: 20px; - text-transform: uppercase; - letter-spacing: 0.02em; -} - -.hero-actions { - display: flex; - align-items: center; - gap: 16px; -} - -.status-pill { - display: inline-flex; - align-items: center; - gap: 6px; - padding: 6px 12px; - border-radius: 20px; - background: rgba(16, 185, 129, 0.08); - border: 1px solid rgba(16, 185, 129, 0.15); - color: var(--success); - font-size: 11px; - font-weight: 700; - text-transform: uppercase; - letter-spacing: 0.05em; -} - -.pulse-dot { - width: 6px; - height: 6px; - background: var(--success); - border-radius: 50%; - animation: pulse-glow 1.5s infinite; -} - -@keyframes pulse-glow { - 0% { transform: scale(0.9); opacity: 0.6; } - 50% { transform: scale(1.3); opacity: 1; box-shadow: 0 0 8px var(--success); } - 100% { transform: scale(0.9); opacity: 0.6; } -} - -.theme-toggle-btn { - background: var(--bg-panel-inner); - border: 1px solid var(--border-cyber); - color: var(--text-primary); - padding: 8px 16px; - border-radius: 8px; - font-size: 11px; - font-weight: 700; - cursor: pointer; - transition: all var(--transition-fast); -} - -.theme-toggle-btn:hover { - border-color: var(--border-glow); - box-shadow: var(--shadow-glow); - transform: translateY(-1px); -} - -/* Core Dashboard Grid - 3 columns, 100vh constraint */ -.cyber-grid { - display: grid; - grid-template-columns: 320px minmax(0, 1fr) 350px; - gap: 16px; - min-height: 0; -} - -/* Panel Containers */ -.cyber-panel { - display: grid; - grid-template-rows: auto minmax(0, 1fr); - background: var(--bg-panel); - border: 1px solid var(--border-cyber); - border-radius: 12px; - box-shadow: var(--shadow-glow); - backdrop-filter: blur(16px); - min-height: 0; - overflow: hidden; -} - -.panel-header { - padding: 20px 20px 12px 20px; - border-bottom: 1px solid rgba(15, 23, 42, 0.04); -} - -.panel-header h2 { - font-size: 18px; - font-weight: 800; - color: var(--text-primary); -} - -.kicker { - font-size: 9px; - font-family: var(--font-cyber); - color: var(--color-neon-cyan); - font-weight: 700; - letter-spacing: 0.15em; - text-transform: uppercase; - margin-bottom: 2px; - display: block; -} - -/* LEFT PANEL: Diagnostics controls & log */ -.control-panel { - grid-template-rows: auto auto minmax(0, 1fr); -} - -.control-actions { - padding: 16px 20px; - display: flex; - flex-direction: column; - gap: 10px; - border-bottom: 1px solid rgba(15, 23, 42, 0.04); -} - -.cyber-btn { - display: flex; - align-items: center; - justify-content: center; - padding: 12px; - border-radius: 8px; - border: 1px solid var(--border-cyber); - font-size: 11px; - font-weight: 700; - cursor: pointer; - transition: all var(--transition-fast); - color: var(--text-primary); - text-transform: uppercase; - letter-spacing: 0.05em; -} - -.primary-glow { - background: linear-gradient(135deg, rgba(37, 99, 235, 0.08), rgba(124, 58, 237, 0.08)); - border-color: rgba(37, 99, 235, 0.25); - color: var(--color-neon-cyan); -} - -.primary-glow:hover { - border-color: var(--color-neon-cyan); - box-shadow: var(--shadow-glow-strong); - transform: translateY(-1px); -} - -.secondary-glow { - background: var(--bg-panel-inner); -} - -.secondary-glow:hover { - border-color: var(--color-neon-purple); - box-shadow: 0 0 15px rgba(124, 58, 237, 0.1); - transform: translateY(-1px); -} - -/* Metrics and Logs Visualizer */ -.metrics-visualizer { - display: grid; - grid-template-rows: auto auto minmax(0, 1fr); - padding: 16px 20px 20px 20px; - min-height: 0; - height: 100%; -} - -.metrics-visualizer .kicker { - margin-bottom: 10px; -} - -.audit-log { - background: #f8fafc; - border: 1px solid rgba(15, 23, 42, 0.06); - border-radius: 8px; - padding: 12px; - font-family: var(--font-cyber); - font-size: 10px; - overflow-y: auto; - overflow-x: hidden; - min-height: 0; - display: flex; - flex-direction: column; - gap: 8px; -} - -.log-line { - line-height: 1.4; - border-left: 2px solid var(--text-muted); - padding-left: 6px; - animation: fadeIn 0.3s ease-out; - display: flex; - align-items: flex-start; - flex-wrap: wrap; -} - -.log-time { - color: var(--text-muted); - margin-right: 6px; - flex-shrink: 0; -} - -.log-msg { - word-break: break-word; - overflow-wrap: anywhere; - white-space: normal; - flex: 1 1 auto; -} - -.log-msg.info { - color: var(--color-neon-cyan); - font-weight: 600; -} - -.log-msg.warn { - color: var(--color-neon-rose); - font-weight: 700; -} - -@keyframes fadeIn { - from { opacity: 0; transform: translateY(-3px); } - to { opacity: 1; transform: translateY(0); } -} - -/* MIDDLE PANEL: Sandbox Components Node Matrix */ -.sandbox-panel { - grid-template-rows: auto minmax(0, 1fr); -} - -.nodes-container { - padding: 20px; - overflow-y: auto; - display: flex; - flex-direction: column; - gap: 16px; - min-height: 0; -} - -/* Custom modern scrollbars */ -.audit-log::-webkit-scrollbar, -.nodes-container::-webkit-scrollbar, -.cart-items::-webkit-scrollbar { - width: 5px; -} - -.audit-log::-webkit-scrollbar-track, -.nodes-container::-webkit-scrollbar-track, -.cart-items::-webkit-scrollbar-track { - background: transparent; -} - -.audit-log::-webkit-scrollbar-thumb, -.nodes-container::-webkit-scrollbar-thumb, -.cart-items::-webkit-scrollbar-thumb { - background: rgba(15, 23, 42, 0.08); - border-radius: 10px; -} - -.audit-log::-webkit-scrollbar-thumb:hover, -.nodes-container::-webkit-scrollbar-thumb:hover, -.cart-items::-webkit-scrollbar-thumb:hover { - background: var(--border-glow); -} - -/* Premium Reskin of Product Cards */ -.product-card { - display: grid; - grid-template-columns: 52px 1fr; - grid-template-rows: auto auto; - gap: 12px; - padding: 16px; - background: var(--bg-panel-inner); - border: 1px solid var(--border-cyber); - border-radius: 10px; - transition: all var(--transition-fast); -} - -.product-card:hover { - border-color: var(--border-glow); - box-shadow: var(--shadow-glow); - transform: translateY(-2px); -} - -.product-icon { - width: 52px; - height: 52px; - display: grid; - place-items: center; - border-radius: 8px; - background: rgba(37, 99, 235, 0.05); - border: 1px solid var(--border-cyber); - font-size: 24px; -} - -.product-info { - display: flex; - flex-direction: column; - justify-content: center; -} - -.product-info h3 { - font-size: 14px; - font-weight: 700; - color: var(--text-primary); -} - -.product-info p { - font-size: 11px; - color: var(--text-muted); - margin-top: 4px; - line-height: 1.3; -} - -.product-footer { - grid-column: 1 / -1; - display: flex; - align-items: center; - justify-content: space-between; - padding-top: 8px; - border-top: 1px solid rgba(15, 23, 42, 0.03); -} - -.price { - font-size: 14px; - font-weight: 800; - font-family: var(--font-cyber); - color: var(--color-neon-cyan); -} - -.product-footer button { - background: linear-gradient(135deg, var(--color-neon-cyan), var(--color-neon-purple)); - color: #fff; - border: none; - padding: 8px 16px; - border-radius: 6px; - font-size: 10px; - font-weight: 700; - text-transform: uppercase; - cursor: pointer; - transition: all var(--transition-fast); -} - -.product-footer button:hover { - box-shadow: 0 4px 12px rgba(37, 99, 235, 0.25); - transform: translateY(-1px); -} - -/* Recommendations Panel (Slow Node) */ -.slow-panel { - display: flex; - flex-direction: column; - gap: 12px; - padding: 16px; - background: rgba(244, 63, 94, 0.02); - border: 1px solid rgba(244, 63, 94, 0.12); - border-radius: 10px; - color: var(--text-primary); -} - -.slow-panel p { - font-size: 11px; - color: var(--text-muted); - line-height: 1.35; -} - -.recommendation-badge { - display: flex; - align-items: center; - justify-content: space-between; - padding: 12px; - background: rgba(244, 63, 94, 0.04); - border: 1px solid rgba(244, 63, 94, 0.08); - border-radius: 8px; -} - -.recommendation-badge span { - font-size: 10px; - font-family: var(--font-cyber); - color: var(--color-neon-rose); - font-weight: 700; -} - -.recommendation-badge strong { - font-size: 20px; - font-family: var(--font-cyber); - color: var(--color-neon-rose); - text-shadow: 0 0 8px rgba(244, 63, 94, 0.15); -} - -.slow-panel button { - width: 100%; - background: transparent; - border: 1px solid var(--color-neon-rose); - color: var(--color-neon-rose); - padding: 10px; - border-radius: 6px; - font-size: 10px; - font-weight: 700; - text-transform: uppercase; - cursor: pointer; - transition: all var(--transition-fast); -} - -.slow-panel button:hover { - background: rgba(244, 63, 94, 0.05); - box-shadow: 0 4px 10px rgba(244, 63, 94, 0.1); -} - -/* RIGHT PANEL: Reactive Shopping Cart Pipeline */ -.cart-sidebar { - display: grid; - grid-template-rows: auto minmax(0, 1fr) auto; - gap: 16px; - padding: 20px; - height: 100%; - min-height: 0; -} - -.cart-summary { - font-size: 11px; - font-family: var(--font-cyber); - color: var(--color-neon-purple); - margin-top: -8px; -} - -.cart-items { - display: flex; - flex-direction: column; - gap: 10px; - overflow-y: auto; - min-height: 0; -} - -.cart-item { - display: flex; - align-items: center; - justify-content: space-between; - padding: 10px 12px; - background: var(--bg-panel-inner); - border: 1px solid var(--border-cyber); - border-radius: 8px; - font-size: 12px; -} - -.cart-item-actions { - display: flex; - align-items: center; - gap: 8px; -} - -.qty { - font-family: var(--font-cyber); - color: var(--color-neon-cyan); - font-weight: 700; -} - -.icon-btn { - background: transparent; - border: none; - color: var(--danger); - font-size: 10px; - cursor: pointer; - transition: transform var(--transition-fast); -} - -.icon-btn:hover { - transform: scale(1.2); -} - -.checkout-btn { - background: linear-gradient(135deg, var(--color-neon-purple), var(--color-neon-rose)); - color: #fff; - border: none; - padding: 12px; - border-radius: 8px; - font-size: 11px; - font-weight: 700; - text-transform: uppercase; - cursor: pointer; - transition: all var(--transition-fast); - width: 100%; -} - -.checkout-btn:hover:not(:disabled) { - box-shadow: 0 4px 15px rgba(244, 63, 94, 0.25); - transform: translateY(-1px); -} - -.checkout-btn:disabled { - opacity: 0.4; - cursor: not-allowed; -} - -.empty-cart { - flex-grow: 1; - display: flex; - align-items: center; - justify-content: center; - flex-direction: column; - border: 1px dashed var(--border-cyber); - border-radius: 8px; - color: var(--text-muted); - font-size: 12px; - text-align: center; -} - -/* Adaptive styles for narrower screens */ -@media (max-width: 1024px) { - .cyber-grid { - grid-template-columns: 260px minmax(0, 1fr) 280px; - } -} + --bg: #0f172a; + --surface: #1e293b; + --border: rgba(255,255,255,.08); + --text: #f1f5f9; + --muted: #94a3b8; + + --indigo-l: rgba(99,102,241,.15); + --green-l: rgba(16,185,129,.12); + --amber-l: rgba(245,158,11,.12); + --red-l: rgba(239,68,68,.12); + --purple-l: rgba(139,92,246,.12); + --cyan-l: rgba(6,182,212,.12); +} + +/* ── Reset ─────────────────────────────────────────────────── */ +*,*::before,*::after{box-sizing:border-box;margin:0;padding:0} +html,body{font-family:var(--font);font-size:14px;line-height:1.6;background:var(--bg);color:var(--text)} +button{font-family:inherit;cursor:pointer;border:none} +code,pre{font-family:var(--mono)} + +/* ── Shell (full-viewport two-column layout) ───────────────── */ +html,body{height:100%;overflow:hidden} +.shell{ + display:grid; + grid-template-columns:340px 1fr; + height:100vh; + gap:0; + background:var(--bg); +} +/* ── Left panel — Shop ─────────────────────────────────────── */ +.panel-left{ + display:flex;flex-direction:column;gap:8px; + padding:12px;overflow-y:auto; + border-right:1px solid var(--border); + background:var(--surface); +} +.log-card{flex:1;min-height:0;display:flex;flex-direction:column} +.log-card .card-body{flex:1;min-height:0} +.log-scroll{overflow-y:auto} +/* ── Right panel — Tabs ────────────────────────────────────── */ +.panel-right{ + display:flex;flex-direction:column; + min-height:0;overflow:hidden; +} +.tab-bar{ + display:flex;gap:0; + border-bottom:1px solid var(--border); + background:var(--surface); + padding:0 12px; + flex-shrink:0; +} +.tab-btn{ + padding:10px 14px;font-size:12px;font-weight:600; + background:none;border:none;border-bottom:2px solid transparent; + color:var(--muted);cursor:pointer;white-space:nowrap; + transition:.15s;margin-bottom:-1px; +} +.tab-btn:hover{color:var(--text)} +.tab-btn.active{color:var(--indigo);border-bottom-color:var(--indigo)} +.tab-content{flex:1;overflow-y:auto;padding:12px} +.tab-panel{display:flex;flex-direction:column;gap:8px} +/* ── Trigger boxes ─────────────────────────────────────────── */ +.trig-box{ + background:rgba(0,0,0,.03);border-radius:var(--r); + padding:10px;display:flex;flex-direction:column;gap:6px; +} +.trig-title{font-size:11px;font-weight:600} + +/* ── Card ──────────────────────────────────────────────────── */ +.card{ + background:var(--surface);border:1px solid var(--border); + border-radius:var(--r2);box-shadow:var(--sh);overflow:hidden; +} +.card + .card{margin-top:12px} +.card-head{ + display:flex;align-items:center;justify-content:space-between; + padding:10px 14px;border-bottom:1px solid var(--border); +} +.card-label{font-size:12px;font-weight:600;color:var(--muted)} +.card-body{padding:14px} +.card-body.flush{padding:0} + +/* ── Buttons ───────────────────────────────────────────────── */ +.btn{ + display:inline-flex;align-items:center;gap:6px; + padding:8px 16px;border-radius:var(--r);font-size:13px;font-weight:500; + border:1px solid transparent;transition:.15s; +} +.btn-primary{background:var(--indigo);color:#fff;border-color:var(--indigo)} +.btn-primary:hover{background:#4f46e5} +.btn-outline{background:transparent;color:var(--text);border-color:var(--border)} +.btn-outline:hover{border-color:var(--indigo);color:var(--indigo)} +.btn-danger{background:var(--red-l);color:var(--red);border-color:rgba(239,68,68,.2)} +.btn-danger:hover{background:var(--red);color:#fff} +.btn-sm{padding:5px 11px;font-size:12px} +.btn:disabled{opacity:.4;cursor:not-allowed;pointer-events:none} +.btn-row{display:flex;gap:8px;flex-wrap:wrap;align-items:center} + +/* ── Badges ────────────────────────────────────────────────── */ +.badge{ + display:inline-flex;align-items:center;gap:4px; + padding:2px 8px;border-radius:20px;font-size:11px;font-weight:600; +} +.badge-green {background:var(--green-l);color:var(--green)} +.badge-amber {background:var(--amber-l);color:var(--amber)} +.badge-red {background:var(--red-l); color:var(--red)} +.badge-indigo{background:var(--indigo-l);color:var(--indigo)} +.badge-purple{background:var(--purple-l);color:var(--purple)} +.badge-cyan {background:var(--cyan-l); color:var(--cyan)} +.badge-gray {background:rgba(0,0,0,.06);color:var(--muted)} + +/* ── Stat row ──────────────────────────────────────────────── */ +.stat-row{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:8px} +.stat{ + flex:1;min-width:100px; + background:var(--surface);border:1px solid var(--border); + border-radius:var(--r);padding:10px 12px;box-shadow:var(--sh); +} +.stat-label{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;color:var(--muted)} +.stat-value{font-size:20px;font-weight:700;letter-spacing:-1px;line-height:1;margin-top:3px} +.stat-sub{font-size:11px;color:var(--muted);margin-top:2px} +.stat.green .stat-value{color:var(--green)} +.stat.amber .stat-value{color:var(--amber)} +.stat.red .stat-value{color:var(--red)} +.stat.indigo.stat-value{color:var(--indigo)} + +/* ── Trigger pill ──────────────────────────────────────────── */ +.tpill{ + display:inline-flex;align-items:center;gap:4px; + padding:3px 9px;border-radius:20px;font-size:11px;font-weight:600;font-family:var(--mono); +} +.tpill.zone {background:var(--amber-l);color:var(--amber)} +.tpill.signal{background:var(--purple-l);color:var(--purple)} +.tpill.router{background:var(--cyan-l);color:var(--cyan)} +.tpill.manual{background:var(--red-l);color:var(--red)} +.tpill.unknown{background:rgba(0,0,0,.05);color:var(--muted)} + +/* ── Progress bar ──────────────────────────────────────────── */ +.bar{height:6px;border-radius:3px;background:rgba(0,0,0,.06);overflow:hidden;flex:1} +.bar-fill{height:100%;border-radius:3px;transition:width .4s} +.bar-fill.green {background:var(--green)} +.bar-fill.amber {background:var(--amber)} +.bar-fill.red {background:var(--red)} +.bar-fill.indigo{background:var(--indigo)} + +/* ── Live log ──────────────────────────────────────────────── */ +.log{display:flex;flex-direction:column;gap:1px;max-height:220px;overflow-y:auto} +.log-row{ + display:flex;gap:10px;padding:6px 14px;border-radius:4px; + font-family:var(--mono);font-size:12px; +} +.log-row:hover{background:rgba(0,0,0,.03)} +.log-time{color:var(--muted);flex-shrink:0} +.log-msg{flex:1;color:var(--text)} +.log-row.warn .log-msg{color:var(--amber)} +.log-row.error .log-msg{color:var(--red)} +.log-row.success .log-msg{color:var(--green)} +.log-row.info .log-msg{color:var(--cyan)} + +/* ── Table ─────────────────────────────────────────────────── */ +.tbl{width:100%;border-collapse:collapse} +.tbl th{ + text-align:left;padding:7px 12px; + font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.4px; + color:var(--muted);border-bottom:1px solid var(--border);background:rgba(0,0,0,.02); +} +.tbl td{padding:7px 12px;font-size:12px;border-bottom:1px solid rgba(0,0,0,.04);vertical-align:middle} +.tbl tr:last-child td{border-bottom:none} +.tbl tr:hover td{background:rgba(0,0,0,.02)} +:root.dark .tbl th{background:rgba(255,255,255,.02)} +:root.dark .tbl tr:hover td{background:rgba(255,255,255,.02)} + +/* ── Code block ────────────────────────────────────────────── */ +.code{ + background:#1e2030;color:#a9b1d6; + border-radius:var(--r);padding:16px; + font-family:var(--mono);font-size:12px;line-height:1.7; + overflow-x:auto;white-space:pre; +} + +/* ── Product list ──────────────────────────────────────────── */ +.product-row{ + display:flex;align-items:center;gap:12px; + padding:10px 16px;border-bottom:1px solid rgba(0,0,0,.04); +} +.product-row:last-child{border-bottom:none} +.product-row:hover{background:rgba(0,0,0,.02)} +.p-emoji{font-size:20px;flex-shrink:0} +.p-name{font-size:13px;font-weight:500;flex:1} +.p-cat{font-size:11px;color:var(--muted)} +.p-price{font-size:14px;font-weight:700;color:var(--indigo);flex-shrink:0} + +/* ── Cart ──────────────────────────────────────────────────── */ +.cart-row{ + display:flex;align-items:center;gap:10px; + padding:8px 16px;border-bottom:1px solid rgba(0,0,0,.04); +} +.cart-row:last-child{border-bottom:none} +.c-emoji{font-size:16px} +.c-name{flex:1;font-size:12px} +.c-qty{font-size:12px;font-weight:600;color:var(--muted)} + + +.demo-zone-label{ + font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.5px; + color:var(--indigo);margin-bottom:12px;display:flex;align-items:center;gap:6px; +} + +/* ── Section divider ───────────────────────────────────────── */ +.section-divider{ + display:flex;align-items:center;gap:12px;margin:48px 0 32px; +} +.section-divider-line{flex:1;height:1px;background:var(--border)} +.section-divider-text{ + font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:1px;color:var(--muted); + white-space:nowrap; +} + +/* ── Empty ─────────────────────────────────────────────────── */ +.empty{ + display:flex;flex-direction:column;align-items:center;justify-content:center; + gap:6px;padding:20px;color:var(--muted);text-align:center;font-size:12px; +} +.empty-icon{font-size:22px;opacity:.35} + +/* ── Slow component callout ────────────────────────────────── */ +.callout{ + display:flex;align-items:flex-start;gap:12px; + padding:14px 16px;border-radius:var(--r); + background:var(--amber-l);border:1px solid rgba(245,158,11,.25); +} +.callout.red{background:var(--red-l);border-color:rgba(239,68,68,.2)} +.callout-icon{font-size:20px;flex-shrink:0} +.callout-title{font-size:13px;font-weight:600;color:var(--amber)} +.callout.red .callout-title{color:var(--red)} +.callout-body{font-size:12px;color:var(--muted);margin-top:2px} + +/* ── Result box (shows live computed value) ────────────────── */ +.result-box{ + background:rgba(0,0,0,.04);border-radius:var(--r); + padding:10px 14px;font-family:var(--mono);font-size:13px; + display:flex;justify-content:space-between;align-items:center; +} +:root.dark .result-box{background:rgba(255,255,255,.05)} + +/* ── Scrollbar ─────────────────────────────────────────────── */ +::-webkit-scrollbar{width:5px;height:5px} +::-webkit-scrollbar-track{background:transparent} +::-webkit-scrollbar-thumb{background:rgba(0,0,0,.1);border-radius:3px} +:root.dark ::-webkit-scrollbar-thumb{background:rgba(255,255,255,.1)} + +/* ── Anim ──────────────────────────────────────────────────── */ +@keyframes fadeUp{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}} +.fade-up{animation:fadeUp .25s ease} diff --git a/docs/assets/angular-render-scan-demo.gif b/docs/assets/angular-render-scan-demo.gif index a4ae2bc..7a410fb 100644 Binary files a/docs/assets/angular-render-scan-demo.gif and b/docs/assets/angular-render-scan-demo.gif differ diff --git a/feature.md b/feature.md index cd55879..425ee51 100644 --- a/feature.md +++ b/feature.md @@ -14,17 +14,23 @@ The project is structured using Domain-Driven Design (DDD) principles: ## Current Feature Slice -- **Public API:** `scan`, `setOptions`, `getOptions`, `stop`, `getSessionData`, `getWastedStats`, `getLeakedComponents`, `startRenderAudit`. -- **Angular Integration:** Provider mode wraps `ApplicationRef.tick()` for full-cycle timing and uses Angular's internal global `ɵsetProfiler` for zero-setup component auto-instrumentation. Supports manual directives as a fallback. -- **Production Safety:** Built-in `isDevMode()` guard entirely shuts down the scanner for production builds unless bypassed via `dangerouslyForceRunInProduction`. -- **Overlay:** Fixed canvas plus shadow-DOM toolbar. Features include pointer-events passthrough, draggable toolbar, clear stats button, checkbox toggle, sampled FPS, current cycle time, CPU main thread tracking, and slowest component. +- **Public API:** `scan`, `setOptions`, `getOptions`, `stop`, `getSessionData`, `getWastedStats`, `getLeakedComponents`, `startRenderAudit`, `getOnPushCandidates`, `getReferentialInstability`, `getZonePollutionEvents`, `getCdGraph`. +- **Angular Integration:** Provider mode wraps `ApplicationRef.tick()` for full-cycle timing and uses Angular's internal global `ɵsetProfiler` for zero-setup component auto-instrumentation. Reads `ɵcmp.changeDetection` metadata for CD strategy per component. Supports manual directives as a fallback. +- **Production Safety:** Built-in `isDevMode()` guard entirely shuts down the scanner for production builds unless bypassed via `dangerouslyForceRunInProduction`. Zone tracker is lazy-loaded so it tree-shakes to a 440-byte lazy chunk in production. `angular-render-scan/noop` subpath exports safe no-op stubs for SSR and unit tests. +- **CD Trigger Attribution:** Zone.js hook via `Zone.current.fork()` intercepts task lifecycle and classifies what fired each CD cycle: `zone:click`, `zone:setTimeout`, `zone:xhr`, `zone:fetch`, `signal:write`, `router:navigation`, `manual:markForCheck`, etc. The trigger is attached to `AngularRenderCycle` and displayed in the toolbar. +- **OnPush Candidates Surface:** `analyzeOnPushCandidates()` filters Default CD components by wasted-render percentage and assigns confidence scores. New toolbar chip shows count; panel ranks candidates. `getOnPushCandidates()` exposed on public API and included in session export + AI prompt. +- **Referential Input Stability Detection:** `checkReferentialStability()` deep-serializes `@Input()` values (configurable depth, default 4) and flags new references with identical values as `isReferentiallyUnstable: true`. Reported per input in the inspect panel with an **UNSTABLE REF** badge. `getReferentialInstability()` on public API. +- **Zone Pollution Detector:** Flags cycles with no user interaction, no signal write, and no router navigation as suspected pollution. Dispatches `angular-render-scan:zone-pollution` custom events. Toolbar **⚠ Zone** chip opens a scrollable feed. `onZonePollution` callback + `getZonePollutionEvents()` on public API. +- **Change Detection Graph:** `buildCdGraph()` accumulates parent→child render relationships across the session. `getCdGraph()` returns nodes (cdStrategy, renderCount, wastedChecks, isOnPushCandidate) and edges (parentId→childId trigger counts). +- **npx CLI Installer:** `npx angular-render-scan-cli init` auto-detects `angular.json`, installs the npm package, finds `main.ts` or `app.config.ts`, and patches `provideAngularRenderScan()` into providers. Supports `--dry-run`, `--force`, `--script-tag` (index.html CDN mode). +- **Overlay:** Fixed canvas plus shadow-DOM toolbar. Features include pointer-events passthrough, draggable toolbar, clear stats button, checkbox toggle, sampled FPS, current cycle time, CPU main thread tracking, slowest component, CD trigger badge, OnPush chip + panel, Zone pollution chip + feed. - **UX & Visuals:** Sleek dark-mode capabilities out of the box, customizable color themes, and inline SVG timeline sparklines. - **DOM Mutation Heatmap:** Outline border flashes are colored dynamically: **green** for no-ops/wasted renders, **blue** for text/attribute changes, and **red** for structural layout modifications. - **Performance Budgets & Toast Alerts:** Standardized warning, error, and rendering-rate millisecond limits. Violet/amber/red toasts float above the toolbar live on performance violations. - **CD Waterfall View:** Expandable timeline panel visualizes rendering duration tree stack offsets and nested child durations. - **Memory Leak Detector:** Audits zombie components whose DOM host elements have been disconnected but not properly garbage collected. - **Click-to-Source IDE Integration:** Pinned recommendations details panel has deep links to open files directly in Cursor, VS Code, or WebStorm. -- **Session Export:** Downloadable JSON file profiling reports containing full cycle stats, budgets violations, and memory leak listings. +- **Session Export:** Downloadable JSON file profiling reports containing full cycle stats, budget violations, memory leak listings, OnPush candidates, Zone pollution events, and referential instability reports. - **Headless Audit E2E API:** Playwright-compatible `startRenderAudit` API for continuous integration performance gate-keeping. - **Demo app:** Cyberpunk storefront dashboard displaying OnPush updates, signal bindings, expensive computations, grid overlays, dark mode, and memory leak sandbox simulations. @@ -52,15 +58,15 @@ Alpha values are kept in the paint loop so highlights fade smoothly with `animat ## Next Feature Candidates -- "Why did this render?" change-cause analysis via `@Input()` tracking or Angular 18's new change detection signals. +- Visual CD graph panel in the overlay (force-directed or hierarchy layout of `getCdGraph()` output). +- Signal dependency tracking — which signals triggered a specific component re-check. - Configurable thresholds for the heat map (currently derived from default budgets: 5ms/10ms). - More robust cycle correlation for async updates. - Angular 9, 16, and latest compatibility smoke matrix. +- Playwright headless integration for `getZonePollutionEvents()` and `getOnPushCandidates()`. ## Non-Goals For This Slice - Flame graph. - Recording export. -- Source-trigger analysis. -- Deep signal dependency graphs. - Full Angular DevTools profiler parity. \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 2fdac1b..4802347 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,26 +6,26 @@ "": { "name": "angular-render-scan-workspace", "dependencies": { - "@angular/animations": "21.2.14", - "@angular/common": "21.2.14", - "@angular/compiler": "21.2.14", - "@angular/core": "21.2.14", - "@angular/forms": "21.2.14", - "@angular/platform-browser": "21.2.14", - "@angular/router": "21.2.14", "rxjs": "^7.8.2", "tslib": "^2.8.1", "zone.js": "^0.16.0" }, "devDependencies": { - "@angular/build": "21.2.12", - "@angular/cli": "21.2.12", - "@angular/compiler-cli": "21.2.14", + "@angular/animations": "^22.0.0", + "@angular/build": "^22.0.0", + "@angular/cli": "^22.0.0", + "@angular/common": "^22.0.0", + "@angular/compiler": "^22.0.0", + "@angular/compiler-cli": "^22.0.0", + "@angular/core": "^22.0.0", + "@angular/forms": "^22.0.0", + "@angular/platform-browser": "^22.0.0", + "@angular/router": "^22.0.0", "@playwright/test": "^1.57.0", "jsdom": "^27.3.0", - "ng-packagr": "^21.2.3", + "ng-packagr": "^22.0.0", "tsup": "^8.5.1", - "typescript": "~5.9.3", + "typescript": "^6.0.3", "vitest": "^4.0.15" } }, @@ -37,57 +37,57 @@ "license": "MIT" }, "node_modules/@algolia/abtesting": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@algolia/abtesting/-/abtesting-1.14.1.tgz", - "integrity": "sha512-Dkj0BgPiLAaim9sbQ97UKDFHJE/880wgStAM18U++NaJ/2Cws34J5731ovJifr6E3Pv4T2CqvMXf8qLCC417Ew==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/@algolia/abtesting/-/abtesting-1.18.0.tgz", + "integrity": "sha512-8siuLG+FIns1AjZ/g2SDVwHz9S+ObacDQISEJvS8XsNei1zl3FXqfqQrBpmrG7ACWCyesXHbicMJtvRbg00FEw==", "dev": true, "license": "MIT", "dependencies": { - "@algolia/client-common": "5.48.1", - "@algolia/requester-browser-xhr": "5.48.1", - "@algolia/requester-fetch": "5.48.1", - "@algolia/requester-node-http": "5.48.1" + "@algolia/client-common": "5.52.0", + "@algolia/requester-browser-xhr": "5.52.0", + "@algolia/requester-fetch": "5.52.0", + "@algolia/requester-node-http": "5.52.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-abtesting": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.48.1.tgz", - "integrity": "sha512-LV5qCJdj+/m9I+Aj91o+glYszrzd7CX6NgKaYdTOj4+tUYfbS62pwYgUfZprYNayhkQpVFcrW8x8ZlIHpS23Vw==", + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.52.0.tgz", + "integrity": "sha512-wtwPgyPmO7b7sQPVgoK29c1VpfS08DnnJCmxX/oU1pV2DlMRJCzQcLN7JSloYpodyKHwM8+9wOzlAM0co3TDmA==", "dev": true, "license": "MIT", "dependencies": { - "@algolia/client-common": "5.48.1", - "@algolia/requester-browser-xhr": "5.48.1", - "@algolia/requester-fetch": "5.48.1", - "@algolia/requester-node-http": "5.48.1" + "@algolia/client-common": "5.52.0", + "@algolia/requester-browser-xhr": "5.52.0", + "@algolia/requester-fetch": "5.52.0", + "@algolia/requester-node-http": "5.52.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-analytics": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.48.1.tgz", - "integrity": "sha512-/AVoMqHhPm14CcHq7mwB+bUJbfCv+jrxlNvRjXAuO+TQa+V37N8k1b0ijaRBPdmSjULMd8KtJbQyUyabXOu6Kg==", + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.52.0.tgz", + "integrity": "sha512-9KY36bRl4AH7RjqSeDDOKnjsz4IxQFBEOB8/fWmEbdQe+Isbs5jGzVJu9NEPQ1Tgwxlf8Uf07Swj3jZyMNUZ2g==", "dev": true, "license": "MIT", "dependencies": { - "@algolia/client-common": "5.48.1", - "@algolia/requester-browser-xhr": "5.48.1", - "@algolia/requester-fetch": "5.48.1", - "@algolia/requester-node-http": "5.48.1" + "@algolia/client-common": "5.52.0", + "@algolia/requester-browser-xhr": "5.52.0", + "@algolia/requester-fetch": "5.52.0", + "@algolia/requester-node-http": "5.52.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-common": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.48.1.tgz", - "integrity": "sha512-VXO+qu2Ep6ota28ktvBm3sG53wUHS2n7bgLWmce5jTskdlCD0/JrV4tnBm1l7qpla1CeoQb8D7ShFhad+UoSOw==", + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.52.0.tgz", + "integrity": "sha512-3a/qM3dzJqqfTx7Yrw7uGQ98I3Q0rDfb4Vkv0wEzko96l7YQMxfBVz/VbLq2N+c59GweYv6Vhp8mPeqnWJSITw==", "dev": true, "license": "MIT", "engines": { @@ -95,151 +95,151 @@ } }, "node_modules/@algolia/client-insights": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.48.1.tgz", - "integrity": "sha512-zl+Qyb0nLg+Y5YvKp1Ij+u9OaPaKg2/EPzTwKNiVyOHnQJlFxmXyUZL1EInczAZsEY8hVpPCLtNfhMhfxluXKQ==", + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.52.0.tgz", + "integrity": "sha512-Rki7ACbMcvbQW0BuM84x9dkGHY47ABmv4jU6tYssat2k02p3mIUms2YOLUAMeknhmnFsj6lb6ZzOXdMWMyc1sA==", "dev": true, "license": "MIT", "dependencies": { - "@algolia/client-common": "5.48.1", - "@algolia/requester-browser-xhr": "5.48.1", - "@algolia/requester-fetch": "5.48.1", - "@algolia/requester-node-http": "5.48.1" + "@algolia/client-common": "5.52.0", + "@algolia/requester-browser-xhr": "5.52.0", + "@algolia/requester-fetch": "5.52.0", + "@algolia/requester-node-http": "5.52.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-personalization": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.48.1.tgz", - "integrity": "sha512-r89Qf9Oo9mKWQXumRu/1LtvVJAmEDpn8mHZMc485pRfQUMAwSSrsnaw1tQ3sszqzEgAr1c7rw6fjBI+zrAXTOw==", + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.52.0.tgz", + "integrity": "sha512-96s4Uzc3kk+/f4jJXIVVGWP5XlngOGNQ1x6hW9AT59pOixHlOs5tqJg+ZUS/GQ6h/iYP0ceQcmxDQeLyCLTaDQ==", "dev": true, "license": "MIT", "dependencies": { - "@algolia/client-common": "5.48.1", - "@algolia/requester-browser-xhr": "5.48.1", - "@algolia/requester-fetch": "5.48.1", - "@algolia/requester-node-http": "5.48.1" + "@algolia/client-common": "5.52.0", + "@algolia/requester-browser-xhr": "5.52.0", + "@algolia/requester-fetch": "5.52.0", + "@algolia/requester-node-http": "5.52.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-query-suggestions": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.48.1.tgz", - "integrity": "sha512-TPKNPKfghKG/bMSc7mQYD9HxHRUkBZA4q1PEmHgICaSeHQscGqL4wBrKkhfPlDV1uYBKW02pbFMUhsOt7p4ZpA==", + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.52.0.tgz", + "integrity": "sha512-lqeycNpSPe5Qa0OUWpejVvYQjQWV5nQuLT0a4aq7XzRAvCxprV/6Lf841EygdD2nrFnuS58ok7Au1uOtXzpnkg==", "dev": true, "license": "MIT", "dependencies": { - "@algolia/client-common": "5.48.1", - "@algolia/requester-browser-xhr": "5.48.1", - "@algolia/requester-fetch": "5.48.1", - "@algolia/requester-node-http": "5.48.1" + "@algolia/client-common": "5.52.0", + "@algolia/requester-browser-xhr": "5.52.0", + "@algolia/requester-fetch": "5.52.0", + "@algolia/requester-node-http": "5.52.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-search": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.48.1.tgz", - "integrity": "sha512-4Fu7dnzQyQmMFknYwTiN/HxPbH4DyxvQ1m+IxpPp5oslOgz8m6PG5qhiGbqJzH4HiT1I58ecDiCAC716UyVA8Q==", + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.52.0.tgz", + "integrity": "sha512-ly1wETVGRo30cx61O7fetESN+ElL9c9K+bD/AVgnT1ar4c6v+/Yqjrhdtu6Fm4D0s4NZP081Isf6tunH1wUXHg==", "dev": true, "license": "MIT", "dependencies": { - "@algolia/client-common": "5.48.1", - "@algolia/requester-browser-xhr": "5.48.1", - "@algolia/requester-fetch": "5.48.1", - "@algolia/requester-node-http": "5.48.1" + "@algolia/client-common": "5.52.0", + "@algolia/requester-browser-xhr": "5.52.0", + "@algolia/requester-fetch": "5.52.0", + "@algolia/requester-node-http": "5.52.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/ingestion": { - "version": "1.48.1", - "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.48.1.tgz", - "integrity": "sha512-/RFq3TqtXDUUawwic/A9xylA2P3LDMO8dNhphHAUOU51b1ZLHrmZ6YYJm3df1APz7xLY1aht6okCQf+/vmrV9w==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.52.0.tgz", + "integrity": "sha512-U4EeTvgmluRjj39ykZSAd5X+a6LD5m7/mcOWDmB7hqm1R6QY0yT8jLxpNVEjYhzgEN5hcDGW6X67EWQY8KiYGQ==", "dev": true, "license": "MIT", "dependencies": { - "@algolia/client-common": "5.48.1", - "@algolia/requester-browser-xhr": "5.48.1", - "@algolia/requester-fetch": "5.48.1", - "@algolia/requester-node-http": "5.48.1" + "@algolia/client-common": "5.52.0", + "@algolia/requester-browser-xhr": "5.52.0", + "@algolia/requester-fetch": "5.52.0", + "@algolia/requester-node-http": "5.52.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/monitoring": { - "version": "1.48.1", - "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.48.1.tgz", - "integrity": "sha512-Of0jTeAZRyRhC7XzDSjJef0aBkgRcvRAaw0ooYRlOw57APii7lZdq+layuNdeL72BRq1snaJhoMMwkmLIpJScw==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.52.0.tgz", + "integrity": "sha512-FCPnDcILfpTE94u7BVlV4DmnSV5wE3+j25EEF+3dYPrVzkVCSoAHs318oWDGxnxsAgiL4HpL12Jc4XHmw9shpA==", "dev": true, "license": "MIT", "dependencies": { - "@algolia/client-common": "5.48.1", - "@algolia/requester-browser-xhr": "5.48.1", - "@algolia/requester-fetch": "5.48.1", - "@algolia/requester-node-http": "5.48.1" + "@algolia/client-common": "5.52.0", + "@algolia/requester-browser-xhr": "5.52.0", + "@algolia/requester-fetch": "5.52.0", + "@algolia/requester-node-http": "5.52.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/recommend": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.48.1.tgz", - "integrity": "sha512-bE7JcpFXzxF5zHwj/vkl2eiCBvyR1zQ7aoUdO+GDXxGp0DGw7nI0p8Xj6u8VmRQ+RDuPcICFQcCwRIJT5tDJFw==", + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.52.0.tgz", + "integrity": "sha512-br3DO7n4N8CXwTRbZS0MnB4WQ9YHfNjCwkCEzVR/wek/qNTDQKDb0nROmkFaNZ8ucUqUVKZi074dbwMwRDlK8Q==", "dev": true, "license": "MIT", "dependencies": { - "@algolia/client-common": "5.48.1", - "@algolia/requester-browser-xhr": "5.48.1", - "@algolia/requester-fetch": "5.48.1", - "@algolia/requester-node-http": "5.48.1" + "@algolia/client-common": "5.52.0", + "@algolia/requester-browser-xhr": "5.52.0", + "@algolia/requester-fetch": "5.52.0", + "@algolia/requester-node-http": "5.52.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/requester-browser-xhr": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.48.1.tgz", - "integrity": "sha512-MK3wZ2koLDnvH/AmqIF1EKbJlhRS5j74OZGkLpxI4rYvNi9Jn/C7vb5DytBnQ4KUWts7QsmbdwHkxY5txQHXVw==", + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.52.0.tgz", + "integrity": "sha512-b0T/Ca2c9KyEslKsVrGZvbe1UrrKKSdfXhBZ2pbpKahFUzJfziRZ0urbOm7V65O0tO/jwU+Lo/+bIiiyhzGt8w==", "dev": true, "license": "MIT", "dependencies": { - "@algolia/client-common": "5.48.1" + "@algolia/client-common": "5.52.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/requester-fetch": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.48.1.tgz", - "integrity": "sha512-2oDT43Y5HWRSIQMPQI4tA/W+TN/N2tjggZCUsqQV440kxzzoPGsvv9QP1GhQ4CoDa+yn6ygUsGp6Dr+a9sPPSg==", + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.52.0.tgz", + "integrity": "sha512-ozBT8J/mtD4H4IAojw8QPirlcL2gHrI1BGuZ4/ZXXO/rTE1yQ4VIPJj4mTTbwo4FbkS1MoJsD/DsrqLzhnc4/g==", "dev": true, "license": "MIT", "dependencies": { - "@algolia/client-common": "5.48.1" + "@algolia/client-common": "5.52.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/requester-node-http": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.48.1.tgz", - "integrity": "sha512-xcaCqbhupVWhuBP1nwbk1XNvwrGljozutEiLx06mvqDf3o8cHyEgQSHS4fKJM+UAggaWVnnFW+Nne5aQ8SUJXg==", + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.52.0.tgz", + "integrity": "sha512-gyyWcLD22tnabmoit4iukCXuoRc5HYJuUjPSEa8a0D/f/NlRafpWi52AlAaa4Uu/rsl7saHsJFTNjTptWbu2+A==", "dev": true, "license": "MIT", "dependencies": { - "@algolia/client-common": "5.48.1" + "@algolia/client-common": "5.52.0" }, "engines": { "node": ">= 14.0.0" @@ -260,32 +260,32 @@ } }, "node_modules/@angular-devkit/architect": { - "version": "0.2102.12", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2102.12.tgz", - "integrity": "sha512-w9FSMHYeeHkk0kRSAOCvNqEVyOHqpC1SUf3iN7tDnXBOA0dtc6JYvJU7O4joiwf7wMPZDK8LKc/6eu8/Tx87Fw==", + "version": "0.2200.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2200.0.tgz", + "integrity": "sha512-PAfnKRM2C7er2PwAkSLvkw/AtnMRTcmdG6pOrb3De++eVTuDeNCuYsIqrygvkFElrpsMHcnAAwTNtvyMds8b+w==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "21.2.12", + "@angular-devkit/core": "22.0.0", "rxjs": "7.8.2" }, "bin": { "architect": "bin/cli.js" }, "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "node": "^22.22.3 || ^24.15.0 || >=26.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" } }, "node_modules/@angular-devkit/core": { - "version": "21.2.12", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.2.12.tgz", - "integrity": "sha512-nXms0jVWwHOJK+z6vHvhw7HYFBelxh2gEnkij0OQMABXZN5hoUlTD0DDP1lYR7hQNi8Yb2Ar0UN9ihyUFVM5Kg==", + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-22.0.0.tgz", + "integrity": "sha512-GCEalkF17uygXnjHNyeIWuTzm16TDlhNLHsxbeYeJSJ48anwkZisL/L+oFzEmg8BGqx48nMGj2EVe4J8ADrSng==", "dev": true, "license": "MIT", "dependencies": { - "ajv": "8.18.0", + "ajv": "8.20.0", "ajv-formats": "3.0.1", "jsonc-parser": "3.3.1", "picomatch": "4.0.4", @@ -293,7 +293,7 @@ "source-map": "0.7.6" }, "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "node": "^22.22.3 || ^24.15.0 || >=26.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" }, @@ -307,98 +307,98 @@ } }, "node_modules/@angular-devkit/schematics": { - "version": "21.2.12", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-21.2.12.tgz", - "integrity": "sha512-29xe6C9nwHejV9zBcu0js7NmzLWuCFzBGBTmL6eD4JN1NcxEZ/nO1JuaGINjPjzb/UDXPZIqEwHbnFNcGS5v1A==", + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-22.0.0.tgz", + "integrity": "sha512-Uefa/kgLD15B0wuxIFJuDvnVf2FuzXdnE/aMTd/fGor3otjrdegaU1tCeK9I0smHaiSWNvtLbhUbkNpuNG1cMw==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "21.2.12", + "@angular-devkit/core": "22.0.0", "jsonc-parser": "3.3.1", "magic-string": "0.30.21", - "ora": "9.3.0", + "ora": "9.4.0", "rxjs": "7.8.2" }, "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "node": "^22.22.3 || ^24.15.0 || >=26.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" } }, "node_modules/@angular/animations": { - "version": "21.2.14", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-21.2.14.tgz", - "integrity": "sha512-9WLnsJE0xqtd1rVtHMvsAUxFy3OdPks4bdmUIqyw23X/je7ytUALAGWNadffcZBwRpa1A6TUnLr9X4+Draz3kw==", + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-22.0.0.tgz", + "integrity": "sha512-Klo9ZiRj5ykXPliUmwy0eXvDad079YMy+Ob4EITSFSXVLRy55qv64/8SvWNtKEQPelF50H9O2vULoqpIvdWoAw==", + "dev": true, "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + "node": "^22.22.3 || ^24.15.0 || >=26.0.0" }, "peerDependencies": { - "@angular/core": "21.2.14" + "@angular/core": "22.0.0" } }, "node_modules/@angular/build": { - "version": "21.2.12", - "resolved": "https://registry.npmjs.org/@angular/build/-/build-21.2.12.tgz", - "integrity": "sha512-zYfo21RuldDWXnshuPfWYtmh5ltlO9+XFHpNObdIInQTFxKD6grLNVNOblFFpi+oIIm4Km+CGSXvBHs/aH0ufA==", + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/@angular/build/-/build-22.0.0.tgz", + "integrity": "sha512-cMtzptD/ewrB435PREv434RXKPdDSfXmptfJTe7ik6Q6ixzmBEFpwmBsMNHQAJHwgV5fCrmLuWVaaq0HY0MpxA==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.2102.12", + "@angular-devkit/architect": "0.2200.0", "@babel/core": "7.29.0", "@babel/helper-annotate-as-pure": "7.27.3", "@babel/helper-split-export-declaration": "7.24.7", - "@inquirer/confirm": "5.1.21", - "@vitejs/plugin-basic-ssl": "2.1.4", - "beasties": "0.4.1", + "@inquirer/confirm": "6.0.12", + "@vitejs/plugin-basic-ssl": "2.3.0", + "beasties": "0.4.2", "browserslist": "^4.26.0", - "esbuild": "0.27.3", - "https-proxy-agent": "7.0.6", - "istanbul-lib-instrument": "6.0.3", + "esbuild": "0.28.0", + "https-proxy-agent": "9.0.0", "jsonc-parser": "3.3.1", - "listr2": "9.0.5", + "listr2": "10.2.1", "magic-string": "0.30.21", "mrmime": "2.0.1", - "parse5-html-rewriting-stream": "8.0.0", + "parse5-html-rewriting-stream": "8.0.1", "picomatch": "4.0.4", "piscina": "5.1.4", - "rolldown": "1.0.0-rc.4", - "sass": "1.97.3", + "rollup": "4.60.2", + "sass": "1.99.0", "semver": "7.7.4", "source-map-support": "0.5.21", - "tinyglobby": "0.2.15", - "undici": "7.24.4", + "tinyglobby": "0.2.16", "vite": "7.3.2", "watchpack": "2.5.1" }, "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "node": "^22.22.3 || ^24.15.0 || >=26.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" }, "optionalDependencies": { - "lmdb": "3.5.1" + "lmdb": "3.5.4" }, "peerDependencies": { - "@angular/compiler": "^21.0.0", - "@angular/compiler-cli": "^21.0.0", - "@angular/core": "^21.0.0", - "@angular/localize": "^21.0.0", - "@angular/platform-browser": "^21.0.0", - "@angular/platform-server": "^21.0.0", - "@angular/service-worker": "^21.0.0", - "@angular/ssr": "^21.2.12", + "@angular/compiler": "^22.0.0", + "@angular/compiler-cli": "^22.0.0", + "@angular/core": "^22.0.0", + "@angular/localize": "^22.0.0", + "@angular/platform-browser": "^22.0.0", + "@angular/platform-server": "^22.0.0", + "@angular/service-worker": "^22.0.0", + "@angular/ssr": "^22.0.0", + "istanbul-lib-instrument": "^6.0.0", "karma": "^6.4.0", "less": "^4.2.0", - "ng-packagr": "^21.0.0", + "ng-packagr": "^22.0.0", "postcss": "^8.4.0", "tailwindcss": "^2.0.0 || ^3.0.0 || ^4.0.0", "tslib": "^2.3.0", - "typescript": ">=5.9 <6.0", + "typescript": ">=6.0 <6.1", "vitest": "^4.0.8" }, "peerDependenciesMeta": { @@ -420,6 +420,9 @@ "@angular/ssr": { "optional": true }, + "istanbul-lib-instrument": { + "optional": true + }, "karma": { "optional": true }, @@ -440,458 +443,1368 @@ } } }, - "node_modules/@angular/cli": { - "version": "21.2.12", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-21.2.12.tgz", - "integrity": "sha512-oLEL1C1fI39b1eQo5f2cyQhQfE+QMv7dm8z2MmxbP7YR7jAdQPVfGU8CXECR5g7mrYi9WgvIRKB+9Oeq2aH6Jw==", + "node_modules/@angular/build/node_modules/@esbuild/aix-ppc64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz", + "integrity": "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==", + "cpu": [ + "ppc64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@angular-devkit/architect": "0.2102.12", - "@angular-devkit/core": "21.2.12", - "@angular-devkit/schematics": "21.2.12", - "@inquirer/prompts": "7.10.1", - "@listr2/prompt-adapter-inquirer": "3.0.5", - "@modelcontextprotocol/sdk": "1.26.0", - "@schematics/angular": "21.2.12", - "@yarnpkg/lockfile": "1.1.0", - "algoliasearch": "5.48.1", - "ini": "6.0.0", - "jsonc-parser": "3.3.1", - "listr2": "9.0.5", - "npm-package-arg": "13.0.2", - "pacote": "21.3.1", - "parse5-html-rewriting-stream": "8.0.0", - "semver": "7.7.4", - "yargs": "18.0.0", - "zod": "4.3.6" - }, - "bin": { - "ng": "bin/ng.js" - }, + "optional": true, + "os": [ + "aix" + ], "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" + "node": ">=18" } }, - "node_modules/@angular/common": { - "version": "21.2.14", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-21.2.14.tgz", - "integrity": "sha512-J6K7cE7uKOKmg4+sxLeGfsmaYDjP5l1XCiMMI0WPT0t68uxLk8g3MzV5Trqfb6ZnRxWcfp9c4c+XxAvMBB7ymA==", + "node_modules/@angular/build/node_modules/@esbuild/android-arm": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.0.tgz", + "integrity": "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==", + "cpu": [ + "arm" + ], + "dev": true, "license": "MIT", - "dependencies": { - "tslib": "^2.3.0" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - }, - "peerDependencies": { - "@angular/core": "21.2.14", - "rxjs": "^6.5.3 || ^7.4.0" + "node": ">=18" } }, - "node_modules/@angular/compiler": { - "version": "21.2.14", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-21.2.14.tgz", - "integrity": "sha512-8mqgwRYfn2Z1vg/5YVt60dDBattnZL45nNJd2vTMwAiDTzhWhgKgRWKOeVL0aj2JqHeHiwuIlrLnz46acJMulQ==", + "node_modules/@angular/build/node_modules/@esbuild/android-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.0.tgz", + "integrity": "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "tslib": "^2.3.0" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + "node": ">=18" } }, - "node_modules/@angular/compiler-cli": { - "version": "21.2.14", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-21.2.14.tgz", - "integrity": "sha512-h+WQfPKFxaDfDhMqUUdOQ1TsDMccav8kLFERmKTRfD4MNOczSMpOMyeXJHCL0Rq4I8WDQvaBJGMG7DXRDefSog==", + "node_modules/@angular/build/node_modules/@esbuild/android-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.0.tgz", + "integrity": "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/core": "7.29.0", - "@jridgewell/sourcemap-codec": "^1.4.14", - "chokidar": "^5.0.0", - "convert-source-map": "^1.5.1", - "reflect-metadata": "^0.2.0", - "semver": "^7.0.0", - "tslib": "^2.3.0", - "yargs": "^18.0.0" - }, - "bin": { - "ng-xi18n": "bundles/src/bin/ng_xi18n.js", - "ngc": "bundles/src/bin/ngc.js" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - }, - "peerDependencies": { - "@angular/compiler": "21.2.14", - "typescript": ">=5.9 <6.1" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "node": ">=18" } }, - "node_modules/@angular/core": { - "version": "21.2.14", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-21.2.14.tgz", - "integrity": "sha512-Z1Ivjh7L2lT//8LA7vQ3tj7Rg6wl2XRA5kPSAukgn8u0Yu0XxG8NE8KG0Eypb3v9CEcbwATwpgnxzbJFZ8TFcw==", + "node_modules/@angular/build/node_modules/@esbuild/darwin-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.0.tgz", + "integrity": "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "tslib": "^2.3.0" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - }, - "peerDependencies": { - "@angular/compiler": "21.2.14", - "rxjs": "^6.5.3 || ^7.4.0", - "zone.js": "~0.15.0 || ~0.16.0" - }, - "peerDependenciesMeta": { - "@angular/compiler": { - "optional": true - }, - "zone.js": { - "optional": true - } + "node": ">=18" } }, - "node_modules/@angular/forms": { - "version": "21.2.14", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-21.2.14.tgz", - "integrity": "sha512-HQYIybyMt0CrI31rW6vXbiDsSM2DDtTcOVeT/nWDRNCoqBrREDg8rVsm2Y+fUMsiQVJNa6dCXPwvYhjzJ4r7ug==", + "node_modules/@angular/build/node_modules/@esbuild/darwin-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.0.tgz", + "integrity": "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==", + "cpu": [ + "x64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "@standard-schema/spec": "^1.0.0", - "tslib": "^2.3.0" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - }, - "peerDependencies": { - "@angular/common": "21.2.14", - "@angular/core": "21.2.14", - "@angular/platform-browser": "21.2.14", - "rxjs": "^6.5.3 || ^7.4.0" + "node": ">=18" } }, - "node_modules/@angular/platform-browser": { - "version": "21.2.14", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-21.2.14.tgz", - "integrity": "sha512-34tBwxh86yN2YifBDhCesm6N+nn9WcbuXjRwfo0mTme15OZ/zt56yw7v1mcK3UFLegIIALtsIgpXXrPWWQoKkA==", + "node_modules/@angular/build/node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.0.tgz", + "integrity": "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "tslib": "^2.3.0" - }, + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - }, - "peerDependencies": { - "@angular/animations": "21.2.14", - "@angular/common": "21.2.14", - "@angular/core": "21.2.14" - }, - "peerDependenciesMeta": { - "@angular/animations": { - "optional": true - } + "node": ">=18" } }, - "node_modules/@angular/router": { - "version": "21.2.14", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-21.2.14.tgz", - "integrity": "sha512-Yo3LdgcqkfMu2/Ycl8o/4QjCBqZhtA+a7B8JVdW5cWdrpFTxKCOrzm+YRUMuIFmH5nzSv9oGnUuz64uk1+7r5Q==", + "node_modules/@angular/build/node_modules/@esbuild/freebsd-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.0.tgz", + "integrity": "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==", + "cpu": [ + "x64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "tslib": "^2.3.0" - }, + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - }, - "peerDependencies": { - "@angular/common": "21.2.14", - "@angular/core": "21.2.14", - "@angular/platform-browser": "21.2.14", - "rxjs": "^6.5.3 || ^7.4.0" + "node": ">=18" } }, - "node_modules/@asamuzakjp/css-color": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-4.1.2.tgz", - "integrity": "sha512-NfBUvBaYgKIuq6E/RBLY1m0IohzNHAYyaJGuTK79Z23uNwmz2jl1mPsC5ZxCCxylinKhT1Amn5oNTlx1wN8cQg==", + "node_modules/@angular/build/node_modules/@esbuild/linux-arm": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.0.tgz", + "integrity": "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", - "dependencies": { - "@csstools/css-calc": "^3.0.0", - "@csstools/css-color-parser": "^4.0.1", - "@csstools/css-parser-algorithms": "^4.0.0", - "@csstools/css-tokenizer": "^4.0.0", - "lru-cache": "^11.2.5" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { - "version": "11.5.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.0.tgz", - "integrity": "sha512-5YgH9UJd7wVb9hIouI2adWpgqrrICkt070Dnj8EUY1+B4B2P9eRLPAkAAo6NICA7CEhOIeBHl46u9zSNpNu7zA==", + "node_modules/@angular/build/node_modules/@esbuild/linux-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.0.tgz", + "integrity": "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "20 || >=22" + "node": ">=18" } }, - "node_modules/@asamuzakjp/dom-selector": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.8.1.tgz", - "integrity": "sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ==", + "node_modules/@angular/build/node_modules/@esbuild/linux-ia32": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.0.tgz", + "integrity": "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==", + "cpu": [ + "ia32" + ], "dev": true, "license": "MIT", - "dependencies": { - "@asamuzakjp/nwsapi": "^2.3.9", - "bidi-js": "^1.0.3", - "css-tree": "^3.1.0", - "is-potential-custom-element-name": "^1.0.1", - "lru-cache": "^11.2.6" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@asamuzakjp/dom-selector/node_modules/lru-cache": { - "version": "11.5.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.0.tgz", - "integrity": "sha512-5YgH9UJd7wVb9hIouI2adWpgqrrICkt070Dnj8EUY1+B4B2P9eRLPAkAAo6NICA7CEhOIeBHl46u9zSNpNu7zA==", + "node_modules/@angular/build/node_modules/@esbuild/linux-loong64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.0.tgz", + "integrity": "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==", + "cpu": [ + "loong64" + ], "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "20 || >=22" + "node": ">=18" } }, - "node_modules/@asamuzakjp/nwsapi": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", - "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/code-frame": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", - "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "node_modules/@angular/build/node_modules/@esbuild/linux-mips64el": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.0.tgz", + "integrity": "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==", + "cpu": [ + "mips64el" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/compat-data": { - "version": "7.29.3", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz", - "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==", + "node_modules/@angular/build/node_modules/@esbuild/linux-ppc64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.0.tgz", + "integrity": "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==", + "cpu": [ + "ppc64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/core": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", - "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "node_modules/@angular/build/node_modules/@esbuild/linux-riscv64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.0.tgz", + "integrity": "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==", + "cpu": [ + "riscv64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helpers": "^7.28.6", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.29.0", - "@babel/types": "^7.29.0", - "@jridgewell/remapping": "^2.3.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "node": ">=18" } }, - "node_modules/@babel/generator": { - "version": "7.29.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", - "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "node_modules/@angular/build/node_modules/@esbuild/linux-s390x": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.0.tgz", + "integrity": "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==", + "cpu": [ + "s390x" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/parser": "^7.29.0", - "@babel/types": "^7.29.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", - "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "node_modules/@angular/build/node_modules/@esbuild/linux-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.0.tgz", + "integrity": "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.3" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", - "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "node_modules/@angular/build/node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.0.tgz", + "integrity": "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "node_modules/@angular/build/node_modules/@esbuild/netbsd-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.0.tgz", + "integrity": "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==", + "cpu": [ + "x64" + ], "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "node_modules/@angular/build/node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.0.tgz", + "integrity": "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/helper-module-imports": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", - "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "node_modules/@angular/build/node_modules/@esbuild/openbsd-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.0.tgz", + "integrity": "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" - }, + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", - "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "node_modules/@angular/build/node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.0.tgz", + "integrity": "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.28.6" - }, + "optional": true, + "os": [ + "openharmony" + ], "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "node": ">=18" } }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", - "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", + "node_modules/@angular/build/node_modules/@esbuild/sunos-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.0.tgz", + "integrity": "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@babel/types": "^7.24.7" - }, + "optional": true, + "os": [ + "sunos" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "node_modules/@angular/build/node_modules/@esbuild/win32-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.0.tgz", + "integrity": "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "node_modules/@angular/build/node_modules/@esbuild/win32-ia32": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.0.tgz", + "integrity": "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==", + "cpu": [ + "ia32" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "node_modules/@angular/build/node_modules/@esbuild/win32-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz", + "integrity": "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/helpers": { - "version": "7.29.2", + "node_modules/@angular/build/node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.2.tgz", + "integrity": "sha512-dnlp69efPPg6Uaw2dVqzWRfAWRnYVb1XJ8CyyhIbZeaq4CA5/mLeZ1IEt9QqQxmbdvagjLIm2ZL8BxXv5lH4Yw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@angular/build/node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.2.tgz", + "integrity": "sha512-OqZTwDRDchGRHHm/hwLOL7uVPB9aUvI0am/eQuWMNyFHf5PSEQmyEeYYheA0EPPKUO/l0uigCp+iaTjoLjVoHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@angular/build/node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.2.tgz", + "integrity": "sha512-UwRE7CGpvSVEQS8gUMBe1uADWjNnVgP3Iusyda1nSRwNDCsRjnGc7w6El6WLQsXmZTbLZx9cecegumcitNfpmA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@angular/build/node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.2.tgz", + "integrity": "sha512-gjEtURKLCC5VXm1I+2i1u9OhxFsKAQJKTVB8WvDAHF+oZlq0GTVFOlTlO1q3AlCTE/DF32c16ESvfgqR7343/g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@angular/build/node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.2.tgz", + "integrity": "sha512-Bcl6CYDeAgE70cqZaMojOi/eK63h5Me97ZqAQoh77VPjMysA/4ORQBRGo3rRy45x4MzVlU9uZxs8Uwy7ZaKnBw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@angular/build/node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.2.tgz", + "integrity": "sha512-LU+TPda3mAE2QB0/Hp5VyeKJivpC6+tlOXd1VMoXV/YFMvk/MNk5iXeBfB4MQGRWyOYVJ01625vjkr0Az98OJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@angular/build/node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.2.tgz", + "integrity": "sha512-2QxQrM+KQ7DAW4o22j+XZ6RKdxjLD7BOWTP0Bv0tmjdyhXSsr2Ul1oJDQqh9Zf5qOwTuTc7Ek83mOFaKnodPjg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@angular/build/node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.2.tgz", + "integrity": "sha512-TbziEu2DVsTEOPif2mKWkMeDMLoYjx95oESa9fkQQK7r/Orta0gnkcDpzwufEcAO2BLBsD7mZkXGFqEdMRRwfw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@angular/build/node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.2.tgz", + "integrity": "sha512-bO/rVDiDUuM2YfuCUwZ1t1cP+/yqjqz+Xf2VtkdppefuOFS2OSeAfgafaHNkFn0t02hEyXngZkxtGqXcXwO8Rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@angular/build/node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.2.tgz", + "integrity": "sha512-hr26p7e93Rl0Za+JwW7EAnwAvKkehh12BU1Llm9Ykiibg4uIr2rbpxG9WCf56GuvidlTG9KiiQT/TXT1yAWxTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@angular/build/node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.2.tgz", + "integrity": "sha512-pOjB/uSIyDt+ow3k/RcLvUAOGpysT2phDn7TTUB3n75SlIgZzM6NKAqlErPhoFU+npgY3/n+2HYIQVbF70P9/A==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@angular/build/node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.2.tgz", + "integrity": "sha512-2/w+q8jszv9Ww1c+6uJT3OwqhdmGP2/4T17cu8WuwyUuuaCDDJ2ojdyYwZzCxx0GcsZBhzi3HmH+J5pZNXnd+Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@angular/build/node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.2.tgz", + "integrity": "sha512-11+aL5vKheYgczxtPVVRhdptAM2H7fcDR5Gw4/bTcteuZBlH4oP9f5s9zYO9aGZvoGeBpqXI/9TZZihZ609wKw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@angular/build/node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.2.tgz", + "integrity": "sha512-i16fokAGK46IVZuV8LIIwMdtqhin9hfYkCh8pf8iC3QU3LpwL+1FSFGej+O7l3E/AoknL6Dclh2oTdnRMpTzFQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@angular/build/node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.2.tgz", + "integrity": "sha512-49FkKS6RGQoriDSK/6E2GkAsAuU5kETFCh7pG4yD/ylj9rKhTmO3elsnmBvRD4PgJPds5W2PkhC82aVwmUcJ7A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@angular/build/node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.2.tgz", + "integrity": "sha512-mjYNkHPfGpUR00DuM1ZZIgs64Hpf4bWcz9Z41+4Q+pgDx73UwWdAYyf6EG/lRFldmdHHzgrYyge5akFUW0D3mQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@angular/build/node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.2.tgz", + "integrity": "sha512-ALyvJz965BQk8E9Al/JDKKDLH2kfKFLTGMlgkAbbYtZuJt9LU8DW3ZoDMCtQpXAltZxwBHevXz5u+gf0yA0YoA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@angular/build/node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.2.tgz", + "integrity": "sha512-UQjrkIdWrKI626Du8lCQ6MJp/6V1LAo2bOK9OTu4mSn8GGXIkPXk/Vsp4bLHCd9Z9Iz2OTEaokUE90VweJgIYQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@angular/build/node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.2.tgz", + "integrity": "sha512-bTsRGj6VlSdn/XD4CGyzMnzaBs9bsRxy79eTqTCBsA8TMIEky7qg48aPkvJvFe1HyzQ5oMZdg7AnVlWQSKLTnw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@angular/build/node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.2.tgz", + "integrity": "sha512-6d4Z3534xitaA1FcMWP7mQPq5zGwBmGbhphh2DwaA1aNIXUu3KTOfwrWpbwI4/Gr0uANo7NTtaykFyO2hPuFLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@angular/build/node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.2.tgz", + "integrity": "sha512-NetAg5iO2uN7eB8zE5qrZ3CSil+7IJt4WDFLcC75Ymywq1VZVD6qJ6EvNLjZ3rEm6gB7XW5JdT60c6MN35Z85Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@angular/build/node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.2.tgz", + "integrity": "sha512-NCYhOotpgWZ5kdxCZsv6Iudx0wX8980Q/oW4pNFNihpBKsDbEA1zpkfxJGC0yugsUuyDZ7gL37dbzwhR0VI7pQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@angular/build/node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.2.tgz", + "integrity": "sha512-RXsaOqXxfoUBQoOgvmmijVxJnW2IGB0eoMO7F8FAjaj0UTywUO/luSqimWBJn04WNgUkeNhh7fs7pESXajWmkg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@angular/build/node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.2.tgz", + "integrity": "sha512-qdAzEULD+/hzObedtmV6iBpdL5TIbKVztGiK7O3/KYSf+HIzU257+MX1EXJcyIiDbMAqmbwaufcYPvyRryeZtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@angular/build/node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.2.tgz", + "integrity": "sha512-Nd/SgG27WoA9e+/TdK74KnHz852TLa94ovOYySo/yMPuTmpckK/jIF2jSwS3g7ELSKXK13/cVdmg1Z/DaCWKxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@angular/build/node_modules/agent-base": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-9.0.0.tgz", + "integrity": "sha512-TQf59BsZnytt8GdJKLPfUZ54g/iaUL2OWDSFCCvMOhsHduDQxO8xC4PNeyIkVcA5KwL2phPSv0douC0fgWzmnA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + } + }, + "node_modules/@angular/build/node_modules/esbuild": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz", + "integrity": "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.0", + "@esbuild/android-arm": "0.28.0", + "@esbuild/android-arm64": "0.28.0", + "@esbuild/android-x64": "0.28.0", + "@esbuild/darwin-arm64": "0.28.0", + "@esbuild/darwin-x64": "0.28.0", + "@esbuild/freebsd-arm64": "0.28.0", + "@esbuild/freebsd-x64": "0.28.0", + "@esbuild/linux-arm": "0.28.0", + "@esbuild/linux-arm64": "0.28.0", + "@esbuild/linux-ia32": "0.28.0", + "@esbuild/linux-loong64": "0.28.0", + "@esbuild/linux-mips64el": "0.28.0", + "@esbuild/linux-ppc64": "0.28.0", + "@esbuild/linux-riscv64": "0.28.0", + "@esbuild/linux-s390x": "0.28.0", + "@esbuild/linux-x64": "0.28.0", + "@esbuild/netbsd-arm64": "0.28.0", + "@esbuild/netbsd-x64": "0.28.0", + "@esbuild/openbsd-arm64": "0.28.0", + "@esbuild/openbsd-x64": "0.28.0", + "@esbuild/openharmony-arm64": "0.28.0", + "@esbuild/sunos-x64": "0.28.0", + "@esbuild/win32-arm64": "0.28.0", + "@esbuild/win32-ia32": "0.28.0", + "@esbuild/win32-x64": "0.28.0" + } + }, + "node_modules/@angular/build/node_modules/https-proxy-agent": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-9.0.0.tgz", + "integrity": "sha512-/MVmHp58WkOypgFhCLk4fzpPcFQvTJ/e6LBI7irpIO2HfxUbpmYoHF+KzipzJpxxzJu7aJNWQ0xojJ/dzV2G5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "9.0.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@angular/build/node_modules/rollup": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.2.tgz", + "integrity": "sha512-J9qZyW++QK/09NyN/zeO0dG/1GdGfyp9lV8ajHnRVLfo/uFsbji5mHnDgn/qYdUHyCkM2N+8VyspgZclfAh0eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.2", + "@rollup/rollup-android-arm64": "4.60.2", + "@rollup/rollup-darwin-arm64": "4.60.2", + "@rollup/rollup-darwin-x64": "4.60.2", + "@rollup/rollup-freebsd-arm64": "4.60.2", + "@rollup/rollup-freebsd-x64": "4.60.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.2", + "@rollup/rollup-linux-arm-musleabihf": "4.60.2", + "@rollup/rollup-linux-arm64-gnu": "4.60.2", + "@rollup/rollup-linux-arm64-musl": "4.60.2", + "@rollup/rollup-linux-loong64-gnu": "4.60.2", + "@rollup/rollup-linux-loong64-musl": "4.60.2", + "@rollup/rollup-linux-ppc64-gnu": "4.60.2", + "@rollup/rollup-linux-ppc64-musl": "4.60.2", + "@rollup/rollup-linux-riscv64-gnu": "4.60.2", + "@rollup/rollup-linux-riscv64-musl": "4.60.2", + "@rollup/rollup-linux-s390x-gnu": "4.60.2", + "@rollup/rollup-linux-x64-gnu": "4.60.2", + "@rollup/rollup-linux-x64-musl": "4.60.2", + "@rollup/rollup-openbsd-x64": "4.60.2", + "@rollup/rollup-openharmony-arm64": "4.60.2", + "@rollup/rollup-win32-arm64-msvc": "4.60.2", + "@rollup/rollup-win32-ia32-msvc": "4.60.2", + "@rollup/rollup-win32-x64-gnu": "4.60.2", + "@rollup/rollup-win32-x64-msvc": "4.60.2", + "fsevents": "~2.3.2" + } + }, + "node_modules/@angular/cli": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-22.0.0.tgz", + "integrity": "sha512-VJv4ryJ2yLy79FqAq6WzZCLU3U5WU3n5NS7av5LbatxxOb07Jg80J/DBPSeA3rJ5EzpSIrj8mHLvW8Eunn58Eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/architect": "0.2200.0", + "@angular-devkit/core": "22.0.0", + "@angular-devkit/schematics": "22.0.0", + "@inquirer/prompts": "8.4.2", + "@listr2/prompt-adapter-inquirer": "4.2.3", + "@modelcontextprotocol/sdk": "1.29.0", + "@schematics/angular": "22.0.0", + "@yarnpkg/lockfile": "1.1.0", + "algoliasearch": "5.52.0", + "ini": "6.0.0", + "jsonc-parser": "3.3.1", + "listr2": "10.2.1", + "npm-package-arg": "13.0.2", + "pacote": "21.5.0", + "parse5-html-rewriting-stream": "8.0.1", + "semver": "7.7.4", + "yargs": "18.0.0", + "zod": "4.4.2" + }, + "bin": { + "ng": "bin/ng.js" + }, + "engines": { + "node": "^22.22.3 || ^24.15.0 || >=26.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular/common": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-22.0.0.tgz", + "integrity": "sha512-O9Qk60/OQQuZXMeXRfOpsq+/B609nd5KIxjSZFddRQUfSMZrdvVDNK0irjgYVKGDkMx3dqCiQ8a4nAIdGy7V6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^22.22.3 || ^24.15.0 || >=26.0.0" + }, + "peerDependencies": { + "@angular/core": "22.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/compiler": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-22.0.0.tgz", + "integrity": "sha512-g8Ab5Lcji2cxADfcPPM7kltEzSlCjUevPK3udm+3S5uhkTcLNH236/XCAwhD1XIgHQDv9p7FWm1xS7zkvbwXhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^22.22.3 || ^24.15.0 || >=26.0.0" + } + }, + "node_modules/@angular/compiler-cli": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-22.0.0.tgz", + "integrity": "sha512-7r4ufQ8CUhlRBol/N8a6psg40kOu/Y3H6iuUGwq9cs6Gs/fII7mVB6QgPi0bCiNDjaQB7xGq6NZ0iT6CPBH8Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "7.29.0", + "@jridgewell/sourcemap-codec": "^1.4.14", + "chokidar": "^5.0.0", + "convert-source-map": "^1.5.1", + "reflect-metadata": "^0.2.0", + "semver": "^7.0.0", + "tslib": "^2.3.0", + "yargs": "^18.0.0" + }, + "bin": { + "ng-xi18n": "bundles/src/bin/ng_xi18n.js", + "ngc": "bundles/src/bin/ngc.js" + }, + "engines": { + "node": "^22.22.3 || ^24.15.0 || >=26.0.0" + }, + "peerDependencies": { + "@angular/compiler": "22.0.0", + "typescript": ">=6.0 <6.1" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@angular/core": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-22.0.0.tgz", + "integrity": "sha512-H4lzunB+LUNylQ3hZGYWDz1NfNAdFzPdOadwuS6VpPyxF4Ti0MLyAfx7NDnyTrmdY2/PFx8I6jXrveNlIsORXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^22.22.3 || ^24.15.0 || >=26.0.0" + }, + "peerDependencies": { + "@angular/compiler": "22.0.0", + "rxjs": "^6.5.3 || ^7.4.0", + "zone.js": "~0.15.0 || ~0.16.0" + }, + "peerDependenciesMeta": { + "@angular/compiler": { + "optional": true + }, + "zone.js": { + "optional": true + } + } + }, + "node_modules/@angular/forms": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-22.0.0.tgz", + "integrity": "sha512-OjyiF0hgbNXrFbIgqazyNJlFTtqfU0kfwJgmlMr4FG+e9P89UmgZhELUWs1CIuNX+jhh3DePm+Fo26dJIS7cfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "tslib": "^2.3.0", + "zod": "^4.0.10" + }, + "engines": { + "node": "^22.22.3 || ^24.15.0 || >=26.0.0" + }, + "peerDependencies": { + "@angular/common": "22.0.0", + "@angular/core": "22.0.0", + "@angular/platform-browser": "22.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/platform-browser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-22.0.0.tgz", + "integrity": "sha512-ry4Hdov19V8sA+MrIEIeISXA8GKWluCDUg06PaAm9nJveYjQUUlElZqa3fTNGOmy3/eNV8H9nmaroD27L8yU1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^22.22.3 || ^24.15.0 || >=26.0.0" + }, + "peerDependencies": { + "@angular/animations": "22.0.0", + "@angular/common": "22.0.0", + "@angular/core": "22.0.0" + }, + "peerDependenciesMeta": { + "@angular/animations": { + "optional": true + } + } + }, + "node_modules/@angular/router": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-22.0.0.tgz", + "integrity": "sha512-CCtonkDVkkfKLtuKol8rC1zmWI4QX7w3uUtdlOoz6K9HXAhpZYGcSq5RyloA767QLj36u7108K9xHBs2abOajQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^22.22.3 || ^24.15.0 || >=26.0.0" + }, + "peerDependencies": { + "@angular/common": "22.0.0", + "@angular/core": "22.0.0", + "@angular/platform-browser": "22.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@asamuzakjp/css-color": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-4.1.2.tgz", + "integrity": "sha512-NfBUvBaYgKIuq6E/RBLY1m0IohzNHAYyaJGuTK79Z23uNwmz2jl1mPsC5ZxCCxylinKhT1Amn5oNTlx1wN8cQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^3.0.0", + "@csstools/css-color-parser": "^4.0.1", + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0", + "lru-cache": "^11.2.5" + } + }, + "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { + "version": "11.5.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.0.tgz", + "integrity": "sha512-5YgH9UJd7wVb9hIouI2adWpgqrrICkt070Dnj8EUY1+B4B2P9eRLPAkAAo6NICA7CEhOIeBHl46u9zSNpNu7zA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@asamuzakjp/dom-selector": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.8.1.tgz", + "integrity": "sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/nwsapi": "^2.3.9", + "bidi-js": "^1.0.3", + "css-tree": "^3.1.0", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.2.6" + } + }, + "node_modules/@asamuzakjp/dom-selector/node_modules/lru-cache": { + "version": "11.5.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.0.tgz", + "integrity": "sha512-5YgH9UJd7wVb9hIouI2adWpgqrrICkt070Dnj8EUY1+B4B2P9eRLPAkAAo6NICA7CEhOIeBHl46u9zSNpNu7zA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@asamuzakjp/nwsapi": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", + "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz", + "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", + "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", "dev": true, @@ -1108,43 +2021,6 @@ "node": ">=20.19.0" } }, - "node_modules/@emnapi/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", - "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.1", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", - "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@esbuild/aix-ppc64": { "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", @@ -1637,30 +2513,29 @@ } }, "node_modules/@inquirer/ansi": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz", - "integrity": "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-2.0.7.tgz", + "integrity": "sha512-3eTuUO1vH2cZm2ZKHeQxnOqlTi9EfZDGgIe3BL3I4u+rJHocr9Fz86M4fjYABPvFnQG/gGK551HqDiIcETwU6Q==", "dev": true, "license": "MIT", "engines": { - "node": ">=18" + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" } }, "node_modules/@inquirer/checkbox": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.3.2.tgz", - "integrity": "sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-5.2.1.tgz", + "integrity": "sha512-b6xmA/VlTe0ZgDQHDui+Nav470u7u49nRd8/iuhOcQPO9Ch7lGuogydhi2VOmNlZ+zXcM8IcPuNSwQcdJaF/kw==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/ansi": "^1.0.2", - "@inquirer/core": "^10.3.2", - "@inquirer/figures": "^1.0.15", - "@inquirer/type": "^3.0.10", - "yoctocolors-cjs": "^2.1.3" + "@inquirer/ansi": "^2.0.7", + "@inquirer/core": "^11.2.1", + "@inquirer/figures": "^2.0.7", + "@inquirer/type": "^4.0.7" }, "engines": { - "node": ">=18" + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" }, "peerDependencies": { "@types/node": ">=18" @@ -1672,17 +2547,17 @@ } }, "node_modules/@inquirer/confirm": { - "version": "5.1.21", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.21.tgz", - "integrity": "sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==", + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-6.0.12.tgz", + "integrity": "sha512-h9FgGun3QwVYNj5TWIZZ+slii73bMoBFjPfVIGtnFuL4t8gBiNDV9PcSfIzkuxvgquJKt9nr1QzszpBzTbH8Og==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.3.2", - "@inquirer/type": "^3.0.10" + "@inquirer/core": "^11.1.9", + "@inquirer/type": "^4.0.5" }, "engines": { - "node": ">=18" + "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" }, "peerDependencies": { "@types/node": ">=18" @@ -1694,23 +2569,22 @@ } }, "node_modules/@inquirer/core": { - "version": "10.3.2", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.2.tgz", - "integrity": "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==", + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-11.2.1.tgz", + "integrity": "sha512-Qd6GJT1yVyrZZCfN8W2qKF5ApmqryXRhRKCuip8h01x2w/esJQ2XIYc6f9abMIHgKQdBfFTSOdbHRLAhuM09UA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/ansi": "^1.0.2", - "@inquirer/figures": "^1.0.15", - "@inquirer/type": "^3.0.10", + "@inquirer/ansi": "^2.0.7", + "@inquirer/figures": "^2.0.7", + "@inquirer/type": "^4.0.7", "cli-width": "^4.1.0", - "mute-stream": "^2.0.0", - "signal-exit": "^4.1.0", - "wrap-ansi": "^6.2.0", - "yoctocolors-cjs": "^2.1.3" + "fast-wrap-ansi": "^0.2.0", + "mute-stream": "^3.0.0", + "signal-exit": "^4.1.0" }, "engines": { - "node": ">=18" + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" }, "peerDependencies": { "@types/node": ">=18" @@ -1722,18 +2596,18 @@ } }, "node_modules/@inquirer/editor": { - "version": "4.2.23", - "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.23.tgz", - "integrity": "sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-5.2.2.tgz", + "integrity": "sha512-ZRVd/oD+sYsUd5zVm0NflqEzlqfYCyHNsqkHl2oWXEUHs12tCbcSFi+wVFEvD8+LGRaMUsVrE7qeo6lSG/S1Vg==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.3.2", - "@inquirer/external-editor": "^1.0.3", - "@inquirer/type": "^3.0.10" + "@inquirer/core": "^11.2.1", + "@inquirer/external-editor": "^3.0.3", + "@inquirer/type": "^4.0.7" }, "engines": { - "node": ">=18" + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" }, "peerDependencies": { "@types/node": ">=18" @@ -1745,18 +2619,17 @@ } }, "node_modules/@inquirer/expand": { - "version": "4.0.23", - "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.23.tgz", - "integrity": "sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-5.1.1.tgz", + "integrity": "sha512-YmQpenjbFSHAK3sOd44puHh3V1KXXr+JiNpUztoSQ4drLh2rTVzTap/YtlAVu/5xavifIlBfNEzJ/neZJ1a/1g==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.3.2", - "@inquirer/type": "^3.0.10", - "yoctocolors-cjs": "^2.1.3" + "@inquirer/core": "^11.2.1", + "@inquirer/type": "^4.0.7" }, "engines": { - "node": ">=18" + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" }, "peerDependencies": { "@types/node": ">=18" @@ -1768,17 +2641,17 @@ } }, "node_modules/@inquirer/external-editor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", - "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-3.0.3.tgz", + "integrity": "sha512-6thf5I8q7lZwzGLAxPaaGEREEkZ3nyePPDQ1oyobblxmEE8mqTLguScP7pDjUTAibiyb4hfXl+qjUEJ+di/aNA==", "dev": true, "license": "MIT", "dependencies": { "chardet": "^2.1.1", - "iconv-lite": "^0.7.0" + "iconv-lite": "^0.7.2" }, "engines": { - "node": ">=18" + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" }, "peerDependencies": { "@types/node": ">=18" @@ -1790,27 +2663,27 @@ } }, "node_modules/@inquirer/figures": { - "version": "1.0.15", - "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz", - "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-2.0.7.tgz", + "integrity": "sha512-aJ8TBPOGB6f/2qziPfElISTCEd5XOYTFckA2SGjhNmiKzfK/u4ot3v0DUzGVdUnKjN10EqnnEPck36BkyfLnJw==", "dev": true, "license": "MIT", "engines": { - "node": ">=18" + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" } }, "node_modules/@inquirer/input": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.3.1.tgz", - "integrity": "sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-5.1.2.tgz", + "integrity": "sha512-9K/DDBSQpOyZSkt6sOVP9Vo0TR7atX2kuILsUu0x3wVcVbe97lJwIJKMLdMw25tDYuXl/qp6erT0Xs1rfmcfZg==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.3.2", - "@inquirer/type": "^3.0.10" + "@inquirer/core": "^11.2.1", + "@inquirer/type": "^4.0.7" }, "engines": { - "node": ">=18" + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" }, "peerDependencies": { "@types/node": ">=18" @@ -1822,17 +2695,17 @@ } }, "node_modules/@inquirer/number": { - "version": "3.0.23", - "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.23.tgz", - "integrity": "sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-4.1.1.tgz", + "integrity": "sha512-XF4IXAbPnGPgw0wsbC/i2tPcyfdZgDpUlhsqU0SfT4IRIGWha6Xm9VRgN5yYxJq+jnyXlfXI/nQ3ulfk0iEICA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.3.2", - "@inquirer/type": "^3.0.10" + "@inquirer/core": "^11.2.1", + "@inquirer/type": "^4.0.7" }, "engines": { - "node": ">=18" + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" }, "peerDependencies": { "@types/node": ">=18" @@ -1844,18 +2717,18 @@ } }, "node_modules/@inquirer/password": { - "version": "4.0.23", - "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.23.tgz", - "integrity": "sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-5.1.1.tgz", + "integrity": "sha512-3XBfF7DAsp5qeDsvN5Rd1HmbNokVvEQoUM0QLrRcybC9nX96w3Pbmu7qUsb3IT3J3jBvs2+mTXaKHOUsgHMLzg==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/ansi": "^1.0.2", - "@inquirer/core": "^10.3.2", - "@inquirer/type": "^3.0.10" + "@inquirer/ansi": "^2.0.7", + "@inquirer/core": "^11.2.1", + "@inquirer/type": "^4.0.7" }, "engines": { - "node": ">=18" + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" }, "peerDependencies": { "@types/node": ">=18" @@ -1867,25 +2740,25 @@ } }, "node_modules/@inquirer/prompts": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.10.1.tgz", - "integrity": "sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-8.4.2.tgz", + "integrity": "sha512-XJmn/wY4AX56l1BRU+ZjDrFtg9+2uBEi4JvJQj82kwJDQKiPgSn4CEsbfGGygS4Gw6rkL4W18oATjfVfaqub2Q==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/checkbox": "^4.3.2", - "@inquirer/confirm": "^5.1.21", - "@inquirer/editor": "^4.2.23", - "@inquirer/expand": "^4.0.23", - "@inquirer/input": "^4.3.1", - "@inquirer/number": "^3.0.23", - "@inquirer/password": "^4.0.23", - "@inquirer/rawlist": "^4.1.11", - "@inquirer/search": "^3.2.2", - "@inquirer/select": "^4.4.2" + "@inquirer/checkbox": "^5.1.4", + "@inquirer/confirm": "^6.0.12", + "@inquirer/editor": "^5.1.1", + "@inquirer/expand": "^5.0.13", + "@inquirer/input": "^5.0.12", + "@inquirer/number": "^4.0.12", + "@inquirer/password": "^5.0.12", + "@inquirer/rawlist": "^5.2.8", + "@inquirer/search": "^4.1.8", + "@inquirer/select": "^5.1.4" }, "engines": { - "node": ">=18" + "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" }, "peerDependencies": { "@types/node": ">=18" @@ -1897,18 +2770,17 @@ } }, "node_modules/@inquirer/rawlist": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.11.tgz", - "integrity": "sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-5.3.1.tgz", + "integrity": "sha512-QqdTqQddL3qPX/PPrjobpsO25NZ4dWXgTLenrR445L2ptLEYE6Z+PD5c5CNDJNx4ugRgELAIpSIJxZaO2jJ2Og==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.3.2", - "@inquirer/type": "^3.0.10", - "yoctocolors-cjs": "^2.1.3" + "@inquirer/core": "^11.2.1", + "@inquirer/type": "^4.0.7" }, "engines": { - "node": ">=18" + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" }, "peerDependencies": { "@types/node": ">=18" @@ -1920,19 +2792,18 @@ } }, "node_modules/@inquirer/search": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.2.2.tgz", - "integrity": "sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-4.2.1.tgz", + "integrity": "sha512-xJj8QWKRSrfKoBIITLZK61dD3zwo0Rz11fgDImku30/Oe81zMdIdGgrLY2h6RkJ+KZ/GhNYIRMKnH/62qBTA5g==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.3.2", - "@inquirer/figures": "^1.0.15", - "@inquirer/type": "^3.0.10", - "yoctocolors-cjs": "^2.1.3" + "@inquirer/core": "^11.2.1", + "@inquirer/figures": "^2.0.7", + "@inquirer/type": "^4.0.7" }, "engines": { - "node": ">=18" + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" }, "peerDependencies": { "@types/node": ">=18" @@ -1944,20 +2815,19 @@ } }, "node_modules/@inquirer/select": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.4.2.tgz", - "integrity": "sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-5.2.1.tgz", + "integrity": "sha512-FlDndEUww8m7BfukO2nJa25vhD+H5jxxCv4oGioKqzyWz3nPHhhw4LKdYRSlXuAx7DsdWia7iyaBPKKS95Evfw==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/ansi": "^1.0.2", - "@inquirer/core": "^10.3.2", - "@inquirer/figures": "^1.0.15", - "@inquirer/type": "^3.0.10", - "yoctocolors-cjs": "^2.1.3" + "@inquirer/ansi": "^2.0.7", + "@inquirer/core": "^11.2.1", + "@inquirer/figures": "^2.0.7", + "@inquirer/type": "^4.0.7" }, "engines": { - "node": ">=18" + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" }, "peerDependencies": { "@types/node": ">=18" @@ -1969,13 +2839,13 @@ } }, "node_modules/@inquirer/type": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.10.tgz", - "integrity": "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-4.0.7.tgz", + "integrity": "sha512-t28inv14nMQ1PhKpsJPY+kEs/c00qzeCOS2gTNRyTjG5d6qsVA2fItxW4hkvGZ5lvanGLdtCzVIx5dwdRpN1+g==", "dev": true, "license": "MIT", "engines": { - "node": ">=18" + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" }, "peerDependencies": { "@types/node": ">=18" @@ -2005,6 +2875,8 @@ "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=8" } @@ -2060,26 +2932,26 @@ } }, "node_modules/@listr2/prompt-adapter-inquirer": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@listr2/prompt-adapter-inquirer/-/prompt-adapter-inquirer-3.0.5.tgz", - "integrity": "sha512-WELs+hj6xcilkloBXYf9XXK8tYEnKsgLj01Xl5ONUJpKjmT5hGVUzNUS5tooUxs7pGMrw+jFD/41WpqW4V3LDA==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@listr2/prompt-adapter-inquirer/-/prompt-adapter-inquirer-4.2.3.tgz", + "integrity": "sha512-Co9U3AJ3LW0J8XBHjVoNKA79dMAyFt8EZH3OaKTMcDTj8r+6kG3vSUPq/eGLHT7P0iK3uLaFfhdFYd3033P24g==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/type": "^3.0.8" + "@inquirer/type": "^4.0.5" }, "engines": { - "node": ">=20.0.0" + "node": ">=22.13.0" }, "peerDependencies": { - "@inquirer/prompts": ">= 3 < 8", - "listr2": "9.0.5" + "@inquirer/prompts": ">= 3 < 9", + "listr2": "10.2.1" } }, "node_modules/@lmdb/lmdb-darwin-arm64": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-3.5.1.tgz", - "integrity": "sha512-tpfN4kKrrMpQ+If1l8bhmoNkECJi0iOu6AEdrTJvWVC+32sLxTARX5Rsu579mPImRP9YFWfWgeRQ5oav7zApQQ==", + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-3.5.4.tgz", + "integrity": "sha512-Kk4Kz3iyu1QiLsLZBS9Af1eSKUC8VR2T+/jyE2iAyuGw2VwK08pp5iTbZnXn6sWu0LogO/RFktMxOjiDA2sS3w==", "cpu": [ "arm64" ], @@ -2091,9 +2963,9 @@ ] }, "node_modules/@lmdb/lmdb-darwin-x64": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-3.5.1.tgz", - "integrity": "sha512-+a2tTfc3rmWhLAolFUWRgJtpSuu+Fw/yjn4rF406NMxhfjbMuiOUTDRvRlMFV+DzyjkwnokisskHbCWkS3Ly5w==", + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-3.5.4.tgz", + "integrity": "sha512-BEe5Rp3trn26oxoXOVL5HVDoiYmjUDwr8NRPkBOdUdCSBEorKI+7JrZLRKAdxO+G6cGQLgseXk0gR7qIQa7aGw==", "cpu": [ "x64" ], @@ -2105,9 +2977,9 @@ ] }, "node_modules/@lmdb/lmdb-linux-arm": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-3.5.1.tgz", - "integrity": "sha512-0EgcE6reYr8InjD7V37EgXcYrloqpxVPINy3ig1MwDSbl6LF/vXTYRH9OE1Ti1D8YZnB35ZH9aTcdfSb5lql2A==", + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-3.5.4.tgz", + "integrity": "sha512-SGbFR7816uBcTHc2ZY4S6WyOkl9bICnzqTQd2Mv4V/j24cfds88xx2nC6cm/y8zGQL7Ds31YF/5NGxjgcdM5Hw==", "cpu": [ "arm" ], @@ -2119,9 +2991,9 @@ ] }, "node_modules/@lmdb/lmdb-linux-arm64": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-3.5.1.tgz", - "integrity": "sha512-aoERa5B6ywXdyFeYGQ1gbQpkMkDbEo45qVoXE5QpIRavqjnyPwjOulMkmkypkmsbJ5z4Wi0TBztON8agCTG0Vg==", + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-3.5.4.tgz", + "integrity": "sha512-cUXEengO8o60v1SWerJTH4/RH4U3+9jC0/4njp2Z9NdmvaGzhKsbRM2wpXuRYrN8tytsoJCg0SvWEWwHAwLbCA==", "cpu": [ "arm64" ], @@ -2133,9 +3005,9 @@ ] }, "node_modules/@lmdb/lmdb-linux-x64": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-3.5.1.tgz", - "integrity": "sha512-SqNDY1+vpji7bh0sFH5wlWyFTOzjbDOl0/kB5RLLYDAFyd/uw3n7wyrmas3rYPpAW7z18lMOi1yKlTPv967E3g==", + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-3.5.4.tgz", + "integrity": "sha512-Gxq8jpgOWXwd0PUR+c9R2Ik1/uBnGd5GMIIzRRDqABCkvmjtC3KWcyhesV9jSPCz759isl0NlbsstZ2oyvk8lA==", "cpu": [ "x64" ], @@ -2147,9 +3019,9 @@ ] }, "node_modules/@lmdb/lmdb-win32-arm64": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-arm64/-/lmdb-win32-arm64-3.5.1.tgz", - "integrity": "sha512-50v0O1Lt37cwrmR9vWZK5hRW0Aw+KEmxJJ75fge/zIYdvNKB/0bSMSVR5Uc2OV9JhosIUyklOmrEvavwNJ8D6w==", + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-arm64/-/lmdb-win32-arm64-3.5.4.tgz", + "integrity": "sha512-pKv1DJ1bPZAaHkdFsSz5IDfUG8x9vntgquXF9/Dm2xuupcIe/EkLzylpoBxppFVK5vzbV561Dq26jNY2fIMA7g==", "cpu": [ "arm64" ], @@ -2161,9 +3033,9 @@ ] }, "node_modules/@lmdb/lmdb-win32-x64": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-3.5.1.tgz", - "integrity": "sha512-qwosvPyl+zpUlp3gRb7UcJ3H8S28XHCzkv0Y0EgQToXjQP91ZD67EHSCDmaLjtKhe+GVIW5om1KUpzVLA0l6pg==", + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-3.5.4.tgz", + "integrity": "sha512-JF1BmLCm9kGEVZgYmJq43zeQVdHVgAJnTi/NURWEsy6L1ZrrlSmdltS+D17QN4LODwf+1LMXAA9auIZVXtWwzw==", "cpu": [ "x64" ], @@ -2175,9 +3047,9 @@ ] }, "node_modules/@modelcontextprotocol/sdk": { - "version": "1.26.0", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.26.0.tgz", - "integrity": "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg==", + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", + "integrity": "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2216,9 +3088,9 @@ } }, "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz", - "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.4.tgz", + "integrity": "sha512-LCkGo6JDfaBhgST7UpPWgNgLINpcpabaHfyz5OBx75nUYxBsaEPxjnyNjWpeb/xBup/682QnBfRBy2/LvPutZQ==", "cpu": [ "arm64" ], @@ -2230,9 +3102,9 @@ ] }, "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz", - "integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.4.tgz", + "integrity": "sha512-zExlW9zUJKZH/tOtVMttwjKa4Xm/3KcNjnE3dPN92uCktwavMxpgCA3MoJK/DOnTWsQgo224OaST27/mPNAf+w==", "cpu": [ "x64" ], @@ -2244,9 +3116,9 @@ ] }, "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz", - "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.4.tgz", + "integrity": "sha512-Tg3yX65f5GbtXLkrYEHE5oibZG9epyYWas7FogTTEJeDEF9JlXJzKgXaNhT3UXlTOeA+AfZpYZYZ0uPj7Cfquw==", "cpu": [ "arm" ], @@ -2258,9 +3130,9 @@ ] }, "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz", - "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.4.tgz", + "integrity": "sha512-dgX0P/9wGPJeHFBG+ZmhgE6bmtMt7NP5CRBGyyktpopdk/mW4POnrpQsSLtKI1dwpc+pPLuXHDh6vvskyQE/sw==", "cpu": [ "arm64" ], @@ -2272,9 +3144,9 @@ ] }, "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz", - "integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.4.tgz", + "integrity": "sha512-8TNXMEjJc3QEy7R/x1INhgiU+XakDAFUzBhaz7+Rbrs8NH5UQeHQxxmzsSBJGyV6I1jW79undiQm8tOI+D+8FQ==", "cpu": [ "x64" ], @@ -2286,9 +3158,9 @@ ] }, "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz", - "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.4.tgz", + "integrity": "sha512-CmCXPQrkbwExx3j946/PtHWHbYJiCRBRDl4BlkRQcJB/YOwQxJRTpoo7aTsortjgoJ1x7opzTSxn7C+ASSLVjQ==", "cpu": [ "x64" ], @@ -2622,29 +3494,10 @@ "node": ">= 10" } }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", - "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@tybys/wasm-util": "^0.10.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "peerDependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1" - } - }, "node_modules/@npmcli/agent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-4.0.0.tgz", - "integrity": "sha512-kAQTcEN9E8ERLVg5AsGwLNoFb+oEG6engbqAU2P43gD4JEIkNGMHdVQ096FsOAAYpZPB0RSt0zgInKIAS1l5QA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-4.0.2.tgz", + "integrity": "sha512-EUEuWAxnL07Sp5/iC/1X6Xj+XThUvnbei9zfRWZdEXa7lss9RTHMhAHBeg+MZ5To9s/gGaSI+UwZTPdYMvKSeg==", "dev": true, "license": "ISC", "dependencies": { @@ -2659,9 +3512,9 @@ } }, "node_modules/@npmcli/agent/node_modules/lru-cache": { - "version": "11.5.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.0.tgz", - "integrity": "sha512-5YgH9UJd7wVb9hIouI2adWpgqrrICkt070Dnj8EUY1+B4B2P9eRLPAkAAo6NICA7CEhOIeBHl46u9zSNpNu7zA==", + "version": "11.5.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", + "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", "dev": true, "license": "BlueOak-1.0.0", "engines": { @@ -2712,9 +3565,9 @@ } }, "node_modules/@npmcli/git/node_modules/lru-cache": { - "version": "11.5.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.0.tgz", - "integrity": "sha512-5YgH9UJd7wVb9hIouI2adWpgqrrICkt070Dnj8EUY1+B4B2P9eRLPAkAAo6NICA7CEhOIeBHl46u9zSNpNu7zA==", + "version": "11.5.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", + "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", "dev": true, "license": "BlueOak-1.0.0", "engines": { @@ -2849,16 +3702,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/@oxc-project/types": { - "version": "0.113.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.113.0.tgz", - "integrity": "sha512-Tp3XmgxwNQ9pEN9vxgJBAqdRamHibi76iowQ38O2I4PMpcvNRQNVsU2n1x1nv9yh0XoTrGFzf7cZSGxmixxrhA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/Boshen" - } - }, "node_modules/@parcel/watcher": { "version": "2.5.6", "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz", @@ -3193,234 +4036,6 @@ "node": ">=18" } }, - "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.0-rc.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.4.tgz", - "integrity": "sha512-vRq9f4NzvbdZavhQbjkJBx7rRebDKYR9zHfO/Wg486+I7bSecdUapzCm5cyXoK+LHokTxgSq7A5baAXUZkIz0w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.0-rc.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.4.tgz", - "integrity": "sha512-kFgEvkWLqt3YCgKB5re9RlIrx9bRsvyVUnaTakEpOPuLGzLpLapYxE9BufJNvPg8GjT6mB1alN4yN1NjzoeM8Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.0-rc.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.4.tgz", - "integrity": "sha512-JXmaOJGsL/+rsmMfutcDjxWM2fTaVgCHGoXS7nE8Z3c9NAYjGqHvXrAhMUZvMpHS/k7Mg+X7n/MVKb7NYWKKww==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.0-rc.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.4.tgz", - "integrity": "sha512-ep3Catd6sPnHTM0P4hNEvIv5arnDvk01PfyJIJ+J3wVCG1eEaPo09tvFqdtcaTrkwQy0VWR24uz+cb4IsK53Qw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.0-rc.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.4.tgz", - "integrity": "sha512-LwA5ayKIpnsgXJEwWc3h8wPiS33NMIHd9BhsV92T8VetVAbGe2qXlJwNVDGHN5cOQ22R9uYvbrQir2AB+ntT2w==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.0-rc.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.4.tgz", - "integrity": "sha512-AC1WsGdlV1MtGay/OQ4J9T7GRadVnpYRzTcygV1hKnypbYN20Yh4t6O1Sa2qRBMqv1etulUknqXjc3CTIsBu6A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.0-rc.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.4.tgz", - "integrity": "sha512-lU+6rgXXViO61B4EudxtVMXSOfiZONR29Sys5VGSetUY7X8mg9FCKIIjcPPj8xNDeYzKl+H8F/qSKOBVFJChCQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0-rc.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.4.tgz", - "integrity": "sha512-DZaN1f0PGp/bSvKhtw50pPsnln4T13ycDq1FrDWRiHmWt1JeW+UtYg9touPFf8yt993p8tS2QjybpzKNTxYEwg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.0-rc.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.4.tgz", - "integrity": "sha512-RnGxwZLN7fhMMAItnD6dZ7lvy+TI7ba+2V54UF4dhaWa/p8I/ys1E73KO6HmPmgz92ZkfD8TXS1IMV8+uhbR9g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.0-rc.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.4.tgz", - "integrity": "sha512-6lcI79+X8klGiGd8yHuTgQRjuuJYNggmEml+RsyN596P23l/zf9FVmJ7K0KVKkFAeYEdg0iMUKyIxiV5vebDNQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.0-rc.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.4.tgz", - "integrity": "sha512-wz7ohsKCAIWy91blZ/1FlpPdqrsm1xpcEOQVveWoL6+aSPKL4VUcoYmmzuLTssyZxRpEwzuIxL/GDsvpjaBtOw==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^1.1.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.0-rc.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.4.tgz", - "integrity": "sha512-cfiMrfuWCIgsFmcVG0IPuO6qTRHvF7NuG3wngX1RZzc6dU8FuBFb+J3MIR5WrdTNozlumfgL4cvz+R4ozBCvsQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.0-rc.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.4.tgz", - "integrity": "sha512-p6UeR9y7ht82AH57qwGuFYn69S6CZ7LLKdCKy/8T3zS9VTrJei2/CGsTUV45Da4Z9Rbhc7G4gyWQ/Ioamqn09g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.4", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.4.tgz", - "integrity": "sha512-1BrrmTu0TWfOP1riA8uakjFc9bpIUGzVKETsOtzY39pPga8zELGDl8eu1Dx7/gjM5CAz14UknsUMpBO8L+YntQ==", - "dev": true, - "license": "MIT" - }, "node_modules/@rollup/plugin-json": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz", @@ -3443,9 +4058,9 @@ } }, "node_modules/@rollup/pluginutils": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", - "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.4.0.tgz", + "integrity": "sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==", "dev": true, "license": "MIT", "dependencies": { @@ -3823,13 +4438,13 @@ ] }, "node_modules/@rollup/wasm-node": { - "version": "4.60.4", - "resolved": "https://registry.npmjs.org/@rollup/wasm-node/-/wasm-node-4.60.4.tgz", - "integrity": "sha512-j6qaRjdDujJ5utX5l6+8eiWlvMLmBfPMBht8mHP2au3xuzf+4deu6PuCquH5GvDIvIOsWHZhA1UVz/s0FvvgAA==", + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/wasm-node/-/wasm-node-4.61.1.tgz", + "integrity": "sha512-mSYYG8nIVGzK2rU38h9wIUncwwkP4z/qyv70+TbFDYK0u1aZIrKDEYnmNs4CBtNy5Ru4pmjo6Zi7kIhJk4RMYQ==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.8" + "@types/estree": "1.0.9" }, "bin": { "rollup": "dist/bin/rollup" @@ -3842,19 +4457,27 @@ "fsevents": "~2.3.2" } }, + "node_modules/@rollup/wasm-node/node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, "node_modules/@schematics/angular": { - "version": "21.2.12", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-21.2.12.tgz", - "integrity": "sha512-eHoAbxd6Kdw9YIQeZO/6lBXTmKKi10t4WTujY8CM5v4qv1zoJu9yiwVeQp9y3e7/Sybz5Ec3m4FmQ0Tw8iVDiA==", + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-22.0.0.tgz", + "integrity": "sha512-LqjnpBD0knsZulOCiaBxb9vDUYq6RHyF2VMlQI1gkgJaDAd2YcvK3/H2Xy9tEH1oA1ftbo2p0kpzNtJzSwtBcA==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "21.2.12", - "@angular-devkit/schematics": "21.2.12", - "jsonc-parser": "3.3.1" + "@angular-devkit/core": "22.0.0", + "@angular-devkit/schematics": "22.0.0", + "jsonc-parser": "3.3.1", + "typescript": "6.0.3" }, "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "node": "^22.22.3 || ^24.15.0 || >=26.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" } @@ -3943,6 +4566,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, "license": "MIT" }, "node_modules/@tufjs/canonical-json": { @@ -3969,17 +4593,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", - "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@types/chai": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", @@ -4006,16 +4619,16 @@ "license": "MIT" }, "node_modules/@vitejs/plugin-basic-ssl": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-2.1.4.tgz", - "integrity": "sha512-HXciTXN/sDBYWgeAD4V4s0DN0g72x5mlxQhHxtYu3Tt8BLa6MzcJZUyDVFCdtjNs3bfENVHVzOsmooTVuNgAAw==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-2.3.0.tgz", + "integrity": "sha512-bdyo8rB3NnQbikdMpHaML9Z1OZPBu6fFOBo+OtxsBlvMJtysWskmBcnbIDhUqgC8tcxNv/a+BcV5U+2nQMm1OQ==", "dev": true, "license": "MIT", "engines": { "node": "^18.0.0 || ^20.0.0 || >=22.0.0" }, "peerDependencies": { - "vite": "^6.0.0 || ^7.0.0" + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "node_modules/@vitest/expect": { @@ -4193,9 +4806,9 @@ } }, "node_modules/ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "dev": true, "license": "MIT", "dependencies": { @@ -4227,40 +4840,30 @@ } } }, - "node_modules/algoliasearch": { - "version": "5.48.1", - "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.48.1.tgz", - "integrity": "sha512-Rf7xmeuIo7nb6S4mp4abW2faW8DauZyE2faBIKFaUfP3wnpOvNSbiI5AwVhqBNj0jPgBWEvhyCu0sLjN2q77Rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@algolia/abtesting": "1.14.1", - "@algolia/client-abtesting": "5.48.1", - "@algolia/client-analytics": "5.48.1", - "@algolia/client-common": "5.48.1", - "@algolia/client-insights": "5.48.1", - "@algolia/client-personalization": "5.48.1", - "@algolia/client-query-suggestions": "5.48.1", - "@algolia/client-search": "5.48.1", - "@algolia/ingestion": "1.48.1", - "@algolia/monitoring": "1.48.1", - "@algolia/recommend": "5.48.1", - "@algolia/requester-browser-xhr": "5.48.1", - "@algolia/requester-fetch": "5.48.1", - "@algolia/requester-node-http": "5.48.1" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "node_modules/algoliasearch": { + "version": "5.52.0", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.52.0.tgz", + "integrity": "sha512-0ZzY9mjqV7gop/AH8pIBiAS8giXP7WcSiUfoFYIzYAK9QC5c37E4SIVtJVBMwlURc0/uNt2o4RcNRvdHa4CJ5w==", "dev": true, "license": "MIT", + "dependencies": { + "@algolia/abtesting": "1.18.0", + "@algolia/client-abtesting": "5.52.0", + "@algolia/client-analytics": "5.52.0", + "@algolia/client-common": "5.52.0", + "@algolia/client-insights": "5.52.0", + "@algolia/client-personalization": "5.52.0", + "@algolia/client-query-suggestions": "5.52.0", + "@algolia/client-search": "5.52.0", + "@algolia/ingestion": "1.52.0", + "@algolia/monitoring": "1.52.0", + "@algolia/recommend": "5.52.0", + "@algolia/requester-browser-xhr": "5.52.0", + "@algolia/requester-fetch": "5.52.0", + "@algolia/requester-node-http": "5.52.0" + }, "engines": { - "node": ">=6" + "node": ">= 14.0.0" } }, "node_modules/ansi-escapes": { @@ -4346,9 +4949,9 @@ } }, "node_modules/beasties": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/beasties/-/beasties-0.4.1.tgz", - "integrity": "sha512-2Imdcw3LznDuxAbJM26RHniOLAzE6WgrK8OuvVXCQtNBS8rsnD9zsSEa3fHl4hHpUY7BYTlrpvtPVbvu9G6neg==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/beasties/-/beasties-0.4.2.tgz", + "integrity": "sha512-NvcGjG/7AVUAfRbvrJmHunDQS9uHnE6Q/7AkaPr8oKE8HjOlpjRG5075z/th2Tmlezk3VlaaS8+X9I1RwHJMQw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4521,9 +5124,9 @@ } }, "node_modules/cacache/node_modules/lru-cache": { - "version": "11.5.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.0.tgz", - "integrity": "sha512-5YgH9UJd7wVb9hIouI2adWpgqrrICkt070Dnj8EUY1+B4B2P9eRLPAkAAo6NICA7CEhOIeBHl46u9zSNpNu7zA==", + "version": "11.5.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", + "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", "dev": true, "license": "BlueOak-1.0.0", "engines": { @@ -4745,33 +5348,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true, - "license": "MIT" - }, "node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -5197,13 +5773,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/err-code": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", - "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", - "dev": true, - "license": "MIT" - }, "node_modules/errno": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", @@ -5358,9 +5927,9 @@ } }, "node_modules/eventsource-parser": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.8.tgz", - "integrity": "sha512-70QWGkr4snxr0OXLRWsFLeRBIRPuQOvt4s8QYjmUlmlkyTZkRqS7EDVRZtzU3TiyDbXSzaOeF0XUKy8PchzukQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.0.tgz", + "integrity": "sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg==", "dev": true, "license": "MIT", "engines": { @@ -5454,6 +6023,23 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-string-truncated-width": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", + "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-string-width": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz", + "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-string-truncated-width": "^3.0.2" + } + }, "node_modules/fast-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", @@ -5471,6 +6057,16 @@ ], "license": "BSD-3-Clause" }, + "node_modules/fast-wrap-ansi": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.2.tgz", + "integrity": "sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-string-width": "^3.0.2" + } + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -5742,599 +6338,877 @@ } }, "node_modules/hasown": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", - "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hono": { + "version": "4.12.23", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.23.tgz", + "integrity": "sha512-eIaZ9qDgu7XV0pxOCrg7/WhnQ6Ivm22UcxhXx/A3dcbqbbYgBEkc6e/J/s7j2tS96zoB0S9VBdLwQNCWwUo4LA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/hosted-git-info": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.3.tgz", + "integrity": "sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^11.1.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "11.5.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", + "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", + "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.6.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/htmlparser2": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz", + "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==", + "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.2.2", + "entities": "^7.0.1" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "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", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ignore-walk": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-8.0.0.tgz", + "integrity": "sha512-FCeMZT4NiRQGh+YkeKMtWrOmBgWjHjMJ26WQWrRQyoyzqevdaGSakUaJW5xQYmjLlUVk2qUnCjYVBax9EKKg8A==", + "dev": true, + "license": "ISC", + "dependencies": { + "minimatch": "^10.0.3" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", + "dev": true, + "license": "MIT", + "optional": true, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/immutable": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.5.tgz", + "integrity": "sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==", + "dev": true, + "license": "MIT" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ini": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-6.0.0.tgz", + "integrity": "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/injection-js": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/injection-js/-/injection-js-2.6.1.tgz", + "integrity": "sha512-dbR5bdhi7TWDoCye9cByZqeg/gAfamm8Vu3G1KZOTYkOif8WkuM8CD0oeDPtZYMzT5YH76JAFB7bkmyY9OJi2A==", "dev": true, "license": "MIT", "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" + "tslib": "^2.0.0" } }, - "node_modules/hono": { - "version": "4.12.22", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.22.tgz", - "integrity": "sha512-7fvVPbB92zNRsQke+uiRGwtTuef0tB2Dg4hWxYfFNvkQhIltWoyi0ONReM5LWA+jJWS3nfT5lTq+qbsIpX0IQw==", + "node_modules/ip-address": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", + "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", "dev": true, "license": "MIT", "engines": { - "node": ">=16.9.0" + "node": ">= 12" } }, - "node_modules/hosted-git-info": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.3.tgz", - "integrity": "sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg==", + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^11.1.0" - }, + "license": "MIT", "engines": { - "node": "^20.17.0 || >=22.9.0" + "node": ">= 0.10" } }, - "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "11.5.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.0.tgz", - "integrity": "sha512-5YgH9UJd7wVb9hIouI2adWpgqrrICkt070Dnj8EUY1+B4B2P9eRLPAkAAo6NICA7CEhOIeBHl46u9zSNpNu7zA==", + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", + "optional": true, "engines": { - "node": "20 || >=22" + "node": ">=0.10.0" } }, - "node_modules/html-encoding-sniffer": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", - "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", + "node_modules/is-fullwidth-code-point": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", + "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", "dev": true, "license": "MIT", "dependencies": { - "@exodus/bytes": "^1.6.0" + "get-east-asian-width": "^1.3.1" }, "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/htmlparser2": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz", - "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==", + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], "license": "MIT", + "optional": true, "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.2.2", - "entities": "^7.0.1" + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/htmlparser2/node_modules/entities": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", - "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "engines": { - "node": ">=0.12" + "node": ">=12" }, "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/http-cache-semantics": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", - "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", "dev": true, - "license": "BSD-2-Clause" + "license": "MIT" }, - "node_modules/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", "dev": true, "license": "MIT", - "dependencies": { - "depd": "~2.0.0", - "inherits": "~2.0.4", - "setprototypeof": "~1.2.0", - "statuses": "~2.0.2", - "toidentifier": "~1.0.1" - }, "engines": { - "node": ">= 0.8" + "node": ">=18" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/is-what": { + "version": "4.1.16", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz", + "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", "dev": true, "license": "MIT", - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" + "engines": { + "node": ">=12.13" }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "peer": true, "engines": { - "node": ">= 14" + "node": ">=8" } }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", + "optional": true, + "peer": true, "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" }, "engines": { - "node": ">= 14" + "node": ">=10" } }, - "node_modules/iconv-lite": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", - "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "node_modules/jose": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.3.tgz", + "integrity": "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==", "dev": true, "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "url": "https://github.com/sponsors/panva" } }, - "node_modules/ignore-walk": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-8.0.0.tgz", - "integrity": "sha512-FCeMZT4NiRQGh+YkeKMtWrOmBgWjHjMJ26WQWrRQyoyzqevdaGSakUaJW5xQYmjLlUVk2qUnCjYVBax9EKKg8A==", + "node_modules/joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", "dev": true, - "license": "ISC", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsdom": { + "version": "27.4.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-27.4.0.tgz", + "integrity": "sha512-mjzqwWRD9Y1J1KUi7W97Gja1bwOOM5Ug0EZ6UDK3xS7j7mndrkwozHtSblfomlzyB4NepioNt+B2sOSzczVgtQ==", + "dev": true, + "license": "MIT", "dependencies": { - "minimatch": "^10.0.3" + "@acemir/cssom": "^0.9.28", + "@asamuzakjp/dom-selector": "^6.7.6", + "@exodus/bytes": "^1.6.0", + "cssstyle": "^5.3.4", + "data-urls": "^6.0.0", + "decimal.js": "^10.6.0", + "html-encoding-sniffer": "^6.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", + "is-potential-custom-element-name": "^1.0.1", + "parse5": "^8.0.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^6.0.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^8.0.0", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^15.1.0", + "ws": "^8.18.3", + "xml-name-validator": "^5.0.0" }, "engines": { - "node": "^20.17.0 || >=22.9.0" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } } }, - "node_modules/image-size": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", - "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "dev": true, "license": "MIT", - "optional": true, "bin": { - "image-size": "bin/image-size.js" + "jsesc": "bin/jsesc" }, "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, - "node_modules/immutable": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.5.tgz", - "integrity": "sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==", - "dev": true, - "license": "MIT" - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/ini": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-6.0.0.tgz", - "integrity": "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==", + "node_modules/json-parse-even-better-errors": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-5.0.0.tgz", + "integrity": "sha512-ZF1nxZ28VhQouRWhUcVlUIN3qwSgPuswK05s/HIaoetAoE/9tngVmCHjSxmSQPav1nd+lPtTL0YZ/2AFdR/iYQ==", "dev": true, - "license": "ISC", + "license": "MIT", "engines": { "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/injection-js": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/injection-js/-/injection-js-2.6.1.tgz", - "integrity": "sha512-dbR5bdhi7TWDoCye9cByZqeg/gAfamm8Vu3G1KZOTYkOif8WkuM8CD0oeDPtZYMzT5YH76JAFB7bkmyY9OJi2A==", + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "^2.0.0" - } + "license": "MIT" }, - "node_modules/ip-address": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", - "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", + "node_modules/json-schema-typed": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", + "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", "dev": true, - "license": "MIT", - "engines": { - "node": ">= 12" - } + "license": "BSD-2-Clause" }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, "engines": { - "node": ">= 0.10" + "node": ">=6" } }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.10.0" - } + "license": "MIT" }, - "node_modules/is-fullwidth-code-point": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", - "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", "dev": true, - "license": "MIT", + "engines": [ + "node >= 0.2.0" + ], + "license": "MIT" + }, + "node_modules/less": { + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/less/-/less-4.6.4.tgz", + "integrity": "sha512-OJmO5+HxZLLw0RLzkqaNHzcgEAQG7C0y3aMbwtCzIUFZsLMNNq/1IdAdHEycQ58CwUO3jPTHmoN+tE5I7FQxNg==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "get-east-asian-width": "^1.3.1" + "copy-anything": "^3.0.5", + "parse-node-version": "^1.0.1" + }, + "bin": { + "lessc": "bin/lessc" }, "engines": { "node": ">=18" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "optionalDependencies": { + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "needle": "^3.1.0", + "source-map": "~0.6.0" } }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "node_modules/less/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "optional": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, "engines": { "node": ">=0.10.0" } }, - "node_modules/is-interactive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", - "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", "dev": true, "license": "MIT", "engines": { - "node": ">=12" + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/antonk52" } }, - "node_modules/is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-promise": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", - "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true, "license": "MIT" }, - "node_modules/is-unicode-supported": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", - "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "node_modules/listr2": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-10.2.1.tgz", + "integrity": "sha512-7I5knELsJKTUjXG+A6BkKAiGkW1i25fNa/xlUl9hFtk15WbE9jndA89xu5FzQKrY5llajE1hfZZFMILXkDHk/Q==", "dev": true, "license": "MIT", - "engines": { - "node": ">=18" + "dependencies": { + "cli-truncate": "^5.2.0", + "eventemitter3": "^5.0.4", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^10.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=22.13.0" } }, - "node_modules/is-what": { - "version": "4.1.16", - "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz", - "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", + "node_modules/lmdb": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-3.5.4.tgz", + "integrity": "sha512-9FKQA6G1MMtqNxfxvSBNXD/axeG2QRjYbNh0/ykRL5xYcRbCm2vXq7B9bhc7nSuKdHzr8/BHIwfPuYYH1UsXXw==", "dev": true, + "hasInstallScript": true, "license": "MIT", - "engines": { - "node": ">=12.13" + "optional": true, + "dependencies": { + "@harperfast/extended-iterable": "^1.0.3", + "msgpackr": "^1.11.2", + "node-addon-api": "^6.1.0", + "node-gyp-build-optional-packages": "5.2.2", + "ordered-binary": "^1.5.3", + "weak-lru-cache": "^1.2.2" }, - "funding": { - "url": "https://github.com/sponsors/mesqueeb" + "bin": { + "download-lmdb-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@lmdb/lmdb-darwin-arm64": "3.5.4", + "@lmdb/lmdb-darwin-x64": "3.5.4", + "@lmdb/lmdb-linux-arm": "3.5.4", + "@lmdb/lmdb-linux-arm64": "3.5.4", + "@lmdb/lmdb-linux-x64": "3.5.4", + "@lmdb/lmdb-win32-arm64": "3.5.4", + "@lmdb/lmdb-win32-x64": "3.5.4" } }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "node_modules/load-tsconfig": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", + "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", "dev": true, - "license": "ISC" + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "node_modules/log-symbols": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-7.0.1.tgz", + "integrity": "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", + "dependencies": { + "is-unicode-supported": "^2.0.0", + "yoctocolors": "^2.1.1" + }, "engines": { - "node": ">=8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", - "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "dependencies": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" }, "engines": { - "node": ">=10" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jose": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.3.tgz", - "integrity": "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==", + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", + "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", "dev": true, "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, "funding": { - "url": "https://github.com/sponsors/panva" + "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/joycon": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", - "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "node_modules/log-update/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, "engines": { - "node": ">=10" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/jsdom": { - "version": "27.4.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-27.4.0.tgz", - "integrity": "sha512-mjzqwWRD9Y1J1KUi7W97Gja1bwOOM5Ug0EZ6UDK3xS7j7mndrkwozHtSblfomlzyB4NepioNt+B2sOSzczVgtQ==", + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", "dev": true, "license": "MIT", "dependencies": { - "@acemir/cssom": "^0.9.28", - "@asamuzakjp/dom-selector": "^6.7.6", - "@exodus/bytes": "^1.6.0", - "cssstyle": "^5.3.4", - "data-urls": "^6.0.0", - "decimal.js": "^10.6.0", - "html-encoding-sniffer": "^6.0.0", - "http-proxy-agent": "^7.0.2", - "https-proxy-agent": "^7.0.6", - "is-potential-custom-element-name": "^1.0.1", - "parse5": "^8.0.0", - "saxes": "^6.0.0", - "symbol-tree": "^3.2.4", - "tough-cookie": "^6.0.0", - "w3c-xmlserializer": "^5.0.0", - "webidl-conversions": "^8.0.0", - "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^15.1.0", - "ws": "^8.18.3", - "xml-name-validator": "^5.0.0" + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - }, - "peerDependencies": { - "canvas": "^3.0.0" + "node": ">=18" }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "dev": true, "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" } }, - "node_modules/json-parse-even-better-errors": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-5.0.0.tgz", - "integrity": "sha512-ZF1nxZ28VhQouRWhUcVlUIN3qwSgPuswK05s/HIaoetAoE/9tngVmCHjSxmSQPav1nd+lPtTL0YZ/2AFdR/iYQ==", + "node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", "dev": true, "license": "MIT", + "optional": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, "engines": { - "node": "^20.17.0 || >=22.9.0" + "node": ">=6" } }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "node_modules/make-dir/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "license": "MIT" + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver" + } }, - "node_modules/json-schema-typed": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", - "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", + "node_modules/make-fetch-happen": { + "version": "15.0.6", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-15.0.6.tgz", + "integrity": "sha512-Je0fLJ0F5atA7F+eIlLzk+Wkcl57JDf4kf+EW8xiP5E31xOQxkIxTbgf1Oi1Lw9tRI9UEMRdI5Vz2xTzoNU1Jw==", "dev": true, - "license": "BSD-2-Clause" + "license": "ISC", + "dependencies": { + "@gar/promise-retry": "^1.0.0", + "@npmcli/agent": "^4.0.0", + "@npmcli/redact": "^4.0.0", + "cacache": "^20.0.1", + "http-cache-semantics": "^4.1.1", + "minipass": "^7.0.2", + "minipass-fetch": "^5.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^1.0.0", + "proc-log": "^6.0.0", + "ssri": "^13.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "dev": true, "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, "engines": { - "node": ">=6" + "node": ">= 0.4" } }, - "node_modules/jsonc-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", - "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", "dev": true, - "license": "MIT" + "license": "CC0-1.0" }, - "node_modules/jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", "dev": true, - "engines": [ - "node >= 0.2.0" - ], - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">= 0.8" + } }, - "node_modules/less": { - "version": "4.6.4", - "resolved": "https://registry.npmjs.org/less/-/less-4.6.4.tgz", - "integrity": "sha512-OJmO5+HxZLLw0RLzkqaNHzcgEAQG7C0y3aMbwtCzIUFZsLMNNq/1IdAdHEycQ58CwUO3jPTHmoN+tE5I7FQxNg==", + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "copy-anything": "^3.0.5", - "parse-node-version": "^1.0.1" - }, - "bin": { - "lessc": "bin/lessc" - }, + "license": "MIT", "engines": { "node": ">=18" }, - "optionalDependencies": { - "errno": "^0.1.1", - "graceful-fs": "^4.1.2", - "image-size": "~0.5.0", - "make-dir": "^2.1.0", - "mime": "^1.4.1", - "needle": "^3.1.0", - "source-map": "~0.6.0" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/less/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "optional": true, + "bin": { + "mime": "cli.js" + }, "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" + "node": ">= 0.6" } }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true, - "license": "MIT" - }, - "node_modules/listr2": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.5.tgz", - "integrity": "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==", + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "dev": true, "license": "MIT", "dependencies": { - "cli-truncate": "^5.0.0", - "colorette": "^2.0.20", - "eventemitter3": "^5.0.1", - "log-update": "^6.1.0", - "rfdc": "^1.4.1", - "wrap-ansi": "^9.0.0" + "mime-db": "^1.54.0" }, "engines": { - "node": ">=20.0.0" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/listr2/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", "dev": true, "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, "engines": { "node": ">=18" }, @@ -6342,661 +7216,791 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/listr2/node_modules/wrap-ansi": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", - "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" + "brace-expansion": "^5.0.5" }, "engines": { - "node": ">=18" + "node": "18 || 20 || >=22" }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/lmdb": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-3.5.1.tgz", - "integrity": "sha512-NYHA0MRPjvNX+vSw8Xxg6FLKxzAG+e7Pt8RqAQA/EehzHVXq9SxDqJIN3JL1hK0dweb884y8kIh6rkWvPyg9Wg==", + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minipass-collect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", + "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minipass-fetch": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-5.0.2.tgz", + "integrity": "sha512-2d0q2a8eCi2IRg/IGubCNRJoYbA1+YPXAzQVRFmB45gdGZafyivnZ5YSEfo3JikbjGxOdntGFvBQGqaSMXlAFQ==", "dev": true, - "hasInstallScript": true, "license": "MIT", - "optional": true, "dependencies": { - "@harperfast/extended-iterable": "^1.0.3", - "msgpackr": "^1.11.2", - "node-addon-api": "^6.1.0", - "node-gyp-build-optional-packages": "5.2.2", - "ordered-binary": "^1.5.3", - "weak-lru-cache": "^1.2.2" + "minipass": "^7.0.3", + "minipass-sized": "^2.0.0", + "minizlib": "^3.0.1" }, - "bin": { - "download-lmdb-prebuilds": "bin/download-prebuilds.js" + "engines": { + "node": "^20.17.0 || >=22.9.0" }, "optionalDependencies": { - "@lmdb/lmdb-darwin-arm64": "3.5.1", - "@lmdb/lmdb-darwin-x64": "3.5.1", - "@lmdb/lmdb-linux-arm": "3.5.1", - "@lmdb/lmdb-linux-arm64": "3.5.1", - "@lmdb/lmdb-linux-x64": "3.5.1", - "@lmdb/lmdb-win32-arm64": "3.5.1", - "@lmdb/lmdb-win32-x64": "3.5.1" + "iconv-lite": "^0.7.2" } }, - "node_modules/load-tsconfig": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", - "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", + "node_modules/minipass-flush": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.7.tgz", + "integrity": "sha512-TbqTz9cUwWyHS2Dy89P3ocAGUGxKjjLuR9z8w4WUTGAVgEj17/4nhgo2Du56i0Fm3Pm30g4iA8Lcqctc76jCzA==", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", + "dependencies": { + "minipass": "^3.0.0" + }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">= 8" } }, - "node_modules/log-symbols": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-7.0.1.tgz", - "integrity": "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==", + "node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "is-unicode-supported": "^2.0.0", - "yoctocolors": "^2.1.1" + "yallist": "^4.0.0" }, "engines": { - "node": ">=18" + "node": ">=8" + } + }, + "node_modules/minipass-flush/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=8" } }, - "node_modules/log-update": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", - "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", + "node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "ansi-escapes": "^7.0.0", - "cli-cursor": "^5.0.0", - "slice-ansi": "^7.1.0", - "strip-ansi": "^7.1.0", - "wrap-ansi": "^9.0.0" + "yallist": "^4.0.0" }, "engines": { - "node": ">=18" + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/minipass-sized": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-2.0.0.tgz", + "integrity": "sha512-zSsHhto5BcUVM2m1LurnXY6M//cGhVaegT71OfOXoprxT6o780GZd792ea6FfrQkuU4usHZIUczAQMRUE2plzA==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.1.2" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=8" } }, - "node_modules/log-update/node_modules/slice-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", - "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", + "node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^6.2.1", - "is-fullwidth-code-point": "^5.0.0" + "minipass": "^7.1.2" }, "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" + "node": ">= 18" } }, - "node_modules/log-update/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "node_modules/mlly": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz", + "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==", "dev": true, "license": "MIT", "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, + "acorn": "^8.16.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.3" + } + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=10" } }, - "node_modules/log-update/node_modules/wrap-ansi": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", - "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/msgpackr": { + "version": "1.11.12", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.12.tgz", + "integrity": "sha512-RBdJ1Un7yGlXWajrkxcSa93nvQ0w4zBf60c0yYv7YtBelP8H2FA7XsfBbMHtXKXUMUxH7zV3Zuozh+kUQWhHvg==", + "dev": true, + "license": "MIT", + "optional": true, + "optionalDependencies": { + "msgpackr-extract": "^3.0.2" + } + }, + "node_modules/msgpackr-extract": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.4.tgz", + "integrity": "sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw==", "dev": true, + "hasInstallScript": true, "license": "MIT", + "optional": true, "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" + "node-gyp-build-optional-packages": "5.2.2" }, - "engines": { - "node": ">=18" + "bin": { + "download-msgpackr-prebuilds": "bin/download-prebuilds.js" }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "optionalDependencies": { + "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.4", + "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.4" } }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "node_modules/mute-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-3.0.0.tgz", + "integrity": "sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==", "dev": true, "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" + "engines": { + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" } }, - "node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", - "optional": true, - "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" + "bin": { + "nanoid": "bin/nanoid.cjs" }, "engines": { - "node": ">=6" + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/make-dir/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "node_modules/needle": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-3.5.0.tgz", + "integrity": "sha512-jaQyPKKk2YokHrEg+vFDYxXIHTCBgiZwSHOoVx/8V3GIBS8/VN6NdVRmg8q1ERtPkMvmOvebsgga4sAj5hls/w==", "dev": true, - "license": "ISC", + "license": "MIT", "optional": true, + "dependencies": { + "iconv-lite": "^0.6.3", + "sax": "^1.2.4" + }, "bin": { - "semver": "bin/semver" + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" } }, - "node_modules/make-fetch-happen": { - "version": "15.0.5", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-15.0.5.tgz", - "integrity": "sha512-uCbIa8jWWmQZt4dSnEStkVC6gdakiinAm4PiGsywIkguF0eWMdcjDz0ECYhUolFU3pFLOev9VNPCEygydXnddg==", + "node_modules/needle/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": "ISC", + "license": "MIT", + "optional": true, "dependencies": { - "@gar/promise-retry": "^1.0.0", - "@npmcli/agent": "^4.0.0", - "@npmcli/redact": "^4.0.0", - "cacache": "^20.0.1", - "http-cache-semantics": "^4.1.1", - "minipass": "^7.0.2", - "minipass-fetch": "^5.0.0", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^1.0.0", - "proc-log": "^6.0.0", - "ssri": "^13.0.0" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { - "node": "^20.17.0 || >=22.9.0" + "node": ">=0.10.0" } }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "dev": true, "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">= 0.6" } }, - "node_modules/mdn-data": { - "version": "2.27.1", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", - "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", - "dev": true, - "license": "CC0-1.0" - }, - "node_modules/media-typer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "node_modules/ng-packagr": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/ng-packagr/-/ng-packagr-22.0.0.tgz", + "integrity": "sha512-2mXzUdprkDHk4j0NVDcpkVztVwdb1b3o63vLK8YQVCJqCMvCv8BBkFjBo9f1KJmuPf+CE/xuvylhyqfzXoTTqw==", "dev": true, "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.3.0", + "@rollup/plugin-json": "^6.1.0", + "@rollup/wasm-node": "^4.24.0", + "ajv": "^8.17.1", + "browserslist": "^4.26.0", + "chokidar": "^5.0.0", + "commander": "^14.0.0", + "dependency-graph": "^1.0.0", + "esbuild": "^0.28.0", + "find-cache-directory": "^6.0.0", + "injection-js": "^2.4.0", + "jsonc-parser": "^3.3.1", + "less": "^4.2.0", + "ora": "^9.0.0", + "piscina": "^5.0.0", + "postcss": "^8.4.47", + "rollup-plugin-dts": "^6.4.0", + "rxjs": "^7.8.1", + "sass": "^1.81.0", + "tinyglobby": "^0.2.12" + }, + "bin": { + "ng-packagr": "src/cli/main.js" + }, "engines": { - "node": ">= 0.8" + "node": "^22.22.3 || ^24.15.0 || >=26.0.0" + }, + "optionalDependencies": { + "rollup": "^4.24.0" + }, + "peerDependencies": { + "@angular/compiler-cli": "^22.0.0 || ^22.1.0-next.0", + "tailwindcss": "^2.0.0 || ^3.0.0 || ^4.0.0", + "tslib": "^2.3.0", + "typescript": ">=6.0 <6.1" + }, + "peerDependenciesMeta": { + "tailwindcss": { + "optional": true + } } }, - "node_modules/merge-descriptors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", - "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "node_modules/ng-packagr/node_modules/@esbuild/aix-ppc64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz", + "integrity": "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==", + "cpu": [ + "ppc64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "aix" + ], "engines": { "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "node_modules/ng-packagr/node_modules/@esbuild/android-arm": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.0.tgz", + "integrity": "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", "optional": true, - "bin": { - "mime": "cli.js" - }, + "os": [ + "android" + ], "engines": { - "node": ">=4" + "node": ">=18" } }, - "node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "node_modules/ng-packagr/node_modules/@esbuild/android-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.0.tgz", + "integrity": "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">= 0.6" + "node": ">=18" } }, - "node_modules/mime-types": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", - "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "node_modules/ng-packagr/node_modules/@esbuild/android-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.0.tgz", + "integrity": "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" - }, + "optional": true, + "os": [ + "android" + ], "engines": { "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" } }, - "node_modules/mimic-function": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", - "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "node_modules/ng-packagr/node_modules/@esbuild/darwin-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.0.tgz", + "integrity": "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "node_modules/ng-packagr/node_modules/@esbuild/darwin-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.0.tgz", + "integrity": "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==", + "cpu": [ + "x64" + ], "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.5" - }, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=18" } }, - "node_modules/minipass": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", - "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "node_modules/ng-packagr/node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.0.tgz", + "integrity": "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=18" } }, - "node_modules/minipass-collect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", - "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", + "node_modules/ng-packagr/node_modules/@esbuild/freebsd-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.0.tgz", + "integrity": "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==", + "cpu": [ + "x64" + ], "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.3" - }, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=18" } }, - "node_modules/minipass-fetch": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-5.0.2.tgz", - "integrity": "sha512-2d0q2a8eCi2IRg/IGubCNRJoYbA1+YPXAzQVRFmB45gdGZafyivnZ5YSEfo3JikbjGxOdntGFvBQGqaSMXlAFQ==", + "node_modules/ng-packagr/node_modules/@esbuild/linux-arm": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.0.tgz", + "integrity": "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", - "dependencies": { - "minipass": "^7.0.3", - "minipass-sized": "^2.0.0", - "minizlib": "^3.0.1" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^20.17.0 || >=22.9.0" - }, - "optionalDependencies": { - "iconv-lite": "^0.7.2" + "node": ">=18" } }, - "node_modules/minipass-flush": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.7.tgz", - "integrity": "sha512-TbqTz9cUwWyHS2Dy89P3ocAGUGxKjjLuR9z8w4WUTGAVgEj17/4nhgo2Du56i0Fm3Pm30g4iA8Lcqctc76jCzA==", + "node_modules/ng-packagr/node_modules/@esbuild/linux-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.0.tgz", + "integrity": "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "minipass": "^3.0.0" - }, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 8" + "node": ">=18" } }, - "node_modules/minipass-flush/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "node_modules/ng-packagr/node_modules/@esbuild/linux-ia32": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.0.tgz", + "integrity": "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==", + "cpu": [ + "ia32" + ], "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/minipass-flush/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, - "license": "ISC" - }, - "node_modules/minipass-pipeline": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", - "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "node_modules/ng-packagr/node_modules/@esbuild/linux-loong64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.0.tgz", + "integrity": "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==", + "cpu": [ + "loong64" + ], "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/minipass-pipeline/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "node_modules/ng-packagr/node_modules/@esbuild/linux-mips64el": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.0.tgz", + "integrity": "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==", + "cpu": [ + "mips64el" + ], "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/minipass-pipeline/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, - "license": "ISC" - }, - "node_modules/minipass-sized": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-2.0.0.tgz", - "integrity": "sha512-zSsHhto5BcUVM2m1LurnXY6M//cGhVaegT71OfOXoprxT6o780GZd792ea6FfrQkuU4usHZIUczAQMRUE2plzA==", + "node_modules/ng-packagr/node_modules/@esbuild/linux-ppc64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.0.tgz", + "integrity": "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==", + "cpu": [ + "ppc64" + ], "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.1.2" - }, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/minizlib": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", - "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "node_modules/ng-packagr/node_modules/@esbuild/linux-riscv64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.0.tgz", + "integrity": "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==", + "cpu": [ + "riscv64" + ], "dev": true, "license": "MIT", - "dependencies": { - "minipass": "^7.1.2" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 18" + "node": ">=18" } }, - "node_modules/mlly": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz", - "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==", + "node_modules/ng-packagr/node_modules/@esbuild/linux-s390x": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.0.tgz", + "integrity": "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==", + "cpu": [ + "s390x" + ], "dev": true, "license": "MIT", - "dependencies": { - "acorn": "^8.16.0", - "pathe": "^2.0.3", - "pkg-types": "^1.3.1", - "ufo": "^1.6.3" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/mrmime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", - "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "node_modules/ng-packagr/node_modules/@esbuild/linux-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.0.tgz", + "integrity": "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=10" + "node": ">=18" } }, - "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==", - "dev": true, - "license": "MIT" - }, - "node_modules/msgpackr": { - "version": "1.11.12", - "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.12.tgz", - "integrity": "sha512-RBdJ1Un7yGlXWajrkxcSa93nvQ0w4zBf60c0yYv7YtBelP8H2FA7XsfBbMHtXKXUMUxH7zV3Zuozh+kUQWhHvg==", + "node_modules/ng-packagr/node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.0.tgz", + "integrity": "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", "optional": true, - "optionalDependencies": { - "msgpackr-extract": "^3.0.2" + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" } }, - "node_modules/msgpackr-extract": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz", - "integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==", + "node_modules/ng-packagr/node_modules/@esbuild/netbsd-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.0.tgz", + "integrity": "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==", + "cpu": [ + "x64" + ], "dev": true, - "hasInstallScript": true, "license": "MIT", "optional": true, - "dependencies": { - "node-gyp-build-optional-packages": "5.2.2" - }, - "bin": { - "download-msgpackr-prebuilds": "bin/download-prebuilds.js" - }, - "optionalDependencies": { - "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", - "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", - "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", - "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", - "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", - "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" } }, - "node_modules/mute-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", - "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", + "node_modules/ng-packagr/node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.0.tgz", + "integrity": "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "ISC", + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": ">=18" } }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "node_modules/ng-packagr/node_modules/@esbuild/openbsd-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.0.tgz", + "integrity": "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" } }, - "node_modules/nanoid": { - "version": "3.3.12", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", - "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } + "node_modules/ng-packagr/node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.0.tgz", + "integrity": "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==", + "cpu": [ + "arm64" ], + "dev": true, "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, + "optional": true, + "os": [ + "openharmony" + ], "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": ">=18" } }, - "node_modules/needle": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/needle/-/needle-3.5.0.tgz", - "integrity": "sha512-jaQyPKKk2YokHrEg+vFDYxXIHTCBgiZwSHOoVx/8V3GIBS8/VN6NdVRmg8q1ERtPkMvmOvebsgga4sAj5hls/w==", + "node_modules/ng-packagr/node_modules/@esbuild/sunos-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.0.tgz", + "integrity": "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", "optional": true, - "dependencies": { - "iconv-lite": "^0.6.3", - "sax": "^1.2.4" - }, - "bin": { - "needle": "bin/needle" - }, + "os": [ + "sunos" + ], "engines": { - "node": ">= 4.4.x" + "node": ">=18" } }, - "node_modules/needle/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==", + "node_modules/ng-packagr/node_modules/@esbuild/win32-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.0.tgz", + "integrity": "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", "optional": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, + "os": [ + "win32" + ], "engines": { - "node": ">=0.10.0" + "node": ">=18" } }, - "node_modules/negotiator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "node_modules/ng-packagr/node_modules/@esbuild/win32-ia32": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.0.tgz", + "integrity": "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==", + "cpu": [ + "ia32" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">= 0.6" + "node": ">=18" } }, - "node_modules/ng-packagr": { - "version": "21.2.3", - "resolved": "https://registry.npmjs.org/ng-packagr/-/ng-packagr-21.2.3.tgz", - "integrity": "sha512-jGq6yu0G6KReVK0i5RYVoV9HDL0mU626HrLBu5xvc8ZJ92n/+rLrFJuXdCnkroB9um+FBTQe/or6/A/2GAKhLw==", + "node_modules/ng-packagr/node_modules/@esbuild/win32-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz", + "integrity": "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.3.0", - "@rollup/plugin-json": "^6.1.0", - "@rollup/wasm-node": "^4.24.0", - "ajv": "^8.17.1", - "ansi-colors": "^4.1.3", - "browserslist": "^4.26.0", - "chokidar": "^5.0.0", - "commander": "^14.0.0", - "dependency-graph": "^1.0.0", - "esbuild": "^0.27.0", - "find-cache-directory": "^6.0.0", - "injection-js": "^2.4.0", - "jsonc-parser": "^3.3.1", - "less": "^4.2.0", - "ora": "^9.0.0", - "piscina": "^5.0.0", - "postcss": "^8.4.47", - "rollup-plugin-dts": "^6.4.0", - "rxjs": "^7.8.1", - "sass": "^1.81.0", - "tinyglobby": "^0.2.12" - }, - "bin": { - "ng-packagr": "src/cli/main.js" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - }, - "optionalDependencies": { - "rollup": "^4.24.0" - }, - "peerDependencies": { - "@angular/compiler-cli": "^21.0.0 || ^21.2.0-next", - "tailwindcss": "^2.0.0 || ^3.0.0 || ^4.0.0", - "tslib": "^2.3.0", - "typescript": ">=5.9 <6.0" - }, - "peerDependenciesMeta": { - "tailwindcss": { - "optional": true - } + "node": ">=18" } }, "node_modules/ng-packagr/node_modules/commander": { @@ -7009,6 +8013,48 @@ "node": ">=20" } }, + "node_modules/ng-packagr/node_modules/esbuild": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz", + "integrity": "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.0", + "@esbuild/android-arm": "0.28.0", + "@esbuild/android-arm64": "0.28.0", + "@esbuild/android-x64": "0.28.0", + "@esbuild/darwin-arm64": "0.28.0", + "@esbuild/darwin-x64": "0.28.0", + "@esbuild/freebsd-arm64": "0.28.0", + "@esbuild/freebsd-x64": "0.28.0", + "@esbuild/linux-arm": "0.28.0", + "@esbuild/linux-arm64": "0.28.0", + "@esbuild/linux-ia32": "0.28.0", + "@esbuild/linux-loong64": "0.28.0", + "@esbuild/linux-mips64el": "0.28.0", + "@esbuild/linux-ppc64": "0.28.0", + "@esbuild/linux-riscv64": "0.28.0", + "@esbuild/linux-s390x": "0.28.0", + "@esbuild/linux-x64": "0.28.0", + "@esbuild/netbsd-arm64": "0.28.0", + "@esbuild/netbsd-x64": "0.28.0", + "@esbuild/openbsd-arm64": "0.28.0", + "@esbuild/openbsd-x64": "0.28.0", + "@esbuild/openharmony-arm64": "0.28.0", + "@esbuild/sunos-x64": "0.28.0", + "@esbuild/win32-arm64": "0.28.0", + "@esbuild/win32-ia32": "0.28.0", + "@esbuild/win32-x64": "0.28.0" + } + }, "node_modules/node-addon-api": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", @@ -7068,16 +8114,6 @@ "node": ">=20" } }, - "node_modules/node-gyp/node_modules/undici": { - "version": "6.25.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.25.0.tgz", - "integrity": "sha512-ZgpWDC5gmNiuY9CnLVXEH8rl50xhRCuLNA97fAUnKi8RRuV4E6KG31pDTsLVUKnohJE0I3XDrTeEydAXRw47xg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.17" - } - }, "node_modules/node-gyp/node_modules/which": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", @@ -7309,9 +8345,9 @@ } }, "node_modules/ora": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-9.3.0.tgz", - "integrity": "sha512-lBX72MWFduWEf7v7uWf5DHp9Jn5BI8bNPGuFgtXMmr2uDz2Gz2749y3am3agSDdkhHPHYmmxEGSKH85ZLGzgXw==", + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-9.4.0.tgz", + "integrity": "sha512-84cglkRILFxdtA8hAvLNdMrtBpPNBTrQ9/ulg0FA7xLMnD6mifv+enAIeRmvtv+WgdCE+LPGOfQmtJRrVaIVhQ==", "dev": true, "license": "MIT", "dependencies": { @@ -7321,7 +8357,7 @@ "is-interactive": "^2.0.0", "is-unicode-supported": "^2.1.0", "log-symbols": "^7.0.1", - "stdin-discarder": "^0.3.1", + "stdin-discarder": "^0.3.2", "string-width": "^8.1.0" }, "engines": { @@ -7353,12 +8389,13 @@ } }, "node_modules/pacote": { - "version": "21.3.1", - "resolved": "https://registry.npmjs.org/pacote/-/pacote-21.3.1.tgz", - "integrity": "sha512-O0EDXi85LF4AzdjG74GUwEArhdvawi/YOHcsW6IijKNj7wm8IvEWNF5GnfuxNpQ/ZpO3L37+v8hqdVh8GgWYhg==", + "version": "21.5.0", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-21.5.0.tgz", + "integrity": "sha512-VtZ0SB8mb5Tzw3dXDfVAIjhyVKUHZkS/ZH9/5mpKenwC9sFOXNI0JI7kEF7IMkwOnsWMFrvAZHzx1T5fmrp9FQ==", "dev": true, "license": "ISC", "dependencies": { + "@gar/promise-retry": "^1.0.0", "@npmcli/git": "^7.0.0", "@npmcli/installed-package-contents": "^4.0.0", "@npmcli/package-json": "^7.0.0", @@ -7372,7 +8409,6 @@ "npm-pick-manifest": "^11.0.1", "npm-registry-fetch": "^19.0.0", "proc-log": "^6.0.0", - "promise-retry": "^2.0.1", "sigstore": "^4.0.0", "ssri": "^13.0.0", "tar": "^7.4.3" @@ -7408,13 +8444,13 @@ } }, "node_modules/parse5-html-rewriting-stream": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-8.0.0.tgz", - "integrity": "sha512-wzh11mj8KKkno1pZEu+l2EVeWsuKDfR5KNWZOTsslfUX8lPDZx77m9T0kIoAVkFtD1nx6YF8oh4BnPHvxMtNMw==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-8.0.1.tgz", + "integrity": "sha512-NaRku2aMpUN1Sh1Gyk1KWUh2A7EJx2c6qYzvwsPtqhoHoaURshdrceYK3LunVCm3WHhm6FS7Vcczbvdh3/UIVw==", "dev": true, "license": "MIT", "dependencies": { - "entities": "^6.0.0", + "entities": "^8.0.0", "parse5": "^8.0.0", "parse5-sax-parser": "^8.0.0" }, @@ -7423,13 +8459,13 @@ } }, "node_modules/parse5-html-rewriting-stream/node_modules/entities": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", - "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz", + "integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==", "dev": true, "license": "BSD-2-Clause", "engines": { - "node": ">=0.12" + "node": ">=20.19.0" }, "funding": { "url": "https://github.com/fb55/entities?sponsor=1" @@ -7499,9 +8535,9 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "11.5.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.0.tgz", - "integrity": "sha512-5YgH9UJd7wVb9hIouI2adWpgqrrICkt070Dnj8EUY1+B4B2P9eRLPAkAAo6NICA7CEhOIeBHl46u9zSNpNu7zA==", + "version": "11.5.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", + "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", "dev": true, "license": "BlueOak-1.0.0", "engines": { @@ -7766,20 +8802,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/promise-retry": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", - "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "err-code": "^2.0.2", - "retry": "^0.12.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -7912,16 +8934,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, "node_modules/rfdc": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", @@ -7929,38 +8941,6 @@ "dev": true, "license": "MIT" }, - "node_modules/rolldown": { - "version": "1.0.0-rc.4", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.4.tgz", - "integrity": "sha512-V2tPDUrY3WSevrvU2E41ijZlpF+5PbZu4giH+VpNraaadsJGHa4fR6IFwsocVwEXDoAdIv5qgPPxgrvKAOIPtA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@oxc-project/types": "=0.113.0", - "@rolldown/pluginutils": "1.0.0-rc.4" - }, - "bin": { - "rolldown": "bin/cli.mjs" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.0-rc.4", - "@rolldown/binding-darwin-arm64": "1.0.0-rc.4", - "@rolldown/binding-darwin-x64": "1.0.0-rc.4", - "@rolldown/binding-freebsd-x64": "1.0.0-rc.4", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.4", - "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.4", - "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.4", - "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.4", - "@rolldown/binding-linux-x64-musl": "1.0.0-rc.4", - "@rolldown/binding-openharmony-arm64": "1.0.0-rc.4", - "@rolldown/binding-wasm32-wasi": "1.0.0-rc.4", - "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.4", - "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.4" - } - }, "node_modules/rollup": { "version": "4.60.4", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.4.tgz", @@ -8073,14 +9053,14 @@ "license": "MIT" }, "node_modules/sass": { - "version": "1.97.3", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.97.3.tgz", - "integrity": "sha512-fDz1zJpd5GycprAbu4Q2PV/RprsRtKC/0z82z0JLgdytmcq0+ujJbJ/09bPGDxCLkKY3Np5cRAOcWiVkLXJURg==", + "version": "1.99.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.99.0.tgz", + "integrity": "sha512-kgW13M54DUB7IsIRM5LvJkNlpH+WhMpooUcaWGFARkF1Tc82v9mIWkCbCYf+MBvpIUBSeSOTilpZjEPr2VYE6Q==", "dev": true, "license": "MIT", "dependencies": { "chokidar": "^4.0.0", - "immutable": "^5.0.2", + "immutable": "^5.1.5", "source-map-js": ">=0.6.2 <2.0.0" }, "bin": { @@ -8589,9 +9569,9 @@ "license": "MIT" }, "node_modules/tar": { - "version": "7.5.15", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.15.tgz", - "integrity": "sha512-dzGK0boVlC4W5QFuQN1EFSl3bIDYsk7Tj40U6eIBnK2k/8ml7TZ5agbI5j5+qnoVcAA+rNtBml8SEiLxZpNqRQ==", + "version": "7.5.16", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.16.tgz", + "integrity": "sha512-56adEpPMouktRlBLXiaYFFzZ/3+JXa8P9n7WbR+ibIjtviN55mEaOkiysCnPnWm+7kkui1Dn8J9l+g6zV8731w==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -8653,14 +9633,14 @@ "license": "MIT" }, "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", - "picomatch": "^4.0.3" + "picomatch": "^4.0.4" }, "engines": { "node": ">=12.0.0" @@ -8890,9 +9870,9 @@ } }, "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -8911,13 +9891,13 @@ "license": "MIT" }, "node_modules/undici": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.4.tgz", - "integrity": "sha512-BM/JzwwaRXxrLdElV2Uo6cTLEjhSb3WXboncJamZ15NgUURmvlXvxa6xkwIOILIjPNo9i8ku136ZvWV0Uly8+w==", + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.26.0.tgz", + "integrity": "sha512-4yqz8a3n5HmGTlsbADNtr/dJlhkh/55Rq798G6ibiULcXbDtaLpTl1pvdqcbFfeoj3iSi52lePFM7h9H21cw/A==", "dev": true, "license": "MIT", "engines": { - "node": ">=20.18.1" + "node": ">=18.17" } }, "node_modules/unpipe": { @@ -9274,89 +10254,21 @@ } }, "node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-10.0.0.tgz", + "integrity": "sha512-SGcvg80f0wUy2/fXES19feHMz8E0JoXv2uNgHOu4Dgi2OrCy1lqwFYEJz1BLbDI0exjPMe/ZdzZ/YpGECBG/aQ==", "dev": true, "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "ansi-styles": "^6.2.3", + "string-width": "^8.2.0", + "strip-ansi": "^7.1.2" }, "engines": { - "node": ">=8" + "node": ">=20" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/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/wrap-ansi/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/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/wrap-ansi/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/wrappy": { @@ -9481,23 +10393,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/yoctocolors-cjs": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", - "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/zod": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", - "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.2.tgz", + "integrity": "sha512-IynmDyxsEsb9RKzO3J9+4SxXnl2FTFSzNBaKKaMV6tsSk0rw9gYw9gs+JFCq/qk2LCZ78KDwyj+Z289TijSkUw==", "dev": true, "license": "MIT", "funding": { diff --git a/package.json b/package.json index 1890dec..ae9c2c2 100644 --- a/package.json +++ b/package.json @@ -11,26 +11,26 @@ "test:e2e": "playwright test" }, "dependencies": { - "@angular/animations": "21.2.14", - "@angular/common": "21.2.14", - "@angular/compiler": "21.2.14", - "@angular/core": "21.2.14", - "@angular/forms": "21.2.14", - "@angular/platform-browser": "21.2.14", - "@angular/router": "21.2.14", "rxjs": "^7.8.2", "tslib": "^2.8.1", "zone.js": "^0.16.0" }, "devDependencies": { - "@angular/build": "21.2.12", - "@angular/cli": "21.2.12", - "@angular/compiler-cli": "21.2.14", + "@angular/animations": "^22.0.0", + "@angular/build": "^22.0.0", + "@angular/cli": "^22.0.0", + "@angular/common": "^22.0.0", + "@angular/compiler": "^22.0.0", + "@angular/compiler-cli": "^22.0.0", + "@angular/core": "^22.0.0", + "@angular/forms": "^22.0.0", + "@angular/platform-browser": "^22.0.0", + "@angular/router": "^22.0.0", "@playwright/test": "^1.57.0", "jsdom": "^27.3.0", - "ng-packagr": "^21.2.3", + "ng-packagr": "^22.0.0", "tsup": "^8.5.1", - "typescript": "~5.9.3", + "typescript": "^6.0.3", "vitest": "^4.0.15" } } diff --git a/packages/angular-render-scan/package.json b/packages/angular-render-scan/package.json index 2b62869..7cf6dfa 100644 --- a/packages/angular-render-scan/package.json +++ b/packages/angular-render-scan/package.json @@ -1,6 +1,6 @@ { "name": "angular-render-scan", - "version": "0.1.5", + "version": "0.1.6", "description": "Visual Angular change-detection scanner for debugging component renders.", "keywords": [ "angular", @@ -42,6 +42,10 @@ "types": "./dist/types/angular-render-scan.d.ts", "default": "./dist/fesm2022/angular-render-scan.mjs" }, + "./noop": { + "types": "./dist/types/noop.d.ts", + "default": "./dist/fesm2022/noop.mjs" + }, "./auto.global": "./dist/auto.global.js" }, "peerDependencies": { diff --git a/packages/angular-render-scan/src/application/cd-graph.ts b/packages/angular-render-scan/src/application/cd-graph.ts new file mode 100644 index 0000000..9267392 --- /dev/null +++ b/packages/angular-render-scan/src/application/cd-graph.ts @@ -0,0 +1,68 @@ +/** + * Change Detection Graph Builder + * + * Builds a snapshot graph of the component tree showing how CD flows through + * the application. Each node is a component, each edge is a parent→child + * render relationship. + * + * Domain layer — no Angular imports, no DOM access. + */ + +import type { CdGraph, CdGraphEdge, CdGraphNode, CdTriggerSource } from '../domain/entities'; + +export interface ComponentGraphData { + id: string; + name: string; + selector: string; + parentId: string | null; + depth: number; + renderCount: number; + totalDuration: number; + wastedChecks: number; + totalChecks: number; + cdStrategy: 'OnPush' | 'Default' | 'unknown'; + isOnPushCandidate: boolean; + lastTrigger?: CdTriggerSource; +} + +// Parent→child trigger counts across the session +const edgeTriggerCounts = new Map(); + +export function recordParentChildRender(parentId: string, childId: string): void { + const key = `${parentId}→${childId}`; + edgeTriggerCounts.set(key, (edgeTriggerCounts.get(key) ?? 0) + 1); +} + +export function buildCdGraph(components: ComponentGraphData[]): CdGraph { + const nodes: CdGraphNode[] = components.map(c => ({ + id: c.id, + name: c.name, + selector: c.selector, + parentId: c.parentId, + depth: c.depth, + renderCount: c.renderCount, + totalDuration: c.totalDuration, + wastedChecks: c.wastedChecks, + cdStrategy: c.cdStrategy, + isOnPushCandidate: c.isOnPushCandidate, + lastTrigger: c.lastTrigger + })); + + const edges: CdGraphEdge[] = []; + for (const [key, count] of edgeTriggerCounts.entries()) { + const [fromId, toId] = key.split('→'); + if (fromId && toId) { + edges.push({ fromId, toId, triggerCount: count }); + } + } + + return { + nodes, + edges, + capturedAt: performance.now() + }; +} + +export function resetCdGraph(): void { + edgeTriggerCounts.clear(); +} diff --git a/packages/angular-render-scan/src/application/onpush-analyzer.ts b/packages/angular-render-scan/src/application/onpush-analyzer.ts new file mode 100644 index 0000000..addc3b0 --- /dev/null +++ b/packages/angular-render-scan/src/application/onpush-analyzer.ts @@ -0,0 +1,88 @@ +/** + * OnPush Candidate Analyzer + * + * Domain-layer module. Analyzes component stats to determine which components + * that use ChangeDetectionStrategy.Default could safely migrate to OnPush. + * + * No Angular imports. No DOM access. Pure computation over component data. + */ + +import type { OnPushCandidate } from '../domain/entities'; + +export interface ComponentStatsForOnPush { + id: string; + name: string; + selector: string; + totalChecks: number; + wastedChecks: number; + totalDuration: number; + cdStrategy: 'OnPush' | 'Default' | 'unknown'; + inputChangeCount: number; // how many checks had input changes +} + +/** + * Analyze a set of component stats and return components that are good + * candidates for ChangeDetectionStrategy.OnPush migration. + * + * A component is a candidate when: + * 1. It uses Default CD (or unknown) + * 2. Its wasted render % exceeds the threshold + * 3. It has been checked enough times to be statistically meaningful + */ +export function analyzeOnPushCandidates( + components: ComponentStatsForOnPush[], + wastedThresholdPct: number = 40, + minChecks: number = 5 +): OnPushCandidate[] { + const candidates: OnPushCandidate[] = []; + + for (const comp of components) { + // Skip already-OnPush components + if (comp.cdStrategy === 'OnPush') continue; + // Need enough data to be meaningful + if (comp.totalChecks < minChecks) continue; + + const wastedPct = comp.totalChecks > 0 + ? Math.round((comp.wastedChecks / comp.totalChecks) * 100) + : 0; + + if (wastedPct < wastedThresholdPct) continue; + + // Estimate savings: if OnPush, only renders when inputs change + // So saving is approximately (wasted checks / total checks) + const estimatedSavingPct = wastedPct; + + let confidence: 'high' | 'medium' | 'low'; + let reason: string; + + if (wastedPct >= 80) { + confidence = 'high'; + reason = `${wastedPct}% of renders produced no DOM changes — component only needs to update when its @Input() props change.`; + } else if (wastedPct >= 60) { + confidence = 'medium'; + reason = `${wastedPct}% of renders were wasted no-ops. OnPush would significantly reduce unnecessary checks.`; + } else { + confidence = 'low'; + reason = `${wastedPct}% wasted renders detected. Review whether this component has internal side effects before switching to OnPush.`; + } + + candidates.push({ + name: comp.name, + selector: comp.selector, + totalChecks: comp.totalChecks, + wastedChecks: comp.wastedChecks, + wastedPercentage: wastedPct, + estimatedSavingPct, + confidence, + reason + }); + } + + // Sort by estimated saving desc, then confidence desc + return candidates.sort((a, b) => { + const confScore = { high: 3, medium: 2, low: 1 }; + const byConf = confScore[b.confidence] - confScore[a.confidence]; + if (byConf !== 0) return byConf; + return b.estimatedSavingPct - a.estimatedSavingPct; + }); +} diff --git a/packages/angular-render-scan/src/application/referential-stability.ts b/packages/angular-render-scan/src/application/referential-stability.ts new file mode 100644 index 0000000..d4f673b --- /dev/null +++ b/packages/angular-render-scan/src/application/referential-stability.ts @@ -0,0 +1,134 @@ +/** + * Referential Input Stability Tracker + * + * Detects when an @Input() receives a new object reference but the value + * is deeply equal to the previous one. This is the #1 cause of unexpected + * re-renders on OnPush components and a very common Angular performance bug. + * + * Domain layer — no Angular imports, no DOM access. + */ + +import type { ReferentialInstabilityReport } from '../domain/entities'; + +interface InputStabilityEntry { + componentName: string; + selector: string; + inputName: string; + /** Last serialized value for deep equality */ + lastSerialized: string; + /** Last raw reference (for reference comparison) */ + lastRef: unknown; + /** Times reference changed but value was equal */ + unstableRefCount: number; + /** Total times we saw a changed input reference */ + totalChangedRefCount: number; +} + +// Map: `${componentId}:${inputName}` → entry +const stabilityMap = new Map(); + +/** + * Serialize a value for deep equality comparison. + * Uses JSON.stringify with a depth guard — not perfect but fast and + * safe for the vast majority of Angular @Input() values. + */ +function serializeDeep(value: unknown, maxDepth: number, currentDepth = 0): string { + if (value === null) return 'null'; + if (value === undefined) return 'undefined'; + if (typeof value !== 'object' && typeof value !== 'function') return String(value); + if (typeof value === 'function') return 'Function'; + if (currentDepth >= maxDepth) return Array.isArray(value) ? `Array(${(value as unknown[]).length})` : 'Object'; + + try { + if (Array.isArray(value)) { + const items = (value as unknown[]).slice(0, 20).map(v => serializeDeep(v, maxDepth, currentDepth + 1)); + return `[${items.join(',')}]`; + } + + const obj = value as Record; + const keys = Object.keys(obj).slice(0, 20).sort(); + const pairs = keys.map(k => `${k}:${serializeDeep(obj[k], maxDepth, currentDepth + 1)}`); + return `{${pairs.join(',')}}`; + } catch { + return 'Object'; + } +} + +/** + * Called during auto-instrumentation when an input changes reference. + * Returns true if the new value is referentially unstable (same deep value, new ref). + */ +export function checkReferentialStability( + componentId: string, + componentName: string, + selector: string, + inputName: string, + newRef: unknown, + maxDepth: number = 4 +): boolean { + // Only objects/arrays can be referentially unstable + if (newRef === null || typeof newRef !== 'object') return false; + + const key = `${componentId}:${inputName}`; + const entry = stabilityMap.get(key); + + const newSerialized = serializeDeep(newRef, maxDepth); + + if (!entry) { + stabilityMap.set(key, { + componentName, + selector, + inputName, + lastSerialized: newSerialized, + lastRef: newRef, + unstableRefCount: 0, + totalChangedRefCount: 1 + }); + return false; + } + + // Reference changed (we're only called when reference changed) + entry.totalChangedRefCount++; + + const isUnstable = entry.lastRef !== newRef && entry.lastSerialized === newSerialized; + + if (isUnstable) { + entry.unstableRefCount++; + } + + entry.lastSerialized = newSerialized; + entry.lastRef = newRef; + + return isUnstable; +} + +export function getReferentialInstabilityReports(minUnstableRefs = 1): ReferentialInstabilityReport[] { + const reports: ReferentialInstabilityReport[] = []; + for (const entry of stabilityMap.values()) { + if (entry.unstableRefCount < minUnstableRefs) continue; + const unstableRefPct = entry.totalChangedRefCount > 0 + ? Math.round((entry.unstableRefCount / entry.totalChangedRefCount) * 100) + : 0; + reports.push({ + componentName: entry.componentName, + selector: entry.selector, + inputName: entry.inputName, + unstableRefCount: entry.unstableRefCount, + totalRenders: entry.totalChangedRefCount, + unstableRefPct, + lastValue: entry.lastSerialized.slice(0, 120) + }); + } + return reports.sort((a, b) => b.unstableRefCount - a.unstableRefCount); +} + +export function resetReferentialStability(): void { + stabilityMap.clear(); +} + +export function clearReferentialStabilityStats(): void { + for (const entry of stabilityMap.values()) { + entry.unstableRefCount = 0; + entry.totalChangedRefCount = 0; + } +} diff --git a/packages/angular-render-scan/src/application/runtime.ts b/packages/angular-render-scan/src/application/runtime.ts index c813dd2..912f8f8 100644 --- a/packages/angular-render-scan/src/application/runtime.ts +++ b/packages/angular-render-scan/src/application/runtime.ts @@ -1,13 +1,28 @@ import { AngularRenderScanOverlay } from '../infrastructure/ui/overlay'; import { getResolvedOptions, resolveOptions, setResolvedOptions } from '../domain/options'; -import { finishCycle, resetStats, startCycle, getWastedStats, getLeakedComponents } from './stats'; +import { + finishCycle, + resetStats, + startCycle, + getWastedStats as statsGetWastedStats, + getLeakedComponents as statsGetLeakedComponents, + getOnPushCandidates as statsGetOnPushCandidates, + getReferentialInstability as statsGetReferentialInstability, + getCdGraph as statsGetCdGraph, + clearStats +} from './stats'; import type { AngularRenderCycle, AngularRenderEntry, AngularRenderScanOptions, BudgetViolation, + CdTriggerAttribution, + OnPushCandidate, + ReferentialInstabilityReport, SessionExportData, - WastedStats + WastedStats, + ZonePollutionEvent, + CdGraph } from '../domain/entities'; let overlay: AngularRenderScanOverlay | undefined; @@ -17,6 +32,24 @@ let lastCycle: AngularRenderCycle | undefined; let implicitCycleScheduled = false; let recentCycles: AngularRenderCycle[] = []; let activeSessionBudgetViolations: BudgetViolation[] = []; +let activeZonePollutionEvents: ZonePollutionEvent[] = []; + +// Lazily-loaded Zone tracker references (avoid importing in test/non-Zone envs) +let _resolveTrigger: (() => CdTriggerAttribution) | null = null; +let _resetCycleTrigger: (() => void) | null = null; + +async function ensureZoneTracker() { + if (_resolveTrigger) return; + try { + const mod = await import('../infrastructure/angular/zone-tracker'); + _resolveTrigger = mod.resolveTriggerAttribution; + _resetCycleTrigger = mod.resetCycleTriggerState; + } catch { + // Zone tracker unavailable + } +} +// Pre-load asynchronously so it's ready by the time the first cycle fires +ensureZoneTracker(); let scheduleTask = (fn: () => void) => queueMicrotask(fn); @@ -51,14 +84,7 @@ export function stop(): void { activeCycleStartedAt = 0; implicitCycleScheduled = false; activeSessionBudgetViolations = []; - - if (typeof window !== 'undefined') { - const globalWindow = window as any; - if (globalWindow.__ANGULAR_RENDER_SCAN_APP_REF__) { - // Assuming restoreApplicationRef was called via the global stop or we can't easily reach it here without circular deps. - // But we can dispatch an event or just let the global handle it. - } - } + activeZonePollutionEvents = []; } export function beginCycle(): number { @@ -96,16 +122,46 @@ export function endCycle(cycleId = activeCycleId): AngularRenderCycle | undefine const options = getResolvedOptions(); const finishedAt = performance.now(); const cycle = finishCycle(cycleId, activeCycleStartedAt, finishedAt, options); + + // ─── CD Trigger Attribution ─────────────────────────────────────────────── + let trigger: CdTriggerAttribution | undefined; + if (_resolveTrigger) { + trigger = _resolveTrigger(); + cycle.trigger = trigger; + cycle.isZonePollution = trigger.isZonePollution; + } + _resetCycleTrigger?.(); + + // ─── Zone Pollution Detection ───────────────────────────────────────────── + if (trigger?.isZonePollution && cycle.entries.length > 0) { + const pollutionEvent: ZonePollutionEvent = { + timestamp: Date.now(), + source: trigger.source, + detail: trigger.detail, + callSite: trigger.callSite, + componentCount: cycle.entries.length, + cycleDuration: cycle.duration + }; + const maxPollution = options.maxZonePollutionEvents ?? 50; + activeZonePollutionEvents.push(pollutionEvent); + if (activeZonePollutionEvents.length > maxPollution) { + activeZonePollutionEvents = activeZonePollutionEvents.slice(-maxPollution); + } + options.onZonePollution?.(pollutionEvent); + if (typeof window !== 'undefined') { + window.dispatchEvent(new CustomEvent('angular-render-scan:zone-pollution', { detail: pollutionEvent })); + } + } + lastCycle = cycle; recordRecentCycle(cycle, options.maxRecordedCycles); - // Budget validation checking + // ─── Budget validation ──────────────────────────────────────────────────── const now = performance.now(); const realNowTimestamp = Date.now(); if (options.budgets) { const { warnMs, errorMs, maxRendersPerSecond } = options.budgets; for (const entry of cycle.entries) { - // Check warnMs if (warnMs !== undefined && entry.latestDuration > warnMs && entry.latestDuration <= (errorMs ?? Infinity)) { const violation: BudgetViolation = { componentName: entry.name, @@ -122,7 +178,6 @@ export function endCycle(cycleId = activeCycleId): AngularRenderCycle | undefine window.dispatchEvent(new CustomEvent('angular-render-scan:budget-violation', { detail: violation })); } } - // Check errorMs if (errorMs !== undefined && entry.latestDuration > errorMs) { const violation: BudgetViolation = { componentName: entry.name, @@ -139,7 +194,6 @@ export function endCycle(cycleId = activeCycleId): AngularRenderCycle | undefine window.dispatchEvent(new CustomEvent('angular-render-scan:budget-violation', { detail: violation })); } } - // Check render rate (maxRendersPerSecond) if (maxRendersPerSecond !== undefined) { const rendersInLastSec = getRendersInLastSecond(entry.id, now); if (rendersInLastSec > maxRendersPerSecond) { @@ -169,15 +223,32 @@ export function endCycle(cycleId = activeCycleId): AngularRenderCycle | undefine overlay?.showCycle(cycle); if (options.log && cycle.entries.length > 0) { - console.groupCollapsed(`%c[angular-render-scan] cycle ${cycle.id} - ${cycle.duration.toFixed(2)}ms, ${cycle.renderedCount} components`, 'color: #7c3aed; font-weight: bold;'); + const triggerLabel = trigger + ? ` [${trigger.source}${trigger.isZonePollution ? ' ⚠️ POLLUTION' : ''}]` + : ''; + console.groupCollapsed( + `%c[angular-render-scan] cycle ${cycle.id} - ${cycle.duration.toFixed(2)}ms, ${cycle.renderedCount} components${triggerLabel}`, + 'color: #7c3aed; font-weight: bold;' + ); const tableData = cycle.entries.map((e) => ({ Name: e.name, Count: e.count, 'Time (ms)': Number(e.latestDuration.toFixed(2)), 'Avg (ms)': Number(e.averageDuration.toFixed(2)), - Reason: e.reason ?? 'unknown' + Reason: e.reason ?? 'unknown', + CD: e.cdStrategy ?? 'unknown', + OnPush: e.isOnPushCandidate ? '⚡ candidate' : '' })); console.table(tableData); + if (trigger) { + console.log( + '%c[trigger]', + 'color: #0ea5e9;', + trigger.source, + trigger.detail ?? '', + trigger.isZonePollution ? '⚠️ Zone Pollution' : '' + ); + } console.groupEnd(); } @@ -223,8 +294,32 @@ export function getRecording(): AngularRenderCycle[] { export function clearRecording(): void { recentCycles = []; + activeZonePollutionEvents = []; + activeSessionBudgetViolations = []; + clearStats(); +} + +// ─── New public exports ──────────────────────────────────────────────────────── + +export function getOnPushCandidates(threshold?: number): OnPushCandidate[] { + const opts = getResolvedOptions(); + return statsGetOnPushCandidates(threshold ?? opts.onPushCandidateThreshold); } +export function getReferentialInstability(minUnstable = 1): ReferentialInstabilityReport[] { + return statsGetReferentialInstability(minUnstable); +} + +export function getZonePollutionEvents(): ZonePollutionEvent[] { + return [...activeZonePollutionEvents]; +} + +export function getCdGraph(): CdGraph { + return statsGetCdGraph(); +} + +// ─── AI prompt ──────────────────────────────────────────────────────────────── + export function getAIPrompt(fps?: number): string { const options = getResolvedOptions(); const cycles = recentCycles.length > 0 ? recentCycles : lastCycle ? [lastCycle] : []; @@ -266,6 +361,10 @@ function buildAIPrompt(cycles: AngularRenderCycle[], options = getResolvedOption const slowThreshold = options.budgets?.warnMs ?? 10; const fastThreshold = slowThreshold / 2; + const onPushCandidates = statsGetOnPushCandidates(options.onPushCandidateThreshold); + const pollutionEvents = activeZonePollutionEvents.slice(-5); + const refInstability = statsGetReferentialInstability().slice(0, 5); + return [ '# ⚡️ Angular change-detection Performance Audit (via angular-render-scan)', 'This prompt is self-contained and includes the telemetry evidence of slow/actionable components in the application.', @@ -275,27 +374,56 @@ function buildAIPrompt(cycles: AngularRenderCycle[], options = getResolvedOption ...environment, '', '## ⏱️ Latest Render Cycle Details:', - 'Here are the telemetry details of the last captured change detection cycle:', `* **Cycle id:** #${latest.id}`, `* **Duration:** \`${formatMs(latest.duration)}\``, + latest.trigger + ? `* **Trigger:** \`${latest.trigger.source}\`${latest.trigger.detail ? ` (${latest.trigger.detail})` : ''}${latest.trigger.isZonePollution ? ' ⚠️ Zone Pollution detected' : ''}` + : '', `* **Rendered components count:** ${latest.renderedCount}`, - latest.slowest ? `* **Slowest component:** \`${latest.slowest.name}\` (${formatMs(latest.slowest.latestDuration)}, reason: \`${latest.slowest.reason ?? 'unknown'}\`)` : '', + latest.slowest + ? `* **Slowest component:** \`${latest.slowest.name}\` (${formatMs(latest.slowest.latestDuration)}, reason: \`${latest.slowest.reason ?? 'unknown'}\`)` + : '', `* **Thresholds:** Fast <= \`${formatMs(fastThreshold)}\` | Slow >= \`${formatMs(slowThreshold)}\``, - `* **Filters:** Min duration \`${formatMs(options.minDurationMs)}\`, min render count ${options.minRenderCount}`, '', '## 📈 Recent cycle history:', ...cycles.slice(-8).map(formatPromptCycle), '', + onPushCandidates.length > 0 ? '## ⚡️ OnPush Migration Candidates:' : '', + onPushCandidates.length > 0 + ? 'These components use Default CD and have high wasted render rates. Switching to OnPush should significantly reduce unnecessary checks.' + : '', + ...onPushCandidates.slice(0, 5).map((c, i) => + `${i + 1}. **${c.name}** (${c.selector}) — ${c.wastedPercentage}% wasted, ~${c.estimatedSavingPct}% estimated saving [${c.confidence} confidence] — ${c.reason}` + ), + '', + pollutionEvents.length > 0 ? '## ⚠️ Zone Pollution Events (last 5):' : '', + pollutionEvents.length > 0 + ? 'These CD cycles were triggered by async operations with no user interaction.' + : '', + ...pollutionEvents.map(e => + `- ${e.source}${e.detail ? ` (${e.detail})` : ''} — ${e.componentCount} components ran, ${formatMs(e.cycleDuration)}` + ), + '', + refInstability.length > 0 ? '## 🔄 Referentially Unstable Inputs:' : '', + refInstability.length > 0 + ? 'These inputs receive new object references with the same value, causing unnecessary OnPush re-renders.' + : '', + ...refInstability.map(r => + `- **${r.componentName}** \`@Input() ${r.inputName}\`: ${r.unstableRefCount}/${r.totalRenders} renders (${r.unstableRefPct}%) had same value, new reference` + ), + '', '## 🚨 Slow/error component issues to fix:', ...issueEntries.map((entry, index) => formatIssueEntry(entry, index + 1, latest.duration, slowThreshold)), - issueEntries.length === 0 ? '- No component exceeded the configured slow threshold in the captured cycles.' : '', + issueEntries.length === 0 + ? '- No component exceeded the configured slow threshold in the captured cycles.' + : '', '', '## 📊 Reference metrics (top observed components):', 'Overall render footprint and frequencies for active components:', ...entries.map((entry, index) => formatReferenceEntry(entry, index + 1)), '', '## 🛠️ Optimization Instructions:', - 'Please identify the likely root causes for each slow/error component issue and suggest concrete Angular fixes. Focus on ChangeDetectionStrategy.OnPush, signal/computed usage, template calculations, input reference stabilization, event handlers, and list tracking. Prioritize resolving the highest estimated cost first. Please generate complete, refactored TypeScript templates showing the exact optimized before/after structures. Do not assume access to source code beyond this diagnostic snapshot.' + 'Please identify the likely root causes for each slow/error component issue and suggest concrete Angular fixes. Focus on ChangeDetectionStrategy.OnPush, signal/computed usage, template calculations, input reference stabilization (memo patterns, pure pipes, stable factories), Zone.js pollution elimination (NgZone.runOutsideAngular), and list tracking (trackBy). For referentially unstable inputs, suggest using pure pipes or stable object factories. Prioritize resolving the highest estimated cost first. Generate complete, refactored TypeScript templates showing optimized before/after structures.' ].filter(Boolean).join('\n'); } @@ -318,17 +446,27 @@ function issueEntriesForPrompt(cycles: AngularRenderCycle[], options = getResolv return entries.filter((entry) => entry.latestDuration >= slowThreshold); } -function formatIssueEntry(entry: AngularRenderEntry, index: number, latestCycleDuration: number, slowThresholdMs: number): string { +function formatIssueEntry( + entry: AngularRenderEntry, + index: number, + latestCycleDuration: number, + slowThresholdMs: number +): string { const overBy = Math.max(0, entry.latestDuration - slowThresholdMs); const cycleShare = latestCycleDuration > 0 ? (entry.latestDuration / latestCycleDuration) * 100 : 0; const estimatedTotalCost = entry.averageDuration * entry.count; const changedInputsStr = entry.changedInputs?.length - ? entry.changedInputs.map((input) => `${input.name} ${input.previous} -> ${input.current}`).join('; ') + ? entry.changedInputs + .map((input) => + `${input.name} ${input.previous} -> ${input.current}${input.isReferentiallyUnstable ? ' [UNSTABLE REF]' : ''}` + ) + .join('; ') : 'none captured'; return [ `### 🛑 Component #${index}: \`${entry.name}\``, - ` Selector: selector ${entry.selector ?? '-'}`, + ` Selector: ${entry.selector ?? '-'}`, + ` CD Strategy: ${entry.cdStrategy ?? 'unknown'}${entry.isOnPushCandidate ? ' → OnPush candidate ⚡' : ''}`, ` Performance Issue: latest render ${formatMs(entry.latestDuration)} exceeded slow threshold ${formatMs(slowThresholdMs)} by ${formatMs(overBy)}.`, ` Cost: ${formatMs(entry.latestDuration)} in latest cycle, about ${cycleShare.toFixed(0)}% of latest cycle time, estimated observed total ${formatMs(estimatedTotalCost)} across ${entry.count} renders.`, ` Average render: ${formatMs(entry.averageDuration)}`, @@ -338,18 +476,22 @@ function formatIssueEntry(entry: AngularRenderEntry, index: number, latestCycleD } function formatReferenceEntry(entry: AngularRenderEntry, index: number): string { - return `${index}. ${entry.name}: selector ${entry.selector ?? '-'}, latest ${formatMs(entry.latestDuration)}, avg ${formatMs(entry.averageDuration)}, renders ${entry.count}, reason ${entry.reason ?? 'unknown'}`; + return `${index}. ${entry.name}: selector ${entry.selector ?? '-'}, latest ${formatMs(entry.latestDuration)}, avg ${formatMs(entry.averageDuration)}, renders ${entry.count}, reason ${entry.reason ?? 'unknown'}, CD: ${entry.cdStrategy ?? '?'}${entry.isOnPushCandidate ? ' ⚡' : ''}`; } function formatPromptCycle(cycle: AngularRenderCycle): string { const slowest = cycle.slowest ? `${cycle.slowest.name} ${formatMs(cycle.slowest.latestDuration)}` : 'none'; - return `- #${cycle.id}: ${formatMs(cycle.duration)}, ${cycle.renderedCount} components, slowest ${slowest}`; + const trigger = cycle.trigger + ? ` [${cycle.trigger.source}${cycle.isZonePollution ? ' ⚠️' : ''}]` + : ''; + return `- #${cycle.id}: ${formatMs(cycle.duration)}, ${cycle.renderedCount} components, slowest ${slowest}${trigger}`; } function getPromptEnvironment(fps?: number): string[] { - const viewport = typeof window !== 'undefined' - ? `${window.innerWidth}x${window.innerHeight} @${window.devicePixelRatio || 1}x` - : ''; + const viewport = + typeof window !== 'undefined' + ? `${window.innerWidth}x${window.innerHeight} @${window.devicePixelRatio || 1}x` + : ''; const url = typeof window !== 'undefined' ? window.location.href : ''; const userAgent = typeof navigator !== 'undefined' ? navigator.userAgent : ''; @@ -368,8 +510,10 @@ function formatMs(value: number): string { export function getSessionData(): SessionExportData { const options = getResolvedOptions(); - const wasted = getWastedStats(); - const leaks = getLeakedComponents().map((c) => c.name); + const wasted = statsGetWastedStats(); + const leaks = statsGetLeakedComponents().map((c) => c.name); + const onPushCandidates = statsGetOnPushCandidates(options.onPushCandidateThreshold); + const refInstability = statsGetReferentialInstability(); const mappedCycles = recentCycles.map((cycle) => ({ id: cycle.id, @@ -389,14 +533,19 @@ export function getSessionData(): SessionExportData { selector: e.selector, wastedChecks: e.wastedChecks, wastedPercentage: e.wastedPercentage, - mutationType: e.mutationType + mutationType: e.mutationType, + cdStrategy: e.cdStrategy, + isOnPushCandidate: e.isOnPushCandidate })), - waterfall: cycle.waterfall + waterfall: cycle.waterfall, + trigger: cycle.trigger, + isZonePollution: cycle.isZonePollution })); - const viewport = typeof window !== 'undefined' - ? `${window.innerWidth}x${window.innerHeight} @${window.devicePixelRatio || 1}x` - : ''; + const viewport = + typeof window !== 'undefined' + ? `${window.innerWidth}x${window.innerHeight} @${window.devicePixelRatio || 1}x` + : ''; const url = typeof window !== 'undefined' ? window.location.href : ''; const userAgent = typeof navigator !== 'undefined' ? navigator.userAgent : ''; @@ -409,9 +558,11 @@ export function getSessionData(): SessionExportData { cycles: mappedCycles, wastedStats: wasted, budgetViolations: activeSessionBudgetViolations, - leakedComponents: leaks + leakedComponents: leaks, + onPushCandidates, + zonePollutionEvents: [...activeZonePollutionEvents], + referentialInstabilityReports: refInstability }; } -export { getWastedStats, getLeakedComponents }; - +export { statsGetWastedStats as getWastedStats, statsGetLeakedComponents as getLeakedComponents }; diff --git a/packages/angular-render-scan/src/application/stats.ts b/packages/angular-render-scan/src/application/stats.ts index 40851e5..e680049 100644 --- a/packages/angular-render-scan/src/application/stats.ts +++ b/packages/angular-render-scan/src/application/stats.ts @@ -5,8 +5,15 @@ import type { AngularRenderScanRenderDetails, AngularRenderScanResolvedOptions, AngularRenderMutationType, - WaterfallEntry + WaterfallEntry, + OnPushCandidate, + ReferentialInstabilityReport, + CdTriggerSource } from '../domain/entities'; +import { analyzeOnPushCandidates } from './onpush-analyzer'; +import { getReferentialInstabilityReports, resetReferentialStability, clearReferentialStabilityStats } from './referential-stability'; +import { buildCdGraph, recordParentChildRender, resetCdGraph } from './cd-graph'; +import type { CdGraph } from '../domain/entities'; interface ComponentStats extends AngularRenderScanRegisteredComponent { totalDuration: number; @@ -15,6 +22,8 @@ interface ComponentStats extends AngularRenderScanRegisteredComponent { latestCycleId: number; latestDetails: AngularRenderScanRenderDetails; wastedChecks: number; + inputChangeCount: number; + lastTrigger?: CdTriggerSource; } let cycleId = 0; @@ -35,7 +44,10 @@ export function registerComponent(component: AngularRenderScanRegisteredComponen latestDuration: existing?.latestDuration ?? 0, latestCycleId: existing?.latestCycleId ?? 0, latestDetails: existing?.latestDetails ?? {}, - wastedChecks: existing?.wastedChecks ?? 0 + wastedChecks: existing?.wastedChecks ?? 0, + inputChangeCount: existing?.inputChangeCount ?? 0, + cdStrategy: component.cdStrategy ?? existing?.cdStrategy ?? 'unknown', + parentId: component.parentId ?? existing?.parentId ?? null }); if (component.element && typeof MutationObserver !== 'undefined' && !observers.has(component.id)) { @@ -62,7 +74,7 @@ export function registerComponent(component: AngularRenderScanRegisteredComponen observer.observe(component.element, { attributes: true, characterData: true, childList: true, subtree: true }); state.observer = observer; observers.set(component.id, state); - } catch (e) { + } catch { // Ignore observer failures in test environments } } @@ -82,7 +94,8 @@ export function recordComponentCheck( duration: number, currentCycleId = cycleId, details: AngularRenderScanRenderDetails = {}, - timing?: { startTime: number; totalDuration: number; depth: number } + timing?: { startTime: number; totalDuration: number; depth: number }, + lastTrigger?: CdTriggerSource ): AngularRenderEntry | undefined { const stats = components.get(id); if (!stats || !stats.element.isConnected) { @@ -93,6 +106,7 @@ export function recordComponentCheck( stats.latestDuration = Math.max(0, duration); stats.totalDuration += stats.latestDuration; stats.latestCycleId = currentCycleId; + if (lastTrigger) stats.lastTrigger = lastTrigger; // Track wasted checks const isWasted = details.mutationType === 'none'; @@ -100,6 +114,16 @@ export function recordComponentCheck( stats.wastedChecks += 1; } + // Track input changes + if (details.changedInputs && details.changedInputs.length > 0) { + stats.inputChangeCount += 1; + } + + // Record parent→child edge in CD graph + if (details.parentId && details.parentId !== id) { + recordParentChildRender(details.parentId, id); + } + // Determine final mutation type const obsState = observers.get(id); let finalMutationType: AngularRenderMutationType = 'none'; @@ -119,7 +143,8 @@ export function recordComponentCheck( stats.latestDetails = { reason: details.reason ?? 'unknown', changedInputs: details.changedInputs?.slice(0, 6), - mutationType: finalMutationType + mutationType: finalMutationType, + parentId: details.parentId ?? stats.parentId ?? null }; // Add waterfall entry @@ -180,6 +205,8 @@ export function resetStats(): void { } observers.clear(); components.clear(); + resetReferentialStability(); + resetCdGraph(); } export function clearStats(): void { @@ -191,10 +218,13 @@ export function clearStats(): void { stats.latestCycleId = 0; stats.latestDetails = {}; stats.wastedChecks = 0; + stats.inputChangeCount = 0; } for (const state of observers.values()) { state.lastMutation = 'none'; } + clearReferentialStabilityStats(); + resetCdGraph(); } export function getWastedStats(): { totalChecks: number; wastedChecks: number; wastedPercentage: number } { @@ -214,10 +244,52 @@ export function getLeakedComponents(): AngularRenderEntry[] { .map(toEntry); } +export function getOnPushCandidates(threshold = 40): OnPushCandidate[] { + const data = [...components.values()].map(c => ({ + id: c.id, + name: c.name, + selector: c.selector ?? selectorFor(c.element), + totalChecks: c.totalChecks, + wastedChecks: c.wastedChecks, + totalDuration: c.totalDuration, + cdStrategy: c.cdStrategy ?? 'unknown', + inputChangeCount: c.inputChangeCount + })); + return analyzeOnPushCandidates(data, threshold); +} + +export function getReferentialInstability(minUnstable = 1): ReferentialInstabilityReport[] { + return getReferentialInstabilityReports(minUnstable); +} + +export function getCdGraph(): CdGraph { + const totalDurationAll = [...components.values()].reduce((s, c) => s + c.totalDuration, 0); + const data = [...components.values()].map(c => { + const wastedPct = c.totalChecks > 0 ? Math.round((c.wastedChecks / c.totalChecks) * 100) : 0; + return { + id: c.id, + name: c.name, + selector: c.selector ?? selectorFor(c.element), + parentId: c.parentId ?? null, + depth: 1, + renderCount: c.totalChecks, + totalDuration: c.totalDuration, + wastedChecks: c.wastedChecks, + totalChecks: c.totalChecks, + cdStrategy: c.cdStrategy ?? 'unknown', + isOnPushCandidate: wastedPct >= 40 && (c.cdStrategy === 'Default' || c.cdStrategy === 'unknown'), + lastTrigger: c.lastTrigger + }; + }); + return buildCdGraph(data); +} + function toEntry(stats: ComponentStats): AngularRenderEntry { const count = stats.totalChecks; const wastedChecks = stats.wastedChecks || 0; const wastedPercentage = count === 0 ? 0 : Math.round((wastedChecks / count) * 100); + const isOnPushCandidate = (stats.cdStrategy === 'Default' || stats.cdStrategy === 'unknown') && + wastedPercentage >= 40 && count >= 5; return { id: stats.id, @@ -235,7 +307,10 @@ function toEntry(stats: ComponentStats): AngularRenderEntry { selector: stats.selector ?? (stats.element ? selectorFor(stats.element) : ''), wastedChecks, wastedPercentage, - mutationType: stats.latestDetails.mutationType ?? 'none' + mutationType: stats.latestDetails.mutationType ?? 'none', + cdStrategy: stats.cdStrategy ?? 'unknown', + isOnPushCandidate, + parentId: stats.parentId ?? null }; } @@ -256,6 +331,10 @@ function shouldIncludeEntry(entry: AngularRenderEntry, options?: AngularRenderSc if (options.exclude.length > 0 && matchesAny(entry, options.exclude)) { return false; } + // trackComponents filter: if specified, only include matching components + if (options.trackComponents && options.trackComponents.length > 0 && !matchesAny(entry, options.trackComponents)) { + return false; + } return true; } @@ -272,6 +351,7 @@ function matchesAny(entry: AngularRenderEntry, patterns: Array) } function selectorFor(element: Element): string { + if (!element) return ''; const tag = element.tagName.toLowerCase(); const id = element.id ? `#${element.id}` : ''; const className = element.classList.length > 0 ? `.${Array.from(element.classList).slice(0, 3).join('.')}` : ''; diff --git a/packages/angular-render-scan/src/domain/entities.ts b/packages/angular-render-scan/src/domain/entities.ts index 040719f..e371a7b 100644 --- a/packages/angular-render-scan/src/domain/entities.ts +++ b/packages/angular-render-scan/src/domain/entities.ts @@ -3,6 +3,122 @@ export type AngularRenderReason = 'input' | 'event' | 'tick' | 'dom' | 'unknown' export type AngularRenderMutationType = 'none' | 'text' | 'attribute' | 'structural'; export type AngularRenderScanDarkMode = 'auto' | 'dark' | 'light'; +// ─── CD Trigger Attribution ─────────────────────────────────────────────────── +/** The source that caused a change-detection cycle to fire. */ +export type CdTriggerSource = + | 'zone:click' + | 'zone:input' + | 'zone:keydown' + | 'zone:keyup' + | 'zone:submit' + | 'zone:change' + | 'zone:focus' + | 'zone:blur' + | 'zone:scroll' + | 'zone:setTimeout' + | 'zone:setInterval' + | 'zone:xhr' + | 'zone:fetch' + | 'zone:promise' + | 'zone:microtask' + | 'zone:macrotask' + | 'zone:eventTask' + | 'signal:write' + | 'manual:markForCheck' + | 'manual:detectChanges' + | 'router:navigation' + | 'zone:unknown' + | 'unknown'; + +export interface CdTriggerAttribution { + /** Primary trigger source */ + source: CdTriggerSource; + /** Optional detail: event type, XHR URL, timer ID, etc. */ + detail?: string; + /** Stack frame that triggered the cycle (best-effort) */ + callSite?: string; + /** Whether this cycle was triggered by user interaction */ + isUserInteraction: boolean; + /** Whether this is suspected Zone pollution (async with no user action) */ + isZonePollution: boolean; +} + +// ─── OnPush Candidate ───────────────────────────────────────────────────────── +export interface OnPushCandidate { + name: string; + selector: string; + totalChecks: number; + wastedChecks: number; + wastedPercentage: number; + /** Estimated % of total CD time saved by switching to OnPush */ + estimatedSavingPct: number; + confidence: 'high' | 'medium' | 'low'; + reason: string; +} + +// ─── Referential Input Stability ───────────────────────────────────────────── +export interface ReferentialInstabilityReport { + /** Component name */ + componentName: string; + selector: string; + /** Input property name */ + inputName: string; + /** Number of times a new reference was passed but value was deeply equal */ + unstableRefCount: number; + /** Total renders of this component */ + totalRenders: number; + /** Percentage of renders where this input was referentially unstable */ + unstableRefPct: number; + /** Last serialized value */ + lastValue: string; +} + +// ─── Zone Pollution ─────────────────────────────────────────────────────────── +export interface ZonePollutionEvent { + /** When this pollution event was detected */ + timestamp: number; + /** Which async source caused it */ + source: CdTriggerSource; + detail?: string; + callSite?: string; + /** How many components ran in the polluted cycle */ + componentCount: number; + /** Duration of the polluted cycle in ms */ + cycleDuration: number; +} + +// ─── CD Graph ───────────────────────────────────────────────────────────────── +export interface CdGraphNode { + id: string; + name: string; + selector: string; + /** parent component id (null = root) */ + parentId: string | null; + depth: number; + renderCount: number; + totalDuration: number; + wastedChecks: number; + cdStrategy: 'OnPush' | 'Default' | 'unknown'; + isOnPushCandidate: boolean; + lastTrigger?: CdTriggerSource; +} + +export interface CdGraphEdge { + fromId: string; + toId: string; + /** How many times parent triggered child render */ + triggerCount: number; +} + +export interface CdGraph { + nodes: CdGraphNode[]; + edges: CdGraphEdge[]; + /** When this snapshot was taken */ + capturedAt: number; +} + +// ─── Existing types ─────────────────────────────────────────────────────────── + export interface AngularRenderScanTheme { fast: readonly [number, number, number]; medium: readonly [number, number, number]; @@ -32,6 +148,14 @@ export interface AngularRenderEntry { wastedChecks: number; wastedPercentage: number; mutationType?: AngularRenderMutationType; + /** NEW: Which input props are referentially unstable */ + unstableInputs?: ReferentialInstabilityReport[]; + /** NEW: CD strategy of this component */ + cdStrategy?: 'OnPush' | 'Default' | 'unknown'; + /** NEW: Whether this is an OnPush migration candidate */ + isOnPushCandidate?: boolean; + /** NEW: parent component id */ + parentId?: string | null; } export interface AngularRenderCycle { @@ -43,6 +167,10 @@ export interface AngularRenderCycle { slowest?: AngularRenderEntry; entries: AngularRenderEntry[]; waterfall: WaterfallEntry[]; + /** NEW: What triggered this CD cycle */ + trigger?: CdTriggerAttribution; + /** NEW: Whether this cycle is suspected Zone pollution */ + isZonePollution?: boolean; } export interface WaterfallEntry { @@ -74,6 +202,10 @@ export interface SessionExportData { wastedStats: WastedStats; budgetViolations: BudgetViolation[]; leakedComponents: string[]; + /** NEW */ + onPushCandidates: OnPushCandidate[]; + zonePollutionEvents: ZonePollutionEvent[]; + referentialInstabilityReports: ReferentialInstabilityReport[]; } export interface SessionCycleData { @@ -84,6 +216,8 @@ export interface SessionCycleData { renderedCount: number; entries: SessionEntryData[]; waterfall: WaterfallEntry[]; + trigger?: CdTriggerAttribution; + isZonePollution?: boolean; } export interface SessionEntryData { @@ -99,6 +233,8 @@ export interface SessionEntryData { wastedChecks: number; wastedPercentage: number; mutationType?: AngularRenderMutationType; + cdStrategy?: 'OnPush' | 'Default' | 'unknown'; + isOnPushCandidate?: boolean; } export interface WastedStats { @@ -129,15 +265,30 @@ export interface AngularRenderScanOptions { onRender?: (entry: AngularRenderEntry) => void; onCycleFinish?: (cycle: AngularRenderCycle) => void; onBudgetViolation?: (violation: BudgetViolation) => void; + /** NEW: callback when Zone pollution is detected */ + onZonePollution?: (event: ZonePollutionEvent) => void; + /** NEW: show CD graph panel in toolbar */ + showCdGraph?: boolean; + /** NEW: how many Zone pollution events to retain in session */ + maxZonePollutionEvents?: number; + /** NEW: Only track specific components by name/regex */ + trackComponents?: Array; + /** NEW: OnPush candidate minimum wasted render % threshold (0-100) */ + onPushCandidateThreshold?: number; + /** NEW: Enable referential input stability tracking (JSON deep-equal check) */ + trackReferentialStability?: boolean; + /** NEW: Max depth for deep equality check in referential stability */ + referentialStabilityDepth?: number; } -export interface AngularRenderScanResolvedOptions extends Required> { +export interface AngularRenderScanResolvedOptions extends Required> { theme: AngularRenderScanTheme; budgets: Required; onCycleStart?: () => void; onRender?: (entry: AngularRenderEntry) => void; onCycleFinish?: (cycle: AngularRenderCycle) => void; onBudgetViolation?: (violation: BudgetViolation) => void; + onZonePollution?: (event: ZonePollutionEvent) => void; } export interface AngularRenderScanRegisteredComponent { @@ -145,16 +296,24 @@ export interface AngularRenderScanRegisteredComponent { name: string; element: Element; selector?: string; + /** NEW */ + cdStrategy?: 'OnPush' | 'Default' | 'unknown'; + /** NEW */ + parentId?: string | null; } export interface AngularRenderChangedInput { name: string; previous: string; current: string; + /** NEW: true when reference changed but value is deeply equal */ + isReferentiallyUnstable?: boolean; } export interface AngularRenderScanRenderDetails { reason?: AngularRenderReason; changedInputs?: AngularRenderChangedInput[]; mutationType?: AngularRenderMutationType; + /** NEW */ + parentId?: string | null; } diff --git a/packages/angular-render-scan/src/domain/options.ts b/packages/angular-render-scan/src/domain/options.ts index 8176059..a1f7f32 100644 --- a/packages/angular-render-scan/src/domain/options.ts +++ b/packages/angular-render-scan/src/domain/options.ts @@ -32,7 +32,13 @@ const defaultOptions: AngularRenderScanResolvedOptions = { theme: defaultTheme, budgets: defaultBudgets, editorProtocol: 'vscode', - darkMode: 'auto' + darkMode: 'auto', + showCdGraph: true, + maxZonePollutionEvents: 50, + trackComponents: [], + onPushCandidateThreshold: 40, + trackReferentialStability: true, + referentialStabilityDepth: 4, }; let options: AngularRenderScanResolvedOptions = { ...defaultOptions }; @@ -48,16 +54,20 @@ export function resolveOptions(next?: AngularRenderScanOptions): AngularRenderSc merged.minRenderCount = normalizeNonNegative(merged.minRenderCount, defaultOptions.minRenderCount); merged.maxLabelCount = normalizePositiveInteger(merged.maxLabelCount, defaultOptions.maxLabelCount); merged.maxRecordedCycles = normalizePositiveInteger(merged.maxRecordedCycles, defaultOptions.maxRecordedCycles); + merged.maxZonePollutionEvents = normalizePositiveInteger(merged.maxZonePollutionEvents, defaultOptions.maxZonePollutionEvents); merged.include = Array.isArray(merged.include) ? merged.include : defaultOptions.include; merged.exclude = Array.isArray(merged.exclude) ? merged.exclude : defaultOptions.exclude; + merged.trackComponents = Array.isArray(merged.trackComponents) ? merged.trackComponents : defaultOptions.trackComponents; merged.promptContext = typeof merged.promptContext === 'string' ? merged.promptContext : defaultOptions.promptContext; merged.showCopyPrompt = typeof next?.showCopyPrompt === 'boolean' ? next.showCopyPrompt : options.showCopyPrompt; + merged.showCdGraph = typeof next?.showCdGraph === 'boolean' ? next.showCdGraph : options.showCdGraph; + merged.trackReferentialStability = typeof next?.trackReferentialStability === 'boolean' ? next.trackReferentialStability : options.trackReferentialStability; merged.theme = { ...options.theme, ...(next?.theme || {}) }; - merged.budgets = defaultBudgets; - merged.editorProtocol = typeof next?.editorProtocol === 'string' ? next.editorProtocol : options.editorProtocol; - merged.darkMode = ['auto', 'dark', 'light'].includes(next?.darkMode as any) ? next!.darkMode! : options.darkMode; + merged.darkMode = ['auto', 'dark', 'light'].includes(next?.darkMode as string) ? next!.darkMode! : options.darkMode; + merged.onPushCandidateThreshold = normalizeNonNegative(merged.onPushCandidateThreshold, defaultOptions.onPushCandidateThreshold); + merged.referentialStabilityDepth = normalizePositiveInteger(merged.referentialStabilityDepth, defaultOptions.referentialStabilityDepth); return merged; } diff --git a/packages/angular-render-scan/src/infrastructure/angular/angular.ts b/packages/angular-render-scan/src/infrastructure/angular/angular.ts index e89165b..3c7bf09 100644 --- a/packages/angular-render-scan/src/infrastructure/angular/angular.ts +++ b/packages/angular-render-scan/src/infrastructure/angular/angular.ts @@ -1,4 +1,20 @@ -import { APP_INITIALIZER, ApplicationRef, Directive, ElementRef, EnvironmentProviders, InjectionToken, input, effect, OnDestroy, Provider, inject, makeEnvironmentProviders, isDevMode, NgZone } from '@angular/core'; +import { + APP_INITIALIZER, + ApplicationRef, + Directive, + ElementRef, + EnvironmentProviders, + InjectionToken, + input, + effect, + OnDestroy, + Provider, + inject, + makeEnvironmentProviders, + isDevMode, + NgZone, + ChangeDetectorRef +} from '@angular/core'; import { beginCycle, copyAIPrompt, @@ -11,11 +27,15 @@ import { setTaskScheduler, getSessionData, getWastedStats, - getLeakedComponents + getLeakedComponents, + getOnPushCandidates, + getReferentialInstability, + getCdGraph } from '../../application/runtime'; import { recordComponentCheck, registerComponent, unregisterComponent } from '../../application/stats'; import type { AngularRenderScanOptions } from '../../domain/entities'; import { setupAutoInstrumentation } from './auto-instrumentation'; +import { installZoneHook, resetZoneTracker } from './zone-tracker'; export const ANGULAR_RENDER_SCAN_OPTIONS = new InjectionToken('ANGULAR_RENDER_SCAN_OPTIONS'); @@ -69,11 +89,13 @@ export class AngularRenderScanMarkDirective implements OnDestroy { curr = curr.parent; } + const parentId = this.parent ? (this.parent as any)['id'] as string : null; + const entry = recordComponentCheck( this.id, selfDuration, cycleId, - {}, + { parentId }, { startTime: this.checkStartedAt, totalDuration, @@ -94,7 +116,8 @@ export class AngularRenderScanMarkDirective implements OnDestroy { id: this.id, name: this.name, element: this.element, - selector: this.element.tagName.toLowerCase() + selector: this.element.tagName.toLowerCase(), + parentId: this.parent ? (this.parent as any)['id'] as string : null }); } @@ -108,10 +131,6 @@ export class AngularRenderScanMarkDirective implements OnDestroy { } } -function normalizeText(text: string): string { - return text.replace(/\s+/g, ' ').trim(); -} - export function provideAngularRenderScan(options: AngularRenderScanOptions = {}): EnvironmentProviders { return makeEnvironmentProviders([ { provide: ANGULAR_RENDER_SCAN_OPTIONS, useValue: options }, @@ -128,18 +147,19 @@ function angularRenderScanInitializerProvider(): Provider { if (!isDevMode() && !options.dangerouslyForceRunInProduction) { return; } - + setTaskScheduler((fn) => { ngZone.runOutsideAngular(() => { - // Promise.resolve().then() is generally safer in Zone.js to escape microtask tracking - // if queued from outside the zone, whereas queueMicrotask might still be patched tightly. Promise.resolve().then(fn); }); }); ngZone.runOutsideAngular(() => { scan(options); + // Install Zone.js hook for CD trigger attribution + installZoneHook(); }); + patchApplicationRef(appRef); registerGlobalApplicationRef(appRef); setupAutoInstrumentation(); @@ -174,25 +194,17 @@ export function restoreApplicationRef(appRef: ApplicationRef): void { candidate.__angularRenderScanPatched = false; originalTick = null; } + resetZoneTracker(); } function registerGlobalApplicationRef(appRef: ApplicationRef): void { const globalWindow = window as Window & { __ANGULAR_RENDER_SCAN_APP_REF__?: ApplicationRef; - AngularRenderScan?: { - scan: typeof scan; - setOptions: typeof setOptions; - getAIPrompt: typeof getAIPrompt; - copyAIPrompt: typeof copyAIPrompt; - getSessionData: () => any; - getWastedStats: () => any; - getLeakedComponents: () => any; - stop: () => void; - }; + AngularRenderScan?: object; }; globalWindow.__ANGULAR_RENDER_SCAN_APP_REF__ = appRef; globalWindow.AngularRenderScan = { - ...globalWindow.AngularRenderScan, + ...(globalWindow.AngularRenderScan as object | undefined), scan, setOptions, getAIPrompt, @@ -200,6 +212,9 @@ function registerGlobalApplicationRef(appRef: ApplicationRef): void { getSessionData, getWastedStats, getLeakedComponents, + getOnPushCandidates, + getReferentialInstability, + getCdGraph, stop: () => { import('../../application/runtime').then(m => m.stop()); restoreApplicationRef(appRef); diff --git a/packages/angular-render-scan/src/infrastructure/angular/auto-instrumentation.ts b/packages/angular-render-scan/src/infrastructure/angular/auto-instrumentation.ts index c4ecc05..f875098 100644 --- a/packages/angular-render-scan/src/infrastructure/angular/auto-instrumentation.ts +++ b/packages/angular-render-scan/src/infrastructure/angular/auto-instrumentation.ts @@ -1,12 +1,35 @@ import { beginCycle, currentCycleId, endCycle, ensureCycleForComponentCheck } from '../../application/runtime'; import { recordComponentCheck, registerComponent, unregisterComponent } from '../../application/stats'; +import { checkReferentialStability } from '../../application/referential-stability'; +import { getResolvedOptions } from '../../domain/options'; import type { AngularRenderChangedInput, AngularRenderScanRenderDetails } from '../../domain/entities'; const ProfilerEventTemplateUpdateStart = 2; const ProfilerEventTemplateUpdateEnd = 3; let nextAutoComponentId = 0; -const instanceMap = new WeakMap }>(); + +interface AutoCompData { + id: string; + name: string; + element: Element; + signature: string; + inputSnapshot: Map; + inputRefSnapshot: Map; + cdStrategy: 'OnPush' | 'Default' | 'unknown'; + parentId: string | null; +} + +const instanceMap = new WeakMap(); + +// Stack frame during template update traversal +interface StackFrame { + id: string; + parentId: string | null; + startTime: number; + childrenDuration: number; + details: AngularRenderScanRenderDetails; +} function normalizeText(text: string): string { return text.replace(/\s+/g, ' ').trim(); @@ -24,15 +47,15 @@ function getRenderedSignature(element: Element): string { ].join('|'); } -function getInputNames(instance: any): Array<{ publicName: string; propertyName: string }> { - const inputs = instance?.constructor?.ɵcmp?.inputs; +function getInputNames(instance: object): Array<{ publicName: string; propertyName: string }> { + const inputs = (instance as any)?.constructor?.ɵcmp?.inputs; if (!inputs || typeof inputs !== 'object') { return []; } return Object.entries(inputs).map(([publicName, metadata]) => { if (Array.isArray(metadata) && typeof metadata[0] === 'string') { - return { publicName, propertyName: metadata[0] }; + return { publicName, propertyName: metadata[0] as string }; } if (typeof metadata === 'string') { return { publicName, propertyName: metadata }; @@ -46,7 +69,7 @@ function summarizeValue(value: unknown): string { if (value === undefined) return 'undefined'; if (typeof value === 'string') return JSON.stringify(value.length > 80 ? `${value.slice(0, 77)}...` : value); if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') return String(value); - if (Array.isArray(value)) return `Array(${value.length})`; + if (Array.isArray(value)) return `Array(${(value as unknown[]).length})`; if (typeof value === 'function') return 'Function'; if (typeof value === 'object') { const ctor = (value as { constructor?: { name?: string } }).constructor?.name; @@ -55,15 +78,54 @@ function summarizeValue(value: unknown): string { return typeof value; } -function detectInputChanges(instance: any, snapshot: Map): AngularRenderChangedInput[] { +/** + * Read the ChangeDetectionStrategy from Angular component metadata. + * 0 = OnPush, 2 = Default (Angular internal enum values) + */ +function readCdStrategy(instance: object): 'OnPush' | 'Default' | 'unknown' { + const cmp = (instance as any)?.constructor?.ɵcmp; + if (!cmp) return 'unknown'; + // changeDetection: 0 = OnPush, 2 = Default + const cd = cmp.changeDetection; + if (cd === 0) return 'OnPush'; + if (cd === 2) return 'Default'; + return 'unknown'; +} + +function detectInputChanges( + instance: object, + compData: AutoCompData, + enableReferentialStability: boolean, + referentialStabilityDepth: number +): AngularRenderChangedInput[] { const changes: AngularRenderChangedInput[] = []; for (const { publicName, propertyName } of getInputNames(instance)) { - const current = summarizeValue(instance[propertyName]); - const previous = snapshot.get(publicName); + const rawValue = (instance as any)[propertyName]; + const current = summarizeValue(rawValue); + const previous = compData.inputSnapshot.get(publicName); + if (previous !== undefined && previous !== current) { - changes.push({ name: publicName, previous, current }); + let isReferentiallyUnstable = false; + + if (enableReferentialStability && rawValue !== null && typeof rawValue === 'object') { + // Check referential stability: new reference, but same deep value? + isReferentiallyUnstable = checkReferentialStability( + compData.id, + compData.name, + compData.element.tagName.toLowerCase(), + publicName, + rawValue, + referentialStabilityDepth + ); + } + + changes.push({ name: publicName, previous, current, isReferentiallyUnstable }); + } + + compData.inputSnapshot.set(publicName, current); + if (rawValue !== null && typeof rawValue === 'object') { + compData.inputRefSnapshot.set(publicName, rawValue); } - snapshot.set(publicName, current); } return changes.slice(0, 6); @@ -75,53 +137,105 @@ export function setupAutoInstrumentation(): void { const trySetup = () => { const globalNg = (window as any).ng; if (!globalNg || !globalNg.ɵsetProfiler || !globalNg.getHostElement) { - if (attempts++ < 50) { // Retry for up to 5 seconds + if (attempts++ < 50) { setTimeout(trySetup, 100); } return; } - const componentCheckStack: { id: string, startTime: number, childrenDuration: number, details: AngularRenderScanRenderDetails }[] = []; + const componentCheckStack: StackFrame[] = []; - globalNg.ɵsetProfiler((event: number, instance: any) => { + globalNg.ɵsetProfiler((event: number, instance: object) => { if (!instance) return; + const options = getResolvedOptions(); + if (event === ProfilerEventTemplateUpdateStart) { ensureCycleForComponentCheck(); - + let compData = instanceMap.get(instance); if (!compData) { try { const element = globalNg.getHostElement(instance); if (element && element instanceof Element && !element.hasAttribute('angularRenderScanMark')) { - const name = instance.constructor?.name || 'AnonymousComponent'; + const name = (instance as any).constructor?.name || 'AnonymousComponent'; const id = `ng-scan-auto-${++nextAutoComponentId}`; - compData = { id, name, element, signature: '', inputSnapshot: new Map() }; + const cdStrategy = readCdStrategy(instance); + + // Determine parent from stack + const parentId = componentCheckStack.length > 0 + ? componentCheckStack[componentCheckStack.length - 1].id + : null; + + compData = { + id, + name, + element, + signature: '', + inputSnapshot: new Map(), + inputRefSnapshot: new Map(), + cdStrategy, + parentId + }; instanceMap.set(instance, compData); - registerComponent({ ...compData, selector: element.tagName.toLowerCase() }); + registerComponent({ + ...compData, + selector: element.tagName.toLowerCase(), + cdStrategy, + parentId + }); } else { - // Null object to prevent retrying or processing tracked elements - instanceMap.set(instance, { id: '', name: '', element: null as any, signature: '', inputSnapshot: new Map() }); + instanceMap.set(instance, { + id: '', + name: '', + element: null as any, + signature: '', + inputSnapshot: new Map(), + inputRefSnapshot: new Map(), + cdStrategy: 'unknown', + parentId: null + }); } - } catch (e) { - instanceMap.set(instance, { id: '', name: '', element: null as any, signature: '', inputSnapshot: new Map() }); + } catch { + instanceMap.set(instance, { + id: '', + name: '', + element: null as any, + signature: '', + inputSnapshot: new Map(), + inputRefSnapshot: new Map(), + cdStrategy: 'unknown', + parentId: null + }); } } if (compData && compData.element) { - const changedInputs = detectInputChanges(instance, compData.inputSnapshot); + const changedInputs = detectInputChanges( + instance, + compData, + options.trackReferentialStability, + options.referentialStabilityDepth + ); + + const parentId = componentCheckStack.length > 0 + ? componentCheckStack[componentCheckStack.length - 1].id + : compData.parentId; + componentCheckStack.push({ id: compData.id, + parentId, startTime: performance.now(), childrenDuration: 0, details: { reason: changedInputs.length > 0 ? 'input' : 'unknown', - changedInputs + changedInputs, + parentId } }); } } else if (event === ProfilerEventTemplateUpdateEnd) { - let compData = instanceMap.get(instance); + const compData = instanceMap.get(instance); if (compData && compData.element && componentCheckStack.length > 0) { const depth = componentCheckStack.length; const frame = componentCheckStack.pop()!; @@ -131,12 +245,12 @@ export function setupAutoInstrumentation(): void { const totalDuration = performance.now() - frame.startTime; const selfDuration = Math.max(0, totalDuration - frame.childrenDuration); - // Add our total duration to the parent's childrenDuration + // Propagate duration to parent if (componentCheckStack.length > 0) { componentCheckStack[componentCheckStack.length - 1].childrenDuration += totalDuration; } - // Check if actual DOM mutation occurred to prevent full-screen flashing + // Check if actual DOM mutation occurred const nextSignature = getRenderedSignature(compData.element); const signatureChanged = compData.signature !== nextSignature; const isWasted = !signatureChanged && frame.details.reason !== 'input'; @@ -152,7 +266,8 @@ export function setupAutoInstrumentation(): void { { reason: frame.details.reason === 'input' ? 'input' : signatureChanged ? 'dom' : 'unknown', changedInputs: frame.details.changedInputs, - mutationType: isWasted ? 'none' : undefined + mutationType: isWasted ? 'none' : undefined, + parentId: frame.parentId }, { startTime: frame.startTime, @@ -165,7 +280,7 @@ export function setupAutoInstrumentation(): void { } } } else { - // Stack mismatch, try to recover by pushing it back or clearing + // Stack mismatch — recover componentCheckStack.length = 0; } } diff --git a/packages/angular-render-scan/src/infrastructure/angular/zone-tracker.ts b/packages/angular-render-scan/src/infrastructure/angular/zone-tracker.ts new file mode 100644 index 0000000..8bba784 --- /dev/null +++ b/packages/angular-render-scan/src/infrastructure/angular/zone-tracker.ts @@ -0,0 +1,209 @@ +/** + * Zone.js Task Tracker + * + * Hooks into Zone.js task scheduling to attribute what triggered each + * Angular change-detection cycle. Works by wrapping zone task lifecycle + * callbacks (onScheduleTask / onInvokeTask) to record the "last active task" + * right before ApplicationRef.tick() fires. + * + * IMPORTANT: This file contains NO Angular imports. It only touches Zone.js + * globals and the browser runtime, so it can be used from the infrastructure + * layer without creating circular dependencies. + */ + +import type { CdTriggerAttribution, CdTriggerSource } from '../../domain/entities'; + +interface ZoneTaskSnapshot { + source: CdTriggerSource; + detail?: string; + callSite?: string; + isUserInteraction: boolean; +} + +// The most recently executed Zone task before tick() fired +let lastTaskSnapshot: ZoneTaskSnapshot | null = null; +// True while a user event is actively being processed +let activeUserEventSource: CdTriggerSource | null = null; +// Whether a signal write occurred in the current microtask queue +let signalWriteDetected = false; +// Whether a router navigation is in progress +let routerNavigationInProgress = false; +// Whether markForCheck / detectChanges was called explicitly +let manualCdCallSite: string | null = null; + +// ─── Public write-side API (called from auto-instrumentation / angular.ts) ─── + +export function notifySignalWrite(): void { + signalWriteDetected = true; +} + +export function notifyRouterNavigation(): void { + routerNavigationInProgress = true; +} + +export function notifyManualCd(type: 'markForCheck' | 'detectChanges', callSite?: string): void { + manualCdCallSite = callSite ?? type; +} + +// ─── Attribution resolution (called at the start of each cycle) ────────────── + +export function resolveTriggerAttribution(): CdTriggerAttribution { + let source: CdTriggerSource = 'unknown'; + let detail: string | undefined; + let callSite: string | undefined; + let isUserInteraction = false; + + if (manualCdCallSite !== null) { + source = manualCdCallSite.startsWith('detectChanges') ? 'manual:detectChanges' : 'manual:markForCheck'; + callSite = manualCdCallSite; + } else if (signalWriteDetected) { + source = 'signal:write'; + isUserInteraction = activeUserEventSource !== null; + } else if (routerNavigationInProgress) { + source = 'router:navigation'; + } else if (activeUserEventSource !== null) { + source = activeUserEventSource; + isUserInteraction = true; + } else if (lastTaskSnapshot !== null) { + source = lastTaskSnapshot.source; + detail = lastTaskSnapshot.detail; + callSite = lastTaskSnapshot.callSite; + isUserInteraction = lastTaskSnapshot.isUserInteraction; + } else { + source = 'unknown'; + } + + const isZonePollution = + !isUserInteraction && + source !== 'signal:write' && + source !== 'router:navigation' && + source !== 'manual:markForCheck' && + source !== 'manual:detectChanges' && + source !== 'unknown'; + + return { source, detail, callSite, isUserInteraction, isZonePollution }; +} + +/** Reset per-cycle state after attribution is collected */ +export function resetCycleTriggerState(): void { + lastTaskSnapshot = null; + signalWriteDetected = false; + routerNavigationInProgress = false; + manualCdCallSite = null; + // Note: activeUserEventSource is reset by the DOM event listener +} + +// ─── Zone.js hook installation ──────────────────────────────────────────────── + +const USER_EVENTS = new Set(['click', 'mousedown', 'mouseup', 'keydown', 'keyup', 'keypress', 'input', 'change', 'submit', 'focus', 'blur', 'scroll', 'touchstart', 'touchend', 'pointerdown', 'pointerup']); + +function mapEventToSource(eventType: string): CdTriggerSource { + switch (eventType) { + case 'click': return 'zone:click'; + case 'input': return 'zone:input'; + case 'keydown': return 'zone:keydown'; + case 'keyup': return 'zone:keyup'; + case 'submit': return 'zone:submit'; + case 'change': return 'zone:change'; + case 'focus': return 'zone:focus'; + case 'blur': return 'zone:blur'; + case 'scroll': return 'zone:scroll'; + default: return 'zone:eventTask'; + } +} + +function extractCallSite(error?: Error): string | undefined { + if (!error?.stack) return undefined; + // Grab first non-internal frame + const frames = error.stack.split('\n').slice(2); + const frame = frames.find(f => + !f.includes('zone-tracker') && + !f.includes('zone.js') && + !f.includes('zone-evergreen') && + f.trim().length > 0 + ); + return frame?.trim().replace(/^at\s+/, '').slice(0, 120); +} + +// Use `any` for Zone.js globals since we don't want to add zone.js as a type dep +declare const Zone: any; + +let zoneHookInstalled = false; + +export function installZoneHook(): void { + if (zoneHookInstalled) return; + if (typeof Zone === 'undefined') return; + + // Track user DOM events to know the "active event context" + const trackEvent = (e: Event) => { + activeUserEventSource = mapEventToSource(e.type); + // Reset after the event has fully propagated + microtasks settled + Promise.resolve().then(() => { + setTimeout(() => { activeUserEventSource = null; }, 0); + }); + }; + + for (const evType of USER_EVENTS) { + document.addEventListener(evType, trackEvent, { capture: true, passive: true }); + } + + // Hook into Zone.js task scheduling via Zone.current.fork + const spec = { + name: 'angular-render-scan', + onScheduleTask(delegate: any, current: any, target: any, task: any) { + return delegate.scheduleTask(target, task); + }, + onInvokeTask(delegate: any, current: any, target: any, task: any, applyThis: any, applyArgs: any) { + // Record what task is being invoked just before it runs + const taskType: string = task.type; + const taskSource: string | undefined = task.source; + + if (taskType === 'eventTask') { + const eventSource = taskSource ?? ''; + // e.g. "HTMLButtonElement.click" → extract event name + const match = eventSource.match(/\.(\w+)$/); + const eventType = match ? match[1] : eventSource; + lastTaskSnapshot = { + source: mapEventToSource(eventType), + detail: eventSource, + isUserInteraction: USER_EVENTS.has(eventType) + }; + } else if (taskType === 'macroTask') { + if (taskSource?.includes('setTimeout')) { + lastTaskSnapshot = { source: 'zone:setTimeout', isUserInteraction: false }; + } else if (taskSource?.includes('setInterval')) { + lastTaskSnapshot = { source: 'zone:setInterval', isUserInteraction: false }; + } else if (taskSource?.includes('XMLHttpRequest')) { + const xhrUrl = task.data?.url as string | undefined; + lastTaskSnapshot = { source: 'zone:xhr', detail: xhrUrl ? xhrUrl.slice(0, 80) : undefined, isUserInteraction: false }; + } else if (taskSource?.includes('fetch')) { + const fetchUrl = task.data?.args?.[0]; + const urlStr = typeof fetchUrl === 'string' ? fetchUrl.slice(0, 80) : undefined; + lastTaskSnapshot = { source: 'zone:fetch', detail: urlStr, isUserInteraction: false }; + } else { + lastTaskSnapshot = { source: 'zone:macrotask', detail: taskSource?.slice(0, 80), isUserInteraction: false }; + } + } else if (taskType === 'microTask') { + lastTaskSnapshot = { source: 'zone:microtask', detail: taskSource?.slice(0, 80), isUserInteraction: false }; + } + + return delegate.invokeTask(target, task, applyThis, applyArgs); + } + }; + + try { + Zone.current.fork(spec); + zoneHookInstalled = true; + } catch { + // Zone.js not available or already fully patched — silently skip + } +} + +export function resetZoneTracker(): void { + zoneHookInstalled = false; + lastTaskSnapshot = null; + activeUserEventSource = null; + signalWriteDetected = false; + routerNavigationInProgress = false; + manualCdCallSite = null; +} diff --git a/packages/angular-render-scan/src/infrastructure/ui/overlay.ts b/packages/angular-render-scan/src/infrastructure/ui/overlay.ts index 29df38f..4734553 100644 --- a/packages/angular-render-scan/src/infrastructure/ui/overlay.ts +++ b/packages/angular-render-scan/src/infrastructure/ui/overlay.ts @@ -1,7 +1,26 @@ import { FpsMeter } from './fps'; import { CpuMeter } from './cpu'; -import { clearRecording, copyAIPrompt, getRecording, getSessionData, getWastedStats, getLeakedComponents } from '../../application/runtime'; -import type { AngularRenderCycle, AngularRenderEntry, AngularRenderScanResolvedOptions, BudgetViolation } from '../../domain/entities'; +import { + clearRecording, + copyAIPrompt, + getRecording, + getSessionData, + getWastedStats, + getLeakedComponents, + getOnPushCandidates, + getReferentialInstability, + getZonePollutionEvents, + getCdGraph +} from '../../application/runtime'; +import type { + AngularRenderCycle, + AngularRenderEntry, + AngularRenderScanResolvedOptions, + BudgetViolation, + OnPushCandidate, + ZonePollutionEvent, + CdTriggerAttribution +} from '../../domain/entities'; interface ActiveHighlight { entry: AngularRenderEntry; @@ -366,20 +385,22 @@ const TOOLBAR_CSS = ` right: 16px; bottom: 72px; z-index: 2147483647; - width: min(300px, calc(100vw - 32px)); + width: min(280px, calc(100vw - 32px)); display: grid; - gap: 8px; - padding: 12px; + gap: 6px; + padding: 10px; border: 1px solid var(--ars-border); - border-top: 4px solid #3b82f6; - border-radius: 12px; + border-top: 3px solid #3b82f6; + border-radius: 10px; background: var(--ars-panel-bg); box-shadow: var(--ars-shadow); color: var(--ars-color); - font: 500 11px/1.3 Inter, system-ui, -apple-system, sans-serif; + font: 500 10px/1.3 Inter, system-ui, -apple-system, sans-serif; pointer-events: auto; backdrop-filter: blur(16px); transition: border-top-color 0.2s ease; + max-height: calc(100vh - 100px); + overflow-y: auto; } .inspect-panel.slow { border-top-color: #ef4444; } .inspect-panel.medium { border-top-color: #f59e0b; } @@ -559,6 +580,14 @@ export class AngularRenderScanOverlay { private dragStartX = 0; private dragStartY = 0; + // New feature state + private showOnPushPanel = false; + private showZonePollutionPanel = false; + private showGraphPanel = false; + private graphCollapsed = false; + private lastTrigger?: CdTriggerAttribution; + private zonePollutionListener?: (e: Event) => void; + private get slowThresholdMs(): number { return this.options.budgets?.warnMs ?? 10; } @@ -607,6 +636,13 @@ export class AngularRenderScanOverlay { }; window.addEventListener('angular-render-scan:budget-violation', this.budgetViolationListener); + // Setup Zone pollution event listener + this.zonePollutionListener = (_e: Event) => { + // Just trigger a toolbar re-render to update the pollution badge count + this.renderToolbar(); + }; + window.addEventListener('angular-render-scan:zone-pollution', this.zonePollutionListener); + // Setup keyboard shortcuts this.keyListener = (e: KeyboardEvent) => { if (e.altKey && e.shiftKey) { @@ -646,11 +682,13 @@ export class AngularRenderScanOverlay { this.renderToolbar(); } } else if (e.key === 'Escape') { - if (this.selectedEntry || this.showCpuDetails || this.showWaterfallPanel || this.showAlertsPanel) { + if (this.selectedEntry || this.showCpuDetails || this.showWaterfallPanel || this.showAlertsPanel || this.showOnPushPanel || this.showZonePollutionPanel) { this.selectedEntry = undefined; this.showCpuDetails = false; this.showWaterfallPanel = false; this.showAlertsPanel = false; + this.showOnPushPanel = false; + this.showZonePollutionPanel = false; this.renderToolbar(); } } @@ -767,7 +805,10 @@ export class AngularRenderScanOverlay { showCycle(cycle: AngularRenderCycle): void { this.latestCycle = cycle; - + if (cycle.trigger) { + this.lastTrigger = cycle.trigger; + } + // Track sparkline durations this.last30CycleDurations.push({ duration: cycle.duration, @@ -804,6 +845,9 @@ export class AngularRenderScanOverlay { if (this.budgetViolationListener) { window.removeEventListener('angular-render-scan:budget-violation', this.budgetViolationListener); } + if (this.zonePollutionListener) { + window.removeEventListener('angular-render-scan:zone-pollution', this.zonePollutionListener); + } window.clearTimeout(this.copyStatusTimer); this.host.remove(); this.canvas.remove(); @@ -948,60 +992,87 @@ export class AngularRenderScanOverlay { } private drawOutline(rect: DOMRect, alpha: number, entry: AngularRenderEntry): void { - if (!this.context) { - return; - } - + if (!this.context) return; + const ctx = this.context; const strokeColor = this.getStrokeColorForMutation(entry); - const glowColor = strokeColor; - - this.context.shadowColor = rgba(glowColor, Math.min(0.45, alpha)); - this.context.shadowBlur = 16; - this.context.strokeStyle = rgba(strokeColor, alpha); - this.context.lineWidth = 2; - this.context.strokeRect(rect.left, rect.top, rect.width, rect.height); - this.context.shadowBlur = 0; + const r = 3; // corner radius + + ctx.save(); + // Subtle fill tint + ctx.fillStyle = rgba(strokeColor, Math.min(0.06, alpha * 0.08)); + ctx.beginPath(); + ctx.roundRect(rect.left, rect.top, rect.width, rect.height, r); + ctx.fill(); + // Clean sharp stroke — no glow, just a crisp colored border + ctx.strokeStyle = rgba(strokeColor, Math.min(0.85, alpha)); + ctx.lineWidth = 1.5; + ctx.shadowBlur = 0; + ctx.beginPath(); + ctx.roundRect(rect.left, rect.top, rect.width, rect.height, r); + ctx.stroke(); + ctx.restore(); } private drawHoverTarget(rect: DOMRect, entry: AngularRenderEntry): void { - if (!this.context) { - return; - } - + if (!this.context) return; + const ctx = this.context; const color = this.getStrokeColorForMutation(entry); - this.context.save(); - this.context.strokeStyle = rgba(color, 0.95); - this.context.lineWidth = 3; - this.context.setLineDash([2, 4]); - this.context.strokeRect(rect.left, rect.top, rect.width, rect.height); - this.context.restore(); - } - - private drawLabel(entry: AngularRenderEntry, rect: DOMRect, alpha: number, duration: number): void { - if (!this.context) { - return; - } + ctx.save(); + // Solid highlight fill + ctx.fillStyle = rgba(color, 0.07); + ctx.beginPath(); + ctx.roundRect(rect.left, rect.top, rect.width, rect.height, 3); + ctx.fill(); + // Dashed border + ctx.strokeStyle = rgba(color, 0.9); + ctx.lineWidth = 2; + ctx.setLineDash([4, 3]); + ctx.beginPath(); + ctx.roundRect(rect.left, rect.top, rect.width, rect.height, 3); + ctx.stroke(); + ctx.restore(); + } + + private drawLabel(entry: AngularRenderEntry, rect: DOMRect, alpha: number, _duration: number): void { + if (!this.context) return; + const ctx = this.context; const maxDuration = Math.max(entry.latestDuration, entry.averageDuration); - let bgColor = this.getColorForDuration(maxDuration, 'bg'); - if (entry.element && !entry.element.isConnected) { - bgColor = this.options.theme.labelBackgroundSlow!; // Red for leaked component labels - } + const strokeColor = this.getStrokeColorForMutation(entry); + const isLeak = entry.element && !entry.element.isConnected; + + ctx.save(); + ctx.font = '600 10px ui-sans-serif, system-ui, sans-serif'; - this.context.fillStyle = rgba(bgColor, Math.min(0.9, alpha + 0.1)); - this.context.font = '600 11px ui-sans-serif, system-ui, sans-serif'; - const maxLabelWidth = Math.max(56, Math.min(rect.width, window.innerWidth - rect.left - 8)); + // Build pill text: "ComponentName · 12ms · ×4" + const durationText = `${entry.latestDuration.toFixed(1)}ms`; const label = truncateText( - this.context, - `${entry.name} #${entry.count} ${entry.latestDuration.toFixed(1)}ms`, - maxLabelWidth - 10 + ctx, + `${entry.name} · ${durationText} · ×${entry.count}`, + Math.max(56, Math.min(rect.width - 4, 200)) ); - const labelWidth = Math.min(this.context.measureText(label).width + 14, maxLabelWidth); - const labelX = Math.max(4, Math.min(rect.left + 4, window.innerWidth - labelWidth - 4)); - const labelY = Math.max(6, rect.top + 4); - this.context.fillRect(labelX, labelY, labelWidth, 18); - this.context.fillStyle = '#ffffff'; - this.context.fillText(label, labelX + 7, labelY + 13, labelWidth - 10); + + const textW = ctx.measureText(label).width; + const pillW = textW + 14; + const pillH = 17; + const pillR = 4; + const pillX = Math.max(4, Math.min(rect.left + 4, window.innerWidth - pillW - 4)); + const pillY = Math.max(4, rect.top + 4); + + // Pill background — use stroke color with opacity + const pillAlpha = Math.min(0.92, alpha + 0.15); + ctx.fillStyle = isLeak + ? `rgba(239,68,68,${pillAlpha})` + : rgba(strokeColor, pillAlpha); + + ctx.beginPath(); + ctx.roundRect(pillX, pillY, pillW, pillH, pillR); + ctx.fill(); + + // Pill text — white + ctx.fillStyle = `rgba(255,255,255,${Math.min(1, alpha + 0.3)})`; + ctx.fillText(label, pillX + 7, pillY + pillH - 4, pillW - 10); + ctx.restore(); } private renderToolbar(): void { @@ -1022,6 +1093,8 @@ export class AngularRenderScanOverlay { const wasted = getWastedStats(); const leaks = getLeakedComponents(); + const onPushCandidates = getOnPushCandidates(); + const pollutionEvents = getZonePollutionEvents(); // Generate timeline sparkline SVG let sparklineSvg = ''; @@ -1047,7 +1120,7 @@ export class AngularRenderScanOverlay { // Leaks metric chip const leaksChip = leaks.length > 0 ? ` - Leaks + Memory leaks ${leaks.length} ` : ''; @@ -1056,35 +1129,66 @@ export class AngularRenderScanOverlay { const hasError = this.budgetViolations.some((v) => v.type === 'error' || v.type === 'render-rate'); const alertsChip = this.budgetViolations.length > 0 ? ` - Alerts + Budget alerts ⚠️ ${this.budgetViolations.length} ` : ''; + // CD Trigger badge + const triggerBadge = this.lastTrigger + ? this.triggerBadgeHtml(this.lastTrigger) + : ''; + + // OnPush candidates chip + const onPushChip = onPushCandidates.length > 0 + ? ` + OnPush savings + ⚡ ${onPushCandidates.length} + ` + : ''; + + // Zone pollution chip + const pollutionChip = pollutionEvents.length > 0 + ? ` + Zone pollution + ⚠ ${pollutionEvents.length} + ` + : ''; + const htmlChanged = this.replaceToolbarHtml(container, ` ${this.inspectPanelHtml()} ${this.cpuDetailsHtml()} ${this.waterfallPanelHtml()} ${this.alertsPanelHtml()} + ${this.onPushPanelHtml(onPushCandidates)} + ${this.zonePollutionPanelHtml(pollutionEvents)} + ${this.cdGraphPanelHtml()}
- ${this.metric('FPS', this.options.showFPS ? String(displayedFps) : '-', this.getFpsClass(displayedFps))} - - CPU + ${this.metric('Frame rate', this.options.showFPS ? String(displayedFps) + ' fps' : '-', this.getFpsClass(displayedFps))} + + CPU busy ${cpuVal}% ${sparklineSvg} - ${this.metric('Cycle', cycle ? `${cycle.duration.toFixed(1)}ms` : '-')} - ${this.metric('Wasted', `${wasted.wastedChecks} (${wasted.wastedPercentage}%)`, wasted.wastedChecks > 0 ? 'cpu-medium' : '')} + ${this.metric('Last cycle', cycle ? `${cycle.duration.toFixed(1)}ms` : '-')} + ${triggerBadge} + ${this.metric('Wasted renders', `${wasted.wastedChecks} (${wasted.wastedPercentage}%)`, wasted.wastedChecks > 0 ? 'cpu-medium' : '')} ${leaksChip} ${alertsChip} - ${this.metric('Slowest', cycle?.slowest ? cycle.slowest.name : '-', '', 'slowest-metric')} + ${onPushChip} + ${pollutionChip} + + CD Graph + + + ${this.metric('Slowest component', cycle?.slowest ? cycle.slowest.name : '-', '', 'slowest-metric')}