diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml new file mode 100644 index 0000000..ae699a8 --- /dev/null +++ b/.github/workflows/ci-cd.yml @@ -0,0 +1,74 @@ +name: CI / CD Pipeline + +on: + push: + branches: + - main + pull_request: + branches: + - main + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: 'pages' + cancel-in-progress: false + +jobs: + build-and-test: + name: Build, Lint & Test + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Setup Node.js 22 + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: 'npm' + cache-dependency-path: apps/portal/package-lock.json + + - name: Install Dependencies + working-directory: apps/portal + run: npm ci + + - name: Verify Content Manifest & Integrity + working-directory: apps/portal + run: npm run test:integrity + + - name: Run Angular Unit Tests + working-directory: apps/portal + run: npm test + + - name: Build Angular Portal for GitHub Pages + working-directory: apps/portal + run: npm run build:pages + + - name: Setup SPA Deep-Linking Fallback & Disable Jekyll + run: | + cp apps/portal/dist/portal/browser/index.html apps/portal/dist/portal/browser/404.html + touch apps/portal/dist/portal/browser/.nojekyll + + - name: Upload Pages Artifact + if: github.ref == 'refs/heads/main' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') + uses: actions/upload-pages-artifact@v3 + with: + path: apps/portal/dist/portal/browser + + deploy: + name: Deploy to GitHub Pages + needs: build-and-test + if: github.ref == 'refs/heads/main' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/01-foundations/01-why-angular.md b/01-foundations/01-why-angular.md index 8c47f07..60baa10 100644 --- a/01-foundations/01-why-angular.md +++ b/01-foundations/01-why-angular.md @@ -1,51 +1,435 @@ # Why Angular -## Purpose +## Interview Priority -Angular was created to help developers build large, maintainable, testable Single Page Applications (SPAs). +**Must Know** -## Problems with Traditional Web Development +## Interview Frequency -- Full page reloads -- Poor user experience -- Difficult maintenance -- Lack of architecture +**Very Common** -## Problems with jQuery +## Recommended Depth -- Easy DOM manipulation -- No application structure -- Hard to scale -- Hard to test +**Strong Understanding** -## Angular Solutions +## Relevant For -- Component-Based Architecture -- Dependency Injection -- Services -- Routing -- TypeScript -- Built-in Framework Features +- Mid-Level Interviews +- Senior Developer Interviews +- Lead Developer Interviews +- Architect Discussions -## Why Enterprises Like Angular +--- + +# What Is Angular? + +Angular is a complete, opinionated, TypeScript-based development platform and framework maintained by Google for building scalable, enterprise-grade Single Page Applications (SPAs) and cross-platform web applications. + +Unlike lightweight UI libraries that focus solely on rendering views, Angular provides an entire **"batteries-included"** ecosystem out of the box: + +- **Component-Driven UI Model**: Encapsulated templates, styles, and TypeScript classes. +- **Hierarchical Dependency Injection (DI)**: First-class inversion of control for decoupled, testable business services. +- **Declarative Reactive Primitives**: Fine-grained Angular Signals combined with RxJS streams for asynchronous operations. +- **Robust Client-Side Routing**: Guards, resolvers, nested child routes, and route-level lazy loading. +- **Type-Safe HTTP Client**: Interceptors, request cloning, progress tracking, and test mocks. +- **Form Management Suites**: Both programmatic Reactive Forms (with typed controls) and Template-Driven forms. +- **Ahead-Of-Time (AOT) Compilation**: Template type-checking and zero-overhead browser execution. +- **CLI & Automated Migrations**: Tooling with built-in code generation, build optimization, and automated version migrations via `ng update`. + +--- + +# Why Does It Exist? + +As web applications evolved from static informational documents into complex, mission-critical business platforms, frontend codebases grew exponentially. + +In early web development, applications suffered from critical structural deficiencies: + +- **Lack of Architectural Standards**: Developers improvised structures, leading to unmaintainable spaghetti code. +- **DOM-Coupled Business Logic**: Logic was tightly coupled to raw DOM element IDs and CSS selectors. +- **State Synchronization Nightmare**: Updating data in one UI section required manual imperative updates across multiple other DOM nodes. +- **Difficult Unit & Integration Testing**: Tightly coupled JavaScript code with direct browser globals (`window`, `document`) was nearly impossible to isolate and test deterministically. +- **Team Scaling Bottlenecks**: Without strict conventions, different developers on the same team wrote code in wildly conflicting styles. + +Angular was conceived to bring **rigorous software engineering principles**—modularity, encapsulation, dependency inversion, and declarative data-binding—directly to browser applications. + +--- + +# Historical Context + +Understanding Angular's evolution is essential for senior engineering discussions: + +```text +2010 2016 2022 2024+ +AngularJS (1.x) ───► Angular (v2–v15) ───► Standalone Revolution ──► Renaissance (v16–v19+) +Two-way binding Complete ground-up Standalone components Angular Signals +$scope, $watch rewrite in TypeScript NgModules optional Fine-grained reactivity +Digest cycle issues Component tree, DI Vite/esbuild bundlers Built-in control flow (@if, @for) +DOM controller model RxJS-centric SSR & Hydration Zoneless change detection +``` + +### 1. The AngularJS Era (v1.x - 2010) +AngularJS pioneered two-way data binding and client-side MVC in the browser using `$scope` and controllers. However, its dirty-checking "digest cycle" degraded severely in complex applications with thousands of bindings, and it lacked static typing. + +### 2. The Great Rewrite: Angular 2+ (2016) +Google rebuilt the framework entirely from scratch in TypeScript. It abandoned `$scope` in favor of a hierarchical Component Tree, unidirectional data flow, Zone.js for change detection, and native Dependency Injection. + +### 3. The Modern Angular Renaissance (v16 - v19+) +Modern Angular represents a major leap forward: +- **Signals**: Native, fine-grained synchronous reactivity replacing heavy reliance on Zone.js and manual RxJS subscriptions for local UI state. +- **Standalone by Default**: Eradication of boilerplate `NgModule` declarations. +- **Built-in Control Flow**: Native `@if`, `@for`, `@switch`, and `@defer` syntax replacing structural directives (`*ngIf`, `*ngFor`). +- **Zoneless Support**: Direct reactivity without Zone.js monkey-patching browser async APIs. +- **Hydration & SSR**: Non-destructive hydration with server-side rendering and event replay. + +--- + +# Problem It Solves + +Consider a global banking portal with 500+ screens, 60 developers, and 5 distinct functional teams (Accounts, Transfers, Cards, Loans, Admin). + +### Without an Opinionated Framework (Ad-hoc Libraries) +- Team A chooses `Zustand` for state; Team B chooses `Redux Toolkit`; Team C writes custom event emitters. +- Team A uses `Axios`; Team B uses native `fetch`; Team C wraps `XMLHttpRequest`. +- Every team invents their own directory structure and routing abstractions. +- Cross-team developer mobility is paralyzed because onboarding requires learning 5 different bespoke architectures. +- Upgrading foundational dependencies requires rebuilding bespoke wiring. + +### With Angular +- Every team uses the same **Dependency Injection container**, the same **HttpClient**, the same **Router**, and the same **Signal reactivity model**. +- A developer from the Loans team can review or contribute to the Transfers team's codebase immediately with zero architectural friction. +- Google provides automated migrations via `ng update`, upgrading foundational framework dependencies, RxJS versions, and syntax automatically across hundreds of enterprise applications. + +--- + +# First Principles Explanation + +From first principles, all client-side web applications are **state machines that project internal state onto the DOM and handle user inputs that transition state**: + +$$\text{UI} = f(\text{State})$$ + +When state changes, the UI must update reliably, efficiently, and predictably. + +```text +┌────────────────────────────────────────────────────────┐ +│ Internal State │ +│ (Signals, Component Properties, Data Models) │ +└──────────────────────────┬─────────────────────────────┘ + │ + Declarative Projection + │ + ▼ +┌────────────────────────────────────────────────────────┐ +│ Document Object Model │ +│ (Rendered HTML Nodes) │ +└──────────────────────────┬─────────────────────────────┘ + │ + User / Network Event + │ + ▼ +┌────────────────────────────────────────────────────────┐ +│ State Transition │ +│ (Methods, Signal Updates, Service Calls) │ +└────────────────────────────────────────────────────────┘ +``` + +In plain JavaScript or jQuery, the developer manually handles both the **state transition** and every individual **DOM element mutation**. If you have $N$ pieces of state and $M$ UI elements, the developer must manage up to $N \times M$ imperative mutation paths. + +Angular solves this by providing: +1. **A Declarative Compiler**: You declare *what* the DOM should look like based on state; the Angular compiler generates highly optimized JavaScript instructions that surgically update the DOM. +2. **Deterministic Data Flow**: Data flows down the component hierarchy via inputs; events flow up via outputs. + +--- + +# Real World Analogy + +Think of building an enterprise commercial skyscraper versus building a customized tiny house: + +```text + LIGHTWEIGHT LIBRARY (e.g., React) OPINIONATED FRAMEWORK (e.g., Angular) + "Custom DIY Timber Frame" "Commercial Steel Skyscraper" + + ┌─────────────────────────────────┐ ┌─────────────────────────────────┐ + │ Pick your own lumber │ │ Certified structural steel │ + │ Pick your own plumbing system │ │ Standardized plumbing shafts │ + │ Pick your own electrical code │ │ Mandated electrical grid │ + │ Build your own elevator shaft │ │ Commercial passenger elevators │ + │ Every house has a unique blueprint│ │ Rigorous architectural codes │ + └─────────────────────────────────┘ └─────────────────────────────────┘ +``` + +- **A Lightweight Library (like React)** gives you the timber and nails (rendering engine). You must independently select, integrate, test, and maintain the plumbing (routing), electrical wiring (forms), HVAC (state management), and foundation inspection (build tooling). +- **An Opinionated Framework (like Angular)** gives you a certified, blueprint-driven commercial building with pre-engineered elevator shafts (routing), standardized electrical conduits (dependency injection), and municipal inspection codes (TypeScript and linting conventions). + +--- + +# Angular vs React vs Vue + +The most common senior interview topic is evaluating frontend technologies for enterprise projects: + +| Architectural Dimension | Angular (Modern 18+/19+) | React (18+/19+) | Vue (3+) | +| :--- | :--- | :--- | :--- | +| **Category** | Full-fledged Framework | View Rendering Library | Progressive Framework | +| **Philosophy** | Highly Opinionated, Standardized | Unopinionated, Flexible | Pragmatic, Approachable | +| **Language** | TypeScript by default (First-class) | JavaScript / TS optional | JavaScript / TS optional | +| **Dependency Injection** | Built-in Hierarchical DI Container | None (Requires Context or 3rd party) | `provide` / `inject` (Hierarchical) | +| **Routing** | Official `@angular/router` | Community libraries (`React Router`, `TanStack`) | Official `vue-router` | +| **Form Handling** | Official Reactive & Template Forms | Community libraries (`React Hook Form`, `Formik`) | Built-in `v-model` + community libs | +| **Reactivity Model** | Angular Signals + RxJS streams | Component re-rendering + Hooks (`useState`, `useMemo`) | Vue Reactivity (`ref`, `reactive`, `computed`) | +| **Change Detection** | Granular Signal tracking / OnPush / Zoneless | Virtual DOM reconciliation | Proxy-based dependency tracking | +| **Tooling & CLI** | Angular CLI (`ng`), official schematics, `ng update` | Vite, Next.js, community setups | Vite, Vue CLI | +| **Enterprise Governance** | Exceptional across large multi-team repos | Requires strict internal standards & conventions | Good, flexible middle ground | + +--- + +# Framework vs Library + +A critical conceptual question in technical interviews: + +### What is a Library? +A library is a collection of packaged helper functions and classes that you call to perform specific tasks. **You are in control of the application flow**, and you decide when and where to call the library. +- *Analogy*: A tool in your toolbox (e.g., a wrench). +- *Example*: Lodash, RxJS, Axios. + +### What is a Framework? +A framework defines the architecture, skeleton, and lifecycle of the application. The framework dictates how code must be organized and calls *your* code at the appropriate moments. +- *Analogy*: A prefabricated assembly line. You provide the parts; the assembly line controls execution. +- *Core Principle*: **Inversion of Control (IoC)**, often called the **"Hollywood Principle"**: *"Don't call us; we'll call you."* + +```text +LIBRARY PATTERN: FRAMEWORK PATTERN: +Developer Controls Flow Framework Controls Flow (Inversion of Control) + +Your Application Code Angular Framework Lifecycle + │ │ + ▼ ▼ + Calls Library ──► Axios / Lodash Calls Your Component ──► ngOnInit() + ▲ │ + │ ▼ +Receives Result Calls Your Template ──► Updates DOM +``` + +--- + +# Why Enterprises Choose Angular + +Global enterprise software engineering teams (banking, healthcare, aerospace, telecom) consistently select Angular because it addresses enterprise governance requirements: + +### 1. Architectural Uniformity +When an organization employs hundreds of developers across distributed geographical locations, architectural consistency is vital. Angular defines an unambiguous, standardized location for: +- Routing (`routes.ts`) +- Business logic & API orchestration (`services/`) +- Presentation & UI interaction (`components/`) +- Cross-cutting concerns (`interceptors/`, `guards/`) + +### 2. Predictable Upgrade Paths (`ng update`) +Enterprises cannot afford to rewrite applications every two years when open-source libraries undergo breaking changes. Angular follows a predictable **6-month major release cycle** and provides automated migration schematics: +```bash +ng update @angular/core @angular/cli +``` +These schematics automatically rewrite deprecated TypeScript APIs across thousands of files. + +### 3. Built-In Security +Angular treats frontend security as a first-class framework responsibility: +- **Built-in DOM Sanitization**: Templates automatically sanitize untrusted values before inserting them into innerHTML, attribute bindings, or styles. +- **XSS Mitigation**: Values bound via `{{ interpolation }}` or `[property]` are encoded automatically. +- **Cross-Site Request Forgery (CSRF)**: Built-in `HttpClientXsrfModule` / `withXsrfConfiguration` automatically extracts anti-forgery tokens from cookies and appends them to outgoing HTTP headers. + +### 4. Native Dependency Injection & Testability +Angular's DI container enables seamless unit testing by allowing developers to substitute real HTTP services with mock implementations using `TestBed` or constructor injection. + +### 5. Monorepo & Scalability Support +Angular integrates natively with enterprise monorepo orchestrators like **Nx**, allowing multi-domain enterprise applications to share common UI component libraries, domain services, and linting rules across dozens of applications. + +--- + +# Common Interview Questions + +## Why Angular? + +Angular provides an end-to-end, enterprise-grade architecture that eliminates decision fatigue, enforces TypeScript type safety, and supplies all critical application infrastructure—Dependency Injection, routing, HTTP communication, reactive state, and automated migrations—as a unified, coherent platform maintained by Google. + +--- + +## Angular vs jQuery? + +| Aspect | jQuery | Angular | +| :--- | :--- | :--- | +| **Paradigm** | Imperative DOM manipulation | Declarative, component-driven data binding | +| **State Management** | State stored directly inside DOM elements | State stored in TypeScript classes/signals; DOM is a projection | +| **Architecture** | No defined architecture | Clear separation: Components, Services, Models, Interceptors | +| **Maintainability** | Degrades rapidly as codebase expands | Scales reliably across hundreds of enterprise modules | +| **Testability** | Requires running browser DOM; very hard to mock | Isolated unit tests using DI and test fixtures | + +--- + +## Angular vs React? + +- **Scope**: Angular is a full platform; React is a view library that requires assembling a custom stack from third-party community packages. +- **Architecture**: Angular provides built-in Dependency Injection, official routing, and official forms; React delegates these concerns to community solutions (`React Router`, `React Hook Form`, `Zustand`). +- **Reactivity**: Modern Angular utilizes fine-grained Signals and RxJS, updating only the specific DOM nodes that depend on changed state. React re-executes component render functions and uses a Virtual DOM reconciliation algorithm to calculate mutations. +- **Enterprise Fit**: Angular excels in large corporate environments where strict architectural standardization and automated upgrades are required. React excels in environments prioritizing flexibility, rapid iteration, and specialized custom architectures. + +--- + +## Framework vs Library? + +The fundamental difference is **Inversion of Control (IoC)**: +- With a **Library**, your application code controls the flow and calls library functions whenever needed. +- With a **Framework**, the framework controls the execution flow, establishes the lifecycle, and calls your code into predefined hooks (e.g., Angular invoking lifecycle methods like `ngOnInit` or change detection cycles). + +--- + +## Why Angular for enterprise applications? + +Enterprises prioritize maintainability, security, and developer interchangeability over bleeding-edge experimental APIs. Angular provides: +1. Standardized architecture minimizing onboarding friction. +2. First-class TypeScript type safety preventing runtime defects. +3. Automated migrations via `ng update` ensuring long-term maintainability. +4. Built-in security (DOM sanitization, CSRF token handling). +5. Native Dependency Injection for robust unit testing and decoupled layers. + +--- + +## What are the trade-offs and disadvantages of Angular? + +- **Steep Learning Curve**: Developers must learn TypeScript, Dependency Injection, RxJS observables, reactive signals, decorators, and template syntax. +- **Initial Bundle Size**: Because Angular includes comprehensive framework capabilities, its baseline bundle is historically larger than a bare React or Vue application (though modern standalone components, tree-shaking, and `@defer` have mitigated this significantly). +- **Overkill for Simple Websites**: For a landing page or content brochure, Angular's architecture introduces unnecessary conceptual overhead compared to static site generators or lightweight libraries like Astro or Alpine.js. + +--- + +## How has Modern Angular (v16–v19+) modernized compared to legacy Angular? + +Modern Angular has eliminated historic pain points: +- **Standalone Components**: Replaced verbose `NgModule` boilerplate; components directly import what they need. +- **Angular Signals**: Introduced fine-grained synchronous reactivity (`signal()`, `computed()`, `effect()`), drastically reducing the complexity of UI state management. +- **Built-in Control Flow**: Modern `@if`, `@for (track id)`, and `@switch` replace legacy `*ngIf` and `*ngFor` directives with cleaner syntax and superior type narrowing. +- **Deferrable Views (`@defer`)**: Declarative lazy-loading of heavy template blocks triggered on viewport scroll, idle, or user interaction. +- **Zoneless Change Detection**: Optional removal of `Zone.js`, enabling surgical DOM updates with significantly smaller runtime overhead. + +--- + +# Senior-Level Discussion: The .NET & Clean Architecture Parallel + +For senior engineers and developers with .NET or Java backgrounds, Angular feels natural because it mirrors backend Clean Architecture: + +```text +ASP.NET CORE LAYER ANGULAR ARCHITECTURE LAYER RESPONSIBILITY +───────────────────────────────────────────────────────────────────────────────────────────── +Controller / Minimal API ◄─► Angular Component / View Handles input & presentation +Application Services ◄─► Angular Injectable Service Coordinates business logic +HttpClient / EF Core ◄─► Angular HttpClient Data access & API transport +Dependency Injection (IoC) ◄─► Angular Hierarchical DI Decoupled lifetime management +Action Filters ◄─► Angular HttpInterceptors Cross-cutting concerns (Auth, Log) +Route Middleware ◄─► Angular Route Guards (canMatch) Access control & redirection +DTOs & Entity Models ◄─► TypeScript Interfaces & Types Compile-time contract enforcement +``` + +This structural parity allows full-stack enterprise teams to apply identical architectural principles (SOLID, Dependency Inversion, Single Responsibility, Separation of Concerns) across both frontend and backend codebases. + +--- + +# Architecture Considerations + +When architecting a new application, evaluate these factors: + +```text + DECISION TREE: WHEN TO CHOOSE ANGULAR + │ + Is this an enterprise platform, + multi-team project, or complex SPA? + │ + ┌────────────────┴────────────────┐ + YES NO + │ │ + Do you require built-in DI, Is it a lightweight brochure, + standardized routing, forms, and simple blog, or static site? + long-term automated migrations? │ + │ ▼ + ▼ Consider Astro, Next.js, + CHOOSE ANGULAR or Vanilla HTML/CSS +``` + +### Team Guidelines +- Standardize on **Standalone Components** and eliminate `NgModule` from new development. +- Use **Signals** for synchronous UI state and reserve **RxJS** for asynchronous streaming, WebSocket events, and cancellation. +- Enforce `ChangeDetectionStrategy.OnPush` across all components. +- Never write business logic inside components; delegate to domain services behind interfaces. + +--- + +# High-Level Architectural Diagram + +```text +┌─────────────────────────────────────────────────────────────────────────────────────────┐ +│ ANGULAR PLATFORM │ +├─────────────────────────────────────────────────────────────────────────────────────────┤ +│ │ +│ PRESENTATION LAYER │ +│ ┌───────────────────────────┐ ┌───────────────────────────┐ │ +│ │ Smart (Container) │ │ Dumb (Presentational) │ │ +│ │ Component │◄───────►│ Component │ │ +│ │ - Injects Domain Service │ Events │ - Pure Inputs (Signals) │ │ +│ │ - Manages Route / Page │ Inputs │ - Pure Outputs │ │ +│ └─────────────┬─────────────┘ └───────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ BUSINESS LOGIC & STATE LAYER │ +│ ┌─────────────────────────────────────────────────────────────────┐ │ +│ │ Domain Feature Service │ │ +│ │ - Exposes Read-only Signals & Derived Computations │ │ +│ │ - Encapsulates Business Rules & State Transitions │ │ +│ └─────────────┬───────────────────────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ INFRASTRUCTURE & TRANSPORT LAYER │ +│ ┌───────────────────────────┐ ┌───────────────────────────┐ │ +│ │ Angular HttpClient │────────►│ HTTP Interceptors │ │ +│ │ - Type-safe requests │ │ - JWT Bearer Injection │ │ +│ │ - Response observables │ │ - Global Error Catching │ │ +│ └─────────────┬─────────────┘ └───────────────────────────┘ │ +│ │ │ +└─────────────────┼───────────────────────────────────────────────────────────────────────┘ + │ + ▼ HTTPS (JSON Payloads) +┌─────────────────────────────────────────────────────────────────┐ +│ Enterprise Backend API │ +│ (ASP.NET Core / Java Spring / Node.js) │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +# Key Takeaways + +1. **Complete Platform**: Angular is not a view library; it is a complete, opinionated application framework providing DI, routing, forms, HTTP client, and reactivity out of the box. +2. **Inversion of Control**: Angular controls the lifecycle and execution flow, calling your components and services according to architectural conventions. +3. **Enterprise Governance**: Standardized structure, built-in security, first-class TypeScript, and automated migrations (`ng update`) make Angular the premier choice for large, multi-team engineering organizations. +4. **Modern Renaissance**: Modern Angular (18+/19+) features Standalone components, fine-grained Signals, built-in control flow (`@if`, `@for`), `@defer`, and zoneless change detection. +5. **Architectural Parallels**: Angular mirrors enterprise backend architectures (like ASP.NET Core Clean Architecture), enabling consistent software engineering patterns across the full stack. + +--- -- Consistent architecture -- Strong tooling -- Scalability -- Testability -- Long-term maintainability +# Interview Notes (Revision Version) -## Common Interview Questions +## Definition +Angular is a full-featured, opinionated, TypeScript-based framework maintained by Google designed to build maintainable, testable, and scalable enterprise Single Page Applications. -- Why Angular? -- Angular vs jQuery? -- Angular vs React? -- Framework vs Library? -- Why Angular for enterprise applications? +## Core Pillars +- Standalone Component Tree +- Hierarchical Dependency Injection +- TypeScript & AOT Compilation +- Reactive Architecture (Signals + RxJS) +- Comprehensive Tooling (`ng`, schematics, `ng update`) -## Key Message +## Top 3 Enterprise Benefits +1. **Architectural Consistency**: Predictable code organization across massive, distributed development teams. +2. **Automated Migrations**: `ng update` handles framework upgrades and API deprecations automatically. +3. **Integrated Security**: Automatic DOM sanitization against XSS and built-in CSRF token handling. -Angular applies software engineering principles to frontend development and provides a structured way to build large web applications. +## Modern vs Legacy Angular +- Legacy: `NgModule`, Zone.js dirty checking, `*ngIf`/`*ngFor`, RxJS required for all local state. +- Modern: Standalone by default, fine-grained Signals, `@if`/`@for`/`@defer`, optional Zoneless, hydration SSR. diff --git a/01-foundations/02-spa-concepts.md b/01-foundations/02-spa-concepts.md index 722ce1e..f6db921 100644 --- a/01-foundations/02-spa-concepts.md +++ b/01-foundations/02-spa-concepts.md @@ -1,57 +1,399 @@ # Single Page Application (SPA) -## Definition +## Interview Priority + +**Must Know** + +## Interview Frequency + +**Very Common** + +## Recommended Depth + +**Strong Understanding** + +## Relevant For + +- Mid-Level Interviews +- Senior Developer Interviews +- Lead Developer Interviews +- Architect Discussions + +--- + +# What Is A Single Page Application (SPA)? + +A **Single Page Application (SPA)** is a web application architecture where the browser loads a single HTML shell page (`index.html`) and associated JavaScript/CSS assets once. Subsequent page transitions, user interactions, and view mutations occur dynamically by manipulating the existing Document Object Model (DOM) via client-side JavaScript, rather than requesting full HTML documents from the server. + +Data synchronization between the client and backend occurs asynchronously over background HTTP calls (typically transmitting lightweight JSON payloads via `fetch` or `HttpClient`). + +--- + +# Why Does It Exist? + +Traditional Multi-Page Applications (MPAs) were built for document retrieval. Every user interaction that changed views triggered a full roundtrip to the web server: + +```text +TRADITIONAL MULTI-PAGE APPLICATION (MPA) FLOW: +User clicks link ──► Browser destroys entire page ──► White screen flash ──► +Server queries DB & renders HTML string ──► Browser downloads full HTML, CSS, JS ──► Re-renders everything +``` + +This traditional flow caused severe user experience drawbacks: +- **White Screen Flashes**: Disruptive rendering flicker during navigation. +- **Lost UI State**: Form inputs, scroll positions, and client-side memory were wiped on every navigation. +- **Excessive Network Bandwidth**: Identical headers, navigation bars, scripts, and stylesheets were repeatedly downloaded on every page hit. +- **High Server CPU Load**: Backend web servers spent substantial CPU cycles assembling repetitive HTML markup instead of serving raw data. + +The SPA architecture was introduced to give web applications the **responsiveness, fluid animations, and persistence of native desktop applications**. + +--- + +# Historical Context + +```text +1995–2004 2005–2009 2010–2015 2016–Present +Static & Server Pages ───► AJAX Revolution ───► Early SPA Frameworks ──► Modern Enterprise SPAs +HTML Forms XMLHttpRequest AngularJS, Backbone Angular, React, Vue +Full page reloads Partial DOM updates Hash-based routing (#) HTML5 History API (pushState) +PHP, ASP Classic, JSP Gmail, Google Maps Client-side MVC SSR + Hydration, Signals, @defer +``` + +### 1. The Pre-AJAX Era +Every form submission or link click required a brand-new page load. Web applications felt clunky, slow, and disconnected. + +### 2. The AJAX Breakthrough (2005) +Google Maps and Gmail proved that web browsers could fetch data asynchronously using `XMLHttpRequest` and update slices of the page without destroying the browser document. + +### 3. Early SPAs & Hash Routing +Early frameworks used the URL hash (`http://app.com/#/dashboard`) because modifying the URL path without reloading the browser was not supported by older browsers. + +### 4. Modern HTML5 History API & Universal SPAs +With HTML5 `history.pushState()` and `history.replaceState()`, browsers can manipulate the address bar URL seamlessly without triggering a network request. Modern Angular combines client-side routing with Server-Side Rendering (SSR) and hydration to deliver the best of both worlds. + +--- + +# Traditional MPA Flow vs Modern SPA Flow + +```text +TRADITIONAL MPA (Server-Rendered): +┌───────────┐ ┌───────────┐ ┌───────────┐ +│ Browser │ │ Server │ │ Database │ +└─────┬─────┘ └─────┬─────┘ └─────┬─────┘ + │ GET /employees │ │ + │───────────────────────────────────►│ Query data │ + │ │───────────────────────────────────►│ + │ │◄───────────────────────────────────│ + │ │ Assemble entire HTML document │ + │ 200 OK (Full HTML Document: 120KB)│ │ + │◄───────────────────────────────────│ │ + │ [Entire page destroyed & rebuilt] │ │ + │ │ │ + │ GET /employees/42 (Click link) │ │ + │───────────────────────────────────►│ Query data │ + │ │───────────────────────────────────►│ + │ │◄───────────────────────────────────│ + │ │ Assemble entire HTML document │ + │ 200 OK (Full HTML Document: 125KB)│ │ + │◄───────────────────────────────────│ │ + │ [White flash, re-render DOM] │ │ +``` + +```text +MODERN ANGULAR SPA: +┌───────────┐ ┌───────────┐ ┌───────────┐ +│ Browser │ │ Server │ │ Database │ +└─────┬─────┘ └─────┬─────┘ └─────┬─────┘ + │ GET / │ │ + │───────────────────────────────────►│ Serve index.html & bundles │ + │ 200 OK (index.html + JS/CSS) │ │ + │◄───────────────────────────────────│ │ + │ [Angular bootstraps in memory] │ │ + │ │ │ + │ Navigate to /employees (Client-side) │ + │ Router intercepts click, updates URL via pushState │ + │ │ │ + │ GET /api/employees (JSON only) │ │ + │───────────────────────────────────►│ Execute REST / GraphQL query │ + │ │───────────────────────────────────►│ + │ │◄───────────────────────────────────│ + │ 200 OK (Raw JSON payload: 4KB) │ │ + │◄───────────────────────────────────│ │ + │ [Surgically patch DOM via Signals]│ │ + │ Zero page reload! Instant update! │ │ +``` + +--- + +# First Principles Explanation + +From first principles, an SPA decouples **view rendering** from **data delivery**: + +1. **Separation of Concerns**: The frontend is responsible for layout, UI state, rendering, and interaction logic. The backend is an API that serves structured business data (JSON). +2. **Persistent Application State in Memory**: The JavaScript runtime environment (`window`, global state, caches, open WebSocket connections) remains active throughout the user's entire session. +3. **Bandwidth Efficiency**: Over-the-wire payloads transition from heavy HTML markup strings (containing redundant layout tags) to raw data structures. + +--- + +# Real World Analogy + +Consider the difference between a **Fast-Food Drive-Thru vs Dining at a Buffet Restaurant**: + +```text +MULTI-PAGE APPLICATION (MPA) SINGLE PAGE APPLICATION (SPA) +"Drive-Thru for Every Single Item" "Entering a Modern Buffet" + +1. Drive up: Order Burger 1. Walk into dining room once +2. Wait for bag, drive away 2. Get your plate & silverware (Bootstrapping) +3. Decide you want Fries 3. Walk up to stations as you please +4. Drive back to start of line, re-order 4. Only take the specific food you need (JSON) +5. Entire packaging & bag handed to you again 5. Your table, drink, and seat remain unchanged +``` + +- **MPA**: You leave the building and go through the entire ordering and packaging process every single time you want another item. +- **SPA**: You sit down once. Whenever you need more food, you only fetch the specific dish, keeping your seat, silverware, and conversation completely uninterrupted. + +--- + +# How Angular Solves SPA Challenges -A Single Page Application loads once and dynamically updates the UI without performing full page reloads. +Building an enterprise SPA from scratch introduces significant engineering challenges. Angular provides comprehensive, built-in solutions: -## Traditional Website Flow +### 1. Client-Side Routing (`@angular/router`) +- Intercepts link clicks to prevent the browser from issuing an HTTP request. +- Uses the **HTML5 History API** (`pushState`, `replaceState`, `popstate` event listeners) to keep the browser address bar, back button, and bookmarks in sync. +- Activates and renders components dynamically inside ``. -Browser -→ Request Page -→ Server Generates HTML -→ Full Page Reload +### 2. State Management & In-Memory Persistence +- Because the browser window does not refresh, Angular services provided at root (`@Injectable({ providedIn: 'root' })`) act as persistent in-memory singletons. +- Angular Signals and RxJS coordinate state transitions across completely decoupled components without losing state during page navigation. -## SPA Flow +### 3. Code Splitting & Lazy Loading +- **Challenge**: Monolithic SPAs can result in massive initial JavaScript bundles that slow down initial page loads. +- **Angular Solution**: + - Route-level lazy loading via `loadComponent: () => import('./feature.component')`. + - Granular template-level lazy loading via **Deferrable Views (`@defer`)**, loading heavy widgets only when scrolled into the viewport or idle. -Browser -→ Load Application Once -→ JavaScript Controls Navigation -→ UI Updates Dynamically +### 4. SEO & Initial Load (Angular SSR & Hydration) +- **Challenge**: Search engine crawlers and low-powered mobile devices can struggle with client-rendered empty `` shells. +- **Angular Solution**: + - Built-in Server-Side Rendering (`@angular/ssr`) renders the initial component tree into static HTML on the server. + - **Non-Destructive Hydration**: Modern Angular reuses the server-rendered DOM nodes on the client rather than destroying and re-creating them, eliminating flicker. + - **Event Replay**: Automatically buffers user clicks before JavaScript finishes downloading and replays them once hydrated. -## Advantages +### 5. Security in SPAs +- **DOM Sanitization**: Angular's `DomSanitizer` automatically inspects and sanitizes untrusted HTML, styles, and URLs to prevent Cross-Site Scripting (XSS). +- **CSRF Token Handling**: Angular's `HttpClient` automatically reads XSRF cookies and attaches the token to outgoing mutable HTTP requests. -- Faster user experience -- No full page refresh -- Rich interactions -- Better frontend/backend separation +--- + +# SPA vs MPA Architectural Matrix + +| Architectural Vector | Single Page Application (SPA) | Multi-Page Application (MPA) | +| :--- | :--- | :--- | +| **Initial Page Load** | Slower (Downloads JS runtime & core bundles) | Faster (Delivers pre-rendered HTML immediately) | +| **Subsequent Navigation** | Near-instantaneous (Updates DOM dynamically) | Slower (Full page reload, re-parses all HTML/CSS) | +| **User Experience (UX)** | Fluid, desktop-like, smooth transitions | Page flickers on navigation; state lost on reload | +| **Server Resource Usage** | Low (Server only serializes JSON data) | High (Server renders HTML templates repeatedly) | +| **Client Resource Usage** | Higher (Browser executes JS, manages DOM) | Minimal (Browser only displays parsed HTML) | +| **State Persistence** | Trivial (In-memory services, Signals, stores) | Difficult (Requires cookies, sessions, query params) | +| **SEO Complexity** | Requires SSR / Prerendering for optimal indexability | Built-in (Static HTML is easily crawled) | +| **Offline Capability** | Excellent (Easily paired with Service Workers/PWA) | Very limited (Requires server for new pages) | +| **Architectural Complexity**| Higher frontend engineering complexity | Traditional, straightforward server routing | + +--- + +# Common Interview Questions + +## What is a Single Page Application (SPA)? + +An SPA is a web application that loads a single HTML document once and updates the user interface dynamically via client-side JavaScript by manipulating the DOM and fetching raw data asynchronously via APIs, eliminating the need for full browser page reloads. + +--- + +## What are the key differences between an SPA and an MPA? + +- **Rendering Location**: MPAs render full HTML pages on the server for every request. SPAs render views in the browser client using JavaScript after an initial shell load. +- **Navigation Behavior**: MPAs perform full browser refreshes on navigation, destroying client memory. SPAs intercept route changes using the HTML5 History API, preserving application state. +- **Data Exchange**: MPAs exchange HTML markup strings. SPAs exchange lightweight JSON data payloads over HTTP. +- **Performance Profile**: MPAs have faster initial loads but slower subsequent navigation. SPAs have higher initial bundle load costs but near-instant subsequent page transitions. + +--- + +## How does client-side routing work under the hood without triggering a page reload? + +Client-side routing relies on the **HTML5 History API**: +1. **Event Interception**: The router intercepts click events on anchor tags (``), preventing the browser's default navigation action (`event.preventDefault()`). +2. **URL Update**: It calls `history.pushState(state, title, url)` to update the browser address bar and history stack without triggering a browser request to the server. +3. **History Navigation**: It listens to the `window.onpopstate` event to detect when the user clicks the browser Back or Forward buttons. +4. **View Resolution**: The router matches the updated URL path against the route configuration table, instantiates the mapped Standalone Component, and inserts it into ``. + +--- + +## What are the main challenges and disadvantages of SPAs? + +- **Initial Load Latency**: Large JavaScript bundles must be downloaded, parsed, and executed before the application becomes interactive (First Contentful Paint & Time to Interactive). +- **SEO Challenges**: Search engine web crawlers that do not execute JavaScript may see only an empty `` tag unless SSR or prerendering is implemented. +- **Client Memory Leaks**: Long-lived single-page sessions can accumulate memory leaks if developers fail to unsubscribe from RxJS observables, detach event listeners, or clean up intervals in `ngOnDestroy`. +- **Browser History & Deep Linking**: Developers must explicitly configure server fallbacks (`try_files $uri $uri/ /index.html;`) so that refreshing a deep route (e.g. `/employees/42`) does not return an HTTP 404 from the web server. + +--- + +## How does Angular solve SPA performance bottlenecks and SEO challenges? + +Angular provides several enterprise mechanisms: +1. **Angular SSR & Non-Destructive Hydration**: Renders views on Node.js server, delivering instant HTML to search engines and users, then seamlessly attaches client reactivity without DOM destruction. +2. **Event Replay**: Automatically captures user clicks during hydration and replays them once the bundle finishes compiling. +3. **Route Lazy Loading (`loadComponent`)**: Splits the application into separate JavaScript chunks, loading features strictly when visited. +4. **Deferrable Views (`@defer`)**: Postpones rendering expensive below-the-fold components until idle or scrolled into view. +5. **OnPush & Signals**: Bypasses deep component tree dirty-checking cycles, providing surgical DOM updates. -## Challenges +--- + +## When should you choose NOT to build an SPA? + +You should avoid an SPA when: +- The website is purely informational or content-heavy (e.g., blogs, documentation, news publishers, marketing landing pages) where content rarely updates dynamically. +- Instant, zero-JavaScript initial load speed is critical on low-end mobile networks. +- SEO indexing is the primary success metric and the engineering team lacks infrastructure to maintain SSR/prerendering pipelines. +- In these cases, Multi-Page static architectures (e.g., Astro, Hugo, or classic server-rendered templates) are superior. + +--- + +# Senior-Level Discussion: ASP.NET Core & SPA Architecture (.NET Parallels) + +For senior full-stack architects working with .NET, the architectural evolution from Razor/MVC to Angular SPAs is a classic discussion: + +```text +HISTORICAL APPROACH: ASP.NET MVC / RAZOR PAGES +┌─────────────────────────────────────────────────────────────┐ +│ ASP.NET Core Web Server │ +│ Controllers return View(model) ──► Razor Engine (.cshtml) │ +│ Generates raw HTML on server for every request │ +└─────────────────────────────────────────────────────────────┘ + +MODERN ENTERPRISE APPROACH: ANGULAR SPA + ASP.NET CORE WEB API (OR BFF) +┌───────────────────────────┐ ┌──────────────────────────────────────┐ +│ Client Tier (Browser) │ │ API Gateway / Backend-for-Frontend │ +│ Angular Standalone App │ │ ASP.NET Core Web API Minimal APIs │ +│ - Routes in browser │◄───────►│ - JWT Bearer Authentication │ +│ - Manages UI State │ HTTPS │ - EF Core / Microservices / DB │ +│ - Signals & OnPush │ JSON │ - OpenAPI / Swagger Contracts │ +└───────────────────────────┘ └──────────────────────────────────────┘ +``` + +### Key Senior Talking Points: +1. **The Fallback Route Configuration**: In ASP.NET Core, an SPA requires configuring `endpoints.MapFallbackToFile("index.html")` so that any unhandled server route is redirected to the Angular shell, allowing Angular Router to resolve the path on the client. +2. **Backend-For-Frontend (BFF) Pattern**: When security requirements prohibit storing sensitive tokens in browser storage, a lightweight server (BFF) manages HttpOnly session cookies with the Angular SPA and translates them to Bearer tokens for internal microservices. + +--- -- Larger initial bundle size -- SEO considerations -- More frontend complexity -- JavaScript dependency +# Architecture Considerations -## Why Angular Fits SPAs +```text + ENTERPRISE SPA ARCHITECTURE CHECKLIST + │ + ┌────────────────────────────────┼────────────────────────────────┐ + ▼ ▼ ▼ + ROUTE CHUNKING SEO & FIRST PAINT STATE HYGIENE + - Standalone lazy routes - Angular SSR + Hydration - Injectable root services + - @defer on heavy widgets - Route prerendering (SSG) - DestroyRef / takeUntilDestroyed + - PreloadStrategy for speed - Event Replay enabled - Zero memory leaks in ngOnDestroy +``` -- Components -- Routing -- Services -- Dependency Injection -- HttpClient -- State Management +### Server Routing Requirement (Avoid 404 on Refresh) +In any SPA, when a user refreshes `https://portal.com/dashboard/reports`, the request hits the physical web server. The web server must be configured to return `index.html` instead of throwing a 404: +- **Nginx**: `try_files $uri $uri/ /index.html;` +- **Apache**: RewriteRule `^index\.html$ - [L]` +- **ASP.NET Core**: `app.MapFallbackToFile("index.html");` -## Common Interview Questions +--- + +# SPA Lifecycle & Routing Flow Diagram + +```text +┌───────────────────────────────────────────────────────────────────────────────────┐ +│ INITIAL BROWSER SESSION │ +└────────────────────────────────────────┬──────────────────────────────────────────┘ + │ + ▼ + Download index.html + JS bundle + │ + ▼ + Bootstrap Angular App + │ + ▼ + Angular Router resolves initial URL path + │ + ▼ +┌───────────────────────────────────────────────────────────────────────────────────┐ +│ USER CLICKS NAVIGATION LINK │ +└────────────────────────────────────────┬──────────────────────────────────────────┘ + │ + ▼ + Angular Router intercepts click event + (Calls preventDefault()) + │ + ▼ + HTML5 History: history.pushState(url) + (URL updates with NO page reload) + │ + ▼ + Execute Route Guards (canMatch / canActivate) + │ + ┌──────────────────┴──────────────────┐ + ▼ Allowed ▼ Denied + Execute Resolvers (if any) Redirect (e.g. /login) + │ + ▼ + Is Component already loaded? + │ + ┌─────────┴─────────┐ + ▼ YES ▼ NO + Instantiate Download lazy chunk + Component via dynamic import() + │ │ + └─────────┬─────────┘ + ▼ + Mount Component inside + │ + ▼ + Fetch asynchronous JSON data via HttpClient + │ + ▼ + Update Signals ──► Surgical DOM update! +``` + +--- + +# Key Takeaways + +1. **Core Concept**: An SPA loads a single HTML document once; all subsequent view changes occur dynamically via client-side JavaScript DOM manipulation. +2. **Data vs Markup**: SPAs exchange lightweight JSON data payloads with backend APIs rather than full HTML documents, reducing server load and bandwidth. +3. **Client-Side Routing**: The HTML5 History API (`pushState`, `popstate`) updates the URL and history stack without triggering browser page reloads. +4. **Performance & SEO Solutions**: Modern Angular solves historical SPA trade-offs using Server-Side Rendering (SSR), non-destructive hydration, `@defer`, and route lazy loading. +5. **Server Fallback Rule**: All unmapped server-side routes must fallback to `index.html` to allow the client-side router to handle deep links. + +--- + +# Interview Notes (Revision Version) + +## Definition +A Single Page Application loads a single HTML page once and dynamically rewrites the current page as the user interacts with the app, exchanging JSON data asynchronously via background API calls. -- What is an SPA? -- SPA vs MPA? -- Why are SPAs popular? -- What are SPA disadvantages? -- How does Angular support SPA development? +## SPA vs MPA Summary +- MPA: Server renders HTML per page; full browser refresh on navigation; higher server CPU. +- SPA: Browser renders UI dynamically; client router manages URLs; lower bandwidth; desktop-like UX. -## Key Message +## Under the Hood Routing +- Uses `history.pushState()` to change the URL without browser reload. +- Listens to `popstate` for Back/Forward navigation. +- `` acts as dynamic viewport placeholder for matched component. -Angular is a framework designed to build maintainable Single Page Applications that provide a rich user experience while keeping frontend and backend concerns separate. +## Top 3 SPA Challenges & Angular Countermeasures +1. **Initial Bundle Size**: Solved via Standalone lazy loading (`loadComponent`) and `@defer`. +2. **SEO & First Paint**: Solved via `@angular/ssr` and non-destructive hydration. +3. **Memory Leaks**: Solved via `takeUntilDestroyed()`, `DestroyRef`, and clean service scoping. diff --git a/01-foundations/03-component-based-architecture.md b/01-foundations/03-component-based-architecture.md index 4b988a9..a199898 100644 --- a/01-foundations/03-component-based-architecture.md +++ b/01-foundations/03-component-based-architecture.md @@ -23,277 +23,427 @@ # What Is Component-Based Architecture? -Component-Based Architecture is a software design approach where the user interface is broken into smaller, reusable, independent building blocks called Components. +Component-Based Architecture is a software design approach where the user interface is composed of smaller, reusable, independent, and self-contained building blocks called **Components**. -Instead of building the entire UI as one large page, the application is divided into logical pieces. +Instead of building an entire user interface as one large, monolithic page, the application is divided into cohesive, decoupled pieces. Example: - ```text -Amazon - -Header -Search Bar -Navigation Menu -Product List -Shopping Cart -Footer +Enterprise Portal +├── HeaderComponent +├── SearchBarComponent +├── NavigationMenuComponent +├── ProductListComponent +├── ShoppingCartComponent +└── FooterComponent ``` -Each section can become a separate component. +Each section encapsulates its own: +1. **Template**: Declares the HTML markup and view structure. +2. **Logic**: Controls state, user interactions, and view logic in TypeScript. +3. **Styles**: Scoped CSS rules applying exclusively to this component. --- # Why Does It Exist? -As web applications became larger, maintaining a single HTML, CSS, and JavaScript codebase became difficult. +As web applications grew from simple text documents into massive enterprise platforms, maintaining monolithic codebases became unmanageable. Problems included: +- **Code Duplication**: Re-writing identical HTML, CSS, and event listeners across dozens of screens. +- **Fragile Maintenance**: Changing a CSS class or JavaScript function in one place unexpectedly broke unrelated sections. +- **Tight Coupling**: Presentation logic was deeply tangled with backend API calls and global browser events. +- **Poor Testability**: Isolating a single feature for automated testing required spinning up entire pages. +- **Team Collaboration Friction**: Multiple engineers working on the same HTML and JavaScript files constantly suffered merge conflicts. -- Code duplication -- Difficult maintenance -- Tight coupling -- Poor reusability -- Complex testing -- Team collaboration issues - -Component-Based Architecture solves these problems by introducing modularity. +Component-Based Architecture introduces **modularity and encapsulation** to frontend engineering, applying the same software engineering best practices that backend systems have used for decades. --- # Historical Context -## Early Web Applications - -Traditional applications often contained: - ```text -index.html -app.js -style.css +1995–2005 2006–2012 2013–Present +Monolithic Pages ───► jQuery Era ───► Component-Based Frameworks +Large index.html DOM manipulation utilities Angular, React, Vue +Global CSS & JS Selectors: $('#btn').click() UI = Tree of Components +Tightly coupled scripts No application architecture Encapsulated State, View & Styles ``` -All functionality existed in a few large files. +### 1. Early Web Applications +Applications were structured around pages (`index.html`, `dashboard.html`). All CSS lived in global stylesheets, and JavaScript existed in sprawling script files. -For small websites this worked. +### 2. The jQuery Era +jQuery revolutionized DOM selection and animation, but it provided zero architectural guidance. State was stored directly inside DOM elements (e.g. data attributes), and business logic was scattered across hundreds of ad-hoc event listeners. -For enterprise applications it became difficult to manage. +### 3. The Component Revolution +Modern frameworks established Components as the foundational building block of frontend engineering. Today, developers design applications as hierarchical trees of reusable UI blocks rather than disconnected HTML documents. --- -## jQuery Era +# Problem It Solves -jQuery improved DOM manipulation. +Consider an Employee Directory in an enterprise application: -Example: +### Without Components: +The HTML markup and click handlers for an "Employee Card" are copied across 4 different views: +- Dashboard +- Employee List +- Organization Chart +- Audit Reports -```javascript -$("#saveButton").click(...) +```text +Dashboard.html ──────► Copies Employee Card HTML (25 lines) +EmployeeList.html ───► Copies Employee Card HTML (25 lines) +OrgChart.html ───────► Copies Employee Card HTML (25 lines) +Reports.html ────────► Copies Employee Card HTML (25 lines) ``` -However, applications still lacked structure. +If product requirements change (e.g. adding an avatar or department badge), developers must find and edit every copy manually. Inconsistencies and regressions are inevitable. -As applications grew: - -- JavaScript became difficult to maintain -- UI logic scattered throughout the application -- Reuse became difficult +### With Components: +```text + +``` +The markup, styling, and behavior are implemented once in `EmployeeCardComponent`. Every view imports and renders the component, guaranteeing 100% UI and behavioral consistency. --- -## Modern Frontend Frameworks +# First Principles Explanation -Frameworks like: +In backend architecture (such as ASP.NET Core or Java Spring), developers never write an entire enterprise system inside a single `Program.cs` or controller. -- Angular -- React -- Vue +Instead, they separate concerns: +```text +Controller ──► Application Service ──► Repository ──► Domain Entity +``` -introduced Component-Based Architecture. +Angular applies this exact principle of **decomposition and single responsibility** to the user interface: -Developers now think in reusable UI building blocks rather than pages. +$$\text{Application UI} = \sum_{i=1}^{n} \text{Component}_i$$ ---- +Each component acts as a bounded context on the screen, managing its own inputs, internal state, and output events. -# Problem It Solves +--- -Consider an Employee Directory application. +# Real World Analogy -Without Components: +Think about modern automobile manufacturing on an assembly line: ```text -Employee Card HTML copied multiple times - -Dashboard -Employee List -Organization Chart -Reports +Automobile Assembly +├── Engine Block +├── Wheels & Braking System +├── Climate Control Dashboard +├── Ergonomic Seats +└── Passenger Doors ``` -Problems: +- Each part has an explicit, engineered responsibility. +- A wheel does not care what brand of engine powers the vehicle, provided the wheel lugs match the axle contract. +- If a headlight bulb fails, you replace the headlight component without rebuilding the car's transmission. -- Duplicate code -- Difficult updates -- Inconsistent UI +Angular applications work identically: the application is an assembly of swappable, testable, independent components conforming to strict interface contracts. -With Components: +--- -```text -EmployeeCardComponent -``` +# Angular's Component Model -Implemented once and reused everywhere. +In Angular, a Component is a TypeScript class decorated with `@Component`: ---- +```typescript +@Component({ + selector: 'app-employee-card', + standalone: true, + templateUrl: './employee-card.component.html', + styleUrl: './employee-card.component.css', + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class EmployeeCardComponent { + // Controller logic, state, inputs, and outputs +} +``` -# First Principles Explanation +A component encompasses four key facets: +1. **Selector**: The custom HTML tag name (e.g., ``) used to place this component in templates. +2. **Template**: The declarative HTML structure defining what renders on screen. +3. **Styles**: CSS/SCSS encapsulated to the component via Angular's View Encapsulation. +4. **Class & Metadata**: TypeScript class holding reactive state, signals, and interaction handlers. -Backend developers do not place all logic inside: +--- -```csharp -Program.cs -``` +# The Component Tree -Instead they create: +Every Angular application is structured as a hierarchical **Component Tree** with `AppComponent` as the root: ```text -Controllers -Services -Repositories -Entities +┌─────────────────────────────────────────────────────────────────┐ +│ AppComponent │ +└────────────────────────────────┬────────────────────────────────┘ + │ + ┌────────────────────────┴────────────────────────┐ + ▼ ▼ +┌───────────────────────┐ ┌───────────────────────┐ +│ AppHeaderComponent │ │ MainLayoutComponent │ +└───────────────────────┘ └───────────┬───────────┘ + │ + ┌───────────────────────────┴───────────────────────────┐ + ▼ ▼ + ┌───────────────────────┐ ┌───────────────────────┐ + │ SidebarComponent │ │ DashboardComponent │ (Smart) + └───────────────────────┘ └───────────┬───────────┘ + │ + ┌───────────────────────────────────────┴───────────────────┐ + ▼ ▼ + ┌───────────────────────┐ ┌───────────────────────┐ + │ EmployeeListComponent │ (Dumb) │ SummaryStatsComponent│ (Dumb) + └───────────┬───────────┘ └───────────────────────┘ + │ + ┌───────────┴───────────┐ + ▼ ▼ + ┌───────────────────────┐ ┌───────────────────────┐ + │ EmployeeCardComponent │ │ EmployeeCardComponent │ + └───────────────────────┘ └───────────────────────┘ ``` -Why? +This tree hierarchy dictates: +- **Change Detection Propagation**: Traversal order for checking updates. +- **Dependency Injection Resolution**: Hierarchical resolution of scoped services. +- **DOM Rendering Hierarchy**: Structural containment in the rendered browser document. -To manage complexity. +--- -Angular applies the same principle to the UI. +# Parent and Child Components: Unidirectional Data Flow -Instead of one giant page: +A fundamental tenet of modern component architecture is **Unidirectional Data Flow**: + +$$\text{Data Flows Down via Inputs} \quad \big\Downarrow \quad \text{Events Flow Up via Outputs}$$ ```text -Application +┌────────────────────────────────────────────────────────┐ +│ Smart / Parent Component │ +│ (Fetches Data, Owns State & Business Logic) │ +└──────────────────────────┬─────────────────────────────┘ + │ + │ [input()] Property Binding (Data Down) + ▼ +┌────────────────────────────────────────────────────────┐ +│ Dumb / Child Component │ +│ (Pure Presentation, Zero External Dependencies) │ +└──────────────────────────┬─────────────────────────────┘ + │ + │ (output()) Event Binding (Events Up) + ▼ +┌────────────────────────────────────────────────────────┐ +│ Parent Handles Event │ +│ (Updates Signal State, Triggers Service Method) │ +└────────────────────────────────────────────────────────┘ ``` -becomes: +1. **Inputs (`input()`)**: The parent passes immutable data properties down to the child. +2. **Outputs (`output()`)**: When the user triggers an action in the child, the child emits an event upwards. The child never directly modifies the parent's state. -```text -Header -Sidebar -Dashboard -Footer -``` +--- -and each becomes a Component. +# Smart (Container) vs Dumb (Presentational) Components + +In enterprise Angular systems, components are strictly split into two patterns: + +| Architectural Dimension | Smart (Container) Components | Dumb (Presentational) Components | +| :--- | :--- | :--- | +| **Primary Responsibility** | Coordinates features, manages routing & state | Renders UI markup, formats data, captures events | +| **Dependency Injection** | Injects domain services, state stores, router | Zero service injections; completely decoupled | +| **Data Ingestion** | Reads from services, APIs, signals, route params | Receives data strictly via `input()` or `@Input()` | +| **Event Emission** | Dispatches actions, mutates state, calls APIs | Emits events strictly via `output()` or `@Output()` | +| **Reusability** | Feature-specific, rarely reused across features | Highly reusable across multiple pages & design systems | +| **Change Detection** | `ChangeDetectionStrategy.OnPush` | `ChangeDetectionStrategy.OnPush` | +| **Testability** | Tested with mock services & injected stores | Unit tested purely via inputs, outputs, and DOM asserts | --- -# Real World Analogy +# Code Example: Modern Angular (Standalone & Signals) -Think about a car manufacturing assembly line. +### 1. Dumb (Presentational) Component +```typescript +import { Component, ChangeDetectionStrategy, input, output } from '@angular/core'; -A car is built from independent parts: +export interface Employee { + id: number; + name: string; + role: string; +} -```text -Engine -Wheels -Seats -Dashboard -Doors +@Component({ + selector: 'app-employee-card', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + template: ` +
+
+

{{ employee().name }}

+ {{ employee().role }} +
+ +
+ `, + styles: [` + .card { border: 1px solid #e2e8f0; padding: 1.25rem; border-radius: 8px; } + .badge { font-size: 0.85rem; color: #4a5568; background: #edf2f7; padding: 2px 8px; border-radius: 4px; } + `] +}) +export class EmployeeCardComponent { + // Required signal input + readonly employee = input.required(); + + // Standard output event + readonly select = output(); +} ``` -Each part has its own responsibility. +### 2. Smart (Container) Component +```typescript +import { Component, ChangeDetectionStrategy, inject } from '@angular/core'; +import { EmployeeService } from './employee.service'; +import { EmployeeCardComponent } from './employee-card.component'; + +@Component({ + selector: 'app-employee-dashboard', + standalone: true, + imports: [EmployeeCardComponent], + changeDetection: ChangeDetectionStrategy.OnPush, + template: ` +
+

Active Team Members ({{ employees().length }})

+ +
+ @for (emp of employees(); track emp.id) { + + } @empty { +

No team members found.

+ } +
+
+ ` +}) +export class EmployeeDashboardComponent { + private readonly employeeService = inject(EmployeeService); + + // Read-only signal state from service + readonly employees = this.employeeService.employees; + + onEmployeeSelected(employeeId: number): void { + this.employeeService.selectEmployee(employeeId); + } +} +``` -The completed car is assembled from these parts. +--- -Angular applications work similarly. +# Common Architectural Mistakes -An application is assembled from Components. +1. **God Components**: Creating monolithic components exceeding 500 lines containing API calls, business logic, form validation, and complex markup. +2. **Mutating Inputs in Children**: Modifying an object passed via input directly in the child, violating unidirectional data flow and causing subtle change detection bugs. +3. **Prop Drilling**: Passing data through 5 layers of intermediate components that don't need it. (Remedy: Use a feature service or state store). +4. **Direct DOM Manipulation**: Bypassing Angular by using `document.querySelector` or raw DOM mutations instead of template bindings, template reference variables (`#ref`), or `ElementRef`. +5. **Omitting OnPush**: Leaving components on the `Default` change detection strategy, forcing Angular to check every single component on every asynchronous event. --- -# Angular's Component Model +# Common Interview Questions -In Angular, every application is built around Components. +## What is Component-Based Architecture? -A Component typically contains: +Component-Based Architecture is a software design methodology where user interfaces are constructed from independent, modular, and reusable units (components) that encapsulate their own view template, TypeScript logic, and scoped styles. -## Template +--- -Defines UI. +## What is the difference between Smart and Dumb components? -```html -

Employee Details

-``` +Smart (Container) components coordinate features, inject business services, manage route parameters, and orchestrate application state. Dumb (Presentational) components are visual; they receive data exclusively through inputs, emit user interactions through outputs, inject zero domain services, and are 100% deterministic and reusable. --- -## Logic +## How do components communicate in Angular? -Defines behavior. +1. **Parent-to-Child**: Property binding via `input()` or `@Input()`. +2. **Child-to-Parent**: Custom event binding via `output()` or `@Output()` event emitters. +3. **Parent-to-Child Direct Access**: Using `viewChild()` or `@ViewChild()` to query a child's public API. +4. **Decoupled / Sibling Communication**: Shared `@Injectable` services exposing Angular Signals, RxJS `BehaviorSubject`, or NgRx state stores. -```typescript -loadEmployees() -``` +--- + +## What is the Component Tree and how does change detection relate to it? + +The Component Tree is the hierarchy of components starting from the root `AppComponent`. Angular traverses this tree from top to bottom during change detection. By using `ChangeDetectionStrategy.OnPush`, Angular skips entire subtrees whose input references have not changed, maximizing runtime rendering efficiency. --- -## Styles +## Why did Angular introduce Signal-based component inputs (`input()`) and outputs (`output()`)? -Defines appearance. +Signal-based inputs produce read-only signals that integrate seamlessly with Angular's fine-grained reactivity system (`computed()`, `effect()`). They guarantee compile-time type safety (via `input.required()`), eliminate boilerplate `ngOnChanges` hooks, and facilitate zoneless, fine-grained DOM updates. -```css -.employee-card { -} -``` +--- + +## How does View Encapsulation work in Angular components? + +Angular provides three View Encapsulation modes: +1. **Emulated (Default)**: Scopes styles to the component by appending unique HTML attributes (e.g. `_ngcontent-c42`) to elements, simulating Shadow DOM. +2. **ShadowDom**: Uses the browser's native Shadow DOM API to achieve true DOM and CSS isolation. +3. **None**: Strips all scoping; component styles are injected globally into the document head. --- -# Component Tree +# Senior-Level Discussion: Backend Parallels & Micro-Frontends -Angular applications are organized in a hierarchy. +In modern enterprise architectures, components represent **bounded contexts on the UI**: +- **Blazor & ASP.NET Parallels**: In .NET, Blazor components (`[Parameter]`, `EventCallback`) operate on identical principles to Angular components (`input()`, `output()`). +- **Micro-Frontend Slicing**: When architecting large enterprise micro-frontends (using Module Federation), components serve as the atomic unit of federation. Container apps import federated remote components across independent release pipelines. -Example: +--- + +# Architecture Considerations ```text -AppComponent -│ -├── HeaderComponent -│ -├── MainLayoutComponent -│ │ -│ ├── SidebarComponent -│ │ -│ ├── DashboardComponent -│ │ │ -│ │ ├── WidgetComponent -│ │ ├── ChartComponent -│ │ └── SummaryComponent -│ -└── FooterComponent + COMPONENT DESIGN CHECKLIST + │ + ┌────────────────────────────┼────────────────────────────┐ + ▼ ▼ ▼ +SINGLE RESPONSIBILITY CLEAN BOUNDARIES ONPUSH BY DEFAULT +- Does this component - Inputs strictly for data - Always enforce OnPush + do exactly ONE thing? - Outputs strictly for event - Use Signals for local state +- Split if > 250 lines - Zero HTTP in dumb comps - Inject services in containers ``` -This structure is called the Component Tree. - --- -# Parent and Child Components +# Key Takeaways -Components often contain other components. +1. **Atomic Building Block**: Components encapsulate template, styles, and TypeScript controller logic into reusable units. +2. **Unidirectional Data Flow**: Data flows down via `input()`, and user events flow up via `output()`. +3. **Smart vs Dumb Separation**: Decoupling business logic (containers) from visual presentation (dumb components) dramatically improves testability and reusability. +4. **Component Hierarchy**: Applications are structured as trees originating from `AppComponent`, driving change detection and dependency injection scoping. +5. **Modern Signal APIs**: Angular's `input()` and `output()` APIs replace legacy `@Input()`/`@Output()` decorators with compile-safe, fine-grained reactive primitives. -Example: +--- -```text -DashboardComponent -│ -├── EmployeeCardComponent -├── EmployeeCardComponent -├── EmployeeCardComponent -``` +# Interview Notes (Revision Version) + +## Definition +Component-Based Architecture divides the user interface into independent, reusable, self-contained building blocks combining HTML templates, TypeScript logic, and scoped styles. -Dashboard becomes the Parent. +## Core Rules +- Unidirectional Data Flow: Data In (`input()`), Events Out (`output()`). +- Smart Components handle business logic, services, and routing. +- Dumb Components are pure, deterministic, and handle only presentation. +- Always use `ChangeDetectionStrategy.OnPush` for optimal performance. -EmployeeCard +## Common Interview Questions +- What is the difference between Smart and Dumb components? +- How do decoupled components communicate? (Shared service with Signals/RxJS) +- Why use Signal inputs over `@Input`? (Compile-time safety, reactive integration, zoneless compatibility). diff --git a/01-foundations/05-typescript-essentials-for-angular.md b/01-foundations/05-typescript-essentials-for-angular.md index 9de9989..338801c 100644 --- a/01-foundations/05-typescript-essentials-for-angular.md +++ b/01-foundations/05-typescript-essentials-for-angular.md @@ -927,8 +927,7 @@ To provide: - Dependency Injection Support - Decorators - Metadata - ---- + --- ## Interface vs Type? @@ -969,7 +968,113 @@ Angular uses metadata to understand how a class should participate in the framew ## How does Dependency Injection benefit from TypeScript? -Type information helps Angular identify and resolve +Type information helps Angular identify and resolve constructor dependencies automatically via reflection metadata (`reflect-metadata` / `emitDecoratorMetadata`). When you inject a service via constructor injection: +```typescript +constructor(private employeeService: EmployeeService) {} +``` +TypeScript emits metadata about the parameter type, enabling Angular's DI container to find and inject the corresponding `EmployeeService` singleton token automatically. In modern Angular, the `inject(EmployeeService)` function also leverages TypeScript generics to infer the return type with zero type casting. + +--- + +## What are Generics and how does Angular rely on them? + +Generics provide a way to create reusable code components that work with a variety of types rather than a single one, while maintaining full compile-time type safety. Angular uses Generics extensively: +- **HttpClient**: `this.http.get('/api/employees')` +- **Signals**: `readonly count = signal(0)` +- **Component Inputs**: `readonly employee = input.required()` +- **RxJS Operators**: `Observable`, `map((data: T) => ...)` + +--- + +## What is the difference between `any`, `unknown`, and `never`? + +| Type | Description | Type Safety | +| :--- | :--- | :--- | +| `any` | Disables all TypeScript type checking; bypasses compiler | None (Dangerous in enterprise codebases) | +| `unknown` | Type-safe counterpart of `any`; value can be anything, but cannot be operated on without type narrowing/checking | High (Requires `typeof`, `instanceof`, or type guards) | +| `never` | Represents values that never occur (e.g., functions that always throw or infinite loops; exhaustive switch cases) | Total (Used for compile-time exhaustiveness checking) | + +--- + +# Senior-Level Discussion: TypeScript as Enterprise Boundary Defense + +For senior developers and architects, TypeScript is not just a syntax convenience—it is the **primary boundary defense mechanism**: + +```text +EXTERNAL WORLD (Untrusted) INTERNAL ANGULAR APPLICATION (Type-Safe) +┌───────────────────────────────┐ ┌────────────────────────────────────────┐ +│ - REST API JSON Payloads │ │ - Strong TypeScript Interfaces & Models│ +│ - LocalStorage strings │───────►│ - Zod / Type-Guard Validation at Gate │ +│ - URL Route Parameters │ Parse │ - 100% deterministic type safety │ +│ - Third-party SDKs │ │ - Zero runtime undefined exceptions │ +└───────────────────────────────┘ └────────────────────────────────────────┘ +``` + +1. **Compile-Time vs Runtime**: TypeScript checks types at compile-time, but TypeScript types are completely erased in the browser bundle. Boundary data from backend APIs should be validated at the HTTP boundary using type guards or validation libraries (like Zod). +2. **Strict Mode Hygiene**: Enterprise Angular projects should always enable `"strict": true`, `"strictNullChecks": true`, and `"noImplicitAny": true` in `tsconfig.json` to prevent null-reference errors. + +--- + +# Architecture Considerations + +```text + TYPESCRIPT ARCHITECTURE RULES + │ + ┌─────────────────────────────┼─────────────────────────────┐ + ▼ ▼ ▼ +NO ANY POLICY EXPLICIT CONTRACTS IMMUTABLE MODELS +- Strictly ban `any` - Define DTOs in domain libs - Mark signal states +- Use `unknown` with guards - Interface for public APIs - Use `readonly` arrays +- Enforce via ESLint rules - Share DTOs with backend - Prevent silent mutations +``` + +--- + +# Diagram: TypeScript Compilation & DI Metadata Pipeline + +```text +┌────────────────────────────────────────────────────────┐ +│ TypeScript Source (.ts files) │ +│ - @Component / @Injectable metadata decorators │ +│ - Static types, Interfaces, Signal definitions │ +└───────────────────────────┬────────────────────────────┘ + │ + Angular Compiler (ngc) + tsc + │ + ┌───────────────────┴───────────────────┐ + ▼ ▼ +┌───────────────────────┐ ┌───────────────────────┐ +│ Clean JavaScript (ESM)│ │ Component Definitions │ +│ - Types erased │ │ - Ivy Instructions │ +│ - Classes retained │ │ - Injected Tokens │ +│ - Runtime execution │ │ - Compiled Templates │ +└───────────────────────┘ └───────────────────────┘ +``` + +--- + +# Key Takeaways + +1. **Enterprise Foundation**: TypeScript gives Angular compile-time type checking, refactoring safety, and autocompletion essential for large multi-team applications. +2. **Dependency Injection Power**: TypeScript types serve as the tokens that power Angular's hierarchical DI container. +3. **Generics Everywhere**: Angular leverages Generics across `HttpClient`, `Signals`, and `RxJS` to ensure end-to-end data contract safety. +4. **Interfaces vs Types**: Use Interfaces for public API and domain entity contracts; use Types for unions, primitives, and complex mapped transformations. +5. **Strict Typing**: Enterprise standards mandate `"strict": true` and strict avoidance of `any`. + +--- + +# Interview Notes (Revision Version) + +## Definition +TypeScript is a strongly typed superset of JavaScript that compiles to plain JavaScript, providing static typing, interfaces, generics, and decorator metadata essential for Angular's enterprise architecture. + +## Key Distinctions +- **Interface vs Type**: Interfaces are extendable object contracts; Types support unions (`|`), intersections (`&`), and utility aliases. +- **`any` vs `unknown`**: `any` disables type checking; `unknown` requires type narrowing before usage. +- **Decorators**: Attach configuration metadata to classes, properties, and parameters. + +## DI Integration +TypeScript parameter types allow Angular to resolve and inject singletons without manual wiring. Modern Angular also uses `inject()` with full generic type inference. diff --git a/02-core-angular/08-directives.md b/02-core-angular/08-directives.md index 2c74b8c..7cfb3a1 100644 --- a/02-core-angular/08-directives.md +++ b/02-core-angular/08-directives.md @@ -204,26 +204,26 @@ Legacy: Structural Directives can: ```text -Create Elements*Remove Elements +Create Elements / Remove Elements Repeat Elements -``* +``` -They change the DOM tree itself.* +They change the DOM tree itself. --- -# Example: Conditional Rende*ing +# Example: Conditional Rendering Modern Angular: ```html -@if *isAdmin) { - } ``` If condition is false: -*```text -Button does not exist in D*M +```text +Button does not exist in DOM ``` Not hidden. @@ -252,7 +252,7 @@ Modern Angular: ```html @for(employee of employees; - track employee.id) * + track employee.id) { } @@ -264,7 +264,7 @@ Angular creates one DOM element per employee. # Legacy Version -`*`html +```html
@@ -300,14 +300,15 @@ which improves readability. Structural Directives: ```text -Add Element* +Add Elements Remove Elements Repeat Elements -`*` +``` They modify DOM structure. ----* +--- + # Interview Question ### What is a Structural Directive? @@ -321,9 +322,9 @@ A Structural Directive modifies the structure of the DOM by adding, removing, or Legacy Example: ```html - + *ngFor="let emp of employees"> ``` @@ -336,22 +337,23 @@ should occur first. Solution: -`*`html - -
+```html + +
- +
``` Common interview question. -*--- +--- # 3. Attribute Directives Attribute Directives modify: -```text*Appearance +```text +Appearance Behavior ``` @@ -375,16 +377,16 @@ Angular changes: ```text CSS Classes -``* +``` -only* +only --- # Another Example ```html -*div [ngStyle]="styles"> +
``` Angular modifies styles. @@ -395,41 +397,42 @@ DOM structure remains unchanged. # Attribute Directive Mental Model -Attribute*Directives: +Attribute Directives: ```text -Element Stays* +Element Stays Behavior Changes -Appearance Chang*s**`` +Appearance Changes +``` --- # ngClass -One of the mos* common Attribute Directives. +One of the most common Attribute Directives. -Exa*ple: +Example: ```typescript -statusClass = *success'; +statusClass = 'success'; ``` Template: ```html -*div [ngClass]="statusClass"> - S*ved Successfully +
+ Saved Successfully
``` -Angul*r updates CSS classes dynamically.* +Angular updates CSS classes dynamically. --- # ngStyle Example: -```type*cript +```typescript styles = { color:'green' *; @@ -448,62 +451,62 @@ Angular applies styles dynamically. # Property Binding vs Attribute Directives A frequent source of confusion. -*-- +--- ## Native DOM Property -```htm* +```html ``` -Targe*: +Target: ```javascript img.src ``` -Nati*e browser property. +Native browser property. --- -## Direc*ive Input +## Directive Input ```html -
+
``` Target: -```typesc*ipt +```typescript NgClass Directive ``` -Angular*passes the value to the directive.* -The directive decides how to upda*e the DOM. +Angular passes the value to the directive. +The directive decides how to update the DOM. --- -# Visual Comparis*n +# Visual Comparison ```text [src] ↓ -Native DOM Pro*erty +Native DOM Property -------------------- -[ngCla*s] +[ngClass] ↓ Angular Directive ↓ -Directi*e Manipulates DOM +Directive Manipulates DOM ``` --- -# Mode*n Angular Control Flow +# Modern Angular Control Flow -Modern Ang*lar: +Modern Angular: ```html @if @@ -511,31 +514,31 @@ Modern Ang*lar: @for ``` -Prefe*red approach. +Preferred approach. --- -# Legacy Angul*r Control Flow +# Legacy Angular Control Flow Older Angular: -``*html +```html *ngIf *ngFor ``` -Still comm*nly encountered. +Still commonly encountered. -Interviewers may*ask both. +Interviewers may ask both. --- -# Built-In Directi*es Commonly Used +# Built-In Directives Commonly Used ## Structural -M*dern: +Modern: ```html @if @@ -543,7 +546,7 @@ M*dern: @for ``` -Lega*y: +Legacy: ```html *ngIf @@ -551,97 +554,100 @@ Lega*y: *ngFor ``` ----* +--- + ## Attribute ```html [ngClass] -*ngStyle] +[ngStyle] ``` --- -# Custom Direct*ves +# Custom Directives -Angular allows creation of cu*tom directives. +Angular allows creation of custom directives. Example: -```type*cript +```typescript @Directive({ - selector:'[a*pHighlight]' + selector: '[appHighlight]' }) -export class Highl*ghtDirective {} +export class HighlightDirective {} ``` Usage: -```ht*l +```html
``` -Purpose:* +Purpose: ```text Reusable DOM behavior -```* +``` Common enterprise use cases: -- H*ghlighting +- Highlighting - Auto Focus -- Permissi*n Based UI +- Permission Based UI - Input Formatting ----* +--- + # Real Enterprise Example -Employ*e Portal +Employee Portal ```html -@if(user.canDele*e) +@if(user.canDelete) { } -*`` +``` Structural Directive. --- -``*html +```html
``` -Where *oes: +Where does: ```typescript *ngIf @@ -289,136 +289,140 @@ Where *oes: Not your component. -*t comes*from: +It comes from: ```typescript CommonModule -*`` +``` Therefore: ```typescript -impo*ts: [ - Common*odule +imports: [ + CommonModule ] ``` -*-- +--- # Another Example Template: -*``html +```html
-<*form> + ``` Requires: -```typescrip* +```typescript ReactiveFormsModule ``` inside: -*```typescript + +```typescript imports:[] ``` --- -*# Mental Model -```text*imports +## Mental Model + +```text +imports = -Things*Others Created +Things Others Created -That*I Want To Use +That I Want To Use ``` -*-- +--- # exports -*# Purpose +## Purpose -Make things*available to other modules. +Make things available to other modules. --- -*xample +Example ```typescript @NgModule({ -* declarations: [ + declarations: [ - Employee*omponent + EmployeeComponent ], exports: [ - *EmployeeComponent + EmployeeComponent ] }) ``` ---* +--- -*ow other modules can use: +Now other modules can use: -```html* +```html + -```* +``` --- # Why exports Exists -Withou* exports: +Without exports: ```text Module A ``` -c*nnot automatically use: +cannot automatically use: ```text -M*dule B Components +Module B Components ``` --- -# Ment*l Model +# Mental Model ```text exports = -Make *ublic +Make Public ``` --- # providers -Usual*y the most misunderstood section. -*--- +Usually the most misunderstood section. +--- # Purpose -Register services *ith Angular's Dependency Injection*container. +Register services with Angular's Dependency Injection container. --- Example Service -*``typescript +```typescript @Injectable() -export *lass EmployeeService {} +export class EmployeeService {} ``` --- @@ -426,10 +430,10 @@ export *lass EmployeeService {} *egister ```typescript -@NgModule({* +@NgModule({ providers: [ - EmployeeServi*e + EmployeeService ] @@ -440,55 +444,56 @@ export *lass EmployeeService {} # Why? -Compo*ent: +Component: ```typescript constructor( * private employeeService: - *Employee*ervice + EmployeeService -**{} -*`` +}) +``` Angular asks: ```text -Who Pro*ides EmployeeService? +Who Provides EmployeeService? ``` -Answer:* +Answer: ```typescript providers:[ - Empl*yeeService + EmployeeService ] ``` --- -# Mental Mo*el +# Mental Model ```text providers = -Dependen*y Injection Registrations +Dependency Injection Registrations ``` ----* +--- + # Dependency Injection Flow -```t*xt +```text Component ↓ asks for -Empl*yeeService +EmployeeService ↓ -Angular DI Cont*iner +Angular DI Container ↓ @@ -499,65 +504,65 @@ Service Created ↓ -Service I*jected +Service Injected ``` --- -# Complete Exampl* +# Complete Example ```typescript @NgModule({ - dec*arations: [ + declarations: [ - EmployeeComponent* + EmployeeComponent ], imports: [ - CommonMod*le, + CommonModule, ReactiveFormsModule - ],* + ], providers: [ - EmployeeServi*e + EmployeeService ] }) -export class EmployeeMo*ule {} +export class EmployeeModule {} ``` --- -# Feature Modules* -Large applications commonly organ*ze features into modules. +# Feature Modules +Large applications commonly organize features into modules. --- -Ex*mple +Example ```text EmployeeModule -Admi*Module +AdminModule ReportsModule -SettingsMod*le +SettingsModule ``` --- Employee Module -```t*pescript +```typescript @NgModule({ - declaratio*s: [ + declarations: [ EmployeeListComponent, @@ -565,40 +570,41 @@ Employee Module ] -*) +}) export class EmployeeModule {} -`*` +``` --- # Shared Module -Common pa*tern. +Common pattern. Contains reusable: -```text*Components +```text +Components Directives Pipes ``` -*--- +--- Example ```typescript -@NgMod*le({ +@NgModule({ declarations: [ - Lo*dingSpinnerComponent, + LoadingSpinnerComponent, - Searc*BoxComponent + SearchBoxComponent ], - exports: [* + exports: [ LoadingSpinnerComponent, * SearchBoxComponent @@ -606,87 +612,88 @@ Example ] }) -`*` +``` --- Used across application. -*-- +--- # Core Module Pattern -Often c*ntains: +Often contains: ```text -Singleton Service* +Singleton Services Authentication Interceptors -Gl*bal Configuration +Global Configuration ``` --- -Exampl* +Example ```typescript CoreModule ``` -lo*ded once for the application. +loaded once for the application. + +--- ----* # Common Interview Questions -## *hat Is An NgModule? +## What Is An NgModule? -Organizationa* container that registers componen*s, directives, pipes, dependencies* and services. +Organizational container that registers components, directives, pipes, dependencies, and services. --- -## Difference*Between declarations And imports? +## Difference Between declarations And imports? *Declarations: ```text -Things I Cr*ated +Things I Created ``` Imports: ```text -Things*Others Created +Things Others Created ``` -that I want to*use. +that I want to use. --- -## What Goes Into provi*ers? +## What Goes Into providers? -Services registered for Depe*dency Injection. +Services registered for Dependency Injection. --- -## What Are*exports? +## What Are exports? -Things other modules are*allowed to use. +Things other modules are allowed to use. --- -# Common Int*rview Traps +# Common Interview Traps ## Trap 1 -Putting se*vices into declarations. +Putting services into declarations. -Incorrec*. +Incorrect. Services belong in: -```typescr*pt +```typescript providers ``` @@ -694,48 +701,49 @@ providers ## Trap 2 -*eclaring same component in multipl* modules. +Declaring same component in multiple modules. Not allowed. --- -## T*ap 3 +## Trap 3 -Confusing imports with decla*ations. +Confusing imports with declarations. --- -# Senior-Level Summa*y +# Senior-Level Summary -NgModules acted as Angular's re*istration and organization mechani*m. +NgModules acted as Angular's registration and organization mechanism. They told Angular: ```text -Wh*t Exists +What Exists What Is Shared -What Can*Be Used +What Can Be Used What Can Be Injected ``` -*For many years they were the backb*ne of Angular architecture. +For many years they were the backbone of Angular architecture. --- -* Key Takeaways +## Key Takeaways -1. NgModules organ*zed Angular applications. -2. decla*ations register components, direct*ves, and pipes. -3. imports bring i* external functionality. -4. export* make functionality available to o*her modules. -5. providers register*services for dependency injection.*6. Feature Modules improve scalabi*ity. -7. Shared Modules support reu*e. -8. Core Modules provide applica*ion-wide services. -9. NgModules re*ain important in many enterprise a*plications. -10. Understanding NgMo*ules makes Standalone Components e*sier to understand. +1. NgModules organized Angular applications. +2. declarations register components, directives, and pipes. +3. imports bring in external functionality. +4. exports make functionality available to other modules. +5. providers register services for dependency injection. +6. Feature Modules improve scalability. +7. Shared Modules support reuse. +8. Core Modules provide application-wide services. +9. NgModules remain important in many enterprise applications. +10. Understanding NgModules makes Standalone Components easier to understand. diff --git a/05-advanced/20-standalone-components.md b/05-advanced/20-standalone-components.md index 1d1ecfd..31b1c42 100644 --- a/05-advanced/20-standalone-components.md +++ b/05-advanced/20-standalone-components.md @@ -1,125 +1,125 @@ # Standalone Components -## I*terview Priority +## Interview Priority -**Must Know (Ang*lar 15+)** +**Must Know (Angular 15+)** -## Interview Frequency* +## Interview Frequency **Very Common** -## Recommended D*pth +## Recommended Depth **Expert Level Understanding** ## Relevant For -- Modern Angula* Development +- Modern Angular Development - Angular 15+ -- Angul*r 16+ +- Angular 16+ - Angular 17+ -- Senior Devel*per Interviews +- Senior Developer Interviews --- -# Why Were St*ndalone Components Introduced? +# Why Were Standalone Components Introduced? -Th*s is the most important question. -*--- +This is the most important question. +--- # Problem With NgModules -To *reate one component: +To create one component: -```typescrip* +```typescript EmployeeComponent ``` -Angular re*uired: +Angular required: ```text Component ↓ -F*ature Module +Feature Module ↓ App Module -```* -Lots of registration and boilerpl*te. +``` +Lots of registration and boilerplate. --- # Angular's Question -Wh* should components require modules*to exist? +Why should components require modules to exist? --- Traditional: -```t*xt +```text Declare Component -Register Com*onent +Register Component Export Component -Import Mo*ule +Import Module Use Component ``` --- -Angul*r wanted: +Angular wanted: ```text Component ↓ -U*e Component +Use Component ``` Much simpler. ---* +--- -# What Is A Standalone Component* +# What Is A Standalone Component? -A component that can exist witho*t being declared in an NgModule. +A component that can exist without being declared in an NgModule. -*-- +--- Example ```typescript -@Compon*nt({ +@Component({ standalone: true, - templ*te: ` + template: `

Hello

` }) -e*port class EmployeeComponent { +export class EmployeeComponent { } -`*` +``` --- # The Biggest Change -Trad*tional Angular: +Traditional Angular: ```typescript -@Ng*odule({ +@NgModule({ declarations:[ - Emp*oyeeComponent + EmployeeComponent ] }) @@ -127,52 +127,52 @@ Trad*tional Angular: --- -St*ndalone Angular: +Standalone Angular: ```typescript -@C*mponent({ +@Component({ - standalone:true + standalone: true }) -`*` +``` No declaration required. --- -* What Happened To declarations? +## What Happened To declarations? -I* disappeared. +It disappeared. --- Why? ```text -*omponent Already Knows -It Is A Com*onent +Component Already Knows +It Is A Component ``` -Angular no longer needs* +Angular no longer needs ```typescript declarations:[] -``* +``` --- -# Imports In Standalone Com*onents +# Imports In Standalone Components Imports still exist. -But *ove closer to where they are used.* +But move closer to where they are used. --- Old ```typescript -@NgModule*{ +@NgModule({ imports:[ CommonModule @@ -185,7 +185,8 @@ Old New -```typescript*@Component({ +```typescript +@Component({ standalone:true, @@ -200,61 +201,61 @@ New # Real Example -```typ*script +```typescript @Component({ - standalone:*rue, + standalone: true, imports:[ - CommonMod*le, + CommonModule, ReactiveFormsModule * }) -export class EmployeeComponen* {} +export class EmployeeComponent {} ``` --- -Template now support*: +Template now supports: ```html *ngIf *ngFor -formGrou* +formGroup ``` -because dependencies were im*orted directly. +because dependencies were imported directly. --- -# Mental Mod*l +# Mental Model ```text Standalone Imports = -*hat This Component Uses +What This Component Uses ``` --- -* Direct Component Imports +## Direct Component Imports -Huge ch*nge. +Huge change. --- Traditional ```text -Co*ponent +Component ↓ @@ -263,13 +264,13 @@ Module ↓ Module -```* +``` --- Standalone ```text -Componen* +Component ↓ @@ -279,10 +280,11 @@ Component --- Example -*```typescript + +```typescript @Component({ - stan*alone:true, + standalone: true, imports:[ @@ -293,79 +295,79 @@ Example }) ``` --*- +--- Now template can use: ```html -*app-employee-card> - + + ``` directly. --- -# What H*ppened To exports? +# What Happened To exports? -Mostly unneces*ary. +Mostly unnecessary. --- Old ```typescript -expo*ts:[ +exports: [ EmployeeComponent ] ``` --*- +--- New ```typescript imports:[ - *EmployeeComponent + EmployeeComponent ] ``` -Direct us*ge. +Direct usage. No export layer. --- -# Depe*dency Injection +# Dependency Injection -Providers still e*ist. +Providers still exist. --- # Preferred Approach -`*`typescript +```typescript @Injectable({ - provi*edIn:'root' + providedIn: 'root' }) -export class Emplo*eeService {} +export class EmployeeService {} ``` --- -Application*wide singleton. +Application-wide singleton. --- -# Component *cope Providers +# Component Scope Providers Possible. --- -Ex*mple +Example ```typescript @Component({ @@ -381,24 +383,24 @@ Ex*mple }) ``` -*-- +--- Meaning: ```text -New Service *nstance +New Service Instance For This Component Tree -`*` +``` --- # Bootstrapping -Old Angul*r: +Old Angular: ```typescript -platformBrowserD*namic() +platformBrowserDynamic() .bootstrapModule( AppModule ); diff --git a/05-advanced/21-routing-and-navigation.md b/05-advanced/21-routing-and-navigation.md index bc9476a..fbcb282 100644 --- a/05-advanced/21-routing-and-navigation.md +++ b/05-advanced/21-routing-and-navigation.md @@ -1367,11 +1367,11 @@ should usually be the **st route. --- -# Senior-Level Me**al Model +# Senior-Level Mental Model Think of routing as: -`**text +```text URL ↓ @@ -1380,18 +1380,18 @@ Angular Router ↓ -R**te Configuration +Route Configuration ↓ -Matched Comp**ent +Matched Component ↓ router-outlet ``` -Everyt**ng else: +Everything else: ```text Parameters @@ -1402,17 +1402,17 @@ Resolvers Guards Lazy Loading -**` +``` -builds on top of that foundati**. +builds on top of that foundation. --- # Key Takeaways -1. Angul** Router maps URLs to components. -** Routing enables SPA navigation w**hout page reloads. -3. Routes defi** URL-to-component mappings. +1. Angular Router maps URLs to components. +2. Routing enables SPA navigation without page reloads. +3. Routes define URL-to-component mappings. 4. router-outlet is where routed components render. 5. routerLink is used for template navigation. 6. router.navigate() is used for programmatic navigation. diff --git a/05-advanced/27-http-interceptors.md b/05-advanced/27-http-interceptors.md index 894b2a6..3008aa3 100644 --- a/05-advanced/27-http-interceptors.md +++ b/05-advanced/27-http-interceptors.md @@ -981,13 +981,13 @@ Request ID Interceptor ```typescript -const request = - req.clone({ - - setHeaders: { - - 'X-Correlation-ID': - crypto.randomUUID +const request = req.clone({ + setHeaders: { + 'X-Correlation-ID': crypto.randomUUID() + } +}); +return next(request); +``` diff --git a/05-advanced/29-performance-optimization.md b/05-advanced/29-performance-optimization.md index 7107a68..b824d9c 100644 --- a/05-advanced/29-performance-optimization.md +++ b/05-advanced/29-performance-optimization.md @@ -451,36 +451,36 @@ Very common interview question. Angular executes: -*``typescript +```typescript calculateTotal() ``` -*during every change detection cycl*. +during every change detection cycle. --- Example ```typescript -cal*ulateTotal() { +calculateTotal() { - console.log('exe*uted'); + console.log('executed'); - return this.items.lengt*; + return this.items.length; } ``` --- -Potentially hundred* of executions. +Potentially hundreds of executions. --- # Better -``*typescript +```typescript totalItems = - computed* + computed( () => this.items().length )* ``` @@ -490,48 +490,49 @@ totalItems = Template ```html -{{ to*alItems() }} +{{ totalItems() }} ``` --- -# Optimizat*on #6: Pure Pipes +# Optimization #6: Pure Pipes -Instead of reca*culating repeatedly. +Instead of recalculating repeatedly. --- -# Avoid* +# Avoid + ```html {{ getFullName(user) }} -`*` +``` --- # Better ```html -{{ user * fullName }} +{{ user | fullName }} ``` --- -Implementat*on +Implementation ```typescript @Pipe({ - name: *fullName', + name: 'fullName', pure: true }) -export *lass FullNamePipe -implements PipeT*ansform { +export class FullNamePipe +implements PipeTransform { transform( - user: *ser + user: User ): string { - return `${u*er.firstName} - ${user.l*stName}`; + return `${user.firstName} + ${user.lastName}`; } @@ -540,45 +541,45 @@ implements PipeT*ansform { --- -# Why *ure Pipes? +# Why Pure Pipes? Execute only when: -``*text +```text Inputs Change ``` --- -# Ben*fits +# Benefits ```text Less CPU Work -Clean*r Templates +Cleaner Templates -Predictable Performan*e +Predictable Performance ``` --- -# Optimization #7: Mem*ization +# Optimization #7: Memoization -Useful for expensive calc*lations. +Useful for expensive calculations. --- Example -```typescr*pt +```typescript calculateReport( - reportId: st*ing + reportId: string ) { // expensive logic } -`*` +``` --- diff --git a/06-senior-architecture/33-error-handling-strategy.md b/06-senior-architecture/33-error-handling-strategy.md index 814f705..cc46b28 100644 --- a/06-senior-architecture/33-error-handling-strategy.md +++ b/06-senior-architecture/33-error-handling-strategy.md @@ -10,7 +10,7 @@ ## Recommended Depth -**Senior**rchitect**evel Understanding** +**Senior / Architect Level Understanding** ## Relevant For diff --git a/08-cheat-sheets/03-signals-cheat-sheet.md b/08-cheat-sheets/03-signals-cheat-sheet.md index c85b7cf..e8058ba 100644 --- a/08-cheat-sheets/03-signals-cheat-sheet.md +++ b/08-cheat-sheets/03-signals-cheat-sheet.md @@ -1,7 +1,8 @@ # Signals Cheat Sheet -# Core APIs +## Core APIs +```text signal() computed() diff --git a/apps/portal/.editorconfig b/apps/portal/.editorconfig new file mode 100644 index 0000000..f166060 --- /dev/null +++ b/apps/portal/.editorconfig @@ -0,0 +1,17 @@ +# Editor configuration, see https://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.ts] +quote_type = single +ij_typescript_use_double_quotes = false + +[*.md] +max_line_length = off +trim_trailing_whitespace = false diff --git a/apps/portal/.gitignore b/apps/portal/.gitignore new file mode 100644 index 0000000..854acd5 --- /dev/null +++ b/apps/portal/.gitignore @@ -0,0 +1,44 @@ +# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files. + +# Compiled output +/dist +/tmp +/out-tsc +/bazel-out + +# Node +/node_modules +npm-debug.log +yarn-error.log + +# IDEs and editors +.idea/ +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# Visual Studio Code +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/mcp.json +.history/* + +# Miscellaneous +/.angular/cache +.sass-cache/ +/connect.lock +/coverage +/libpeerconnection.log +testem.log +/typings +__screenshots__/ + +# System files +.DS_Store +Thumbs.db diff --git a/apps/portal/.prettierrc b/apps/portal/.prettierrc new file mode 100644 index 0000000..d6c16d7 --- /dev/null +++ b/apps/portal/.prettierrc @@ -0,0 +1,12 @@ +{ + "printWidth": 100, + "singleQuote": true, + "overrides": [ + { + "files": "*.html", + "options": { + "parser": "angular" + } + } + ] +} diff --git a/apps/portal/.vscode/extensions.json b/apps/portal/.vscode/extensions.json new file mode 100644 index 0000000..77b3745 --- /dev/null +++ b/apps/portal/.vscode/extensions.json @@ -0,0 +1,4 @@ +{ + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846 + "recommendations": ["angular.ng-template"] +} diff --git a/apps/portal/.vscode/launch.json b/apps/portal/.vscode/launch.json new file mode 100644 index 0000000..925af83 --- /dev/null +++ b/apps/portal/.vscode/launch.json @@ -0,0 +1,20 @@ +{ + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "ng serve", + "type": "chrome", + "request": "launch", + "preLaunchTask": "npm: start", + "url": "http://localhost:4200/" + }, + { + "name": "ng test", + "type": "chrome", + "request": "launch", + "preLaunchTask": "npm: test", + "url": "http://localhost:9876/debug.html" + } + ] +} diff --git a/apps/portal/.vscode/tasks.json b/apps/portal/.vscode/tasks.json new file mode 100644 index 0000000..244306f --- /dev/null +++ b/apps/portal/.vscode/tasks.json @@ -0,0 +1,42 @@ +{ + // For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558 + "version": "2.0.0", + "tasks": [ + { + "type": "npm", + "script": "start", + "isBackground": true, + "problemMatcher": { + "owner": "typescript", + "pattern": "$tsc", + "background": { + "activeOnStart": true, + "beginsPattern": { + "regexp": "Changes detected" + }, + "endsPattern": { + "regexp": "bundle generation (complete|failed)" + } + } + } + }, + { + "type": "npm", + "script": "test", + "isBackground": true, + "problemMatcher": { + "owner": "typescript", + "pattern": "$tsc", + "background": { + "activeOnStart": true, + "beginsPattern": { + "regexp": "Changes detected" + }, + "endsPattern": { + "regexp": "bundle generation (complete|failed)" + } + } + } + } + ] +} diff --git a/apps/portal/README.md b/apps/portal/README.md new file mode 100644 index 0000000..7493677 --- /dev/null +++ b/apps/portal/README.md @@ -0,0 +1,59 @@ +# Portal + +This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 22.0.7. + +## Development server + +To start a local development server, run: + +```bash +ng serve +``` + +Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files. + +## Code scaffolding + +Angular CLI includes powerful code scaffolding tools. To generate a new component, run: + +```bash +ng generate component component-name +``` + +For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run: + +```bash +ng generate --help +``` + +## Building + +To build the project run: + +```bash +ng build +``` + +This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed. + +## Running unit tests + +To execute unit tests with the [Vitest](https://vitest.dev/) test runner, use the following command: + +```bash +ng test +``` + +## Running end-to-end tests + +For end-to-end (e2e) testing, run: + +```bash +ng e2e +``` + +Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs. + +## Additional Resources + +For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page. diff --git a/apps/portal/angular.json b/apps/portal/angular.json new file mode 100644 index 0000000..92babe3 --- /dev/null +++ b/apps/portal/angular.json @@ -0,0 +1,76 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "cli": { + "packageManager": "npm" + }, + "newProjectRoot": "projects", + "projects": { + "portal": { + "projectType": "application", + "schematics": {}, + "root": "", + "sourceRoot": "src", + "prefix": "app", + "architect": { + "build": { + "builder": "@angular/build:application", + "options": { + "browser": "src/main.ts", + "tsConfig": "tsconfig.app.json", + "assets": [ + { + "glob": "**/*", + "input": "public" + } + ], + "styles": [ + "src/styles.css" + ], + "allowedCommonJsDependencies": [ + "prismjs" + ] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "600kB", + "maximumError": "1MB" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "4kB", + "maximumError": "8kB" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "builder": "@angular/build:dev-server", + "configurations": { + "production": { + "buildTarget": "portal:build:production" + }, + "development": { + "buildTarget": "portal:build:development" + } + }, + "defaultConfiguration": "development" + }, + "test": { + "builder": "@angular/build:unit-test" + } + } + } + } +} diff --git a/apps/portal/docs/architecture.md b/apps/portal/docs/architecture.md new file mode 100644 index 0000000..e05e6a3 --- /dev/null +++ b/apps/portal/docs/architecture.md @@ -0,0 +1,27 @@ +# Angular Interview Prep Portal — Architecture Specification + +## 1. Executive Summary +The **Angular Interview Prep Portal** is a production-grade web application built to render, search, and interact with 60+ in-depth Angular architecture and interview guides. + +## 2. Core Architectural Tenets +1. **Single Source of Truth (SSOT)**: + - The markdown files in the parent directory (`01-foundations/` through `08-cheat-sheets/`) remain the sole content source. + - Content is never duplicated into Angular templates or compiled static HTML. +2. **Hybrid Content Pipeline (CQRS / Read Model)**: + - **Query / Read Index (`content-manifest.json`)**: A build-time node script creates a 35KB manifest indexing topic metadata, categories, flashcards, and search tokens. + - **Document Stream**: Full markdown content is fetched on demand via `HttpClient` when a user navigates to a topic. +3. **Reactivity & State**: + - Built on modern **Angular Signals** (`signal()`, `computed()`, `effect()`, `input()`, `output()`). + - `ChangeDetectionStrategy.OnPush` enforced across all components. + - Zoneless-ready architecture. +4. **Security & Sanitization**: + - `DOMPurify` pre-sanitization before `DomSanitizer.bypassSecurityTrustHtml()`. + - Host-level `MarkdownLinkInterceptorDirective` to convert internal markdown file links into seamless client-side SPA routing. + +## 3. Layer Separation +- **`core/`**: Layout shell (Header, Sidebar, Drawer), singleton services (`ContentService`, `ThemeService`, `SearchEngineService`), state facades. +- **`shared/`**: Presentational UI controls (Liquid Glass buttons, tabs, modal dialogs, badges, pipes, directives). +- **`features/`**: + - `topic-reader`: Multi-mode reader container (Deep Dive, Quick Recap, Flashcards). + - `search-modal`: Instant `Cmd+K` global index search. + - `visualizers`: Interactive Signals vs RxJS and Change Detection trees. diff --git a/apps/portal/docs/design-system.md b/apps/portal/docs/design-system.md new file mode 100644 index 0000000..b2a0280 --- /dev/null +++ b/apps/portal/docs/design-system.md @@ -0,0 +1,63 @@ +# Liquid Glass Design System Specification + +## 1. Typography +- **UI & Prose**: `Plus Jakarta Sans` / `Inter`, system fallback. +- **Code & Syntax**: `JetBrains Mono` / `Fira Code`, monospace fallback. + +## 2. Liquid Glass Tokens (`:root`) + +```css +:root { + /* Dimensions & Offsets */ + --header-height: 76px; + --sticky-offset: calc(var(--header-height) + 0.75rem); + --sidebar-width: 320px; + --drawer-width: 400px; + + /* Fonts */ + --font-sans: 'Plus Jakarta Sans', system-ui, -apple-system, sans-serif; + --font-mono: 'JetBrains Mono', 'Fira Code', monospace; + + /* Transitions */ + --transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1); + --transition-smooth: 300ms cubic-bezier(0.16, 1, 0.3, 1); +} + +/* Light Theme */ +[data-theme='light'] { + --bg-gradient: radial-gradient(circle at 10% 20%, rgba(241, 245, 249, 0.8) 0%, rgba(248, 250, 252, 1) 90%); + --bg-surface: #ffffff; + --text-primary: #0f172a; + --text-secondary: #475569; + --text-muted: #94a3b8; + + --glass-bg: rgba(255, 255, 255, 0.78); + --glass-bg-accent: linear-gradient(135deg, rgba(14, 165, 233, 0.08) 0%, rgba(255, 255, 255, 0.8) 100%); + --glass-border: 1px solid rgba(226, 232, 240, 0.85); + --glass-border-cyan: 1px solid rgba(14, 165, 233, 0.4); + --glass-blur: blur(16px); + --glass-shadow: 0 8px 32px 0 rgba(15, 23, 42, 0.06), inset 0 1px 1px 0 rgba(255, 255, 255, 0.9); + --glass-shadow-glow: 0 8px 32px 0 rgba(14, 165, 233, 0.12), inset 0 1px 1px 0 rgba(255, 255, 255, 0.9); + --accent-cyan: #0284c7; + --accent-cyan-glow: rgba(2, 132, 199, 0.2); +} + +/* Dark Theme (Liquid Glass Default) */ +[data-theme='dark'] { + --bg-gradient: radial-gradient(circle at 15% 15%, #0f172a 0%, #030712 100%); + --bg-surface: #0a0f1d; + --text-primary: #f8fafc; + --text-secondary: #94a3b8; + --text-muted: #64748b; + + --glass-bg: linear-gradient(135deg, rgba(255, 255, 255, 0.07) 0%, rgba(255, 255, 255, 0.02) 100%), rgba(15, 23, 42, 0.55); + --glass-bg-accent: linear-gradient(135deg, rgba(0, 243, 255, 0.06) 0%, rgba(15, 23, 42, 0.6) 100%), rgba(15, 23, 42, 0.55); + --glass-border: 1px solid rgba(255, 255, 255, 0.12); + --glass-border-cyan: 1px solid rgba(0, 243, 255, 0.35); + --glass-blur: blur(16px); + --glass-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.45), inset 0 1px 1px 0 rgba(255, 255, 255, 0.12); + --glass-shadow-glow: 0 8px 32px 0 rgba(0, 0, 0, 0.45), 0 0 20px rgba(0, 243, 255, 0.18), inset 0 1px 1px 0 rgba(255, 255, 255, 0.2); + --accent-cyan: #00f3ff; + --accent-cyan-glow: rgba(0, 243, 255, 0.25); +} +``` diff --git a/apps/portal/docs/progress-tracker.md b/apps/portal/docs/progress-tracker.md new file mode 100644 index 0000000..bbd78b9 --- /dev/null +++ b/apps/portal/docs/progress-tracker.md @@ -0,0 +1,41 @@ +# Phase 2 Implementation & Progress Tracker + +## Milestone Overview + +- [x] **Phase 2.1: Foundation & Project Scaffolding** + - [x] Initialize standalone Angular app in `apps/portal/` + - [x] Install core runtime dependencies (`@lucide/angular`, `marked`, `dompurify`, `prismjs`) + - [x] Set up app-specific documentation & tracking (`apps/portal/docs/`) + - [x] Configure `tsconfig.json` path aliases (`@core/*`, `@shared/*`, `@features/*`) + - [x] Configure `angular.json` asset mappings to expose root markdown directories via sync script + - [x] Implement Liquid Glass CSS token system and theme variables in `src/styles.css` + - [x] Add web fonts (Plus Jakarta Sans & JetBrains Mono) to `src/index.html` + +- [x] **Phase 2.2: Markdown Pipeline & Manifest Generator** + - [x] Create `scripts/generate-manifest.mjs` to extract headings, flashcards, search tokens, and sync content + - [x] Implement `ThemeService` (Signals-based theme toggle with localStorage persistence) + - [x] Implement `ContentService` (Manifest loading, markdown streaming) + - [x] Implement `MarkdownSanitizerService` (DOMPurify + DomSanitizer + Prism syntax highlighting) + - [x] Implement `SearchService` (Instant client-side fuzzy search) + - [x] Implement `ReaderFacade` (Signals state store, track filters, topic completion) + +- [x] **Phase 2.3: Layout & Liquid Glass Shell** + - [x] Build `HeaderComponent` (brand, search trigger, difficulty filter, theme switch, progress pill) + - [x] Build `SidebarComponent` (collapsible category tree, active route indicator, completion checkmarks) + - [x] Build `QuickRecapDrawerComponent` (cheat sheet slideout) + - [x] Build `SearchModalComponent` (global `Ctrl+K` instant search) + - [x] Assemble `ShellComponent` layout + +- [x] **Phase 2.4: Reader Experience & Multi-View Tabs** + - [x] Build `TopicReaderComponent` (Deep Dive, Quick Recap, Flashcards, Interactive Lab tabs) + - [x] Build `MarkdownViewerComponent` prose rendering with syntax highlighting & link interceptor + - [x] Build `FlashcardDeckComponent` with interactive reveal & deck progress + - [x] Build `RecapSummaryComponent` for 5-minute cram + +- [x] **Phase 2.5: Interactive Visualizers** + - [x] Build `SignalsGraphVisualizerComponent` (live reactive graph simulation with increment/reset) + - [x] Build `ChangeDetectionTreeVisualizerComponent` (Default vs OnPush vs Zoneless traversal) + +- [x] **Phase 2.6: Test Suite & Polish** + - [x] Verification & build run (`ng build` passing with 0 errors) + - [x] Browser session verification at `http://localhost:4200` diff --git a/apps/portal/package-lock.json b/apps/portal/package-lock.json new file mode 100644 index 0000000..77ad246 --- /dev/null +++ b/apps/portal/package-lock.json @@ -0,0 +1,8132 @@ +{ + "name": "portal", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "portal", + "version": "0.0.0", + "dependencies": { + "@angular/common": "^22.0.0", + "@angular/compiler": "^22.0.0", + "@angular/core": "^22.0.0", + "@angular/forms": "^22.0.0", + "@angular/platform-browser": "^22.0.0", + "@angular/router": "^22.0.0", + "dompurify": "^3.4.15", + "marked": "^18.0.13", + "prismjs": "^1.30.0", + "rxjs": "~7.8.0", + "tslib": "^2.3.0" + }, + "devDependencies": { + "@angular/build": "^22.0.7", + "@angular/cli": "^22.0.7", + "@angular/compiler-cli": "^22.0.0", + "@types/dompurify": "^3.0.5", + "@types/prismjs": "^1.26.6", + "jsdom": "^28.0.0", + "prettier": "^3.8.1", + "typescript": "~6.0.2", + "vitest": "^4.0.8" + } + }, + "node_modules/@acemir/cssom": { + "version": "0.9.31", + "resolved": "https://registry.npmjs.org/@acemir/cssom/-/cssom-0.9.31.tgz", + "integrity": "sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@angular-devkit/architect": { + "version": "0.2201.8", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2201.8.tgz", + "integrity": "sha512-EUQo8RDS1my2Bo5FRS+gBYgz1/klfIp9XESfMpTBO04nBkjF6DkPCeOxeAf1CYjWy9nCXcWn968eSdEhV5jXiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "22.1.8", + "rxjs": "7.8.2" + }, + "bin": { + "architect": "bin/cli.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-devkit/core": { + "version": "22.1.8", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-22.1.8.tgz", + "integrity": "sha512-34lsgg2FwMVBX7nR/ZqzRUcdvdYPvSB8XAEr2GudXAyZwLI9ehzfQlagAWR/gEb8p4uCFZW0r7uU0toxydq4Rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "8.20.0", + "ajv-formats": "3.0.1", + "jsonc-parser": "3.3.1", + "picomatch": "4.0.5", + "rxjs": "7.8.2", + "source-map": "0.7.6" + }, + "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" + }, + "peerDependencies": { + "chokidar": "^5.0.0" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/schematics": { + "version": "22.1.8", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-22.1.8.tgz", + "integrity": "sha512-Pv3cPa/44kvEwYcqwx4Ns5dhIDmcFNSHhbpheqiEG1Z/u4e2t4zHKDtE3eHZB+8o+IcC7xJj+d+AqGR44RoZDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "22.1.8", + "jsonc-parser": "3.3.1", + "magic-string": "1.0.0", + "ora": "9.4.1", + "rxjs": "7.8.2" + }, + "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/build": { + "version": "22.1.8", + "resolved": "https://registry.npmjs.org/@angular/build/-/build-22.1.8.tgz", + "integrity": "sha512-tw+Evk0EITb8p8dTu743ONoFztCEiyDkdRbtZsh7C0vBS7Q9ElsOcFeg2Z9OAAwrmtZMIBuW3jrdPRQp7OCSdw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "2.3.0", + "@angular-devkit/architect": "0.2201.8", + "@babel/core": "8.0.1", + "@babel/helper-annotate-as-pure": "8.0.0", + "@babel/helper-split-export-declaration": "7.24.7", + "@inquirer/confirm": "6.1.1", + "@vitejs/plugin-basic-ssl": "2.3.0", + "beasties": "0.4.3", + "browserslist": "^4.26.0", + "esbuild": "0.28.2", + "https-proxy-agent": "9.1.0", + "jsonc-parser": "3.3.1", + "listr2": "11.0.0", + "magic-string": "1.0.0", + "mrmime": "2.0.1", + "oxc-parser": "0.142.0", + "parse5-html-rewriting-stream": "8.0.1", + "picomatch": "4.0.5", + "piscina": "5.2.0", + "rolldown": "1.2.0", + "sass": "1.101.0", + "semver": "7.8.5", + "source-map-support": "0.5.21", + "tinyglobby": "0.2.17", + "vite": "8.1.5", + "watchpack": "2.5.2" + }, + "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" + }, + "optionalDependencies": { + "lmdb": "3.5.6" + }, + "peerDependencies": { + "@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.1.8", + "istanbul-lib-instrument": "^6.0.0", + "karma": "^6.4.0", + "less": "^4.2.0", + "ng-packagr": "^22.0.0", + "postcss": "^8.4.0", + "rollup": "^4.0.0", + "tailwindcss": "^2.0.0 || ^3.0.0 || ^4.0.0", + "tslib": "^2.3.0", + "typescript": ">=6.0 <6.1", + "vitest": "^4.0.8" + }, + "peerDependenciesMeta": { + "@angular/core": { + "optional": true + }, + "@angular/localize": { + "optional": true + }, + "@angular/platform-browser": { + "optional": true + }, + "@angular/platform-server": { + "optional": true + }, + "@angular/service-worker": { + "optional": true + }, + "@angular/ssr": { + "optional": true + }, + "istanbul-lib-instrument": { + "optional": true + }, + "karma": { + "optional": true + }, + "less": { + "optional": true + }, + "ng-packagr": { + "optional": true + }, + "postcss": { + "optional": true + }, + "rollup": { + "optional": true + }, + "tailwindcss": { + "optional": true + }, + "vitest": { + "optional": true + } + } + }, + "node_modules/@angular/cli": { + "version": "22.1.8", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-22.1.8.tgz", + "integrity": "sha512-MFN/FydqDndai8vF12hDxG5RlBt1HUUheeJgJNmuHYIne4bOAfOT+xuMpIK8+gLSRwCemdqR3OMM38pnsFJ/nA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/architect": "0.2201.8", + "@angular-devkit/core": "22.1.8", + "@angular-devkit/schematics": "22.1.8", + "@inquirer/prompts": "8.5.2", + "@listr2/prompt-adapter-inquirer": "4.2.5", + "@modelcontextprotocol/sdk": "1.30.0", + "@schematics/angular": "22.1.8", + "jsonc-parser": "3.3.1", + "listr2": "11.0.0", + "npm-package-arg": "14.0.0", + "parse5-html-rewriting-stream": "8.0.1", + "semver": "7.8.5", + "yargs": "18.1.0", + "zod": "4.4.3" + }, + "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.1.7", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-22.1.7.tgz", + "integrity": "sha512-SLW4AdH2Y8G9SM/iFRNpCQtkmvq55RcIQWsADT9ypZ7FxxcnJRsr4Q/WnbUBbuCslsCkrgzU1i9WDiW0m7gcnw==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^22.22.3 || ^24.15.0 || >=26.0.0" + }, + "peerDependencies": { + "@angular/core": "22.1.7", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/compiler": { + "version": "22.1.7", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-22.1.7.tgz", + "integrity": "sha512-qhoHAByh805nT4Xy4Wd2kE5FvkTc7wcg8Qps+EH6+z4KRfwGiL5cwdgooGWjTAldFCeqTgJ0BO/0dB6GHZjdkQ==", + "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.1.7", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-22.1.7.tgz", + "integrity": "sha512-VGg8Kyt0YymQfnUUFk79vFaM+x5xmIMHy+BZh3P2Lh/uxqCXTWhJJH8kOHKtIGGH5/wKhBEmSy2CezdcIuM9xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "8.0.1", + "@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.1.7", + "typescript": ">=6.0 <6.1" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@angular/core": { + "version": "22.1.7", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-22.1.7.tgz", + "integrity": "sha512-T9bt1PIgoN7n+TxGHTUgu0EsomlGMQ84YMXbMB+N6HhD8C1qZxn0uCbnAtCUjcecg1am8ioKxNTO0eII9ouioQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^22.22.3 || ^24.15.0 || >=26.0.0" + }, + "peerDependencies": { + "@angular/compiler": "22.1.7", + "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.1.7", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-22.1.7.tgz", + "integrity": "sha512-oc0DT39C3ZboJpDx8xKUCltgn56LHi0kbv+ThNK/dCSZLgjF2nd+muMMRYF+amdljQp2q7+hm2ORmhON3CNFog==", + "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.1.7", + "@angular/core": "22.1.7", + "@angular/platform-browser": "22.1.7", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/platform-browser": { + "version": "22.1.7", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-22.1.7.tgz", + "integrity": "sha512-4dT3qGw3ZHVQUcHKALzMx9MTo+pxAMzHIPxc8HUESklqXfq6GRaIIfFMvE0RsquI9iqu6admD6lOAEzLWS8DxA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^22.22.3 || ^24.15.0 || >=26.0.0" + }, + "peerDependencies": { + "@angular/animations": "22.1.7", + "@angular/common": "22.1.7", + "@angular/core": "22.1.7" + }, + "peerDependenciesMeta": { + "@angular/animations": { + "optional": true + } + } + }, + "node_modules/@angular/router": { + "version": "22.1.7", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-22.1.7.tgz", + "integrity": "sha512-CxYa2Ym5L0WmqToHU2I0Z4VEpocdryLlvx5svLfo9M3uOaP3/9icB/gjS/vXPbbQxV8/GZ59E/7/LF14anIoRg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^22.22.3 || ^24.15.0 || >=26.0.0" + }, + "peerDependencies": { + "@angular/common": "22.1.7", + "@angular/core": "22.1.7", + "@angular/platform-browser": "22.1.7", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@asamuzakjp/css-color": { + "version": "5.1.11", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.11.tgz", + "integrity": "sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/generational-cache": "^1.0.1", + "@csstools/css-calc": "^3.2.0", + "@csstools/css-color-parser": "^4.1.0", + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "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/generational-cache": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/generational-cache/-/generational-cache-1.0.1.tgz", + "integrity": "sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "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": "8.0.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-8.0.0.tgz", + "integrity": "sha512-dYYg153EyN2Ekbqw2zAsbd6/JR+9N2SEoC7YV2GyyqMM7x9bLDTjBD6XBhSMLH0wtIVyJj03jWNriQhaN+eoCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^8.0.0", + "js-tokens": "^10.0.0" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-8.0.5.tgz", + "integrity": "sha512-YLsYoQMvL8l8WrGpN3Zj7O1wK5LEBN+cQtux7BcuHyxIXve724XG+zuJ1n3U1cUweRtTzQOA4IHbuQw3N34SZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@babel/core": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-8.0.1.tgz", + "integrity": "sha512-5FgxM4dLQpMJHSiVATk8foW263dVHQHBVpXYiimNECVWG01f4nFyEbQixeT6Mwvg7TayREJ2gpKl3o2RoMdnqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^8.0.0", + "@babel/generator": "^8.0.0", + "@babel/helper-compilation-targets": "^8.0.0", + "@babel/helpers": "^8.0.0", + "@babel/parser": "^8.0.0", + "@babel/template": "^8.0.0", + "@babel/traverse": "^8.0.0", + "@babel/types": "^8.0.0", + "@types/gensync": "^1.0.5", + "convert-source-map": "^2.0.0", + "empathic": "^2.0.1", + "gensync": "^1.0.0-beta.2", + "import-meta-resolve": "^4.2.0", + "json5": "^2.2.3", + "obug": "^2.1.1", + "semver": "^7.7.3" + }, + "engines": { + "node": "^22.18.0 || >=24.11.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/generator": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-8.0.5.tgz", + "integrity": "sha512-f/TuhuMAxJqhwxEGNsJrswuG9VHmh0oNFoQoo6TbpgtFAz9wYZXcTAcWZMHfp7ljesr0RG04bp3Aos9GI59L7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^8.0.5", + "@babel/types": "^8.0.5", + "@jridgewell/gen-mapping": "0.4.0-beta.0", + "@jridgewell/trace-mapping": "^0.3.31", + "@types/jsesc": "^2.5.0", + "jsesc": "^3.0.2" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { + "version": "0.4.0-beta.0", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.4.0-beta.0.tgz", + "integrity": "sha512-JdGNkbE4GlNPYQhM0L95fBQr7ctLZJ276QXQLTad4t1oSdnnCI3fDq9DW3BqYAWv8Wc3+HS+4Gsii1oPMCfz1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.6.0-beta.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-8.0.0.tgz", + "integrity": "sha512-NSpMkMsvvZqzThJ0p1B02cbtA2ObEyfBvq950bmNkyxsxvcxwhvvCB036rKhlEnuBBo30bOrk13u3FzlKSoRrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^8.0.0" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-8.0.5.tgz", + "integrity": "sha512-Qk8ahMGooH5mz6uuhoDvfZGkUf/Mf3RTBucVVl4MKx4LKMTv872TeW8O92h15iVtlN8wAROBIpI1aV6x1z0LCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^8.0.5", + "@babel/helper-validator-option": "^8.0.0", + "browserslist": "^4.24.0", + "lru-cache": "^11.0.0", + "verkit": "^0.3.2" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-8.0.0.tgz", + "integrity": "sha512-lLozHOM6sWWlxNo8CYqHy4MBZeTvHXNgVPBfPOGsjPKUzHC2Az9QwB6gxdQmpwHl6GlQtbGgS+lj5887guDiLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^22.18.0 || >=24.11.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-split-export-declaration/node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration/node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration/node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-8.0.0.tgz", + "integrity": "sha512-6mJgmFFFIIO82vvoLt9XtRC7/TkzXfts1t/SpRX4IHSzMgqoPYCWesVu1udUPUWioAE/2fcG6WuI8zrkE1gwrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.4.tgz", + "integrity": "sha512-4wFaiLd0bVo4cIoTXI3zKI038NIWE/cr3jvBjejOVYVxV/m8Ltav1USiGzG1fmS5J2RhgEOgXNNK46cRPnRsrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-8.0.0.tgz", + "integrity": "sha512-U4Dybxh4WESWHt5XhBeExi4DrY0/DNK1aHpQbsrQXCUbFHuMweT0TpLEWKvaraV2Y6fS+ZXunsZ8zIuZIgvF2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@babel/helpers": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-8.0.5.tgz", + "integrity": "sha512-fQtPOXjYOYv85PIdwotp2TJGVYOycX0PQq+l844fFAxOULtBy8BVF35GyeueX0r4KvDthqPH5xAI1clQPk/2uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^8.0.0", + "@babel/types": "^8.0.5" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@babel/parser": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-8.0.5.tgz", + "integrity": "sha512-51RXvQNFakaS0bTpYiGkxNbUVwkPO4kONv6EVLorZABxsx+KZ6Z7uSYvi/wmKS/+X+rfj9RvOw0/ZNh+cmI0Rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^8.0.5" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@babel/template": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-8.0.0.tgz", + "integrity": "sha512-eAD0QW/AlbamBbw0FeGiwasbCVPq5ncW0HNVyLP3B9czqLyh4gvw+5JTSNt6le9+ziAU7mqDZsKTHf3jTb4chQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^8.0.0", + "@babel/parser": "^8.0.0", + "@babel/types": "^8.0.0" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@babel/traverse": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-8.0.5.tgz", + "integrity": "sha512-XFfnuvapSc/vJOcUO7kwORSvpBIvraofKEZ2dhT0PjiF21BRCD7YbAFC8UEeDJNeLoQz82/gVqzgX5hCzkCbdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^8.0.0", + "@babel/generator": "^8.0.5", + "@babel/helper-globals": "^8.0.0", + "@babel/parser": "^8.0.5", + "@babel/template": "^8.0.0", + "@babel/types": "^8.0.5", + "obug": "^2.1.1" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@babel/types": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-8.0.5.tgz", + "integrity": "sha512-eVdMqi3ej5aHhyQ2Si6yD2cAWeV8FJK9UrhK5aL0Sd8hu5GhT+YswhVNbVheOGVYMg8kuGuMaUpkB3stjj4z8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^8.0.0", + "@babel/helper-validator-identifier": "^8.0.4" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@bramus/specificity": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz", + "integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-tree": "^3.0.0" + }, + "bin": { + "specificity": "bin/cli.js" + } + }, + "node_modules/@csstools/color-helpers": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.1.1.tgz", + "integrity": "sha512-gLNsunvwf3mCi5u5o46/Z/JcJMnhbHSaZ69rkgPzNM3J4s8hWwpPUQB6/tt0EDFyCiWzxANlx+2LJwpYj4zS1w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@csstools/css-calc": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.4.0.tgz", + "integrity": "sha512-XQKj5B7QiZcHiegCOCAzcAOJdhGgWOHbbu62h5e5mkHnn8lWcfiJhllkqWmxu5zWR9jucPHuo1iTB56P033hcg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.2.3.tgz", + "integrity": "sha512-y4LpL+lmpuyKDiEFq2PnZUVFdAjsoB/qQJod79yLNokXyW7jewi+/WJ69EfItj8A2unWtxXnGjw6LYXgXu5ZjA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^6.1.1", + "@csstools/css-calc": "^3.4.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", + "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.14.tgz", + "integrity": "sha512-HpbVXyrofRXpHpgkNIjU/3EWR4WJvOkO3emNK/L6X/mTJU7bGUI3AkkpoTNXznQLp0KRjLHELTGeKI5dIkI9JQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "peerDependencies": { + "css-tree": "^3.2.1" + }, + "peerDependenciesMeta": { + "css-tree": { + "optional": true + } + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", + "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.2.tgz", + "integrity": "sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz", + "integrity": "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@exodus/bytes": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.1.tgz", + "integrity": "sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@noble/hashes": "^1.8.0 || ^2.0.0" + }, + "peerDependenciesMeta": { + "@noble/hashes": { + "optional": true + } + } + }, + "node_modules/@harperfast/extended-iterable": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@harperfast/extended-iterable/-/extended-iterable-1.0.3.tgz", + "integrity": "sha512-sSAYhQca3rDWtQUHSAPeO7axFIUJOI6hn1gjRC5APVE1a90tuyT8f5WIgRsFhhWA7htNkju2veB9eWL6YHi/Lw==", + "dev": true, + "license": "Apache-2.0", + "optional": true + }, + "node_modules/@hono/node-server": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.1.1.tgz", + "integrity": "sha512-ELuehkj5VCBdgEw9zs+ivkKwyzzUCSQuE96YmiPvn1ECBoZCczbFXJLeEGMTYjphP6gydh4pHMqEYPVMYUVgQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "hono": "^4" + } + }, + "node_modules/@inquirer/ansi": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-2.0.8.tgz", + "integrity": "sha512-WpQM+Ti6Z40EFwwt+uL2p4UabT+W179zHp6HhLVOzfbwnVn05IPO/eXIZXGNqcT1jbQ15SujNLzQ39k4QPPxBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + } + }, + "node_modules/@inquirer/checkbox": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-5.2.5.tgz", + "integrity": "sha512-bRt8J8m+Fot9CXv+zNQGXUq2ET0MggR1fPz7v6edN6MFYmsbfGnMmkmWZJEegMKqrAC8ej/o1sqisHZXZJMAfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^2.0.8", + "@inquirer/core": "^12.0.3", + "@inquirer/figures": "^2.0.9", + "@inquirer/type": "4.1.1" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/checkbox/node_modules/@inquirer/core": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-12.0.3.tgz", + "integrity": "sha512-wsSy0sznmXwkty+2PzZwx00Cazc/E0r0B7mAzdGROz2Ct+DFZXaK7WDjGZvgjRldxH5ZhFVfF2lgkYrqgOw2KA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^2.0.8", + "@inquirer/figures": "^2.0.9", + "@inquirer/type": "4.1.1", + "cli-width": "^4.1.0", + "fast-wrap-ansi": "^0.2.0", + "mute-stream": "^3.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/confirm": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-6.1.1.tgz", + "integrity": "sha512-eb8DBZcz/2qHWQda4rk2JiQk5h9QV/cVHi1yjt0f69WFZMRFn0sJTye3EAP8icut8UDMjQPsaH5KbcOogefrFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^11.2.1", + "@inquirer/type": "^4.0.7" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core": { + "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": "^2.0.7", + "@inquirer/figures": "^2.0.7", + "@inquirer/type": "^4.0.7", + "cli-width": "^4.1.0", + "fast-wrap-ansi": "^0.2.0", + "mute-stream": "^3.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/editor": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-5.3.3.tgz", + "integrity": "sha512-YsKkS2q63IiLtaDK/9nqzdComN97SDQrmKiyNggN+ceP4ty+Z6VwyTz3FpjeUWeW1Efss2xHFKCC9sx7hnrsxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^12.0.3", + "@inquirer/external-editor": "^3.0.5", + "@inquirer/type": "4.1.1" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/editor/node_modules/@inquirer/core": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-12.0.3.tgz", + "integrity": "sha512-wsSy0sznmXwkty+2PzZwx00Cazc/E0r0B7mAzdGROz2Ct+DFZXaK7WDjGZvgjRldxH5ZhFVfF2lgkYrqgOw2KA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^2.0.8", + "@inquirer/figures": "^2.0.9", + "@inquirer/type": "4.1.1", + "cli-width": "^4.1.0", + "fast-wrap-ansi": "^0.2.0", + "mute-stream": "^3.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/expand": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-5.1.5.tgz", + "integrity": "sha512-uHuXLmXW+TtIfT/9vSBotypAkqn1n34Ul+CLGPos/xANyO4Ff5xZzkYhbKR4NEcfVK4a9mHQOpwVZzluSHFRGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^12.0.3", + "@inquirer/type": "4.1.1" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/expand/node_modules/@inquirer/core": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-12.0.3.tgz", + "integrity": "sha512-wsSy0sznmXwkty+2PzZwx00Cazc/E0r0B7mAzdGROz2Ct+DFZXaK7WDjGZvgjRldxH5ZhFVfF2lgkYrqgOw2KA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^2.0.8", + "@inquirer/figures": "^2.0.9", + "@inquirer/type": "4.1.1", + "cli-width": "^4.1.0", + "fast-wrap-ansi": "^0.2.0", + "mute-stream": "^3.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/external-editor": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-3.0.5.tgz", + "integrity": "sha512-f3QQJRIX5ZEneBHNUIuPjmbdzHnmRFJA8r2dkcb8q+OM5Uv5KtnuAttQumnrjcBVBM3mcTX1CkmtAkU58VRZxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chardet": "^2.1.1", + "iconv-lite": "^0.7.2" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/figures": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-2.0.9.tgz", + "integrity": "sha512-EAWgUTGQ/Umgga51dE3B2PUHbufuXarDfg86uVgoSgNHNNQnyFKcOrQLWVqYMghuSyHh8+2HUH0Js9cTC1WAdg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + } + }, + "node_modules/@inquirer/input": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-5.1.6.tgz", + "integrity": "sha512-HtcJhB2QFVXbLuJ5S3syhNbTUVxYvwqV4VRBDkQceBloC9bmTViUoRFP5PbSaDZb3HzfPmpuU/gG4ybVBz4FHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^12.0.3", + "@inquirer/type": "4.1.1" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/input/node_modules/@inquirer/core": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-12.0.3.tgz", + "integrity": "sha512-wsSy0sznmXwkty+2PzZwx00Cazc/E0r0B7mAzdGROz2Ct+DFZXaK7WDjGZvgjRldxH5ZhFVfF2lgkYrqgOw2KA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^2.0.8", + "@inquirer/figures": "^2.0.9", + "@inquirer/type": "4.1.1", + "cli-width": "^4.1.0", + "fast-wrap-ansi": "^0.2.0", + "mute-stream": "^3.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/number": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-4.2.3.tgz", + "integrity": "sha512-6Yuwh1NGSbu1Lo4N1EWjXs1jKRntLg/ZCwhmeorEHde90v1XxAozdbd4Iu30eOQLW+6h1hp2O9ujNfLSbTPJnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^12.0.3", + "@inquirer/type": "4.1.1" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/number/node_modules/@inquirer/core": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-12.0.3.tgz", + "integrity": "sha512-wsSy0sznmXwkty+2PzZwx00Cazc/E0r0B7mAzdGROz2Ct+DFZXaK7WDjGZvgjRldxH5ZhFVfF2lgkYrqgOw2KA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^2.0.8", + "@inquirer/figures": "^2.0.9", + "@inquirer/type": "4.1.1", + "cli-width": "^4.1.0", + "fast-wrap-ansi": "^0.2.0", + "mute-stream": "^3.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/password": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-5.2.2.tgz", + "integrity": "sha512-W9zYdyzogK+6110mqwaSJWCBu2yA5Q/OfnGSjjZB1bNpHlmUozXxTl0+QOZBNeVd6Qo81/qT75gW05gLAtITxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^2.0.8", + "@inquirer/core": "^12.0.3", + "@inquirer/type": "4.1.1" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/password/node_modules/@inquirer/core": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-12.0.3.tgz", + "integrity": "sha512-wsSy0sznmXwkty+2PzZwx00Cazc/E0r0B7mAzdGROz2Ct+DFZXaK7WDjGZvgjRldxH5ZhFVfF2lgkYrqgOw2KA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^2.0.8", + "@inquirer/figures": "^2.0.9", + "@inquirer/type": "4.1.1", + "cli-width": "^4.1.0", + "fast-wrap-ansi": "^0.2.0", + "mute-stream": "^3.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/prompts": { + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-8.5.2.tgz", + "integrity": "sha512-IYR/3C/paEVVQYQvdDlFZVjRCJVYHHON0XXMH91KO9GSxs0TdKYWlUdvfQl2EfAHDxUaN3IBffkE/BDTh5nJ6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/checkbox": "^5.2.1", + "@inquirer/confirm": "^6.1.1", + "@inquirer/editor": "^5.2.2", + "@inquirer/expand": "^5.1.1", + "@inquirer/input": "^5.1.2", + "@inquirer/number": "^4.1.1", + "@inquirer/password": "^5.1.1", + "@inquirer/rawlist": "^5.3.1", + "@inquirer/search": "^4.2.1", + "@inquirer/select": "^5.2.1" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/rawlist": { + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-5.3.5.tgz", + "integrity": "sha512-1oHky1ONfCOwNrnkQGDE1oaSij/3fI6HFMSf2H/WsGO2lEyDX9My82iggITSy9ddSZ8yk8j9v41OI0fVoSIoaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^12.0.3", + "@inquirer/type": "4.1.1" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/rawlist/node_modules/@inquirer/core": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-12.0.3.tgz", + "integrity": "sha512-wsSy0sznmXwkty+2PzZwx00Cazc/E0r0B7mAzdGROz2Ct+DFZXaK7WDjGZvgjRldxH5ZhFVfF2lgkYrqgOw2KA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^2.0.8", + "@inquirer/figures": "^2.0.9", + "@inquirer/type": "4.1.1", + "cli-width": "^4.1.0", + "fast-wrap-ansi": "^0.2.0", + "mute-stream": "^3.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/search": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-4.3.3.tgz", + "integrity": "sha512-fyuIU1Nbpvwlikjg3gXwJFDI11+EFjqQ7P+iByfmivIKQ1vmaykNrD/vy5unHuUqUpsOsnvJ25//tPF7E/RBRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^12.0.3", + "@inquirer/figures": "^2.0.9", + "@inquirer/type": "4.1.1" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/search/node_modules/@inquirer/core": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-12.0.3.tgz", + "integrity": "sha512-wsSy0sznmXwkty+2PzZwx00Cazc/E0r0B7mAzdGROz2Ct+DFZXaK7WDjGZvgjRldxH5ZhFVfF2lgkYrqgOw2KA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^2.0.8", + "@inquirer/figures": "^2.0.9", + "@inquirer/type": "4.1.1", + "cli-width": "^4.1.0", + "fast-wrap-ansi": "^0.2.0", + "mute-stream": "^3.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/select": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-5.2.5.tgz", + "integrity": "sha512-9kc15hr8r/kI+3DO/xLog5nOzTz1jqsHXa6JBFzmQKhkoJ8Slda1I1L/uD8ZSZ9tF1yp79wwXe7mclvX1rqR2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^2.0.8", + "@inquirer/core": "^12.0.3", + "@inquirer/figures": "^2.0.9", + "@inquirer/type": "4.1.1" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/select/node_modules/@inquirer/core": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-12.0.3.tgz", + "integrity": "sha512-wsSy0sznmXwkty+2PzZwx00Cazc/E0r0B7mAzdGROz2Ct+DFZXaK7WDjGZvgjRldxH5ZhFVfF2lgkYrqgOw2KA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^2.0.8", + "@inquirer/figures": "^2.0.9", + "@inquirer/type": "4.1.1", + "cli-width": "^4.1.0", + "fast-wrap-ansi": "^0.2.0", + "mute-stream": "^3.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/type": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-4.1.1.tgz", + "integrity": "sha512-yJoHYrMnxIsJZCY+0Vb66Dy3he3kL3e2wOBKhoSwWWAzZAY82emlxwgprCtp6yRixvNRNq9ztfRWQYPNr3Go7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@listr2/prompt-adapter-inquirer": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@listr2/prompt-adapter-inquirer/-/prompt-adapter-inquirer-4.2.5.tgz", + "integrity": "sha512-pYGy9dTdTwXdasPgyohkr0HoQ4FrkAzFnsUZl/gcnadDArbpZ8e+fgr+F9WBdNEl2y00mb9bCM4WgmoBkZJ27A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/type": "^4.0.7" + }, + "engines": { + "node": ">=22.13.0" + }, + "peerDependencies": { + "@inquirer/prompts": ">= 3 < 9", + "listr2": "11.0.0" + } + }, + "node_modules/@lmdb/lmdb-darwin-arm64": { + "version": "3.5.6", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-3.5.6.tgz", + "integrity": "sha512-mY5FG4TjPAkY4P0w+OhHaUka5mDh2TX2WKYIwuKzJ1zeW3VvRgxdam/lGJTquI+bthTx5CSHDW+BAQCnNAzkEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@lmdb/lmdb-darwin-x64": { + "version": "3.5.6", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-3.5.6.tgz", + "integrity": "sha512-foa+pwitysO8k+xhs7psBFfTKnVgR69NlZRRTHaFVDqphh7AdGpLeyRzKw/ofatr/sN6TiHRRW6mmop0ZrrppQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@lmdb/lmdb-linux-arm": { + "version": "3.5.6", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-3.5.6.tgz", + "integrity": "sha512-QR4YRyR5h5Z8eGXrNQjiyo2NNDfqi3tCc9dQG5Is1blCt+qWw1ZoBWhlWAr5d+jshkifMIJjVHzHGKbkKzF8Tw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lmdb/lmdb-linux-arm64": { + "version": "3.5.6", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-3.5.6.tgz", + "integrity": "sha512-HmiyFFdJa38s1heCMSooSPaBSFTHJ3C+ERPp28xAPlDX1YiALJVOgbry065nXd8Y7KISWjnw05zpG1RX8IfftA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lmdb/lmdb-linux-x64": { + "version": "3.5.6", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-3.5.6.tgz", + "integrity": "sha512-ADzCuCF2cTNiX9kDScqcz1fjnAkxPpQNneV3KFTdV3wWtVlI2sTGzySoMTgDpinkMMFj1NTJlxA6XR8fwc4hlA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lmdb/lmdb-win32-arm64": { + "version": "3.5.6", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-arm64/-/lmdb-win32-arm64-3.5.6.tgz", + "integrity": "sha512-J7A9aEQsQiv0TYtBGL7NDIPp2lOS8nnl+zm4sWZm1xlsTTaQ4PgD096Adzdrk27rw3UxCkDXdCUa4ax41oztBQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@lmdb/lmdb-win32-x64": { + "version": "3.5.6", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-3.5.6.tgz", + "integrity": "sha512-1g7G0knRX2iV/voDu54yxrGqw5Dk0w2oIYb7dgJq8IkOi+m7wbD8Q3QpPFjh0C01G58S88dqGn03len6UPCXsg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.30.0.tgz", + "integrity": "sha512-xKd8OIzlqNzcqcNumGAa6g+PW2kjD5vrpcKOnfldAUPP3j7lnqMPwlTXQm8gF+UwH72z0lqaRbjr9hqGz0eITA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@hono/node-server": "^1.19.9 || ^2.0.5", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.2.1", + "express-rate-limit": "^8.2.1", + "hono": "^4.11.4", + "jose": "^6.1.3", + "json-schema-typed": "^8.0.2", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } + } + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { + "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" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@napi-rs/nice": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice/-/nice-1.1.1.tgz", + "integrity": "sha512-xJIPs+bYuc9ASBl+cvGsKbGrJmS6fAKaSZCnT0lhahT5rhA2VVy9/EcIgd2JhtEuFOJNx7UHNn/qiTPTY4nrQw==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "optionalDependencies": { + "@napi-rs/nice-android-arm-eabi": "1.1.1", + "@napi-rs/nice-android-arm64": "1.1.1", + "@napi-rs/nice-darwin-arm64": "1.1.1", + "@napi-rs/nice-darwin-x64": "1.1.1", + "@napi-rs/nice-freebsd-x64": "1.1.1", + "@napi-rs/nice-linux-arm-gnueabihf": "1.1.1", + "@napi-rs/nice-linux-arm64-gnu": "1.1.1", + "@napi-rs/nice-linux-arm64-musl": "1.1.1", + "@napi-rs/nice-linux-ppc64-gnu": "1.1.1", + "@napi-rs/nice-linux-riscv64-gnu": "1.1.1", + "@napi-rs/nice-linux-s390x-gnu": "1.1.1", + "@napi-rs/nice-linux-x64-gnu": "1.1.1", + "@napi-rs/nice-linux-x64-musl": "1.1.1", + "@napi-rs/nice-openharmony-arm64": "1.1.1", + "@napi-rs/nice-win32-arm64-msvc": "1.1.1", + "@napi-rs/nice-win32-ia32-msvc": "1.1.1", + "@napi-rs/nice-win32-x64-msvc": "1.1.1" + } + }, + "node_modules/@napi-rs/nice-android-arm-eabi": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-android-arm-eabi/-/nice-android-arm-eabi-1.1.1.tgz", + "integrity": "sha512-kjirL3N6TnRPv5iuHw36wnucNqXAO46dzK9oPb0wj076R5Xm8PfUVA9nAFB5ZNMmfJQJVKACAPd/Z2KYMppthw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-android-arm64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-android-arm64/-/nice-android-arm64-1.1.1.tgz", + "integrity": "sha512-blG0i7dXgbInN5urONoUCNf+DUEAavRffrO7fZSeoRMJc5qD+BJeNcpr54msPF6qfDD6kzs9AQJogZvT2KD5nw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-darwin-arm64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-darwin-arm64/-/nice-darwin-arm64-1.1.1.tgz", + "integrity": "sha512-s/E7w45NaLqTGuOjC2p96pct4jRfo61xb9bU1unM/MJ/RFkKlJyJDx7OJI/O0ll/hrfpqKopuAFDV8yo0hfT7A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-darwin-x64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-darwin-x64/-/nice-darwin-x64-1.1.1.tgz", + "integrity": "sha512-dGoEBnVpsdcC+oHHmW1LRK5eiyzLwdgNQq3BmZIav+9/5WTZwBYX7r5ZkQC07Nxd3KHOCkgbHSh4wPkH1N1LiQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-freebsd-x64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-freebsd-x64/-/nice-freebsd-x64-1.1.1.tgz", + "integrity": "sha512-kHv4kEHAylMYmlNwcQcDtXjklYp4FCf0b05E+0h6nDHsZ+F0bDe04U/tXNOqrx5CmIAth4vwfkjjUmp4c4JktQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-arm-gnueabihf": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm-gnueabihf/-/nice-linux-arm-gnueabihf-1.1.1.tgz", + "integrity": "sha512-E1t7K0efyKXZDoZg1LzCOLxgolxV58HCkaEkEvIYQx12ht2pa8hoBo+4OB3qh7e+QiBlp1SRf+voWUZFxyhyqg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-arm64-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm64-gnu/-/nice-linux-arm64-gnu-1.1.1.tgz", + "integrity": "sha512-CIKLA12DTIZlmTaaKhQP88R3Xao+gyJxNWEn04wZwC2wmRapNnxCUZkVwggInMJvtVElA+D4ZzOU5sX4jV+SmQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-arm64-musl": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm64-musl/-/nice-linux-arm64-musl-1.1.1.tgz", + "integrity": "sha512-+2Rzdb3nTIYZ0YJF43qf2twhqOCkiSrHx2Pg6DJaCPYhhaxbLcdlV8hCRMHghQ+EtZQWGNcS2xF4KxBhSGeutg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-ppc64-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-ppc64-gnu/-/nice-linux-ppc64-gnu-1.1.1.tgz", + "integrity": "sha512-4FS8oc0GeHpwvv4tKciKkw3Y4jKsL7FRhaOeiPei0X9T4Jd619wHNe4xCLmN2EMgZoeGg+Q7GY7BsvwKpL22Tg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-riscv64-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-riscv64-gnu/-/nice-linux-riscv64-gnu-1.1.1.tgz", + "integrity": "sha512-HU0nw9uD4FO/oGCCk409tCi5IzIZpH2agE6nN4fqpwVlCn5BOq0MS1dXGjXaG17JaAvrlpV5ZeyZwSon10XOXw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-s390x-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-s390x-gnu/-/nice-linux-s390x-gnu-1.1.1.tgz", + "integrity": "sha512-2YqKJWWl24EwrX0DzCQgPLKQBxYDdBxOHot1KWEq7aY2uYeX+Uvtv4I8xFVVygJDgf6/92h9N3Y43WPx8+PAgQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-x64-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-x64-gnu/-/nice-linux-x64-gnu-1.1.1.tgz", + "integrity": "sha512-/gaNz3R92t+dcrfCw/96pDopcmec7oCcAQ3l/M+Zxr82KT4DljD37CpgrnXV+pJC263JkW572pdbP3hP+KjcIg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-x64-musl": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-x64-musl/-/nice-linux-x64-musl-1.1.1.tgz", + "integrity": "sha512-xScCGnyj/oppsNPMnevsBe3pvNaoK7FGvMjT35riz9YdhB2WtTG47ZlbxtOLpjeO9SqqQ2J2igCmz6IJOD5JYw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-openharmony-arm64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-openharmony-arm64/-/nice-openharmony-arm64-1.1.1.tgz", + "integrity": "sha512-6uJPRVwVCLDeoOaNyeiW0gp2kFIM4r7PL2MczdZQHkFi9gVlgm+Vn+V6nTWRcu856mJ2WjYJiumEajfSm7arPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-win32-arm64-msvc": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-arm64-msvc/-/nice-win32-arm64-msvc-1.1.1.tgz", + "integrity": "sha512-uoTb4eAvM5B2aj/z8j+Nv8OttPf2m+HVx3UjA5jcFxASvNhQriyCQF1OB1lHL43ZhW+VwZlgvjmP5qF3+59atA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-win32-ia32-msvc": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-ia32-msvc/-/nice-win32-ia32-msvc-1.1.1.tgz", + "integrity": "sha512-CNQqlQT9MwuCsg1Vd/oKXiuH+TcsSPJmlAFc5frFyX/KkOh0UpBLEj7aoY656d5UKZQMQFP7vJNa1DNUNORvug==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-win32-x64-msvc": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-x64-msvc/-/nice-win32-x64-msvc-1.1.1.tgz", + "integrity": "sha512-vB+4G/jBQCAh0jelMTY3+kgFy00Hlx2f2/1zjMoH821IbplbWZOkLiTYXQkygNTzQJTq5cvwBDgn2ppHD+bglQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.4.tgz", + "integrity": "sha512-AJxoUD2/15ESHbvpcyjU274nsAPLuOtPHCk0vKJM5pj//Fg/B1FXNWjPnXTT9PymCYYiHo4zPj0ZomXBKhoy7g==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.4", + "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.4" + } + }, + "node_modules/@oxc-parser/binding-android-arm-eabi": { + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.142.0.tgz", + "integrity": "sha512-ZiRGDutGsv1G6bL/ozy/koC0Sv39T1DqyoC4KD1DOy9ZoACm1O5UWhEK2c02Qdk+4lfLVkvFa/mQ0fm/4h1BtQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-android-arm64": { + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.142.0.tgz", + "integrity": "sha512-WZkvGRLNQTz8lR9zP5nLjUdlroRCopBu3g9zF1p/laE6DzT1UbQo8Rdz5MWhaJUPYg/6gp+jo7HUgsyKaN1FtQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-darwin-arm64": { + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.142.0.tgz", + "integrity": "sha512-l4khS8LQOOVYsGRVARo1gSaCT/aBSceUVXgtovWc2+drnxVuDr082WA3OCHVdVzIz5JIrP/y9CWsSKxBDNmYGg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-darwin-x64": { + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.142.0.tgz", + "integrity": "sha512-QBsNF3nqlXmcH2B1YOPqQYmCJoy4HuIjUxGbBO/k5JAJUl68ghU2psRY2zPk+RyBaWqKP/qfL4oaFgEMCdwskA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-freebsd-x64": { + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.142.0.tgz", + "integrity": "sha512-b7Q7m4Cqc6XqNhri3R+QhU+GVy646Pn+bkdhrDdWym/Fdi0ZUa+d73H9dm5H91JtbtAQ/z1d8XKMW3oOV8a4tQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-arm-gnueabihf": { + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.142.0.tgz", + "integrity": "sha512-3riVS5IhdH3uCZj1Y9ftDQlR0dvLsIlw/edrRqk8JhgNd5K0XSs+UBtgh50N13CAlW9/TXj6sVGXaKNBocd0Yg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-arm-musleabihf": { + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.142.0.tgz", + "integrity": "sha512-NmXUOpgpTSkhl795TiXmWppTwmSJ92RC1qvD6e4XOF+slgmo3e6Ah+kEu+6AN8s7NAOEwqGmir58MgSQSWmBSA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-arm64-gnu": { + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.142.0.tgz", + "integrity": "sha512-gc0EXsKtXgerujmU2Bql3u1L1HsSQ2774R83idq/FoNMPVV/RY/1ErFsvnit7KoiP/sLvzQixeUo4Ut0ic0wmw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-arm64-musl": { + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.142.0.tgz", + "integrity": "sha512-F2XvmWSE0uWpie+jHKKIFgdVOe9ypGhkEZxKx5DuW215K6cbAC274yYaPkcM7EqY4Df3Weyhpcz3lsURyH2LVg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-ppc64-gnu": { + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.142.0.tgz", + "integrity": "sha512-wLMbT21U/QxknQsk+VvNF0b9D2/aGWhcaQQQ+VYlE8FwD5+GoWZIPPXNzyHmkYyhm0KB3itL+TBavjMatqNnYA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-riscv64-gnu": { + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.142.0.tgz", + "integrity": "sha512-+G8F/4ckwT7FCJV4H2bt09xEzJbjNCfuL4Sp1AYNaFtFMVtgIGMuJlteT82U+K0UIZ/DzAR/LDlMFnEuajG7Kw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-riscv64-musl": { + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.142.0.tgz", + "integrity": "sha512-hTsHtTLxMAfCo+rpF5K3qZJKW2NpPN/CHd4mYB3y7XlSdspHkd2gehDIofP64AacA9nWQw2tY3O7wR6UY8IVOA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-s390x-gnu": { + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.142.0.tgz", + "integrity": "sha512-6y7qYY3TCUDYjqswImdTGl92y+KA/80twALegQPN27kfY+bG7Ib1+L3jbmrCZQx6wrVnai9IPsEZp07I0hx7JQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-x64-gnu": { + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.142.0.tgz", + "integrity": "sha512-i69kAWU+2LgoH5bR+zWiiu+UzAw7Oxkwv7COeJTeY19pn4e70nKQcr9Pm6cL2Z0Z54d+gl9qADlK/0yyuCPiBA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-x64-musl": { + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.142.0.tgz", + "integrity": "sha512-4SQs678MmjYVrmhAgCWD4o0vpaFszXw9xLX5p2Z9MMFcltxiLkA88wQjh80YHjPrXtpyZ2CWI5m+1yNKM0m2Pw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-openharmony-arm64": { + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.142.0.tgz", + "integrity": "sha512-YHpx9N7Ln3a++Tc8rv+H7mrK1zyJQOAwCFg8LZ3lTs1T5afGWeZrLPhPT9HLnIwSjCyJqPWVMIrMxbjcmBr2oQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-wasm32-wasi": { + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.142.0.tgz", + "integrity": "sha512-3pLDyY3+oogW73RM5uehNgAiR/Xfb7fvO2Q1Z1gIqZ2+50XDVQmBVlRkHXZTU4gKnQHpwETNsYQVsJ3joVB2iA==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.11.2", + "@emnapi/runtime": "1.11.2", + "@napi-rs/wasm-runtime": "^1.1.6" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-win32-arm64-msvc": { + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.142.0.tgz", + "integrity": "sha512-Had/VeVY28Oyb0K+Q4FV8KCzoBycIh93oDK6pCbya9lkzdq+ikMHMgBubsdqqlybjJmQRawCQRrnBRHyQwYvcQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-win32-ia32-msvc": { + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.142.0.tgz", + "integrity": "sha512-GGi3+YphVHavvgs6gum2UXoNCqzHAmPt/nXkn8ZQZstV2Q1qZD1Mn8fz/nWrDkefHQtrG/+1/XrbMxsBTo6Svw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-win32-x64-msvc": { + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.142.0.tgz", + "integrity": "sha512-Ny/Wv4Us1LGC/ljwNTp+Hx3r/pH15EFfeDF0p+n898gt+TtRd6C9SccHcuUhDiNTb8s5tt7jdeAMDRQZ4Vq6hg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.142.0.tgz", + "integrity": "sha512-7W+2q5AKQVU36fkaryontrHn3YDt1RyUYXatw9i5H8ocYe2sPKSFB6eS8WNPeRKiN1qAWWZUPm7gwFzJGrccqQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.6.0.tgz", + "integrity": "sha512-7FNeNl8NCE7aINx7WXiKQrPYZWC/hvrTsmk6zmxbI7LTXE7hVek/n8AfVgpe2y82zl3w0HvCHN0bVKMBoJcC0w==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.3", + "is-glob": "^4.0.3", + "node-addon-api": "^7.0.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.6.0", + "@parcel/watcher-darwin-arm64": "2.6.0", + "@parcel/watcher-darwin-x64": "2.6.0", + "@parcel/watcher-freebsd-x64": "2.6.0", + "@parcel/watcher-linux-arm-glibc": "2.6.0", + "@parcel/watcher-linux-arm-musl": "2.6.0", + "@parcel/watcher-linux-arm64-glibc": "2.6.0", + "@parcel/watcher-linux-arm64-musl": "2.6.0", + "@parcel/watcher-linux-x64-glibc": "2.6.0", + "@parcel/watcher-linux-x64-musl": "2.6.0", + "@parcel/watcher-win32-arm64": "2.6.0", + "@parcel/watcher-win32-x64": "2.6.0" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.6.0.tgz", + "integrity": "sha512-trgpLSCKRC/huFjXX/Smh+0sWe4+YtKfktIToiMl59ghz7z+qkH6kMvNnUbLyRs9N11t8l4svSCs1+5B3rOAhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.6.0.tgz", + "integrity": "sha512-Y3QV0gl7Q1zbfueunkWIERICbEojQFCgpyG7YqOGNFLsckXyI1xu9mAIUpKY9QBYzBtSkN8dBPwd3yiAO9ovMw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.6.0.tgz", + "integrity": "sha512-Ohv6OpzhUfKYD7Beb8kDvG0jbIxORCYY1JRdZnaBtnjjkJxgD7ZVL0nw2sCYd0yTMKTvz3nnTnOF3cDifK+kvw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.6.0.tgz", + "integrity": "sha512-5HmXvDgs8VK+74jF9y9/2FE3/OnlcKmc56tjmSrEuZjpSZOGL+fvAu+HKJBdPs9uwoP2hE6TlSUpXZ/C5jUFmQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.6.0.tgz", + "integrity": "sha512-Ps/hui3A+vMbjdqlqAowK2ZL8+BO8dBjxeWXj6npTBs3jx4wWmbPpaLuqwrQrSqIVMCnpWo238bJ1U37GhQOYg==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.6.0.tgz", + "integrity": "sha512-9c6AUHgHoG+IY88MRIHupztQiQnrbqHYQjkM2btA+Bf/wQnQMuiD0Wfk1EVv3TlNT3x41uU71rn6E4xh/+zvkw==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.6.0.tgz", + "integrity": "sha512-yHRqS2owEXe6Hic9z6Mh1ECsCd+ODVOGvZDyciqRd21+v+o+DnXMOrw50DSpIG2sb8GPEaPPmfeCAWKPJdq46g==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.6.0.tgz", + "integrity": "sha512-WhB2e/V7rqdHHWZusBSPuy5Ei8S6lSz6FE5TKKQz5h3a0O+C+mhY7vxU9b/stqvMb8beLnPY82ZrFTLKs+SrKA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.6.0.tgz", + "integrity": "sha512-ulGE6x6Oz6iAwg75T8YQSoguBWasniIbX+QWpaYPcCnDOpdWX3k+4xbEYPZVLxOuoJI+svJJPD3sEj8G7lrQ3A==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.6.0.tgz", + "integrity": "sha512-tkBYKt7YQrjIJWYDnto2YgO8MRkjlMTSNoRHzsXinBqbLdeOM3L32wPZJvIZxqaLMfSlS/4sUjH/6STVP/XDLw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.6.0.tgz", + "integrity": "sha512-gIZAP23jaHjGWasY/TY6yL7NHFClf0Ga7FN+iINvk+KN94rhm94lYZhFsbYFNcA04/onvGD9kKmiJLJB2HbNwQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.6.0.tgz", + "integrity": "sha512-cA+/pXV2YkfxlIcXOQ5fSWqAzzPyD78/x5qbK/I0vUkrlYHA8TIz+MXjAbGouguKVSI4bOmkTSJ1/poVSsgt+A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher/node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.0.tgz", + "integrity": "sha512-9yB1l95IrJuNGDFdOYe79vdApdz6WWBCObE+rQ2LUliYUlcyFwSYIb2xb5/Ifw7dAtMy2ZqNyd8QTSOc7duAKw==", + "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.2.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.0.tgz", + "integrity": "sha512-pexNaW9ACLUOaBITOpU6qVu4VrsOFIjTv6bzgu0YUATo4eUJx0V605PxwZfndpPOn0ilqGqvGQ0M8UW0IE24jg==", + "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.2.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.0.tgz", + "integrity": "sha512-NqKYaq0355ZmNMG4QGpxtEDxsc7tGDhjhCm4PpE0cwnBW+5Il95LJyq414niEiaKLVjnVHBEjSo1wngKxJNiFw==", + "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.2.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.0.tgz", + "integrity": "sha512-3vPoHzh6eBTz9IbB0/qZdSr0Qeks2echn+I4cHu2joV74VriPDdldswksEDzrl1mBB+oPRi+67+3Ib59paxIPQ==", + "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.2.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.0.tgz", + "integrity": "sha512-E6NNefZ1bUVmKJq2tJkf45J4Zyczj7qm9rUT7NY+Xo2474Y13qWAwc2tvBt0BAVbmtXR1llkxXg0Ou1jbDf2SQ==", + "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.2.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.0.tgz", + "integrity": "sha512-D+TgkdgM1vu+7/Fpf8+v0ARW+RXEP9Ccazgm8zQ4JFFd9Q7SrYQ2TakU5S5ihazQDgpKyAgZDOcIFsvoHmTZ8w==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.0.tgz", + "integrity": "sha512-wUqdwJBbAv0APN87GecstdMUtLjjNTs0hBALpxETD73mccFxdmt/XeizXDtN5RAlBwNKmI+Tg+blect2G+8IeQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.0.tgz", + "integrity": "sha512-9DtF35qR9/NrfhM4oxLplCzVVjE+KKm8Pjemi0i/sdhAWkUasjmSo8WTTubNJClhSHCfyk2yeyoXDQEDPtDAAw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.0.tgz", + "integrity": "sha512-RzuHrBh8X8Hntd2N4VR02QGEciq/9JhcZoTpR/Cee6otRrlILGCf3cg2ygHuih+ZebUnWmMrDX6ITI85btO6rQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.0.tgz", + "integrity": "sha512-MK7L0018jjh1jR3mh21G2j1zAVcpscJBlPo2z19pRjv2XOYGRhaV4LyiD8HO6nCDdZln9IFgCMIV5yt4E3klGQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.0.tgz", + "integrity": "sha512-gyrxLQ9NfGb/9LoVnC4kb9miUghw1mghnkfYvNHSnVIXriabnfgGPUP4RLcJm87q3KgYz4FYUG8IDiWUT+CpSw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.0.tgz", + "integrity": "sha512-/6VFMQGRmrhP77KXDC+StIxGzcNp5JOIyYtw0CQ8gPlzhpiIRucYfoM5FaFamHd5BJYIdH86yfP46l1p3WdrFA==", + "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.2.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.2.0.tgz", + "integrity": "sha512-rwdbUL465kisF24WEJLvP3JrEG6E5GRuIHt5wpMwHGERtHe4Wm2CIvtf5gTBgr2tGOHKh5NdKEAFS2VkOPE91g==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.11.2", + "@emnapi/runtime": "1.11.2", + "@napi-rs/wasm-runtime": "^1.1.6" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.0.tgz", + "integrity": "sha512-+5suHwRiKGmhwyUaNT8a5QbrBvLFh2DbO910TEmGRH1aSxwrCezodvGQnulv4uiWEIv1Kq4ypRsJ5+O+ry1DiA==", + "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.2.0", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.0.tgz", + "integrity": "sha512-WfFv6/qGufotqBSBzBYwgpCkJBk8Nj7697LL9vTz/XWc67e0r3oewu8iMRwQj3AUL45GVD7wVsPjCsAAtW66Wg==", + "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.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@schematics/angular": { + "version": "22.1.8", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-22.1.8.tgz", + "integrity": "sha512-V37T9uHOQVHyxxOqwcJ9xjSIW/mW9UuSfjOc7WJE4V8+3zj0abDJLHuoxDZKe0icYGapgOcvHyYjtNOjSeSivw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "22.1.8", + "@angular-devkit/schematics": "22.1.8", + "jsonc-parser": "3.3.1", + "typescript": "6.0.3" + }, + "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/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.4.tgz", + "integrity": "sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A==", + "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", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/dompurify": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz", + "integrity": "sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/trusted-types": "*" + } + }, + "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/@types/gensync": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/gensync/-/gensync-1.0.5.tgz", + "integrity": "sha512-MbsRCT7mTikHwKZ0X+LVUTLRrZZRLipTuXEO9qOYO+zmjMVk81axyClMROf6uoPD9MRVu46bx8zoR0Ad9q3NAg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/jsesc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@types/jsesc/-/jsesc-2.5.1.tgz", + "integrity": "sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/prismjs": { + "version": "1.26.6", + "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.6.tgz", + "integrity": "sha512-vqlvI7qlMvcCBbVe0AKAb4f97//Hy0EBTaiW8AalRnG/xAN5zOiWWyrNqNXeq8+KAuvRewjCVY1+IPxk4RdNYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@vitejs/plugin-basic-ssl": { + "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 || ^8.0.0" + } + }, + "node_modules/@vitest/expect": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.11.tgz", + "integrity": "sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.11", + "@vitest/utils": "4.1.11", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.11.tgz", + "integrity": "sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.11", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/mocker/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", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.11.tgz", + "integrity": "sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.11.tgz", + "integrity": "sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.11", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.11.tgz", + "integrity": "sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.11", + "@vitest/utils": "4.1.11", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot/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", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.11.tgz", + "integrity": "sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.11.tgz", + "integrity": "sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.11", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils/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/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "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/ajv": { + "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": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ansi-escapes": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz", + "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz", + "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.24", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.24.tgz", + "integrity": "sha512-hYrgxie335U08WqICoGqKRzV1HFXv6zdxwJE4ekCb80CM9a0SVVsN4QPwT67RraRo+9h8IATk6uxHJw7QSkdOg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/beasties": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/beasties/-/beasties-0.4.3.tgz", + "integrity": "sha512-fIIeLOcbAB/K1kb1HBVJoiq1alHL4RCYBSo5e7HzrNkkgMggXR1Vqt/Z9JWnkfe/qdCo66Ux3QRwZioAIBdWRA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "css-select": "^6.0.0", + "css-what": "^7.0.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "htmlparser2": "^10.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.49", + "postcss-media-query-parser": "^0.2.3", + "postcss-safe-parser": "^7.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/bidi-js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.1.0.tgz", + "integrity": "sha512-fX1Onk0tdVPC7obPWB5EbJ1z7NVhLq4m2xZLq2YXBkxzMXIGRpNMU88n0EPgWseKl12J7zXs7qrDxPK4sRs2fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, + "node_modules/body-parser": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", + "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^2.0.0", + "debug": "^4.4.3", + "http-errors": "^2.0.1", + "iconv-lite": "^0.7.2", + "on-finished": "^2.4.1", + "qs": "^6.15.2", + "raw-body": "^3.0.2", + "type-is": "^2.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/body-parser/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, + "node_modules/browserslist": { + "version": "4.29.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.29.0.tgz", + "integrity": "sha512-3GSvyjvDI4Dur1Meg2BekJquu5uF+9R9a1+5M1Mde192eZoXbeXjzgOsgqPS2V8D5wrrip0gR5Hf/GhWQ9ZzaA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.11.23", + "caniuse-lite": "^1.0.30001810", + "electron-to-chromium": "^1.5.427", + "node-releases": "^2.0.55", + "update-browserslist-db": "^1.3.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001810", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz", + "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chardet": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.2.0.tgz", + "integrity": "sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA==", + "dev": true, + "license": "MIT" + }, + "node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-3.4.0.tgz", + "integrity": "sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-6.1.1.tgz", + "integrity": "sha512-06p9vyLahLa4zkGcgsGxU6iEkSOiuI4fhCH6Emhe2lPAcoUv73n72DnODsnHA+5wwXGnV0n9M9/qOQJSjYhFhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^9.0.0", + "string-width": "^8.2.0" + }, + "engines": { + "node": ">=22" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, + "node_modules/cliui": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cliui/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": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui/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": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/content-disposition": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-select": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-6.0.0.tgz", + "integrity": "sha512-rZZVSLle8v0+EY8QAkDWrKhpgt6SA5OtHsgBnsj6ZaLb5dmDVOWUDtQitd9ydxxvEjhewNudS6eTVU7uOyzvXw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^7.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "nth-check": "^2.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css-what": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-7.0.0.tgz", + "integrity": "sha512-wD5oz5xibMOPHzy13CyGmogB3phdvcDaB5t0W/Nr5Z2O/agcB8YwOz6e2Lsp10pNDzBoDO9nVa3RGs/2BttpHQ==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssstyle": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-6.2.0.tgz", + "integrity": "sha512-Fm5NvhYathRnXNVndkUsCCuR63DCLVVwGOOwQw782coXFi5HhkXdu289l59HlXZBawsyNccXfWRYvLzcDCdDig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^5.0.1", + "@csstools/css-syntax-patches-for-csstree": "^1.0.28", + "css-tree": "^3.1.0", + "lru-cache": "^11.2.6" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/data-urls": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz", + "integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/dompurify": { + "version": "3.4.15", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.15.tgz", + "integrity": "sha512-EUBjM+B+lkDE41iE82DDSCfkoPGfXx8IxFxPMjNzm/Uk4xDet77rTN9wqlxlVg71kK7XGuUMv6wUxJUwwv+Xyw==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.430", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.430.tgz", + "integrity": "sha512-e1QEj72Y4zd8RlNZVmoTg+iCOSVwpk05IOiiQwdrkwCSVlZfPthevErhE+nckGd2YbsXfp1SkisznhGVIXP2NQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/empathic": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.1.tgz", + "integrity": "sha512-YGRs8knHhKHVShLkFET/rWAU8kmHbOV5LwN938RHI0pljAJ1Gf6SzXsSmRaEzcXTtOOmVqJ5+WtQPL5uigY50Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz", + "integrity": "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.1.tgz", + "integrity": "sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/expect-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.7.0.tgz", + "integrity": "sha512-hOwV7WOxXfjRpAM1DSJWZDXx3GhplwD8IfwuwvogD8i1Qnkgosw/H45s4ZnFAUHDAhPjlY9hLBvJhKmGMyY26g==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "ip-address": "^10.2.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "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.8", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz", + "integrity": "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "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", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.7.0.tgz", + "integrity": "sha512-XjH1AECxf0giL2V1aU8vKyRR2ppRUb5c0EvT7zuJTokQ74bNo52zOtghqdWIqrhUD79fo3x0WfKZdOqxF6LG1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "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.13.8", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.13.8.tgz", + "integrity": "sha512-/Gng7NfoykZl2pjukW5Z6+8Yxm3BPRf86GTbQnt0SbySkvax4fyL4H3HhY1cCpBGmiW9XDRFzRV+CXK2W8QudQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/hosted-git-info": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-10.1.1.tgz", + "integrity": "sha512-DeOnSPAvOndYKfw075gt8yZzQ7S2hNztw34zBTfhIzLhmBTswIBg5/y+pqu/VD5cYWm5goAFTusDmUEmKZ0PEQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^11.1.0" + }, + "engines": { + "node": "^22.22.2 || ^24.15.0 || >=26.0.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==", + "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-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/http-proxy-agent/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-9.1.0.tgz", + "integrity": "sha512-ag87y7cJJ9/3+GxFr8Oy4O5faDsGRGnBGsJj/YjOSsSx/5eadKLYTMPlzuR6obgoCDDm0abAAZitXXQkMOPSpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "9.0.0", + "debug": "^4.3.4", + "proxy-agent-negotiate": "1.1.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", + "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/immutable": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.9.tgz", + "integrity": "sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==", + "dev": true, + "license": "MIT" + }, + "node_modules/import-meta-resolve": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", + "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "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/ip-address": { + "version": "10.7.2", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.2.tgz", + "integrity": "sha512-7H/2gFSIitxc0hG3nOI1glS8QLo/EHBFFLk8vEUjXY/xu0AdL8jZ9U1IzO2PUm0d2D/ofQcAifb0g6OBkt8U7w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "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": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "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": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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": { + "get-east-asian-width": "^1.3.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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, + "license": "MIT", + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "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", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/jose": { + "version": "6.2.12", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.12.tgz", + "integrity": "sha512-9NiFmJEex0sy2Dk58j2UGBSHgUs2ypF9eZSu4L6vjOX3Dp96Sw1F3uL+H+D1sx02jZZdzUT0HgvCy59CuvXcWw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/js-tokens": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", + "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsdom": { + "version": "28.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-28.1.0.tgz", + "integrity": "sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@acemir/cssom": "^0.9.31", + "@asamuzakjp/dom-selector": "^6.8.1", + "@bramus/specificity": "^2.4.2", + "@exodus/bytes": "^1.11.0", + "cssstyle": "^6.0.1", + "data-urls": "^7.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", + "undici": "^7.21.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^8.0.1", + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.0", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/jsdom/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/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "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==", + "dev": true, + "license": "MIT" + }, + "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": "BSD-2-Clause" + }, + "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": ">=6" + } + }, + "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" + }, + "node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/listr2": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-11.0.0.tgz", + "integrity": "sha512-8K88S0aSrcSXdJfiZtEy5BQMnR+TyjrCGLcgAvQs6ta0NEnIm0RJ72/Pv67Jvg07cfBhDbuN74V81lSSVYEFEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cli-truncate": "^6.1.1", + "log-update": "^8.0.0", + "wrap-ansi": "^10.0.0" + }, + "engines": { + "node": ">=22.13.0" + } + }, + "node_modules/lmdb": { + "version": "3.5.6", + "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-3.5.6.tgz", + "integrity": "sha512-j3uE8ReKNyUWDjhfEFSJqE/1DLtfTR5Z8yFzVHvBjAk37wNg7HdScjcv8ttPHRvrdgPQMPWxFFI0SsdBzI5lBw==", + "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" + }, + "bin": { + "download-lmdb-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@lmdb/lmdb-darwin-arm64": "3.5.6", + "@lmdb/lmdb-darwin-x64": "3.5.6", + "@lmdb/lmdb-linux-arm": "3.5.6", + "@lmdb/lmdb-linux-arm64": "3.5.6", + "@lmdb/lmdb-linux-x64": "3.5.6", + "@lmdb/lmdb-win32-arm64": "3.5.6", + "@lmdb/lmdb-win32-x64": "3.5.6" + } + }, + "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": "MIT", + "dependencies": { + "is-unicode-supported": "^2.0.0", + "yoctocolors": "^2.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-8.0.0.tgz", + "integrity": "sha512-lddSgOt3bPASrylL54ZSpy8nBHns+vBVSoILlVOx+dei300pnLRN958rj/EdlVLKuWlSESU3qdnDZdAI7FXYGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.3.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^9.0.0", + "string-width": "^8.2.0", + "strip-ansi": "^7.2.0", + "wrap-ansi": "^10.0.0" + }, + "engines": { + "node": ">=22" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/magic-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.0.0.tgz", + "integrity": "sha512-CGvjzMN08iv6w1mm4/x3Gh1hLb4VnyRUA15FFpl6CsCIGGoe36k7kY5KNz9QDbSBN5I/fWHM6ZlIkUTa5xdUEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/marked": { + "version": "18.0.13", + "resolved": "https://registry.npmjs.org/marked/-/marked-18.0.13.tgz", + "integrity": "sha512-xTxVzZsBFwunP6HDmtBkabUQEYArnP7/rMDGmPj9SlrKlQ4i8MdYVow+nJL0eOqwpUqhzBoTBRADGN6uYwPyOw==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, + "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", + "engines": { + "node": ">= 0.4" + } + }, + "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.1", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.1.tgz", + "integrity": "sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "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": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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": ">= 0.6" + } + }, + "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": { + "mime-db": "^1.54.0" + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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": ">=10" + } + }, + "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.12.1", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.12.1.tgz", + "integrity": "sha512-4EUH9tQHnMmEgzW/MdAP0KIfa1T9AF+htl0ffe2n5vb2EKn9y2co8ccpgWko6S52Jy1PQZKwRnx5/KkYjtd9MQ==", + "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": { + "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.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/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", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.19", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.19.tgz", + "integrity": "sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/negotiator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.1.0.tgz", + "integrity": "sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "content-type": "^2.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/negotiator/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/node-addon-api": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-gyp-build-optional-packages": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", + "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.1" + }, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } + }, + "node_modules/node-releases": { + "version": "2.0.56", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.56.tgz", + "integrity": "sha512-x0InOIyzgdk+eyaWaRJFH5snEtiImgBgblZ2CyPrLmqqcuMQkEvcDPHbzqbD8eDsSeJbVOjn+crzyzHaM4D+/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm-package-arg": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-14.0.0.tgz", + "integrity": "sha512-69XQh3k+dtGa1p+7RaR57IuG3rCko96xr/nUfN4yDYBXbTYICiWcOpsFKLN2GtGE9cyIljE+f1exnaYt9MvM+Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^10.1.0", + "proc-log": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^8.0.0" + }, + "engines": { + "node": "^22.22.2 || ^24.15.0 || >=26.0.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obug": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.2.1.tgz", + "integrity": "sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-9.4.1.tgz", + "integrity": "sha512-6VlU9MLXbjVQD04AZCMX28hVtA5bUoadvUqO76MUCVA0ilwJbMiHsITRPfyVm6p/BC0Av/BXMujx39WCe1LEqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.6.2", + "cli-cursor": "^5.0.0", + "cli-spinners": "^3.2.0", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.1.0", + "log-symbols": "^7.0.1", + "stdin-discarder": "^0.3.2", + "string-width": "^8.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ordered-binary": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.6.1.tgz", + "integrity": "sha512-QkCdPooczexPLiXIrbVOPYkR3VO3T6v2OyKRkR1Xbhpy7/LAVXwahnRCgRp78Oe/Ehf0C/HATAxfSr6eA1oX+w==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/oxc-parser": { + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.142.0.tgz", + "integrity": "sha512-kKR+jPiRJYJDexVoziIg/FVGvr1fT1FZSSJOk6tVoMKKSlsf1Cso+cgGCJkOEDWOP174vRntCPFKg+AS7InWvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "^0.142.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/Boshen" + }, + "optionalDependencies": { + "@oxc-parser/binding-android-arm-eabi": "0.142.0", + "@oxc-parser/binding-android-arm64": "0.142.0", + "@oxc-parser/binding-darwin-arm64": "0.142.0", + "@oxc-parser/binding-darwin-x64": "0.142.0", + "@oxc-parser/binding-freebsd-x64": "0.142.0", + "@oxc-parser/binding-linux-arm-gnueabihf": "0.142.0", + "@oxc-parser/binding-linux-arm-musleabihf": "0.142.0", + "@oxc-parser/binding-linux-arm64-gnu": "0.142.0", + "@oxc-parser/binding-linux-arm64-musl": "0.142.0", + "@oxc-parser/binding-linux-ppc64-gnu": "0.142.0", + "@oxc-parser/binding-linux-riscv64-gnu": "0.142.0", + "@oxc-parser/binding-linux-riscv64-musl": "0.142.0", + "@oxc-parser/binding-linux-s390x-gnu": "0.142.0", + "@oxc-parser/binding-linux-x64-gnu": "0.142.0", + "@oxc-parser/binding-linux-x64-musl": "0.142.0", + "@oxc-parser/binding-openharmony-arm64": "0.142.0", + "@oxc-parser/binding-wasm32-wasi": "0.142.0", + "@oxc-parser/binding-win32-arm64-msvc": "0.142.0", + "@oxc-parser/binding-win32-ia32-msvc": "0.142.0", + "@oxc-parser/binding-win32-x64-msvc": "0.142.0" + } + }, + "node_modules/parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", + "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^8.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-html-rewriting-stream": { + "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": "^8.0.0", + "parse5": "^8.0.0", + "parse5-sax-parser": "^8.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-html-rewriting-stream/node_modules/entities": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-8.1.0.tgz", + "integrity": "sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/parse5-sax-parser": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-8.0.0.tgz", + "integrity": "sha512-/dQ8UzHZwnrzs3EvDj6IkKrD/jIZyTlB+8XrHJvcjNgRdmWruNdN9i9RK/JtxakmlUdPwKubKPTCqvbTgzGhrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse5": "^8.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-8.1.0.tgz", + "integrity": "sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/piscina": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-5.2.0.tgz", + "integrity": "sha512-DszUCKeVN/5G5QKo6jAVHL8fmKnkJvQ0ACiVgY7YGCq3TUB2oznAOayvZPIAdEThvhczkXR+qm3IHsNXpFCYfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.x" + }, + "optionalDependencies": { + "@napi-rs/nice": "^1.0.4" + } + }, + "node_modules/pkce-challenge": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/postcss": { + "version": "8.5.28", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", + "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.18", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-media-query-parser": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", + "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss-safe-parser": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.1.0.tgz", + "integrity": "sha512-1WzZxRLaAFwEh6Do+zyGpjWV3nGJNxxhuh7Ubu/q1ICImMgZJnLwhoaEpRbG4pJppp2Y1ncL9ffA2f+LhrefQg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss-safe-parser" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/prettier": { + "version": "3.9.7", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.7.tgz", + "integrity": "sha512-T3mF5P7HFzZVLDQuCUNtJj6WK1pcpLMweMNUVwGb01bLNkH9AkKxtZvmInw8K6bnn2O3d6/5RHl6zSHiBAD0Og==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/proc-log": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-7.0.0.tgz", + "integrity": "sha512-FYgfaA69XZ93zaXLoMNQ+ViDXGGBgR8aLh03txzcFhV+9xOXx7+8DLCULrKKpR9+GsH9ZfHm82aSUPpozX0Ztg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^22.22.2 || ^24.15.0 || >=26.0.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.8.tgz", + "integrity": "sha512-5nnx0yGyVUcY6t9RnWcARWtwT9F1D8O9rt08htPvnd49W1IgZtmLkhu9WfMzQj1cFxjHIO6connUNVW5k7AVyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/proxy-agent-negotiate": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-agent-negotiate/-/proxy-agent-negotiate-1.1.0.tgz", + "integrity": "sha512-N8IBcM3UgCVzz2L2Lqv8DVntDnnC8/hiV4nEDUPkqq72TPUgYWjQc+bdZlBPZK9LzPAvOY//gAt0S0DApoOXWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "kerberos": "^2.0.0" + }, + "peerDependenciesMeta": { + "kerberos": { + "optional": true + } + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.0.tgz", + "integrity": "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", + "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/readdirp": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.1.1.tgz", + "integrity": "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rolldown": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.0.tgz", + "integrity": "sha512-u7tgm5l4Yw1iTqUL4EcYOAt7fFvCgQMLeidrnD4GALlC6aOznCjezYajgxeyKw27u0Q5N7fwgCzjVyPIWzwuBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.140.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.2.0", + "@rolldown/binding-darwin-arm64": "1.2.0", + "@rolldown/binding-darwin-x64": "1.2.0", + "@rolldown/binding-freebsd-x64": "1.2.0", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.0", + "@rolldown/binding-linux-arm64-gnu": "1.2.0", + "@rolldown/binding-linux-arm64-musl": "1.2.0", + "@rolldown/binding-linux-ppc64-gnu": "1.2.0", + "@rolldown/binding-linux-s390x-gnu": "1.2.0", + "@rolldown/binding-linux-x64-gnu": "1.2.0", + "@rolldown/binding-linux-x64-musl": "1.2.0", + "@rolldown/binding-openharmony-arm64": "1.2.0", + "@rolldown/binding-wasm32-wasi": "1.2.0", + "@rolldown/binding-win32-arm64-msvc": "1.2.0", + "@rolldown/binding-win32-x64-msvc": "1.2.0" + } + }, + "node_modules/rolldown/node_modules/@oxc-project/types": { + "version": "0.140.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.140.0.tgz", + "integrity": "sha512-h5LUOzGArYemnW1NMz/DuuQhBi96J6JL2Bk8zE4kvqxB5Sg3jxmCiH4uyOWHDkiKSt5vWlG4FIwCR/DbstcNRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/sass": { + "version": "1.101.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.101.0.tgz", + "integrity": "sha512-OL3GoQyoUdDt843DpVmDO6y2k1sc5IhUDSpu8XucEI+35neq5QivZ1iuegnpraEVTJXlQGK1gl27zKcTLEPbQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^5.0.0", + "immutable": "^5.1.5", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=20.19.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true, + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slice-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-9.0.0.tgz", + "integrity": "sha512-SO/3iYL5S3W57LLEniscOGPZgOqZUPCx6d3dB+52B80yJ0XstzsC/eV8gnA4tM3MHDrKz+OCFSLNjswdSC+/bA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.3", + "is-fullwidth-code-point": "^5.1.0" + }, + "engines": { + "node": ">=22" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/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": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/std-env": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz", + "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==", + "dev": true, + "license": "MIT" + }, + "node_modules/stdin-discarder": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.3.2.tgz", + "integrity": "sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.2.tgz", + "integrity": "sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", + "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyrainbow": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.1.tgz", + "integrity": "sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tldts": { + "version": "7.4.13", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.13.tgz", + "integrity": "sha512-iHtaIWWIbMDkCeJdTBzZFGgbluE5J+oHlb2g7+oAz1S1gpuVpabRZdQyd471Vl8UUkcz2vXSL8xZH2kyCe8tfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^7.4.13" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "7.4.13", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.13.tgz", + "integrity": "sha512-mbYsrih5FRtGxs3Usvl/PqwJsNpp+jsmrdFviiK02teHDG0/HebBG/pqCylje3kzgXYzuLoHJF/0mz9W53t8Xg==", + "dev": true, + "license": "MIT" + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tough-cookie": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.2.tgz", + "integrity": "sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^7.0.5" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-is": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", + "dev": true, + "license": "MIT", + "dependencies": { + "content-type": "^2.0.0", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/type-is/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/typescript": { + "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": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.29.1.tgz", + "integrity": "sha512-RYONW2MeafgYlkVOKYKkA/Ag7BmXqgIWCa8t1m0JcxrQg9pI9lEqRhAOruOBCbAohOa/gkCF+iPi9hrgvTzu6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.3.tgz", + "integrity": "sha512-pJ2sYawQS0R/WI928Gj5GlPhTGzbMelq0+4INtSYNDV9ErKJcX6xjGWkoG/VnB3dpUm00zALaqkrUD77pO5TDQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/validate-npm-package-name": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-8.0.0.tgz", + "integrity": "sha512-SCv6OOV6Xj2/3cXy3dGmADluJTNcL3o7hZAglNPTe+WYuEuvxgJzxPrSDLZhF+CwyQOubqgecjMmTJGMVLWjYQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^22.22.2 || ^24.15.0 || >=26.0.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/verkit": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/verkit/-/verkit-0.3.2.tgz", + "integrity": "sha512-zj/ob3UsvJGN0whEAKFp53REA5X66hvffVqoCtVQAakJKnKlH+/PcOfMoFwIG/o4rElqLv/ycAFlx8ZlXUorCg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/vite": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.5.tgz", + "integrity": "sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.17", + "rolldown": "~1.1.5", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.3.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/@emnapi/core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/vite/node_modules/@emnapi/runtime": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/vite/node_modules/@oxc-project/types": { + "version": "0.139.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.139.0.tgz", + "integrity": "sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-android-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz", + "integrity": "sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.5.tgz", + "integrity": "sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-darwin-x64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.5.tgz", + "integrity": "sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.5.tgz", + "integrity": "sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.5.tgz", + "integrity": "sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.5.tgz", + "integrity": "sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.5.tgz", + "integrity": "sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.5.tgz", + "integrity": "sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.5.tgz", + "integrity": "sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.5.tgz", + "integrity": "sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.5.tgz", + "integrity": "sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.5.tgz", + "integrity": "sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.5.tgz", + "integrity": "sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.6" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.5.tgz", + "integrity": "sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.5.tgz", + "integrity": "sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite/node_modules/rolldown": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.5.tgz", + "integrity": "sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.139.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.1.5", + "@rolldown/binding-darwin-arm64": "1.1.5", + "@rolldown/binding-darwin-x64": "1.1.5", + "@rolldown/binding-freebsd-x64": "1.1.5", + "@rolldown/binding-linux-arm-gnueabihf": "1.1.5", + "@rolldown/binding-linux-arm64-gnu": "1.1.5", + "@rolldown/binding-linux-arm64-musl": "1.1.5", + "@rolldown/binding-linux-ppc64-gnu": "1.1.5", + "@rolldown/binding-linux-s390x-gnu": "1.1.5", + "@rolldown/binding-linux-x64-gnu": "1.1.5", + "@rolldown/binding-linux-x64-musl": "1.1.5", + "@rolldown/binding-openharmony-arm64": "1.1.5", + "@rolldown/binding-wasm32-wasi": "1.1.5", + "@rolldown/binding-win32-arm64-msvc": "1.1.5", + "@rolldown/binding-win32-x64-msvc": "1.1.5" + } + }, + "node_modules/vitest": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.11.tgz", + "integrity": "sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.11", + "@vitest/mocker": "4.1.11", + "@vitest/pretty-format": "4.1.11", + "@vitest/runner": "4.1.11", + "@vitest/snapshot": "4.1.11", + "@vitest/spy": "4.1.11", + "@vitest/utils": "4.1.11", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.11", + "@vitest/browser-preview": "4.1.11", + "@vitest/browser-webdriverio": "4.1.11", + "@vitest/coverage-istanbul": "4.1.11", + "@vitest/coverage-v8": "4.1.11", + "@vitest/ui": "4.1.11", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } + } + }, + "node_modules/vitest/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", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/watchpack": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.2.tgz", + "integrity": "sha512-6i/00NBjP4yGPs+caKSyRfpTF/8Torsu0MOW3mMzIbhgISFder8i7xbqgHlLMwJrdiN8ndBV3UA1/AfzPSr+jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/weak-lru-cache": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz", + "integrity": "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/webidl-conversions": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", + "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-mimetype": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", + "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-url": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz", + "integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.11.0", + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-10.0.1.tgz", + "integrity": "sha512-M0N4xzyzosiIok3svYlEo1sdLZts/8FPgYH/GPC3wvlmPoRvnoManGMrE54waYj3tISA8w6lsdesfVv67qSr8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.3", + "string-width": "^8.2.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "18.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.1.0.tgz", + "integrity": "sha512-2rAgRKu54VsHkqI0/tYkmluGXHD4KW7yZoycuqDQ15QOTnc2VVfy0nN/1eMhnQLO00A+dwtK20xuCnc1YGeUyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^9.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "string-width": "^8.2.1", + "y18n": "^5.0.5", + "yargs-parser": "^22.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yoctocolors": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.2.0.tgz", + "integrity": "sha512-xYqdZFUK/VYazNl/oCDYN+3WloWQwMfZxBoiNt6qNyk+xfOdi598muWE42rNZFp1kNOiqW936q5RhUdnpqElSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.25.2", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", + "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", + "dev": true, + "license": "ISC", + "peerDependencies": { + "zod": "^3.25.28 || ^4" + } + } + } +} diff --git a/apps/portal/package.json b/apps/portal/package.json new file mode 100644 index 0000000..1778956 --- /dev/null +++ b/apps/portal/package.json @@ -0,0 +1,42 @@ +{ + "name": "portal", + "version": "0.0.0", + "scripts": { + "ng": "ng", + "generate-manifest": "node scripts/generate-manifest.mjs", + "prebuild": "npm run generate-manifest", + "start": "ng serve", + "build": "ng build", + "build:pages": "ng build --base-href /angular-interview-prep/", + "watch": "ng build --watch --configuration development", + "test": "ng test --watch=false", + "test:watch": "ng test", + "test:integrity": "node scripts/test-manifest-integrity.mjs" + }, + "private": true, + "packageManager": "npm@12.0.1", + "dependencies": { + "@angular/common": "^22.0.0", + "@angular/compiler": "^22.0.0", + "@angular/core": "^22.0.0", + "@angular/forms": "^22.0.0", + "@angular/platform-browser": "^22.0.0", + "@angular/router": "^22.0.0", + "dompurify": "^3.4.15", + "marked": "^18.0.13", + "prismjs": "^1.30.0", + "rxjs": "~7.8.0", + "tslib": "^2.3.0" + }, + "devDependencies": { + "@angular/build": "^22.0.7", + "@angular/cli": "^22.0.7", + "@angular/compiler-cli": "^22.0.0", + "@types/dompurify": "^3.0.5", + "@types/prismjs": "^1.26.6", + "jsdom": "^28.0.0", + "prettier": "^3.8.1", + "typescript": "~6.0.2", + "vitest": "^4.0.8" + } +} diff --git a/apps/portal/public/assets/content-manifest.json b/apps/portal/public/assets/content-manifest.json new file mode 100644 index 0000000..c22fcfe --- /dev/null +++ b/apps/portal/public/assets/content-manifest.json @@ -0,0 +1,6851 @@ +{ + "generatedAt": "2026-09-17T17:34:24.052Z", + "totalTopics": 54, + "totalFlashcards": 188, + "categories": [ + { + "key": "01-foundations", + "title": "Foundations & Architecture", + "badge": "Basics", + "icon": "layers", + "order": 1, + "topicCount": 5, + "topics": [ + { + "id": "01-why-angular", + "title": "Why Angular", + "readingTime": 16, + "flashcardCount": 6 + }, + { + "id": "02-spa-concepts", + "title": "Single Page Application (SPA)", + "readingTime": 14, + "flashcardCount": 5 + }, + { + "id": "03-component-based-architecture", + "title": "Component-Based Architecture", + "readingTime": 12, + "flashcardCount": 4 + }, + { + "id": "04-angular-architecture-overview", + "title": "Angular Architecture Overview", + "readingTime": 7, + "flashcardCount": 5 + }, + { + "id": "05-typescript-essentials-for-angular", + "title": "TypeScript Essentials for Angular", + "readingTime": 11, + "flashcardCount": 4 + } + ] + }, + { + "key": "02-core-angular", + "title": "Core Angular Essentials", + "badge": "Core", + "icon": "cpu", + "order": 2, + "topicCount": 6, + "topics": [ + { + "id": "06-angular-components", + "title": "Angular Components", + "readingTime": 8, + "flashcardCount": 8 + }, + { + "id": "07-templates-and-data-binding", + "title": "Templates and Data Binding", + "readingTime": 8, + "flashcardCount": 7 + }, + { + "id": "08-directives", + "title": "Directives", + "readingTime": 6, + "flashcardCount": 5 + }, + { + "id": "09-services", + "title": "Services", + "readingTime": 4, + "flashcardCount": 1 + }, + { + "id": "10-dependency-injection", + "title": "Dependency Injection (DI)", + "readingTime": 12, + "flashcardCount": 8 + }, + { + "id": "11-http-client", + "title": "HttpClient", + "readingTime": 9, + "flashcardCount": 5 + } + ] + }, + { + "key": "03-intermediate", + "title": "Intermediate Patterns", + "badge": "Intermediate", + "icon": "workflow", + "order": 3, + "topicCount": 4, + "topics": [ + { + "id": "12-rxjs-and-observables-fundamentals", + "title": "RxJS & Observables Fundamentals", + "readingTime": 9, + "flashcardCount": 8 + }, + { + "id": "13-component-communication", + "title": "Component Communication", + "readingTime": 8, + "flashcardCount": 8 + }, + { + "id": "14-lifecycle-hooks", + "title": "Lifecycle Hooks", + "readingTime": 8, + "flashcardCount": 2 + }, + { + "id": "15-forms", + "title": "Forms", + "readingTime": 9, + "flashcardCount": 4 + } + ] + }, + { + "key": "04-reactivity", + "title": "Reactivity, RxJS & Signals", + "badge": "Must Know", + "icon": "zap", + "order": 4, + "topicCount": 6, + "topics": [ + { + "id": "16-subjects-and-behaviorsubject", + "title": "Subjects & BehaviorSubject", + "readingTime": 9, + "flashcardCount": 2 + }, + { + "id": "17-rxjs-operators-deep-dive", + "title": "RxJS Operators Deep Dive", + "readingTime": 11, + "flashcardCount": 4 + }, + { + "id": "17a-common-enterprise-rxjs-scenarios", + "title": "Common Enterprise RxJS Scenarios", + "readingTime": 8, + "flashcardCount": 4 + }, + { + "id": "17b-async-pipe-and-rxjs-template-patterns", + "title": "Async Pipe & RxJS Template Patterns", + "readingTime": 7, + "flashcardCount": 3 + }, + { + "id": "18-signals", + "title": "Signals", + "readingTime": 10, + "flashcardCount": 5 + }, + { + "id": "18a-signal-based-component-apis", + "title": "Signal-Based Component APIs", + "readingTime": 7, + "flashcardCount": 3 + } + ] + }, + { + "key": "05-advanced", + "title": "Advanced & Performance", + "badge": "Advanced", + "icon": "gauge", + "order": 5, + "topicCount": 11, + "topics": [ + { + "id": "19-ngmodules-fundamentals", + "title": "NgModules Fundamentals", + "readingTime": 5, + "flashcardCount": 3 + }, + { + "id": "20-standalone-components", + "title": "Standalone Components", + "readingTime": 5, + "flashcardCount": 3 + }, + { + "id": "21-routing-and-navigation", + "title": "Routing & Navigation", + "readingTime": 9, + "flashcardCount": 7 + }, + { + "id": "22-route-parameters-and-query-parameters-deep-dive", + "title": "Route Parameters & Query Parameters Deep Dive", + "readingTime": 8, + "flashcardCount": 2 + }, + { + "id": "23-route-guards", + "title": "Route Guards", + "readingTime": 9, + "flashcardCount": 2 + }, + { + "id": "24-nested-routes", + "title": "Nested Routes", + "readingTime": 4, + "flashcardCount": 2 + }, + { + "id": "25-route-resolvers", + "title": "Route Resolvers", + "readingTime": 9, + "flashcardCount": 2 + }, + { + "id": "26-lazy-loading", + "title": "Lazy Loading", + "readingTime": 7, + "flashcardCount": 4 + }, + { + "id": "27-http-interceptors", + "title": "HTTP Interceptors", + "readingTime": 5, + "flashcardCount": 2 + }, + { + "id": "28-change-detection", + "title": "Change Detection", + "readingTime": 7, + "flashcardCount": 2 + }, + { + "id": "29-performance-optimization", + "title": "Angular Performance Optimization", + "readingTime": 10, + "flashcardCount": 7 + } + ] + }, + { + "key": "06-senior-architecture", + "title": "Senior Architecture & Enterprise", + "badge": "Architect", + "icon": "shield-check", + "order": 6, + "topicCount": 7, + "topics": [ + { + "id": "30-state-management-fundamentals", + "title": "State Management Fundamentals", + "readingTime": 11, + "flashcardCount": 3 + }, + { + "id": "31-ngrx-fundamentals", + "title": "NgRx Fundamentals", + "readingTime": 14, + "flashcardCount": 2 + }, + { + "id": "32-authentication-authorization", + "title": "Authentication & Authorization", + "readingTime": 11, + "flashcardCount": 2 + }, + { + "id": "33-error-handling-strategy", + "title": "Error Handling Strategy", + "readingTime": 9, + "flashcardCount": 2 + }, + { + "id": "34-enterprise-architecture", + "title": "Enterprise Architecture", + "readingTime": 9, + "flashcardCount": 3 + }, + { + "id": "35-micro-frontends", + "title": "Micro Frontends", + "readingTime": 11, + "flashcardCount": 3 + }, + { + "id": "web-security-guide", + "title": "Web & Angular Security Guide", + "readingTime": 10, + "flashcardCount": 1 + } + ] + }, + { + "key": "07-interview-preparation", + "title": "Interview Drills & System Design", + "badge": "Interview", + "icon": "target", + "order": 7, + "topicCount": 6, + "topics": [ + { + "id": "36-mock-interview-questions", + "title": "Angular Mock Interview Questions", + "readingTime": 5, + "flashcardCount": 8 + }, + { + "id": "37-frontend-system-design", + "title": "Frontend System Design", + "readingTime": 2, + "flashcardCount": 5 + }, + { + "id": "38-angular-testing-deep-dive", + "title": "Angular Testing Deep Dive", + "readingTime": 2, + "flashcardCount": 2 + }, + { + "id": "39-behavioral-interview-preparation", + "title": "Behavioral Interview Preparation", + "readingTime": 3, + "flashcardCount": 1 + }, + { + "id": "40-angular-system-design-case-studies", + "title": "Angular System Design Case Studies", + "readingTime": 4, + "flashcardCount": 2 + }, + { + "id": "41-quick-fire-qa", + "title": "Quick-Fire Interview Q&A", + "readingTime": 2, + "flashcardCount": 2 + } + ] + }, + { + "key": "08-cheat-sheets", + "title": "Rapid Cheat Sheets", + "badge": "Quick Cram", + "icon": "bookmark", + "order": 8, + "topicCount": 9, + "topics": [ + { + "id": "01-angular-cheat-sheet", + "title": "Angular Cheat Sheet", + "readingTime": 1, + "flashcardCount": 2 + }, + { + "id": "02-rxjs-cheat-sheet", + "title": "RxJS Cheat Sheet", + "readingTime": 1, + "flashcardCount": 2 + }, + { + "id": "03-signals-cheat-sheet", + "title": "Signals Cheat Sheet", + "readingTime": 1, + "flashcardCount": 2 + }, + { + "id": "04-routing-cheat-sheet", + "title": "Routing Cheat Sheet", + "readingTime": 1, + "flashcardCount": 2 + }, + { + "id": "05-forms-cheat-sheet", + "title": "Forms Cheat Sheet", + "readingTime": 1, + "flashcardCount": 2 + }, + { + "id": "06-ngrx-cheat-sheet", + "title": "NgRx Cheat Sheet", + "readingTime": 1, + "flashcardCount": 1 + }, + { + "id": "07-auth-cheat-sheet", + "title": "Authentication & Authorization Cheat Sheet", + "readingTime": 1, + "flashcardCount": 2 + }, + { + "id": "08-architecture-cheat-sheet", + "title": "Enterprise Architecture Cheat Sheet", + "readingTime": 1, + "flashcardCount": 1 + }, + { + "id": "09-interview-day-cheat-sheet", + "title": "Interview Day Quick Revision", + "readingTime": 1, + "flashcardCount": 1 + } + ] + } + ], + "topics": [ + { + "id": "01-why-angular", + "slug": "01-why-angular", + "title": "Why Angular", + "summary": "Angular is a complete, opinionated, TypeScript-based development platform and framework maintained by Google for building scalable, enterprise-grade Single Page...", + "category": "01-foundations", + "categoryTitle": "Foundations & Architecture", + "categoryBadge": "Basics", + "categoryIcon": "layers", + "categoryOrder": 1, + "order": "01", + "fileName": "01-why-angular.md", + "contentUrl": "assets/content/01-foundations/01-why-angular.md", + "wordCount": 3050, + "readingTime": 16, + "headings": [ + { + "level": 1, + "text": "Why Angular" + }, + { + "level": 1, + "text": "What Is Angular?" + }, + { + "level": 1, + "text": "Why Does It Exist?" + }, + { + "level": 1, + "text": "Historical Context" + }, + { + "level": 3, + "text": "1. The AngularJS Era (v1.x - 2010)" + }, + { + "level": 3, + "text": "2. The Great Rewrite: Angular 2+ (2016)" + }, + { + "level": 3, + "text": "3. The Modern Angular Renaissance (v16 - v19+)" + }, + { + "level": 1, + "text": "Problem It Solves" + }, + { + "level": 3, + "text": "Without an Opinionated Framework (Ad-hoc Libraries)" + }, + { + "level": 3, + "text": "With Angular" + }, + { + "level": 1, + "text": "First Principles Explanation" + }, + { + "level": 1, + "text": "Real World Analogy" + } + ], + "flashcards": [ + { + "question": "What is Angular and what primary architectural problem does it solve?", + "answer": "Angular is a full-featured, opinionated, TypeScript-based platform maintained by Google. It provides an end-to-end architecture (DI, routing, forms, HTTP client, signals) to eliminate spaghetti code and build maintainable, testable enterprise SPAs.", + "source": "Why Angular" + }, + { + "question": "What is the fundamental difference between Angular and jQuery?", + "answer": "jQuery relies on imperative DOM manipulation where application state is stored directly inside HTML elements. Angular uses declarative data binding where state resides in TypeScript classes/signals and the DOM is a reactive projection of that state.", + "source": "Why Angular" + }, + { + "question": "How does Angular differ from React architecturally?", + "answer": "Angular is a complete \"batteries-included\" framework with built-in DI, routing, forms, and fine-grained Signals. React is a view rendering library requiring community packages for routing, forms, and state, with rendering driven by virtual DOM reconciliation.", + "source": "Why Angular" + }, + { + "question": "What is the core difference between a Framework and a Library?", + "answer": "Inversion of Control (IoC). With a library, your application code controls the execution flow and calls the library. With a framework, the framework owns the lifecycle and architecture, calling your code into predefined hooks.", + "source": "Why Angular" + }, + { + "question": "Why do enterprise organizations overwhelmingly prefer Angular?", + "answer": "Standardized architecture across distributed teams, automated framework migrations via ng update, first-class TypeScript type safety, integrated XSS/CSRF security, and built-in hierarchical dependency injection.", + "source": "Why Angular" + }, + { + "question": "How has Modern Angular (v16–v19+) modernized compared to legacy Angular?", + "answer": "Introduced Standalone components by default (no NgModules), fine-grained Signals reactivity, built-in control flow (@if, @for), deferrable views (@defer), and zoneless change detection support.", + "source": "Why Angular" + } + ], + "recapNotes": [ + "1. **Complete Platform**: Angular is not a view library; it is a complete, opinionated application framework providing DI, routing, forms, HTTP client, and reactivity out of the box.", + "2. **Inversion of Control**: Angular controls the lifecycle and execution flow, calling your components and services according to architectural conventions.", + "3. **Enterprise Governance**: Standardized structure, built-in security, first-class TypeScript, and automated migrations (`ng update`) make Angular the premier choice for large, multi-team engineering organizations.", + "4. **Modern Renaissance**: Modern Angular (18+/19+) features Standalone components, fine-grained Signals, built-in control flow (`@if`, `@for`), `@defer`, and zoneless change detection.", + "5. **Architectural Parallels**: Angular mirrors enterprise backend architectures (like ASP.NET Core Clean Architecture), enabling consistent software engineering patterns across the full stack." + ], + "searchTokens": [ + "why", + "angular", + "foundations", + "architecture", + "window", + "document", + "$scope", + "ngmodule", + "@if", + "@for", + "@switch", + "@defer", + "zustand", + "axios", + "fetch", + "xmlhttprequest", + "provide", + "inject", + "tanstack", + "vue-router", + "formik", + "v-model", + "usestate", + "usememo", + "ref", + "reactive", + "computed", + "httpclientxsrfmodule", + "withxsrfconfiguration", + "testbed", + "ngoninit", + "signal()", + "computed()", + "effect()", + "interview", + "priority", + "frequency", + "recommended", + "depth", + "relevant" + ] + }, + { + "id": "02-spa-concepts", + "slug": "02-spa-concepts", + "title": "Single Page Application (SPA)", + "summary": "A Single Page Application (SPA) is a web application architecture where the browser loads a single HTML shell page (index.html) and associated JavaScript/CSS as...", + "category": "01-foundations", + "categoryTitle": "Foundations & Architecture", + "categoryBadge": "Basics", + "categoryIcon": "layers", + "categoryOrder": 1, + "order": "02", + "fileName": "02-spa-concepts.md", + "contentUrl": "assets/content/01-foundations/02-spa-concepts.md", + "wordCount": 2734, + "readingTime": 14, + "headings": [ + { + "level": 1, + "text": "Single Page Application (SPA)" + }, + { + "level": 1, + "text": "What Is A Single Page Application (SPA)?" + }, + { + "level": 1, + "text": "Why Does It Exist?" + }, + { + "level": 1, + "text": "Historical Context" + }, + { + "level": 3, + "text": "1. The Pre-AJAX Era" + }, + { + "level": 3, + "text": "2. The AJAX Breakthrough (2005)" + }, + { + "level": 3, + "text": "3. Early SPAs & Hash Routing" + }, + { + "level": 3, + "text": "4. Modern HTML5 History API & Universal SPAs" + }, + { + "level": 1, + "text": "Traditional MPA Flow vs Modern SPA Flow" + }, + { + "level": 1, + "text": "First Principles Explanation" + }, + { + "level": 1, + "text": "Real World Analogy" + }, + { + "level": 1, + "text": "How Angular Solves SPA Challenges" + } + ], + "flashcards": [ + { + "question": "What is a Single Page Application (SPA)?", + "answer": "A web application that loads a single HTML document shell once and dynamically rewrites the UI via client-side JavaScript by manipulating the DOM and exchanging raw JSON data asynchronously over HTTP APIs without full page reloads.", + "source": "Single Page Application (SPA)" + }, + { + "question": "What are the main differences between an SPA and a traditional Multi-Page Application (MPA)?", + "answer": "MPAs re-render entire HTML documents on the server for each request, causing white screen flashes and lost client state. SPAs render views in the browser, preserve client memory and state, and exchange lightweight JSON data.", + "source": "Single Page Application (SPA)" + }, + { + "question": "How does client-side routing work under the hood without refreshing the browser?", + "answer": "It intercepts anchor clicks via preventDefault(), updates the URL and history stack using the HTML5 History API (history.pushState), listens for popstate events, and dynamically renders the matched component inside .", + "source": "Single Page Application (SPA)" + }, + { + "question": "What are the primary challenges of SPAs and how does Angular solve them?", + "answer": "Initial load latency and SEO challenges. Angular solves these using Server-Side Rendering (@angular/ssr) with non-destructive hydration, event replay, route-level code splitting (loadComponent), and deferrable views (@defer).", + "source": "Single Page Application (SPA)" + }, + { + "question": "What server configuration is required to support deep linking in an SPA?", + "answer": "The web server (Nginx, Apache, or ASP.NET Core) must be configured with a fallback rule (e.g. try_files $uri $uri/ /index.html) to serve index.html for all non-file routes so the client-side router can resolve the path.", + "source": "Single Page Application (SPA)" + } + ], + "recapNotes": [ + "1. **Core Concept**: An SPA loads a single HTML document once; all subsequent view changes occur dynamically via client-side JavaScript DOM manipulation.", + "2. **Data vs Markup**: SPAs exchange lightweight JSON data payloads with backend APIs rather than full HTML documents, reducing server load and bandwidth.", + "3. **Client-Side Routing**: The HTML5 History API (`pushState`, `popstate`) updates the URL and history stack without triggering browser page reloads.", + "4. **Performance & SEO Solutions**: Modern Angular solves historical SPA trade-offs using Server-Side Rendering (SSR), non-destructive hydration, `@defer`, and route lazy loading.", + "5. **Server Fallback Rule**: All unmapped server-side routes must fallback to `index.html` to allow the client-side router to handle deep links." + ], + "searchTokens": [ + "single", + "page", + "application", + "spa", + "foundations", + "architecture", + "fetch", + "httpclient", + "xmlhttprequest", + "window", + "pushstate", + "replacestate", + "popstate", + "@defer", + "domsanitizer", + "ngondestroy", + "loadcomponent", + "takeuntildestroyed()", + "destroyref", + "interview", + "priority", + "frequency", + "recommended", + "depth", + "relevant", + "what", + "differences", + "between", + "mpa?", + "does", + "client", + "side", + "routing", + "work", + "under", + "hood", + "without", + "triggering", + "reload?", + "main" + ] + }, + { + "id": "03-component-based-architecture", + "slug": "03-component-based-architecture", + "title": "Component-Based Architecture", + "summary": "Component-Based Architecture is a software design approach where the user interface is composed of smaller, reusable, independent, and self-contained building b...", + "category": "01-foundations", + "categoryTitle": "Foundations & Architecture", + "categoryBadge": "Basics", + "categoryIcon": "layers", + "categoryOrder": 1, + "order": "03", + "fileName": "03-component-based-architecture.md", + "contentUrl": "assets/content/01-foundations/03-component-based-architecture.md", + "wordCount": 2205, + "readingTime": 12, + "headings": [ + { + "level": 1, + "text": "Component-Based Architecture" + }, + { + "level": 1, + "text": "What Is Component-Based Architecture?" + }, + { + "level": 1, + "text": "Why Does It Exist?" + }, + { + "level": 1, + "text": "Historical Context" + }, + { + "level": 3, + "text": "1. Early Web Applications" + }, + { + "level": 3, + "text": "2. The jQuery Era" + }, + { + "level": 3, + "text": "3. The Component Revolution" + }, + { + "level": 1, + "text": "Problem It Solves" + }, + { + "level": 3, + "text": "Without Components:" + }, + { + "level": 3, + "text": "With Components:" + }, + { + "level": 1, + "text": "First Principles Explanation" + }, + { + "level": 1, + "text": "Real World Analogy" + } + ], + "flashcards": [ + { + "question": "What is the core philosophy of Component-Based Architecture?", + "answer": "Decomposing complex user interfaces into independent, self-contained, and reusable building blocks that encapsulate their own template, styles, and controller logic.", + "source": "Component-Based Architecture" + }, + { + "question": "What is the difference between Smart (Container) and Dumb (Presentational) components?", + "answer": "Smart components manage state, inject services, and handle business flows. Dumb components receive data strictly via inputs, emit events via outputs, and focus purely on UI rendering with zero external dependencies.", + "source": "Component-Based Architecture" + }, + { + "question": "How does unidirectional data flow operate between parent and child components?", + "answer": "Data flows down from parent to child via property bindings (input()), and user events flow up from child to parent via custom event emitters (output()). Children never directly mutate parent state.", + "source": "Component-Based Architecture" + }, + { + "question": "Why did Angular introduce Signal-based component inputs and outputs?", + "answer": "Signal inputs (input(), input.required()) provide compile-time safety, integrate with computed() and effect(), eliminate ngOnChanges boilerplate, and support zoneless change detection.", + "source": "Component-Based Architecture" + } + ], + "recapNotes": [ + "1. **Atomic Building Block**: Components encapsulate template, styles, and TypeScript controller logic into reusable units.", + "2. **Unidirectional Data Flow**: Data flows down via `input()`, and user events flow up via `output()`.", + "3. **Smart vs Dumb Separation**: Decoupling business logic (containers) from visual presentation (dumb components) dramatically improves testability and reusability.", + "4. **Component Hierarchy**: Applications are structured as trees originating from `AppComponent`, driving change detection and dependency injection scoping.", + "5. **Modern Signal APIs**: Angular's `input()` and `output()` APIs replace legacy `@Input()`/`@Output()` decorators with compile-safe, fine-grained reactive primitives." + ], + "searchTokens": [ + "component", + "based", + "architecture", + "foundations", + "employeecardcomponent", + "@component", + "appcomponent", + "input()", + "output()", + "@input()", + "@output()", + "elementref", + "default", + "viewchild()", + "@viewchild()", + "@injectable", + "behaviorsubject", + "computed()", + "effect()", + "ngonchanges", + "_ngcontent-c42", + "eventcallback", + "@input", + "interview", + "priority", + "frequency", + "recommended", + "depth", + "relevant", + "what", + "architecture?", + "difference", + "between", + "smart", + "dumb", + "components?", + "components", + "communicate", + "angular?", + "tree" + ] + }, + { + "id": "04-angular-architecture-overview", + "slug": "04-angular-architecture-overview", + "title": "Angular Architecture Overview", + "summary": "Angular Architecture refers to the overall structure and organization of an Angular application.", + "category": "01-foundations", + "categoryTitle": "Foundations & Architecture", + "categoryBadge": "Basics", + "categoryIcon": "layers", + "categoryOrder": 1, + "order": "04", + "fileName": "04-angular-architecture-overview.md", + "contentUrl": "assets/content/01-foundations/04-angular-architecture-overview.md", + "wordCount": 1370, + "readingTime": 7, + "headings": [ + { + "level": 1, + "text": "Angular Architecture Overview" + }, + { + "level": 1, + "text": "What Is Angular Architecture?" + }, + { + "level": 1, + "text": "Why Does Angular Have a Defined Architecture?" + }, + { + "level": 1, + "text": "First Principles" + }, + { + "level": 1, + "text": "High-Level Architecture Diagram" + }, + { + "level": 1, + "text": "Core Building Blocks" + }, + { + "level": 2, + "text": "Components" + }, + { + "level": 3, + "text": "Component Responsibilities" + }, + { + "level": 2, + "text": "Templates" + }, + { + "level": 2, + "text": "Services" + }, + { + "level": 3, + "text": "Why Services Exist" + }, + { + "level": 2, + "text": "Dependency Injection (DI)" + } + ], + "flashcards": [ + { + "question": "What Is Angular Architecture?", + "answer": "Angular Architecture refers to the overall structure and organization of an Angular application. Angular is not just a UI library. Component-Based UI Development", + "source": "Angular Architecture Overview" + }, + { + "question": "Why Does Angular Have a Defined Architecture?", + "answer": "200 Screens 500 Components 100+ APIs", + "source": "Angular Architecture Overview" + }, + { + "question": "What is the role of a Component?", + "answer": "Responsible for presentation and handling user interactions.", + "source": "Angular Architecture Overview" + }, + { + "question": "Why do Services exist?", + "answer": "To move business and data-access logic out of Components.", + "source": "Angular Architecture Overview" + }, + { + "question": "Why does Angular use Dependency Injection?", + "answer": "To reduce coupling and improve testability and maintainability.", + "source": "Angular Architecture Overview" + } + ], + "recapNotes": [ + "1. Angular is a complete application framework.", + "2. Components represent the UI layer.", + "3. Templates define presentation.", + "4. Services contain business logic.", + "5. Dependency Injection manages dependencies.", + "6. HttpClient communicates with APIs." + ], + "searchTokens": [ + "angular", + "architecture", + "overview", + "foundations", + "interview", + "priority", + "frequency", + "recommended", + "depth", + "relevant", + "components", + "templates", + "services", + "dependency", + "injection", + "httpclient", + "routing", + "forms", + "state", + "management", + "what", + "main", + "building", + "blocks", + "angular?", + "role", + "component?", + "exist?", + "does", + "injection?", + "communicate", + "with", + "backend?", + "navigate", + "without", + "page", + "refresh?", + "typical", + "request", + "flow?" + ] + }, + { + "id": "05-typescript-essentials-for-angular", + "slug": "05-typescript-essentials-for-angular", + "title": "TypeScript Essentials for Angular", + "summary": "TypeScript is a superset of JavaScript that adds features such as:", + "category": "01-foundations", + "categoryTitle": "Foundations & Architecture", + "categoryBadge": "Basics", + "categoryIcon": "layers", + "categoryOrder": 1, + "order": "05", + "fileName": "05-typescript-essentials-for-angular.md", + "contentUrl": "assets/content/01-foundations/05-typescript-essentials-for-angular.md", + "wordCount": 2162, + "readingTime": 11, + "headings": [ + { + "level": 1, + "text": "TypeScript Essentials for Angular" + }, + { + "level": 1, + "text": "What Is TypeScript?" + }, + { + "level": 1, + "text": "Why Angular Chose TypeScript" + }, + { + "level": 1, + "text": "First Principles" + }, + { + "level": 1, + "text": "Why TypeScript Matters in Angular" + }, + { + "level": 1, + "text": "Angular-Relevant TypeScript Features" + }, + { + "level": 1, + "text": "1. Classes" + }, + { + "level": 2, + "text": "What Is a Class?" + }, + { + "level": 2, + "text": "Why Classes Matter in Angular" + }, + { + "level": 2, + "text": "Angular Perspective" + }, + { + "level": 1, + "text": "2. Interfaces" + }, + { + "level": 2, + "text": "What Is an Interface?" + } + ], + "flashcards": [ + { + "question": "Why did Google choose TypeScript as the foundation for Angular?", + "answer": "TypeScript provides static type safety, refactoring support, compile-time contract enforcement, decorator metadata for framework wiring, and parameter types essential for Dependency Injection.", + "source": "TypeScript Essentials for Angular" + }, + { + "question": "How does Angular Dependency Injection leverage TypeScript?", + "answer": "TypeScript emits type metadata for constructor parameters, allowing Angular’s injector to inspect parameter types at runtime and automatically inject the corresponding singleton service token.", + "source": "TypeScript Essentials for Angular" + }, + { + "question": "What is the difference between interface and type in TypeScript?", + "answer": "Interfaces define extensible object and class contracts. Types are aliases that can represent primitives, unions (|), intersections (&), tuples, and complex mapped transformations.", + "source": "TypeScript Essentials for Angular" + }, + { + "question": "What is the difference between any, unknown, and never in TypeScript?", + "answer": "any disables all type checking; unknown is a type-safe counterpart requiring narrowing before operations; never represents impossible states (e.g. exhaustive switch cases or functions that throw).", + "source": "TypeScript Essentials for Angular" + } + ], + "recapNotes": [ + "1. **Enterprise Foundation**: TypeScript gives Angular compile-time type checking, refactoring safety, and autocompletion essential for large multi-team applications.", + "2. **Dependency Injection Power**: TypeScript types serve as the tokens that power Angular's hierarchical DI container.", + "3. **Generics Everywhere**: Angular leverages Generics across `HttpClient`, `Signals`, and `RxJS` to ensure end-to-end data contract safety.", + "4. **Interfaces vs Types**: Use Interfaces for public API and domain entity contracts; use Types for unions, primitives, and complex mapped transformations.", + "5. **Strict Typing**: Enterprise standards mandate `\"strict\": true` and strict avoidance of `any`." + ], + "searchTokens": [ + "typescript", + "essentials", + "for", + "angular", + "foundations", + "architecture", + "reflect-metadata", + "emitdecoratormetadata", + "employeeservice", + "inject(employeeservice)", + "any", + "unknown", + "never", + "typeof", + "instanceof", + "readonly", + "httpclient", + "signals", + "rxjs", + "interview", + "priority", + "frequency", + "recommended", + "depth", + "relevant", + "what", + "class?", + "classes", + "matter", + "perspective", + "interface?", + "interfaces", + "exist", + "usage", + "interface", + "class", + "type?", + "useful?", + "type", + "public" + ] + }, + { + "id": "06-angular-components", + "slug": "06-angular-components", + "title": "Angular Components", + "summary": "A Component is a reusable, self-contained UI building block that controls a portion of the screen.", + "category": "02-core-angular", + "categoryTitle": "Core Angular Essentials", + "categoryBadge": "Core", + "categoryIcon": "cpu", + "categoryOrder": 2, + "order": "06", + "fileName": "06-angular-components.md", + "contentUrl": "assets/content/02-core-angular/06-angular-components.md", + "wordCount": 1512, + "readingTime": 8, + "headings": [ + { + "level": 1, + "text": "Angular Components" + }, + { + "level": 1, + "text": "What Is An Angular Component?" + }, + { + "level": 1, + "text": "Why Do Components Exist?" + }, + { + "level": 1, + "text": "First Principles" + }, + { + "level": 1, + "text": "Component-Based Architecture" + }, + { + "level": 1, + "text": "Angular Component Anatomy" + }, + { + "level": 1, + "text": "Anatomy Breakdown" + }, + { + "level": 2, + "text": "Component Class" + }, + { + "level": 2, + "text": "Component Metadata" + }, + { + "level": 2, + "text": "Selector" + }, + { + "level": 2, + "text": "Template" + }, + { + "level": 2, + "text": "Styles" + } + ], + "flashcards": [ + { + "question": "What Is An Angular Component?", + "answer": "A Component is a reusable, self-contained UI building block that controls a portion of the screen. Angular applications are built by combining multiple Components together.", + "source": "Angular Components" + }, + { + "question": "Why Do Components Exist?", + "answer": "As web applications grow, maintaining one large HTML, CSS, and JavaScript file becomes difficult. Code duplication Difficult maintenance", + "source": "Angular Components" + }, + { + "question": "What Are Standalone Components?", + "answer": "Standalone Components can be used without being declared inside an NgModule.", + "source": "Angular Components" + }, + { + "question": "What Should Components Do?", + "answer": "Displaying data Handling user interactions Managing UI state", + "source": "Angular Components" + }, + { + "question": "What Should Components NOT Do?", + "answer": "Complex Business Logic Caching Logic Authentication Logic", + "source": "Angular Components" + }, + { + "question": "What is a Component?", + "answer": "A reusable UI building block that encapsulates template, logic, and styling.", + "source": "Angular Components" + }, + { + "question": "What does @Component do?", + "answer": "It provides metadata that allows Angular to identify and configure a Component.", + "source": "Angular Components" + }, + { + "question": "What is a Selector?", + "answer": "A custom HTML element used to render a Component.", + "source": "Angular Components" + } + ], + "recapNotes": [ + "1. Components are the fundamental building blocks of Angular.", + "2. Angular applications are organized as Component Trees.", + "3. Components encapsulate template, logic, and styling.", + "4. State inside Components drives the UI.", + "5. Modern Angular promotes Standalone Components.", + "6. Components should focus on presentation and coordination." + ], + "searchTokens": [ + "angular", + "components", + "core", + "essentials", + "@component()", + "interview", + "priority", + "frequency", + "recommended", + "depth", + "relevant", + "component", + "class", + "metadata", + "selector", + "template", + "styles", + "state", + "driven", + "modern", + "legacy", + "smart", + "dumb", + "what", + "component?", + "exist?", + "does", + "@component", + "selector?", + "standalone", + "introduce", + "components?", + "should", + "belong", + "definition", + "responsibilities", + "concepts", + "common", + "questions", + "senior" + ] + }, + { + "id": "07-templates-and-data-binding", + "slug": "07-templates-and-data-binding", + "title": "Templates and Data Binding", + "summary": "A Template is the HTML view associated with an Angular Component.", + "category": "02-core-angular", + "categoryTitle": "Core Angular Essentials", + "categoryBadge": "Core", + "categoryIcon": "cpu", + "categoryOrder": 2, + "order": "07", + "fileName": "07-templates-and-data-binding.md", + "contentUrl": "assets/content/02-core-angular/07-templates-and-data-binding.md", + "wordCount": 1597, + "readingTime": 8, + "headings": [ + { + "level": 1, + "text": "Templates and Data Binding" + }, + { + "level": 1, + "text": "What Is a Template?" + }, + { + "level": 1, + "text": "Why Do Templates Exist?" + }, + { + "level": 1, + "text": "Component and Template Relationship" + }, + { + "level": 1, + "text": "What Is Data Binding?" + }, + { + "level": 1, + "text": "Why Data Binding Exists" + }, + { + "level": 1, + "text": "Types of Data Binding" + }, + { + "level": 1, + "text": "1. Interpolation" + }, + { + "level": 2, + "text": "What Is Interpolation?" + }, + { + "level": 2, + "text": "Flow" + }, + { + "level": 2, + "text": "Common Examples" + }, + { + "level": 2, + "text": "Typical Use Cases" + } + ], + "flashcards": [ + { + "question": "What Is a Template?", + "answer": "A Template is the HTML view associated with an Angular Component.", + "source": "Templates and Data Binding" + }, + { + "question": "What Is Interpolation?", + "answer": "Interpolation displays Component data inside a Template.", + "source": "Templates and Data Binding" + }, + { + "question": "What Is Property Binding?", + "answer": "Property Binding assigns Component values to DOM properties.", + "source": "Templates and Data Binding" + }, + { + "question": "What Is Event Binding?", + "answer": "Event Binding allows user actions to trigger Component methods.", + "source": "Templates and Data Binding" + }, + { + "question": "What is Two-Way Binding?", + "answer": "Two-Way Binding keeps a Component property and UI control synchronized in both directions.", + "source": "Templates and Data Binding" + }, + { + "question": "What is Data Binding?", + "answer": "The mechanism that synchronizes Component data and the UI.", + "source": "Templates and Data Binding" + }, + { + "question": "What are the four types of Data Binding?", + "answer": "1. Interpolation 2. Property Binding 3. Event Binding", + "source": "Templates and Data Binding" + } + ], + "recapNotes": [ + "1. Templates are Angular's view layer.", + "2. Templates extend HTML with Angular-specific capabilities.", + "3. Data Binding synchronizes Component state and the UI.", + "4. Angular provides four binding types:", + "Interpolation", + "Property Binding" + ], + "searchTokens": [ + "templates", + "and", + "data", + "binding", + "core", + "angular", + "essentials", + "@if", + "@for", + "interview", + "priority", + "frequency", + "recommended", + "depth", + "relevant", + "what", + "interpolation?", + "flow", + "common", + "examples", + "typical", + "cases", + "property", + "binding?", + "event", + "events", + "example", + "syntax", + "practical", + "interpolation", + "good", + "avoid", + "component", + "responsibilities", + "template", + "template?", + "four", + "types", + "difference", + "between" + ] + }, + { + "id": "08-directives", + "slug": "08-directives", + "title": "Directives", + "summary": "A Directive is a class that tells Angular how to modify an element's:", + "category": "02-core-angular", + "categoryTitle": "Core Angular Essentials", + "categoryBadge": "Core", + "categoryIcon": "cpu", + "categoryOrder": 2, + "order": "08", + "fileName": "08-directives.md", + "contentUrl": "assets/content/02-core-angular/08-directives.md", + "wordCount": 1192, + "readingTime": 6, + "headings": [ + { + "level": 1, + "text": "Directives" + }, + { + "level": 1, + "text": "What Is a Directive?" + }, + { + "level": 1, + "text": "First Principles" + }, + { + "level": 1, + "text": "Why Do Directives Exist?" + }, + { + "level": 1, + "text": "Angular Building Blocks" + }, + { + "level": 1, + "text": "Types of Directives" + }, + { + "level": 1, + "text": "1. Component Directives" + }, + { + "level": 1, + "text": "2. Structural Directives" + }, + { + "level": 1, + "text": "Structural Directive Mental Model" + }, + { + "level": 1, + "text": "Example: Conditional Rendering" + }, + { + "level": 1, + "text": "Legacy Version" + }, + { + "level": 1, + "text": "Example: Loops" + } + ], + "flashcards": [ + { + "question": "What is a Structural Directive?", + "answer": "A Structural Directive modifies the structure of the DOM by adding, removing, or repeating elements.", + "source": "Directives" + }, + { + "question": "What is a Directive?", + "answer": "A Directive is a class that modifies the behavior, appearance, or structure of DOM elements.", + "source": "Directives" + }, + { + "question": "Types of Directives?", + "answer": "1. Component Directives 2. Structural Directives 3. Attribute Directives", + "source": "Directives" + }, + { + "question": "What is an Attribute Directive?", + "answer": "A Directive that changes appearance or behavior without modifying DOM structure.", + "source": "Directives" + }, + { + "question": "Why Only One Structural Directive Per Element?", + "answer": "Angular can apply only one structural transformation to an element at a time.", + "source": "Directives" + } + ], + "recapNotes": [ + "Mid-Level Interviews", + "Senior Developer Interviews", + "Lead Developer Interviews" + ], + "searchTokens": [ + "directives", + "core", + "angular", + "essentials", + "interview", + "priority", + "frequency", + "recommended", + "depth", + "relevant", + "native", + "property", + "directive", + "input", + "structural", + "attribute", + "trap", + "what", + "directive?", + "types", + "directives?", + "examples", + "difference", + "between", + "only", + "element?" + ] + }, + { + "id": "09-services", + "slug": "09-services", + "title": "Services", + "summary": "A Service is a reusable TypeScript class that contains business logic, shared functionality, or data-access logic that can be used across multiple Components.", + "category": "02-core-angular", + "categoryTitle": "Core Angular Essentials", + "categoryBadge": "Core", + "categoryIcon": "cpu", + "categoryOrder": 2, + "order": "09", + "fileName": "09-services.md", + "contentUrl": "assets/content/02-core-angular/09-services.md", + "wordCount": 631, + "readingTime": 4, + "headings": [ + { + "level": 1, + "text": "Services" + }, + { + "level": 1, + "text": "What Is a Service?" + }, + { + "level": 1, + "text": "Why Do Services Exist?" + }, + { + "level": 1, + "text": "First Principles" + }, + { + "level": 1, + "text": "Before Services" + }, + { + "level": 1, + "text": "After Services" + }, + { + "level": 1, + "text": "Angular Architecture Perspective" + }, + { + "level": 1, + "text": ".NET Comparison" + }, + { + "level": 1, + "text": "Typical Service Example" + }, + { + "level": 1, + "text": "What Responsibilities Belong in a Service?" + }, + { + "level": 2, + "text": "API Communication" + }, + { + "level": 2, + "text": "Business Logic" + } + ], + "flashcards": [ + { + "question": "What Is a Service?", + "answer": "A Service is a reusable TypeScript class that contains business logic, shared functionality, or data-access logic that can be used across multiple Components.", + "source": "Services" + } + ], + "recapNotes": [ + "Mid-Level Interviews", + "Senior Developer Interviews", + "Lead Developer Interviews", + "Architect Discussions" + ], + "searchTokens": [ + "services", + "core", + "angular", + "essentials", + "interview", + "priority", + "frequency", + "recommended", + "depth", + "relevant", + "communication", + "business", + "logic", + "validation", + "state", + "management", + "caching", + "shared", + "functionality", + "component", + "service", + "reusability", + "maintainability", + "testability", + "separation", + "concerns", + "scalability" + ] + }, + { + "id": "10-dependency-injection", + "slug": "10-dependency-injection", + "title": "Dependency Injection (DI)", + "summary": "Dependency Injection (DI) is a design pattern where an object receives its dependencies from an external source instead of creating them itself.", + "category": "02-core-angular", + "categoryTitle": "Core Angular Essentials", + "categoryBadge": "Core", + "categoryIcon": "cpu", + "categoryOrder": 2, + "order": "10", + "fileName": "10-dependency-injection.md", + "contentUrl": "assets/content/02-core-angular/10-dependency-injection.md", + "wordCount": 2238, + "readingTime": 12, + "headings": [ + { + "level": 1, + "text": "Dependency Injection (DI)" + }, + { + "level": 1, + "text": "What is Dependency Injection?" + }, + { + "level": 1, + "text": "Why Does Dependency Injection Exist?" + }, + { + "level": 1, + "text": "First Principles" + }, + { + "level": 1, + "text": "The Core Idea" + }, + { + "level": 1, + "text": "Restaurant Analogy" + }, + { + "level": 1, + "text": "Key Terminology" + }, + { + "level": 1, + "text": "Dependency" + }, + { + "level": 1, + "text": "Injection" + }, + { + "level": 1, + "text": "Provider" + }, + { + "level": 1, + "text": "Injector" + }, + { + "level": 1, + "text": "ASP.NET Core Comparison" + } + ], + "flashcards": [ + { + "question": "What is Dependency Injection?", + "answer": "Dependency Injection (DI) is a design pattern where an object receives its dependencies from an external source instead of creating them itself.", + "source": "Dependency Injection (DI)" + }, + { + "question": "Why is DI important?", + "answer": "Maintainability Testability Reusability", + "source": "Dependency Injection (DI)" + }, + { + "question": "What is a Dependency?", + "answer": "An object required by another object.", + "source": "Dependency Injection (DI)" + }, + { + "question": "What is an Injector?", + "answer": "Angular's container responsible for creating and providing dependencies.", + "source": "Dependency Injection (DI)" + }, + { + "question": "What is a Provider?", + "answer": "Configuration that tells Angular how to create a dependency.", + "source": "Dependency Injection (DI)" + }, + { + "question": "What does providedIn: 'root' mean?", + "answer": "Registers the service at application level and generally creates a singleton instance.", + "source": "Dependency Injection (DI)" + }, + { + "question": "What is @Injectable()?", + "answer": "A decorator indicating that a class participates in Angular's Dependency Injection system.", + "source": "Dependency Injection (DI)" + }, + { + "question": "Constructor Injection vs inject()?", + "answer": "Constructor injection is the traditional approach. inject() is the modern approach and useful in functional APIs.", + "source": "Dependency Injection (DI)" + } + ], + "recapNotes": [ + "1. Dependency Injection means receiving dependencies rather than creating them.", + "2. DI solves tight coupling problems.", + "3. Angular uses an Injector to create and manage dependencies.", + "4. Providers tell Angular how to create dependencies.", + "5. `providedIn: 'root'` typically creates application-wide singleton services.", + "6. Angular supports both constructor injection and `inject()`." + ], + "searchTokens": [ + "dependency", + "injection", + "core", + "angular", + "essentials", + "inject()", + "interview", + "priority", + "frequency", + "recommended", + "depth", + "relevant", + "loose", + "coupling", + "root", + "service", + "component", + "service?", + "scoped", + "constructor", + "inject", + "what", + "injection?", + "important?", + "dependency?", + "injector?", + "provider?", + "does", + "providedin:", + "'root'", + "mean?", + "@injectable", + "better", + "than", + "new?", + "trap", + "definition", + "concepts", + "typical", + "flow" + ] + }, + { + "id": "11-http-client", + "slug": "11-http-client", + "title": "HttpClient", + "summary": "HttpClient is Angular's built-in service for communicating with HTTP endpoints.", + "category": "02-core-angular", + "categoryTitle": "Core Angular Essentials", + "categoryBadge": "Core", + "categoryIcon": "cpu", + "categoryOrder": 2, + "order": "11", + "fileName": "11-http-client.md", + "contentUrl": "assets/content/02-core-angular/11-http-client.md", + "wordCount": 1692, + "readingTime": 9, + "headings": [ + { + "level": 1, + "text": "HttpClient" + }, + { + "level": 1, + "text": "What Is HttpClient?" + }, + { + "level": 1, + "text": "Why Do We Need HttpClient?" + }, + { + "level": 1, + "text": "First Principles" + }, + { + "level": 1, + "text": "Evolution of HTTP Communication" + }, + { + "level": 2, + "text": "XMLHttpRequest" + }, + { + "level": 2, + "text": "Fetch API" + }, + { + "level": 2, + "text": "Angular HttpClient" + }, + { + "level": 1, + "text": "Angular Architecture Context" + }, + { + "level": 1, + "text": "Why API Calls Should Not Live In Components" + }, + { + "level": 1, + "text": "Responsibilities" + }, + { + "level": 2, + "text": "Component" + } + ], + "flashcards": [ + { + "question": "What Is HttpClient?", + "answer": "HttpClient is Angular's built-in service for communicating with HTTP endpoints.", + "source": "HttpClient" + }, + { + "question": "Why Do We Need HttpClient?", + "answer": "Most Angular applications are not standalone systems.", + "source": "HttpClient" + }, + { + "question": "Why use HttpClient instead of Fetch?", + "answer": "Strong Typing Observables Interceptors", + "source": "HttpClient" + }, + { + "question": "Why does HttpClient return Observable?", + "answer": "Because HTTP operations are asynchronous.", + "source": "HttpClient" + }, + { + "question": "What are Interceptors?", + "answer": "Middleware-like mechanisms for modifying requests and responses globally.", + "source": "HttpClient" + } + ], + "recapNotes": [ + "1. HttpClient is Angular's HTTP communication service.", + "2. API calls should usually live in Services.", + "3. HttpClient supports GET, POST, PUT, PATCH, and DELETE.", + "4. Strong typing is achieved using TypeScript Generics.", + "5. HttpClient returns Observables because HTTP operations are asynchronous.", + "6. Modern Angular uses `provideHttpClient()`." + ], + "searchTokens": [ + "httpclient", + "core", + "angular", + "essentials", + "providehttpclient()", + "interview", + "priority", + "frequency", + "recommended", + "depth", + "relevant", + "xmlhttprequest", + "fetch", + "component", + "service", + "modern", + "legacy", + "setup", + "what", + "httpclient?", + "instead", + "fetch?", + "where", + "should", + "calls", + "live?", + "does", + "return", + "observable?", + "difference", + "between", + "patch?", + "interceptors?", + "trap", + "configuration", + "crud", + "operations", + "typical", + "flow", + "type" + ] + }, + { + "id": "12-rxjs-and-observables-fundamentals", + "slug": "12-rxjs-and-observables-fundamentals", + "title": "RxJS & Observables Fundamentals", + "summary": "``typescript this.http.get( '/api/employees' ); ``", + "category": "03-intermediate", + "categoryTitle": "Intermediate Patterns", + "categoryBadge": "Intermediate", + "categoryIcon": "workflow", + "categoryOrder": 3, + "order": "12", + "fileName": "12-rxjs-and-observables-fundamentals.md", + "contentUrl": "assets/content/03-intermediate/12-rxjs-and-observables-fundamentals.md", + "wordCount": 1621, + "readingTime": 9, + "headings": [ + { + "level": 1, + "text": "RxJS & Observables Fundamentals" + }, + { + "level": 1, + "text": "Why Do We Need RxJS?" + }, + { + "level": 1, + "text": "The Real Problem Angular Needed To Solve" + }, + { + "level": 1, + "text": "What Is RxJS?" + }, + { + "level": 1, + "text": "The Most Important Word: Stream" + }, + { + "level": 1, + "text": "Example: Water Flow Analogy" + }, + { + "level": 1, + "text": "Real Angular Streams" + }, + { + "level": 1, + "text": "What Is An Observable?" + }, + { + "level": 1, + "text": "Observable Mental Model" + }, + { + "level": 1, + "text": "Basic Observable Example" + }, + { + "level": 1, + "text": "Why We Rarely Create Observables Manually" + }, + { + "level": 1, + "text": "Observable vs Array" + } + ], + "flashcards": [ + { + "question": "Why Do We Need RxJS?", + "answer": "Let's revisit a simple HttpClient call.", + "source": "RxJS & Observables Fundamentals" + }, + { + "question": "What Is An Observable?", + "answer": "An Observable is a data source that emits values over time. > An Observable is a producer of values that can be observed by subscribers.", + "source": "RxJS & Observables Fundamentals" + }, + { + "question": "What Is subscribe()?", + "answer": "Observable produces values. We need something to consume them.", + "source": "RxJS & Observables Fundamentals" + }, + { + "question": "What is RxJS?", + "answer": "A library for reactive programming using Observables.", + "source": "RxJS & Observables Fundamentals" + }, + { + "question": "Why does HttpClient return Observables?", + "answer": "Because HTTP operations are asynchronous.", + "source": "RxJS & Observables Fundamentals" + }, + { + "question": "What are Operators?", + "answer": "Functions used to transform and process Observable streams.", + "source": "RxJS & Observables Fundamentals" + }, + { + "question": "What does pipe() do?", + "answer": "Allows chaining multiple RxJS operators.", + "source": "RxJS & Observables Fundamentals" + }, + { + "question": "What is a Cold Observable?", + "answer": "An Observable where each subscriber gets a separate execution.", + "source": "RxJS & Observables Fundamentals" + } + ], + "recapNotes": [ + "1. RxJS is Angular's reactive programming library.", + "2. Observables represent streams of values over time.", + "3. HttpClient returns Observables because HTTP requests are asynchronous.", + "4. `subscribe()` consumes Observable values.", + "5. Operators transform streams.", + "6. `pipe()` chains operators together." + ], + "searchTokens": [ + "rxjs", + "observables", + "fundamentals", + "intermediate", + "patterns", + "subscribe()", + "pipe()", + "interview", + "priority", + "frequency", + "recommended", + "depth", + "relevant", + "array", + "observable", + "promise", + "httpclient", + "form", + "changes", + "route", + "parameters", + "events", + "timers", + "what", + "type", + "httpclient?", + "rxjs?", + "observable?", + "does", + "return", + "observables?", + "subscribe", + "promise?", + "operators?", + "pipe", + "cold", + "trap", + "core", + "concepts", + "lifecycle" + ] + }, + { + "id": "13-component-communication", + "slug": "13-component-communication", + "title": "Component Communication", + "summary": "Component Communication refers to the mechanisms Angular provides for exchanging data and events between components.", + "category": "03-intermediate", + "categoryTitle": "Intermediate Patterns", + "categoryBadge": "Intermediate", + "categoryIcon": "workflow", + "categoryOrder": 3, + "order": "13", + "fileName": "13-component-communication.md", + "contentUrl": "assets/content/03-intermediate/13-component-communication.md", + "wordCount": 1496, + "readingTime": 8, + "headings": [ + { + "level": 1, + "text": "Component Communication" + }, + { + "level": 1, + "text": "What Is Component Communication?" + }, + { + "level": 1, + "text": "First Principles" + }, + { + "level": 1, + "text": "Types of Component Communication" + }, + { + "level": 1, + "text": "Communication Overview" + }, + { + "level": 1, + "text": "Parent to Child Communication" + }, + { + "level": 1, + "text": "What Is @Input()?" + }, + { + "level": 1, + "text": "Child Component Example" + }, + { + "level": 1, + "text": "Parent Component Example" + }, + { + "level": 1, + "text": "Flow Diagram" + }, + { + "level": 1, + "text": "Important Observation" + }, + { + "level": 1, + "text": "Interview Question" + } + ], + "flashcards": [ + { + "question": "What Is Component Communication?", + "answer": "Component Communication refers to the mechanisms Angular provides for exchanging data and events between components. In real-world applications, components rarely exist in isolation.", + "source": "Component Communication" + }, + { + "question": "What Is @Input()?", + "answer": "@Input() allows a Parent Component to pass data into a Child Component.", + "source": "Component Communication" + }, + { + "question": "What Is @Output()?", + "answer": "@Output() allows a Child Component to emit custom events to its Parent Component.", + "source": "Component Communication" + }, + { + "question": "What Is EventEmitter?", + "answer": "EventEmitter is Angular's mechanism for publishing custom events from Child Components.", + "source": "Component Communication" + }, + { + "question": "What Does emit() Do?", + "answer": "Raises an event and notifies subscribers.", + "source": "Component Communication" + }, + { + "question": "How Do Sibling Components Communicate?", + "answer": "Typically using a shared Service.", + "source": "Component Communication" + }, + { + "question": "What Is ViewChild?", + "answer": "A mechanism for obtaining a reference to a Child Component or DOM element.", + "source": "Component Communication" + }, + { + "question": "Why Avoid Excessive ViewChild Usage?", + "answer": "Because it creates tight coupling between components.", + "source": "Component Communication" + } + ], + "recapNotes": [ + "1. Components frequently need to exchange data and events.", + "2. Parent-to-Child communication uses `@Input()`.", + "3. Child-to-Parent communication uses `@Output()`.", + "4. EventEmitter powers custom events.", + "5. Sibling components should communicate via shared Services.", + "6. `@ViewChild()` provides direct access to Child Components." + ], + "searchTokens": [ + "component", + "communication", + "intermediate", + "patterns", + "@input()", + "@output()", + "@viewchild()", + "interview", + "priority", + "frequency", + "recommended", + "depth", + "relevant", + "what", + "@input", + "parent", + "child", + "communication?", + "does", + "send", + "data", + "child?", + "parent?", + "eventemitter?", + "emit", + "sibling", + "components", + "communicate?", + "viewchild?", + "avoid", + "excessive", + "viewchild", + "usage?", + "support", + "trap", + "raise", + "event", + "direct", + "reference", + "flow" + ] + }, + { + "id": "14-lifecycle-hooks", + "slug": "14-lifecycle-hooks", + "title": "Lifecycle Hooks", + "summary": "Every Angular Component goes through a lifecycle.", + "category": "03-intermediate", + "categoryTitle": "Intermediate Patterns", + "categoryBadge": "Intermediate", + "categoryIcon": "workflow", + "categoryOrder": 3, + "order": "14", + "fileName": "14-lifecycle-hooks.md", + "contentUrl": "assets/content/03-intermediate/14-lifecycle-hooks.md", + "wordCount": 1496, + "readingTime": 8, + "headings": [ + { + "level": 1, + "text": "Lifecycle Hooks" + }, + { + "level": 1, + "text": "What Are Lifecycle Hooks?" + }, + { + "level": 1, + "text": "Why Do Lifecycle Hooks Exist?" + }, + { + "level": 1, + "text": "Lifecycle Overview" + }, + { + "level": 1, + "text": "Complete Lifecycle Order" + }, + { + "level": 1, + "text": "Constructor" + }, + { + "level": 1, + "text": "Purpose Of Constructor" + }, + { + "level": 1, + "text": "Avoid Doing This In Constructor" + }, + { + "level": 1, + "text": "Constructor vs ngOnInit" + }, + { + "level": 2, + "text": "Constructor" + }, + { + "level": 2, + "text": "ngOnInit" + }, + { + "level": 1, + "text": "ngOnChanges" + } + ], + "flashcards": [ + { + "question": "What is the difference between ngOnInit and the constructor in Angular?", + "answer": "The constructor is standard TypeScript instantiation for dependency injection. ngOnInit is called by Angular after the component’s @Input properties are initialized and data-bound for the first time.", + "source": "Lifecycle Hooks" + }, + { + "question": "Why is ngOnDestroy critical for enterprise Angular applications?", + "answer": "It cleans up resources to prevent memory leaks, such as unsubscribing from RxJS observables, disconnecting MutationObservers or WebSockets, and clearing intervals/timeouts.", + "source": "Lifecycle Hooks" + } + ], + "recapNotes": [ + "1. Components have a lifecycle managed by Angular.", + "2. Lifecycle Hooks execute at specific stages.", + "3. Constructor is for Dependency Injection, not business logic.", + "4. `ngOnInit()` is commonly used for initialization and API calls.", + "5. `ngOnChanges()` reacts to Input changes.", + "6. `ngAfterViewInit()` is where ViewChild becomes available." + ], + "searchTokens": [ + "lifecycle", + "hooks", + "intermediate", + "patterns", + "@input()", + "@viewchild()", + "ngoninit()", + "ngonchanges()", + "ngafterviewinit()", + "ngondestroy()", + "interview", + "priority", + "frequency", + "recommended", + "depth", + "relevant", + "constructor", + "ngoninit", + "ngonchanges?", + "ngonchanges", + "when", + "viewchild", + "available?", + "commonly", + "used", + "occasionally", + "rarely", + "what", + "hooks?", + "difference", + "between", + "ngoninit?", + "does", + "execute?", + "which", + "hook", + "runs", + "only", + "once?", + "should" + ] + }, + { + "id": "15-forms", + "slug": "15-forms", + "title": "Forms", + "summary": "A Form is a mechanism for collecting, validating, and processing user input.", + "category": "03-intermediate", + "categoryTitle": "Intermediate Patterns", + "categoryBadge": "Intermediate", + "categoryIcon": "workflow", + "categoryOrder": 3, + "order": "15", + "fileName": "15-forms.md", + "contentUrl": "assets/content/03-intermediate/15-forms.md", + "wordCount": 1772, + "readingTime": 9, + "headings": [ + { + "level": 1, + "text": "Forms" + }, + { + "level": 1, + "text": "What Is A Form?" + }, + { + "level": 1, + "text": "Why Do We Need Forms?" + }, + { + "level": 1, + "text": "Angular Form Approaches" + }, + { + "level": 1, + "text": "High-Level Comparison" + }, + { + "level": 2, + "text": "Template-Driven Forms" + }, + { + "level": 2, + "text": "Reactive Forms" + }, + { + "level": 1, + "text": "Interview Answer" + }, + { + "level": 3, + "text": "Which Form Approach Is Preferred In Enterprise Applications?" + }, + { + "level": 1, + "text": "Template-Driven Forms" + }, + { + "level": 1, + "text": "Configuration" + }, + { + "level": 1, + "text": "Simple Example" + } + ], + "flashcards": [ + { + "question": "What Is A Form?", + "answer": "A Form is a mechanism for collecting, validating, and processing user input.", + "source": "Forms" + }, + { + "question": "What Is FormArray?", + "answer": "Dynamic collection of controls.", + "source": "Forms" + }, + { + "question": "What Is FormBuilder?", + "answer": "Utility for creating forms with less boilerplate.", + "source": "Forms" + }, + { + "question": "What Is valueChanges?", + "answer": "Observable that emits whenever a form value changes.", + "source": "Forms" + } + ], + "recapNotes": [ + "1. Angular supports Template-Driven and Reactive Forms.", + "2. Reactive Forms are preferred for enterprise applications.", + "3. FormControl represents a single field.", + "4. FormGroup represents multiple controls.", + "5. FormArray supports dynamic collections.", + "6. Validators provide built-in validation support." + ], + "searchTokens": [ + "forms", + "intermediate", + "patterns", + "interview", + "priority", + "frequency", + "recommended", + "depth", + "relevant", + "template", + "driven", + "reactive", + "valid", + "invalid", + "pristine", + "dirty", + "untouched", + "touched", + "setvalue", + "patchvalue", + "what", + "angular", + "form", + "types?", + "which", + "preferred", + "enterprise", + "applications?", + "formcontrol?", + "formgroup?", + "formarray?", + "formbuilder?", + "valuechanges?", + "difference", + "between", + "touched?", + "patchvalue?", + "trap", + "types", + "building" + ] + }, + { + "id": "16-subjects-and-behaviorsubject", + "slug": "16-subjects-and-behaviorsubject", + "title": "Subjects & BehaviorSubject", + "summary": "``typescript Observable ``", + "category": "04-reactivity", + "categoryTitle": "Reactivity, RxJS & Signals", + "categoryBadge": "Must Know", + "categoryIcon": "zap", + "categoryOrder": 4, + "order": "16", + "fileName": "16-subjects-and-behaviorsubject.md", + "contentUrl": "assets/content/04-reactivity/16-subjects-and-behaviorsubject.md", + "wordCount": 1795, + "readingTime": 9, + "headings": [ + { + "level": 1, + "text": "Subjects & BehaviorSubject" + }, + { + "level": 1, + "text": "Why Do We Need Subjects?" + }, + { + "level": 1, + "text": "The Missing Problem" + }, + { + "level": 1, + "text": "What Is A Subject?" + }, + { + "level": 1, + "text": "Simple Mental Model" + }, + { + "level": 1, + "text": "Visual Representation" + }, + { + "level": 1, + "text": "Subject Is Like A Broadcast Channel" + }, + { + "level": 1, + "text": "Creating A Subject" + }, + { + "level": 1, + "text": "Subscribing To Subject" + }, + { + "level": 1, + "text": "Emitting Values" + }, + { + "level": 1, + "text": "Multiple Subscribers" + }, + { + "level": 1, + "text": "Multicasting" + } + ], + "flashcards": [ + { + "question": "What is the difference between Subject, BehaviorSubject, and ReplaySubject?", + "answer": "Subject emits only future events. BehaviorSubject requires an initial value and emits the current/latest value to new subscribers immediately. ReplaySubject buffers a configured number of past values and replays them to new subscribers.", + "source": "Subjects & BehaviorSubject" + }, + { + "question": "Why should you expose Subjects as Observables in services using asObservable()?", + "answer": "Encapsulation: it prevents external consumers from calling .next() or manipulating internal state directly, ensuring only the service controls mutations.", + "source": "Subjects & BehaviorSubject" + } + ], + "recapNotes": [ + "1. Subject is both an Observable and Observer.", + "2. Subjects support multicasting.", + "3. Subject does not remember previous values.", + "4. BehaviorSubject remembers the latest value.", + "5. BehaviorSubject requires an initial value.", + "6. New subscribers immediately receive the latest value." + ], + "searchTokens": [ + "subjects", + "behaviorsubject", + "reactivity", + "rxjs", + "signals", + "interview", + "priority", + "frequency", + "recommended", + "depth", + "relevant", + "subject", + "replaysubject", + "asyncsubject", + "eventemitter", + "what", + "subject?", + "behaviorsubject?", + "does", + "require", + "initial", + "value?", + "difference", + "between", + "services?", + "next", + "getvalue", + "sibling", + "components", + "communicate?", + "trap", + "characteristics", + "common", + "angular", + "service", + "pattern", + "updating", + "state", + "reading", + "message" + ] + }, + { + "id": "17-rxjs-operators-deep-dive", + "slug": "17-rxjs-operators-deep-dive", + "title": "RxJS Operators Deep Dive", + "summary": "Before learning operators, let's understand the problem.", + "category": "04-reactivity", + "categoryTitle": "Reactivity, RxJS & Signals", + "categoryBadge": "Must Know", + "categoryIcon": "zap", + "categoryOrder": 4, + "order": "17", + "fileName": "17-rxjs-operators-deep-dive.md", + "contentUrl": "assets/content/04-reactivity/17-rxjs-operators-deep-dive.md", + "wordCount": 2003, + "readingTime": 11, + "headings": [ + { + "level": 1, + "text": "RxJS Operators Deep Dive" + }, + { + "level": 1, + "text": "Why Do RxJS Operators Exist?" + }, + { + "level": 1, + "text": "Mental Model For Every Operator" + }, + { + "level": 1, + "text": "What Is pipe()?" + }, + { + "level": 1, + "text": "Operator Categories" + }, + { + "level": 1, + "text": "map()" + }, + { + "level": 2, + "text": "Purpose" + }, + { + "level": 1, + "text": "First Principle" + }, + { + "level": 1, + "text": "Example" + }, + { + "level": 1, + "text": "Real Angular Example" + }, + { + "level": 1, + "text": "Mental Model" + }, + { + "level": 1, + "text": "filter()" + } + ], + "flashcards": [ + { + "question": "Why Do RxJS Operators Exist?", + "answer": "Before learning operators, let's understand the problem.", + "source": "RxJS Operators Deep Dive" + }, + { + "question": "What does distinctUntilChanged() do?", + "answer": "Suppress consecutive duplicate values.", + "source": "RxJS Operators Deep Dive" + }, + { + "question": "When should switchMap() be used?", + "answer": "When only the latest result matters.", + "source": "RxJS Operators Deep Dive" + }, + { + "question": "What does catchError() do?", + "answer": "Handles stream errors gracefully.", + "source": "RxJS Operators Deep Dive" + } + ], + "recapNotes": [ + "1. Operators process and control data streams.", + "2. `pipe()` creates a stream processing pipeline.", + "3. `map()` transforms data.", + "4. `filter()` removes unwanted values.", + "5. `tap()` performs side effects without modifying data.", + "6. `debounceTime()` reduces rapid emissions." + ], + "searchTokens": [ + "rxjs", + "operators", + "deep", + "dive", + "reactivity", + "signals", + "pipe()", + "map()", + "filter()", + "tap()", + "debouncetime()", + "distinctuntilchanged()", + "switchmap()", + "concatmap()", + "mergemap()", + "exhaustmap()", + "combinelatest()", + "forkjoin()", + "catcherror()", + "retry()", + "interview", + "priority", + "frequency", + "recommended", + "depth", + "relevant", + "purpose", + "switchmap", + "concatmap", + "mergemap", + "exhaustmap", + "combinelatest", + "forkjoin", + "transformation", + "filtering", + "side", + "effects", + "search", + "optimization", + "request" + ] + }, + { + "id": "17a-common-enterprise-rxjs-scenarios", + "slug": "17a-common-enterprise-rxjs-scenarios", + "title": "Common Enterprise RxJS Scenarios", + "summary": "Most Angular developers learn RxJS operators like:", + "category": "04-reactivity", + "categoryTitle": "Reactivity, RxJS & Signals", + "categoryBadge": "Must Know", + "categoryIcon": "zap", + "categoryOrder": 4, + "order": "17a", + "fileName": "17a-common-enterprise-rxjs-scenarios.md", + "contentUrl": "assets/content/04-reactivity/17a-common-enterprise-rxjs-scenarios.md", + "wordCount": 1539, + "readingTime": 8, + "headings": [ + { + "level": 1, + "text": "Common Enterprise RxJS Scenarios" + }, + { + "level": 1, + "text": "Why This Chapter Exists" + }, + { + "level": 1, + "text": "Scenario 1: Search Box / Type-Ahead Search" + }, + { + "level": 2, + "text": "Business Requirement" + }, + { + "level": 2, + "text": "Bad Solution" + }, + { + "level": 2, + "text": "Recommended Solution" + }, + { + "level": 2, + "text": "Why?" + }, + { + "level": 3, + "text": "debounceTime()" + }, + { + "level": 3, + "text": "distinctUntilChanged()" + }, + { + "level": 3, + "text": "switchMap()" + }, + { + "level": 2, + "text": "Interview Rule" + }, + { + "level": 1, + "text": "Scenario 2: Prevent Double Submit" + } + ], + "flashcards": [ + { + "question": "What is a key architectural rule regarding Common Enterprise RxJS Scenarios (Point 1)?", + "answer": "Operator selection is more important than operator syntax.", + "source": "Common Enterprise RxJS Scenarios" + }, + { + "question": "What is a key architectural rule regarding Common Enterprise RxJS Scenarios (Point 2)?", + "answer": "switchMap() is ideal when only the latest result matters.", + "source": "Common Enterprise RxJS Scenarios" + }, + { + "question": "What is a key architectural rule regarding Common Enterprise RxJS Scenarios (Point 3)?", + "answer": "concatMap() preserves execution order.", + "source": "Common Enterprise RxJS Scenarios" + }, + { + "question": "What is a key architectural rule regarding Common Enterprise RxJS Scenarios (Point 4)?", + "answer": "mergeMap() runs streams concurrently.", + "source": "Common Enterprise RxJS Scenarios" + } + ], + "recapNotes": [ + "1. Operator selection is more important than operator syntax.", + "2. `switchMap()` is ideal when only the latest result matters.", + "3. `concatMap()` preserves execution order.", + "4. `mergeMap()` runs streams concurrently.", + "5. `exhaustMap()` prevents duplicate concurrent operations.", + "6. `combineLatest()` combines multiple ongoing streams." + ], + "searchTokens": [ + "common", + "enterprise", + "rxjs", + "scenarios", + "reactivity", + "signals", + "switchmap()", + "concatmap()", + "mergemap()", + "exhaustmap()", + "combinelatest()", + "forkjoin()", + "interview", + "priority", + "frequency", + "recommended", + "depth", + "relevant", + "business", + "requirement", + "solution", + "why?", + "rule", + "problem", + "what", + "happens?", + "real", + "examples", + "execution", + "order", + "mergemap?", + "benefits", + "option", + "example", + "result", + ".net", + "mental", + "model", + "modern", + "angular" + ] + }, + { + "id": "17b-async-pipe-and-rxjs-template-patterns", + "slug": "17b-async-pipe-and-rxjs-template-patterns", + "title": "Async Pipe & RxJS Template Patterns", + "summary": "Before learning syntax, understand the problem.", + "category": "04-reactivity", + "categoryTitle": "Reactivity, RxJS & Signals", + "categoryBadge": "Must Know", + "categoryIcon": "zap", + "categoryOrder": 4, + "order": "17b", + "fileName": "17b-async-pipe-and-rxjs-template-patterns.md", + "contentUrl": "assets/content/04-reactivity/17b-async-pipe-and-rxjs-template-patterns.md", + "wordCount": 1343, + "readingTime": 7, + "headings": [ + { + "level": 1, + "text": "Async Pipe & RxJS Template Patterns" + }, + { + "level": 1, + "text": "Why Does Async Pipe Exist?" + }, + { + "level": 1, + "text": "Traditional RxJS Approach" + }, + { + "level": 1, + "text": "Problems" + }, + { + "level": 1, + "text": "Angular Solution" + }, + { + "level": 1, + "text": "What Is Async Pipe?" + }, + { + "level": 1, + "text": "Simplest Example" + }, + { + "level": 1, + "text": "What Happens Internally?" + }, + { + "level": 1, + "text": "Mental Model" + }, + { + "level": 1, + "text": "First Example" + }, + { + "level": 1, + "text": "Why The Parentheses?" + }, + { + "level": 1, + "text": "HTTP Request Example" + } + ], + "flashcards": [ + { + "question": "Why Does Async Pipe Exist?", + "answer": "Before learning syntax, understand the problem.", + "source": "Async Pipe & RxJS Template Patterns" + }, + { + "question": "What Is \"as employees\"?", + "answer": "This syntax stores emitted value locally.", + "source": "Async Pipe & RxJS Template Patterns" + }, + { + "question": "What Is Async Pipe?", + "answer": "Angular pipe that subscribes to Observables and Promises in templates.", + "source": "Async Pipe & RxJS Template Patterns" + } + ], + "recapNotes": [ + "1. Async Pipe subscribes to Observables automatically.", + "2. Async Pipe unsubscribes automatically.", + "3. Async Pipe reduces boilerplate.", + "4. Async Pipe helps prevent memory leaks.", + "5. Async Pipe is ideal for template rendering.", + "6. Manual subscriptions are still useful for business logic." + ], + "searchTokens": [ + "async", + "pipe", + "rxjs", + "template", + "patterns", + "reactivity", + "signals", + "interview", + "priority", + "frequency", + "recommended", + "depth", + "relevant", + "manual", + "subscribe", + "rendering", + "data", + "performing", + "actions", + "pattern", + "what", + "pipe?", + "does", + "automatically?", + "unsubscribe", + "subscribe?", + "used", + "with", + "signals?", + "trap", + "display", + "observable", + "execute", + "logic", + "automatic", + "works", + "observables", + "needed", + "signal", + "access" + ] + }, + { + "id": "18-signals", + "slug": "18-signals", + "title": "Signals", + "summary": "This is the most important Signals question.", + "category": "04-reactivity", + "categoryTitle": "Reactivity, RxJS & Signals", + "categoryBadge": "Must Know", + "categoryIcon": "zap", + "categoryOrder": 4, + "order": "18", + "fileName": "18-signals.md", + "contentUrl": "assets/content/04-reactivity/18-signals.md", + "wordCount": 1868, + "readingTime": 10, + "headings": [ + { + "level": 1, + "text": "Signals" + }, + { + "level": 1, + "text": "Why Were Signals Introduced?" + }, + { + "level": 1, + "text": "The Traditional Angular Approach" + }, + { + "level": 1, + "text": "Traditional BehaviorSubject Example" + }, + { + "level": 1, + "text": "Problems Angular Observed" + }, + { + "level": 1, + "text": "Angular's Question" + }, + { + "level": 1, + "text": "What Is A Signal?" + }, + { + "level": 1, + "text": "Traditional Variable" + }, + { + "level": 1, + "text": "Signal" + }, + { + "level": 1, + "text": "Creating A Signal" + }, + { + "level": 1, + "text": "Reading A Signal" + }, + { + "level": 1, + "text": "Example" + } + ], + "flashcards": [ + { + "question": "Why Were Signals Introduced?", + "answer": "This is the most important Signals question. Before learning syntax, understand the motivation.", + "source": "Signals" + }, + { + "question": "What Is A Signal?", + "answer": "> A reactive value that Angular automatically tracks.", + "source": "Signals" + }, + { + "question": "What Is A Computed Signal?", + "answer": "A derived value that automatically updates when dependencies change.", + "source": "Signals" + }, + { + "question": "What Is An Effect?", + "answer": "Code that runs whenever dependent Signals change.", + "source": "Signals" + }, + { + "question": "Do Signals Replace RxJS?", + "answer": "Both solve different problems.", + "source": "Signals" + } + ], + "recapNotes": [ + "1. Signals are Angular's modern reactive state primitive.", + "2. A Signal is a reactive value Angular automatically tracks.", + "3. `signal()` stores state.", + "4. `computed()` derives state.", + "5. `effect()` reacts to state changes.", + "6. Signals reduce RxJS boilerplate." + ], + "searchTokens": [ + "signals", + "reactivity", + "rxjs", + "signal()", + "computed()", + "effect()", + "interview", + "priority", + "frequency", + "recommended", + "depth", + "relevant", + "signal", + "computed", + "effect", + "current", + "value", + "subscription", + "required", + "dependency", + "boilerplate", + "state", + "async", + "streams", + "what", + "signal?", + "were", + "introduced?", + "read", + "update", + "effect?", + "replace", + "behaviorsubject?", + "rxjs?", + "trap", + "prefer", + "common", + "modern", + "architecture", + "store" + ] + }, + { + "id": "18a-signal-based-component-apis", + "slug": "18a-signal-based-component-apis", + "title": "Signal-Based Component APIs", + "summary": "Before Signals, Angular component communication was primarily built using:", + "category": "04-reactivity", + "categoryTitle": "Reactivity, RxJS & Signals", + "categoryBadge": "Must Know", + "categoryIcon": "zap", + "categoryOrder": 4, + "order": "18a", + "fileName": "18a-signal-based-component-apis.md", + "contentUrl": "assets/content/04-reactivity/18a-signal-based-component-apis.md", + "wordCount": 1333, + "readingTime": 7, + "headings": [ + { + "level": 1, + "text": "Signal-Based Component APIs" + }, + { + "level": 1, + "text": "Why Were Signal-Based Component APIs Introduced?" + }, + { + "level": 1, + "text": "Angular's Observation" + }, + { + "level": 1, + "text": "Angular's Goal" + }, + { + "level": 1, + "text": "Traditional Component APIs" + }, + { + "level": 1, + "text": "Parent To Child Communication" + }, + { + "level": 1, + "text": "Child To Parent Communication" + }, + { + "level": 1, + "text": "Signal-Based APIs" + }, + { + "level": 1, + "text": "Overview" + }, + { + "level": 2, + "text": "Parent → Child" + }, + { + "level": 2, + "text": "Child → Parent" + }, + { + "level": 2, + "text": "Two-Way Binding" + } + ], + "flashcards": [ + { + "question": "What Is model()?", + "answer": "Signal-based API that simplifies two-way binding.", + "source": "Signal-Based Component APIs" + }, + { + "question": "Can Signal Inputs Be Used In computed()?", + "answer": "This is one of their biggest advantages.", + "source": "Signal-Based Component APIs" + }, + { + "question": "Does @Input() Still Work?", + "answer": "Absolutely. Nothing has been deprecated.", + "source": "Signal-Based Component APIs" + } + ], + "recapNotes": [ + "Modern Angular Development", + "Senior Developer Interviews", + "Lead Developer Interviews" + ], + "searchTokens": [ + "signal", + "based", + "component", + "apis", + "reactivity", + "rxjs", + "signals", + "interview", + "priority", + "frequency", + "recommended", + "depth", + "relevant", + "parent", + "child", + "binding", + "input", + "output", + "existing", + "angular", + "applications", + "what", + "model", + "inputs", + "replace", + "ngonchanges", + "used", + "computed", + "does", + "@input", + "still", + "work?", + "trap" + ] + }, + { + "id": "19-ngmodules-fundamentals", + "slug": "19-ngmodules-fundamentals", + "title": "NgModules Fundamentals", + "summary": "Before Standalone Components existed, Angular needed a way to organize:", + "category": "05-advanced", + "categoryTitle": "Advanced & Performance", + "categoryBadge": "Advanced", + "categoryIcon": "gauge", + "categoryOrder": 5, + "order": "19", + "fileName": "19-ngmodules-fundamentals.md", + "contentUrl": "assets/content/05-advanced/19-ngmodules-fundamentals.md", + "wordCount": 869, + "readingTime": 5, + "headings": [ + { + "level": 1, + "text": "NgModules Fundamentals" + }, + { + "level": 1, + "text": "First Principles" + }, + { + "level": 1, + "text": "What Is An NgModule?" + }, + { + "level": 1, + "text": "NgModule Anatomy" + }, + { + "level": 1, + "text": "declarations" + }, + { + "level": 2, + "text": "Purpose" + }, + { + "level": 1, + "text": "What Goes Into declarations?" + }, + { + "level": 1, + "text": "Why Is declarations Needed?" + }, + { + "level": 1, + "text": "Mental Model" + }, + { + "level": 1, + "text": "imports" + }, + { + "level": 2, + "text": "Purpose" + }, + { + "level": 1, + "text": "Why Is imports Needed?" + } + ], + "flashcards": [ + { + "question": "What Is An NgModule?", + "answer": "Organizational container that registers components, directives, pipes, dependencies, and services.", + "source": "NgModules Fundamentals" + }, + { + "question": "What Goes Into providers?", + "answer": "Services registered for Dependency Injection.", + "source": "NgModules Fundamentals" + }, + { + "question": "What Are exports?", + "answer": "Things other modules are allowed to use.", + "source": "NgModules Fundamentals" + } + ], + "recapNotes": [ + "1. NgModules organized Angular applications.", + "2. declarations register components, directives, and pipes.", + "3. imports bring in external functionality.", + "4. exports make functionality available to other modules.", + "5. providers register services for dependency injection.", + "6. Feature Modules improve scalability." + ], + "searchTokens": [ + "ngmodules", + "fundamentals", + "advanced", + "performance", + "interview", + "priority", + "frequency", + "recommended", + "depth", + "relevant", + "purpose", + "mental", + "model", + "what", + "ngmodule?", + "difference", + "between", + "declarations", + "imports?", + "goes", + "into", + "providers?", + "exports?", + "trap", + "takeaways" + ] + }, + { + "id": "20-standalone-components", + "slug": "20-standalone-components", + "title": "Standalone Components", + "summary": "This is the most important question. ---", + "category": "05-advanced", + "categoryTitle": "Advanced & Performance", + "categoryBadge": "Advanced", + "categoryIcon": "gauge", + "categoryOrder": 5, + "order": "20", + "fileName": "20-standalone-components.md", + "contentUrl": "assets/content/05-advanced/20-standalone-components.md", + "wordCount": 997, + "readingTime": 5, + "headings": [ + { + "level": 1, + "text": "Standalone Components" + }, + { + "level": 1, + "text": "Why Were Standalone Components Introduced?" + }, + { + "level": 1, + "text": "Problem With NgModules" + }, + { + "level": 1, + "text": "Angular's Question" + }, + { + "level": 1, + "text": "What Is A Standalone Component?" + }, + { + "level": 1, + "text": "The Biggest Change" + }, + { + "level": 2, + "text": "What Happened To declarations?" + }, + { + "level": 1, + "text": "Imports In Standalone Components" + }, + { + "level": 1, + "text": "Real Example" + }, + { + "level": 1, + "text": "Mental Model" + }, + { + "level": 2, + "text": "Direct Component Imports" + }, + { + "level": 1, + "text": "What Happened To exports?" + } + ], + "flashcards": [ + { + "question": "Why Were Standalone Components Introduced?", + "answer": "This is the most important question.", + "source": "Standalone Components" + }, + { + "question": "What Is A Standalone Component?", + "answer": "A component that can exist without being declared in an NgModule.", + "source": "Standalone Components" + }, + { + "question": "What Happened To exports?", + "answer": "Mostly unnecessary. Components are imported directly.", + "source": "Standalone Components" + } + ], + "recapNotes": [ + "1. Standalone Components eliminate the need for declarations.", + "2. Components become self-contained.", + "3. Dependencies are imported directly into components.", + "4. exports become largely unnecessary.", + "5. Dependency injection continues to work normally.", + "6. Bootstrapping becomes simpler." + ], + "searchTokens": [ + "standalone", + "components", + "advanced", + "performance", + "interview", + "priority", + "frequency", + "recommended", + "depth", + "relevant", + "what", + "happened", + "declarations?", + "direct", + "component", + "imports", + "registration", + "sharing", + "complexity", + "dependency", + "visibility", + "modern", + "angular", + "component?", + "were", + "introduced?", + "ngmodules", + "still", + "work?", + "recommended?", + "dependencies", + "imported?", + "exports?", + "trap", + "declarations", + "exports", + "providers", + "bootstrap", + "message" + ] + }, + { + "id": "21-routing-and-navigation", + "slug": "21-routing-and-navigation", + "title": "Routing & Navigation", + "summary": "Before understanding Angular Routing, let's understand why routing exists.", + "category": "05-advanced", + "categoryTitle": "Advanced & Performance", + "categoryBadge": "Advanced", + "categoryIcon": "gauge", + "categoryOrder": 5, + "order": "21", + "fileName": "21-routing-and-navigation.md", + "contentUrl": "assets/content/05-advanced/21-routing-and-navigation.md", + "wordCount": 1627, + "readingTime": 9, + "headings": [ + { + "level": 1, + "text": "Routing & Navigation" + }, + { + "level": 1, + "text": "First Principles" + }, + { + "level": 1, + "text": "Traditional Web Applications" + }, + { + "level": 1, + "text": "The Problem" + }, + { + "level": 1, + "text": "Angular SPA Solution" + }, + { + "level": 1, + "text": "Mental Model" + }, + { + "level": 1, + "text": "What Is Angular Router?" + }, + { + "level": 1, + "text": "High-Level Architecture" + }, + { + "level": 1, + "text": "Core Routing Concepts" + }, + { + "level": 1, + "text": "Modern Angular Routing" + }, + { + "level": 1, + "text": "Route Configuration" + }, + { + "level": 1, + "text": "Understanding Routes" + } + ], + "flashcards": [ + { + "question": "Why provideRouter()?", + "answer": "Registers Angular Router and route configuration.", + "source": "Routing & Navigation" + }, + { + "question": "What Is Angular Router?", + "answer": "Framework service responsible for URL management and navigation.", + "source": "Routing & Navigation" + }, + { + "question": "What Is router-outlet?", + "answer": "Placeholder where Angular renders routed components.", + "source": "Routing & Navigation" + }, + { + "question": "What Is routerLink?", + "answer": "Directive used for SPA navigation in templates.", + "source": "Routing & Navigation" + }, + { + "question": "What Is A Route Parameter?", + "answer": "Used to identify a specific resource.", + "source": "Routing & Navigation" + }, + { + "question": "What Is A Query Parameter?", + "answer": "Used to modify behavior or filtering.", + "source": "Routing & Navigation" + }, + { + "question": "Why Is router-outlet Required?", + "answer": "Because Angular needs a location where routed components can render.", + "source": "Routing & Navigation" + } + ], + "recapNotes": [ + "1. Angular Router maps URLs to components.", + "2. Routing enables SPA navigation without page reloads.", + "3. Routes define URL-to-component mappings.", + "4. router-outlet is where routed components render.", + "5. routerLink is used for template navigation.", + "6. router.navigate() is used for programmatic navigation." + ], + "searchTokens": [ + "routing", + "navigation", + "advanced", + "performance", + "interview", + "priority", + "frequency", + "recommended", + "depth", + "relevant", + "route", + "parameters", + "query", + "what", + "angular", + "router?", + "router", + "outlet?", + "routerlink?", + "difference", + "between", + "routerlink", + "href?", + "programmatic", + "navigation?", + "parameter?", + "params", + "params?", + "wildcard", + "route?", + "outlet", + "required?", + "trap", + "template", + "placeholder", + "invalid", + "routes", + "redirect", + "modern", + "configuration" + ] + }, + { + "id": "22-route-parameters-and-query-parameters-deep-dive", + "slug": "22-route-parameters-and-query-parameters-deep-dive", + "title": "Route Parameters & Query Parameters Deep Dive", + "summary": "Most applications need to answer two questions:", + "category": "05-advanced", + "categoryTitle": "Advanced & Performance", + "categoryBadge": "Advanced", + "categoryIcon": "gauge", + "categoryOrder": 5, + "order": "22", + "fileName": "22-route-parameters-and-query-parameters-deep-dive.md", + "contentUrl": "assets/content/05-advanced/22-route-parameters-and-query-parameters-deep-dive.md", + "wordCount": 1443, + "readingTime": 8, + "headings": [ + { + "level": 1, + "text": "Route Parameters & Query Parameters Deep Dive" + }, + { + "level": 1, + "text": "First Principles" + }, + { + "level": 1, + "text": "Route Parameters vs Query Parameters" + }, + { + "level": 2, + "text": "Route Parameters" + }, + { + "level": 2, + "text": "Query Parameters" + }, + { + "level": 1, + "text": "Simple Rule" + }, + { + "level": 2, + "text": "Route Parameter" + }, + { + "level": 2, + "text": "Query Parameter" + }, + { + "level": 1, + "text": "Real Enterprise Example" + }, + { + "level": 1, + "text": "Route Parameters" + }, + { + "level": 2, + "text": "Why They Exist" + }, + { + "level": 1, + "text": "Solution" + } + ], + "flashcards": [ + { + "question": "What Is A Route Parameter?", + "answer": "Dynamic URL segment used to identify a resource.", + "source": "Route Parameters & Query Parameters Deep Dive" + }, + { + "question": "What Is A Query Parameter?", + "answer": "Optional URL parameter used to modify behavior.", + "source": "Route Parameters & Query Parameters Deep Dive" + } + ], + "recapNotes": [ + "1. Route parameters identify resources.", + "2. Query parameters modify behavior and presentation.", + "3. Route parameters are defined using `:parameterName`.", + "4. `ActivatedRoute` is used to access route information.", + "5. `snapshot` reads values once.", + "6. `paramMap` and `queryParamMap` provide reactive updates." + ], + "searchTokens": [ + "route", + "parameters", + "query", + "deep", + "dive", + "advanced", + "performance", + "activatedroute", + "snapshot", + "parammap", + "queryparammap", + "interview", + "priority", + "frequency", + "recommended", + "depth", + "relevant", + "parameter", + "they", + "exist", + "params", + "what", + "parameter?", + "difference", + "between", + "queryparammap?", + "subscribe?", + "when", + "should", + "used?", + "trap", + "employee", + "details", + "search", + "pagination", + "active", + "resource", + "identity", + "paging", + "changes" + ] + }, + { + "id": "23-route-guards", + "slug": "23-route-guards", + "title": "Route Guards", + "summary": "Before understanding Route Guards, let's understand the problem they solve.", + "category": "05-advanced", + "categoryTitle": "Advanced & Performance", + "categoryBadge": "Advanced", + "categoryIcon": "gauge", + "categoryOrder": 5, + "order": "23", + "fileName": "23-route-guards.md", + "contentUrl": "assets/content/05-advanced/23-route-guards.md", + "wordCount": 1713, + "readingTime": 9, + "headings": [ + { + "level": 1, + "text": "Route Guards" + }, + { + "level": 1, + "text": "First Principles" + }, + { + "level": 1, + "text": "The Problem" + }, + { + "level": 1, + "text": "Why UI Protection Is Not Enough" + }, + { + "level": 1, + "text": "Solution" + }, + { + "level": 1, + "text": "What Is A Route Guard?" + }, + { + "level": 1, + "text": "Mental Model" + }, + { + "level": 1, + "text": "Router Navigation Lifecycle" + }, + { + "level": 1, + "text": "Why Guards Are Used" + }, + { + "level": 1, + "text": "Types Of Guards" + }, + { + "level": 1, + "text": "Quick Summary" + }, + { + "level": 2, + "text": "CanActivate" + } + ], + "flashcards": [ + { + "question": "What is the difference between canActivate and canMatch in Angular routing?", + "answer": "canActivate runs after the route matches to check if a component can be activated; if it fails, the route still matched. canMatch runs during route matching; if it returns false, Angular continues searching for alternative routes with the same path (ideal for feature flags or MFE variants).", + "source": "Route Guards" + }, + { + "question": "When would you use a canDeactivate guard?", + "answer": "To prevent users from accidentally navigating away from a page with unsaved form changes, prompting them with a confirmation dialog before route transition.", + "source": "Route Guards" + } + ], + "recapNotes": [ + "1. Route Guards control Angular navigation decisions.", + "2. `CanActivate` is primarily used for authentication and authorization.", + "3. `CanActivateChild` protects all child routes.", + "4. `CanDeactivate` prevents accidental data loss.", + "5. `CanMatch` controls route matching before activation.", + "6. Modern Angular prefers functional guards." + ], + "searchTokens": [ + "route", + "guards", + "advanced", + "performance", + "canactivate", + "canactivatechild", + "candeactivate", + "canmatch", + "interview", + "priority", + "frequency", + "recommended", + "depth", + "relevant", + "what", + "guard?", + "most", + "common", + "canactivatechild?", + "candeactivate?", + "canmatch?", + "return", + "types?", + "functional", + "class", + "based", + "guards?", + "provide", + "security?", + "trap", + "authentication", + "authorization", + "unsaved", + "changes", + "feature", + "flags", + "redirect", + "modern", + "message" + ] + }, + { + "id": "24-nested-routes", + "slug": "24-nested-routes", + "title": "Nested Routes", + "summary": "Before understanding Nested Routes, let's understand the problem they solve.", + "category": "05-advanced", + "categoryTitle": "Advanced & Performance", + "categoryBadge": "Advanced", + "categoryIcon": "gauge", + "categoryOrder": 5, + "order": "24", + "fileName": "24-nested-routes.md", + "contentUrl": "assets/content/05-advanced/24-nested-routes.md", + "wordCount": 614, + "readingTime": 4, + "headings": [ + { + "level": 1, + "text": "Nested Routes" + }, + { + "level": 1, + "text": "First Principles" + }, + { + "level": 1, + "text": "The Problem" + }, + { + "level": 1, + "text": "Desired UI" + }, + { + "level": 1, + "text": "Solution" + }, + { + "level": 1, + "text": "What Are Nested Routes?" + }, + { + "level": 1, + "text": "Mental Model" + }, + { + "level": 1, + "text": "Parent Route" + }, + { + "level": 1, + "text": "Dashboard As A Shell" + }, + { + "level": 1, + "text": "Dashboard Template" + }, + { + "level": 1, + "text": "Important Concept" + }, + { + "level": 1, + "text": "Why Another router-outlet?" + } + ], + "flashcards": [ + { + "question": "How do nested (child) routes work in Angular?", + "answer": "Configured via the children property in route definitions. When a child route activates, its component renders inside the of its parent component rather than replacing the top-level viewport.", + "source": "Nested Routes" + }, + { + "question": "How do child routes access parameters from parent routes?", + "answer": "By configuring paramsInheritanceStrategy: \"always\" in the router, or by traversing route.parent.paramMap to read parent path and query parameters.", + "source": "Nested Routes" + } + ], + "recapNotes": [ + "Enterprise Applications", + "Dashboard Applications", + "SaaS Applications", + "Multi-Level Navigation" + ], + "searchTokens": [ + "nested", + "routes", + "advanced", + "performance", + "interview", + "priority", + "frequency", + "recommended", + "depth", + "relevant", + "relative", + "absolute" + ] + }, + { + "id": "25-route-resolvers", + "slug": "25-route-resolvers", + "title": "Route Resolvers", + "summary": "Before understanding Route Resolvers, let's understand the problem they solve.", + "category": "05-advanced", + "categoryTitle": "Advanced & Performance", + "categoryBadge": "Advanced", + "categoryIcon": "gauge", + "categoryOrder": 5, + "order": "25", + "fileName": "25-route-resolvers.md", + "contentUrl": "assets/content/05-advanced/25-route-resolvers.md", + "wordCount": 1706, + "readingTime": 9, + "headings": [ + { + "level": 1, + "text": "Route Resolvers" + }, + { + "level": 1, + "text": "First Principles" + }, + { + "level": 1, + "text": "Traditional Approach" + }, + { + "level": 1, + "text": "User Experience" + }, + { + "level": 1, + "text": "The Problem" + }, + { + "level": 1, + "text": "Resolver Approach" + }, + { + "level": 1, + "text": "What Is A Resolver?" + }, + { + "level": 1, + "text": "Mental Model" + }, + { + "level": 1, + "text": "Angular Navigation Lifecycle" + }, + { + "level": 1, + "text": "Important Observation" + }, + { + "level": 1, + "text": "Modern Angular Resolver" + }, + { + "level": 1, + "text": "Understanding ResolveFn" + } + ], + "flashcards": [ + { + "question": "What is the primary architectural purpose of a Route Resolver in Angular?", + "answer": "A resolver pre-fetches essential data before the route component activates and renders, preventing UI flickering and blank layout states while waiting for API responses.", + "source": "Route Resolvers" + }, + { + "question": "What is the trade-off of using Route Resolvers?", + "answer": "Navigation is delayed until the resolver observable completes. If the API response is slow, the user stays on the previous page with no visual progress unless a global navigation loading indicator is shown.", + "source": "Route Resolvers" + } + ], + "recapNotes": [ + "1. Resolvers load route-critical data before route activation.", + "2. Modern Angular uses `ResolveFn`.", + "3. Resolvers execute after Guards and before route activation.", + "4. Route parameters are commonly used within resolvers.", + "5. Resolved data is accessed through `route.data`.", + "6. Multiple resolvers can run together." + ], + "searchTokens": [ + "route", + "resolvers", + "advanced", + "performance", + "interview", + "priority", + "frequency", + "recommended", + "depth", + "relevant", + "improve", + "performance?", + "what", + "resolver?", + "when", + "execute?", + "resolvefn?", + "parameters?", + "multiple", + "run?", + "resolver", + "ngoninit?", + "trap", + "purpose", + "execution", + "order", + "access", + "data", + "common", + "cases", + "avoid", + "message" + ] + }, + { + "id": "26-lazy-loading", + "slug": "26-lazy-loading", + "title": "Lazy Loading", + "summary": "Before understanding Lazy Loading, let's understand the problem it solves.", + "category": "05-advanced", + "categoryTitle": "Advanced & Performance", + "categoryBadge": "Advanced", + "categoryIcon": "gauge", + "categoryOrder": 5, + "order": "26", + "fileName": "26-lazy-loading.md", + "contentUrl": "assets/content/05-advanced/26-lazy-loading.md", + "wordCount": 1355, + "readingTime": 7, + "headings": [ + { + "level": 1, + "text": "Lazy Loading" + }, + { + "level": 1, + "text": "First Principles" + }, + { + "level": 1, + "text": "The Problem" + }, + { + "level": 1, + "text": "Without Lazy Loading" + }, + { + "level": 1, + "text": "Application Startup" + }, + { + "level": 1, + "text": "Visual" + }, + { + "level": 1, + "text": "Problems" + }, + { + "level": 1, + "text": "Mental Model" + }, + { + "level": 1, + "text": "What Is Lazy Loading?" + }, + { + "level": 1, + "text": "Mental Model" + }, + { + "level": 1, + "text": "Startup Flow" + }, + { + "level": 1, + "text": "Comparison" + } + ], + "flashcards": [ + { + "question": "What Is Lazy Loading?", + "answer": "Lazy Loading is a technique where Angular loads application features only when they are needed.", + "source": "Lazy Loading" + }, + { + "question": "What Is Code Splitting?", + "answer": "Breaking one large bundle into multiple smaller bundles.", + "source": "Lazy Loading" + }, + { + "question": "Why Is CanMatch Important?", + "answer": "Because Angular can prevent loading an entire feature bundle for unauthorized users.", + "source": "Lazy Loading" + }, + { + "question": "What Is Preloading?", + "answer": "Background downloading of lazy-loaded bundles after startup.", + "source": "Lazy Loading" + } + ], + "recapNotes": [ + "Enterprise Applications", + "Performance Optimization", + "Large Angular Applications", + "SaaS Platforms" + ], + "searchTokens": [ + "lazy", + "loading", + "advanced", + "performance", + "interview", + "priority", + "frequency", + "recommended", + "depth", + "relevant", + "without", + "with", + "loadcomponent", + "loadchildren", + "canactivate", + "canmatch", + "preloading", + "what", + "loading?", + "most", + "common", + "apis?", + "difference", + "between", + "loadchildren?", + "dynamic", + "import?", + "code", + "splitting?", + "routes", + "have", + "guards?", + "important?", + "preloading?", + "trap" + ] + }, + { + "id": "27-http-interceptors", + "slug": "27-http-interceptors", + "title": "HTTP Interceptors", + "summary": "Before understanding HTTP Interceptors, let's understand the problem they solve.", + "category": "05-advanced", + "categoryTitle": "Advanced & Performance", + "categoryBadge": "Advanced", + "categoryIcon": "gauge", + "categoryOrder": 5, + "order": "27", + "fileName": "27-http-interceptors.md", + "contentUrl": "assets/content/05-advanced/27-http-interceptors.md", + "wordCount": 999, + "readingTime": 5, + "headings": [ + { + "level": 1, + "text": "HTTP Interceptors" + }, + { + "level": 1, + "text": "First Principles" + }, + { + "level": 1, + "text": "Traditional Approach" + }, + { + "level": 1, + "text": "Problems" + }, + { + "level": 1, + "text": "Solution" + }, + { + "level": 1, + "text": "What Is An HTTP Interceptor?" + }, + { + "level": 1, + "text": "Mental Model" + }, + { + "level": 1, + "text": "HttpClient Request Lifecycle" + }, + { + "level": 1, + "text": "Full Request Flow" + }, + { + "level": 1, + "text": "Why Interceptors Exist" + }, + { + "level": 1, + "text": "Modern Angular Interceptors" + }, + { + "level": 1, + "text": "Old Style" + } + ], + "flashcards": [ + { + "question": "What is an HttpInterceptor in Angular?", + "answer": "A middleware pattern that inspects, transforms, or handles outgoing HttpRequest objects and incoming HttpResponse events globally (e.g. attaching auth headers, logging, caching, retry logic).", + "source": "HTTP Interceptors" + }, + { + "question": "How are modern functional interceptors defined in Angular 15+?", + "answer": "As pure functions conforming to HttpInterceptorFn: (req, next) => next(req), configured globally via provideHttpClient(withInterceptors([authInterceptor])).", + "source": "HTTP Interceptors" + } + ], + "recapNotes": [ + "Enterprise Applications", + "Authentication", + "HTTP Communication", + "Global Error Handling" + ], + "searchTokens": [ + "http", + "interceptors", + "advanced", + "performance", + "interview", + "priority", + "frequency", + "recommended", + "depth", + "relevant", + "next" + ] + }, + { + "id": "28-change-detection", + "slug": "28-change-detection", + "title": "Change Detection", + "summary": "Let's begin with the most fundamental Angular question.", + "category": "05-advanced", + "categoryTitle": "Advanced & Performance", + "categoryBadge": "Advanced", + "categoryIcon": "gauge", + "categoryOrder": 5, + "order": "28", + "fileName": "28-change-detection.md", + "contentUrl": "assets/content/05-advanced/28-change-detection.md", + "wordCount": 1310, + "readingTime": 7, + "headings": [ + { + "level": 1, + "text": "Change Detection" + }, + { + "level": 1, + "text": "First Principles" + }, + { + "level": 1, + "text": "What Is Change Detection?" + }, + { + "level": 1, + "text": "Mental Model" + }, + { + "level": 1, + "text": "Example" + }, + { + "level": 1, + "text": "The Core Problem" + }, + { + "level": 1, + "text": "Angular's Question" + }, + { + "level": 1, + "text": "What Triggers Change Detection?" + }, + { + "level": 1, + "text": "Zone.js" + }, + { + "level": 1, + "text": "What Is Zone.js?" + }, + { + "level": 1, + "text": "Example" + }, + { + "level": 1, + "text": "Without Zone.js" + } + ], + "flashcards": [ + { + "question": "What is the difference between Default and OnPush change detection strategies?", + "answer": "Default checks the entire component tree on any asynchronous event. OnPush skips component subtrees unless an @Input() reference changes, an event originated inside the component, or a Signal/AsyncPipe triggers an update.", + "source": "Change Detection" + }, + { + "question": "How do Angular Signals improve change detection over Zone.js?", + "answer": "Signals provide fine-grained, surgical reactivity: Angular knows exactly which template node depends on which signal, enabling localized DOM updates without traversing the entire component hierarchy.", + "source": "Change Detection" + } + ], + "recapNotes": [ + "Angular Internals", + "Performance Optimization", + "OnPush Strategy", + "Enterprise Applications" + ], + "searchTokens": [ + "change", + "detection", + "advanced", + "performance", + "interview", + "priority", + "frequency", + "recommended", + "depth", + "relevant" + ] + }, + { + "id": "29-performance-optimization", + "slug": "29-performance-optimization", + "title": "Angular Performance Optimization", + "summary": "Performance Optimization is not about making Angular faster.", + "category": "05-advanced", + "categoryTitle": "Advanced & Performance", + "categoryBadge": "Advanced", + "categoryIcon": "gauge", + "categoryOrder": 5, + "order": "29", + "fileName": "29-performance-optimization.md", + "contentUrl": "assets/content/05-advanced/29-performance-optimization.md", + "wordCount": 1845, + "readingTime": 10, + "headings": [ + { + "level": 1, + "text": "Angular Performance Optimization" + }, + { + "level": 1, + "text": "First Principles" + }, + { + "level": 1, + "text": "Performance Optimization Pyramid" + }, + { + "level": 1, + "text": "Golden Rule" + }, + { + "level": 1, + "text": "Angular Performance Categories" + }, + { + "level": 1, + "text": "Optimization #1: OnPush Change Detection" + }, + { + "level": 1, + "text": "Problem" + }, + { + "level": 1, + "text": "Solution" + }, + { + "level": 1, + "text": "Benefits" + }, + { + "level": 1, + "text": "Incorrect Pattern" + }, + { + "level": 1, + "text": "Correct Pattern" + }, + { + "level": 1, + "text": "Why?" + } + ], + "flashcards": [ + { + "question": "Why Is trackBy Important?", + "answer": "Prevents unnecessary DOM recreation.", + "source": "Angular Performance Optimization" + }, + { + "question": "Why Avoid Functions In Templates?", + "answer": "Functions execute during every change detection cycle.", + "source": "Angular Performance Optimization" + }, + { + "question": "Why Are Signals Faster?", + "answer": "Angular knows exactly which consumers depend on the changed signal.", + "source": "Angular Performance Optimization" + }, + { + "question": "Why Is OnPush Faster?", + "answer": "Angular performs fewer component checks.", + "source": "Angular Performance Optimization" + }, + { + "question": "What Is Virtual Scrolling?", + "answer": "Rendering only visible items instead of the full dataset.", + "source": "Angular Performance Optimization" + }, + { + "question": "What Does shareReplay(1) Solve?", + "answer": "Avoids duplicate API calls by sharing the same response among subscribers.", + "source": "Angular Performance Optimization" + }, + { + "question": "What Is A Deferrable View?", + "answer": "A delayed rendering mechanism using @defer for non-critical UI.", + "source": "Angular Performance Optimization" + } + ], + "recapNotes": [ + "1. Performance optimization is about reducing unnecessary work.", + "2. OnPush reduces change detection overhead.", + "3. Signals provide fine-grained reactivity.", + "4. Computed signals provide reactive memoization.", + "5. trackBy minimizes DOM recreation.", + "6. Pure pipes reduce repeated calculations." + ], + "searchTokens": [ + "angular", + "performance", + "optimization", + "advanced", + "@defer", + "interview", + "priority", + "frequency", + "recommended", + "depth", + "relevant", + "change", + "detection", + "list", + "rendering", + "routing", + "http", + "memory", + "assets", + "what", + "improves", + "most?", + "trackby", + "important?", + "avoid", + "functions", + "templates?", + "signals", + "faster?", + "onpush", + "virtual", + "scrolling?", + "does", + "sharereplay", + "solve?", + "deferrable", + "view?", + "trap" + ] + }, + { + "id": "30-state-management-fundamentals", + "slug": "30-state-management-fundamentals", + "title": "State Management Fundamentals", + "summary": "Before discussing NgRx, Signal Store, Redux, or any state management library, we must understand:", + "category": "06-senior-architecture", + "categoryTitle": "Senior Architecture & Enterprise", + "categoryBadge": "Architect", + "categoryIcon": "shield-check", + "categoryOrder": 6, + "order": "30", + "fileName": "30-state-management-fundamentals.md", + "contentUrl": "assets/content/06-senior-architecture/30-state-management-fundamentals.md", + "wordCount": 2124, + "readingTime": 11, + "headings": [ + { + "level": 1, + "text": "State Management Fundamentals" + }, + { + "level": 1, + "text": "First Principles" + }, + { + "level": 1, + "text": "What Is State?" + }, + { + "level": 1, + "text": "Examples Of State" + }, + { + "level": 1, + "text": "Why?" + }, + { + "level": 1, + "text": "Everything Is State" + }, + { + "level": 1, + "text": "Why State Management Exists" + }, + { + "level": 1, + "text": "The Real Problem" + }, + { + "level": 1, + "text": "State Ownership" + }, + { + "level": 1, + "text": "Bad Example" + }, + { + "level": 1, + "text": "Single Source Of Truth" + }, + { + "level": 1, + "text": "Types Of State" + } + ], + "flashcards": [ + { + "question": "What are the three categories of state in Angular applications?", + "answer": "1. Component/Local State (UI flags, forms, modal toggles). 2. Shared/Feature State (state shared among a feature’s components via services). 3. Global Application State (authenticated user, global config, cross-domain entities).", + "source": "State Management Fundamentals" + }, + { + "question": "When is a dedicated state management library like NgRx necessary?", + "answer": "When complex state needs to be accessed by many decoupled components across different routes, requires undo/redo or time-travel debugging, has high-frequency updates, or needs strict unidirectional immutability.", + "source": "State Management Fundamentals" + }, + { + "question": "What problem does unidirectional data flow solve in state management?", + "answer": "It eliminates race conditions, unpredictable state mutations, and hard-to-trace bugs by ensuring state changes can only be triggered via explicit actions and flows in one predictable direction to view components.", + "source": "State Management Fundamentals" + } + ], + "recapNotes": [ + "1. State is any data that changes over time and affects the UI.", + "2. State can be Local, Shared, or Global.", + "3. Every piece of state should have a single owner.", + "4. Prefer a Single Source Of Truth.", + "5. Keep state as local as possible.", + "6. Signals are excellent for service-based state management." + ], + "searchTokens": [ + "state", + "management", + "fundamentals", + "senior", + "architecture", + "enterprise", + "interview", + "priority", + "frequency", + "recommended", + "depth", + "relevant", + "what", + "state?", + "local", + "shared", + "global", + "single", + "source", + "truth?", + "important?", + "store", + "pattern?", + "signals", + "derived", + "ngrx?", + "trap", + "types", + "golden", + "rule", + "modern", + "angular", + "pattern", + "avoid", + "prefer", + "message" + ] + }, + { + "id": "31-ngrx-fundamentals", + "slug": "31-ngrx-fundamentals", + "title": "NgRx Fundamentals", + "summary": "Senior-Level Conceptual Understanding", + "category": "06-senior-architecture", + "categoryTitle": "Senior Architecture & Enterprise", + "categoryBadge": "Architect", + "categoryIcon": "shield-check", + "categoryOrder": 6, + "order": "31", + "fileName": "31-ngrx-fundamentals.md", + "contentUrl": "assets/content/06-senior-architecture/31-ngrx-fundamentals.md", + "wordCount": 2746, + "readingTime": 14, + "headings": [ + { + "level": 1, + "text": "NgRx Fundamentals" + }, + { + "level": 1, + "text": "First Principles" + }, + { + "level": 1, + "text": "What Is NgRx?" + }, + { + "level": 1, + "text": "Important Distinction" + }, + { + "level": 1, + "text": "What Problem Does NgRx Solve?" + }, + { + "level": 1, + "text": "Typical Problems" + }, + { + "level": 2, + "text": "Problem 1" + }, + { + "level": 1, + "text": "Problem 2" + }, + { + "level": 1, + "text": "Problem 3" + }, + { + "level": 1, + "text": "Problem 4" + }, + { + "level": 1, + "text": "NgRx Philosophy" + }, + { + "level": 1, + "text": "Core NgRx Flow" + } + ], + "flashcards": [ + { + "question": "What is the core data flow in NgRx?", + "answer": "Components dispatch Actions. Reducers take the current state and action to calculate new state immutably. Selectors allow components to query slices of state reactively. Effects handle side effects like asynchronous HTTP calls.", + "source": "NgRx Fundamentals" + }, + { + "question": "Why should NgRx state be immutable?", + "answer": "Immutability allows OnPush change detection to verify object references in O(1) time without dirty checking, guarantees predictable state transitions, and enables deterministic time-travel debugging.", + "source": "NgRx Fundamentals" + } + ], + "recapNotes": [ + "1. NgRx is a state management library, not an Angular feature.", + "2. NgRx was created to solve large-scale state management problems.", + "3. The Store is the single source of truth.", + "4. Actions describe events.", + "5. dispatch() publishes actions.", + "6. Reducers are the only place state changes." + ], + "searchTokens": [ + "ngrx", + "fundamentals", + "senior", + "architecture", + "enterprise", + "interview", + "priority", + "frequency", + "recommended", + "depth", + "relevant", + "problem", + "what", + "does", + "solve?", + "store?", + "action?", + "dispatch", + "reducer?", + "effect?", + "selector?", + "reducers", + "pure?", + "happens", + "without", + "signals", + "ngrx?", + "trap" + ] + }, + { + "id": "32-authentication-authorization", + "slug": "32-authentication-authorization", + "title": "Authentication & Authorization", + "summary": "Before discussing JWT, Guards, Interceptors, or Access Tokens, we must understand:", + "category": "06-senior-architecture", + "categoryTitle": "Senior Architecture & Enterprise", + "categoryBadge": "Architect", + "categoryIcon": "shield-check", + "categoryOrder": 6, + "order": "32", + "fileName": "32-authentication-authorization.md", + "contentUrl": "assets/content/06-senior-architecture/32-authentication-authorization.md", + "wordCount": 2011, + "readingTime": 11, + "headings": [ + { + "level": 1, + "text": "Authentication & Authorization" + }, + { + "level": 1, + "text": "First Principles" + }, + { + "level": 1, + "text": "Authentication vs Authorization" + }, + { + "level": 1, + "text": "Authentication" + }, + { + "level": 1, + "text": "Authorization" + }, + { + "level": 1, + "text": "Easy Interview Answer" + }, + { + "level": 1, + "text": "Real Example" + }, + { + "level": 1, + "text": "Authentication Evolution" + }, + { + "level": 1, + "text": "Session-Based Authentication" + }, + { + "level": 1, + "text": "JWT Authentication" + }, + { + "level": 1, + "text": "Login Flow" + }, + { + "level": 1, + "text": "JWT Structure" + } + ], + "flashcards": [ + { + "question": "What is the difference between Authentication and Authorization?", + "answer": "Authentication verifies WHO the user is (e.g. login credentials, JWT validation). Authorization determines WHAT resources, features, or routes that authenticated user is permitted to access (roles, permissions, scopes).", + "source": "Authentication & Authorization" + }, + { + "question": "Where should JWT tokens be stored securely in an Angular application?", + "answer": "Ideally in httpOnly, Secure, SameSite cookies to protect against XSS attacks. If stored in memory or storage, access tokens should be short-lived with refresh tokens handled securely.", + "source": "Authentication & Authorization" + } + ], + "recapNotes": [ + "1. Authentication verifies identity.", + "2. Authorization verifies permissions.", + "3. JWT is the most common SPA authentication mechanism.", + "4. JWT is signed, not encrypted.", + "5. Claims contain user information and permissions.", + "6. Access tokens should be short-lived." + ], + "searchTokens": [ + "authentication", + "authorization", + "senior", + "architecture", + "enterprise", + "interview", + "priority", + "frequency", + "recommended", + "depth", + "relevant", + "access", + "token", + "refresh", + "authorization?", + "what", + "jwt?", + "encrypted?", + "claim?", + "token?", + "interceptors?", + "guards?", + "rbac?", + "where", + "should", + "tokens", + "stored?", + "exist", + "only", + "angular?", + "trap" + ] + }, + { + "id": "33-error-handling-strategy", + "slug": "33-error-handling-strategy", + "title": "Error Handling Strategy", + "summary": "Senior / Architect Level Understanding", + "category": "06-senior-architecture", + "categoryTitle": "Senior Architecture & Enterprise", + "categoryBadge": "Architect", + "categoryIcon": "shield-check", + "categoryOrder": 6, + "order": "33", + "fileName": "33-error-handling-strategy.md", + "contentUrl": "assets/content/06-senior-architecture/33-error-handling-strategy.md", + "wordCount": 1757, + "readingTime": 9, + "headings": [ + { + "level": 1, + "text": "Error Handling Strategy" + }, + { + "level": 1, + "text": "First Principles" + }, + { + "level": 1, + "text": "What Is Error Handling Strategy?" + }, + { + "level": 1, + "text": "Goals Of Error Handling" + }, + { + "level": 1, + "text": "What Can Go Wrong?" + }, + { + "level": 1, + "text": "Error Classification" + }, + { + "level": 1, + "text": "Category 1: Runtime Errors" + }, + { + "level": 1, + "text": "Category 2: Network Errors" + }, + { + "level": 1, + "text": "Category 3: Authentication Errors" + }, + { + "level": 1, + "text": "Category 4: Authorization Errors" + }, + { + "level": 1, + "text": "Category 5: Business Validation Errors" + }, + { + "level": 1, + "text": "Category 6: Server Errors" + } + ], + "flashcards": [ + { + "question": "How should global errors be captured and logged in Angular?", + "answer": "Implement a custom ErrorHandler class to intercept uncaught runtime exceptions globally, extract stack traces, and transmit structured telemetry to monitoring services like Sentry or Application Insights.", + "source": "Error Handling Strategy" + }, + { + "question": "How should HTTP errors be handled gracefully across the application?", + "answer": "Use an HttpInterceptor with RxJS catchError to centralize error mapping (e.g. 401 redirect, 403 forbidden toast, 500 retry mechanism) before re-throwing or providing user-friendly fallback notifications.", + "source": "Error Handling Strategy" + } + ], + "recapNotes": [ + "1. Failures are normal in production systems.", + "2. Errors should be categorized (runtime, network, auth, authorization, business, server).", + "3. Handle errors at the highest reasonable layer.", + "4. Use HTTP interceptors for centralized HTTP error handling.", + "5. Use Angular Global ErrorHandler for unexpected runtime failures.", + "6. Refresh tokens should handle most 401 responses." + ], + "searchTokens": [ + "error", + "handling", + "strategy", + "senior", + "architecture", + "enterprise", + "interview", + "priority", + "frequency", + "recommended", + "depth", + "relevant", + "what", + "layers", + "handle", + "errors?", + "http", + "interceptors?", + "401?", + "difference", + "between", + "403?", + "global", + "errorhandler?", + "should", + "users", + "technical", + "when", + "retry", + "used?", + "trap" + ] + }, + { + "id": "34-enterprise-architecture", + "slug": "34-enterprise-architecture", + "title": "Enterprise Architecture", + "summary": "Very High (Senior / Lead / Architect Roles)", + "category": "06-senior-architecture", + "categoryTitle": "Senior Architecture & Enterprise", + "categoryBadge": "Architect", + "categoryIcon": "shield-check", + "categoryOrder": 6, + "order": "34", + "fileName": "34-enterprise-architecture.md", + "contentUrl": "assets/content/06-senior-architecture/34-enterprise-architecture.md", + "wordCount": 1656, + "readingTime": 9, + "headings": [ + { + "level": 1, + "text": "Enterprise Architecture" + }, + { + "level": 1, + "text": "First Principles" + }, + { + "level": 1, + "text": "Definition" + }, + { + "level": 1, + "text": "Architecture vs Implementation" + }, + { + "level": 1, + "text": "What Problems Does Enterprise Architecture Solve?" + }, + { + "level": 1, + "text": "Core Architectural Principles" + }, + { + "level": 1, + "text": "Principle 1: Separation Of Concerns" + }, + { + "level": 1, + "text": "Principle 2: Single Responsibility Principle" + }, + { + "level": 1, + "text": "Principle 3: High Cohesion" + }, + { + "level": 1, + "text": "Principle 4: Low Coupling" + }, + { + "level": 1, + "text": "Angular Architecture Evolution" + }, + { + "level": 1, + "text": "Modern Angular Architecture" + } + ], + "flashcards": [ + { + "question": "What is the primary purpose of enterprise frontend architecture?", + "answer": "To prevent architectural decay, decouple business domains, ensure independent team velocity, and enforce strict module boundaries using patterns like Nx monorepos and clean architecture.", + "source": "Enterprise Architecture" + }, + { + "question": "What are the three core architectural layers in a scalable Angular enterprise app?", + "answer": "Core (singleton services, auth, global error handling), Shared (dumb UI components, reusable pipes, design system tokens), and Features (domain-specific modules, smart containers, routing, local state).", + "source": "Enterprise Architecture" + }, + { + "question": "How do you prevent architectural decay and circular dependencies across features?", + "answer": "Enforce strict unidirectional dependency rules (e.g. via Nx boundary lint rules): features can import from shared/core, but features must never import from peer features directly. Inter-feature communication occurs via URL navigation or global event buses.", + "source": "Enterprise Architecture" + } + ], + "recapNotes": [ + "Enterprise Applications", + "Angular Architecture", + "System Design Discussions", + "Technical Leadership" + ], + "searchTokens": [ + "enterprise", + "architecture", + "senior", + "interview", + "priority", + "frequency", + "recommended", + "depth", + "relevant", + "what", + "architecture?", + "organize", + "feature?", + "belongs", + "core?", + "shared?", + "lazy", + "loading?", + "container", + "presentational", + "components?", + "where", + "should", + "calls", + "live?", + "cross", + "cutting", + "concerns?" + ] + }, + { + "id": "35-micro-frontends", + "slug": "35-micro-frontends", + "title": "Micro Frontends", + "summary": "Medium to High (Senior, Lead, Architect Roles)", + "category": "06-senior-architecture", + "categoryTitle": "Senior Architecture & Enterprise", + "categoryBadge": "Architect", + "categoryIcon": "shield-check", + "categoryOrder": 6, + "order": "35", + "fileName": "35-micro-frontends.md", + "contentUrl": "assets/content/06-senior-architecture/35-micro-frontends.md", + "wordCount": 2188, + "readingTime": 11, + "headings": [ + { + "level": 1, + "text": "Micro Frontends" + }, + { + "level": 1, + "text": "First Principles" + }, + { + "level": 1, + "text": "Problem 1: Build Size" + }, + { + "level": 1, + "text": "Problem 2: Deployment Coupling" + }, + { + "level": 1, + "text": "Problem 3: Team Coupling" + }, + { + "level": 1, + "text": "Problem 4: Upgrade Pressure" + }, + { + "level": 1, + "text": "Problem 5: Ownership" + }, + { + "level": 1, + "text": "What Are Micro Frontends?" + }, + { + "level": 1, + "text": "Monolith vs Micro Frontend" + }, + { + "level": 2, + "text": "Traditional Frontend Monolith" + }, + { + "level": 2, + "text": "Micro Frontend Architecture" + }, + { + "level": 1, + "text": "Core Objective" + } + ], + "flashcards": [ + { + "question": "When should you choose Micro Frontends over a Modular Monolith?", + "answer": "When multiple autonomous teams require independent deployment pipelines, independent release cadences, and distinct domain ownership. For single teams or small-to-medium apps, a modular monolith with strict domain boundaries is preferred.", + "source": "Micro Frontends" + }, + { + "question": "What are the architectural trade-offs of Micro Frontends in Angular?", + "answer": "Benefits include independent deployments and domain isolation. Trade-offs include increased tooling complexity, bundle overhead across remotes, version skew between shared libraries, and complex cross-MFE integration testing.", + "source": "Micro Frontends" + }, + { + "question": "Should Micro Frontends share a single global NgRx store?", + "answer": "No. Sharing a single mutable global store creates tight coupling across MFEs, defeating independent deployments. Each MFE should manage its own isolated domain state, communicating across boundaries only via contracts, custom events, or URL state.", + "source": "Micro Frontends" + } + ], + "recapNotes": [ + "1. Micro Frontends are primarily an organizational scaling strategy.", + "2. Module Federation is the most common Angular implementation mechanism.", + "3. The shell owns platform concerns.", + "4. Business domains define micro frontend boundaries.", + "5. Runtime loading enables independent deployment.", + "6. Shared state should be minimal." + ], + "searchTokens": [ + "micro", + "frontends", + "senior", + "architecture", + "enterprise", + "interview", + "priority", + "frequency", + "recommended", + "depth", + "relevant", + "traditional", + "frontend", + "monolith", + "team", + "autonomy", + "independent", + "deployment", + "scalability", + "clear", + "ownership", + "technology", + "flexibility", + "operational", + "complexity", + "communication", + "governance", + "challenges", + "potential", + "performance", + "issues", + "what", + "frontend?", + "frontends?", + "module", + "federation?", + "shell", + "application?", + "should", + "mfes" + ] + }, + { + "id": "36-mock-interview-questions", + "slug": "36-mock-interview-questions", + "title": "Angular Mock Interview Questions", + "summary": "This document is intended for rapid interview revision.", + "category": "07-interview-preparation", + "categoryTitle": "Interview Drills & System Design", + "categoryBadge": "Interview", + "categoryIcon": "target", + "categoryOrder": 7, + "order": "36", + "fileName": "36-mock-interview-questions.md", + "contentUrl": "assets/content/07-interview-preparation/36-mock-interview-questions.md", + "wordCount": 854, + "readingTime": 5, + "headings": [ + { + "level": 1, + "text": "Angular Mock Interview Questions" + }, + { + "level": 2, + "text": "Purpose" + }, + { + "level": 1, + "text": "Angular Fundamentals" + }, + { + "level": 2, + "text": "Q: What is Angular?" + }, + { + "level": 2, + "text": "Q: SPA vs MPA?" + }, + { + "level": 2, + "text": "Q: What is Bootstrapping?" + }, + { + "level": 1, + "text": "Components" + }, + { + "level": 2, + "text": "Q: What is a Component?" + }, + { + "level": 2, + "text": "Q: Smart vs Dumb Components?" + }, + { + "level": 2, + "text": "Q: Lifecycle Hooks Order?" + }, + { + "level": 1, + "text": "Change Detection" + }, + { + "level": 2, + "text": "Q: What is Change Detection?" + } + ], + "flashcards": [ + { + "question": "What is Angular?", + "answer": "Angular is a TypeScript-based frontend framework used for building Single Page Applications (SPAs). Component-based architecture Dependency Injection", + "source": "Angular Mock Interview Questions" + }, + { + "question": "SPA vs MPA?", + "answer": "Single HTML page Client-side routing Faster navigation", + "source": "Angular Mock Interview Questions" + }, + { + "question": "What is Bootstrapping?", + "answer": "Process of loading the Angular application and creating the root component.", + "source": "Angular Mock Interview Questions" + }, + { + "question": "What is a Component?", + "answer": "A component controls a portion of the UI. Template Styles", + "source": "Angular Mock Interview Questions" + }, + { + "question": "Smart vs Dumb Components?", + "answer": "Business logic Service interaction State management", + "source": "Angular Mock Interview Questions" + }, + { + "question": "What is Change Detection?", + "answer": "Mechanism Angular uses to synchronize data and UI.", + "source": "Angular Mock Interview Questions" + }, + { + "question": "Default vs OnPush?", + "answer": "Checks entire tree Input changes Event occurs", + "source": "Angular Mock Interview Questions" + }, + { + "question": "Why Use OnPush?", + "answer": "Improves performance by reducing unnecessary checks.", + "source": "Angular Mock Interview Questions" + } + ], + "recapNotes": [ + "Before interviews", + "During revision sessions", + "For self-assessment", + "For mock interview practice" + ], + "searchTokens": [ + "angular", + "mock", + "interview", + "questions", + "drills", + "system", + "design", + "purpose", + "what", + "angular?", + "mpa?", + "bootstrapping?", + "component?", + "smart", + "dumb", + "components?", + "lifecycle", + "hooks", + "order?", + "change", + "detection?", + "default", + "onpush?", + "singleton", + "service?", + "observable", + "promise?", + "switchmap", + "mergemap?", + "concatmap?", + "exhaustmap?", + "sharereplay?", + "problem", + "signals", + "solve?", + "signal", + "computed", + "effect?", + "rxjs?", + "lazy" + ] + }, + { + "id": "37-frontend-system-design", + "slug": "37-frontend-system-design", + "title": "Frontend System Design", + "summary": "``text How Do We Design Large Applications? ``", + "category": "07-interview-preparation", + "categoryTitle": "Interview Drills & System Design", + "categoryBadge": "Interview", + "categoryIcon": "target", + "categoryOrder": 7, + "order": "37", + "fileName": "37-frontend-system-design.md", + "contentUrl": "assets/content/07-interview-preparation/37-frontend-system-design.md", + "wordCount": 278, + "readingTime": 2, + "headings": [ + { + "level": 1, + "text": "Frontend System Design" + }, + { + "level": 2, + "text": "Purpose" + }, + { + "level": 1, + "text": "Design Interview Mindset" + }, + { + "level": 1, + "text": "Design Framework" + }, + { + "level": 2, + "text": "Step 1" + }, + { + "level": 2, + "text": "Step 2" + }, + { + "level": 2, + "text": "Step 3" + }, + { + "level": 2, + "text": "Step 4" + }, + { + "level": 2, + "text": "Step 5" + }, + { + "level": 1, + "text": "Design: Dashboard" + }, + { + "level": 1, + "text": "Design: E-Commerce" + }, + { + "level": 1, + "text": "Design: Chat Application" + } + ], + "flashcards": [ + { + "question": "What is the primary mindset shift in frontend system design interviews?", + "answer": "Shift focus from framework syntax to large-scale architecture, scalability, maintainability, business domain boundaries, and architectural trade-offs.", + "source": "Frontend System Design" + }, + { + "question": "What are the 5 core stages of the Angular system design framework?", + "answer": "1. Clarify functional and non-functional requirements. 2. Identify business domains. 3. Define UI architecture & routing. 4. Determine state management strategy. 5. Establish performance, caching, and offline strategies.", + "source": "Frontend System Design" + }, + { + "question": "How should state management be structured in large-scale Angular applications?", + "answer": "Categorize state into Local UI state (Signals / component state), Shared feature state (scoped services), and Global entity state (NgRx / Signals Store). Only lift state when multiple decoupled domains require access.", + "source": "Frontend System Design" + }, + { + "question": "What performance strategies should be considered when designing enterprise dashboards?", + "answer": "Implement route lazy loading, OnPush change detection with Signals, deferrable views (@defer) for expensive widgets, virtual scrolling for tabular data, and API response caching via HTTP interceptors.", + "source": "Frontend System Design" + }, + { + "question": "How do you architect real-time event-driven applications in Angular?", + "answer": "Abstract WebSocket connections behind singleton services, pipe incoming streams into RxJS subjects or signals, normalize entity state in memory, and implement CDK virtual scroll to handle high-frequency renders.", + "source": "Frontend System Design" + } + ], + "recapNotes": [], + "searchTokens": [ + "frontend", + "system", + "design", + "interview", + "drills", + "purpose", + "step" + ] + }, + { + "id": "38-angular-testing-deep-dive", + "slug": "38-angular-testing-deep-dive", + "title": "Angular Testing Deep Dive", + "summary": "``typescript it('should create component'); ``", + "category": "07-interview-preparation", + "categoryTitle": "Interview Drills & System Design", + "categoryBadge": "Interview", + "categoryIcon": "target", + "categoryOrder": 7, + "order": "38", + "fileName": "38-angular-testing-deep-dive.md", + "contentUrl": "assets/content/07-interview-preparation/38-angular-testing-deep-dive.md", + "wordCount": 349, + "readingTime": 2, + "headings": [ + { + "level": 1, + "text": "Angular Testing Deep Dive" + }, + { + "level": 1, + "text": "Why Testing?" + }, + { + "level": 1, + "text": "Testing Pyramid" + }, + { + "level": 1, + "text": "Unit Testing" + }, + { + "level": 1, + "text": "TestBed" + }, + { + "level": 1, + "text": "Component Testing" + }, + { + "level": 1, + "text": "Service Testing" + }, + { + "level": 1, + "text": "Mocking Dependencies" + }, + { + "level": 1, + "text": "HTTP Testing" + }, + { + "level": 1, + "text": "Guard Testing" + }, + { + "level": 1, + "text": "Interceptor Testing" + }, + { + "level": 1, + "text": "Async Testing" + } + ], + "flashcards": [ + { + "question": "What is the primary role of TestBed in Angular unit testing?", + "answer": "TestBed is Angular’s test execution module that configures and initializes a mock dependency injection environment, compiling components and templates in isolation.", + "source": "Angular Testing Deep Dive" + }, + { + "question": "What is the difference between ComponentFixture and DebugElement?", + "answer": "ComponentFixture provides a test harness for the component instance and change detection cycle. DebugElement is an abstraction over native DOM elements with cross-platform inspection utilities.", + "source": "Angular Testing Deep Dive" + } + ], + "recapNotes": [], + "searchTokens": [ + "angular", + "testing", + "deep", + "dive", + "interview", + "drills", + "system", + "design", + "priority", + "frequency", + "difference", + "between", + "unit", + "integration", + "testing?", + "mock", + "dependencies?", + "what", + "httptestingcontroller?", + "e2e?" + ] + }, + { + "id": "39-behavioral-interview-preparation", + "slug": "39-behavioral-interview-preparation", + "title": "Behavioral Interview Preparation", + "summary": "Many senior candidates prepare extensively for:", + "category": "07-interview-preparation", + "categoryTitle": "Interview Drills & System Design", + "categoryBadge": "Interview", + "categoryIcon": "target", + "categoryOrder": 7, + "order": "39", + "fileName": "39-behavioral-interview-preparation.md", + "contentUrl": "assets/content/07-interview-preparation/39-behavioral-interview-preparation.md", + "wordCount": 464, + "readingTime": 3, + "headings": [ + { + "level": 1, + "text": "Behavioral Interview Preparation" + }, + { + "level": 2, + "text": "Why Behavioral Interviews Matter" + }, + { + "level": 1, + "text": "Recommended Answer Framework" + }, + { + "level": 1, + "text": "Tell Me About Yourself" + }, + { + "level": 1, + "text": "Why Do You Want To Change?" + }, + { + "level": 1, + "text": "Tell Me About A Challenging Project" + }, + { + "level": 1, + "text": "Tell Me About A Technical Decision You Made" + }, + { + "level": 1, + "text": "Describe A Production Incident" + }, + { + "level": 1, + "text": "Conflict With Team Member" + }, + { + "level": 1, + "text": "Mentoring Experience" + }, + { + "level": 1, + "text": "Handling Tight Deadlines" + }, + { + "level": 1, + "text": "Failure Question" + } + ], + "flashcards": [ + { + "question": "What is the core architectural purpose of Behavioral Interview Preparation?", + "answer": "Behavioral Interview Preparation provides structured, maintainable patterns for modern Angular enterprise applications. Refer to the deep dive guide for implementation details and trade-offs.", + "source": "Behavioral Interview Preparation" + } + ], + "recapNotes": [], + "searchTokens": [ + "behavioral", + "interview", + "preparation", + "drills", + "system", + "design", + "interviews", + "matter" + ] + }, + { + "id": "40-angular-system-design-case-studies", + "slug": "40-angular-system-design-case-studies", + "title": "Angular System Design Case Studies", + "summary": "1. Design an Employee Portal. 2. Design an E-Commerce Platform. 3. Design a Real-Time Chat App. 4. Design a Dashboard Platform. 5. Design a Multi-Tenant SaaS Pr...", + "category": "07-interview-preparation", + "categoryTitle": "Interview Drills & System Design", + "categoryBadge": "Interview", + "categoryIcon": "target", + "categoryOrder": 7, + "order": "40", + "fileName": "40-angular-system-design-case-studies.md", + "contentUrl": "assets/content/07-interview-preparation/40-angular-system-design-case-studies.md", + "wordCount": 610, + "readingTime": 4, + "headings": [ + { + "level": 1, + "text": "Angular System Design Case Studies" + }, + { + "level": 2, + "text": "Purpose" + }, + { + "level": 1, + "text": "Case Study 1" + }, + { + "level": 1, + "text": "Design An Employee Management Portal" + }, + { + "level": 2, + "text": "Requirements" + }, + { + "level": 2, + "text": "High-Level Architecture" + }, + { + "level": 2, + "text": "State Strategy" + }, + { + "level": 2, + "text": "Routing" + }, + { + "level": 2, + "text": "Security" + }, + { + "level": 2, + "text": "Error Handling" + }, + { + "level": 1, + "text": "Case Study 2" + }, + { + "level": 1, + "text": "Design An E-Commerce Frontend" + } + ], + "flashcards": [ + { + "question": "How do you design an enterprise notification center with high event volume?", + "answer": "Use Server-Sent Events (SSE) or WebSockets managed by a singleton service, pipe events through an RxJS buffer/debounce, maintain an in-memory normalized queue, and virtualize the notification dropdown list.", + "source": "Angular System Design Case Studies" + }, + { + "question": "How do you architect multi-tenant SaaS dashboards in Angular?", + "answer": "Dynamically load tenant configuration via APP_INITIALIZER, inject theme CSS variables at runtime, lazy load tenant-specific feature modules, and enforce tenant-scoped HTTP headers via interceptors.", + "source": "Angular System Design Case Studies" + } + ], + "recapNotes": [], + "searchTokens": [ + "angular", + "system", + "design", + "case", + "studies", + "interview", + "drills", + "purpose", + "requirements", + "high", + "level", + "architecture", + "state", + "strategy", + "routing", + "security", + "error", + "handling", + "domains", + "management", + "performance", + "communication", + "challenges", + "focus", + "shared", + "context", + "avoid", + "sharing", + "step" + ] + }, + { + "id": "41-quick-fire-qa", + "slug": "41-quick-fire-qa", + "title": "Quick-Fire Interview Q&A", + "summary": "How does angular help Components? Data Binding → Update Component UI DI → Give Components Services Lifecycle Hooks → Manage Component Lifecycle Change Detection...", + "category": "07-interview-preparation", + "categoryTitle": "Interview Drills & System Design", + "categoryBadge": "Interview", + "categoryIcon": "target", + "categoryOrder": 7, + "order": "41", + "fileName": "41-quick-fire-qa.md", + "contentUrl": "assets/content/07-interview-preparation/41-quick-fire-qa.md", + "wordCount": 396, + "readingTime": 2, + "headings": [ + { + "level": 1, + "text": "Quick-Fire Interview Q&A" + }, + { + "level": 2, + "text": "Core Technical Q&A" + }, + { + "level": 3, + "text": "Why does Angular use TypeScript?" + } + ], + "flashcards": [ + { + "question": "What is the difference between declarations, imports, and exports in NgModules?", + "answer": "declarations are components/directives/pipes belonging to this module; imports bring in external modules needed by this module; exports make internal declarations available to other modules importing this one.", + "source": "Quick-Fire Interview Q&A" + }, + { + "question": "What is tree-shaking and how does Angular achieve it?", + "answer": "Tree-shaking eliminates dead/unused code from final production bundles using ES modules static analysis and providedIn: \"root\" injectable metadata.", + "source": "Quick-Fire Interview Q&A" + } + ], + "recapNotes": [], + "searchTokens": [ + "quick", + "fire", + "interview", + "q&a", + "drills", + "system", + "design", + "core", + "technical" + ] + }, + { + "id": "01-angular-cheat-sheet", + "slug": "01-angular-cheat-sheet", + "title": "Angular Cheat Sheet", + "summary": "Angular is a TypeScript-based framework for building:", + "category": "08-cheat-sheets", + "categoryTitle": "Rapid Cheat Sheets", + "categoryBadge": "Quick Cram", + "categoryIcon": "bookmark", + "categoryOrder": 8, + "order": "01", + "fileName": "01-angular-cheat-sheet.md", + "contentUrl": "assets/content/08-cheat-sheets/01-angular-cheat-sheet.md", + "wordCount": 173, + "readingTime": 1, + "headings": [ + { + "level": 1, + "text": "Angular Cheat Sheet" + }, + { + "level": 1, + "text": "Angular" + }, + { + "level": 1, + "text": "Angular Building Blocks" + }, + { + "level": 1, + "text": "Data Binding" + }, + { + "level": 1, + "text": "Component Communication" + }, + { + "level": 1, + "text": "Lifecycle Hooks" + }, + { + "level": 1, + "text": "Dependency Injection" + }, + { + "level": 1, + "text": "Service Scopes" + }, + { + "level": 1, + "text": "Common Interview Questions" + }, + { + "level": 1, + "text": "Golden Rules" + } + ], + "flashcards": [ + { + "question": "What is the fundamental difference between Components and Directives?", + "answer": "A Component is a Directive with an associated HTML template and styles. Directives without templates attach custom behavior or styling to existing DOM elements.", + "source": "Angular Cheat Sheet" + }, + { + "question": "Why does Angular use Dependency Injection?", + "answer": "DI provides loose coupling, simplifies unit testing via mock injection, promotes single responsibility, and allows flexible hierarchical scoping (root, feature, or component level).", + "source": "Angular Cheat Sheet" + } + ], + "recapNotes": [], + "searchTokens": [ + "angular", + "cheat", + "sheet", + "rapid", + "sheets" + ] + }, + { + "id": "02-rxjs-cheat-sheet", + "slug": "02-rxjs-cheat-sheet", + "title": "RxJS Cheat Sheet", + "summary": "``text Stores Current Value ``", + "category": "08-cheat-sheets", + "categoryTitle": "Rapid Cheat Sheets", + "categoryBadge": "Quick Cram", + "categoryIcon": "bookmark", + "categoryOrder": 8, + "order": "02", + "fileName": "02-rxjs-cheat-sheet.md", + "contentUrl": "assets/content/08-cheat-sheets/02-rxjs-cheat-sheet.md", + "wordCount": 142, + "readingTime": 1, + "headings": [ + { + "level": 1, + "text": "RxJS Cheat Sheet" + }, + { + "level": 1, + "text": "Observable vs Promise" + }, + { + "level": 1, + "text": "Subjects" + }, + { + "level": 1, + "text": "Operator Decision Guide" + }, + { + "level": 1, + "text": "Transformation" + }, + { + "level": 1, + "text": "Filtering" + }, + { + "level": 1, + "text": "Combination" + }, + { + "level": 1, + "text": "Error Handling" + }, + { + "level": 1, + "text": "Caching" + }, + { + "level": 1, + "text": "Interview Traps" + }, + { + "level": 1, + "text": "Golden Rule" + } + ], + "flashcards": [ + { + "question": "What is the difference between switchMap, mergeMap, concatMap, and exhaustMap?", + "answer": "switchMap cancels previous in-flight requests (search/typeahead); mergeMap runs requests concurrently; concatMap queues requests in strict sequence; exhaustMap ignores new emissions while the current request is pending (submit buttons).", + "source": "RxJS Cheat Sheet" + }, + { + "question": "How do you prevent memory leaks when subscribing to Observables?", + "answer": "Use the AsyncPipe in templates, takeUntilDestroyed() in component constructors, take(1) / first() for single emissions, or manually unsubscribe in ngOnDestroy.", + "source": "RxJS Cheat Sheet" + } + ], + "recapNotes": [], + "searchTokens": [ + "rxjs", + "cheat", + "sheet", + "rapid", + "sheets" + ] + }, + { + "id": "03-signals-cheat-sheet", + "slug": "03-signals-cheat-sheet", + "title": "Signals Cheat Sheet", + "summary": "``typescript total = computed( () => price() * qty() ); ``", + "category": "08-cheat-sheets", + "categoryTitle": "Rapid Cheat Sheets", + "categoryBadge": "Quick Cram", + "categoryIcon": "bookmark", + "categoryOrder": 8, + "order": "03", + "fileName": "03-signals-cheat-sheet.md", + "contentUrl": "assets/content/08-cheat-sheets/03-signals-cheat-sheet.md", + "wordCount": 116, + "readingTime": 1, + "headings": [ + { + "level": 1, + "text": "Signals Cheat Sheet" + }, + { + "level": 2, + "text": "Core APIs" + }, + { + "level": 1, + "text": "Updates" + }, + { + "level": 1, + "text": "Why Signals?" + }, + { + "level": 1, + "text": "Signals vs RxJS" + }, + { + "level": 1, + "text": "Interview Question" + }, + { + "level": 1, + "text": "Golden Rule" + } + ], + "flashcards": [ + { + "question": "Do Angular Signals replace RxJS?", + "answer": "No. Signals are ideal for synchronous reactive UI state, template binding, and localized derivation. RxJS remains the best tool for complex asynchronous streams, WebSocket events, debounce/throttle, and cancellation.", + "source": "Signals Cheat Sheet" + }, + { + "question": "What is the difference between computed() and effect() in Angular Signals?", + "answer": "computed() produces a read-only derived signal lazily and memoizes the result without side effects. effect() executes side effects (logging, DOM mutations, third-party libraries) whenever its dependent signals change.", + "source": "Signals Cheat Sheet" + } + ], + "recapNotes": [], + "searchTokens": [ + "signals", + "cheat", + "sheet", + "rapid", + "sheets", + "core", + "apis" + ] + }, + { + "id": "04-routing-cheat-sheet", + "slug": "04-routing-cheat-sheet", + "title": "Routing Cheat Sheet", + "summary": "Authorization = Guard + Backend Validation ```", + "category": "08-cheat-sheets", + "categoryTitle": "Rapid Cheat Sheets", + "categoryBadge": "Quick Cram", + "categoryIcon": "bookmark", + "categoryOrder": 8, + "order": "04", + "fileName": "04-routing-cheat-sheet.md", + "contentUrl": "assets/content/08-cheat-sheets/04-routing-cheat-sheet.md", + "wordCount": 107, + "readingTime": 1, + "headings": [ + { + "level": 1, + "text": "Routing Cheat Sheet" + }, + { + "level": 1, + "text": "Route Types" + }, + { + "level": 1, + "text": "Parameters" + }, + { + "level": 1, + "text": "Guards" + }, + { + "level": 1, + "text": "Resolvers" + }, + { + "level": 1, + "text": "Lazy Loading" + }, + { + "level": 1, + "text": "Interview Questions" + }, + { + "level": 1, + "text": "Golden Rule" + } + ], + "flashcards": [ + { + "question": "How does Angular lazy loading work with standalone components?", + "answer": "Using loadComponent: () => import(\"./path\").then(m => m.Component) in route configuration, Angular creates a separate chunk loaded only when the route is requested.", + "source": "Routing Cheat Sheet" + }, + { + "question": "What is the role of Route Resolvers?", + "answer": "Resolvers fetch required API data before the route component is rendered, ensuring the component doesn’t render an empty or partial UI while waiting for initial data.", + "source": "Routing Cheat Sheet" + } + ], + "recapNotes": [], + "searchTokens": [ + "routing", + "cheat", + "sheet", + "rapid", + "sheets" + ] + }, + { + "id": "05-forms-cheat-sheet", + "slug": "05-forms-cheat-sheet", + "title": "Forms Cheat Sheet", + "summary": "``typescript Validators.required ``", + "category": "08-cheat-sheets", + "categoryTitle": "Rapid Cheat Sheets", + "categoryBadge": "Quick Cram", + "categoryIcon": "bookmark", + "categoryOrder": 8, + "order": "05", + "fileName": "05-forms-cheat-sheet.md", + "contentUrl": "assets/content/08-cheat-sheets/05-forms-cheat-sheet.md", + "wordCount": 82, + "readingTime": 1, + "headings": [ + { + "level": 1, + "text": "Forms Cheat Sheet" + }, + { + "level": 1, + "text": "Form Types" + }, + { + "level": 1, + "text": "Enterprise Choice" + }, + { + "level": 1, + "text": "Reactive Building Blocks" + }, + { + "level": 1, + "text": "Validation" + }, + { + "level": 1, + "text": "Dynamic Forms" + }, + { + "level": 1, + "text": "Common Questions" + }, + { + "level": 1, + "text": "Golden Rule" + } + ], + "flashcards": [ + { + "question": "Why are Reactive Forms preferred over Template-Driven Forms in enterprise applications?", + "answer": "Reactive Forms are synchronous, programmatic, fully typed, easier to unit test without DOM interaction, and provide observable streams (valueChanges, statusChanges) for complex cross-field validation.", + "source": "Forms Cheat Sheet" + }, + { + "question": "How do Async Validators work in Angular Reactive Forms?", + "answer": "Async Validators return an Observable or Promise that emits validation errors or null after completing asynchronous operations (such as verifying username availability via an HTTP call).", + "source": "Forms Cheat Sheet" + } + ], + "recapNotes": [], + "searchTokens": [ + "forms", + "cheat", + "sheet", + "rapid", + "sheets" + ] + }, + { + "id": "06-ngrx-cheat-sheet", + "slug": "06-ngrx-cheat-sheet", + "title": "NgRx Cheat Sheet", + "summary": "``text Old State + Action = New State ``", + "category": "08-cheat-sheets", + "categoryTitle": "Rapid Cheat Sheets", + "categoryBadge": "Quick Cram", + "categoryIcon": "bookmark", + "categoryOrder": 8, + "order": "06", + "fileName": "06-ngrx-cheat-sheet.md", + "contentUrl": "assets/content/08-cheat-sheets/06-ngrx-cheat-sheet.md", + "wordCount": 134, + "readingTime": 1, + "headings": [ + { + "level": 1, + "text": "NgRx Cheat Sheet" + }, + { + "level": 1, + "text": "Mental Model" + }, + { + "level": 1, + "text": "Store" + }, + { + "level": 1, + "text": "Action" + }, + { + "level": 1, + "text": "dispatch()" + }, + { + "level": 1, + "text": "Reducer" + }, + { + "level": 1, + "text": "Effect" + }, + { + "level": 1, + "text": "Selector" + }, + { + "level": 1, + "text": "Pure Reducer" + }, + { + "level": 1, + "text": "Do Not Put In Reducers" + }, + { + "level": 1, + "text": "Typical Pattern" + }, + { + "level": 1, + "text": "Golden Rule" + } + ], + "flashcards": [ + { + "question": "What are the main components of NgRx architecture?", + "answer": "Store (single source of truth), Actions (dispatched event payloads), Reducers (pure functions creating new state), Selectors (memoized queries), and Effects (handling async side-effects like HTTP calls).", + "source": "NgRx Cheat Sheet" + } + ], + "recapNotes": [], + "searchTokens": [ + "ngrx", + "cheat", + "sheet", + "rapid", + "sheets" + ] + }, + { + "id": "07-auth-cheat-sheet", + "slug": "07-auth-cheat-sheet", + "title": "Authentication & Authorization Cheat Sheet", + "summary": "``http Authorization: Bearer token ``", + "category": "08-cheat-sheets", + "categoryTitle": "Rapid Cheat Sheets", + "categoryBadge": "Quick Cram", + "categoryIcon": "bookmark", + "categoryOrder": 8, + "order": "07", + "fileName": "07-auth-cheat-sheet.md", + "contentUrl": "assets/content/08-cheat-sheets/07-auth-cheat-sheet.md", + "wordCount": 122, + "readingTime": 1, + "headings": [ + { + "level": 1, + "text": "Authentication & Authorization Cheat Sheet" + }, + { + "level": 1, + "text": "Authentication" + }, + { + "level": 1, + "text": "Authorization" + }, + { + "level": 1, + "text": "JWT" + }, + { + "level": 1, + "text": "JWT Important" + }, + { + "level": 1, + "text": "Access Token" + }, + { + "level": 1, + "text": "Refresh Token" + }, + { + "level": 1, + "text": "Interceptor" + }, + { + "level": 1, + "text": "Guards" + }, + { + "level": 1, + "text": "RBAC" + }, + { + "level": 1, + "text": "Permission-Based" + }, + { + "level": 1, + "text": "Important Rule" + } + ], + "flashcards": [ + { + "question": "How should HTTP requests automatically attach JWT tokens in Angular?", + "answer": "Using an HttpInterceptor (via withInterceptors([authInterceptor])), which clones outgoing HttpRequest objects and appends the Authorization: Bearer header before forwarding the request.", + "source": "Authentication & Authorization Cheat Sheet" + }, + { + "question": "How do you handle 401 Unauthorized errors globally?", + "answer": "In an HTTP error interceptor, catch 401 responses using RxJS catchError, attempt token refresh if available, or clear session state and redirect to the login route.", + "source": "Authentication & Authorization Cheat Sheet" + } + ], + "recapNotes": [], + "searchTokens": [ + "authentication", + "authorization", + "cheat", + "sheet", + "rapid", + "sheets" + ] + }, + { + "id": "08-architecture-cheat-sheet", + "slug": "08-architecture-cheat-sheet", + "title": "Enterprise Architecture Cheat Sheet", + "summary": "Application-wide concerns.", + "category": "08-cheat-sheets", + "categoryTitle": "Rapid Cheat Sheets", + "categoryBadge": "Quick Cram", + "categoryIcon": "bookmark", + "categoryOrder": 8, + "order": "08", + "fileName": "08-architecture-cheat-sheet.md", + "contentUrl": "assets/content/08-cheat-sheets/08-architecture-cheat-sheet.md", + "wordCount": 144, + "readingTime": 1, + "headings": [ + { + "level": 1, + "text": "Enterprise Architecture Cheat Sheet" + }, + { + "level": 1, + "text": "Layers" + }, + { + "level": 1, + "text": "Core" + }, + { + "level": 1, + "text": "Shared" + }, + { + "level": 1, + "text": "Feature" + }, + { + "level": 1, + "text": "Enterprise Flow" + }, + { + "level": 1, + "text": "Container Components" + }, + { + "level": 1, + "text": "Presentational Components" + }, + { + "level": 1, + "text": "Cross-Cutting Concerns" + }, + { + "level": 1, + "text": "Micro Frontends" + }, + { + "level": 1, + "text": "Shell Owns" + }, + { + "level": 1, + "text": "Shared State" + } + ], + "flashcards": [ + { + "question": "What is the difference between Core and Shared modules?", + "answer": "Core contains singleton services, interceptors, and guards loaded once at startup. Shared contains purely reusable dumb components, directives, and pipes imported by multiple feature modules.", + "source": "Enterprise Architecture Cheat Sheet" + } + ], + "recapNotes": [], + "searchTokens": [ + "enterprise", + "architecture", + "cheat", + "sheet", + "rapid", + "sheets" + ] + }, + { + "id": "09-interview-day-cheat-sheet", + "slug": "09-interview-day-cheat-sheet", + "title": "Interview Day Quick Revision", + "summary": "DI = Dependency Management", + "category": "08-cheat-sheets", + "categoryTitle": "Rapid Cheat Sheets", + "categoryBadge": "Quick Cram", + "categoryIcon": "bookmark", + "categoryOrder": 8, + "order": "09", + "fileName": "09-interview-day-cheat-sheet.md", + "contentUrl": "assets/content/08-cheat-sheets/09-interview-day-cheat-sheet.md", + "wordCount": 155, + "readingTime": 1, + "headings": [ + { + "level": 1, + "text": "Interview Day Quick Revision" + }, + { + "level": 1, + "text": "Angular" + }, + { + "level": 1, + "text": "RxJS" + }, + { + "level": 1, + "text": "Signals" + }, + { + "level": 1, + "text": "Routing" + }, + { + "level": 1, + "text": "Forms" + }, + { + "level": 1, + "text": "Performance" + }, + { + "level": 1, + "text": "NgRx" + }, + { + "level": 1, + "text": "Auth" + }, + { + "level": 1, + "text": "Error Handling" + }, + { + "level": 1, + "text": "Architecture" + }, + { + "level": 1, + "text": "Micro Frontends" + } + ], + "flashcards": [ + { + "question": "What are the top 3 architectural topics senior Angular interviewers focus on?", + "answer": "1. Change Detection & Performance (Zone.js vs Signals, OnPush, @defer). 2. Reactive Architecture (RxJS stream management, state layering). 3. Scalable App Structure (modular monolith, Nx domain boundaries, lazy loading).", + "source": "Interview Day Quick Revision" + } + ], + "recapNotes": [], + "searchTokens": [ + "interview", + "day", + "quick", + "revision", + "rapid", + "cheat", + "sheets" + ] + }, + { + "id": "web-security-guide", + "slug": "web-security-guide", + "title": "Web & Angular Security Guide", + "summary": "Comprehensive enterprise security guide covering XSS, CSRF, CSP, sanitized DOM, and auth tokens.", + "category": "06-senior-architecture", + "categoryTitle": "Senior Architecture & Enterprise", + "categoryBadge": "Security", + "categoryIcon": "shield-check", + "categoryOrder": 6, + "order": "99s", + "fileName": "web_security_guide.md", + "contentUrl": "assets/content/web_security_guide.md", + "wordCount": 1926, + "readingTime": 10, + "headings": [ + { + "level": 1, + "text": "Web Application Security Guide: Core Vulnerabilities, First Principles, and Mitigations" + }, + { + "level": 2, + "text": "1. Cross-Site Scripting (XSS)" + }, + { + "level": 3, + "text": "🧩 First Principles" + }, + { + "level": 3, + "text": "💥 Practical Scenarios & Code Examples" + }, + { + "level": 3, + "text": "🛡️ Mitigation Steps" + }, + { + "level": 2, + "text": "2. Cross-Site Request Forgery (CSRF)" + }, + { + "level": 3, + "text": "🧩 First Principles" + }, + { + "level": 3, + "text": "💥 Practical Scenarios & Code Examples" + }, + { + "level": 3, + "text": "🛡️ Mitigation Steps" + }, + { + "level": 2, + "text": "3. SQL Injection (SQLi)" + }, + { + "level": 3, + "text": "🧩 First Principles" + }, + { + "level": 3, + "text": "💥 Practical Scenarios & Code Examples" + } + ], + "flashcards": [ + { + "question": "What is the core architectural purpose of Web & Angular Security?", + "answer": "Web & Angular Security provides structured, maintainable patterns for modern Angular enterprise applications. Refer to the deep dive guide for implementation details and trade-offs.", + "source": "Web & Angular Security" + } + ], + "recapNotes": [], + "searchTokens": [ + "web", + "angular", + "security", + "guide", + "domsanitizer", + "bypasssecuritytrusthtml", + "bypasssecuritytrustscript", + "@variable", + "httponly", + "httpclientmodule", + "xsrf-token", + "x-xsrf-token", + "canactivate", + "cross", + "site", + "scripting", + "request", + "forgery", + "csrf", + "injection", + "sqli", + "broken", + "object", + "level", + "authorization", + "bola", + "idor", + "server", + "side", + "ssrf" + ] + } + ] +} \ No newline at end of file diff --git a/apps/portal/public/assets/content/01-foundations/01-why-angular.md b/apps/portal/public/assets/content/01-foundations/01-why-angular.md new file mode 100644 index 0000000..60baa10 --- /dev/null +++ b/apps/portal/public/assets/content/01-foundations/01-why-angular.md @@ -0,0 +1,446 @@ +# Why Angular + +## Interview Priority + +**Must Know** + +## Interview Frequency + +**Very Common** + +## Recommended Depth + +**Strong Understanding** + +## Relevant For + +- Mid-Level Interviews +- Senior Developer Interviews +- Lead Developer Interviews +- Architect Discussions + +--- + +# What Is Angular? + +Angular is a complete, opinionated, TypeScript-based development platform and framework maintained by Google for building scalable, enterprise-grade Single Page Applications (SPAs) and cross-platform web applications. + +Unlike lightweight UI libraries that focus solely on rendering views, Angular provides an entire **"batteries-included"** ecosystem out of the box: + +- **Component-Driven UI Model**: Encapsulated templates, styles, and TypeScript classes. +- **Hierarchical Dependency Injection (DI)**: First-class inversion of control for decoupled, testable business services. +- **Declarative Reactive Primitives**: Fine-grained Angular Signals combined with RxJS streams for asynchronous operations. +- **Robust Client-Side Routing**: Guards, resolvers, nested child routes, and route-level lazy loading. +- **Type-Safe HTTP Client**: Interceptors, request cloning, progress tracking, and test mocks. +- **Form Management Suites**: Both programmatic Reactive Forms (with typed controls) and Template-Driven forms. +- **Ahead-Of-Time (AOT) Compilation**: Template type-checking and zero-overhead browser execution. +- **CLI & Automated Migrations**: Tooling with built-in code generation, build optimization, and automated version migrations via `ng update`. + +--- + +# Why Does It Exist? + +As web applications evolved from static informational documents into complex, mission-critical business platforms, frontend codebases grew exponentially. + +In early web development, applications suffered from critical structural deficiencies: + +- **Lack of Architectural Standards**: Developers improvised structures, leading to unmaintainable spaghetti code. +- **DOM-Coupled Business Logic**: Logic was tightly coupled to raw DOM element IDs and CSS selectors. +- **State Synchronization Nightmare**: Updating data in one UI section required manual imperative updates across multiple other DOM nodes. +- **Difficult Unit & Integration Testing**: Tightly coupled JavaScript code with direct browser globals (`window`, `document`) was nearly impossible to isolate and test deterministically. +- **Team Scaling Bottlenecks**: Without strict conventions, different developers on the same team wrote code in wildly conflicting styles. + +Angular was conceived to bring **rigorous software engineering principles**—modularity, encapsulation, dependency inversion, and declarative data-binding—directly to browser applications. + +--- + +# Historical Context + +Understanding Angular's evolution is essential for senior engineering discussions: + +```text +2010 2016 2022 2024+ +AngularJS (1.x) ───► Angular (v2–v15) ───► Standalone Revolution ──► Renaissance (v16–v19+) +Two-way binding Complete ground-up Standalone components Angular Signals +$scope, $watch rewrite in TypeScript NgModules optional Fine-grained reactivity +Digest cycle issues Component tree, DI Vite/esbuild bundlers Built-in control flow (@if, @for) +DOM controller model RxJS-centric SSR & Hydration Zoneless change detection +``` + +### 1. The AngularJS Era (v1.x - 2010) +AngularJS pioneered two-way data binding and client-side MVC in the browser using `$scope` and controllers. However, its dirty-checking "digest cycle" degraded severely in complex applications with thousands of bindings, and it lacked static typing. + +### 2. The Great Rewrite: Angular 2+ (2016) +Google rebuilt the framework entirely from scratch in TypeScript. It abandoned `$scope` in favor of a hierarchical Component Tree, unidirectional data flow, Zone.js for change detection, and native Dependency Injection. + +### 3. The Modern Angular Renaissance (v16 - v19+) +Modern Angular represents a major leap forward: +- **Signals**: Native, fine-grained synchronous reactivity replacing heavy reliance on Zone.js and manual RxJS subscriptions for local UI state. +- **Standalone by Default**: Eradication of boilerplate `NgModule` declarations. +- **Built-in Control Flow**: Native `@if`, `@for`, `@switch`, and `@defer` syntax replacing structural directives (`*ngIf`, `*ngFor`). +- **Zoneless Support**: Direct reactivity without Zone.js monkey-patching browser async APIs. +- **Hydration & SSR**: Non-destructive hydration with server-side rendering and event replay. + +--- + +# Problem It Solves + +Consider a global banking portal with 500+ screens, 60 developers, and 5 distinct functional teams (Accounts, Transfers, Cards, Loans, Admin). + +### Without an Opinionated Framework (Ad-hoc Libraries) +- Team A chooses `Zustand` for state; Team B chooses `Redux Toolkit`; Team C writes custom event emitters. +- Team A uses `Axios`; Team B uses native `fetch`; Team C wraps `XMLHttpRequest`. +- Every team invents their own directory structure and routing abstractions. +- Cross-team developer mobility is paralyzed because onboarding requires learning 5 different bespoke architectures. +- Upgrading foundational dependencies requires rebuilding bespoke wiring. + +### With Angular +- Every team uses the same **Dependency Injection container**, the same **HttpClient**, the same **Router**, and the same **Signal reactivity model**. +- A developer from the Loans team can review or contribute to the Transfers team's codebase immediately with zero architectural friction. +- Google provides automated migrations via `ng update`, upgrading foundational framework dependencies, RxJS versions, and syntax automatically across hundreds of enterprise applications. + +--- + +# First Principles Explanation + +From first principles, all client-side web applications are **state machines that project internal state onto the DOM and handle user inputs that transition state**: + +$$\text{UI} = f(\text{State})$$ + +When state changes, the UI must update reliably, efficiently, and predictably. + +```text +┌────────────────────────────────────────────────────────┐ +│ Internal State │ +│ (Signals, Component Properties, Data Models) │ +└──────────────────────────┬─────────────────────────────┘ + │ + Declarative Projection + │ + ▼ +┌────────────────────────────────────────────────────────┐ +│ Document Object Model │ +│ (Rendered HTML Nodes) │ +└──────────────────────────┬─────────────────────────────┘ + │ + User / Network Event + │ + ▼ +┌────────────────────────────────────────────────────────┐ +│ State Transition │ +│ (Methods, Signal Updates, Service Calls) │ +└────────────────────────────────────────────────────────┘ +``` + +In plain JavaScript or jQuery, the developer manually handles both the **state transition** and every individual **DOM element mutation**. If you have $N$ pieces of state and $M$ UI elements, the developer must manage up to $N \times M$ imperative mutation paths. + +Angular solves this by providing: +1. **A Declarative Compiler**: You declare *what* the DOM should look like based on state; the Angular compiler generates highly optimized JavaScript instructions that surgically update the DOM. +2. **Deterministic Data Flow**: Data flows down the component hierarchy via inputs; events flow up via outputs. + +--- + +# Real World Analogy + +Think of building an enterprise commercial skyscraper versus building a customized tiny house: + +```text + LIGHTWEIGHT LIBRARY (e.g., React) OPINIONATED FRAMEWORK (e.g., Angular) + "Custom DIY Timber Frame" "Commercial Steel Skyscraper" + + ┌─────────────────────────────────┐ ┌─────────────────────────────────┐ + │ Pick your own lumber │ │ Certified structural steel │ + │ Pick your own plumbing system │ │ Standardized plumbing shafts │ + │ Pick your own electrical code │ │ Mandated electrical grid │ + │ Build your own elevator shaft │ │ Commercial passenger elevators │ + │ Every house has a unique blueprint│ │ Rigorous architectural codes │ + └─────────────────────────────────┘ └─────────────────────────────────┘ +``` + +- **A Lightweight Library (like React)** gives you the timber and nails (rendering engine). You must independently select, integrate, test, and maintain the plumbing (routing), electrical wiring (forms), HVAC (state management), and foundation inspection (build tooling). +- **An Opinionated Framework (like Angular)** gives you a certified, blueprint-driven commercial building with pre-engineered elevator shafts (routing), standardized electrical conduits (dependency injection), and municipal inspection codes (TypeScript and linting conventions). + +--- + +# Angular vs React vs Vue + +The most common senior interview topic is evaluating frontend technologies for enterprise projects: + +| Architectural Dimension | Angular (Modern 18+/19+) | React (18+/19+) | Vue (3+) | +| :--- | :--- | :--- | :--- | +| **Category** | Full-fledged Framework | View Rendering Library | Progressive Framework | +| **Philosophy** | Highly Opinionated, Standardized | Unopinionated, Flexible | Pragmatic, Approachable | +| **Language** | TypeScript by default (First-class) | JavaScript / TS optional | JavaScript / TS optional | +| **Dependency Injection** | Built-in Hierarchical DI Container | None (Requires Context or 3rd party) | `provide` / `inject` (Hierarchical) | +| **Routing** | Official `@angular/router` | Community libraries (`React Router`, `TanStack`) | Official `vue-router` | +| **Form Handling** | Official Reactive & Template Forms | Community libraries (`React Hook Form`, `Formik`) | Built-in `v-model` + community libs | +| **Reactivity Model** | Angular Signals + RxJS streams | Component re-rendering + Hooks (`useState`, `useMemo`) | Vue Reactivity (`ref`, `reactive`, `computed`) | +| **Change Detection** | Granular Signal tracking / OnPush / Zoneless | Virtual DOM reconciliation | Proxy-based dependency tracking | +| **Tooling & CLI** | Angular CLI (`ng`), official schematics, `ng update` | Vite, Next.js, community setups | Vite, Vue CLI | +| **Enterprise Governance** | Exceptional across large multi-team repos | Requires strict internal standards & conventions | Good, flexible middle ground | + +--- + +# Framework vs Library + +A critical conceptual question in technical interviews: + +### What is a Library? +A library is a collection of packaged helper functions and classes that you call to perform specific tasks. **You are in control of the application flow**, and you decide when and where to call the library. +- *Analogy*: A tool in your toolbox (e.g., a wrench). +- *Example*: Lodash, RxJS, Axios. + +### What is a Framework? +A framework defines the architecture, skeleton, and lifecycle of the application. The framework dictates how code must be organized and calls *your* code at the appropriate moments. +- *Analogy*: A prefabricated assembly line. You provide the parts; the assembly line controls execution. +- *Core Principle*: **Inversion of Control (IoC)**, often called the **"Hollywood Principle"**: *"Don't call us; we'll call you."* + +```text +LIBRARY PATTERN: FRAMEWORK PATTERN: +Developer Controls Flow Framework Controls Flow (Inversion of Control) + +Your Application Code Angular Framework Lifecycle + │ │ + ▼ ▼ + Calls Library ──► Axios / Lodash Calls Your Component ──► ngOnInit() + ▲ │ + │ ▼ +Receives Result Calls Your Template ──► Updates DOM +``` + +--- + +# Why Enterprises Choose Angular + +Global enterprise software engineering teams (banking, healthcare, aerospace, telecom) consistently select Angular because it addresses enterprise governance requirements: + +### 1. Architectural Uniformity +When an organization employs hundreds of developers across distributed geographical locations, architectural consistency is vital. Angular defines an unambiguous, standardized location for: +- Routing (`routes.ts`) +- Business logic & API orchestration (`services/`) +- Presentation & UI interaction (`components/`) +- Cross-cutting concerns (`interceptors/`, `guards/`) + +### 2. Predictable Upgrade Paths (`ng update`) +Enterprises cannot afford to rewrite applications every two years when open-source libraries undergo breaking changes. Angular follows a predictable **6-month major release cycle** and provides automated migration schematics: +```bash +ng update @angular/core @angular/cli +``` +These schematics automatically rewrite deprecated TypeScript APIs across thousands of files. + +### 3. Built-In Security +Angular treats frontend security as a first-class framework responsibility: +- **Built-in DOM Sanitization**: Templates automatically sanitize untrusted values before inserting them into innerHTML, attribute bindings, or styles. +- **XSS Mitigation**: Values bound via `{{ interpolation }}` or `[property]` are encoded automatically. +- **Cross-Site Request Forgery (CSRF)**: Built-in `HttpClientXsrfModule` / `withXsrfConfiguration` automatically extracts anti-forgery tokens from cookies and appends them to outgoing HTTP headers. + +### 4. Native Dependency Injection & Testability +Angular's DI container enables seamless unit testing by allowing developers to substitute real HTTP services with mock implementations using `TestBed` or constructor injection. + +### 5. Monorepo & Scalability Support +Angular integrates natively with enterprise monorepo orchestrators like **Nx**, allowing multi-domain enterprise applications to share common UI component libraries, domain services, and linting rules across dozens of applications. + +--- + +# Common Interview Questions + +## Why Angular? + +Angular provides an end-to-end, enterprise-grade architecture that eliminates decision fatigue, enforces TypeScript type safety, and supplies all critical application infrastructure—Dependency Injection, routing, HTTP communication, reactive state, and automated migrations—as a unified, coherent platform maintained by Google. + +--- + +## Angular vs jQuery? + +| Aspect | jQuery | Angular | +| :--- | :--- | :--- | +| **Paradigm** | Imperative DOM manipulation | Declarative, component-driven data binding | +| **State Management** | State stored directly inside DOM elements | State stored in TypeScript classes/signals; DOM is a projection | +| **Architecture** | No defined architecture | Clear separation: Components, Services, Models, Interceptors | +| **Maintainability** | Degrades rapidly as codebase expands | Scales reliably across hundreds of enterprise modules | +| **Testability** | Requires running browser DOM; very hard to mock | Isolated unit tests using DI and test fixtures | + +--- + +## Angular vs React? + +- **Scope**: Angular is a full platform; React is a view library that requires assembling a custom stack from third-party community packages. +- **Architecture**: Angular provides built-in Dependency Injection, official routing, and official forms; React delegates these concerns to community solutions (`React Router`, `React Hook Form`, `Zustand`). +- **Reactivity**: Modern Angular utilizes fine-grained Signals and RxJS, updating only the specific DOM nodes that depend on changed state. React re-executes component render functions and uses a Virtual DOM reconciliation algorithm to calculate mutations. +- **Enterprise Fit**: Angular excels in large corporate environments where strict architectural standardization and automated upgrades are required. React excels in environments prioritizing flexibility, rapid iteration, and specialized custom architectures. + +--- + +## Framework vs Library? + +The fundamental difference is **Inversion of Control (IoC)**: +- With a **Library**, your application code controls the flow and calls library functions whenever needed. +- With a **Framework**, the framework controls the execution flow, establishes the lifecycle, and calls your code into predefined hooks (e.g., Angular invoking lifecycle methods like `ngOnInit` or change detection cycles). + +--- + +## Why Angular for enterprise applications? + +Enterprises prioritize maintainability, security, and developer interchangeability over bleeding-edge experimental APIs. Angular provides: +1. Standardized architecture minimizing onboarding friction. +2. First-class TypeScript type safety preventing runtime defects. +3. Automated migrations via `ng update` ensuring long-term maintainability. +4. Built-in security (DOM sanitization, CSRF token handling). +5. Native Dependency Injection for robust unit testing and decoupled layers. + +--- + +## What are the trade-offs and disadvantages of Angular? + +- **Steep Learning Curve**: Developers must learn TypeScript, Dependency Injection, RxJS observables, reactive signals, decorators, and template syntax. +- **Initial Bundle Size**: Because Angular includes comprehensive framework capabilities, its baseline bundle is historically larger than a bare React or Vue application (though modern standalone components, tree-shaking, and `@defer` have mitigated this significantly). +- **Overkill for Simple Websites**: For a landing page or content brochure, Angular's architecture introduces unnecessary conceptual overhead compared to static site generators or lightweight libraries like Astro or Alpine.js. + +--- + +## How has Modern Angular (v16–v19+) modernized compared to legacy Angular? + +Modern Angular has eliminated historic pain points: +- **Standalone Components**: Replaced verbose `NgModule` boilerplate; components directly import what they need. +- **Angular Signals**: Introduced fine-grained synchronous reactivity (`signal()`, `computed()`, `effect()`), drastically reducing the complexity of UI state management. +- **Built-in Control Flow**: Modern `@if`, `@for (track id)`, and `@switch` replace legacy `*ngIf` and `*ngFor` directives with cleaner syntax and superior type narrowing. +- **Deferrable Views (`@defer`)**: Declarative lazy-loading of heavy template blocks triggered on viewport scroll, idle, or user interaction. +- **Zoneless Change Detection**: Optional removal of `Zone.js`, enabling surgical DOM updates with significantly smaller runtime overhead. + +--- + +# Senior-Level Discussion: The .NET & Clean Architecture Parallel + +For senior engineers and developers with .NET or Java backgrounds, Angular feels natural because it mirrors backend Clean Architecture: + +```text +ASP.NET CORE LAYER ANGULAR ARCHITECTURE LAYER RESPONSIBILITY +───────────────────────────────────────────────────────────────────────────────────────────── +Controller / Minimal API ◄─► Angular Component / View Handles input & presentation +Application Services ◄─► Angular Injectable Service Coordinates business logic +HttpClient / EF Core ◄─► Angular HttpClient Data access & API transport +Dependency Injection (IoC) ◄─► Angular Hierarchical DI Decoupled lifetime management +Action Filters ◄─► Angular HttpInterceptors Cross-cutting concerns (Auth, Log) +Route Middleware ◄─► Angular Route Guards (canMatch) Access control & redirection +DTOs & Entity Models ◄─► TypeScript Interfaces & Types Compile-time contract enforcement +``` + +This structural parity allows full-stack enterprise teams to apply identical architectural principles (SOLID, Dependency Inversion, Single Responsibility, Separation of Concerns) across both frontend and backend codebases. + +--- + +# Architecture Considerations + +When architecting a new application, evaluate these factors: + +```text + DECISION TREE: WHEN TO CHOOSE ANGULAR + │ + Is this an enterprise platform, + multi-team project, or complex SPA? + │ + ┌────────────────┴────────────────┐ + YES NO + │ │ + Do you require built-in DI, Is it a lightweight brochure, + standardized routing, forms, and simple blog, or static site? + long-term automated migrations? │ + │ ▼ + ▼ Consider Astro, Next.js, + CHOOSE ANGULAR or Vanilla HTML/CSS +``` + +### Team Guidelines +- Standardize on **Standalone Components** and eliminate `NgModule` from new development. +- Use **Signals** for synchronous UI state and reserve **RxJS** for asynchronous streaming, WebSocket events, and cancellation. +- Enforce `ChangeDetectionStrategy.OnPush` across all components. +- Never write business logic inside components; delegate to domain services behind interfaces. + +--- + +# High-Level Architectural Diagram + +```text +┌─────────────────────────────────────────────────────────────────────────────────────────┐ +│ ANGULAR PLATFORM │ +├─────────────────────────────────────────────────────────────────────────────────────────┤ +│ │ +│ PRESENTATION LAYER │ +│ ┌───────────────────────────┐ ┌───────────────────────────┐ │ +│ │ Smart (Container) │ │ Dumb (Presentational) │ │ +│ │ Component │◄───────►│ Component │ │ +│ │ - Injects Domain Service │ Events │ - Pure Inputs (Signals) │ │ +│ │ - Manages Route / Page │ Inputs │ - Pure Outputs │ │ +│ └─────────────┬─────────────┘ └───────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ BUSINESS LOGIC & STATE LAYER │ +│ ┌─────────────────────────────────────────────────────────────────┐ │ +│ │ Domain Feature Service │ │ +│ │ - Exposes Read-only Signals & Derived Computations │ │ +│ │ - Encapsulates Business Rules & State Transitions │ │ +│ └─────────────┬───────────────────────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ INFRASTRUCTURE & TRANSPORT LAYER │ +│ ┌───────────────────────────┐ ┌───────────────────────────┐ │ +│ │ Angular HttpClient │────────►│ HTTP Interceptors │ │ +│ │ - Type-safe requests │ │ - JWT Bearer Injection │ │ +│ │ - Response observables │ │ - Global Error Catching │ │ +│ └─────────────┬─────────────┘ └───────────────────────────┘ │ +│ │ │ +└─────────────────┼───────────────────────────────────────────────────────────────────────┘ + │ + ▼ HTTPS (JSON Payloads) +┌─────────────────────────────────────────────────────────────────┐ +│ Enterprise Backend API │ +│ (ASP.NET Core / Java Spring / Node.js) │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +# Key Takeaways + +1. **Complete Platform**: Angular is not a view library; it is a complete, opinionated application framework providing DI, routing, forms, HTTP client, and reactivity out of the box. +2. **Inversion of Control**: Angular controls the lifecycle and execution flow, calling your components and services according to architectural conventions. +3. **Enterprise Governance**: Standardized structure, built-in security, first-class TypeScript, and automated migrations (`ng update`) make Angular the premier choice for large, multi-team engineering organizations. +4. **Modern Renaissance**: Modern Angular (18+/19+) features Standalone components, fine-grained Signals, built-in control flow (`@if`, `@for`), `@defer`, and zoneless change detection. +5. **Architectural Parallels**: Angular mirrors enterprise backend architectures (like ASP.NET Core Clean Architecture), enabling consistent software engineering patterns across the full stack. + +--- + +# Interview Notes (Revision Version) + +## Definition +Angular is a full-featured, opinionated, TypeScript-based framework maintained by Google designed to build maintainable, testable, and scalable enterprise Single Page Applications. + +## Core Pillars +- Standalone Component Tree +- Hierarchical Dependency Injection +- TypeScript & AOT Compilation +- Reactive Architecture (Signals + RxJS) +- Comprehensive Tooling (`ng`, schematics, `ng update`) + +## Top 3 Enterprise Benefits +1. **Architectural Consistency**: Predictable code organization across massive, distributed development teams. +2. **Automated Migrations**: `ng update` handles framework upgrades and API deprecations automatically. +3. **Integrated Security**: Automatic DOM sanitization against XSS and built-in CSRF token handling. + +## Modern vs Legacy Angular +- Legacy: `NgModule`, Zone.js dirty checking, `*ngIf`/`*ngFor`, RxJS required for all local state. +- Modern: Standalone by default, fine-grained Signals, `@if`/`@for`/`@defer`, optional Zoneless, hydration SSR. + + + +
+
+ +--- + +
+ +⬅️ **Previous:** *None (First Topic)*  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [Single Page Application (SPA)](02-spa-concepts.md) + +
+ diff --git a/apps/portal/public/assets/content/01-foundations/02-spa-concepts.md b/apps/portal/public/assets/content/01-foundations/02-spa-concepts.md new file mode 100644 index 0000000..f6db921 --- /dev/null +++ b/apps/portal/public/assets/content/01-foundations/02-spa-concepts.md @@ -0,0 +1,410 @@ +# Single Page Application (SPA) + +## Interview Priority + +**Must Know** + +## Interview Frequency + +**Very Common** + +## Recommended Depth + +**Strong Understanding** + +## Relevant For + +- Mid-Level Interviews +- Senior Developer Interviews +- Lead Developer Interviews +- Architect Discussions + +--- + +# What Is A Single Page Application (SPA)? + +A **Single Page Application (SPA)** is a web application architecture where the browser loads a single HTML shell page (`index.html`) and associated JavaScript/CSS assets once. Subsequent page transitions, user interactions, and view mutations occur dynamically by manipulating the existing Document Object Model (DOM) via client-side JavaScript, rather than requesting full HTML documents from the server. + +Data synchronization between the client and backend occurs asynchronously over background HTTP calls (typically transmitting lightweight JSON payloads via `fetch` or `HttpClient`). + +--- + +# Why Does It Exist? + +Traditional Multi-Page Applications (MPAs) were built for document retrieval. Every user interaction that changed views triggered a full roundtrip to the web server: + +```text +TRADITIONAL MULTI-PAGE APPLICATION (MPA) FLOW: +User clicks link ──► Browser destroys entire page ──► White screen flash ──► +Server queries DB & renders HTML string ──► Browser downloads full HTML, CSS, JS ──► Re-renders everything +``` + +This traditional flow caused severe user experience drawbacks: +- **White Screen Flashes**: Disruptive rendering flicker during navigation. +- **Lost UI State**: Form inputs, scroll positions, and client-side memory were wiped on every navigation. +- **Excessive Network Bandwidth**: Identical headers, navigation bars, scripts, and stylesheets were repeatedly downloaded on every page hit. +- **High Server CPU Load**: Backend web servers spent substantial CPU cycles assembling repetitive HTML markup instead of serving raw data. + +The SPA architecture was introduced to give web applications the **responsiveness, fluid animations, and persistence of native desktop applications**. + +--- + +# Historical Context + +```text +1995–2004 2005–2009 2010–2015 2016–Present +Static & Server Pages ───► AJAX Revolution ───► Early SPA Frameworks ──► Modern Enterprise SPAs +HTML Forms XMLHttpRequest AngularJS, Backbone Angular, React, Vue +Full page reloads Partial DOM updates Hash-based routing (#) HTML5 History API (pushState) +PHP, ASP Classic, JSP Gmail, Google Maps Client-side MVC SSR + Hydration, Signals, @defer +``` + +### 1. The Pre-AJAX Era +Every form submission or link click required a brand-new page load. Web applications felt clunky, slow, and disconnected. + +### 2. The AJAX Breakthrough (2005) +Google Maps and Gmail proved that web browsers could fetch data asynchronously using `XMLHttpRequest` and update slices of the page without destroying the browser document. + +### 3. Early SPAs & Hash Routing +Early frameworks used the URL hash (`http://app.com/#/dashboard`) because modifying the URL path without reloading the browser was not supported by older browsers. + +### 4. Modern HTML5 History API & Universal SPAs +With HTML5 `history.pushState()` and `history.replaceState()`, browsers can manipulate the address bar URL seamlessly without triggering a network request. Modern Angular combines client-side routing with Server-Side Rendering (SSR) and hydration to deliver the best of both worlds. + +--- + +# Traditional MPA Flow vs Modern SPA Flow + +```text +TRADITIONAL MPA (Server-Rendered): +┌───────────┐ ┌───────────┐ ┌───────────┐ +│ Browser │ │ Server │ │ Database │ +└─────┬─────┘ └─────┬─────┘ └─────┬─────┘ + │ GET /employees │ │ + │───────────────────────────────────►│ Query data │ + │ │───────────────────────────────────►│ + │ │◄───────────────────────────────────│ + │ │ Assemble entire HTML document │ + │ 200 OK (Full HTML Document: 120KB)│ │ + │◄───────────────────────────────────│ │ + │ [Entire page destroyed & rebuilt] │ │ + │ │ │ + │ GET /employees/42 (Click link) │ │ + │───────────────────────────────────►│ Query data │ + │ │───────────────────────────────────►│ + │ │◄───────────────────────────────────│ + │ │ Assemble entire HTML document │ + │ 200 OK (Full HTML Document: 125KB)│ │ + │◄───────────────────────────────────│ │ + │ [White flash, re-render DOM] │ │ +``` + +```text +MODERN ANGULAR SPA: +┌───────────┐ ┌───────────┐ ┌───────────┐ +│ Browser │ │ Server │ │ Database │ +└─────┬─────┘ └─────┬─────┘ └─────┬─────┘ + │ GET / │ │ + │───────────────────────────────────►│ Serve index.html & bundles │ + │ 200 OK (index.html + JS/CSS) │ │ + │◄───────────────────────────────────│ │ + │ [Angular bootstraps in memory] │ │ + │ │ │ + │ Navigate to /employees (Client-side) │ + │ Router intercepts click, updates URL via pushState │ + │ │ │ + │ GET /api/employees (JSON only) │ │ + │───────────────────────────────────►│ Execute REST / GraphQL query │ + │ │───────────────────────────────────►│ + │ │◄───────────────────────────────────│ + │ 200 OK (Raw JSON payload: 4KB) │ │ + │◄───────────────────────────────────│ │ + │ [Surgically patch DOM via Signals]│ │ + │ Zero page reload! Instant update! │ │ +``` + +--- + +# First Principles Explanation + +From first principles, an SPA decouples **view rendering** from **data delivery**: + +1. **Separation of Concerns**: The frontend is responsible for layout, UI state, rendering, and interaction logic. The backend is an API that serves structured business data (JSON). +2. **Persistent Application State in Memory**: The JavaScript runtime environment (`window`, global state, caches, open WebSocket connections) remains active throughout the user's entire session. +3. **Bandwidth Efficiency**: Over-the-wire payloads transition from heavy HTML markup strings (containing redundant layout tags) to raw data structures. + +--- + +# Real World Analogy + +Consider the difference between a **Fast-Food Drive-Thru vs Dining at a Buffet Restaurant**: + +```text +MULTI-PAGE APPLICATION (MPA) SINGLE PAGE APPLICATION (SPA) +"Drive-Thru for Every Single Item" "Entering a Modern Buffet" + +1. Drive up: Order Burger 1. Walk into dining room once +2. Wait for bag, drive away 2. Get your plate & silverware (Bootstrapping) +3. Decide you want Fries 3. Walk up to stations as you please +4. Drive back to start of line, re-order 4. Only take the specific food you need (JSON) +5. Entire packaging & bag handed to you again 5. Your table, drink, and seat remain unchanged +``` + +- **MPA**: You leave the building and go through the entire ordering and packaging process every single time you want another item. +- **SPA**: You sit down once. Whenever you need more food, you only fetch the specific dish, keeping your seat, silverware, and conversation completely uninterrupted. + +--- + +# How Angular Solves SPA Challenges + +Building an enterprise SPA from scratch introduces significant engineering challenges. Angular provides comprehensive, built-in solutions: + +### 1. Client-Side Routing (`@angular/router`) +- Intercepts link clicks to prevent the browser from issuing an HTTP request. +- Uses the **HTML5 History API** (`pushState`, `replaceState`, `popstate` event listeners) to keep the browser address bar, back button, and bookmarks in sync. +- Activates and renders components dynamically inside ``. + +### 2. State Management & In-Memory Persistence +- Because the browser window does not refresh, Angular services provided at root (`@Injectable({ providedIn: 'root' })`) act as persistent in-memory singletons. +- Angular Signals and RxJS coordinate state transitions across completely decoupled components without losing state during page navigation. + +### 3. Code Splitting & Lazy Loading +- **Challenge**: Monolithic SPAs can result in massive initial JavaScript bundles that slow down initial page loads. +- **Angular Solution**: + - Route-level lazy loading via `loadComponent: () => import('./feature.component')`. + - Granular template-level lazy loading via **Deferrable Views (`@defer`)**, loading heavy widgets only when scrolled into the viewport or idle. + +### 4. SEO & Initial Load (Angular SSR & Hydration) +- **Challenge**: Search engine crawlers and low-powered mobile devices can struggle with client-rendered empty `` shells. +- **Angular Solution**: + - Built-in Server-Side Rendering (`@angular/ssr`) renders the initial component tree into static HTML on the server. + - **Non-Destructive Hydration**: Modern Angular reuses the server-rendered DOM nodes on the client rather than destroying and re-creating them, eliminating flicker. + - **Event Replay**: Automatically buffers user clicks before JavaScript finishes downloading and replays them once hydrated. + +### 5. Security in SPAs +- **DOM Sanitization**: Angular's `DomSanitizer` automatically inspects and sanitizes untrusted HTML, styles, and URLs to prevent Cross-Site Scripting (XSS). +- **CSRF Token Handling**: Angular's `HttpClient` automatically reads XSRF cookies and attaches the token to outgoing mutable HTTP requests. + +--- + +# SPA vs MPA Architectural Matrix + +| Architectural Vector | Single Page Application (SPA) | Multi-Page Application (MPA) | +| :--- | :--- | :--- | +| **Initial Page Load** | Slower (Downloads JS runtime & core bundles) | Faster (Delivers pre-rendered HTML immediately) | +| **Subsequent Navigation** | Near-instantaneous (Updates DOM dynamically) | Slower (Full page reload, re-parses all HTML/CSS) | +| **User Experience (UX)** | Fluid, desktop-like, smooth transitions | Page flickers on navigation; state lost on reload | +| **Server Resource Usage** | Low (Server only serializes JSON data) | High (Server renders HTML templates repeatedly) | +| **Client Resource Usage** | Higher (Browser executes JS, manages DOM) | Minimal (Browser only displays parsed HTML) | +| **State Persistence** | Trivial (In-memory services, Signals, stores) | Difficult (Requires cookies, sessions, query params) | +| **SEO Complexity** | Requires SSR / Prerendering for optimal indexability | Built-in (Static HTML is easily crawled) | +| **Offline Capability** | Excellent (Easily paired with Service Workers/PWA) | Very limited (Requires server for new pages) | +| **Architectural Complexity**| Higher frontend engineering complexity | Traditional, straightforward server routing | + +--- + +# Common Interview Questions + +## What is a Single Page Application (SPA)? + +An SPA is a web application that loads a single HTML document once and updates the user interface dynamically via client-side JavaScript by manipulating the DOM and fetching raw data asynchronously via APIs, eliminating the need for full browser page reloads. + +--- + +## What are the key differences between an SPA and an MPA? + +- **Rendering Location**: MPAs render full HTML pages on the server for every request. SPAs render views in the browser client using JavaScript after an initial shell load. +- **Navigation Behavior**: MPAs perform full browser refreshes on navigation, destroying client memory. SPAs intercept route changes using the HTML5 History API, preserving application state. +- **Data Exchange**: MPAs exchange HTML markup strings. SPAs exchange lightweight JSON data payloads over HTTP. +- **Performance Profile**: MPAs have faster initial loads but slower subsequent navigation. SPAs have higher initial bundle load costs but near-instant subsequent page transitions. + +--- + +## How does client-side routing work under the hood without triggering a page reload? + +Client-side routing relies on the **HTML5 History API**: +1. **Event Interception**: The router intercepts click events on anchor tags (`
`), preventing the browser's default navigation action (`event.preventDefault()`). +2. **URL Update**: It calls `history.pushState(state, title, url)` to update the browser address bar and history stack without triggering a browser request to the server. +3. **History Navigation**: It listens to the `window.onpopstate` event to detect when the user clicks the browser Back or Forward buttons. +4. **View Resolution**: The router matches the updated URL path against the route configuration table, instantiates the mapped Standalone Component, and inserts it into ``. + +--- + +## What are the main challenges and disadvantages of SPAs? + +- **Initial Load Latency**: Large JavaScript bundles must be downloaded, parsed, and executed before the application becomes interactive (First Contentful Paint & Time to Interactive). +- **SEO Challenges**: Search engine web crawlers that do not execute JavaScript may see only an empty `` tag unless SSR or prerendering is implemented. +- **Client Memory Leaks**: Long-lived single-page sessions can accumulate memory leaks if developers fail to unsubscribe from RxJS observables, detach event listeners, or clean up intervals in `ngOnDestroy`. +- **Browser History & Deep Linking**: Developers must explicitly configure server fallbacks (`try_files $uri $uri/ /index.html;`) so that refreshing a deep route (e.g. `/employees/42`) does not return an HTTP 404 from the web server. + +--- + +## How does Angular solve SPA performance bottlenecks and SEO challenges? + +Angular provides several enterprise mechanisms: +1. **Angular SSR & Non-Destructive Hydration**: Renders views on Node.js server, delivering instant HTML to search engines and users, then seamlessly attaches client reactivity without DOM destruction. +2. **Event Replay**: Automatically captures user clicks during hydration and replays them once the bundle finishes compiling. +3. **Route Lazy Loading (`loadComponent`)**: Splits the application into separate JavaScript chunks, loading features strictly when visited. +4. **Deferrable Views (`@defer`)**: Postpones rendering expensive below-the-fold components until idle or scrolled into view. +5. **OnPush & Signals**: Bypasses deep component tree dirty-checking cycles, providing surgical DOM updates. + +--- + +## When should you choose NOT to build an SPA? + +You should avoid an SPA when: +- The website is purely informational or content-heavy (e.g., blogs, documentation, news publishers, marketing landing pages) where content rarely updates dynamically. +- Instant, zero-JavaScript initial load speed is critical on low-end mobile networks. +- SEO indexing is the primary success metric and the engineering team lacks infrastructure to maintain SSR/prerendering pipelines. +- In these cases, Multi-Page static architectures (e.g., Astro, Hugo, or classic server-rendered templates) are superior. + +--- + +# Senior-Level Discussion: ASP.NET Core & SPA Architecture (.NET Parallels) + +For senior full-stack architects working with .NET, the architectural evolution from Razor/MVC to Angular SPAs is a classic discussion: + +```text +HISTORICAL APPROACH: ASP.NET MVC / RAZOR PAGES +┌─────────────────────────────────────────────────────────────┐ +│ ASP.NET Core Web Server │ +│ Controllers return View(model) ──► Razor Engine (.cshtml) │ +│ Generates raw HTML on server for every request │ +└─────────────────────────────────────────────────────────────┘ + +MODERN ENTERPRISE APPROACH: ANGULAR SPA + ASP.NET CORE WEB API (OR BFF) +┌───────────────────────────┐ ┌──────────────────────────────────────┐ +│ Client Tier (Browser) │ │ API Gateway / Backend-for-Frontend │ +│ Angular Standalone App │ │ ASP.NET Core Web API Minimal APIs │ +│ - Routes in browser │◄───────►│ - JWT Bearer Authentication │ +│ - Manages UI State │ HTTPS │ - EF Core / Microservices / DB │ +│ - Signals & OnPush │ JSON │ - OpenAPI / Swagger Contracts │ +└───────────────────────────┘ └──────────────────────────────────────┘ +``` + +### Key Senior Talking Points: +1. **The Fallback Route Configuration**: In ASP.NET Core, an SPA requires configuring `endpoints.MapFallbackToFile("index.html")` so that any unhandled server route is redirected to the Angular shell, allowing Angular Router to resolve the path on the client. +2. **Backend-For-Frontend (BFF) Pattern**: When security requirements prohibit storing sensitive tokens in browser storage, a lightweight server (BFF) manages HttpOnly session cookies with the Angular SPA and translates them to Bearer tokens for internal microservices. + +--- + +# Architecture Considerations + +```text + ENTERPRISE SPA ARCHITECTURE CHECKLIST + │ + ┌────────────────────────────────┼────────────────────────────────┐ + ▼ ▼ ▼ + ROUTE CHUNKING SEO & FIRST PAINT STATE HYGIENE + - Standalone lazy routes - Angular SSR + Hydration - Injectable root services + - @defer on heavy widgets - Route prerendering (SSG) - DestroyRef / takeUntilDestroyed + - PreloadStrategy for speed - Event Replay enabled - Zero memory leaks in ngOnDestroy +``` + +### Server Routing Requirement (Avoid 404 on Refresh) +In any SPA, when a user refreshes `https://portal.com/dashboard/reports`, the request hits the physical web server. The web server must be configured to return `index.html` instead of throwing a 404: +- **Nginx**: `try_files $uri $uri/ /index.html;` +- **Apache**: RewriteRule `^index\.html$ - [L]` +- **ASP.NET Core**: `app.MapFallbackToFile("index.html");` + +--- + +# SPA Lifecycle & Routing Flow Diagram + +```text +┌───────────────────────────────────────────────────────────────────────────────────┐ +│ INITIAL BROWSER SESSION │ +└────────────────────────────────────────┬──────────────────────────────────────────┘ + │ + ▼ + Download index.html + JS bundle + │ + ▼ + Bootstrap Angular App + │ + ▼ + Angular Router resolves initial URL path + │ + ▼ +┌───────────────────────────────────────────────────────────────────────────────────┐ +│ USER CLICKS NAVIGATION LINK │ +└────────────────────────────────────────┬──────────────────────────────────────────┘ + │ + ▼ + Angular Router intercepts click event + (Calls preventDefault()) + │ + ▼ + HTML5 History: history.pushState(url) + (URL updates with NO page reload) + │ + ▼ + Execute Route Guards (canMatch / canActivate) + │ + ┌──────────────────┴──────────────────┐ + ▼ Allowed ▼ Denied + Execute Resolvers (if any) Redirect (e.g. /login) + │ + ▼ + Is Component already loaded? + │ + ┌─────────┴─────────┐ + ▼ YES ▼ NO + Instantiate Download lazy chunk + Component via dynamic import() + │ │ + └─────────┬─────────┘ + ▼ + Mount Component inside + │ + ▼ + Fetch asynchronous JSON data via HttpClient + │ + ▼ + Update Signals ──► Surgical DOM update! +``` + +--- + +# Key Takeaways + +1. **Core Concept**: An SPA loads a single HTML document once; all subsequent view changes occur dynamically via client-side JavaScript DOM manipulation. +2. **Data vs Markup**: SPAs exchange lightweight JSON data payloads with backend APIs rather than full HTML documents, reducing server load and bandwidth. +3. **Client-Side Routing**: The HTML5 History API (`pushState`, `popstate`) updates the URL and history stack without triggering browser page reloads. +4. **Performance & SEO Solutions**: Modern Angular solves historical SPA trade-offs using Server-Side Rendering (SSR), non-destructive hydration, `@defer`, and route lazy loading. +5. **Server Fallback Rule**: All unmapped server-side routes must fallback to `index.html` to allow the client-side router to handle deep links. + +--- + +# Interview Notes (Revision Version) + +## Definition +A Single Page Application loads a single HTML page once and dynamically rewrites the current page as the user interacts with the app, exchanging JSON data asynchronously via background API calls. + +## SPA vs MPA Summary +- MPA: Server renders HTML per page; full browser refresh on navigation; higher server CPU. +- SPA: Browser renders UI dynamically; client router manages URLs; lower bandwidth; desktop-like UX. + +## Under the Hood Routing +- Uses `history.pushState()` to change the URL without browser reload. +- Listens to `popstate` for Back/Forward navigation. +- `` acts as dynamic viewport placeholder for matched component. + +## Top 3 SPA Challenges & Angular Countermeasures +1. **Initial Bundle Size**: Solved via Standalone lazy loading (`loadComponent`) and `@defer`. +2. **SEO & First Paint**: Solved via `@angular/ssr` and non-destructive hydration. +3. **Memory Leaks**: Solved via `takeUntilDestroyed()`, `DestroyRef`, and clean service scoping. + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [Why Angular](01-why-angular.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [Component-Based Architecture](03-component-based-architecture.md) + +
+ diff --git a/apps/portal/public/assets/content/01-foundations/03-component-based-architecture.md b/apps/portal/public/assets/content/01-foundations/03-component-based-architecture.md new file mode 100644 index 0000000..a199898 --- /dev/null +++ b/apps/portal/public/assets/content/01-foundations/03-component-based-architecture.md @@ -0,0 +1,460 @@ +# Component-Based Architecture + +## Interview Priority + +**Must Know** + +## Interview Frequency + +**Very Common** + +## Recommended Depth + +**Strong Understanding** + +## Relevant For + +- Mid-Level Interviews +- Senior Developer Interviews +- Lead Developer Interviews +- Architect Discussions + +--- + +# What Is Component-Based Architecture? + +Component-Based Architecture is a software design approach where the user interface is composed of smaller, reusable, independent, and self-contained building blocks called **Components**. + +Instead of building an entire user interface as one large, monolithic page, the application is divided into cohesive, decoupled pieces. + +Example: +```text +Enterprise Portal +├── HeaderComponent +├── SearchBarComponent +├── NavigationMenuComponent +├── ProductListComponent +├── ShoppingCartComponent +└── FooterComponent +``` + +Each section encapsulates its own: +1. **Template**: Declares the HTML markup and view structure. +2. **Logic**: Controls state, user interactions, and view logic in TypeScript. +3. **Styles**: Scoped CSS rules applying exclusively to this component. + +--- + +# Why Does It Exist? + +As web applications grew from simple text documents into massive enterprise platforms, maintaining monolithic codebases became unmanageable. + +Problems included: +- **Code Duplication**: Re-writing identical HTML, CSS, and event listeners across dozens of screens. +- **Fragile Maintenance**: Changing a CSS class or JavaScript function in one place unexpectedly broke unrelated sections. +- **Tight Coupling**: Presentation logic was deeply tangled with backend API calls and global browser events. +- **Poor Testability**: Isolating a single feature for automated testing required spinning up entire pages. +- **Team Collaboration Friction**: Multiple engineers working on the same HTML and JavaScript files constantly suffered merge conflicts. + +Component-Based Architecture introduces **modularity and encapsulation** to frontend engineering, applying the same software engineering best practices that backend systems have used for decades. + +--- + +# Historical Context + +```text +1995–2005 2006–2012 2013–Present +Monolithic Pages ───► jQuery Era ───► Component-Based Frameworks +Large index.html DOM manipulation utilities Angular, React, Vue +Global CSS & JS Selectors: $('#btn').click() UI = Tree of Components +Tightly coupled scripts No application architecture Encapsulated State, View & Styles +``` + +### 1. Early Web Applications +Applications were structured around pages (`index.html`, `dashboard.html`). All CSS lived in global stylesheets, and JavaScript existed in sprawling script files. + +### 2. The jQuery Era +jQuery revolutionized DOM selection and animation, but it provided zero architectural guidance. State was stored directly inside DOM elements (e.g. data attributes), and business logic was scattered across hundreds of ad-hoc event listeners. + +### 3. The Component Revolution +Modern frameworks established Components as the foundational building block of frontend engineering. Today, developers design applications as hierarchical trees of reusable UI blocks rather than disconnected HTML documents. + +--- + +# Problem It Solves + +Consider an Employee Directory in an enterprise application: + +### Without Components: +The HTML markup and click handlers for an "Employee Card" are copied across 4 different views: +- Dashboard +- Employee List +- Organization Chart +- Audit Reports + +```text +Dashboard.html ──────► Copies Employee Card HTML (25 lines) +EmployeeList.html ───► Copies Employee Card HTML (25 lines) +OrgChart.html ───────► Copies Employee Card HTML (25 lines) +Reports.html ────────► Copies Employee Card HTML (25 lines) +``` + +If product requirements change (e.g. adding an avatar or department badge), developers must find and edit every copy manually. Inconsistencies and regressions are inevitable. + +### With Components: +```text + +``` +The markup, styling, and behavior are implemented once in `EmployeeCardComponent`. Every view imports and renders the component, guaranteeing 100% UI and behavioral consistency. + +--- + +# First Principles Explanation + +In backend architecture (such as ASP.NET Core or Java Spring), developers never write an entire enterprise system inside a single `Program.cs` or controller. + +Instead, they separate concerns: +```text +Controller ──► Application Service ──► Repository ──► Domain Entity +``` + +Angular applies this exact principle of **decomposition and single responsibility** to the user interface: + +$$\text{Application UI} = \sum_{i=1}^{n} \text{Component}_i$$ + +Each component acts as a bounded context on the screen, managing its own inputs, internal state, and output events. + +--- + +# Real World Analogy + +Think about modern automobile manufacturing on an assembly line: + +```text +Automobile Assembly +├── Engine Block +├── Wheels & Braking System +├── Climate Control Dashboard +├── Ergonomic Seats +└── Passenger Doors +``` + +- Each part has an explicit, engineered responsibility. +- A wheel does not care what brand of engine powers the vehicle, provided the wheel lugs match the axle contract. +- If a headlight bulb fails, you replace the headlight component without rebuilding the car's transmission. + +Angular applications work identically: the application is an assembly of swappable, testable, independent components conforming to strict interface contracts. + +--- + +# Angular's Component Model + +In Angular, a Component is a TypeScript class decorated with `@Component`: + +```typescript +@Component({ + selector: 'app-employee-card', + standalone: true, + templateUrl: './employee-card.component.html', + styleUrl: './employee-card.component.css', + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class EmployeeCardComponent { + // Controller logic, state, inputs, and outputs +} +``` + +A component encompasses four key facets: +1. **Selector**: The custom HTML tag name (e.g., ``) used to place this component in templates. +2. **Template**: The declarative HTML structure defining what renders on screen. +3. **Styles**: CSS/SCSS encapsulated to the component via Angular's View Encapsulation. +4. **Class & Metadata**: TypeScript class holding reactive state, signals, and interaction handlers. + +--- + +# The Component Tree + +Every Angular application is structured as a hierarchical **Component Tree** with `AppComponent` as the root: + +```text +┌─────────────────────────────────────────────────────────────────┐ +│ AppComponent │ +└────────────────────────────────┬────────────────────────────────┘ + │ + ┌────────────────────────┴────────────────────────┐ + ▼ ▼ +┌───────────────────────┐ ┌───────────────────────┐ +│ AppHeaderComponent │ │ MainLayoutComponent │ +└───────────────────────┘ └───────────┬───────────┘ + │ + ┌───────────────────────────┴───────────────────────────┐ + ▼ ▼ + ┌───────────────────────┐ ┌───────────────────────┐ + │ SidebarComponent │ │ DashboardComponent │ (Smart) + └───────────────────────┘ └───────────┬───────────┘ + │ + ┌───────────────────────────────────────┴───────────────────┐ + ▼ ▼ + ┌───────────────────────┐ ┌───────────────────────┐ + │ EmployeeListComponent │ (Dumb) │ SummaryStatsComponent│ (Dumb) + └───────────┬───────────┘ └───────────────────────┘ + │ + ┌───────────┴───────────┐ + ▼ ▼ + ┌───────────────────────┐ ┌───────────────────────┐ + │ EmployeeCardComponent │ │ EmployeeCardComponent │ + └───────────────────────┘ └───────────────────────┘ +``` + +This tree hierarchy dictates: +- **Change Detection Propagation**: Traversal order for checking updates. +- **Dependency Injection Resolution**: Hierarchical resolution of scoped services. +- **DOM Rendering Hierarchy**: Structural containment in the rendered browser document. + +--- + +# Parent and Child Components: Unidirectional Data Flow + +A fundamental tenet of modern component architecture is **Unidirectional Data Flow**: + +$$\text{Data Flows Down via Inputs} \quad \big\Downarrow \quad \text{Events Flow Up via Outputs}$$ + +```text +┌────────────────────────────────────────────────────────┐ +│ Smart / Parent Component │ +│ (Fetches Data, Owns State & Business Logic) │ +└──────────────────────────┬─────────────────────────────┘ + │ + │ [input()] Property Binding (Data Down) + ▼ +┌────────────────────────────────────────────────────────┐ +│ Dumb / Child Component │ +│ (Pure Presentation, Zero External Dependencies) │ +└──────────────────────────┬─────────────────────────────┘ + │ + │ (output()) Event Binding (Events Up) + ▼ +┌────────────────────────────────────────────────────────┐ +│ Parent Handles Event │ +│ (Updates Signal State, Triggers Service Method) │ +└────────────────────────────────────────────────────────┘ +``` + +1. **Inputs (`input()`)**: The parent passes immutable data properties down to the child. +2. **Outputs (`output()`)**: When the user triggers an action in the child, the child emits an event upwards. The child never directly modifies the parent's state. + +--- + +# Smart (Container) vs Dumb (Presentational) Components + +In enterprise Angular systems, components are strictly split into two patterns: + +| Architectural Dimension | Smart (Container) Components | Dumb (Presentational) Components | +| :--- | :--- | :--- | +| **Primary Responsibility** | Coordinates features, manages routing & state | Renders UI markup, formats data, captures events | +| **Dependency Injection** | Injects domain services, state stores, router | Zero service injections; completely decoupled | +| **Data Ingestion** | Reads from services, APIs, signals, route params | Receives data strictly via `input()` or `@Input()` | +| **Event Emission** | Dispatches actions, mutates state, calls APIs | Emits events strictly via `output()` or `@Output()` | +| **Reusability** | Feature-specific, rarely reused across features | Highly reusable across multiple pages & design systems | +| **Change Detection** | `ChangeDetectionStrategy.OnPush` | `ChangeDetectionStrategy.OnPush` | +| **Testability** | Tested with mock services & injected stores | Unit tested purely via inputs, outputs, and DOM asserts | + +--- + +# Code Example: Modern Angular (Standalone & Signals) + +### 1. Dumb (Presentational) Component +```typescript +import { Component, ChangeDetectionStrategy, input, output } from '@angular/core'; + +export interface Employee { + id: number; + name: string; + role: string; +} + +@Component({ + selector: 'app-employee-card', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + template: ` +
+
+

{{ employee().name }}

+ {{ employee().role }} +
+ +
+ `, + styles: [` + .card { border: 1px solid #e2e8f0; padding: 1.25rem; border-radius: 8px; } + .badge { font-size: 0.85rem; color: #4a5568; background: #edf2f7; padding: 2px 8px; border-radius: 4px; } + `] +}) +export class EmployeeCardComponent { + // Required signal input + readonly employee = input.required(); + + // Standard output event + readonly select = output(); +} +``` + +### 2. Smart (Container) Component +```typescript +import { Component, ChangeDetectionStrategy, inject } from '@angular/core'; +import { EmployeeService } from './employee.service'; +import { EmployeeCardComponent } from './employee-card.component'; + +@Component({ + selector: 'app-employee-dashboard', + standalone: true, + imports: [EmployeeCardComponent], + changeDetection: ChangeDetectionStrategy.OnPush, + template: ` +
+

Active Team Members ({{ employees().length }})

+ +
+ @for (emp of employees(); track emp.id) { + + } @empty { +

No team members found.

+ } +
+
+ ` +}) +export class EmployeeDashboardComponent { + private readonly employeeService = inject(EmployeeService); + + // Read-only signal state from service + readonly employees = this.employeeService.employees; + + onEmployeeSelected(employeeId: number): void { + this.employeeService.selectEmployee(employeeId); + } +} +``` + +--- + +# Common Architectural Mistakes + +1. **God Components**: Creating monolithic components exceeding 500 lines containing API calls, business logic, form validation, and complex markup. +2. **Mutating Inputs in Children**: Modifying an object passed via input directly in the child, violating unidirectional data flow and causing subtle change detection bugs. +3. **Prop Drilling**: Passing data through 5 layers of intermediate components that don't need it. (Remedy: Use a feature service or state store). +4. **Direct DOM Manipulation**: Bypassing Angular by using `document.querySelector` or raw DOM mutations instead of template bindings, template reference variables (`#ref`), or `ElementRef`. +5. **Omitting OnPush**: Leaving components on the `Default` change detection strategy, forcing Angular to check every single component on every asynchronous event. + +--- + +# Common Interview Questions + +## What is Component-Based Architecture? + +Component-Based Architecture is a software design methodology where user interfaces are constructed from independent, modular, and reusable units (components) that encapsulate their own view template, TypeScript logic, and scoped styles. + +--- + +## What is the difference between Smart and Dumb components? + +Smart (Container) components coordinate features, inject business services, manage route parameters, and orchestrate application state. Dumb (Presentational) components are visual; they receive data exclusively through inputs, emit user interactions through outputs, inject zero domain services, and are 100% deterministic and reusable. + +--- + +## How do components communicate in Angular? + +1. **Parent-to-Child**: Property binding via `input()` or `@Input()`. +2. **Child-to-Parent**: Custom event binding via `output()` or `@Output()` event emitters. +3. **Parent-to-Child Direct Access**: Using `viewChild()` or `@ViewChild()` to query a child's public API. +4. **Decoupled / Sibling Communication**: Shared `@Injectable` services exposing Angular Signals, RxJS `BehaviorSubject`, or NgRx state stores. + +--- + +## What is the Component Tree and how does change detection relate to it? + +The Component Tree is the hierarchy of components starting from the root `AppComponent`. Angular traverses this tree from top to bottom during change detection. By using `ChangeDetectionStrategy.OnPush`, Angular skips entire subtrees whose input references have not changed, maximizing runtime rendering efficiency. + +--- + +## Why did Angular introduce Signal-based component inputs (`input()`) and outputs (`output()`)? + +Signal-based inputs produce read-only signals that integrate seamlessly with Angular's fine-grained reactivity system (`computed()`, `effect()`). They guarantee compile-time type safety (via `input.required()`), eliminate boilerplate `ngOnChanges` hooks, and facilitate zoneless, fine-grained DOM updates. + +--- + +## How does View Encapsulation work in Angular components? + +Angular provides three View Encapsulation modes: +1. **Emulated (Default)**: Scopes styles to the component by appending unique HTML attributes (e.g. `_ngcontent-c42`) to elements, simulating Shadow DOM. +2. **ShadowDom**: Uses the browser's native Shadow DOM API to achieve true DOM and CSS isolation. +3. **None**: Strips all scoping; component styles are injected globally into the document head. + +--- + +# Senior-Level Discussion: Backend Parallels & Micro-Frontends + +In modern enterprise architectures, components represent **bounded contexts on the UI**: +- **Blazor & ASP.NET Parallels**: In .NET, Blazor components (`[Parameter]`, `EventCallback`) operate on identical principles to Angular components (`input()`, `output()`). +- **Micro-Frontend Slicing**: When architecting large enterprise micro-frontends (using Module Federation), components serve as the atomic unit of federation. Container apps import federated remote components across independent release pipelines. + +--- + +# Architecture Considerations + +```text + COMPONENT DESIGN CHECKLIST + │ + ┌────────────────────────────┼────────────────────────────┐ + ▼ ▼ ▼ +SINGLE RESPONSIBILITY CLEAN BOUNDARIES ONPUSH BY DEFAULT +- Does this component - Inputs strictly for data - Always enforce OnPush + do exactly ONE thing? - Outputs strictly for event - Use Signals for local state +- Split if > 250 lines - Zero HTTP in dumb comps - Inject services in containers +``` + +--- + +# Key Takeaways + +1. **Atomic Building Block**: Components encapsulate template, styles, and TypeScript controller logic into reusable units. +2. **Unidirectional Data Flow**: Data flows down via `input()`, and user events flow up via `output()`. +3. **Smart vs Dumb Separation**: Decoupling business logic (containers) from visual presentation (dumb components) dramatically improves testability and reusability. +4. **Component Hierarchy**: Applications are structured as trees originating from `AppComponent`, driving change detection and dependency injection scoping. +5. **Modern Signal APIs**: Angular's `input()` and `output()` APIs replace legacy `@Input()`/`@Output()` decorators with compile-safe, fine-grained reactive primitives. + +--- + +# Interview Notes (Revision Version) + +## Definition +Component-Based Architecture divides the user interface into independent, reusable, self-contained building blocks combining HTML templates, TypeScript logic, and scoped styles. + +## Core Rules +- Unidirectional Data Flow: Data In (`input()`), Events Out (`output()`). +- Smart Components handle business logic, services, and routing. +- Dumb Components are pure, deterministic, and handle only presentation. +- Always use `ChangeDetectionStrategy.OnPush` for optimal performance. + +## Common Interview Questions +- What is the difference between Smart and Dumb components? +- How do decoupled components communicate? (Shared service with Signals/RxJS) +- Why use Signal inputs over `@Input`? (Compile-time safety, reactive integration, zoneless compatibility). + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [Single Page Application (SPA)](02-spa-concepts.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [Angular Architecture Overview](04-angular-architecture-overview.md) + +
+ diff --git a/apps/portal/public/assets/content/01-foundations/04-angular-architecture-overview.md b/apps/portal/public/assets/content/01-foundations/04-angular-architecture-overview.md new file mode 100644 index 0000000..e740245 --- /dev/null +++ b/apps/portal/public/assets/content/01-foundations/04-angular-architecture-overview.md @@ -0,0 +1,844 @@ +# Angular Architecture Overview + +## Interview Priority + +**Must Know** + +## Interview Frequency + +**Very Common** + +## Recommended Depth + +**Strong Understanding** + +## Relevant For + +- Mid-Level Interviews +- Senior Developer Interviews +- Lead Developer Interviews +- Architect Discussions + +--- + +# What Is Angular Architecture? + +Angular Architecture refers to the overall structure and organization of an Angular application. + +Angular is not just a UI library. + +It is a complete framework that provides: + +- Component-Based UI Development +- Dependency Injection +- Routing +- HTTP Communication +- Forms +- State Management +- Performance Optimization Features + +The goal is to help developers build large, maintainable, scalable, and testable Single Page Applications (SPAs). + +--- + +# Why Does Angular Have a Defined Architecture? + +Imagine developing an enterprise application with: + +- 200 Screens +- 500 Components +- 100+ APIs +- Multiple Development Teams + +Without a clear architecture: + +- Business logic becomes scattered +- Code duplication increases +- Testing becomes difficult +- Maintenance becomes expensive + +Angular provides a structured approach that enforces separation of concerns. + +--- + +# First Principles + +Think about a typical ASP.NET Core application. + +```text +Controller + ↓ +Service + ↓ +Repository + ↓ +Database +``` + +Angular follows a similar layered approach. + +```text +Component + ↓ +Service + ↓ +HttpClient + ↓ +Backend API + ↓ +Database +``` + +Understanding this flow explains most Angular applications. + +--- + +# High-Level Architecture Diagram + +```text +User + │ + ▼ +Template (HTML) + │ + ▼ +Component + │ + ▼ +Service + │ + ▼ +HttpClient + │ + ▼ +Backend API + │ + ▼ +Database +``` + +Supporting Features: + +```text +Routing +Dependency Injection +Forms +Signals +RxJS +Guards +Interceptors +Change Detection +``` + +--- + +# Core Building Blocks + +## Components + +Components are the fundamental building blocks of Angular. + +Responsibilities: + +- Display UI +- Handle user interactions +- Coordinate with services + +Examples: + +```text +LoginComponent +DashboardComponent +EmployeeListComponent +EmployeeDetailsComponent +``` + +Think of Components as the Presentation Layer. + +--- + +### Component Responsibilities + +Good Components: + +- Display data +- Capture user actions +- Call services +- Update UI + +Bad Components: + +- Complex business rules +- Direct database access +- Excessive API logic +- Heavy processing + +Such logic should move into Services. + +--- + +## Templates + +Templates define how the UI looks. + +Example: + +```html +

Employees

+ +``` + +Templates are responsible for: + +- Rendering data +- Binding events +- Displaying content + +Relationship: + +```text +Component + ↓ +Template +``` + +The Component supplies data. + +The Template displays it. + +--- + +## Services + +Services contain reusable business logic. + +Examples: + +```text +EmployeeService +AuthService +ReportService +NotificationService +``` + +Services help keep Components clean and focused. + +--- + +### Why Services Exist + +Without services: + +```text +Component + ├── UI Logic + ├── API Calls + ├── Validation + ├── Calculations + ├── Caching + └── Business Rules +``` + +Components become difficult to maintain. + +Angular promotes: + +```text +Component + ↓ +Service +``` + +This creates better separation of concerns. + +--- + +## Dependency Injection (DI) + +Once Services exist, Components need access to them. + +Bad approach: + +```typescript +const service = new EmployeeService(); +``` + +Problems: + +- Tight coupling +- Hard to test +- Difficult to replace implementations + +Angular uses Dependency Injection. + +Example: + +```typescript +constructor( + private employeeService: EmployeeService +) {} +``` + +Angular automatically provides dependencies. + +--- + +### ASP.NET Core Comparison + +```csharp +builder.Services.AddScoped(); +``` + +Very similar concept. + +Angular's Injector acts as the DI container. + +--- + +## HttpClient + +Angular applications often communicate with APIs. + +Examples: + +```text +Get Employees +Create Employee +Update Employee +Delete Employee +``` + +Angular provides HttpClient for this purpose. + +Flow: + +```text +Component + ↓ +Service + ↓ +HttpClient + ↓ +REST API +``` + +HttpClient supports: + +- GET +- POST +- PUT +- DELETE +- PATCH + +--- + +## Routing + +Angular applications are SPAs. + +Navigation should happen without page refresh. + +Routing connects URLs to Components. + +Example: + +```text +/employees +``` + +Displays: + +```text +EmployeeListComponent +``` + +--- + +```text +/employees/1 +``` + +Displays: + +```text +EmployeeDetailsComponent +``` + +--- + +High-Level Routing Flow: + +```text +URL + ↓ +Router + ↓ +Component +``` + +--- + +## Forms + +Most applications collect user input. + +Examples: + +```text +Login +Registration +Search +Employee Creation +``` + +Angular supports: + +### Template-Driven Forms + +Simpler approach. + +Best for smaller forms. + +### Reactive Forms + +More scalable. + +Commonly used in enterprise applications. + +Features: + +- Validation +- Dynamic Forms +- Testability + +--- + +## State Management + +Applications maintain state. + +Examples: + +```text +Logged-In User +Shopping Cart +Theme +Selected Employee +Filters +``` + +State can be stored in: + +```text +Component State + +Service State + +Signals + +RxJS + +NgRx +``` + +State management becomes increasingly important as applications grow. + +--- + +# Request Lifecycle Example + +Consider: + +```text +Open Employee Details Screen +``` + +Complete flow: + +```text +User Clicks Employee + + ↓ + +EmployeeComponent + + ↓ + +EmployeeService + + ↓ + +HttpClient + + ↓ + +ASP.NET Core API + + ↓ + +Database +``` + +Response: + +```text +Database + + ↓ + +API + + ↓ + +HttpClient + + ↓ + +EmployeeService + + ↓ + +EmployeeComponent + + ↓ + +Template Updates + + ↓ + +User Sees Data +``` + +This is one of the most important Angular diagrams to remember. + +--- + +# Modern Angular Architecture + +Earlier Angular versions relied heavily on: + +```text +NgModules +RxJS +Traditional Control Flow +``` + +Modern Angular increasingly emphasizes: + +```text +Standalone Components +Signals +Improved Template Syntax +Lazy Loading +Performance Improvements +``` + +Examples: + +```text +@if +@for +Signal-Based Reactivity +Standalone Components +``` + +Interviewers increasingly expect awareness of these newer concepts. + +--- + +# How Future Topics Fit Into Architecture + +```text +Angular Application +│ +├── Components +│ +├── Templates +│ ├── Data Binding +│ └── Directives +│ +├── Services +│ +├── Dependency Injection +│ +├── HttpClient +│ +├── Routing +│ ├── Guards +│ └── Lazy Loading +│ +├── Forms +│ +├── State +│ ├── Signals +│ ├── RxJS +│ └── NgRx +│ +└── Performance + ├── Change Detection + ├── OnPush + └── Optimization Techniques +``` + +This roadmap represents how Angular concepts connect together. + +--- + +# Common Interview Questions + +## What are the main building blocks of Angular? + +- Components +- Templates +- Services +- Dependency Injection +- Routing +- HttpClient +- Forms +- State Management + +--- + +## What is the role of a Component? + +Responsible for presentation and handling user interactions. + +--- + +## Why do Services exist? + +To move business and data-access logic out of Components. + +--- + +## Why does Angular use Dependency Injection? + +To reduce coupling and improve testability and maintainability. + +--- + +## How does Angular communicate with a backend? + +Using HttpClient. + +--- + +## How does Angular navigate without page refresh? + +Using Angular Router. + +--- + +## What is the typical Angular request flow? + +```text +User +→ Component +→ Service +→ API +→ Database + +Database +→ API +→ Service +→ Component +→ User +``` + +--- + +# Senior-Level Discussion + +A senior engineer should understand Angular as a layered architecture. + +```text +Presentation Layer + ↓ +Business Logic Layer + ↓ +Data Access Layer +``` + +Mapping: + +```text +Presentation Layer +→ Components + Templates + +Business Layer +→ Services + +Infrastructure Layer +→ HttpClient, Interceptors + +Navigation Layer +→ Routing + +Cross-Cutting Concerns +→ DI, Guards, Error Handling +``` + +This separation improves: + +- Scalability +- Maintainability +- Testability +- Team Productivity + +--- + +# Architecture Considerations + +When designing Angular applications: + +Ask: + +1. Does this belong in a Component or Service? +2. Can this be reused? +3. Is this business logic or UI logic? +4. Is state managed in the right location? +5. Can this be easily tested? + +Good Angular architecture prioritizes: + +- Separation of concerns +- Reusability +- Predictability +- Maintainability + +--- + +# Complete Architecture Diagram + +```text +┌───────────────────┐ +│ User │ +└─────────┬─────────┘ + │ + ▼ +┌───────────────────┐ +│ Template │ +└─────────┬─────────┘ + │ + ▼ +┌───────────────────┐ +│ Component │ +└─────────┬─────────┘ + │ + ▼ +┌───────────────────┐ +│ Service │ +└─────────┬─────────┘ + │ + ▼ +┌───────────────────┐ +│ HttpClient │ +└─────────┬─────────┘ + │ + ▼ +┌───────────────────┐ +│ Backend API │ +└─────────┬─────────┘ + │ + ▼ +┌───────────────────┐ +│ Database │ +└───────────────────┘ + +Supporting Layers: + +Routing +Dependency Injection +Forms +Signals +RxJS +Interceptors +Guards +Change Detection +``` + +--- + +# Key Takeaways + +1. Angular is a complete application framework. +2. Components represent the UI layer. +3. Templates define presentation. +4. Services contain business logic. +5. Dependency Injection manages dependencies. +6. HttpClient communicates with APIs. +7. Routing enables SPA navigation. +8. Forms handle user input. +9. Signals, RxJS, and NgRx help manage state. +10. Most Angular applications follow: + +```text +User +→ Component +→ Service +→ API +→ Database +``` + +--- + +# Interview Notes (Revision Version) + +## Main Building Blocks + +- Components +- Templates +- Services +- Dependency Injection +- Routing +- HttpClient +- Forms +- State Management + +## Typical Flow + +```text +User +→ Component +→ Service +→ API +→ Database +``` + +## Responsibilities + +Components: +- UI +- User interactions + +Services: +- Business logic +- API calls + +HttpClient: +- Backend communication + +Router: +- Navigation + +DI: +- Dependency management + +## Key Message + +Angular follows a layered architecture that separates UI, business logic, navigation, and infrastructure concerns, making enterprise applications easier to maintain and scale. + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [Component-Based Architecture](03-component-based-architecture.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [TypeScript Essentials for Angular](05-typescript-essentials-for-angular.md) + +
+ diff --git a/apps/portal/public/assets/content/01-foundations/05-typescript-essentials-for-angular.md b/apps/portal/public/assets/content/01-foundations/05-typescript-essentials-for-angular.md new file mode 100644 index 0000000..338801c --- /dev/null +++ b/apps/portal/public/assets/content/01-foundations/05-typescript-essentials-for-angular.md @@ -0,0 +1,1091 @@ +# TypeScript Essentials for Angular + +## Interview Priority + +**Must Know** + +## Interview Frequency + +**Very Common** + +## Recommended Depth + +**Strong Understanding** + +## Relevant For + +- Mid-Level Interviews +- Senior Developer Interviews +- Lead Developer Interviews + +--- + +# What Is TypeScript? + +TypeScript is a superset of JavaScript that adds features such as: + +- Static Typing +- Interfaces +- Access Modifiers +- Generics +- Decorators +- Better Tooling Support + +TypeScript code is compiled into JavaScript before running in the browser. + +--- + +# Why Angular Chose TypeScript + +Angular targets large enterprise applications. + +Google needed a solution that provided: + +- Type Safety +- Scalability +- Maintainability +- Refactoring Support +- Better Tooling +- Dependency Injection Support +- Metadata Support + +TypeScript provides all these capabilities. + +--- + +# First Principles + +Imagine a large Angular application: + +```text +500 Components +200 Services +100 APIs +50 Developers +``` + +With plain JavaScript: + +```javascript +function saveUser(user) { +} +``` + +Questions: + +```text +What is user? +Customer? +Employee? +Admin? +String? +Object? +``` + +Nobody knows. + +TypeScript introduces contracts and type information. + +Example: + +```typescript +function saveUser(user: User) { +} +``` + +Now: + +- Developers know the expected data +- IDE understands the contract +- Compiler can validate usage + +--- + +# Why TypeScript Matters in Angular + +Angular heavily relies on TypeScript for: + +```text +Components +Services +Dependency Injection +Routing +Decorators +Metadata +HTTP Responses +Observables +``` + +Most Angular classes are simply TypeScript classes enhanced with Angular features. + +--- + +# Angular-Relevant TypeScript Features + +This chapter focuses only on TypeScript features Angular heavily depends on. + +```text +Classes +Interfaces +Types +Access Modifiers +Generics +Decorators +Metadata +Dependency Injection Support +``` + +--- + +# 1. Classes + +## What Is a Class? + +A class is a blueprint for creating objects. + +A class typically contains: + +```text +Properties +Methods +Behavior +``` + +Example: + +```typescript +export class Employee { + id: number = 0; + name: string = ''; + + save(): void { + console.log('Employee Saved'); + } +} +``` + +--- + +## Why Classes Matter in Angular + +Angular is built around classes. + +Examples: + +```typescript +@Component(...) +export class EmployeeComponent {} +``` + +```typescript +@Injectable() +export class EmployeeService {} +``` + +```typescript +export class Employee {} +``` + +Components and Services are fundamentally TypeScript classes. + +--- + +## Angular Perspective + +Angular does not create a new programming model. + +Instead: + +```text +TypeScript Class ++ +Angular Metadata += +Angular Component / Service +``` + +--- + +# 2. Interfaces + +## What Is an Interface? + +An Interface defines a contract. + +It describes: + +```text +What should exist +``` + +but not: + +```text +How it works +``` + +Example: + +```typescript +export interface Employee { + id: number; + name: string; +} +``` + +--- + +## Why Interfaces Exist + +Suppose an API returns: + +```json +{ + "id": 1, + "name": "Adarsh" +} +``` + +We can describe that structure: + +```typescript +interface Employee { + id: number; + name: string; +} +``` + +Now all developers understand the shape of the object. + +--- + +## Angular Usage + +Interfaces are extremely common for: + +```text +API Responses +Request Models +DTOs +Configuration Objects +View Models +``` + +Example: + +```typescript +getEmployee(): Observable +``` + +--- + +# Interface vs Class + +## Interface + +```typescript +interface Employee { + id: number; +} +``` + +Contains: + +```text +Contract Only +``` + +--- + +## Class + +```typescript +class Employee { + id = 0; + + save() {} +} +``` + +Contains: + +```text +Data +Behavior +Implementation +``` + +--- + +# 3. Types + +## What Is a Type? + +Types provide another way to define data structures. + +Example: + +```typescript +type EmployeeRole = + | 'Admin' + | 'Manager' + | 'Employee'; +``` + +--- + +## Why Useful? + +Provides stricter control over values. + +Example: + +```typescript +role = 'Admin'; +``` + +Valid. + +--- + +```typescript +role = 'SuperAdmin'; +``` + +Invalid. + +Compiler error. + +--- + +## Angular Usage + +Types are commonly used for: + +```text +Union Types +State Definitions +API Models +Configuration Objects +``` + +--- + +# Interface vs Type + +## Interface + +Typically preferred for: + +```text +Object Contracts +``` + +Example: + +```typescript +interface Employee { + id: number; +} +``` + +--- + +## Type + +More flexible. + +Supports: + +```typescript +type Status = + 'Active' + | 'Inactive'; +``` + +and: + +```typescript +type EmployeeId = number; +``` + +Both are commonly used in Angular projects. + +--- + +# 4. Access Modifiers + +Access Modifiers control visibility. + +Angular uses these heavily. + +--- + +## Public + +Accessible from everywhere. + +```typescript +public name = 'Adarsh'; +``` + +Often optional because public is the default. + +--- + +## Private + +Accessible only inside the class. + +```typescript +private apiUrl = '/api/employees'; +``` + +--- + +### Angular Example + +```typescript +constructor( + private employeeService: EmployeeService +) {} +``` + +Common Angular pattern. + +--- + +## Protected + +Accessible inside: + +```text +Class +Derived Classes +``` + +Example: + +```typescript +protected loadData() {} +``` + +Less common but useful for inheritance scenarios. + +--- + +## Readonly + +Value cannot be modified after initialization. + +Example: + +```typescript +readonly apiUrl = '/api/employees'; +``` + +--- + +# Why Access Modifiers Matter + +Benefits: + +```text +Encapsulation +Code Safety +Clear Intent +Better Maintainability +``` + +--- + +# 5. Generics + +## What Problem Do Generics Solve? + +Without Generics: + +```typescript +function getData(): any +``` + +Return type is unknown. + +--- + +With Generics: + +```typescript +function getData() +``` + +The caller decides the type. + +--- + +# Angular Examples + +## HttpClient + +```typescript +http.get() +``` + +Angular now understands: + +```text +Response = Employee +``` + +--- + +## Observable + +```typescript +Observable +``` + +--- + +```typescript +Observable +``` + +--- + +# Why Angular Uses Generics + +Generics provide: + +```text +Strong Typing +Compile-Time Validation +Better Tooling +Safer Refactoring +``` + +They are used extensively throughout Angular APIs. + +--- + +# 6. Decorators + +Decorators are one of Angular's most important TypeScript features. + +--- + +# Problem + +Angular sees: + +```typescript +export class EmployeeComponent {} +``` + +How does Angular know: + +```text +Component? +Service? +Directive? +Pipe? +``` + +It cannot know. + +--- + +# Solution + +Decorators. + +Example: + +```typescript +@Component({ + selector: 'app-employee' +}) +export class EmployeeComponent {} +``` + +Decorator supplies additional information. + +--- + +# Common Angular Decorators + +## Component + +```typescript +@Component() +``` + +Marks a class as a Component. + +--- + +## Injectable + +```typescript +@Injectable() +``` + +Marks a class as a Service. + +--- + +## Input + +```typescript +@Input() +``` + +Receives data from Parent Components. + +--- + +## Output + +```typescript +@Output() +``` + +Sends events to Parent Components. + +--- + +## ViewChild + +```typescript +@ViewChild() +``` + +Accesses child elements or Components. + +--- + +# Why Decorators Matter + +Decorators allow Angular to attach framework behavior to TypeScript classes. + +Without Decorators, Angular would not know how classes should participate in the framework. + +--- + +# 7. Metadata + +## What Is Metadata? + +Metadata means: + +```text +Data About Data +``` + +or + +```text +Information About a Class +``` + +--- + +# Example + +```typescript +@Component({ + selector: 'app-user', + templateUrl: './user.component.html' +}) +``` + +This configuration is Metadata. + +--- + +# What Metadata Tells Angular + +Examples: + +```text +Component Selector +Template +Styles +Providers +Dependencies +Routing Information +``` + +Angular uses this metadata during compilation and runtime. + +--- + +# Why Metadata Is Important + +Without Metadata Angular would not know: + +```text +Which classes are Components +Which classes are Services +Which template belongs to which Component +``` + +Metadata drives Angular's behavior. + +--- + +# 8. Dependency Injection Support + +One of the strongest reasons Angular benefits from TypeScript. + +--- + +# Example + +```typescript +constructor( + private employeeService: EmployeeService +) {} +``` + +Question: + +How does Angular know what to inject? + +--- + +Angular sees: + +```typescript +EmployeeService +``` + +and uses type information plus framework metadata to resolve dependencies. + +--- + +# Dependency Injection Flow + +```text +Component + + │ + + ▼ + +Constructor Parameter + + │ + + ▼ + +EmployeeService + + │ + + ▼ + +Angular Injector + + │ + + ▼ + +Instance Supplied +``` + +--- + +# ASP.NET Core Comparison + +ASP.NET Core: + +```csharp +public EmployeeController( + IEmployeeService service) +{ +} +``` + +Angular: + +```typescript +constructor( + private service: EmployeeService +) +{} +``` + +Conceptually the same pattern. + +--- + +# How These Concepts Fit Together + +```text +TypeScript Class + ↓ + +Decorator + ↓ + +Metadata + ↓ + +Angular Understands Class + + ↓ + +Dependency Injection + + ↓ + +Templates + + ↓ + +Routing + + ↓ + +Application Executes +``` + +Most Angular features build upon these TypeScript foundations. + +--- + +# Modern Angular (16+) + +Modern Angular still heavily relies on: + +```text +Classes +Interfaces +Types +Generics +Decorators +Metadata +Dependency Injection +``` + +Additionally introduces: + +```typescript +signal() +computed() +effect() +``` + +for reactive state management. + +--- + +# Legacy Angular Approach + +The same TypeScript concepts were used before Angular 16. + +Differences mainly involved: + +```text +NgModules +Older Template Syntax +RxJS-Heavy Patterns +``` + +The TypeScript foundations remain largely unchanged. + +--- + +# Why Angular Did Not Move Away From Classes + +Many modern frameworks increasingly favor functions. + +Angular continues to use classes because they work naturally with: + +```text +Dependency Injection +Decorators +Metadata +Object-Oriented Design +Enterprise Development +``` + +This aligns with Angular's enterprise focus. + +--- + +# Common Interview Questions + +## Why does Angular use TypeScript? + +To provide: + +- Type Safety +- Better Tooling +- Refactoring Support +- Dependency Injection Support +- Decorators +- Metadata + --- + +## Interface vs Type? + +Interface: + +- Object contract + +Type: + +- More flexible +- Supports unions and intersections + +--- + +## Interface vs Class? + +Interface: + +- Contract only + +Class: + +- Data and behavior + +--- + +## What are Decorators? + +Functions that attach metadata to classes, properties, methods, or parameters. + +--- + +## Why is Metadata important? + +Angular uses metadata to understand how a class should participate in the framework. + +--- + +## How does Dependency Injection benefit from TypeScript? + +Type information helps Angular identify and resolve constructor dependencies automatically via reflection metadata (`reflect-metadata` / `emitDecoratorMetadata`). When you inject a service via constructor injection: +```typescript +constructor(private employeeService: EmployeeService) {} +``` +TypeScript emits metadata about the parameter type, enabling Angular's DI container to find and inject the corresponding `EmployeeService` singleton token automatically. In modern Angular, the `inject(EmployeeService)` function also leverages TypeScript generics to infer the return type with zero type casting. + +--- + +## What are Generics and how does Angular rely on them? + +Generics provide a way to create reusable code components that work with a variety of types rather than a single one, while maintaining full compile-time type safety. Angular uses Generics extensively: +- **HttpClient**: `this.http.get('/api/employees')` +- **Signals**: `readonly count = signal(0)` +- **Component Inputs**: `readonly employee = input.required()` +- **RxJS Operators**: `Observable`, `map((data: T) => ...)` + +--- + +## What is the difference between `any`, `unknown`, and `never`? + +| Type | Description | Type Safety | +| :--- | :--- | :--- | +| `any` | Disables all TypeScript type checking; bypasses compiler | None (Dangerous in enterprise codebases) | +| `unknown` | Type-safe counterpart of `any`; value can be anything, but cannot be operated on without type narrowing/checking | High (Requires `typeof`, `instanceof`, or type guards) | +| `never` | Represents values that never occur (e.g., functions that always throw or infinite loops; exhaustive switch cases) | Total (Used for compile-time exhaustiveness checking) | + +--- + +# Senior-Level Discussion: TypeScript as Enterprise Boundary Defense + +For senior developers and architects, TypeScript is not just a syntax convenience—it is the **primary boundary defense mechanism**: + +```text +EXTERNAL WORLD (Untrusted) INTERNAL ANGULAR APPLICATION (Type-Safe) +┌───────────────────────────────┐ ┌────────────────────────────────────────┐ +│ - REST API JSON Payloads │ │ - Strong TypeScript Interfaces & Models│ +│ - LocalStorage strings │───────►│ - Zod / Type-Guard Validation at Gate │ +│ - URL Route Parameters │ Parse │ - 100% deterministic type safety │ +│ - Third-party SDKs │ │ - Zero runtime undefined exceptions │ +└───────────────────────────────┘ └────────────────────────────────────────┘ +``` + +1. **Compile-Time vs Runtime**: TypeScript checks types at compile-time, but TypeScript types are completely erased in the browser bundle. Boundary data from backend APIs should be validated at the HTTP boundary using type guards or validation libraries (like Zod). +2. **Strict Mode Hygiene**: Enterprise Angular projects should always enable `"strict": true`, `"strictNullChecks": true`, and `"noImplicitAny": true` in `tsconfig.json` to prevent null-reference errors. + +--- + +# Architecture Considerations + +```text + TYPESCRIPT ARCHITECTURE RULES + │ + ┌─────────────────────────────┼─────────────────────────────┐ + ▼ ▼ ▼ +NO ANY POLICY EXPLICIT CONTRACTS IMMUTABLE MODELS +- Strictly ban `any` - Define DTOs in domain libs - Mark signal states +- Use `unknown` with guards - Interface for public APIs - Use `readonly` arrays +- Enforce via ESLint rules - Share DTOs with backend - Prevent silent mutations +``` + +--- + +# Diagram: TypeScript Compilation & DI Metadata Pipeline + +```text +┌────────────────────────────────────────────────────────┐ +│ TypeScript Source (.ts files) │ +│ - @Component / @Injectable metadata decorators │ +│ - Static types, Interfaces, Signal definitions │ +└───────────────────────────┬────────────────────────────┘ + │ + Angular Compiler (ngc) + tsc + │ + ┌───────────────────┴───────────────────┐ + ▼ ▼ +┌───────────────────────┐ ┌───────────────────────┐ +│ Clean JavaScript (ESM)│ │ Component Definitions │ +│ - Types erased │ │ - Ivy Instructions │ +│ - Classes retained │ │ - Injected Tokens │ +│ - Runtime execution │ │ - Compiled Templates │ +└───────────────────────┘ └───────────────────────┘ +``` + +--- + +# Key Takeaways + +1. **Enterprise Foundation**: TypeScript gives Angular compile-time type checking, refactoring safety, and autocompletion essential for large multi-team applications. +2. **Dependency Injection Power**: TypeScript types serve as the tokens that power Angular's hierarchical DI container. +3. **Generics Everywhere**: Angular leverages Generics across `HttpClient`, `Signals`, and `RxJS` to ensure end-to-end data contract safety. +4. **Interfaces vs Types**: Use Interfaces for public API and domain entity contracts; use Types for unions, primitives, and complex mapped transformations. +5. **Strict Typing**: Enterprise standards mandate `"strict": true` and strict avoidance of `any`. + +--- + +# Interview Notes (Revision Version) + +## Definition +TypeScript is a strongly typed superset of JavaScript that compiles to plain JavaScript, providing static typing, interfaces, generics, and decorator metadata essential for Angular's enterprise architecture. + +## Key Distinctions +- **Interface vs Type**: Interfaces are extendable object contracts; Types support unions (`|`), intersections (`&`), and utility aliases. +- **`any` vs `unknown`**: `any` disables type checking; `unknown` requires type narrowing before usage. +- **Decorators**: Attach configuration metadata to classes, properties, and parameters. + +## DI Integration +TypeScript parameter types allow Angular to resolve and inject singletons without manual wiring. Modern Angular also uses `inject()` with full generic type inference. + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [Angular Architecture Overview](04-angular-architecture-overview.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [Angular Components](../02-core-angular/06-angular-components.md) + +
+ diff --git a/apps/portal/public/assets/content/02-core-angular/06-angular-components.md b/apps/portal/public/assets/content/02-core-angular/06-angular-components.md new file mode 100644 index 0000000..77348d2 --- /dev/null +++ b/apps/portal/public/assets/content/02-core-angular/06-angular-components.md @@ -0,0 +1,899 @@ +# Angular Components + +## Interview Priority + +**Must Know** + +## Interview Frequency + +**Extremely Common** + +## Recommended Depth + +**Expert Level Understanding** + +## Relevant For + +- Mid-Level Interviews +- Senior Developer Interviews +- Lead Developer Interviews +- Architect Discussions + +--- + +# What Is An Angular Component? + +A Component is a reusable, self-contained UI building block that controls a portion of the screen. + +Angular applications are built by combining multiple Components together. + +Think of a Component as: + +```text +Mini Application +Inside a Larger Application +``` + +Examples: + +```text +Header +Sidebar +Dashboard +Employee List +Employee Details +Footer +``` + +Each of these can be implemented as a separate Component. + +--- + +# Why Do Components Exist? + +As web applications grow, maintaining one large HTML, CSS, and JavaScript file becomes difficult. + +Problems include: + +- Code duplication +- Difficult maintenance +- Tight coupling +- Poor scalability +- Difficult testing +- Team collaboration issues + +Angular solves these problems through Component-Based Architecture. + +--- + +# First Principles + +Consider an Employee Management Portal. + +Instead of: + +```text +Single Giant Page +``` + +Angular promotes: + +```text +Employee Portal + +├── HeaderComponent +├── SidebarComponent +├── EmployeeListComponent +├── EmployeeDetailsComponent +└── FooterComponent +``` + +Complexity is distributed across smaller reusable units. + +--- + +# Component-Based Architecture + +Angular applications are built as a hierarchy of Components. + +Example: + +```text +AppComponent +│ +├── HeaderComponent +│ +├── DashboardComponent +│ │ +│ ├── EmployeeListComponent +│ │ +│ └── EmployeeCardComponent +│ +└── FooterComponent +``` + +This hierarchy is called the **Component Tree**. + +--- + +# Angular Component Anatomy + +A typical Angular Component consists of: + +```text +Component Class +Template +Styles +Metadata +``` + +Example: + +```typescript +@Component({ + selector: 'app-employee', + standalone: true, + templateUrl: './employee.component.html', + styleUrl: './employee.component.css' +}) +export class EmployeeComponent { + +} +``` + +--- + +# Anatomy Breakdown + +## Component Class + +A Component is fundamentally a TypeScript class. + +Example: + +```typescript +export class EmployeeComponent { + + employees = []; + + loadEmployees() { + } + + saveEmployee() { + } +} +``` + +The class contains: + +- State +- Methods +- UI Logic +- Event Handlers + +--- + +## Component Metadata + +Metadata is defined through the `@Component()` decorator. + +Example: + +```typescript +@Component({...}) +``` + +Purpose: + +```text +Tell Angular that this class is a Component +``` + +Without the decorator: + +```typescript +export class EmployeeComponent {} +``` + +Angular sees only a normal TypeScript class. + +--- + +## Selector + +Example: + +```typescript +selector: 'app-employee' +``` + +Usage: + +```html + +``` + +When Angular encounters: + +```html + +``` + +it renders: + +```text +EmployeeComponent +``` + +--- + +## Template + +Defines the UI. + +Example: + +```html +

Employees

+ +``` + +Responsibilities: + +- Display data +- Capture events +- Render UI +- Host child components + +--- + +## Styles + +Defines Component-specific styling. + +Example: + +```css +.employee-grid { + display: grid; +} +``` + +Benefits: + +- Encapsulation +- Maintainability +- Better organization + +--- + +# Component Lifecycle + +Components do not exist forever. + +They move through stages. + +```text +Created +↓ +Initialized +↓ +Updated +↓ +Destroyed +``` + +Angular provides Lifecycle Hooks to participate in these stages. + +Examples: + +```typescript +ngOnInit() +``` + +```typescript +ngOnDestroy() +``` + +Lifecycle Hooks will be covered in detail later. + +--- + +# Component State + +Every Component maintains state. + +Example: + +```typescript +employees = []; +``` + +```typescript +isLoading = false; +``` + +```typescript +selectedEmployee = null; +``` + +State drives the UI. + +--- + +## State Driven UI + +Example: + +```typescript +isLoading = true; +``` + +UI: + +```html +Loading... +``` + +--- + +```typescript +isLoading = false; +``` + +UI: + +```html +Employee List +``` + +A major responsibility of Angular is keeping UI synchronized with Component state. + +--- + +# Modern Angular (16+) + +Modern Angular promotes: + +```typescript +standalone: true +``` + +Example: + +```typescript +@Component({ + standalone: true +}) +``` + +This is now the preferred approach. + +--- + +# What Are Standalone Components? + +Standalone Components can be used without being declared inside an NgModule. + +Example: + +```typescript +@Component({ + standalone: true +}) +export class EmployeeComponent {} +``` + +Benefits: + +- Simpler architecture +- Less boilerplate +- Better developer experience +- Easier lazy loading + +--- + +# Legacy Angular Approach + +Prior to Angular 14/15/16, Components were typically declared inside NgModules. + +Example: + +```typescript +@NgModule({ + declarations: [ + EmployeeComponent + ] +}) +export class EmployeeModule {} +``` + +--- + +# Modern vs Legacy + +## Modern Angular + +```typescript +@Component({ + standalone: true +}) +``` + +Recommended approach. + +--- + +## Legacy Angular + +```typescript +@NgModule({ + declarations: [] +}) +``` + +Older approach. + +Still commonly found in enterprise applications. + +--- + +# Why Angular Introduced Standalone Components + +Problems with NgModules: + +```text +Additional Boilerplate +Additional Complexity +Learning Curve +Configuration Overhead +``` + +Angular simplified this by making Components more self-sufficient. + +--- + +# What Should Components Do? + +Good Components should focus on: + +- Displaying data +- Handling user interactions +- Managing UI state +- Coordinating services + +Example: + +```text +Employee List Screen + +Load Employees +Handle Search +Handle User Actions +Display UI +``` + +--- + +# What Should Components NOT Do? + +Avoid putting: + +- Complex Business Logic +- Caching Logic +- Authentication Logic +- Reporting Logic +- Heavy Calculations +- Data Access Logic + +inside Components. + +Those responsibilities usually belong to Services. + +--- + +# Smart vs Dumb Components + +Common enterprise design pattern. + +--- + +## Smart Components + +Responsibilities: + +- Fetch data +- Manage state +- Call services +- Coordinate workflow + +Example: + +```text +EmployeeListComponent +``` + +--- + +## Dumb Components + +Responsibilities: + +- Display data +- Raise events +- Remain reusable + +Example: + +```text +EmployeeCardComponent +``` + +Input: + +```text +Employee Data +``` + +Output: + +```text +Edit Clicked +Delete Clicked +Selected +``` + +No business logic. + +--- + +# Enterprise Example + +Employee Management Portal + +```text +EmployeeListComponent +``` + +Responsibilities: + +```text +Load Employees +Manage Filters +Call EmployeeService +``` + +--- + +```text +EmployeeCardComponent +``` + +Responsibilities: + +```text +Display Employee Information +Raise User Actions +``` + +Reusable throughout the application. + +--- + +# Parent and Child Components + +Components often contain other Components. + +Example: + +```text +EmployeeListComponent +│ +├── EmployeeCardComponent +├── EmployeeCardComponent +├── EmployeeCardComponent +``` + +Relationship: + +```text +Parent Component +↓ +Child Component +``` + +This introduces the need for Component Communication. + +--- + +# Component Communication + +A common challenge: + +```text +How do Parent and Child Components exchange data? +``` + +Angular solves this through: + +```typescript +@Input() +``` + +and + +```typescript +@Output() +``` + +These topics will be covered later. + +--- + +# How Components Fit Into Angular Architecture + +High-Level Flow: + +```text +User +↓ +Template +↓ +Component +↓ +Service +↓ +API +↓ +Database +``` + +Component sits at the center of the UI layer. + +--- + +# Relationship With Other Angular Concepts + +Most Angular features support Components. + +```text +Templates +→ Render Components + +Data Binding +→ Connect UI and State + +Directives +→ Enhance Templates + +Services +→ Provide Logic + +Dependency Injection +→ Provide Services + +Lifecycle Hooks +→ Manage Component Creation and Destruction + +Signals +→ Manage Component State + +Routing +→ Display Components +``` + +--- + +# Common Interview Questions + +## What is a Component? + +A reusable UI building block that encapsulates template, logic, and styling. + +--- + +## Why do Components exist? + +To improve: + +- Reusability +- Maintainability +- Testability +- Scalability + +--- + +## What does @Component do? + +It provides metadata that allows Angular to identify and configure a Component. + +--- + +## What is a Selector? + +A custom HTML element used to render a Component. + +--- + +## What is a Standalone Component? + +A Component that does not require declaration inside an NgModule. + +--- + +## Why did Angular introduce Standalone Components? + +To reduce boilerplate and simplify application structure. + +--- + +## What should belong in a Component? + +- UI Logic +- State Management +- User Interactions + +--- + +## What should not belong in a Component? + +- Complex Business Logic +- Data Access Logic +- Cross-Cutting Concerns + +--- + +# Senior-Level Discussion + +Components are not merely UI elements. + +They represent architectural boundaries in a frontend application. + +A well-designed Component should: + +- Have a single responsibility +- Be reusable +- Be easy to test +- Minimize coupling +- Maximize cohesion + +Standalone Components further simplify architecture by reducing framework complexity while preserving these engineering principles. + +--- + +# Architecture Considerations + +When designing Components: + +Ask: + +1. Is this reusable? +2. Does it have a single responsibility? +3. Should this logic live in a Service? +4. Is this becoming too large? +5. Can it be independently tested? + +Good Components are: + +- Focused +- Predictable +- Testable +- Reusable + +--- + +# Diagram + +```text +AppComponent +│ +├── HeaderComponent +│ +├── DashboardComponent +│ │ +│ ├── EmployeeListComponent +│ │ │ +│ │ ├── EmployeeCardComponent +│ │ ├── EmployeeCardComponent +│ │ └── EmployeeCardComponent +│ │ +│ └── EmployeeSummaryComponent +│ +└── FooterComponent +``` + +--- + +# Key Takeaways + +1. Components are the fundamental building blocks of Angular. +2. Angular applications are organized as Component Trees. +3. Components encapsulate template, logic, and styling. +4. State inside Components drives the UI. +5. Modern Angular promotes Standalone Components. +6. Components should focus on presentation and coordination. +7. Business logic should usually move into Services. +8. Most Angular features exist to support Components. + +--- + +# Interview Notes (Revision Version) + +## Definition + +A Component is a reusable UI building block that encapsulates: + +- Template +- Logic +- Styling + +--- + +## Responsibilities + +- Display Data +- Manage UI State +- Handle User Interactions +- Coordinate Services + +--- + +## Key Concepts + +- Component Tree +- Selector +- Template +- Metadata +- Standalone Components + +--- + +## Modern Angular + +Preferred: + +```typescript +@Component({ + standalone: true +}) +``` + +--- + +## Legacy Angular + +```typescript +@NgModule({ + declarations: [...] +}) +``` + +--- + +## Common Interview Questions + +- What is a Component? +- What does @Component do? +- What is a Selector? +- What is a Standalone Component? +- Why did Angular move away from NgModules? + +--- + +## Senior Talking Point + +Components are architectural boundaries that improve modularity, reusability, maintainability, and testability in large Angular applications. + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [TypeScript Essentials for Angular](../01-foundations/05-typescript-essentials-for-angular.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [Templates and Data Binding](07-templates-and-data-binding.md) + +
+ diff --git a/apps/portal/public/assets/content/02-core-angular/07-templates-and-data-binding.md b/apps/portal/public/assets/content/02-core-angular/07-templates-and-data-binding.md new file mode 100644 index 0000000..af48bdf --- /dev/null +++ b/apps/portal/public/assets/content/02-core-angular/07-templates-and-data-binding.md @@ -0,0 +1,1098 @@ +# Templates and Data Binding + +## Interview Priority + +**Must Know** + +## Interview Frequency + +**Extremely Common** + +## Recommended Depth + +**Expert Level Understanding** + +## Relevant For + +- Mid-Level Interviews +- Senior Developer Interviews +- Lead Developer Interviews +- Architect Discussions + +--- + +# What Is a Template? + +A Template is the HTML view associated with an Angular Component. + +Example: + +```html +

Employee List

+``` + +However, Angular Templates are much more than plain HTML. + +Angular extends HTML with additional capabilities such as: + +- Data Binding +- Directives +- Control Flow +- Event Handling +- Component Composition + +Think of a Template as: + +```text +HTML ++ +Angular Features += +Angular Template +``` + +--- + +# Why Do Templates Exist? + +A Component contains: + +```text +State +Logic +Methods +Business Interaction +``` + +But users cannot see TypeScript code. + +A Template acts as the visual representation of a Component. + +Example: + +```typescript +export class EmployeeComponent { + employeeName = 'Adarsh'; +} +``` + +Template: + +```html +

{{ employeeName }}

+``` + +Result: + +```text +Adarsh +``` + +The Template displays Component data. + +--- + +# Component and Template Relationship + +```text +Component +(State + Logic) + + ↓ + +Template +(UI Representation) + + ↓ + +User +``` + +The Component owns the data. + +The Template presents the data. + +--- + +# What Is Data Binding? + +Data Binding is the mechanism that synchronizes: + +```text +Component +⇄ +Template +``` + +Without Angular: + +```javascript +document.getElementById("name") + .innerText = employee.name; +``` + +Developers manually update the DOM. + +With Angular: + +```text +State Changes +↓ +Angular Updates UI +``` + +This significantly reduces boilerplate code. + +--- + +# Why Data Binding Exists + +Angular promotes a State-Driven UI design. + +Example: + +```typescript +isLoading = true; +``` + +UI: + +```text +Loading... +``` + +--- + +```typescript +isLoading = false; +``` + +UI: + +```text +Employee List +``` + +The UI reflects the Component's state. + +--- + +# Types of Data Binding + +Angular provides four primary types of Data Binding. + +```text +1. Interpolation +2. Property Binding +3. Event Binding +4. Two-Way Binding +``` + +This is one of the most frequently asked Angular interview questions. + +--- + +# 1. Interpolation + +## What Is Interpolation? + +Interpolation displays Component data inside a Template. + +Syntax: + +```html +{{ expression }} +``` + +Example: + +Component: + +```typescript +employeeName = 'Adarsh'; +``` + +Template: + +```html +

{{ employeeName }}

+``` + +Result: + +```html +

Adarsh

+``` + +--- + +## Flow + +```text +Component + ↓ +Template +``` + +One-way communication. + +Display only. + +--- + +## Common Examples + +```html +{{ title }} + +{{ employee.name }} + +{{ employees.length }} + +{{ totalEmployees }} +``` + +--- + +## Typical Use Cases + +- Displaying Names +- Displaying Counts +- Displaying Labels +- Displaying Dates +- Displaying Calculated Values + +--- + +# Interview Question + +### What is Interpolation? + +Interpolation displays Component data inside a Template using double curly braces. + +--- + +# 2. Property Binding + +## What Is Property Binding? + +Property Binding assigns Component values to DOM properties. + +Syntax: + +```html +[property]="value" +``` + +Example: + +Component: + +```typescript +imageUrl = 'employee.png'; +``` + +Template: + +```html + +``` + +Angular updates: + +```text +img.src +``` + +using the Component property. + +--- + +## Flow + +```text +Component + ↓ +DOM Property +``` + +One-way communication. + +--- + +## Common Examples + +```html + + + +``` + +Component: + +```typescript +saveEmployee() { + console.log('Saved'); +} +``` + +--- + +## Flow + +```text +User + ↓ +Event + ↓ +Component Method +``` + +--- + +## Common Events + +```html +(click) + +(change) + +(input) + +(keyup) + +(submit) + +(mouseenter) +``` + +--- + +## Example + +```html + +``` + +```html + +``` + +--- + +# Interview Question + +### What is Event Binding? + +Event Binding connects browser events to Component methods. + +--- + +# 4. Two-Way Binding + +## What Is Two-Way Binding? + +Two-Way Binding synchronizes: + +```text +Component +⇄ +UI Element +``` + +Changes on either side automatically update the other. + +--- + +## Syntax + +```html +[(ngModel)] +``` + +Example: + +Component: + +```typescript +employeeName = ''; +``` + +Template: + +```html + +``` + +--- + +## Flow + +```text +Component + ⇄ +Input Control +``` + +Bidirectional. + +--- + +## Practical Example + +User types: + +```text +Adarsh +``` + +Input updates: + +```typescript +employeeName +``` + +automatically. + +If Component changes: + +```typescript +employeeName = 'John'; +``` + +Input updates automatically. + +--- + +# Why Two-Way Binding Exists + +Forms frequently require synchronization between: + +```text +User Input +⇄ +Application State +``` + +Angular provides this capability with minimal code. + +--- + +# Common Use Cases + +- Login Forms +- Registration Forms +- Search Boxes +- User Profiles +- Employee Forms + +--- + +# Interview Question + +### What is Two-Way Binding? + +Two-Way Binding keeps a Component property and UI control synchronized in both directions. + +--- + +# Data Binding Summary + +## Interpolation + +```text +Component → Template +``` + +Display Text + +--- + +## Property Binding + +```text +Component → DOM Property +``` + +Update Element Properties + +--- + +## Event Binding + +```text +User → Component +``` + +Capture User Actions + +--- + +## Two-Way Binding + +```text +Component ⇄ Template +``` + +Synchronization + +--- + +# Visual Diagram + +```text +Interpolation + +Component + ↓ +Template + +---------------------- + +Property Binding + +Component + ↓ +DOM Property + +---------------------- + +Event Binding + +User + ↓ +Event + ↓ +Component + +---------------------- + +Two-Way Binding + +Component + ⇄ +Template +``` + +--- + +# Template Expressions + +Templates can evaluate simple expressions. + +Examples: + +```html +{{ employee.name }} +``` + +```html +{{ employees.length }} +``` + +```html +{{ firstName + ' ' + lastName }} +``` + +--- + +# Best Practices + +## Good + +```html +{{ employee.name }} +``` + +Simple and readable. + +--- + +```html +{{ employeeCount }} +``` + +Pre-computed value. + +--- + +## Avoid + +```html +{{ calculateEmployeeMetrics() }} +``` + +Complex methods inside Templates. + +Reason: + +- Frequent execution +- Performance impact +- Harder debugging + +--- + +# Modern Angular (16+/17+/18+) + +Angular introduced improved control flow. + +Recommended approach: + +```html +@if (employees.length > 0) { + +} +``` + +--- + +```html +@for (employee of employees; + track employee.id) { + +} +``` + +This is cleaner and more intuitive. + +--- + +# Legacy Angular Approach + +Before Angular 17: + +```html +
+ +
+``` + +--- + +```html +
+ +
+``` + +Widely used in existing applications. + +--- + +# Why Angular Changed + +Older syntax: + +```html +*ngIf +*ngFor +``` + +was powerful but less intuitive. + +New syntax resembles normal programming constructs: + +```typescript +if +for +``` + +which improves readability and developer experience. + +--- + +# Template vs Component Responsibilities + +## Component Responsibilities + +- State Management +- Event Handling +- Service Interaction +- Business Coordination + +Example: + +```typescript +loadEmployees() +``` + +--- + +## Template Responsibilities + +- Rendering Data +- Displaying Layout +- Binding UI Elements +- Handling Presentation + +Example: + +```html +Employee Table + +Search Input + +Action Buttons +``` + +--- + +# Enterprise Example + +Employee Directory Screen + +Component: + +```typescript +employees = []; + +loadEmployees(); +``` + +Responsibilities: + +- Retrieve data +- Manage state +- Call services + +--- + +Template: + +```html +Employee Table + +Search Controls + +Paging Controls +``` + +Responsibilities: + +- Display UI +- Display Data +- Receive User Input + +--- + +# How This Fits Into Angular Architecture + +```text +User + ↓ +Template + ↓ +Component + ↓ +Service + ↓ +API + ↓ +Database +``` + +Templates are the visible layer. + +Components control behavior behind the Templates. + +--- + +# Common Interview Questions + +## What is a Template? + +A Template is the HTML view associated with a Component. + +--- + +## What is Data Binding? + +The mechanism that synchronizes Component data and the UI. + +--- + +## What are the four types of Data Binding? + +1. Interpolation +2. Property Binding +3. Event Binding +4. Two-Way Binding + +--- + +## What is Interpolation? + +Displays Component data in a Template using: + +```html +{{ }} +``` + +--- + +## What is Property Binding? + +Updates DOM properties using Component values. + +--- + +## What is Event Binding? + +Connects browser events to Component methods. + +--- + +## What is Two-Way Binding? + +Synchronizes Component properties and UI controls in both directions. + +--- + +## Difference Between Property Binding and Event Binding? + +Property Binding: + +```text +Component → UI +``` + +Event Binding: + +```text +UI → Component +``` + +--- + +## How Does Two-Way Binding Work? + +Combination of: + +```text +Property Binding ++ +Event Binding +``` + +--- + +## What Are Modern Angular Control Flow Features? + +```html +@if + +@for +``` + +--- + +## What Was Used Before Angular 17? + +```html +*ngIf + +*ngFor +``` + +--- + +# Senior-Level Discussion + +Data Binding is a foundational Angular concept that enables declarative UI development. + +Instead of manually manipulating the DOM, developers describe relationships between application state and the UI, while Angular keeps both synchronized. + +Benefits: + +- Less DOM manipulation code +- Better maintainability +- Cleaner architecture +- More predictable UI behavior + +This is one of the key reasons Angular applications remain manageable at enterprise scale. + +--- + +# Architecture Considerations + +When designing Templates: + +- Keep Templates focused on presentation. +- Avoid heavy calculations. +- Avoid business logic. +- Keep bindings simple. +- Move complex logic into Components or Services. + +Good Angular applications maintain a clear separation between: + +```text +Presentation + +and + +Behavior +``` + +--- + +# Key Takeaways + +1. Templates are Angular's view layer. +2. Templates extend HTML with Angular-specific capabilities. +3. Data Binding synchronizes Component state and the UI. +4. Angular provides four binding types: + - Interpolation + - Property Binding + - Event Binding + - Two-Way Binding +5. Modern Angular uses: + - `@if` + - `@for` +6. Components manage state. +7. Templates display state. + +--- + +# Interview Notes (Revision Version) + +## Data Binding Types + +### Interpolation + +```html +{{ value }} +``` + +Component → Template + +--- + +### Property Binding + +```html +[src]="imageUrl" +``` + +Component → DOM Property + +--- + +### Event Binding + +```html +(click)="save()" +``` + +UI Event → Component + +--- + +### Two-Way Binding + +```html +[(ngModel)]="name" +``` + +Component ⇄ UI + +--- + +## Modern Angular + +```html +@if + +@for +``` + +Preferred approach. + +--- + +## Legacy Angular + +```html +*ngIf + +*ngFor +``` + +Still common in enterprise applications. + +--- + +## Key Message + +Templates are Angular's view layer, and Data Binding is the mechanism that keeps Component state and the UI synchronized. + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [Angular Components](06-angular-components.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [Directives](08-directives.md) + +
+ diff --git a/apps/portal/public/assets/content/02-core-angular/08-directives.md b/apps/portal/public/assets/content/02-core-angular/08-directives.md new file mode 100644 index 0000000..7cfb3a1 --- /dev/null +++ b/apps/portal/public/assets/content/02-core-angular/08-directives.md @@ -0,0 +1,847 @@ +# Directives + +## Interview Priority + +**Must Know** + +## Interview Frequency + +**Very Common** + +## Recommended Depth + +**Strong Understanding** + +## Relevant For + +- Mid-Level Interviews +- Senior Developer Interviews +- Lead Developer Interviews + +--- + +# What Is a Directive? + +A Directive is a class that tells Angular how to modify an element's: + +- Structure +- Appearance +- Behavior + +Think of Directives as instructions attached to HTML elements. + +Example: + +```html +@if (isLoggedIn()) { +

Welcome

+} +``` + +Angular receives an instruction: + +```text +Render this section only if the condition is true. +``` + +That instruction is implemented through a Directive-like mechanism. + +--- + +# First Principles + +Normal HTML is static. + +Example: + +```html +

Employees

+``` + +HTML alone cannot say: + +```text +Show if user is logged in + +Repeat for every employee + +Apply CSS dynamically + +Disable button conditionally +``` + +Angular introduces Directives to add dynamic behavior. + +--- + +# Why Do Directives Exist? + +Without Directives: + +```javascript +if(isLoggedIn) +{ + document.getElementById(...) +} +``` + +Developers manually manipulate the DOM. + +Angular's philosophy: + +```text +Describe what should happen + +instead of + +Manually manipulating the DOM +``` + +Directives make templates declarative. + +--- + +# Angular Building Blocks + +Angular provides: + +```text +Components +Templates +Data Binding +Directives +Services +Dependency Injection +``` + +Templates define UI. + +Directives make templates dynamic. + +--- + +# Types of Directives + +Angular Directives are generally categorized into: + +```text +1. Component Directives +2. Structural Directives +3. Attribute Directives +``` + +This is one of the most common interview questions. + +--- + +# 1. Component Directives + +Technically, every Component is a Directive. + +Example: + +```typescript +@Component({ + selector: 'app-employee' +}) +export class EmployeeComponent {} +``` + +Usage: + +```html + +``` + +Angular injects an entire UI section. + +Think: + +```text +Component += +Directive + Template +``` + +A Component is simply a special Directive that owns a view. + +--- + +# 2. Structural Directives + +Structural Directives change the structure of the DOM. + +Key Question: + +```text +Should this element exist? +``` + +Examples: + +```html +@if +``` + +```html +@for +``` + +Legacy: + +```html +*ngIf +``` + +```html +*ngFor +``` + +--- + +# Structural Directive Mental Model + +Structural Directives can: + +```text +Create Elements / Remove Elements +Repeat Elements +``` + +They change the DOM tree itself. +--- + +# Example: Conditional Rendering + +Modern Angular: + +```html +@if (isAdmin) { + +} +``` + +If condition is false: +```text +Button does not exist in DOM +``` + +Not hidden. + +Removed. + +Important distinction. + +--- + +# Legacy Version + +```html + +``` + +Still very common in enterprise projects. + +--- + +# Example: Loops + +Modern Angular: + +```html +@for(employee of employees; + track employee.id) { + + +} +``` + +Angular creates one DOM element per employee. + +--- + +# Legacy Version + +```html +
+ +
+``` + +--- + +# Why Angular Introduced @if and @for + +Legacy syntax: + +```html +*ngIf +*ngFor +``` + +was powerful but not intuitive. + +Modern syntax resembles: + +```typescript +if + +for +``` + +which improves readability. + +--- + +# Structural directive Characteristics + +Structural Directives: + +```text +Add Elements +Remove Elements +Repeat Elements +``` + +They modify DOM structure. + +--- + +# Interview Question + +### What is a Structural Directive? + +A Structural Directive modifies the structure of the DOM by adding, removing, or repeating elements. + +--- + +# Why Only One Structural Directive Per Element? + +Legacy Example: + +```html +
+
+``` + +Problem: + +```text +Angular does not know which structural transformation +should occur first. +``` + +Solution: + +```html + +
+ +
+
+``` + +Common interview question. +--- + +# 3. Attribute Directives + +Attribute Directives modify: + +```text +Appearance +Behavior +``` + +without changing DOM structure. + +Important distinction. + +--- + +# Example + +```html +
+``` + +Angular does NOT add or remove the div. + +The div already exists. + +Angular changes: + +```text +CSS Classes +``` + +only + +--- + +# Another Example + +```html +
+``` + +Angular modifies styles. + +DOM structure remains unchanged. + +--- + +# Attribute Directive Mental Model + +Attribute Directives: + +```text +Element Stays +Behavior Changes +Appearance Changes +``` + +--- + +# ngClass + +One of the most common Attribute Directives. + +Example: + +```typescript +statusClass = 'success'; +``` + +Template: + +```html +
+ Saved Successfully +
+``` + +Angular updates CSS classes dynamically. +--- + +# ngStyle + +Example: + +```typescript +styles = { + color:'green' +*; +``` + +Template: + +```html +
+``` + +Angular applies styles dynamically. + +--- + +# Property Binding vs Attribute Directives +A frequent source of confusion. + +--- + +## Native DOM Property + +```html + +``` + +Target: + +```javascript +img.src +``` + +Native browser property. + +--- + +## Directive Input + +```html +
+``` + +Target: + +```typescript +NgClass Directive +``` + +Angular passes the value to the directive. +The directive decides how to update the DOM. + +--- + +# Visual Comparison + +```text +[src] + ↓ +Native DOM Property + +-------------------- + +[ngClass] + ↓ +Angular Directive + ↓ +Directive Manipulates DOM +``` + +--- + +# Modern Angular Control Flow + +Modern Angular: + +```html +@if + +@for +``` + +Preferred approach. + +--- + +# Legacy Angular Control Flow + +Older Angular: + +```html +*ngIf + +*ngFor +``` + +Still commonly encountered. + +Interviewers may ask both. + +--- + +# Built-In Directives Commonly Used + +## Structural + +Modern: + +```html +@if + +@for +``` + +Legacy: + +```html +*ngIf + +*ngFor +``` + +--- + +## Attribute + +```html +[ngClass] + +[ngStyle] +``` + +--- + +# Custom Directives + +Angular allows creation of custom directives. + +Example: + +```typescript +@Directive({ + selector: '[appHighlight]' +}) +export class HighlightDirective {} +``` + +Usage: + +```html +
+``` + +Purpose: +```text +Reusable DOM behavior +``` +Common enterprise use cases: + +- Highlighting +- Auto Focus +- Permission Based UI +- Input Formatting + +--- + +# Real Enterprise Example + +Employee Portal + +```html +@if(user.canDelete) +{ + +} +``` + +Structural Directive. + +--- + +```html + + ` +}) +export class EmployeeCardComponent { + + @Output() + delete = + new EventEmitter(); + + deleteClicked(): void { + + this.delete.emit(1); + + } + +} +``` + +--- + +# Parent Component Example + +```html + + +``` + +Parent Component: + +```typescript +onDelete(employeeId: number): void { + + console.log(employeeId); + +} +``` + +--- + +# Flow Diagram + +```text +Child Component + + │ + +EventEmitter + + │ + + ▼ + +@Output() + + │ + + ▼ + +Parent Component +``` + +--- + +# What Is EventEmitter? + +EventEmitter is Angular's mechanism for publishing custom events from Child Components. + +Example: + +```typescript +@Output() +save = new EventEmitter(); +``` + +--- + +# What Does emit() Do? + +```typescript +this.save.emit(); +``` + +Meaning: + +```text +Raise Event + +Notify Parent + +Send Data +``` + +--- + +# Parent-Child Communication Summary + +## Parent → Child + +```typescript +@Input() +``` + +Used for passing data. + +--- + +## Child → Parent + +```typescript +@Output() +``` + +plus: + +```typescript +EventEmitter +``` + +Used for raising events. + +--- + +# Real Enterprise Example + +Employee Portal + +Parent: + +```text +EmployeeListComponent +``` + +Responsibilities: + +```text +Load Employees + +Call APIs + +Manage State +``` + +--- + +Child: + +```text +EmployeeCardComponent +``` + +Responsibilities: + +```text +Display Employee + +Raise Events +``` + +--- + +Communication Flow + +```text +Employee + ↓ +@Input() + +Delete Click + ↓ +@Output() +``` + +--- + +# Two-Way Communication + +Angular achieves two-way communication by combining: + +```typescript +@Input() +``` + +and + +```typescript +@Output() +``` + +--- + +Example: + +```text +Parent + ⇄ +Child +``` + +Parent provides state. + +Child raises events. + +--- + +# Sibling Component Communication + +Example: + +```text +HeaderComponent + +EmployeeListComponent + +FooterComponent +``` + +These components are siblings. + +Question: + +```text +How can HeaderComponent +communicate with EmployeeListComponent? +``` + +--- + +# Bad Approach + +Trying to directly access sibling components. + +Problems: + +```text +Tight Coupling + +Hard Maintenance + +Poor Testability +``` + +--- + +# Recommended Approach + +Use a shared Service. + +--- + +# Service-Based Communication + +```text +HeaderComponent + + │ + + ▼ + +SearchService + + ▲ + + │ + +EmployeeListComponent +``` + +Both components communicate through the same Service. + +--- + +# Example + +```typescript +@Injectable({ + providedIn: 'root' +}) +export class SearchService { + +} +``` + +We will later enhance this using: + +```text +BehaviorSubject + +Signals + +State Management +``` + +--- + +# Unrelated Components + +For components without any parent-child relationship: + +```text +Navbar + +Sidebar + +Dashboard + +Profile +``` + +Use: + +```text +Shared Services + +BehaviorSubject + +Signals + +State Management Solutions +``` + +instead of direct references. + +--- + +# Parent Access To Child + +Sometimes a Parent needs direct access to a Child Component. + +Angular provides: + +```typescript +@ViewChild() +``` + +--- + +# Example + +```typescript +@ViewChild(EmployeeCardComponent) +employeeCard!: EmployeeCardComponent; +``` + +--- + +# Usage + +```typescript +this.employeeCard.refresh(); +``` + +Angular gives us direct access to the Child Component instance. + +--- + +# Should ViewChild Be Used Frequently? + +Generally: + +```text +No +``` + +Prefer: + +```text +@Input() + +@Output() +``` + +because they maintain loose coupling. + +Use `@ViewChild()` only when necessary. + +--- + +# Common ViewChild Use Cases + +Examples: + +```text +Working with third-party components + +Accessing child methods + +Manipulating DOM elements + +Managing focus +``` + +--- + +# Content Projection (Preview) + +Angular also supports Content Projection. + +Example: + +```html + + +

Employee Details

+ +
+``` + +Child Component: + +```html +
+ + + +
+``` + +This allows content to flow from Parent into Child templates. + +We'll cover this topic separately later. + +--- + +# Modern Angular (16+/17+/18+) + +Angular still relies heavily on: + +```typescript +@Input() + +@Output() +``` + +for component communication. + +New Signal-based APIs are emerging, but these remain foundational concepts and are heavily used across enterprise applications. + +--- + +# Common Interview Questions + +## What Is Component Communication? + +The process through which Angular components exchange data and events. + +--- + +## How Does Parent Send Data To Child? + +Using: + +```typescript +@Input() +``` + +--- + +## How Does Child Send Data To Parent? + +Using: + +```typescript +@Output() +``` + +and + +```typescript +EventEmitter +``` + +--- + +## What Is EventEmitter? + +An Angular class used to emit custom events from Child Components. + +--- + +## What Does emit() Do? + +Raises an event and notifies subscribers. + +--- + +## How Do Sibling Components Communicate? + +Typically using a shared Service. + +--- + +## What Is ViewChild? + +A mechanism for obtaining a reference to a Child Component or DOM element. + +--- + +## Why Avoid Excessive ViewChild Usage? + +Because it creates tight coupling between components. + +--- + +## Can @Input() Support Two-Way Communication? + +No. + +It only supports: + +```text +Parent → Child +``` + +communication. + +--- + +# Common Interview Traps + +## Trap 1 + +Thinking: + +```typescript +@Input() +``` + +supports two-way communication. + +Incorrect. + +It supports: + +```text +Parent → Child +``` + +only. + +--- + +## Trap 2 + +Using EventEmitter inside Services. + +EventEmitter is primarily intended for Component communication. + +--- + +## Trap 3 + +Using ViewChild for every communication scenario. + +Prefer: + +```text +@Input() + +@Output() +``` + +first. + +--- + +## Trap 4 + +Making sibling components communicate directly. + +Use: + +```text +Shared Services +``` + +instead. + +--- + +# Senior-Level Discussion + +Component communication is fundamentally about maintaining clean architectural boundaries. + +Parent components generally own: + +```text +State + +Data + +Business Coordination +``` + +Child components generally own: + +```text +Presentation + +Rendering + +User Interaction +``` + +Recommended architecture: + +```text +Parent + ↓ +@Input() + +Child + ↓ +@Output() + +Parent +``` + +For broader communication: + +```text +Service + +BehaviorSubject + +Signals + +State Management +``` + +should be preferred. + +This approach improves: + +- Maintainability +- Reusability +- Testability +- Scalability + +--- + +# Architecture Considerations + +Use: + +```typescript +@Input() +``` + +when: + +```text +Parent Owns Data +``` + +--- + +Use: + +```typescript +@Output() +``` + +when: + +```text +Child Raises Events +``` + +--- + +Use: + +```text +Service-Based Communication +``` + +when: + +```text +Sibling Components Need Communication + +Cross-Feature Communication Exists +``` + +--- + +Avoid: + +```text +Direct Component References +``` + +unless absolutely necessary. + +--- + +# Component Communication Decision Matrix + +```text +Parent → Child + ↓ + @Input() + +Child → Parent + ↓ + @Output() + + EventEmitter + +Sibling → Sibling + ↓ + Service + +Application-Wide + ↓ +Service / State Management + +Direct Child Access + ↓ + @ViewChild() +``` + +--- + +# Key Takeaways + +1. Components frequently need to exchange data and events. +2. Parent-to-Child communication uses `@Input()`. +3. Child-to-Parent communication uses `@Output()`. +4. EventEmitter powers custom events. +5. Sibling components should communicate via shared Services. +6. `@ViewChild()` provides direct access to Child Components. +7. Prefer Inputs and Outputs over direct component manipulation. +8. Clean communication patterns improve long-term maintainability and scalability. + +--- + +# Interview Notes (Revision Version) + +## Parent → Child + +```typescript +@Input() +``` + +--- + +## Child → Parent + +```typescript +@Output() + +EventEmitter +``` + +--- + +## Raise Event + +```typescript +this.save.emit(); +``` + +--- + +## Sibling Communication + +```text +Shared Service +``` + +--- + +## Direct Child Reference + +```typescript +@ViewChild() +``` + +--- + +## Recommended Flow + +```text +Parent + ↓ +@Input() + +Child + ↓ +@Output() + +Parent +``` + +--- + +## Key Message + +Angular component communication is primarily built around `@Input()` and `@Output()`, enabling clean, maintainable, and loosely coupled interaction between components. + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [RxJS & Observables Fundamentals](12-rxjs-and-observables-fundamentals.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [Lifecycle Hooks](14-lifecycle-hooks.md) + +
+ diff --git a/apps/portal/public/assets/content/03-intermediate/14-lifecycle-hooks.md b/apps/portal/public/assets/content/03-intermediate/14-lifecycle-hooks.md new file mode 100644 index 0000000..5dc4eee --- /dev/null +++ b/apps/portal/public/assets/content/03-intermediate/14-lifecycle-hooks.md @@ -0,0 +1,1249 @@ +# Lifecycle Hooks + +## Interview Priority + +**Must Know** + +## Interview Frequency + +**Extremely Common** + +## Recommended Depth + +**Expert Level Understanding** + +## Relevant For + +- Mid-Level Interviews +- Senior Developer Interviews +- Lead Developer Interviews + +--- + +# What Are Lifecycle Hooks? + +Every Angular Component goes through a lifecycle. + +Example: + +```text +Component Created + + ↓ + +Initialized + + ↓ + +Input Values Updated + + ↓ + +View Rendered + + ↓ + +Component Destroyed +``` + +Angular provides special methods called Lifecycle Hooks that execute at specific points during this lifecycle. + +These hooks allow developers to run custom logic when components are: + +- Created +- Updated +- Rendered +- Destroyed + +--- + +# Why Do Lifecycle Hooks Exist? + +Imagine a Component that: + +- Loads employees from an API +- Receives data through `@Input()` +- Uses `@ViewChild()` +- Subscribes to Observables + +Questions arise: + +```text +When should I call APIs? + +When can I access Input values? + +When is ViewChild available? + +When should I cleanup subscriptions? +``` + +Lifecycle Hooks answer these questions. + +--- + +# Lifecycle Overview + +Simplified lifecycle flow: + +```text +Constructor + ↓ + +ngOnChanges + ↓ + +ngOnInit + ↓ + +ngAfterContentInit + ↓ + +ngAfterViewInit + ↓ + +Component Running + ↓ + +ngOnDestroy +``` + +This is one of the most important Angular interview diagrams. + +--- + +# Complete Lifecycle Order + +```text +Constructor + +↓ + +ngOnChanges + +↓ + +ngOnInit + +↓ + +ngDoCheck + +↓ + +ngAfterContentInit + +↓ + +ngAfterContentChecked + +↓ + +ngAfterViewInit + +↓ + +ngAfterViewChecked + +↓ + +Component Running + +↓ + +ngOnDestroy +``` + +--- + +# Constructor + +The constructor is a TypeScript class feature. + +It is not an Angular lifecycle hook. + +Example: + +```typescript +@Component({...}) +export class EmployeeComponent { + + constructor() { + + console.log('Constructor'); + + } + +} +``` + +--- + +# Purpose Of Constructor + +Use constructor for: + +```text +Dependency Injection + +Basic Field Initialization +``` + +Example: + +```typescript +constructor( + private employeeService: EmployeeService +) { +} +``` + +--- + +# Avoid Doing This In Constructor + +Avoid: + +```typescript +API Calls + +Complex Logic + +View Access + +Input Processing +``` + +Reason: + +Angular has not finished initializing the component yet. + +--- + +# Constructor vs ngOnInit + +Very common interview question. + +--- + +## Constructor + +```text +Class Creation + +Dependency Injection + +Basic Initialization +``` + +--- + +## ngOnInit + +```text +Angular Initialization Complete + +Inputs Ready + +Safe Place For Startup Logic +``` + +--- + +# ngOnChanges + +Triggered whenever an Input property changes. + +Requires: + +```typescript +@Input() +``` + +--- + +# Example + +Child Component: + +```typescript +@Component({...}) +export class EmployeeCardComponent +implements OnChanges { + + @Input() + employee!: Employee; + + ngOnChanges() { + + console.log('Employee Changed'); + + } + +} +``` + +--- + +# When Does ngOnChanges Run? + +```text +Initial Input Assignment + +Every Subsequent Input Change +``` + +--- + +# Flow + +```text +Parent Changes Input + + ↓ + +Child Receives New Value + + ↓ + +ngOnChanges Executes +``` + +--- + +# Practical Example + +Parent: + +```html + + +``` + +Whenever: + +```typescript +selectedEmployee +``` + +changes, + +Angular executes: + +```typescript +ngOnChanges() +``` + +inside the child. + +--- + +# Interview Question + +## Why Use ngOnChanges? + +To react whenever `@Input()` values change. + +--- + +# ngOnInit + +Most commonly used hook. + +Runs once after Angular initializes the component. + +--- + +# Example + +```typescript +@Component({...}) +export class EmployeeComponent +implements OnInit { + + ngOnInit(): void { + + console.log('Initialized'); + + } + +} +``` + +--- + +# Typical Uses + +```text +HTTP Calls + +Load Initial Data + +Initialize Forms + +Setup Component State +``` + +--- + +# Real Example + +```typescript +ngOnInit() { + + this.employeeService + .getEmployees() + .subscribe(data => { + + this.employees = data; + + }); + +} +``` + +This is the most common place for API calls. + +--- + +# ngOnChanges vs ngOnInit + +## ngOnChanges + +Runs: + +```text +Every Input Change +``` + +--- + +## ngOnInit + +Runs: + +```text +Only Once +``` + +after component initialization. + +--- + +# Interview Comparison + +```text +ngOnChanges + +Many Times + +------------------ + +ngOnInit + +One Time +``` + +--- + +# ngDoCheck + +Runs during Angular's change detection cycle. + +Example: + +```typescript +ngDoCheck() { + + console.log('Checking'); + +} +``` + +--- + +# Why It Exists + +Allows custom change detection. + +--- + +# Real Usage + +Rare. + +Most projects do not implement it. + +--- + +# Interview Advice + +Know: + +```text +Purpose + +Execution Order +``` + +But do not expect heavy usage in enterprise applications. + +--- + +# Content Projection Refresher + +Consider: + +```html + + +

Employee Details

+ +
+``` + +Inside Child: + +```html + +``` + +Projected content has its own lifecycle hooks. + +--- + +# ngAfterContentInit + +Runs after projected content is initialized. + +Example: + +```typescript +ngAfterContentInit() { + +} +``` + +--- + +# Usage + +```text +Content Projection Scenarios +``` + +Not commonly used. + +--- + +# ngAfterContentChecked + +Runs whenever Angular checks projected content. + +```typescript +ngAfterContentChecked() { + +} +``` + +Rarely used. + +--- + +# View Initialization + +Now consider: + +```typescript +@ViewChild(EmployeeCardComponent) +employeeCard!: EmployeeCardComponent; +``` + +Question: + +```text +When is employeeCard available? +``` + +Not during constructor. + +Not during ngOnInit. + +--- + +# ngAfterViewInit + +Runs after Angular initializes the component view and child views. + +Example: + +```typescript +ngAfterViewInit() { + + console.log(this.employeeCard); + +} +``` + +Now `@ViewChild()` is available. + +--- + +# Why ngAfterViewInit Matters + +Used when working with: + +```text +ViewChild + +Child Components + +DOM Access + +Third-Party Libraries +``` + +--- + +# Example + +```typescript +@ViewChild('searchInput') +searchInput!: ElementRef; + +ngAfterViewInit() { + + this.searchInput.nativeElement.focus(); + +} +``` + +Very common use case. + +--- + +# Interview Question + +## When Is ViewChild Available? + +Answer: + +```text +ngAfterViewInit() +``` + +--- + +# ngAfterViewChecked + +Runs whenever Angular checks component views. + +```typescript +ngAfterViewChecked() { + +} +``` + +--- + +# Usage + +Rare. + +Mostly encountered in advanced scenarios. + +--- + +# Component Running State + +After initialization: + +```text +Handle User Actions + +Update UI + +Receive Input Changes + +Respond To Events +``` + +Angular repeatedly performs change detection. + +--- + +# ngOnDestroy + +One of the most important lifecycle hooks. + +Runs before Angular destroys a component. + +--- + +# Why Is It Important? + +Used for cleanup. + +Examples: + +```text +Subscriptions + +Timers + +Intervals + +WebSocket Connections + +Event Listeners +``` + +--- + +# Observable Cleanup Example + +```typescript +private subscription!: Subscription; + +ngOnInit() { + + this.subscription = + this.employeeService + .getEmployees() + .subscribe(); + +} +``` + +--- + +Cleanup: + +```typescript +ngOnDestroy() { + + this.subscription.unsubscribe(); + +} +``` + +--- + +# Why Cleanup Matters + +Without cleanup: + +```text +Memory Leaks + +Unused Network Activity + +Unexpected Behavior +``` + +can occur. + +--- + +# Lifecycle Example Timeline + +```text +Component Created + + ↓ + +Constructor + + ↓ + +ngOnChanges + + ↓ + +ngOnInit + + ↓ + +ngAfterContentInit + + ↓ + +ngAfterViewInit + + ↓ + +User Interacts + + ↓ + +Input Changes + + ↓ + +ngOnChanges + + ↓ + +Component Removed + + ↓ + +ngOnDestroy +``` + +--- + +# Modern Angular Cleanup + +Modern Angular provides: + +```typescript +takeUntilDestroyed() +``` + +Example: + +```typescript +this.employeeService + .getEmployees() + .pipe( + takeUntilDestroyed() + ) + .subscribe(); +``` + +Reduces manual cleanup code. + +We'll discuss it in detail later. + +--- + +# Lifecycle Hook Frequency + +## Commonly Used + +```typescript +ngOnInit() + +ngOnChanges() + +ngOnDestroy() + +ngAfterViewInit() +``` + +--- + +## Occasionally Used + +```typescript +ngAfterContentInit() + +ngAfterContentChecked() +``` + +--- + +## Rarely Used + +```typescript +ngDoCheck() + +ngAfterViewChecked() +``` + +--- + +# Modern Angular (16+/17+/18+) + +Lifecycle Hooks remain fundamental. + +Even with: + +```text +Signals + +Standalone Components + +Modern APIs +``` + +these hooks are still heavily used. + +--- + +# Common Interview Questions + +## What Are Lifecycle Hooks? + +Methods invoked by Angular during a component's lifecycle. + +--- + +## Difference Between Constructor And ngOnInit? + +Constructor: + +```text +Dependency Injection + +Basic Initialization +``` + +ngOnInit: + +```text +Component Startup Logic + +API Calls + +Data Loading +``` + +--- + +## When Does ngOnChanges Execute? + +Whenever an `@Input()` property changes. + +--- + +## Which Hook Runs Only Once? + +```typescript +ngOnInit() +``` + +--- + +## When Is ViewChild Available? + +```typescript +ngAfterViewInit() +``` + +--- + +## When Should API Calls Be Made? + +Typically: + +```typescript +ngOnInit() +``` + +--- + +## When Should Cleanup Occur? + +```typescript +ngOnDestroy() +``` + +--- + +## Why Use ngOnDestroy? + +To prevent: + +```text +Memory Leaks + +Lingering Subscriptions + +Unused Resources +``` + +--- + +# Common Interview Traps + +## Trap 1 + +Making API calls inside Constructor. + +Incorrect. + +Prefer: + +```typescript +ngOnInit() +``` + +--- + +## Trap 2 + +Accessing ViewChild inside Constructor. + +Incorrect. + +Use: + +```typescript +ngAfterViewInit() +``` + +--- + +## Trap 3 + +Ignoring Observable Cleanup. + +Can cause memory leaks. + +--- + +## Trap 4 + +Thinking ngOnInit executes multiple times. + +It executes once per component instance. + +--- + +# Senior-Level Discussion + +Lifecycle Hooks provide precise control over component behavior throughout its lifespan. + +A senior engineer should understand: + +```text +When Components Are Created + +When Input Values Arrive + +When Views Are Initialized + +When Cleanup Must Occur +``` + +This knowledge becomes critical when working with: + +- RxJS +- Change Detection +- Forms +- Child Components +- Third-Party Libraries + +Proper lifecycle management leads to more reliable, maintainable, and performant Angular applications. + +--- + +# Architecture Considerations + +Use: + +```typescript +Constructor +``` + +for: + +```text +Dependency Injection +``` + +--- + +Use: + +```typescript +ngOnInit() +``` + +for: + +```text +Startup Logic + +API Calls + +Initialization +``` + +--- + +Use: + +```typescript +ngOnChanges() +``` + +for: + +```text +Reacting To Input Changes +``` + +--- + +Use: + +```typescript +ngAfterViewInit() +``` + +for: + +```text +ViewChild + +DOM Access +``` + +--- + +Use: + +```typescript +ngOnDestroy() +``` + +for: + +```text +Cleanup +``` + +--- + +# Lifecycle Hook Decision Matrix + +```text +Need Dependency Injection? + ↓ +Constructor + +Need Initial Data Load? + ↓ +ngOnInit + +Need To Detect @Input Changes? + ↓ +ngOnChanges + +Need ViewChild Access? + ↓ +ngAfterViewInit + +Need Cleanup? + ↓ +ngOnDestroy +``` + +--- + +# Key Takeaways + +1. Components have a lifecycle managed by Angular. +2. Lifecycle Hooks execute at specific stages. +3. Constructor is for Dependency Injection, not business logic. +4. `ngOnInit()` is commonly used for initialization and API calls. +5. `ngOnChanges()` reacts to Input changes. +6. `ngAfterViewInit()` is where ViewChild becomes available. +7. `ngOnDestroy()` is critical for resource cleanup. +8. Proper lifecycle management prevents memory leaks and improves maintainability. + +--- + +# Interview Notes (Revision Version) + +## Most Common Hooks + +```typescript +ngOnInit() + +ngOnChanges() + +ngAfterViewInit() + +ngOnDestroy() +``` + +--- + +## Constructor vs ngOnInit + +Constructor: + +```text +Dependency Injection +``` + +--- + +ngOnInit: + +```text +Startup Logic +``` + +--- + +## Input Changes + +```typescript +ngOnChanges() +``` + +--- + +## ViewChild Access + +```typescript +ngAfterViewInit() +``` + +--- + +## Cleanup + +```typescript +ngOnDestroy() +``` + +--- + +## Typical Order + +```text +Constructor + +↓ + +ngOnChanges + +↓ + +ngOnInit + +↓ + +ngAfterViewInit + +↓ + +ngOnDestroy +``` + +--- + +## Key Message + +Lifecycle Hooks provide controlled entry points into Angular's component lifecycle, enabling initialization, change handling, view interaction, and cleanup at the appropriate stages. + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [Component Communication](13-component-communication.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [Forms](15-forms.md) + +
+ diff --git a/apps/portal/public/assets/content/03-intermediate/15-forms.md b/apps/portal/public/assets/content/03-intermediate/15-forms.md new file mode 100644 index 0000000..e3738a4 --- /dev/null +++ b/apps/portal/public/assets/content/03-intermediate/15-forms.md @@ -0,0 +1,1464 @@ +# Forms + +## Interview Priority + +**Must Know** + +## Interview Frequency + +**Extremely Common** + +## Recommended Depth + +**Expert Level Understanding** + +## Relevant For + +- Mid-Level Interviews +- Senior Developer Interviews +- Lead Developer Interviews + +--- + +# What Is A Form? + +A Form is a mechanism for collecting, validating, and processing user input. + +Examples: + +```text +Login Form + +Registration Form + +Employee Form + +Search Form + +Settings Screen + +Profile Screen +``` + +Forms are one of the most commonly used features in Angular applications. + +--- + +# Why Do We Need Forms? + +Applications frequently need to: + +```text +Capture User Input + +Validate Input + +Display Validation Errors + +Submit Data To APIs + +Track User Changes +``` + +Angular provides built-in support for all of these scenarios. + +--- + +# Angular Form Approaches + +Angular supports two form architectures: + +```text +1. Template-Driven Forms + +2. Reactive Forms +``` + +This is one of the most common Angular interview questions. + +--- + +# High-Level Comparison + +## Template-Driven Forms + +```text +HTML Driven + +Simple + +Quick Setup + +Suitable For Small Forms +``` + +--- + +## Reactive Forms + +```text +Code Driven + +Scalable + +Testable + +Preferred For Enterprise Applications +``` + +--- + +# Interview Answer + +### Which Form Approach Is Preferred In Enterprise Applications? + +```text +Reactive Forms +``` + +because they provide: + +- Better Testability +- Better Scalability +- Strong Validation Support +- Predictable State Management + +--- + +# Template-Driven Forms + +Template-driven forms rely heavily on: + +```typescript +ngModel +``` + +and template configuration. + +--- + +# Configuration + +Import: + +```typescript +FormsModule +``` + +Example: + +```typescript +import { + FormsModule +} from '@angular/forms'; +``` + +--- + +# Simple Example + +Component: + +```typescript +employeeName = ''; +``` + +--- + +Template: + +```html + +``` + +--- + +# Data Flow + +```text +Input Field + + ⇄ + +Component Property +``` + +Angular automatically synchronizes both. + +--- + +# How ngModel Works + +```html + +``` + +combines: + +```html +[value]="employeeName" +``` + +and + +```html +(input)="employeeName = $event" +``` + +into one syntax. + +--- + +# Advantages Of Template-Driven Forms + +```text +Easy To Learn + +Less Code + +Quick Setup +``` + +--- + +# Limitations Of Template-Driven Forms + +As forms grow: + +```text +Many Controls + +Complex Validation + +Dynamic Fields + +Conditional Logic +``` + +templates become difficult to maintain. + +--- + +# Reactive Forms + +Reactive Forms are Angular's preferred enterprise solution. + +Core idea: + +```text +Form Model Lives In TypeScript +``` + +rather than HTML. + +--- + +# Configuration + +Import: + +```typescript +ReactiveFormsModule +``` + +or modern standalone configuration. + +--- + +Example: + +```typescript +import { + ReactiveFormsModule +} from '@angular/forms'; +``` + +--- + +# Why Reactive Forms? + +Benefits: + +```text +Strong Validation + +Predictable Behavior + +Scalable Architecture + +Observable Integration + +Easy Testing +``` + +--- + +# Core Building Blocks + +Reactive Forms are built using: + +```text +FormControl + +FormGroup + +FormArray +``` + +--- + +# FormControl + +Represents a single form field. + +Example: + +```typescript +name = + new FormControl(''); +``` + +--- + +# Visual + +```text +Input Field + ↓ +FormControl +``` + +--- + +# Example + +```typescript +email = + new FormControl(''); +``` + +Template: + +```html + +``` + +--- + +# FormGroup + +Represents a group of controls. + +--- + +# Example + +```typescript +profileForm = + new FormGroup({ + + firstName: + new FormControl(''), + + lastName: + new FormControl(''), + + email: + new FormControl('') + + }); +``` + +--- + +# Visual + +```text +FormGroup + + ├── firstName + + ├── lastName + + └── email +``` + +--- + +# Template + +```html + + + + + + + + + +``` + +--- + +# FormArray + +Represents a dynamic collection of controls. + +Used when number of controls is unknown. + +--- + +# Typical Use Cases + +```text +Phone Numbers + +Addresses + +Skills + +Contacts + +Dynamic Rows +``` + +--- + +# Example + +```typescript +skills = + new FormArray([ + new FormControl('') + ]); +``` + +--- + +# Visual + +```text +Employee + + Skills + + ├── Angular + + ├── .NET + + └── Azure +``` + +Each skill can be dynamically added or removed. + +--- + +# Accessing Form Values + +```typescript +console.log( + this.profileForm.value +); +``` + +Output: + +```typescript +{ + firstName: 'Adarsh', + lastName: 'Pawaskar', + email: 'adarsh@test.com' +} +``` + +--- + +# Validation + +One of the biggest reasons Reactive Forms are preferred. + +Angular provides built-in validators. + +--- + +# Built-In Validators + +```typescript +Validators.required + +Validators.email + +Validators.minLength() + +Validators.maxLength() + +Validators.min() + +Validators.max() + +Validators.pattern() +``` + +--- + +# Example + +```typescript +email: + new FormControl( + '', + [ + Validators.required, + Validators.email + ] + ) +``` + +--- + +# Multiple Validators + +```typescript +password: + new FormControl( + '', + [ + Validators.required, + Validators.minLength(8) + ] + ) +``` + +--- + +# Validation Flow + +```text +User Types + + ↓ + +Validator Executes + + ↓ + +Valid / Invalid +``` + +--- + +# Form State + +Every control tracks state information. + +--- + +## valid + +```text +Control Passes Validation +``` + +--- + +## invalid + +```text +Control Fails Validation +``` + +--- + +## pristine + +```text +User Has Not Changed Value +``` + +--- + +## dirty + +```text +User Modified Value +``` + +--- + +## untouched + +```text +Control Never Received Focus +``` + +--- + +## touched + +```text +User Focused And Left Control +``` + +--- + +# Interview Question + +## Dirty vs Touched + +Dirty: + +```text +Value Changed +``` + +Touched: + +```text +Received Focus And Lost Focus +``` + +--- + +# Example + +User clicks field: + +```text +Email +``` + +and leaves. + +Result: + +```text +Touched = true + +Dirty = false +``` + +--- + +User changes value: + +```text +Dirty = true +``` + +--- + +# Displaying Validation Errors + +Template: + +```html +@if( + profileForm.controls.email.invalid && + profileForm.controls.email.touched +) { + + + Invalid Email + + +} +``` + +--- + +# Form Submission + +Template: + +```html +
+``` + +--- + +Component: + +```typescript +save() { + + if(this.profileForm.valid) { + + console.log(this.profileForm.value); + + } + +} +``` + +--- + +# FormBuilder + +Angular provides FormBuilder to reduce boilerplate. + +--- + +# Without FormBuilder + +```typescript +profileForm = + new FormGroup({ + + name: + new FormControl(''), + + email: + new FormControl('') + + }); +``` + +--- + +# With FormBuilder + +```typescript +constructor( + private fb: FormBuilder +) {} + +profileForm = this.fb.group({ + + name: [''], + + email: [''] + +}); +``` + +Cleaner and preferred in enterprise applications. + +--- + +# RxJS Integration + +Reactive Forms integrate naturally with RxJS. + +--- + +# valueChanges + +Every control exposes: + +```typescript +valueChanges +``` + +which returns: + +```typescript +Observable +``` + +--- + +# Example + +```typescript +this.profileForm + .valueChanges + .subscribe(value => { + + console.log(value); + + }); +``` + +--- + +# What Happens? + +User types: + +```text +A + +Ad + +Ada + +Adar + +Adars + +Adarsh +``` + +Observable emits each value. + +--- + +# Why valueChanges Is Powerful + +Used for: + +```text +Search + +Auto Save + +Dynamic Validation + +Dependent Dropdowns + +Real-Time Filtering +``` + +--- + +# Example: Search Screen + +```typescript +this.searchControl + .valueChanges + .subscribe(searchTerm => { + + }); +``` + +As the user types: + +```text +Angular +``` + +the form continuously emits updated values. + +--- + +# Custom Validators + +Built-in validators cannot solve every business rule. + +Example: + +```text +Employee Age Must Be 18+ +``` + +--- + +# Custom Validator Example + +```typescript +function ageValidator( + control: AbstractControl +) { + + return control.value >= 18 + ? null + : { ageInvalid: true }; + +} +``` + +--- + +# Usage + +```typescript +age: + new FormControl( + '', + [ + ageValidator + ] + ) +``` + +--- + +# Async Validators + +Sometimes validation requires API calls. + +Examples: + +```text +Email Already Exists? + +Username Available? +``` + +Angular supports asynchronous validators. + +--- + +# Cross-Field Validation + +Some rules depend on multiple fields. + +Example: + +```text +Password + +Confirm Password +``` + +--- + +Requirement: + +```text +Both Values Must Match +``` + +Often implemented at FormGroup level. + +--- + +# Example + +```typescript +registerForm = + this.fb.group({ + + password: [''], + + confirmPassword: [''] + + }); +``` + +Group validator checks both values. + +--- + +# Disable / Enable Controls + +Disable: + +```typescript +this.profileForm + .controls.email + .disable(); +``` + +--- + +Enable: + +```typescript +this.profileForm + .controls.email + .enable(); +``` + +--- + +# Update Values + +Single field: + +```typescript +this.profileForm + .controls.name + .setValue('Adarsh'); +``` + +--- + +Multiple fields: + +```typescript +this.profileForm.patchValue({ + + name: 'Adarsh' + +}); +``` + +--- + +# setValue vs patchValue + +Common interview question. + +--- + +## setValue() + +Requires all fields. + +```typescript +setValue({ + name: '', + email: '' +}); +``` + +Missing fields cause errors. + +--- + +## patchValue() + +Allows partial updates. + +```typescript +patchValue({ + name: 'Adarsh' +}); +``` + +More flexible. + +--- + +# Template-Driven vs Reactive Forms + +## Template-Driven + +Advantages: + +```text +Simple + +Quick Setup + +Less Code +``` + +Disadvantages: + +```text +Hard To Scale + +Difficult Testing + +Limited Flexibility +``` + +--- + +## Reactive Forms + +Advantages: + +```text +Testable + +Predictable + +Scalable + +Observable-Based + +Enterprise Friendly +``` + +Disadvantages: + +```text +More Initial Code +``` + +--- + +# Real Enterprise Example + +Employee Registration Form + +Fields: + +```text +Name + +Email + +Department + +Address + +Skills + +Salary +``` + +Requirements: + +```text +Validation + +Dynamic Controls + +Cross-Field Rules + +Auto Save + +API Integration +``` + +Reactive Forms are usually the best choice. + +--- + +# Modern Angular (16+/17+/18+) + +Reactive Forms remain the dominant enterprise approach. + +Signals have introduced new reactive capabilities, but Forms still heavily rely on: + +```text +FormControl + +FormGroup + +FormArray + +Validators + +valueChanges +``` + +--- + +# Common Interview Questions + +## What Are Angular Form Types? + +```text +Template-Driven Forms + +Reactive Forms +``` + +--- + +## Which Is Preferred In Enterprise Applications? + +```text +Reactive Forms +``` + +--- + +## What Is FormControl? + +Represents a single field. + +--- + +## What Is FormGroup? + +Collection of controls. + +--- + +## What Is FormArray? + +Dynamic collection of controls. + +--- + +## What Is FormBuilder? + +Utility for creating forms with less boilerplate. + +--- + +## What Is valueChanges? + +Observable that emits whenever a form value changes. + +--- + +## Difference Between Dirty And Touched? + +Dirty: + +```text +Value Changed +``` + +Touched: + +```text +Focused And Left +``` + +--- + +## Difference Between setValue And patchValue? + +setValue: + +```text +Requires Complete Object +``` + +patchValue: + +```text +Allows Partial Update +``` + +--- + +# Common Interview Traps + +## Trap 1 + +Thinking Reactive Forms are reactive because of two-way binding. + +Incorrect. + +They are reactive because they expose Observable-based APIs. + +--- + +## Trap 2 + +Ignoring FormArray. + +Many enterprise applications rely heavily on dynamic collections. + +--- + +## Trap 3 + +Using Template-Driven Forms for large business applications. + +Usually not recommended. + +--- + +## Trap 4 + +Not understanding valueChanges. + +This is a major RxJS integration point. + +--- + +## Trap 5 + +Confusing touched and dirty. + +They track different user interactions. + +--- + +# Senior-Level Discussion + +Reactive Forms provide Angular's preferred enterprise form architecture. + +They offer: + +- Predictable State Management +- Strong Validation Support +- Observable-Based Change Tracking +- Excellent Testing Capabilities +- Scalability For Large Applications + +Most enterprise Angular teams standardize on Reactive Forms because they align naturally with: + +```text +Services + +RxJS + +Dependency Injection + +Validation + +State Management +``` + +--- + +# Architecture Considerations + +Prefer: + +```text +Reactive Forms +``` + +for: + +- Enterprise Applications +- Complex Validation +- Dynamic Forms +- Multi-Step Wizards +- Large Teams + +--- + +Use: + +```text +Template-Driven Forms +``` + +for: + +- Small Forms +- Simple Screens +- Learning Purposes + +--- + +# Decision Matrix + +```text +Simple Form + ↓ +Template-Driven + +Complex Business Form + ↓ +Reactive Forms + +Dynamic Controls + ↓ +FormArray + +Validation + ↓ +Validators + +Business Rules + ↓ +Custom Validators + +API Validation + ↓ +Async Validators +``` + +--- + +# Key Takeaways + +1. Angular supports Template-Driven and Reactive Forms. +2. Reactive Forms are preferred for enterprise applications. +3. FormControl represents a single field. +4. FormGroup represents multiple controls. +5. FormArray supports dynamic collections. +6. Validators provide built-in validation support. +7. FormBuilder reduces boilerplate. +8. valueChanges exposes form changes as Observables. +9. Custom Validators support business rules. +10. Forms are one of the most important Angular features. + +--- + +# Interview Notes (Revision Version) + +## Form Types + +```text +Template-Driven + +Reactive +``` + +--- + +## Reactive Form Building Blocks + +```text +FormControl + +FormGroup + +FormArray +``` + +--- + +## Validation + +```typescript +Validators.required + +Validators.email + +Validators.minLength() +``` + +--- + +## Form State + +```text +valid + +invalid + +dirty + +pristine + +touched + +untouched +``` + +--- + +## RxJS Integration + +```typescript +valueChanges +``` + +returns: + +```typescript +Observable +``` + +--- + +## Updates + +```typescript +setValue() + +patchValue() +``` + +--- + +## Enterprise Recommendation + +```text +Use Reactive Forms +``` + +for most business applications. + +--- + +## Key Message + +Reactive Forms are Angular's preferred enterprise solution for collecting, validating, and managing user input through a scalable, testable, and Observable-driven architecture. + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [Lifecycle Hooks](14-lifecycle-hooks.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [Subjects & BehaviorSubject](../04-reactivity/16-subjects-and-behaviorsubject.md) + +
+ diff --git a/apps/portal/public/assets/content/04-reactivity/16-subjects-and-behaviorsubject.md b/apps/portal/public/assets/content/04-reactivity/16-subjects-and-behaviorsubject.md new file mode 100644 index 0000000..f3674e4 --- /dev/null +++ b/apps/portal/public/assets/content/04-reactivity/16-subjects-and-behaviorsubject.md @@ -0,0 +1,1424 @@ +# Subjects & BehaviorSubject + +## Interview Priority + +**Must Know** + +## Interview Frequency + +**Very Common** + +## Recommended Depth + +**Expert Level Understanding** + +## Relevant For + +- Mid-Level Interviews +- Senior Developer Interviews +- Lead Developer Interviews +- Architect Discussions + +--- + +# Why Do We Need Subjects? + +Let's start from what we already know. + +HttpClient returns: + +```typescript +Observable +``` + +Example: + +```typescript +this.employeeService + .getEmployees() + .subscribe(...); +``` + +Here: + +```text +Angular Creates Data + +We Consume Data +``` + +We are subscribers. + +We don't control the stream. + +--- + +# The Missing Problem + +Suppose we have: + +```text +Login Page + +Navbar + +Dashboard + +Profile Page +``` + +When a user logs in: + +```text +Navbar should update + +Dashboard should update + +Profile should update +``` + +Question: + +```text +Who informs all these components? +``` + +We need a mechanism that allows us to: + +```text +Push Data + +Store State + +Notify Multiple Components +``` + +This is exactly what Subjects solve. + +--- + +# What Is A Subject? + +A Subject is a special RxJS type that is both: + +```text +Observable ++ +Observer +``` + +Meaning: + +```text +Can Receive Values + +Can Emit Values + +Can Be Subscribed To +``` + +--- + +# Simple Mental Model + +Observable: + +```text +Receive Data Only +``` + +Subject: + +```text +Receive Data + +Emit Data +``` + +--- + +# Visual Representation + +```text + Subject + + ┌─────┐ + +next() ─► ├──► Subscriber A + │ + ├──► Subscriber B + │ + └──► Subscriber C +``` + +One emission can notify many subscribers. + +--- + +# Subject Is Like A Broadcast Channel + +Imagine: + +```text +Radio Station +``` + +Broadcaster: + +```text +Sends Message +``` + +Listeners: + +```text +Receive Message +``` + +Anyone tuned in receives future broadcasts. + +--- + +# Creating A Subject + +```typescript +import { Subject } from 'rxjs'; + +const subject = + new Subject(); +``` + +--- + +# Subscribing To Subject + +```typescript +subject.subscribe(value => { + + console.log(value); + +}); +``` + +--- + +# Emitting Values + +```typescript +subject.next('Hello'); +``` + +Output: + +```text +Hello +``` + +--- + +# Multiple Subscribers + +```typescript +subject.subscribe(value => { + + console.log('A:', value); + +}); + +subject.subscribe(value => { + + console.log('B:', value); + +}); +``` + +--- + +Emit: + +```typescript +subject.next('Angular'); +``` + +Output: + +```text +A: Angular + +B: Angular +``` + +--- + +# Multicasting + +One of the most important Subject concepts. + +Normal Observable: + +```text +Producer + + ↓ + +Single Execution +``` + +Subject: + +```text +One Value + + ↓ + +Many Subscribers +``` + +This is called: + +```text +Multicasting +``` + +--- + +# Subject Timeline + +```text +Subscriber A Joins + + ↓ + +next(A) + + ↓ + +A Receives Value + + ↓ + +Subscriber B Joins + + ↓ + +next(B) + + ↓ + +A Receives B + +B Receives B +``` + +Notice: + +```text +Subscriber B missed A +``` + +--- + +# The Biggest Limitation Of Subject + +Subjects do not store previously emitted values. + +Once a value is emitted: + +```text +It's Gone +``` + +--- + +# Example + +```typescript +const subject = + new Subject(); + +subject.next(1); + +subject.subscribe(value => { + + console.log(value); + +}); +``` + +Output: + +```text +Nothing +``` + +--- + +# Why? + +Because: + +```text +1 was emitted + +before subscription occurred +``` + +Subject has: + +```text +No Memory +``` + +--- + +# Real Angular Problem + +Imagine: + +```text +User Logs In +``` + +AuthService emits: + +```typescript +userSubject.next(user); +``` + +Later: + +```text +Navbar Component Loads +``` + +Navbar subscribes. + +Question: + +```text +Will it receive user? +``` + +With Subject: + +```text +No +``` + +The value was already lost. + +--- + +# This Is Why BehaviorSubject Exists + +Most applications require: + +```text +Current User + +Current Theme + +Current Language + +Shopping Cart + +Selected Project +``` + +These all have: + +```text +Current State +``` + +We need a Subject that remembers. + +--- + +# What Is BehaviorSubject? + +BehaviorSubject is a Subject that: + +```text +Stores Current Value + +Automatically Sends Current Value +To New Subscribers +``` + +--- + +# Key Difference + +Subject: + +```text +Forgets Everything +``` + +BehaviorSubject: + +```text +Remembers Latest Value +``` + +--- + +# Creating A BehaviorSubject + +```typescript +import { + BehaviorSubject +} from 'rxjs'; + +const userSubject = + new BehaviorSubject( + 'Guest' + ); +``` + +--- + +# Important Observation + +BehaviorSubject requires: + +```text +Initial Value +``` + +Always. + +--- + +# Why Initial Value Is Required + +BehaviorSubject promises: + +```text +Every subscriber receives +current value immediately. +``` + +A current value must therefore exist. + +--- + +# Subscribe Immediately + +```typescript +userSubject.subscribe(user => { + + console.log(user); + +}); +``` + +Output: + +```text +Guest +``` + +Even before: + +```typescript +next() +``` + +is called. + +--- + +# Emit New Value + +```typescript +userSubject.next( + 'Adarsh' +); +``` + +Output: + +```text +Adarsh +``` + +--- + +# New Subscriber Later + +```typescript +userSubject.subscribe(user => { + + console.log( + 'New Subscriber:', + user + ); + +}); +``` + +Output: + +```text +New Subscriber: +Adarsh +``` + +Even though: + +```text +Adarsh was emitted earlier +``` + +--- + +# Timeline Comparison + +## Subject + +```text +next(User) + + ↓ + +Subscriber Joins + + ↓ + +Nothing Received +``` + +--- + +## BehaviorSubject + +```text +next(User) + + ↓ + +Stores User + + ↓ + +Subscriber Joins + + ↓ + +User Immediately Received +``` + +--- + +# Subject vs BehaviorSubject + +## Subject + +```typescript +const subject = + new Subject(); +``` + +Characteristics: + +```text +No Initial Value + +No Memory + +Missed Values Lost Forever +``` + +--- + +## BehaviorSubject + +```typescript +const subject = + new BehaviorSubject( + null + ); +``` + +Characteristics: + +```text +Initial Value Required + +Stores Current Value + +New Subscribers Get Latest Value +``` + +--- + +# Real Enterprise Example: Authentication + +Most Angular applications have: + +```text +AuthService +``` + +managing current user state. + +--- + +# Service + +```typescript +@Injectable({ + providedIn:'root' +}) +export class AuthService { + + private userSubject = + new BehaviorSubject< + User | null + >(null); + + user$ = + this.userSubject.asObservable(); + +} +``` + +--- + +# Why Is Subject Private? + +Bad: + +```typescript +public userSubject = + new BehaviorSubject(...); +``` + +Now any component can: + +```typescript +this.authService + .userSubject + .next(...) +``` + +Very risky. + +--- + +# Recommended Pattern + +```typescript +private userSubject = + new BehaviorSubject(...); + +public user$ = + this.userSubject.asObservable(); +``` + +Benefits: + +```text +Encapsulation + +Controlled Updates + +Safer Architecture +``` + +--- + +# Login Example + +```typescript +login(user: User) { + + this.userSubject.next(user); + +} +``` + +--- + +# Logout Example + +```typescript +logout() { + + this.userSubject.next(null); + +} +``` + +--- + +# Navbar Component + +```typescript +this.authService + .user$ + .subscribe(user => { + + this.user = user; + + }); +``` + +--- + +# Dashboard Component + +```typescript +this.authService + .user$ + .subscribe(user => { + + this.currentUser = user; + + }); +``` + +--- + +# Profile Component + +```typescript +this.authService + .user$ + .subscribe(user => { + + this.profile = user; + + }); +``` + +--- + +# Architecture Flow + +```text +User Logs In + + ↓ + +AuthService + + ↓ + +BehaviorSubject + + ↓ + +Navbar Updated + +Dashboard Updated + +Profile Updated +``` + +One update. + +Multiple consumers. + +--- + +# Another Enterprise Example: Theme Management + +```typescript +private themeSubject = + new BehaviorSubject( + 'light' + ); +``` + +--- + +# Change Theme + +```typescript +setTheme( + theme:string +){ + + this.themeSubject.next( + theme + ); + +} +``` + +--- + +# Any Component + +```typescript +this.themeService + .theme$ + .subscribe(theme => { + + }); +``` + +Immediately receives current theme. + +--- + +# Search Screen Example + +Search Component: + +```typescript +search(term:string){ + + this.searchSubject + .next(term); + +} +``` + +--- + +Results Component: + +```typescript +this.searchService + .search$ + .subscribe(term => { + + }); +``` + +Classic sibling communication. + +--- + +# Accessing Current Value + +BehaviorSubject provides: + +```typescript +getValue() +``` + +--- + +# Example + +```typescript +const currentUser = + + this.userSubject + .getValue(); +``` + +--- + +# Should We Use getValue Frequently? + +Usually: + +```text +No +``` + +Prefer: + +```text +Reactive Subscriptions +``` + +when possible. + +--- + +# Subject Types + +Interview awareness only. + +--- + +## Subject + +```text +No Stored Value +``` + +--- + +## BehaviorSubject + +```text +Stores Latest Value +``` + +--- + +## ReplaySubject + +```text +Stores Multiple Values +``` + +Example: + +```text +Last 5 Values +``` + +--- + +## AsyncSubject + +```text +Emits Final Value Only +``` + +Rarely used in Angular. + +--- + +# Subject vs EventEmitter + +Very common interview question. + +--- + +## EventEmitter + +Used for: + +```typescript +@Output() +``` + +component communication. + +--- + +Example: + +```typescript +@Output() +save = + new EventEmitter(); +``` + +--- + +## Subject + +Typically used for: + +```text +Services + +State Sharing + +Cross-Component Communication +``` + +--- + +# Memory Leak Considerations + +Subjects and BehaviorSubjects are still Observables. + +Subscriptions still require cleanup. + +--- + +Traditional: + +```typescript +ngOnDestroy() { + + this.subscription + .unsubscribe(); + +} +``` + +--- + +Modern Angular: + +```typescript +takeUntilDestroyed() +``` + +--- + +# Common Angular Pattern + +```typescript +private userSubject = + new BehaviorSubject< + User | null + >(null); + +user$ = + this.userSubject + .asObservable(); +``` + +You'll see this pattern constantly in enterprise Angular projects. + +--- + +# BehaviorSubject vs Signals (Preview) + +Traditional Angular: + +```typescript +private userSubject = + new BehaviorSubject< + User | null + >(null); +``` + +--- + +Modern Angular: + +```typescript +user = + signal( + null + ); +``` + +Signals reduce: + +```text +Subscriptions + +Boilerplate + +RxJS Complexity +``` + +We'll compare them later in depth. + +--- + +# Common Interview Questions + +## What Is A Subject? + +A special RxJS type that acts as both an Observable and an Observer. + +--- + +## What Is A BehaviorSubject? + +A Subject that stores the latest value and immediately emits it to new subscribers. + +--- + +## Why Does BehaviorSubject Require An Initial Value? + +It always maintains a current value. + +--- + +## Difference Between Subject And BehaviorSubject? + +Subject: + +```text +No Stored Value +``` + +BehaviorSubject: + +```text +Stores Current Value +``` + +--- + +## Why Use BehaviorSubject In Services? + +To share reactive state between multiple components. + +--- + +## What Does next() Do? + +Pushes a new value into the stream. + +--- + +## What Does getValue() Do? + +Returns the current value stored inside a BehaviorSubject. + +--- + +## How Do Sibling Components Communicate? + +Common approach: + +```text +Shared Service + +BehaviorSubject +``` + +--- + +# Common Interview Traps + +## Trap 1 + +Thinking Subject stores emitted values. + +Incorrect. + +It does not. + +--- + +## Trap 2 + +Exposing BehaviorSubject publicly. + +Prefer: + +```typescript +private subject + +public observable +``` + +pattern. + +--- + +## Trap 3 + +Using getValue() everywhere. + +Prefer reactive subscriptions. + +--- + +## Trap 4 + +Thinking BehaviorSubject is a state management solution like NgRx. + +It works well for simple and medium-sized state. + +Large applications may require dedicated state management. + +--- + +# Senior-Level Discussion + +BehaviorSubject became the de-facto Angular state-sharing mechanism for many years because it provides: + +```text +Current State + +Reactive Updates + +Multicasting + +Simple API +``` + +A very common enterprise architecture looks like: + +```text +Component + + ↓ + +Service + + ↓ + +BehaviorSubject + + ↓ + +Multiple Components +``` + +This pattern is widely used for: + +- Authentication +- User Preferences +- Search Filters +- Theme Switching +- Shopping Cart State +- Feature-Level State + +Understanding BehaviorSubject is extremely important because it bridges the gap between basic RxJS and modern Angular Signals. + +--- + +# Architecture Considerations + +Use Subject when: + +```text +Only Future Events Matter +``` + +Examples: + +```text +Button Clicks + +Notifications + +Temporary Events +``` + +--- + +Use BehaviorSubject when: + +```text +Current State Must Be Remembered +``` + +Examples: + +```text +Current User + +Current Theme + +Current Language + +Current Filters +``` + +--- + +# Decision Matrix + +```text +Need Current Value? + ↓ +BehaviorSubject + +Need Only Future Events? + ↓ +Subject + +Need Multiple Previous Values? + ↓ +ReplaySubject +``` + +--- + +# Key Takeaways + +1. Subject is both an Observable and Observer. +2. Subjects support multicasting. +3. Subject does not remember previous values. +4. BehaviorSubject remembers the latest value. +5. BehaviorSubject requires an initial value. +6. New subscribers immediately receive the latest value. +7. BehaviorSubject is commonly used in Angular services. +8. Keep Subjects private and expose Observables publicly. +9. BehaviorSubject is a popular lightweight state-sharing mechanism. +10. Understanding BehaviorSubject makes Signals much easier to learn. + +--- + +# Interview Notes (Revision Version) + +## Subject + +```text +Observable + Observer +``` + +--- + +## Subject Characteristics + +```text +No Memory + +No Initial Value + +Multicasting +``` + +--- + +## BehaviorSubject Characteristics + +```text +Initial Value Required + +Stores Current Value + +Immediate Value For New Subscribers +``` + +--- + +## Common Angular Service Pattern + +```typescript +private subject = + new BehaviorSubject(...); + +public state$ = + this.subject.asObservable(); +``` + +--- + +## Updating State + +```typescript +this.subject.next(value); +``` + +--- + +## Reading State + +Reactive: + +```typescript +state$.subscribe(...) +``` + +--- + +Current Value: + +```typescript +subject.getValue() +``` + +--- + +## Key Message + +A Subject enables multicast event publishing, while a BehaviorSubject additionally stores and exposes the current state, making it one of the most common mechanisms for sharing reactive data across Angular applications. + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [Forms](../03-intermediate/15-forms.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [RxJS Operators Deep Dive](17-rxjs-operators-deep-dive.md) + +
+ diff --git a/apps/portal/public/assets/content/04-reactivity/17-rxjs-operators-deep-dive.md b/apps/portal/public/assets/content/04-reactivity/17-rxjs-operators-deep-dive.md new file mode 100644 index 0000000..c29edc9 --- /dev/null +++ b/apps/portal/public/assets/content/04-reactivity/17-rxjs-operators-deep-dive.md @@ -0,0 +1,1818 @@ +# RxJS Operators Deep Dive + +## Interview Priority + +**Must Know** + +## Interview Frequency + +**Extremely Common** + +## Recommended Depth + +**Expert Level Understanding** + +## Relevant For + +- Mid-Level Interviews +- Senior Developer Interviews +- Lead Developer Interviews +- Architect Discussions + +--- + +# Why Do RxJS Operators Exist? + +Before learning operators, let's understand the problem. + +Suppose a user types: + +```text +A +Ad +Ada +Adar +Adars +Adarsh +``` + +This is not: + +```text +One Value +``` + +It is: + +```text +A Stream Of Values +``` + +Visual: + +```text +A + ↓ + +Ad + ↓ + +Ada + ↓ + +Adar + ↓ + +Adars + ↓ + +Adarsh +``` + +RxJS was created to process such streams. + +Operators are simply functions that allow us to: + +```text +Transform Streams + +Filter Streams + +Combine Streams + +Control Streams + +Handle Errors + +Manage Concurrency +``` + +--- + +# Mental Model For Every Operator + +Imagine water flowing through a pipe. + +Without operators: + +```text +Source + ↓ +Output +``` + +With operators: + +```text +Source + ↓ + +Filter + ↓ + +Transform + ↓ + +Output +``` + +Operators are processing steps inside a stream pipeline. + +--- + +# What Is pipe()? + +Operators are typically chained using: + +```typescript +pipe() +``` + +Example: + +```typescript +observable.pipe( + map(...), + filter(...), + tap(...) +); +``` + +Think of pipe as: + +```text +Stream Processing Pipeline +``` + +--- + +# Operator Categories + +Rather than memorizing operators individually: + +```text +Transformation + map() + +Filtering + filter() + take() + first() + +Side Effects + tap() + +Search Optimization + debounceTime() + distinctUntilChanged() + +Request Flattening + switchMap() + mergeMap() + concatMap() + exhaustMap() + +Stream Combination + combineLatest() + forkJoin() + +Error Handling + catchError() + retry() + +Subscription Control + takeUntil() +``` + +--- + +# map() + +## Purpose + +Transform values from one form into another. + +--- + +# First Principle + +Input Stream: + +```text +1 +2 +3 +``` + +Desired Output: + +```text +10 +20 +30 +``` + +--- + +# Example + +```typescript +of(1, 2, 3) +.pipe( + map(x => x * 10) +) +.subscribe(console.log); +``` + +Output: + +```text +10 +20 +30 +``` + +--- + +# Real Angular Example + +API returns: + +```json +{ + "data": [ + { + "id": 1, + "name": "Adarsh" + } + ] +} +``` + +But component only needs: + +```text +data +``` + +--- + +```typescript +this.http + .get('/api/employees') + .pipe( + map(response => response.data) + ); +``` + +--- + +# Mental Model + +```text +Input Value + ↓ + map() + ↓ +Output Value +``` + +--- + +# filter() + +## Purpose + +Remove unwanted values. + +--- + +# First Principle + +Input: + +```text +1 +2 +3 +4 +5 +``` + +Need: + +```text +3 +4 +5 +``` + +--- + +# Example + +```typescript +of(1, 2, 3, 4, 5) +.pipe( + filter(x => x > 2) +) +.subscribe(console.log); +``` + +Output: + +```text +3 +4 +5 +``` + +--- + +# Real Angular Example + +```typescript +users$ +.pipe( + filter(user => user.active) +); +``` + +Only active users continue through the pipeline. + +--- + +# Mental Model + +```text +Condition Met? + +Yes → Continue + +No → Discard +``` + +--- + +# tap() + +## Purpose + +Perform side effects without changing stream values. + +--- + +# First Principle + +Sometimes we want to: + +```text +Log Data + +Show Loader + +Track Analytics + +Debug Streams +``` + +without modifying values. + +--- + +# Example + +```typescript +of('Angular') +.pipe( + tap(value => console.log(value)) +) +.subscribe(); +``` + +Output: + +```text +Angular +``` + +--- + +# Important + +Input: + +```text +Angular +``` + +Output: + +```text +Angular +``` + +No transformation occurs. + +--- + +# Real Angular Example + +```typescript +this.http.get(...) +.pipe( + tap(() => { + this.loading = true; + }) +); +``` + +--- + +# Mental Model + +```text +Look At Value + +Do Something + +Pass Value Through +``` + +--- + +# take() + +## Purpose + +Take a fixed number of values and complete. + +--- + +# Example + +```typescript +of(1, 2, 3, 4, 5) +.pipe( + take(3) +) +.subscribe(console.log); +``` + +Output: + +```text +1 +2 +3 +``` + +--- + +# Common Angular Use + +```typescript +this.authService.user$ + .pipe( + take(1) + ) + .subscribe(user => { + }); +``` + +Read current value once and stop. + +--- + +# first() + +## Purpose + +Take first matching value and complete. + +--- + +# Example + +```typescript +of(10, 20, 30) +.pipe( + first() +) +.subscribe(console.log); +``` + +Output: + +```text +10 +``` + +--- + +# debounceTime() + +## Purpose + +Wait for user inactivity before emitting. + +--- + +# Real Problem + +User typing: + +```text +A +Ad +Ada +Adar +Adars +Adarsh +``` + +Without debounce: + +```text +6 HTTP Requests +``` + +--- + +# Example + +```typescript +this.searchControl + .valueChanges + .pipe( + debounceTime(500) + ); +``` + +Meaning: + +```text +Wait 500ms + +No New Value? + +Then Emit +``` + +--- + +# Timeline + +Without debounce: + +```text +A API + +Ad API + +Ada API + +Adar API +``` + +--- + +With debounce: + +```text +A +Ad +Ada +Adar + +(wait) + +Adar +``` + +Single request. + +--- + +# Common Use Cases + +```text +Search + +Autocomplete + +Filtering + +Type-Ahead Suggestions +``` + +--- + +# distinctUntilChanged() + +## Purpose + +Prevent duplicate consecutive values. + +--- + +# Problem + +Stream: + +```text +Angular +Angular +Angular +Angular +``` + +Should API be called 4 times? + +```text +No +``` + +--- + +# Example + +```typescript +of( + 'Angular', + 'Angular', + 'Angular' +) +.pipe( + distinctUntilChanged() +) +.subscribe(console.log); +``` + +Output: + +```text +Angular +``` + +--- + +# Mental Model + +```text +Same As Previous? + +Yes → Ignore + +No → Emit +``` + +--- + +# Common Search Pattern + +```typescript +this.searchControl.valueChanges +.pipe( + + debounceTime(500), + + distinctUntilChanged() + +) +``` + +This combination is extremely common. + +--- + +# switchMap() + +## Purpose + +Switch to a new Observable and cancel the previous one. + +--- + +# The Problem + +User types: + +```text +A +Ad +Ada +Adar +``` + +Each value triggers: + +```text +Search Request +``` + +Without control: + +```text +Request A + +Request Ad + +Request Ada + +Request Adar +``` + +All continue executing. + +What if: + +```text +Request A +``` + +returns last? + +Wrong results may be displayed. + +--- + +# Example + +```typescript +this.searchControl.valueChanges +.pipe( + switchMap(term => + this.employeeService.search(term) + ) +) +.subscribe(); +``` + +--- + +# What Happens? + +```text +New Value Arrives + ↓ + +Cancel Previous Request + ↓ + +Execute Latest Request +``` + +--- + +# Visual + +```text +Search A + + X Cancelled + +Search Ad + + X Cancelled + +Search Ada + + X Cancelled + +Search Adar + + ✓ Active +``` + +--- + +# Common Use Cases + +```text +Search + +Autocomplete + +Route Parameter Changes + +Live Filtering +``` + +--- + +# Interview Rule + +```text +Only Latest Result Matters +``` + +Use: + +```typescript +switchMap() +``` + +--- + +# concatMap() + +## Purpose + +Process requests sequentially. + +--- + +# Rule + +```text +Queue Requests + +One At A Time + +Preserve Order +``` + +--- + +# Example + +```typescript +source$ +.pipe( + concatMap(id => + this.api.get(id) + ) +); +``` + +--- + +# Timeline + +```text +Request A + +Wait + +Request B + +Wait + +Request C +``` + +--- + +# Production Example + +```text +Create Order + +Create Invoice + +Create Shipment +``` + +All operations must execute in order. + +--- + +# Mental Model + +```text +Queue +``` + +--- + +# mergeMap() + +## Purpose + +Execute multiple Observables concurrently. + +--- + +# Rule + +```text +No Cancellation + +No Waiting + +Run Everything +``` + +--- + +# Example + +```typescript +source$ +.pipe( + mergeMap(id => + this.api.get(id) + ) +); +``` + +--- + +# Timeline + +```text +Request A + +Request B + +Request C + +All Run Together +``` + +--- + +# Use Cases + +```text +Independent API Calls + +Background Processing + +Bulk Operations +``` + +--- + +# Mental Model + +```text +Parallel Execution +``` + +--- + +# exhaustMap() + +## Purpose + +Ignore new requests while current request is running. + +--- + +# Example + +```text +User Clicks Submit +User Clicks Submit +User Clicks Submit +``` + +Without control: + +```text +3 Form Submissions +``` + +--- + +# Example + +```typescript +submitClicks$ +.pipe( + exhaustMap(() => + this.saveForm() + ) +); +``` + +--- + +# Result + +```text +1st Click Executes + +Remaining Clicks Ignored +``` + +until completion. + +--- + +# Common Use Cases + +```text +Login Buttons + +Payment Processing + +Form Submission +``` + +--- + +# Operator Comparison + +## switchMap + +```text +Cancel Previous + +Keep Latest +``` + +Example: + +```text +Search +``` + +--- + +## concatMap + +```text +Queue Requests +``` + +Example: + +```text +Order Processing +``` + +--- + +## mergeMap + +```text +Run Everything +``` + +Example: + +```text +Independent Requests +``` + +--- + +## exhaustMap + +```text +Ignore New Requests +``` + +Example: + +```text +Prevent Double Submit +``` + +--- + +# combineLatest() + +## Purpose + +Combine latest values from multiple streams. + +--- + +# Problem + +Need: + +```text +Current User + +Current Theme +``` + +at the same time. + +--- + +# Example + +```typescript +combineLatest([ + user$, + theme$ +]) +.subscribe( + ([user, theme]) => { + + }); +``` + +--- + +# Result + +Whenever either changes: + +```text +Latest User + ++ + +Latest Theme +``` + +are emitted. + +--- + +# Mental Model + +```text +Latest Value +From Each Stream +``` + +--- + +# Real Example + +```text +Filters + +User Preferences + +Settings + +Dashboard Data +``` + +from multiple streams. + +--- + +# forkJoin() + +## Purpose + +Wait for all Observables to complete. + +--- + +# .NET Equivalent + +```csharp +Task.WhenAll(...) +``` + +--- + +# Example + +```typescript +forkJoin({ + + employees: + employeeApi(), + + departments: + departmentApi(), + + projects: + projectApi() + +}) +.subscribe(result => { + +}); +``` + +--- + +# Result + +```text +Wait For All Requests + +Then Emit One Result +``` + +--- + +# Common Use Cases + +```text +Page Initialization + +Dashboard Loading + +Reference Data Loading +``` + +--- + +# combineLatest vs forkJoin + +## combineLatest + +```text +Keeps Listening + +Re-Emits On Changes +``` + +--- + +## forkJoin + +```text +Waits Once + +Returns One Final Result +``` + +--- + +# catchError() + +## Purpose + +Recover gracefully from errors. + +--- + +# Example + +```typescript +this.http + .get( + '/api/employees' + ) + .pipe( + + catchError(error => { + + console.error(error); + + return of([]); + + }) + + ); +``` + +--- + +# Mental Model + +```text +Error Occurred + ↓ + +Handle Error + ↓ + +Continue Stream +``` + +--- + +# retry() + +## Purpose + +Retry failed operations. + +--- + +# Example + +```typescript +this.http.get(...) +.pipe( + retry(3) +); +``` + +--- + +# Flow + +```text +Attempt 1 + +Fail + +Attempt 2 + +Fail + +Attempt 3 + +Fail + +Give Up +``` + +--- + +# finalize() + +## Purpose + +Execute cleanup regardless of success or failure. + +--- + +# Example + +```typescript +this.http.get(...) +.pipe( + + finalize(() => { + + this.loading = false; + + }) + +); +``` + +--- + +# Why Useful? + +Avoid duplicating: + +```typescript +loading = false +``` + +inside both: + +```text +Success + +Error +``` + +blocks. + +--- + +# takeUntil() + +## Purpose + +Automatically unsubscribe. + +--- + +# Traditional Approach + +```typescript +ngOnDestroy() { + + this.subscription + .unsubscribe(); + +} +``` + +--- + +# RxJS Approach + +```typescript +private destroy$ = + new Subject(); + +this.user$ + .pipe( + takeUntil( + this.destroy$ + ) + ) + .subscribe(); +``` + +--- + +Destroy: + +```typescript +ngOnDestroy() { + + this.destroy$.next(); + + this.destroy$.complete(); + +} +``` + +--- + +# Modern Angular Alternative + +```typescript +takeUntilDestroyed() +``` + +preferred in newer Angular versions. + +--- + +# Production Search Example + +One of the most common Angular examples. + +```typescript +this.searchControl + .valueChanges + .pipe( + + debounceTime(500), + + distinctUntilChanged(), + + switchMap(term => + + this.employeeService + .search(term) + + ), + + catchError(() => + of([]) + ) + + ) + .subscribe(results => { + + this.results = results; + + }); +``` + +--- + +# Why This Is Considered Senior Code + +Problems solved: + +```text +Too Many Requests + ↓ +debounceTime() + +Duplicate Requests + ↓ +distinctUntilChanged() + +Race Conditions + ↓ +switchMap() + +Failures + ↓ +catchError() +``` + +Declarative and maintainable. + +--- + +# Operator Cheat Sheet + +## Transformation + +```typescript +map() +``` + +--- + +## Filtering + +```typescript +filter() + +take() + +first() +``` + +--- + +## Side Effects + +```typescript +tap() +``` + +--- + +## Search Optimization + +```typescript +debounceTime() + +distinctUntilChanged() +``` + +--- + +## Request Handling + +```typescript +switchMap() + +concatMap() + +mergeMap() + +exhaustMap() +``` + +--- + +## Combining Streams + +```typescript +combineLatest() + +forkJoin() +``` + +--- + +## Error Handling + +```typescript +catchError() + +retry() + +finalize() +``` + +--- + +## Cleanup + +```typescript +takeUntil() +``` + +--- + +# Common Interview Questions + +## What does map() do? + +Transforms stream values. + +--- + +## Difference between map() and tap()? + +map(): + +```text +Changes Data +``` + +tap(): + +```text +Does Not Change Data +``` + +--- + +## Why use debounceTime()? + +Reduce unnecessary emissions. + +--- + +## What does distinctUntilChanged() do? + +Suppress consecutive duplicate values. + +--- + +## When should switchMap() be used? + +When only the latest result matters. + +Example: + +```text +Search +``` + +--- + +## Difference between switchMap and mergeMap? + +switchMap: + +```text +Cancels Previous +``` + +mergeMap: + +```text +Runs Everything +``` + +--- + +## Difference between combineLatest and forkJoin? + +combineLatest: + +```text +Continuous Updates +``` + +forkJoin: + +```text +One Final Combined Result +``` + +--- + +## What does catchError() do? + +Handles stream errors gracefully. + +--- + +# Senior-Level Mental Model + +Do not think: + +```text +RxJS Operators +``` + +Think: + +```text +Data Processing Pipeline +``` + +```text +Input Stream + ↓ + +Transform + ↓ + +Filter + ↓ + +Optimize + ↓ + +Combine + ↓ + +Handle Errors + ↓ + +Output Stream +``` + +That mindset is the foundation of reactive programming. + +--- + +# Key Takeaways + +1. Operators process and control data streams. +2. `pipe()` creates a stream processing pipeline. +3. `map()` transforms data. +4. `filter()` removes unwanted values. +5. `tap()` performs side effects without modifying data. +6. `debounceTime()` reduces rapid emissions. +7. `distinctUntilChanged()` removes consecutive duplicates. +8. `switchMap()` is ideal when only the latest result matters. +9. `concatMap()` preserves execution order. +10. `mergeMap()` runs streams concurrently. +11. `exhaustMap()` prevents duplicate concurrent operations. +12. `combineLatest()` combines multiple ongoing streams. +13. `forkJoin()` waits for all streams to complete. +14. `catchError()` and `retry()` improve resiliency. +15. RxJS operators enable declarative, maintainable reactive applications. + +--- + +# Interview Notes (Revision Version) + +## Search Pattern + +```typescript +valueChanges + .pipe( + debounceTime(500), + distinctUntilChanged(), + switchMap(...) + ) +``` + +--- + +## Transformation + +```typescript +map() +``` + +--- + +## Filtering + +```typescript +filter() +``` + +--- + +## Side Effects + +```typescript +tap() +``` + +--- + +## HTTP Request Patterns + +```typescript +switchMap() + +mergeMap() + +concatMap() + +exhaustMap() +``` + +--- + +## Combining Streams + +```typescript +combineLatest() + +forkJoin() +``` + +--- + +## Error Handling + +```typescript +catchError() + +retry() + +finalize() +``` + +--- + +## Cleanup + +```typescript +takeUntil() + +takeUntilDestroyed() +``` + +--- + +## Key Message + +RxJS operators are not isolated functions to memorize. They are composable stream-processing tools that transform, filter, orchestrate, combine, and control asynchronous data flows in Angular applications. + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [Subjects & BehaviorSubject](16-subjects-and-behaviorsubject.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [Common Enterprise RxJS Scenarios](17a-common-enterprise-rxjs-scenarios.md) + +
+ diff --git a/apps/portal/public/assets/content/04-reactivity/17a-common-enterprise-rxjs-scenarios.md b/apps/portal/public/assets/content/04-reactivity/17a-common-enterprise-rxjs-scenarios.md new file mode 100644 index 0000000..a5c3fed --- /dev/null +++ b/apps/portal/public/assets/content/04-reactivity/17a-common-enterprise-rxjs-scenarios.md @@ -0,0 +1,1376 @@ +# Common Enterprise RxJS Scenarios + +## Interview Priority + +**Must Know** + +## Interview Frequency + +**Extremely Common** + +## Recommended Depth + +**Expert Level Understanding** + +## Relevant For + +- Mid-Level Interviews +- Senior Developer Interviews +- Lead Developer Interviews +- Architect Discussions + +--- + +# Why This Chapter Exists + +Most Angular developers learn RxJS operators like: + +```typescript +switchMap() + +mergeMap() + +concatMap() + +exhaustMap() + +combineLatest() + +forkJoin() +``` + +but struggle to answer: + +```text +When should I use them? + +Why should I use them? + +What problem do they solve? +``` + +This chapter focuses on: + +```text +Real Business Problems + +Operator Selection + +Enterprise Use Cases + +Interview Thinking +``` + +The goal is to move from: + +```text +I know the syntax +``` + +to: + +```text +I know when and why to use it +``` + +--- + +# Scenario 1: Search Box / Type-Ahead Search + +## Business Requirement + +User types into a search box. + +Example: + +```text +A + +Ad + +Ada + +Adar + +Adars + +Adarsh +``` + +Need: + +```text +Avoid Too Many Requests + +Prevent Duplicate Calls + +Use Latest Search Only +``` + +--- + +## Bad Solution + +```typescript +this.searchControl.valueChanges + .subscribe(term => { + + this.employeeService + .search(term) + .subscribe(); + + }); +``` + +Problems: + +```text +Too Many Requests + +Race Conditions + +Nested Subscriptions +``` + +--- + +## Recommended Solution + +```typescript +this.searchControl.valueChanges + .pipe( + + debounceTime(500), + + distinctUntilChanged(), + + switchMap(term => + + this.employeeService + .search(term) + + ) + + ) + .subscribe(); +``` + +--- + +## Why? + +### debounceTime() + +```text +Reduce Requests +``` + +--- + +### distinctUntilChanged() + +```text +Ignore Duplicates +``` + +--- + +### switchMap() + +```text +Cancel Old Searches + +Keep Latest Search +``` + +--- + +## Interview Rule + +```text +Search + +Autocomplete + +Type Ahead + +Live Filtering + + ↓ + +switchMap() +``` + +--- + +# Scenario 2: Prevent Double Submit + +## Business Requirement + +User clicks: + +```text +Submit + +Submit + +Submit + +Submit +``` + +within one second. + +Need: + +```text +Only One Request +``` + +--- + +## Problem + +Without protection: + +```text +Duplicate Orders + +Duplicate Payments + +Duplicate Logins +``` + +--- + +## Recommended Solution + +```typescript +submitClicks$ +.pipe( + + exhaustMap(() => + + this.orderService + .submit() + + ) + +); +``` + +--- + +## What Happens? + +```text +First Request Starts + + ↓ + +Other Clicks Ignored + + ↓ + +Request Completes + + ↓ + +New Click Allowed +``` + +--- + +## Real Enterprise Examples + +```text +Login + +Payment Processing + +Booking Systems + +Order Submission + +Registration Forms +``` + +--- + +## Interview Rule + +```text +Prevent Duplicate Requests + +Prevent Double Submit + +Ignore Repeated Clicks + + ↓ + +exhaustMap() +``` + +--- + +# Scenario 3: File Upload Queue + +## Business Requirement + +User selects: + +```text +File1.pdf + +File2.pdf + +File3.pdf +``` + +All files must upload: + +```text +One At A Time + +In Exact Order +``` + +--- + +## Recommended Solution + +```typescript +files$ +.pipe( + + concatMap(file => + + this.uploadService + .upload(file) + + ) + +); +``` + +--- + +## Execution Order + +```text +Upload File1 + + ↓ + +Upload File2 + + ↓ + +Upload File3 +``` + +--- + +## Why Not mergeMap? + +Because: + +```text +Ordering Matters +``` + +--- + +## Enterprise Examples + +```text +Document Processing + +Bulk Uploads + +Invoice Generation + +Workflow Approval Systems +``` + +--- + +## Interview Rule + +```text +Need Sequential Execution + +Need Ordered Processing + + ↓ + +concatMap() +``` + +--- + +# Scenario 4: Order Processing Workflow + +## Business Requirement + +Business process: + +```text +Create Order + + ↓ + +Create Invoice + + ↓ + +Create Shipment +``` + +Each step depends on the previous step. + +--- + +## Recommended Solution + +```typescript +steps$ +.pipe( + + concatMap(step => + + executeStep(step) + + ) + +); +``` + +--- + +## Why? + +Order must be preserved. + +--- + +## Interview Rule + +```text +Step 2 Depends On Step 1 + +Step 3 Depends On Step 2 + + ↓ + +concatMap() +``` + +--- + +# Scenario 5: Independent Background Operations + +## Business Requirement + +When customer registers: + +```text +Send Welcome Email + +Send SMS + +Create Audit Record +``` + +All operations are independent. + +--- + +## Recommended Solution + +```typescript +events$ +.pipe( + + mergeMap(event => + + processEvent(event) + + ) + +); +``` + +--- + +## Execution + +```text +Email + +SMS + +Audit + +Run Together +``` + +--- + +## Benefits + +```text +Faster + +Better Throughput + +No Waiting +``` + +--- + +## Enterprise Examples + +```text +Notifications + +Background Jobs + +Independent API Calls + +Bulk Processing +``` + +--- + +## Interview Rule + +```text +Everything Matters + +No Dependency Exists + +Run In Parallel + + ↓ + +mergeMap() +``` + +--- + +# Scenario 6: Dashboard Widget Loading + +## Business Requirement + +Dashboard requires: + +```text +Employees + +Departments + +Projects + +Notifications +``` + +Each API is independent. + +--- + +## Option 1 + +```typescript +mergeMap() +``` + +for processing multiple stream events. + +--- + +## Option 2 + +```typescript +forkJoin() +``` + +for waiting on multiple API calls. + +--- + +Example: + +```typescript +forkJoin({ + + employees: + this.employeeApi(), + + departments: + this.departmentApi(), + + projects: + this.projectApi() + +}); +``` + +--- + +## Why? + +All data required before rendering dashboard. + +--- + +## Interview Rule + +```text +Load Multiple APIs + +Wait For Everything + + ↓ + +forkJoin() +``` + +--- + +# Scenario 7: Dashboard Filters + +## Business Requirement + +Dashboard contains: + +```text +Search Text + +Department + +Location + +Status +``` + +Whenever any filter changes: + +```text +Refresh Results +``` + +--- + +## Recommended Solution + +```typescript +combineLatest([ + + search$, + + department$, + + location$, + + status$ + +]); +``` + +--- + +## Example + +Initial State: + +```text +Search = Angular + +Department = IT + +Location = Bengaluru +``` + +--- + +Output: + +```text +[Angular, IT, Bengaluru] +``` + +--- + +Location Changes: + +```text +Mumbai +``` + +Output: + +```text +[Angular, IT, Mumbai] +``` + +--- + +## Why? + +Need latest values from all streams. + +--- + +## Enterprise Examples + +```text +Filters + +Settings + +Preferences + +Dashboards +``` + +--- + +## Interview Rule + +```text +Need Latest Values + +From Multiple Streams + + ↓ + +combineLatest() +``` + +--- + +# Scenario 8: Current User + Theme + Language + +## Business Requirement + +Application needs: + +```text +Current User + +Current Theme + +Current Language +``` + +to render UI. + +--- + +## Solution + +```typescript +combineLatest([ + + user$, + + theme$, + + language$ + +]); +``` + +--- + +## Result + +Whenever any value changes: + +```text +Entire Combined State +``` + +updates automatically. + +--- + +## Interview Rule + +```text +Reactive Application State + + ↓ + +combineLatest() +``` + +--- + +# Scenario 9: Initial Page Load + +## Business Requirement + +Application startup requires: + +```text +Current User + +Permissions + +Feature Flags + +Reference Data +``` + +Screen should load only after everything returns. + +--- + +## Recommended Solution + +```typescript +forkJoin({ + + user: + this.userApi(), + + permissions: + this.permissionApi(), + + features: + this.featureApi(), + + lookup: + this.lookupApi() + +}); +``` + +--- + +## What Happens? + +```text +User Done? Wait + +Permissions Done? Wait + +Features Done? Wait + +Lookup Done? Wait + +--------------------- + +All Complete + +Return Result +``` + +--- + +## .NET Mental Model + +```csharp +Task.WhenAll(...) +``` + +--- + +## Interview Rule + +```text +Need Everything First + + ↓ + +forkJoin() +``` + +--- + +# Scenario 10: Route Parameter Changes + +## Business Requirement + +User navigates rapidly: + +```text +Employee/1 + +Employee/2 + +Employee/3 +``` + +Need: + +```text +Display Employee 3 +``` + +only. + +--- + +## Recommended Solution + +```typescript +this.route.params + .pipe( + + switchMap(params => + + this.employeeService + .getEmployee( + params['id'] + ) + + ) + + ); +``` + +--- + +## Why? + +Previous requests become irrelevant. + +--- + +## Interview Rule + +```text +Only Latest Result Matters + + ↓ + +switchMap() +``` + +--- + +# Scenario 11: API Response Transformation + +## Business Requirement + +API returns: + +```json +{ + "data": [], + "message": "" +} +``` + +Component only needs: + +```text +data +``` + +--- + +## Recommended Solution + +```typescript +map(response => response.data) +``` + +--- + +## Interview Rule + +```text +Transform Data + + ↓ + +map() +``` + +--- + +# Scenario 12: Logging And Analytics + +## Business Requirement + +Need: + +```text +Log Response + +Track Analytics + +Show Loader +``` + +without modifying stream. + +--- + +## Recommended Solution + +```typescript +tap(response => { + + console.log(response); + +}); +``` + +--- + +## Interview Rule + +```text +Need Side Effects + + ↓ + +tap() +``` + +--- + +# Scenario 13: Temporary Network Failure + +## Business Requirement + +API occasionally fails. + +Need automatic retry. + +--- + +## Solution + +```typescript +retry(3) +``` + +--- + +## Example + +```text +Attempt 1 + +Fail + +Attempt 2 + +Fail + +Attempt 3 + +Success +``` + +--- + +## Interview Rule + +```text +Transient Failure + + ↓ + +retry() +``` + +--- + +# Scenario 14: Graceful Error Recovery + +## Business Requirement + +API fails. + +Need fallback value. + +--- + +## Solution + +```typescript +catchError(() => + + of([]) + +) +``` + +--- + +## Why? + +Application continues functioning. + +--- + +## Interview Rule + +```text +Recover From Error + + ↓ + +catchError() +``` + +--- + +# Scenario 15: Read Current State Once + +## Business Requirement + +Need current user only once. + +--- + +## Solution + +```typescript +this.authService.user$ + .pipe( + take(1) + ); +``` + +--- + +## Why? + +Receive value and complete automatically. + +--- + +## Interview Rule + +```text +Read Once + + ↓ + +take(1) +``` + +--- + +# Scenario 16: Auto Subscription Cleanup + +## Business Requirement + +Avoid memory leaks. + +--- + +## Modern Angular Solution + +```typescript +takeUntilDestroyed() +``` + +--- + +## Legacy RxJS Solution + +```typescript +takeUntil() +``` + +--- + +## Interview Rule + +```text +Cleanup + + ↓ + +takeUntilDestroyed() +``` + +--- + +# Operator Decision Framework + +## Need Latest Search Result? + +```typescript +switchMap() +``` + +--- + +## Need Sequential Processing? + +```typescript +concatMap() +``` + +--- + +## Need Parallel Processing? + +```typescript +mergeMap() +``` + +--- + +## Need To Ignore Duplicate Requests? + +```typescript +exhaustMap() +``` + +--- + +## Need Latest Values From Multiple Streams? + +```typescript +combineLatest() +``` + +--- + +## Need All Requests Completed First? + +```typescript +forkJoin() +``` + +--- + +## Need To Transform Data? + +```typescript +map() +``` + +--- + +## Need Side Effects? + +```typescript +tap() +``` + +--- + +## Need Search Optimization? + +```typescript +debounceTime() + +distinctUntilChanged() +``` + +--- + +## Need Error Recovery? + +```typescript +catchError() +``` + +--- + +## Need Retry Logic? + +```typescript +retry() +``` + +--- + +## Need Automatic Cleanup? + +```typescript +takeUntilDestroyed() +``` + +--- + +# Senior Interview Cheat Sheet + +```text +Search + ↓ +switchMap() + +Ordered Processing + ↓ +concatMap() + +Independent Processing + ↓ +mergeMap() + +Prevent Double Submit + ↓ +exhaustMap() + +Current Values From Multiple Streams + ↓ +combineLatest() + +Wait For Multiple APIs + ↓ +forkJoin() + +Transform Data + ↓ +map() + +Side Effects + ↓ +tap() + +Error Handling + ↓ +catchError() + +Retry Failures + ↓ +retry() +``` + +--- + +# Key Takeaways + +1. Operator selection is more important than operator syntax. +2. `switchMap()` is ideal when only the latest result matters. +3. `concatMap()` preserves execution order. +4. `mergeMap()` runs streams concurrently. +5. `exhaustMap()` prevents duplicate concurrent operations. +6. `combineLatest()` combines multiple ongoing streams. +7. `forkJoin()` waits for all streams to complete. +8. Most enterprise Angular applications use these operators daily. +9. Interviewers often ask for operator choice based on business requirements. +10. Thinking in terms of scenarios and constraints leads to correct operator selection. + +--- + +# Key Message + +Senior Angular developers do not memorize RxJS operators. They identify the business requirement, understand the desired execution behavior, and then select the operator that best matches that behavior. + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [RxJS Operators Deep Dive](17-rxjs-operators-deep-dive.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [Async Pipe & RxJS Template Patterns](17b-async-pipe-and-rxjs-template-patterns.md) + +
+ diff --git a/apps/portal/public/assets/content/04-reactivity/17b-async-pipe-and-rxjs-template-patterns.md b/apps/portal/public/assets/content/04-reactivity/17b-async-pipe-and-rxjs-template-patterns.md new file mode 100644 index 0000000..85181a0 --- /dev/null +++ b/apps/portal/public/assets/content/04-reactivity/17b-async-pipe-and-rxjs-template-patterns.md @@ -0,0 +1,1154 @@ +# Async Pipe & RxJS Template Patterns + +## Interview Priority + +**Must Know** + +## Interview Frequency + +**Very Common** + +## Recommended Depth + +**Senior Level Understanding** + +## Relevant For + +- Angular Applications +- RxJS +- HTTP Calls +- State Management +- Enterprise Applications +- Senior Developer Interviews + +--- + +# Why Does Async Pipe Exist? + +Before learning syntax, understand the problem. + +Suppose we have an Observable: + +```typescript +employees$ = + this.employeeService + .getEmployees(); +``` + +How do we display this data in the UI? + +--- + +# Traditional RxJS Approach + +Component: + +```typescript +employees: Employee[] = []; + +ngOnInit() { + + this.employeeService + .getEmployees() + .subscribe(data => { + + this.employees = data; + + }); + +} +``` + +Template: + +```html +@for(emp of employees; track emp.id) { + +
{{ emp.name }}
+ +} +``` + +--- + +# Problems + +```text +Manual subscribe() + +Manual state assignment + +Potential memory leaks + +More boilerplate + +Extra component properties +``` + +--- + +# Angular Solution + +Use: + +```html +async +``` + +pipe. + +--- + +# What Is Async Pipe? + +The Async Pipe is an Angular template pipe that: + +```text +Subscribes To Observable + +Receives Values + +Updates UI + +Unsubscribes Automatically +``` + +--- + +# Simplest Example + +Component: + +```typescript +employee$ = + this.employeeService + .getEmployee(101); +``` + +Template: + +```html +{{ (employee$ | async)?.name }} +``` + +--- + +# What Happens Internally? + +Angular performs: + +```typescript +employee$ + .subscribe(...) +``` + +for you. + +--- + +And when the component is destroyed: + +```typescript +unsubscribe() +``` + +is performed automatically. + +--- + +# Mental Model + +Without Async Pipe + +```text +Observable + +↓ + +subscribe() + +↓ + +Component Property + +↓ + +Template +``` + +--- + +With Async Pipe + +```text +Observable + +↓ + +Async Pipe + +↓ + +Template +``` + +--- + +# First Example + +Component + +```typescript +employee$ = + this.employeeService + .getEmployee(101); +``` + +--- + +Template + +```html +
+ + {{ (employee$ | async)?.name }} + +
+``` + +--- + +Result + +```text +Observable Data + +↓ + +Template +``` + +No manual subscription. + +--- + +# Why The Parentheses? + +Example: + +```html +(employee$ | async)?.name +``` + +Angular first evaluates: + +```html +employee$ | async +``` + +then: + +```typescript +.name +``` + +--- + +Without parentheses Angular may parse the expression incorrectly. + +--- + +# HTTP Request Example + +Very common enterprise scenario. + +--- + +Component + +```typescript +employees$ = + this.employeeService + .getEmployees(); +``` + +--- + +Template + +```html +@if(employees$ | async; as employees) { + + @for(emp of employees; + track emp.id) { + +
+ {{ emp.name }} +
+ + } + +} +``` + +--- + +# What Is "as employees"? + +This syntax stores emitted value locally. + +--- + +Equivalent Thinking + +```typescript +employees = + emittedValue; +``` + +--- + +Example + +```html +@if(employee$ | async; as employee) { + + {{ employee.name }} + +} +``` + +--- + +Much cleaner than: + +```html +{{ (employee$ | async)?.name }} +{{ (employee$ | async)?.email }} +{{ (employee$ | async)?.department }} +``` + +--- + +# Async Pipe With BehaviorSubject + +Very common. + +--- + +Service + +```typescript +private userSubject = + new BehaviorSubject( + null + ); + +user$ = + this.userSubject + .asObservable(); +``` + +--- + +Component + +```typescript +user$ = + this.authService.user$; +``` + +--- + +Template + +```html +@if(user$ | async; as user) { + + Welcome + + {{ user.name }} + +} +``` + +--- + +What Happens? + +Whenever: + +```typescript +userSubject.next(...) +``` + +executes, + +the template updates automatically. + +--- + +# Async Pipe With Route Parameters + +Very common senior-level pattern. + +--- + +Component + +```typescript +employee$ = + this.route.paramMap.pipe( + + map(params => + params.get('id') + ), + + switchMap(id => + + this.employeeService + .getById(id!) + + ) + + ); +``` + +--- + +Template + +```html +@if(employee$ | async; as employee) { + +

+ + {{ employee.name }} + +

+ +} +``` + +--- + +Benefits + +```text +No Manual Subscribe + +No Memory Leaks + +Cleaner Component +``` + +--- + +# Manual Subscribe vs Async Pipe + +## Manual Subscribe + +```typescript +employee!: Employee; + +ngOnInit() { + + this.employeeService + .getEmployee(101) + .subscribe(employee => { + + this.employee = + employee; + + }); + +} +``` + +--- + +Template + +```html +{{ employee.name }} +``` + +--- + +## Async Pipe + +```typescript +employee$ = + this.employeeService + .getEmployee(101); +``` + +--- + +Template + +```html +{{ (employee$ | async)?.name }} +``` + +--- + +# Which Is Better? + +For data displayed in templates: + +✅ Prefer Async Pipe + +--- + +Why? + +```text +Less Boilerplate + +Automatic Cleanup + +Better Readability +``` + +--- + +# Automatic Unsubscribe + +One of the biggest advantages. + +--- + +Without Async Pipe + +```typescript +private subscription?: Subscription; + +ngOnInit() { + + this.subscription = + this.employeeService + .getEmployees() + .subscribe(); + +} + +ngOnDestroy() { + + this.subscription + ?.unsubscribe(); + +} +``` + +--- + +With Async Pipe + +```html +employees$ | async +``` + +Angular does everything automatically. + +--- + +# Memory Leak Prevention + +Without cleanup: + +```text +Component Destroyed + +↓ + +Subscription Still Alive + +↓ + +Memory Leak +``` + +--- + +Async Pipe prevents this. + +--- + +# Async Pipe With Multiple Observables + +Example + +```typescript +employees$ = ...; + +projects$ = ...; +``` + +--- + +Template + +```html +@if(employees$ | async; as employees) { + +} + +@if(projects$ | async; as projects) { + +} +``` + +--- + +# Loading State Pattern + +Common enterprise use case. + +--- + +Component + +```typescript +employees$ = + this.employeeService + .getEmployees(); +``` + +--- + +Template + +```html +@if(employees$ | async; as employees) { + + @for(emp of employees; + track emp.id) { + + } + +} +@else { + + Loading... + +} +``` + +--- + +# Async Pipe With Signals + +Important modern Angular discussion. + +--- + +Signals don't need Async Pipe. + +--- + +Signal + +```typescript +user = + signal(null); +``` + +--- + +Template + +```html +{{ user()?.name }} +``` + +--- + +Not: + +```html +user | async +``` + +--- + +# Mental Model + +Observable + +```typescript +employee$ +``` + +Template + +```html +employee$ | async +``` + +--- + +Signal + +```typescript +employee +``` + +Template + +```html +employee() +``` + +--- + +# Common Enterprise Pattern + +HTTP + +↓ + +Observable + +↓ + +Async Pipe + +↓ + +Template + +--- + +Example + +```typescript +employees$ = + this.employeeService + .getEmployees(); +``` + +Template + +```html +@if(employees$ | async; as employees) { + +} +``` + +--- + +# When Subscribe Is Still Necessary + +Important interview topic. + +Async Pipe is not a replacement for every subscribe. + +--- + +Suppose: + +```text +Show Notification + +Navigate + +Write To Service + +Save Data + +Call Analytics +``` + +These are actions. + +--- + +Example + +```typescript +this.employeeService + .save(employee) + .subscribe(() => { + + this.router.navigate( + ['/employees'] + ); + + }); +``` + +--- + +Here: + +```typescript +subscribe() +``` + +is appropriate. + +--- + +Why? + +Because: + +```text +Business Logic + +Needs To Execute +``` + +--- + +# Rule Of Thumb + +## Rendering Data + +Prefer: + +```html +| async +``` + +--- + +## Performing Actions + +Prefer: + +```typescript +.subscribe(...) +``` + +--- + +# Common Patterns + +## Pattern 1 + +Display List + +```typescript +employees$ +``` + +↓ + +```html +employees$ | async +``` + +--- + +## Pattern 2 + +Current User + +```typescript +user$ +``` + +↓ + +```html +user$ | async +``` + +--- + +## Pattern 3 + +Route-Based Loading + +```typescript +paramMap + ↓ +switchMap() + ↓ +HTTP +``` + +↓ + +```html +| async +``` + +--- + +# Common Interview Questions + +## What Is Async Pipe? + +Angular pipe that subscribes to Observables and Promises in templates. + +--- + +## Does Async Pipe Subscribe Automatically? + +Yes. + +--- + +## Does Async Pipe Unsubscribe Automatically? + +Yes. + +--- + +## Why Use Async Pipe? + +```text +Less Boilerplate + +Cleaner Components + +Memory Leak Prevention +``` + +--- + +## Async Pipe vs Subscribe? + +Async Pipe: + +```text +Display Data +``` + +--- + +Subscribe: + +```text +Execute Logic +``` + +--- + +## Can Async Pipe Be Used With Signals? + +No need. + +Signals are read using: + +```typescript +signal() +``` + +syntax. + +--- + +# Common Interview Traps + +## Trap 1 + +Using subscribe only to populate template variables. + +Prefer: + +```html +| async +``` + +--- + +## Trap 2 + +Thinking Async Pipe Works With Signals. + +Signals use: + +```typescript +user() +``` + +instead. + +--- + +## Trap 3 + +Using multiple: + +```html +employee$ | async +``` + +expressions repeatedly. + +Prefer: + +```html +@if(employee$ | async; as employee) +``` + +--- + +# Senior-Level Mental Model + +Think: + +```text +Observable + +↓ + +Async Pipe + +↓ + +Template +``` + +and + +```text +Observable + +↓ + +Subscribe + +↓ + +Business Logic +``` + +--- + +# Decision Framework + +Need To Render Data? + +↓ + +Use: + +```html +| async +``` + +--- + +Need To Perform An Action? + +↓ + +Use: + +```typescript +subscribe() +``` + +--- + +Need Reactive UI? + +↓ + +Use: + +```html +| async +``` + +--- + +Need Navigation / Analytics / Save Logic? + +↓ + +Use: + +```typescript +subscribe() +``` + +--- + +# Key Takeaways + +1. Async Pipe subscribes to Observables automatically. +2. Async Pipe unsubscribes automatically. +3. Async Pipe reduces boilerplate. +4. Async Pipe helps prevent memory leaks. +5. Async Pipe is ideal for template rendering. +6. Manual subscriptions are still useful for business logic. +7. Async Pipe works naturally with RxJS streams. +8. Signals do not require Async Pipe. +9. Prefer Async Pipe when displaying Observable data. +10. Prefer subscribe() when imperative actions must occur. + +--- + +# Interview Notes (Revision Version) + +## Display Observable Data + +```html +| async +``` + +--- + +## Execute Logic + +```typescript +subscribe() +``` + +--- + +## Automatic Subscribe + +✅ + +--- + +## Automatic Unsubscribe + +✅ + +--- + +## Works With Observables + +✅ + +--- + +## Needed For Signals + +❌ + +--- + +## Signal Access + +```typescript +user() +``` + +--- + +## Key Message + +The Async Pipe is Angular's preferred way to consume Observable values in templates because it automatically manages subscriptions, updates the UI reactively, and prevents memory leaks while keeping components cleaner and easier to maintain. + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [Common Enterprise RxJS Scenarios](17a-common-enterprise-rxjs-scenarios.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [Signals](18-signals.md) + +
+ diff --git a/apps/portal/public/assets/content/04-reactivity/18-signals.md b/apps/portal/public/assets/content/04-reactivity/18-signals.md new file mode 100644 index 0000000..e17f6ce --- /dev/null +++ b/apps/portal/public/assets/content/04-reactivity/18-signals.md @@ -0,0 +1,1683 @@ +# Signals + +## Interview Priority + +**Must Know (Angular 16+)** + +## Interview Frequency + +**Very Common** + +## Recommended Depth + +**Expert Level Understanding** + +## Relevant For + +- Mid-Level Interviews +- Senior Developer Interviews +- Lead Developer Interviews +- Modern Angular Applications + +--- + +# Why Were Signals Introduced? + +This is the most important Signals question. + +Before learning syntax, understand the motivation. + +--- + +# The Traditional Angular Approach + +Simple component state: + +```typescript +counter = 0; +``` + +works fine. + +But as applications grow: + +```text +Navbar + +Dashboard + +Profile + +Settings +``` + +need shared state. + +Developers historically used: + +```typescript +BehaviorSubject +``` + +and RxJS. + +--- + +# Traditional BehaviorSubject Example + +```typescript +private userSubject = + new BehaviorSubject(null); + +user$ = + this.userSubject.asObservable(); +``` + +Update: + +```typescript +this.userSubject.next(user); +``` + +Read: + +```typescript +this.user$ + .subscribe(user => { + this.user = user; + }); +``` + +--- + +# Problems Angular Observed + +A lot of code looked like: + +```typescript +private dataSubject = + new BehaviorSubject(...); + +data$ = + dataSubject.asObservable(); + +updateData() { + dataSubject.next(...); +} +``` + +Along with: + +```typescript +.subscribe() + +.unsubscribe() + +takeUntil() + +takeUntilDestroyed() +``` + +For simple UI state this felt excessive. + +--- + +# Angular's Question + +For things like: + +```text +Current User + +Current Theme + +Loading State + +Selected Employee + +Current Tab +``` + +Do we really need: + +```text +BehaviorSubject + +Observable + +Subscription + +Cleanup +``` + +? + +Angular answered: + +```text +No +``` + +Signals were introduced as a simpler reactive primitive. + +--- + +# What Is A Signal? + +A Signal is: + +> A reactive value that Angular automatically tracks. + +Think of it as: + +```text +Reactive Variable +``` + +--- + +# Traditional Variable + +```typescript +count = 0; +``` + +--- + +# Signal + +```typescript +count = signal(0); +``` + +Both hold values. + +The difference: + +```text +Angular Tracks Signal Dependencies +``` + +automatically. + +--- + +# Creating A Signal + +```typescript +import { + signal +} from '@angular/core'; + +count = signal(0); +``` + +--- + +# Reading A Signal + +Signals are read using: + +```typescript +count() +``` + +Notice: + +```text +Function Call Syntax +``` + +not: + +```typescript +count +``` + +--- + +# Example + +```typescript +count = signal(10); + +console.log( + count() +); +``` + +Output: + +```text +10 +``` + +--- + +# Why Are Signals Functions? + +A common interview question. + +--- + +# Traditional Variable + +```typescript +count +``` + +Angular cannot easily track reads. + +--- + +# Signal + +```typescript +count() +``` + +Angular can track: + +```text +Who Read This Signal + +When It Was Read +``` + +This enables automatic dependency tracking. + +--- + +# Updating Signals + +Signals are immutable from the outside. + +Updates occur through dedicated APIs. + +--- + +# set() + +Replace the current value. + +```typescript +count.set(25); +``` + +--- + +# Example + +```typescript +count = signal(10); + +count.set(20); +``` + +Result: + +```text +20 +``` + +--- + +# Visual + +```text +10 + +↓ + +set(20) + +↓ + +20 +``` + +--- + +# update() + +Calculate a new value from the previous value. + +--- + +Example: + +```typescript +count.update( + value => value + 1 +); +``` + +--- + +Equivalent to: + +```typescript +count++; +``` + +in traditional code. + +--- + +# Example + +```typescript +count = signal(5); + +count.update( + value => value + 5 +); +``` + +Result: + +```text +10 +``` + +--- + +# Why update() Exists + +Without update: + +```typescript +count.set( + count() + 1 +); +``` + +Works. + +But: + +```typescript +update() +``` + +is cleaner and expresses intent more clearly. + +--- + +# Simple Counter Example + +```typescript +count = signal(0); + +increment() { + + this.count.update( + value => value + 1 + ); + +} +``` + +Template: + +```html +

{{ count() }}

+ + +``` + +--- + +# Signals As Reactive State + +Traditional state: + +```typescript +loading = false; +``` + +--- + +Signal state: + +```typescript +loading = signal(false); +``` + +--- + +Update: + +```typescript +loading.set(true); +``` + +--- + +Template: + +```html +@if (loading()) { + +

Loading...

+ +} +``` + +--- + +# Real-Time Use Case: Loading State + +One of the most common enterprise examples. + +--- + +Traditional + +```typescript +loading = false; +``` + +--- + +Signal + +```typescript +loading = + signal(false); +``` + +--- + +Before Request + +```typescript +loading.set(true); +``` + +--- + +After Request + +```typescript +loading.set(false); +``` + +--- + +Template + +```html +@if(loading()) { + + + +} +``` + +Perfect use case. + +--- + +# Real-Time Use Case: Selected Employee + +Employee list screen: + +```text +Employee List + +Employee Details +``` + +Need to track selected employee. + +--- + +```typescript +selectedEmployee = + signal( + null + ); +``` + +--- + +User Clicks Employee + +```typescript +selectEmployee( + employee: Employee +) { + + this.selectedEmployee + .set(employee); + +} +``` + +--- + +Template + +```html +@if(selectedEmployee()) { + + + + +} +``` + +--- + +# Real-Time Use Case: Active Tab + +```typescript +activeTab = + signal('overview'); +``` + +--- + +Update + +```typescript +activeTab.set('settings'); +``` + +--- + +Use + +```html +@if(activeTab() === 'settings') { + +} +``` + +Very common dashboard pattern. + +--- + +# Computed Signals + +One of the most important Signals concepts. + +--- + +# Problem + +Suppose: + +```typescript +firstName = + signal('Adarsh'); + +lastName = + signal('Pawaskar'); +``` + +Need: + +```text +Full Name +``` + +--- + +Bad approach: + +```typescript +fullName = + firstName() + + ' ' + + lastName(); +``` + +This is not reactive. + +--- + +# Solution + +```typescript +fullName = computed( + + () => + + firstName() + + ' ' + + lastName() + +); +``` + +--- + +# What Happens? + +Whenever: + +```typescript +firstName +``` + +changes + +or + +```typescript +lastName +``` + +changes, + +Angular automatically recomputes: + +```typescript +fullName +``` + +--- + +# Visual + +```text +firstName + + ↓ + + ┐ + + │ + +computed() + + │ + + ┘ + +lastName +``` + +--- + +# Example + +```typescript +firstName.set('John'); +``` + +Result: + +```text +John Pawaskar +``` + +without additional code. + +--- + +# Mental Model + +Computed Signals represent: + +```text +Derived State +``` + +--- + +# Real-Time Use Case: Cart Total + +E-commerce application. + +--- + +Signals + +```typescript +items = + signal([]); +``` + +Need: + +```text +Cart Total +``` + +--- + +Computed + +```typescript +total = computed(() => + + items() + .reduce( + (sum, item) => + sum + + item.price, + 0 + ) + +); +``` + +--- + +Whenever cart items change: + +```text +Total Automatically Updates +``` + +--- + +# Real-Time Use Case: Full Name + +```typescript +firstName = + signal(''); + +lastName = + signal(''); +``` + +--- + +Computed: + +```typescript +fullName = + computed(() => + + firstName() + + ' ' + + lastName() + + ); +``` + +--- + +Forms and profile pages use this frequently. + +--- + +# Effects + +The third core Signals API. + +--- + +# What Problem Do Effects Solve? + +Sometimes we need to: + +```text +Log Data + +Track Analytics + +Store Data + +React To Changes +``` + +when Signals change. + +--- + +# Effect Example + +```typescript +effect(() => { + + console.log( + count() + ); + +}); +``` + +--- + +Whenever: + +```typescript +count +``` + +changes, + +effect automatically reruns. + +--- + +# Example + +```typescript +count.set(1); + +count.set(2); + +count.set(3); +``` + +Output: + +```text +1 + +2 + +3 +``` + +--- + +# Mental Model + +```text +Signal Changes + + ↓ + +Effect Runs +``` + +--- + +# Real-Time Use Case: Persist Theme + +```typescript +theme = + signal('light'); +``` + +Store choice. + +--- + +Effect + +```typescript +effect(() => { + + localStorage.setItem( + 'theme', + theme() + ); + +}); +``` + +--- + +Whenever theme changes: + +```text +Storage Automatically Updates +``` + +--- + +# Real-Time Use Case: Analytics + +```typescript +selectedEmployee = + signal( + null + ); +``` + +--- + +Effect + +```typescript +effect(() => { + + console.log( + selectedEmployee() + ); + +}); +``` + +Used for: + +```text +Logging + +Tracking + +Auditing +``` + +--- + +# The Three Core Signal APIs + +## signal() + +Stores state. + +--- + +Example + +```typescript +count = + signal(0); +``` + +--- + +## computed() + +Derives state. + +--- + +Example + +```typescript +fullName = + computed(...); +``` + +--- + +## effect() + +Reacts to state. + +--- + +Example + +```typescript +effect(...); +``` + +--- + +# Enterprise Example: Theme Service + +Before Signals: + +```typescript +private themeSubject = + new BehaviorSubject( + 'light' + ); +``` + +--- + +With Signals: + +```typescript +theme = + signal('light'); +``` + +--- + +Update + +```typescript +theme.set('dark'); +``` + +--- + +Read + +```typescript +theme() +``` + +Much simpler. + +--- + +# Enterprise Example: Authentication State + +Traditional: + +```typescript +private userSubject = + new BehaviorSubject( + null + ); +``` + +--- + +Signals + +```typescript +currentUser = + signal( + null + ); +``` + +--- + +Login + +```typescript +currentUser.set(user); +``` + +--- + +Logout + +```typescript +currentUser.set(null); +``` + +--- + +Component + +```typescript +authStore.currentUser() +``` + +No subscription required. + +--- + +# Enterprise Example: Feature Store + +```typescript +@Injectable({ + providedIn: 'root' +}) +export class EmployeeStore { + + employees = + signal([]); + + selectedEmployee = + signal( + null + ); + + loading = + signal(false); + +} +``` + +This is becoming increasingly common. + +--- + +# Signals vs BehaviorSubject + +One of the hottest Angular interview topics. + +--- + +# Similarities + +Both provide: + +```text +Current Value + +Reactive Updates + +State Management +``` + +--- + +# BehaviorSubject + +```typescript +private subject = + new BehaviorSubject( + value + ); +``` + +Requires: + +```text +Observable + +Subscription + +RxJS +``` + +--- + +# Signal + +```typescript +state = + signal(value); +``` + +Requires: + +```text +Nothing Else +``` + +--- + +# Comparison + +## Current Value + +BehaviorSubject + +✅ + +Signal + +✅ + +--- + +## Subscription Required + +BehaviorSubject + +✅ + +Signal + +❌ + +--- + +## RxJS Dependency + +BehaviorSubject + +✅ + +Signal + +❌ + +--- + +## Boilerplate + +BehaviorSubject + +High + +Signal + +Low + +--- + +## UI State + +BehaviorSubject + +Good + +Signal + +Excellent + +--- + +## Async Streams + +BehaviorSubject + +Excellent + +Signal + +Not Primary Purpose + +--- + +# Signals Do NOT Replace RxJS + +Extremely important interview point. + +--- + +# Use Signals For + +```text +UI State + +Loading State + +Modal Visibility + +Current User + +Theme + +Selected Entity + +Feature State +``` + +--- + +Examples + +```typescript +loading = + signal(false); + +theme = + signal('dark'); +``` + +--- + +# Use RxJS For + +```text +Http Requests + +WebSockets + +valueChanges + +Streaming Data + +Complex Async Pipelines +``` + +--- + +Example + +```typescript +valueChanges + .pipe( + debounceTime(500), + switchMap(...) + ) +``` + +Still RxJS. + +--- + +# Modern Angular Pattern + +Very common in Angular 17+. + +```text +HttpClient + + ↓ + +Observable + + ↓ + +Signal + + ↓ + +Template +``` + +--- + +Example + +```typescript +employees = + signal([]); +``` + +Fetch: + +```typescript +this.employeeService + .getEmployees() + .subscribe(data => { + + this.employees + .set(data); + + }); +``` + +--- + +# Signals And Change Detection + +Historically Angular relied heavily on: + +```text +Zone.js +``` + +and broad change detection. + +Signals allow Angular to know: + +```text +Exactly What Changed + +Exactly What Depends On It +``` + +leading to more efficient updates. + +--- + +# Common Interview Questions + +## What Is A Signal? + +A reactive value that Angular automatically tracks. + +--- + +## Why Were Signals Introduced? + +To simplify state management and reduce RxJS boilerplate for UI state. + +--- + +## How Do You Read A Signal? + +```typescript +count() +``` + +--- + +## How Do You Update A Signal? + +```typescript +count.set() + +count.update() +``` + +--- + +## What Is A Computed Signal? + +A derived value that automatically updates when dependencies change. + +--- + +## What Is An Effect? + +Code that runs whenever dependent Signals change. + +--- + +## Can Signals Replace BehaviorSubject? + +For many UI-state scenarios: + +```text +Yes +``` + +For asynchronous streams: + +```text +No +``` + +--- + +## Do Signals Replace RxJS? + +No. + +Both solve different problems. + +--- + +# Common Interview Traps + +## Trap 1 + +Thinking Signals replace HttpClient. + +Incorrect. + +HttpClient still returns: + +```typescript +Observable +``` + +--- + +## Trap 2 + +Thinking Signals replace RxJS entirely. + +Incorrect. + +RxJS remains critical for: + +```text +Http Calls + +WebSockets + +Reactive Forms + +Async Event Streams +``` + +--- + +## Trap 3 + +Using effect() for derived state. + +Prefer: + +```typescript +computed() +``` + +when deriving values. + +--- + +## Trap 4 + +Using Signals everywhere. + +Choose the right tool: + +```text +UI State + ↓ +Signal + +Async Streams + ↓ +RxJS +``` + +--- + +# Architecture Considerations + +## Prefer Signals For + +```text +Local Component State + +Feature State + +View State + +UI State +``` + +--- + +## Prefer RxJS For + +```text +Async Workflows + +Streams + +Socket Events + +Form Pipelines +``` + +--- + +## Common Modern Architecture + +```text +API + + ↓ + +Observable + + ↓ + +Store Signal + + ↓ + +Template +``` + +--- + +# Key Takeaways + +1. Signals are Angular's modern reactive state primitive. +2. A Signal is a reactive value Angular automatically tracks. +3. `signal()` stores state. +4. `computed()` derives state. +5. `effect()` reacts to state changes. +6. Signals reduce RxJS boilerplate. +7. Signals are ideal for UI and component state. +8. Signals do not replace RxJS. +9. RxJS remains the preferred solution for async streams. +10. Understanding BehaviorSubject makes Signals much easier to understand. + +--- + +# Interview Notes (Revision Version) + +## Store State + +```typescript +signal() +``` + +--- + +## Derived State + +```typescript +computed() +``` + +--- + +## Side Effects + +```typescript +effect() +``` + +--- + +## Read Value + +```typescript +state() +``` + +--- + +## Update Value + +```typescript +state.set() + +state.update() +``` + +--- + +## Use Signals For + +```text +UI State + +Current User + +Theme + +Loading + +Selection +``` + +--- + +## Use RxJS For + +```text +HTTP + +WebSockets + +valueChanges + +Search Pipelines +``` + +--- + +## Key Message + +Signals provide Angular's modern, lightweight, and reactive approach to UI state management, reducing the boilerplate traditionally associated with BehaviorSubject-based state while complementing, not replacing, RxJS. + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [Async Pipe & RxJS Template Patterns](17b-async-pipe-and-rxjs-template-patterns.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [Signal-Based Component APIs](18a-signal-based-component-apis.md) + +
+ diff --git a/apps/portal/public/assets/content/04-reactivity/18a-signal-based-component-apis.md b/apps/portal/public/assets/content/04-reactivity/18a-signal-based-component-apis.md new file mode 100644 index 0000000..8f3bd77 --- /dev/null +++ b/apps/portal/public/assets/content/04-reactivity/18a-signal-based-component-apis.md @@ -0,0 +1,1275 @@ +# Signal-Based Component APIs + +## Interview Priority + +**Must Know (Angular 17+)** + +## Interview Frequency + +**Increasing Rapidly** + +## Recommended Depth + +**Expert Level Understanding** + +## Relevant For + +- Modern Angular Development +- Angular 17+ +- Angular 18+ +- Senior Developer Interviews +- Lead Developer Interviews + +--- + +# Why Were Signal-Based Component APIs Introduced? + +Before Signals, Angular component communication was primarily built using: + +```typescript +@Input() + +@Output() + +EventEmitter + +ngOnChanges() +``` + +Example: + +```typescript +@Component({...}) +export class EmployeeCardComponent { + + @Input() + employee!: Employee; + + @Output() + delete = + new EventEmitter(); + +} +``` + +This worked well and is still fully supported. + +--- + +# Angular's Observation + +Signals introduced a new reactive model: + +```typescript +count = signal(0); +``` + +which provides: + +```text +Automatic Dependency Tracking + +Reactive Updates + +Computed Values + +Effects +``` + +However: + +```typescript +@Input() +employee!: Employee; +``` + +was still: + +```text +Property Based + +Not Signal Based +``` + +Angular wanted a unified reactive model. + +--- + +# Angular's Goal + +Make these APIs feel consistent: + +```typescript +signal() + +input() + +output() + +model() + +computed() + +effect() +``` + +All following the same reactive principles. + +--- + +# Traditional Component APIs + +For years Angular used: + +```typescript +@Input() + +@Output() +``` + +to communicate between components. + +--- + +# Parent To Child Communication + +Parent: + +```html + + +``` + +Child: + +```typescript +@Input() +employee!: Employee; +``` + +--- + +# Child To Parent Communication + +Child: + +```typescript +@Output() +save = + new EventEmitter(); +``` + +Emit: + +```typescript +this.save.emit(employee); +``` + +Parent: + +```html + + +``` + +--- + +# Signal-Based APIs + +Modern Angular introduces: + +```typescript +input() + +output() + +model() +``` + +--- + +# Overview + +## Parent → Child + +```typescript +input() +``` + +--- + +## Child → Parent + +```typescript +output() +``` + +--- + +## Two-Way Binding + +```typescript +model() +``` + +--- + +# input() + +Signal-based replacement for: + +```typescript +@Input() +``` + +--- + +# Traditional Input + +```typescript +@Input() +employee!: Employee; +``` + +--- + +# Signal Input + +```typescript +employee = + input.required(); +``` + +--- + +# What's Different? + +Traditional input: + +```typescript +employee +``` + +is simply a property. + +--- + +Signal input: + +```typescript +employee +``` + +is a Signal. + +Meaning: + +```typescript +employee() +``` + +returns the value. + +--- + +# Reading Input Values + +Traditional: + +```typescript +console.log( + this.employee.name +); +``` + +--- + +Signal Input: + +```typescript +console.log( + this.employee().name +); +``` + +--- + +# Why Is This Important? + +Signal inputs can participate directly in: + +```typescript +computed() + +effect() +``` + +This is one of their biggest advantages. + +--- + +# Simple Rendering Example + +Parent: + +```html + + +``` + +--- + +Child: + +```typescript +employee = + input.required(); +``` + +Template: + +```html +{{ employee().firstName }} +{{ employee().lastName }} +``` + +--- + +# Is Traditional @Input Already Reactive? + +Yes. + +Traditional code: + +```html +{{ employee.firstName }} +{{ employee.lastName }} +``` + +already updates when the parent passes a new employee. + +--- + +# Important Interview Point + +Signal inputs are not primarily solving: + +```text +Template Rendering +``` + +Traditional inputs already handle that well. + +--- + +Signal inputs become more valuable when: + +```text +Derived State + +Input-Based Logic + +Replacing ngOnChanges() + +Computed Values + +Effects +``` + +are involved. + +--- + +# Optional Inputs + +Traditional: + +```typescript +@Input() +employee?: Employee; +``` + +--- + +Signal Version + +```typescript +employee = + input(); +``` + +Value can be: + +```typescript +undefined +``` + +--- + +# Required Inputs + +Traditional: + +```typescript +@Input({ required: true }) +employee!: Employee; +``` + +--- + +Signal Version: + +```typescript +employee = + input.required(); +``` + +--- + +# computed() With Inputs + +One of the strongest use cases. + +--- + +# Real Problem + +Parent passes: + +```typescript +Employee +``` + +Child needs: + +```text +Display Name + +Full Name + +Formatted Address +``` + +derived from employee. + +--- + +# Traditional Approach + +```typescript +@Input() +employee!: Employee; + +get displayName() { + + return `${this.employee.id} + - ${this.employee.name}`; + +} +``` + +--- + +Or: + +```typescript +ngOnChanges() { + +} +``` + +--- + +# Signal Approach + +```typescript +employee = + input.required(); + +displayName = + computed(() => + + `${employee().id} + - ${employee().name}` + + ); +``` + +--- + +# What Happens? + +Parent updates: + +```typescript +employee +``` + +↓ + +Signal updates + +↓ + +Computed recalculates + +↓ + +UI updates + +Automatically. + +--- + +# Real Enterprise Example + +```typescript +employee = + input.required(); + +fullName = + computed(() => + + `${employee().firstName} + ${employee().lastName}` + + ); + +emailDisplay = + computed(() => + + employee().email + .toLowerCase() + + ); +``` + +No lifecycle hooks required. + +--- + +# Replacing ngOnChanges() + +One of the most important Angular modernization topics. + +--- + +# Traditional Workflow + +Input changes: + +```text +EmployeeId +``` + +Need: + +```text +Load Employee + +Update View + +Log Change +``` + +Traditionally: + +```typescript +@Input() +employeeId!: number; + +ngOnChanges( + changes: SimpleChanges +) { + + if(changes['employeeId']) { + + this.loadEmployee(); + + } + +} +``` + +--- + +# Problems + +```text +Extra Lifecycle Hook + +Manual Checks + +Verbose Code +``` + +--- + +# Signal Version + +```typescript +employeeId = + input.required(); + +effect(() => { + + const id = + employeeId(); + + console.log( + 'Loading Employee', + id + ); + +}); +``` + +No: + +```typescript +ngOnChanges() +``` + +required. + +--- + +# Mental Model + +Traditional: + +```text +Input Changes + + ↓ + +ngOnChanges() + + ↓ + +Execute Logic +``` + +--- + +Signal Version + +```text +Input Changes + + ↓ + +Signal Updates + + ↓ + +effect() + + ↓ + +Execute Logic +``` + +--- + +# Real Enterprise Example + +Parent passes: + +```typescript +employeeId +``` + +Child must load employee details. + +--- + +Traditional + +```typescript +@Input() +employeeId!: number; + +ngOnChanges() { + + this.loadEmployee( + this.employeeId + ); + +} +``` + +--- + +Signal Style + +```typescript +employeeId = + input.required(); + +effect(() => { + + this.loadEmployee( + employeeId() + ); + +}); +``` + +Cleaner reactive model. + +--- + +# Important Caution + +Avoid placing large subscription logic directly inside effects. + +Example: + +```typescript +effect(() => { + + this.employeeService + .getEmployee( + employeeId() + ) + .subscribe(); + +}); +``` + +Technically works. + +But for complex async flows: + +```text +RxJS Still Often Makes Sense +``` + +--- + +Typical solution: + +```typescript +employeeId = + input.required(); + +employee$ = + toObservable( + employeeId + ).pipe( + + switchMap(id => + + this.employeeService + .getEmployee(id) + + ) + + ); +``` + +Signals and RxJS frequently work together. + +--- + +# output() + +Signal-era replacement for: + +```typescript +@Output() +``` + +--- + +# Traditional Output + +```typescript +@Output() +delete = + new EventEmitter(); +``` + +--- + +Emit: + +```typescript +this.delete.emit(id); +``` + +--- + +# Signal Version + +```typescript +delete = + output(); +``` + +--- + +Emit: + +```typescript +this.delete.emit(id); +``` + +Same usage. + +--- + +# Parent Usage + +```html + + +``` + +No change. + +--- + +# Why Introduce output()? + +Benefits: + +```text +Consistent API Style + +Better Typing + +Aligned With Signals +``` + +--- + +# Real Enterprise Example + +```typescript +saveEmployee = + output(); + +save() { + + this.saveEmployee.emit( + + this.employeeForm.value + + ); + +} +``` + +--- + +# model() + +Perhaps the most powerful component API. + +--- + +# The Problem + +Traditional two-way binding requires: + +```typescript +@Input() + +@Output() +``` + +pair. + +--- + +# Traditional Child Component + +```typescript +@Input() +name!: string; + +@Output() +nameChange = + new EventEmitter(); +``` + +--- + +Update + +```typescript +this.nameChange.emit( + newValue +); +``` + +--- + +Parent + +```html + + +``` + +--- + +# Problem + +Lots of boilerplate. + +--- + +# Signal-Based Model + +```typescript +name = model(''); +``` + +That's it. + +--- + +# What Angular Creates + +Internally Angular provides: + +```text +Input Behavior + +Output Behavior + +Signal Behavior + +Two-Way Binding +``` + +All automatically. + +--- + +# Parent + +```html + + +``` + +--- + +# Child + +```typescript +name = model(''); +``` + +Update: + +```typescript +name.set( + 'Adarsh' +); +``` + +Parent automatically updates. + +--- + +# Mental Model + +```text +Input + + + + +Output + + + + +Signal + + = + +model() +``` + +--- + +# Real Enterprise Example + +Reusable Search Component + +--- + +Parent + +```html + + +``` + +--- + +Child + +```typescript +searchText = + model(''); +``` + +--- + +Input Changes + +```typescript +searchText.set( + value +); +``` + +Parent receives updates automatically. + +--- + +# Traditional vs Signal APIs + +## Input + +Traditional: + +```typescript +@Input() +employee!: Employee; +``` + +Signal: + +```typescript +employee = + input.required(); +``` + +--- + +## Output + +Traditional: + +```typescript +@Output() +save = + new EventEmitter(); +``` + +Signal: + +```typescript +save = + output(); +``` + +--- + +## Two-Way Binding + +Traditional: + +```typescript +@Input() +value!: string; + +@Output() +valueChange = + new EventEmitter(); +``` + +Signal: + +```typescript +value = + model(''); +``` + +--- + +# input() + computed() + +Powerful combination. + +--- + +Example + +```typescript +employee = + input.required(); + +displayName = + computed(() => + + `${employee().id} + - ${employee().name}` + + ); +``` + +--- + +# input() + effect() + +Powerful replacement for input change tracking. + +--- + +Example + +```typescript +employeeId = + input.required(); + +effect(() => { + + console.log( + employeeId() + ); + +}); +``` + +--- + +# When Should You Use Signal Component APIs? + +## Existing Angular Applications + +No urgent need to migrate. + +Traditional: + +```typescript +@Input() + +@Output() +``` + +remain fully supported. + +--- + +## New Angular Applications + +Consider: + +```typescript +input() + +output() + +model() +``` + +especially when using Signals elsewhere. + +--- + +# Common Interview Questions + +## What Is input()? + +Signal-based alternative to: + +```typescript +@Input() +``` + +--- + +## What Is output()? + +Modern alternative to: + +```typescript +@Output() +``` + +--- + +## What Is model()? + +Signal-based API that simplifies two-way binding. + +--- + +## Can Signal Inputs Replace ngOnChanges()? + +Often yes. + +Using: + +```typescript +effect() +``` + +or + +```typescript +computed() +``` + +instead. + +--- + +## Can Signal Inputs Be Used In computed()? + +Yes. + +This is one of their biggest advantages. + +--- + +## Does @Input() Still Work? + +Absolutely. + +Nothing has been deprecated. + +--- + +# Common Interview Traps + +## Trap 1 + +Thinking signal inputs are required. + +Incorrect. + +```typescript +@Input() +``` + +still + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [Signals](18-signals.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [NgModules Fundamentals](../05-advanced/19-ngmodules-fundamentals.md) + +
+ diff --git a/apps/portal/public/assets/content/05-advanced/19-ngmodules-fundamentals.md b/apps/portal/public/assets/content/05-advanced/19-ngmodules-fundamentals.md new file mode 100644 index 0000000..36c9723 --- /dev/null +++ b/apps/portal/public/assets/content/05-advanced/19-ngmodules-fundamentals.md @@ -0,0 +1,760 @@ +# NgModules Fundamentals + +## Interview Priority + +**Important** + +## Interview Frequency + +**Common** + +## Recommended Depth + +**Senior Level Understanding** + +## Relevant For + +- Legacy Angular Applications +- Enterprise Applications +- Angular 2-15 Era +- Migration Projects +- Senior Developer Interviews + +--- + +# First Principles + +Before Standalone Components existed, Angular needed a way to organize: + +```text +Components + +Directives + +Pipes + +Services + +Dependencies +``` + +Angular solved this problem using: + +```typescript +@NgModule() +``` + +Think of a module as: + +```text +Application Registry +``` + +that tells Angular: + +```text +What Exists + +What Can Be Used + +What Can Be Shared + +What Can Be Injected +``` + +--- + +# What Is An NgModule? + +Example: + +```typescript +@NgModule({ + + declarations: [], + + imports: [], + + exports: [], + + providers: [] + +}) +export class EmployeeModule { +} +``` + +Each section has a specific purpose. + +--- + +# NgModule Anatomy + +```typescript +@NgModule({ + + declarations: [], + + imports: [], + + exports: [], + + providers: [] + +}) +``` + +Remember: + +```text +Declarations + ↓ +I Created It + +Imports + ↓ +I Use It + +Exports + ↓ +Others Can Use It + +Providers + ↓ +Angular Can Inject It +``` + +--- + +# declarations + +## Purpose + +Register things that YOU created. + +--- + +# What Goes Into declarations? + +```typescript +@Component(...) +``` + +```typescript +@Directive(...) +``` + +```typescript +@Pipe(...) +``` + +--- + +Example + +```typescript +@Component(...) +export class EmployeeComponent {} + +@Component(...) +export class EmployeeDetailsComponent {} + +@Directive(...) +export class HighlightDirective {} + +@Pipe(...) +export class EmployeeFilterPipe {} +``` + +--- + +Register + +```typescript +@NgModule({ + + declarations: [ + + EmployeeComponent, + + EmployeeDetailsComponent, + + HighlightDirective, + + EmployeeFilterPipe + + ] + +}) +``` + +--- + +# Why Is declarations Needed? + +Suppose: + +```html + + +``` + +Question: + +```text +How does Angular know +what app-employee is? +``` + +Answer: + +```text +Because it was declared +inside a module. +``` + +--- + +# Mental Model + +```text +declarations + += + +Things I Created +``` + +--- + +# imports + +## Purpose + +Use things created somewhere else. + +--- + +Common examples: + +```typescript +CommonModule + +FormsModule + +ReactiveFormsModule + +RouterModule +``` + +--- + +Example + +```typescript +@NgModule({ + + imports: [ + + CommonModule, + + FormsModule, + + ReactiveFormsModule + + ] + +}) +``` + +--- + +# Why Is imports Needed? + +Template: + +```html +
+ +
+``` + +Where does: + +```typescript +*ngIf +``` + +*ome from? + +Not your component. + +It comes from: + +```typescript +CommonModule +``` + +Therefore: + +```typescript +imports: [ + CommonModule +] +``` + +--- + +# Another Example + +Template: + +```html + + +``` + +Requires: + +```typescript +ReactiveFormsModule +``` + +inside: + +```typescript +imports:[] +``` + +--- + +## Mental Model + +```text +imports + += + +Things Others Created + +That I Want To Use +``` + +--- + +# exports + +## Purpose + +Make things available to other modules. + +--- + +Example + +```typescript +@NgModule({ + declarations: [ + + EmployeeComponent + + ], + + exports: [ + + EmployeeComponent + + ] + +}) +``` + +--- + +Now other modules can use: + +```html + + +``` +--- + +# Why exports Exists + +Without exports: + +```text +Module A +``` + +cannot automatically use: + +```text +Module B Components +``` + +--- + +# Mental Model + +```text +exports + += + +Make Public +``` + +--- + +# providers + +Usually the most misunderstood section. +--- + +# Purpose + +Register services with Angular's Dependency Injection container. + +--- + +Example Service + +```typescript +@Injectable() +export class EmployeeService {} +``` + +--- + +*egister + +```typescript +@NgModule({ + providers: [ + + EmployeeService + + ] + +}) +``` + +--- + +# Why? + +Component: + +```typescript +constructor( + +* private employeeService: + EmployeeService + +}) +``` + +Angular asks: + +```text +Who Provides EmployeeService? +``` + +Answer: +```typescript +providers:[ + EmployeeService +] +``` + +--- + +# Mental Model + +```text +providers + += + +Dependency Injection Registrations +``` + +--- + +# Dependency Injection Flow + +```text +Component + + ↓ asks for + +EmployeeService + + ↓ + +Angular DI Container + + ↓ + +Provider Found + + ↓* +Service Created + + ↓ + +Service Injected +``` + +--- + +# Complete Example + +```typescript +@NgModule({ + + declarations: [ + + EmployeeComponent + ], + + imports: [ + + CommonModule, + + ReactiveFormsModule + + ], + providers: [ + + EmployeeService + + ] + +}) +export class EmployeeModule {} +``` + +--- + +# Feature Modules +Large applications commonly organize features into modules. + +--- + +Example + +```text +EmployeeModule + +AdminModule + +ReportsModule + +SettingsModule +``` + +--- + +Employee Module + +```typescript +@NgModule({ + + declarations: [ + + EmployeeListComponent, + +* EmployeeDetailsComponent + + ] + +}) +export class EmployeeModule {} +``` + +--- + +# Shared Module + +Common pattern. + +Contains reusable: + +```text +Components + +Directives + +Pipes +``` +--- + +Example + +```typescript +@NgModule({ + + declarations: [ + + LoadingSpinnerComponent, + + SearchBoxComponent + + ], + + exports: [ + LoadingSpinnerComponent, + + * SearchBoxComponent + + ] + +}) +``` + +--- + +Used across application. + +--- + +# Core Module Pattern + +Often contains: + +```text +Singleton Services + +Authentication + +Interceptors + +Global Configuration +``` + +--- + +Example + +```typescript +CoreModule +``` + +loaded once for the application. + +--- + +# Common Interview Questions + +## What Is An NgModule? + +Organizational container that registers components, directives, pipes, dependencies, and services. + +--- + +## Difference Between declarations And imports? +*Declarations: + +```text +Things I Created +``` + +Imports: + +```text +Things Others Created +``` + +that I want to use. + +--- + +## What Goes Into providers? + +Services registered for Dependency Injection. + +--- + +## What Are exports? + +Things other modules are allowed to use. + +--- + +# Common Interview Traps + +## Trap 1 + +Putting services into declarations. + +Incorrect. + +Services belong in: + +```typescript +providers +``` + +--- + +## Trap 2 + +Declaring same component in multiple modules. + +Not allowed. + +--- + +## Trap 3 + +Confusing imports with declarations. + +--- + +# Senior-Level Summary + +NgModules acted as Angular's registration and organization mechanism. + +They told Angular: + +```text +What Exists + +What Is Shared + +What Can Be Used + +What Can Be Injected +``` +For many years they were the backbone of Angular architecture. + +--- + +## Key Takeaways + +1. NgModules organized Angular applications. +2. declarations register components, directives, and pipes. +3. imports bring in external functionality. +4. exports make functionality available to other modules. +5. providers register services for dependency injection. +6. Feature Modules improve scalability. +7. Shared Modules support reuse. +8. Core Modules provide application-wide services. +9. NgModules remain important in many enterprise applications. +10. Understanding NgModules makes Standalone Components easier to understand. + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [Signal-Based Component APIs](../04-reactivity/18a-signal-based-component-apis.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [Standalone Components](20-standalone-components.md) + +
+ diff --git a/apps/portal/public/assets/content/05-advanced/20-standalone-components.md b/apps/portal/public/assets/content/05-advanced/20-standalone-components.md new file mode 100644 index 0000000..31b1c42 --- /dev/null +++ b/apps/portal/public/assets/content/05-advanced/20-standalone-components.md @@ -0,0 +1,942 @@ +# Standalone Components + +## Interview Priority + +**Must Know (Angular 15+)** + +## Interview Frequency +**Very Common** + +## Recommended Depth + +**Expert Level Understanding** + +## Relevant For + +- Modern Angular Development +- Angular 15+ +- Angular 16+ +- Angular 17+ +- Senior Developer Interviews + +--- + +# Why Were Standalone Components Introduced? + +This is the most important question. +--- + +# Problem With NgModules + +To create one component: + +```typescript +EmployeeComponent +``` + +Angular required: + +```text +Component + + ↓ + +Feature Module + + ↓ + +App Module +``` +Lots of registration and boilerplate. + +--- + +# Angular's Question + +Why should components require modules to exist? + +--- + +Traditional: + +```text +Declare Component + +Register Component + +Export Component + +Import Module + +Use Component +``` + +--- + +Angular wanted: + +```text +Component + +↓ + +Use Component +``` + +Much simpler. + +--- + +# What Is A Standalone Component? + +A component that can exist without being declared in an NgModule. + +--- + +Example + +```typescript +@Component({ + + standalone: true, + + template: ` +

Hello

+ ` + +}) +export class EmployeeComponent { +} +``` + +--- + +# The Biggest Change + +Traditional Angular: + +```typescript +@NgModule({ + + declarations:[ + EmployeeComponent + ] + +}) +``` + +--- + +Standalone Angular: + +```typescript +@Component({ + + standalone: true + +}) +``` + +No declaration required. + +--- + +## What Happened To declarations? + +It disappeared. + +--- + +Why? + +```text +Component Already Knows +It Is A Component +``` + +Angular no longer needs + +```typescript +declarations:[] +``` + +--- + +# Imports In Standalone Components + +Imports still exist. + +But move closer to where they are used. +--- + +Old + +```typescript +@NgModule({ + + imports:[ + CommonModule + *] + +}) +``` + +--- + +New + +```typescript +@Component({ + + standalone:true, + +* imports:[ + CommonModule + ] + +*) +``` + +--- + +# Real Example + +```typescript +@Component({ + + standalone: true, + + imports:[ + + CommonModule, + + ReactiveFormsModule + + * + +}) +export class EmployeeComponent {} +``` + +--- + +Template now supports: + +```html +*ngIf + +*ngFor + +formGroup +``` + +because dependencies were imported directly. + +--- + +# Mental Model + +```text +Standalone Imports + += + +What This Component Uses +``` + +--- + +## Direct Component Imports + +Huge change. + +--- + +Traditional + +```text +Component + + ↓ + +Module + + ↓ + +Module +``` +--- + +Standalone + +```text +Component + + ↓ + +Component +``` + +--- + +Example + +```typescript +@Component({ + + standalone: true, + + imports:[ + + EmployeeCardComponent + + ] + +}) +``` + +--- + +Now template can use: + +```html + + +``` + +directly. + +--- + +# What Happened To exports? + +Mostly unnecessary. + +--- + +Old + +```typescript +exports: [ + EmployeeComponent +] +``` + +--- + +New + +```typescript +imports:[ + EmployeeComponent +] +``` + +Direct usage. + +No export layer. + +--- + +# Dependency Injection + +Providers still exist. + +--- + +# Preferred Approach + +```typescript +@Injectable({ + + providedIn: 'root' + +}) +export class EmployeeService {} +``` + +--- + +Application-wide singleton. + +--- + +# Component Scope Providers + +Possible. + +--- + +Example + +```typescript +@Component({ + +* standalone:true, + + providers:[ + + EmployeeService + + ] + +}) +``` + +--- + +Meaning: + +```text +New Service Instance + +For This Component Tree +``` + +--- + +# Bootstrapping + +Old Angular: + +```typescript +platformBrowserDynamic() + .bootstrapModule( + AppModule + ); +``` + +--- + +Standalone Angular: + +```typescript +bootstrapApplication( + AppComponent +); +``` + +Much simpler. + +--- + +# Routing In Standalone World + +Traditional: + +```typescript +RouterModule.forRoot(...) +``` + +--- + +Modern: + +```typescript +bootstrapApplication( + AppComponent, + { + providers: [ + provideRouter(routes) + ] + } +); +``` + +--- + +# Lazy Loading + +Traditional + +```typescript +loadChildren() +``` + +--- + +Standalone + +```typescript +loadComponent: + () => + import(...) +``` + +--- + +Example + +```typescript +{ + path:'employees', + + loadComponent: () => + + import( + './employee.component' + ) +} +``` + +--- + +# Standalone Directives + +Possible. + +--- + +Example + +```typescript +@Directive({ + + standalone:true + +}) +``` + +--- + +# Standalone Pipes + +Possible. + +--- + +Example + +```typescript +@Pipe({ + + standalone:true + +}) +``` + +--- + +# Migration From NgModules + +Traditional + +```typescript +@NgModule({ + + declarations:[ + EmployeeComponent + ] + +}) +``` + +--- + +Standalone + +```typescript +@Component({ + + standalone:true + +}) +``` + +--- + +Traditional + +```typescript +imports:[ + CommonModule +] +``` + +--- + +Standalone + +```typescript +@Component({ + + imports:[ + CommonModule + ] + +}) +``` + +--- + +Traditional + +```typescript +exports:[ + EmployeeComponent +] +``` + +--- + +Standalone + +```typescript +imports:[ + EmployeeComponent +] +``` + +directly. + +--- + +# Standalone + Signals + +Modern Angular frequently combines: + +```typescript +standalone:true +``` + +with + +```typescript +signal() + +input() + +computed() + +effect() + +model() +``` + +creating highly reactive component-based architecture. + +--- + +# Real Enterprise Example + +```typescript +@Component({ + + standalone:true, + + imports:[ + + CommonModule, + + ReactiveFormsModule, + + EmployeeCardComponent + + ] + +}) +export class EmployeeListComponent { + +} +``` + +Everything needed is visible immediately. + +--- + +# Standalone vs NgModules + +## Registration + +NgModules + +```typescript +declarations:[] +``` + +Standalone + +```typescript +standalone:true +``` + +--- + +## Sharing + +NgModules + +```typescript +exports:[] +``` + +Standalone + +```typescript +imports:[] +``` + +--- + +## Complexity + +NgModules + +Medium + +Standalone + +Low + +--- + +## Dependency Visibility + +NgModules + +Distributed + +Standalone + +Local + +--- + +## Modern Angular + +NgModules + +Supported + +Standalone + +Preferred + +--- + +# Common Interview Questions + +## What Is A Standalone Component? + +A component that can operate without being declared in an NgModule. + +--- + +## Why Were Standalone Components Introduced? + +To reduce boilerplate and simplify Angular architecture. + +--- + +## Do NgModules Still Work? + +Yes. + +Fully supported. + +--- + +## Are Standalone Components Recommended? + +For new Angular applications: + +```text +Yes +``` + +--- + +## What Happened To declarations? + +Replaced by: + +```typescript +standalone:true +``` + +--- + +## How Are Dependencies Imported? + +Using: + +```typescript +imports:[] +``` + +inside the component. + +--- + +## What Happened To exports? + +Mostly unnecessary. + +Components are imported directly. + +--- + +# Common Interview Traps + +## Trap 1 + +Thinking NgModules are deprecated. + +Incorrect. + +They remain supported. + +--- + +## Trap 2 + +Thinking standalone removes DI. + +Incorrect. + +Dependency Injection remains unchanged. + +--- + +## Trap 3 + +Thinking standalone removes routing. + +Incorrect. + +Routing becomes even simpler. + +--- + +# Architecture Considerations + +Use Standalone Components when: + +```text +Building New Applications + +Creating Reusable Components + +Using Signals + +Using Modern Angular +``` + +NgModules still make sense when: + +```text +Maintaining Existing Enterprise Applications + +Large Legacy Codebases + +Gradual Migrations +``` + +--- + +# Key Takeaways + +1. Standalone Components eliminate the need for declarations. +2. Components become self-contained. +3. Dependencies are imported directly into components. +4. exports become largely unnecessary. +5. Dependency injection continues to work normally. +6. Bootstrapping becomes simpler. +7. Routing integrates naturally with standalone components. +8. Standalone Components are Angular's preferred modern architecture. +9. NgModules are still fully supported. +10. Standalone Components simplify development while improving dependency visibility. + +--- + +# Interview Notes (Revision Version) + +## Old Angular + +```text +Component + +↓ + +Module + +↓ + +Application +``` + +--- + +## Modern Angular + +```text +Component + +↓ + +Component + +↓ + +Application +``` + +--- + +## Declarations + +```text +Removed +``` + +--- + +## Imports + +```text +Moved To Component Level +``` + +--- + +## Exports + +```text +Mostly Unnecessary +``` + +--- + +## Providers + +```text +Still Exist +``` + +--- + +## Bootstrap + +```typescript +bootstrapApplication() +``` + +--- + +## Key Message + +Standalone Components remove much of the ceremony historically associated with Angular modules, resulting in a simpler, more explicit, and more maintainable architecture while preserving Angular's powerful dependency injection and routing capabilities. + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [NgModules Fundamentals](19-ngmodules-fundamentals.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [Routing & Navigation](21-routing-and-navigation.md) + +
+ diff --git a/apps/portal/public/assets/content/05-advanced/21-routing-and-navigation.md b/apps/portal/public/assets/content/05-advanced/21-routing-and-navigation.md new file mode 100644 index 0000000..fbcb282 --- /dev/null +++ b/apps/portal/public/assets/content/05-advanced/21-routing-and-navigation.md @@ -0,0 +1,1508 @@ +# Routing & Navigation + +## Interview Priority + +**Must Know** + +## Interview Frequency + +**Extremely Common** + +## Recommended Depth + +**Expert Level Understanding** + +## Relevant For + +- Angular Applications +- Enterprise Applications +- Dashboard Applications +- Admin Portals +- E-commerce Applications +- Senior Developer Interviews + +--- + +# First Principles + +Before understanding Angular Routing, let's understand why routing exists. + +--- + +# Traditional Web Applications + +Before Single Page Applications (SPAs), websites worked like this: + +```text +Home Page + ↓ +Request Home.html + +Employees Page + ↓ +Request Employees.html + +Reports Page + ↓ +Request Reports.html +``` + +Every navigation caused: + +```text +Full Page Refresh + +HTML Reload + +CSS Reload + +JavaScript Reload +``` + +--- + +# The Problem + +Imagine an application with: + +```text +Dashboard + +Employees + +Projects + +Reports + +Settings + +Profile +``` + +Every navigation would: + +```text +Reload Entire Application +``` + +Result: + +```text +Slower User Experience + +Lost State + +Network Overhead +``` + +--- + +# Angular SPA Solution + +Angular applications typically load once. + +After that: + +```text +URL Changes + +↓ + +Angular Router Processes URL + +↓ + +Component Changes + +↓ + +No Full Page Reload +``` + +--- + +# Mental Model + +Think of Angular Routing as: + +```text +URL + +↓ + +Route Configuration + +↓ + +Component + +↓ + +Displayed In router-outlet +``` + +Example: + +```text +/employees + +↓ + +EmployeeListComponent +``` + +--- + +# What Is Angular Router? + +Angular Router is a framework service responsible for: + +```text +URL Management + +Navigation + +Route Matching + +Lazy Loading + +Route Parameters + +Query Parameters + +Guards + +Resolvers +``` + +--- + +# High-Level Architecture + +```text +Browser URL + + ↓ + +Angular Router + + ↓ + +Route Configuration + + ↓ + +Matched Component + + ↓ + +router-outlet +``` + +--- + +# Core Routing Concepts + +Almost every routing discussion involves: + +```typescript +Routes +``` + +```typescript +Router +``` + +```typescript +ActivatedRoute +``` + +```html + +``` + +```html +routerLink +``` + +--- + +# Modern Angular Routing + +Standalone Angular uses: + +```typescript +provideRouter() +``` + +--- + +# Route Configuration + +```typescript +import { Routes } from '@angular/router'; + +export const routes: Routes = [ + + { + path: '', + component: HomeComponent + }, + + { + path: 'employees', + component: EmployeeListComponent + } + +]; +``` + +--- + +# Understanding Routes + +Each route maps: + +```text +URL + +↓ + +Component +``` + +--- + +Example + +```typescript +{ + path: 'employees', + component: EmployeeListComponent +} +``` + +Meaning: + +```text +/employees + +↓ + +EmployeeListComponent +``` + +--- + +# Route Matching + +Suppose routes: + +```typescript +const routes: Routes = [ + + { + path: '', + component: HomeComponent + }, + + { + path: 'employees', + component: EmployeeListComponent + } + +]; +``` + +URLs: + +```text +/ +``` + +loads: + +```typescript +HomeComponent +``` + +--- + +URL: + +```text +/employees +``` + +loads: + +```typescript +EmployeeListComponent +``` + +--- + +# Bootstrapping Routing + +Modern Angular: + +```typescript +bootstrapApplication( + AppComponent, + { + providers: [ + provideRouter(routes) + ] + } +); +``` + +--- + +# Why provideRouter()? + +Registers Angular Router and route configuration. + +Think: + +```text +Enable Routing + +Register Routes +``` + +--- + +# router-outlet + +One of the most important routing concepts. + +--- + +# Problem + +When a route matches: + +```typescript +EmployeeListComponent +``` + +Where should Angular display it? + +--- + +# Solution + +```html + + +``` + +--- + +# Example + +App Component + +```html +
+ + Company Portal + +
+ + + +``` + +--- + +# Visual + +```text +Header + +↓ + +router-outlet + +↓ + +Current Route Component +``` + +--- + +# Navigation Using routerLink + +Most navigation starts from templates. + +--- + +# Traditional HTML + +```html +/employees + Employees +
+``` + +Problem: + +```text +Full Browser Refresh +``` + +--- + +# Angular Navigation + +```html + + Employees + +``` + +--- + +# Benefits + +```text +SPA Navigation + +No Full Reload + +Faster Experience +``` + +--- + +# Real Enterprise Example + +```html + +``` + +--- + +# How routerLink Works + +```text +User Clicks Link + +↓ + +Angular Intercepts Click + +↓ + +URL Changes + +↓ + +Router Finds Route + +↓ + +Component Loads + +↓ + +router-outlet Updates +``` + +--- + +# Programmatic Navigation + +Navigation can also happen from TypeScript. + +--- + +# Why? + +Sometimes navigation depends on application logic. + +Examples: + +```text +Login Success + +Save Employee + +Delete Employee + +Wizard Completion +``` + +--- + +# Inject Router + +```typescript +constructor( + private router: Router +) {} +``` + +--- + +# Navigate + +```typescript +this.router.navigate([ + '/employees' +]); +``` + +--- + +# Example: Login + +```typescript +login() { + + this.authService.login(); + + this.router.navigate([ + '/dashboard' + ]); + +} +``` + +--- + +# Example: Save Employee + +```typescript +save() { + + this.employeeService.save(); + + this.router.navigate([ + '/employees' + ]); + +} +``` + +--- + +# Route Parameters + +One of the most frequently asked interview topics. + +--- + +# Problem + +Need employee details page. + +Employee IDs: + +```text +101 + +102 + +103 +``` + +Creating: + +```text +/employees101 + +/employees102 + +/employees103 +``` + +does not scale. + +--- + +# Solution + +Parameterized Routes + +```typescript +{ + path: 'employees/:id', + component: EmployeeDetailComponent +} +``` + +--- + +# URLs + +```text +/employees/101 + +/employees/102 + +/employees/103 +``` + +All use: + +```typescript +EmployeeDetailComponent +``` + +--- + +# Reading Route Parameters + +Inject: + +```typescript +constructor( + private route: + ActivatedRoute +) {} +``` + +--- + +# Snapshot Approach + +```typescript +const id = + this.route.snapshot + .paramMap + .get('id'); +``` + +--- + +Result + +For: + +```text +/employees/101 +``` + +returns: + +```text +101 +``` + +--- + +# Reactive Approach + +```typescript +this.route.paramMap + .subscribe(params => { + + const id = + params.get('id'); + + }); +``` + +--- + +# Why Reactive? + +Useful when route changes: + +```text +Employee 101 + +↓ + +Employee 102 + +↓ + +Employee 103 +``` + +without recreating the component. + +--- + +# Query Parameters + +Another extremely common Angular feature. + +--- + +# Route Parameters + +Identify resource. + +Example: + +```text +/employees/101 +``` + +--- + +# Query Parameters + +Modify resource behavior. + +Example: + +```text +/employees?page=2 +``` + +--- + +# Real Examples + +```text +/employees?page=2 + +/employees?sort=name + +/employees?status=active + +/employees?page=2&sort=name +``` + +--- + +# Reading Query Parameters + +```typescript +this.route.queryParamMap + .subscribe(params => { + + const page = + params.get('page'); + + }); +``` + +--- + +# Real Enterprise Example + +Employee Search Screen + +```text +/employees + +?page=2 + +&sort=name + +&department=IT + +&status=active +``` + +--- + +# Benefits + +```text +Bookmarkable + +Sharable + +Browser Back Support + +Persistence +``` + +--- + +# Route Parameters vs Query Parameters + +## Route Parameters + +```text +Resource Identity +``` + +Example: + +```text +/employees/101 +``` + +--- + +## Query Parameters + +```text +Display Options + +Filters + +Paging + +Sorting +``` + +Example: + +```text +?page=2 +``` + +--- + +# Redirect Routes + +Used frequently. + +--- + +Example + +```typescript +{ + path: '', + redirectTo: 'dashboard', + pathMatch: 'full' +} +``` + +--- + +Meaning + +```text +/ + +↓ + +/dashboard +``` + +--- + +# pathMatch + +Very common interview question. + +--- + +# full + +```typescript +pathMatch:'full' +``` + +Means: + +```text +Entire URL Must Match +``` + +--- + +Usually used for: + +```typescript +redirectTo +``` + +configurations. + +--- + +# Wildcard Routes + +Handle invalid URLs. + +--- + +Example + +```typescript +{ + path: '**', + component: NotFoundComponent +} +``` + +--- + +Examples + +```text +/xyz + +/unknown-page + +/wrong-url +``` + +--- + +Load: + +```typescript +NotFoundComponent +``` + +--- + +# Nested Routes + +Extremely common in enterprise applications. + +--- + +# Example Structure + +```text +/dashboard + +/dashboard/users + +/dashboard/reports + +/dashboard/settings +``` + +--- + +# Route Definition + +```typescript +{ + path: 'dashboard', + + component: DashboardComponent, + + children: [ + + { + path: 'users', + component: + UsersComponent + }, + + { + path: 'reports', + component: + ReportsComponent + } + + ] +} +``` + +--- + +# Visual + +```text +Dashboard + + ↓ + +Users + +Reports + +Settings +``` + +All render inside dashboard layout. + +--- + +# Nested router-outlet + +Dashboard template: + +```html +

Dashboard

+ + + +``` + +--- + +# Route Data + +Attach metadata to routes. + +--- + +Example + +```typescript +{ + path: 'employees', + + component: + EmployeeListComponent, + + data: { + + title: 'Employees' + + } +} +``` + +--- + +# Uses + +```text +Page Titles + +Permissions + +Breadcrumbs + +Metadata +``` + +--- + +# Reading Route Data + +```typescript +this.route.data + .subscribe(data => { + + }); +``` + +--- + +# Router Events + +Advanced but useful. + +--- + +Examples + +```text +Global Loader + +Analytics + +Tracking + +Performance Monitoring +``` + +--- + +```typescript +this.router.events + .subscribe(event => { + + }); +``` + +--- + +# Common Enterprise Routing Structure + +```typescript +const routes: Routes = [ + + { + path: '', + redirectTo: + 'dashboard', + pathMatch: + 'full' + }, + + { + path:'dashboard', + component: + DashboardComponent + }, + + { + path:'employees', + component: + EmployeeListComponent + }, + + { + path:'employees/:id', + component: + EmployeeDetailComponent + }, + + { + path:'projects', + component: + ProjectListComponent + }, + + { + path:'reports', + component: + ReportDashboardComponent + }, + + { + path:'**', + component: + NotFoundComponent + } + +]; +``` + +--- + +# Routing Lifecycle + +```text +User Navigates + +↓ + +Router Receives URL + +↓ + +Route Matching + +↓ + +Guards Execute + +↓ + +Resolvers Execute + +↓ + +Component Loads + +↓ + +router-outlet Updates +``` + +(We will deep dive Guards and Resolvers separately.) + +--- + +# Common Interview Questions + +## What Is Angular Router? + +Framework service responsible for URL management and navigation. + +--- + +## What Is router-outlet? + +Placeholder where Angular renders routed components. + +--- + +## What Is routerLink? + +Directive used for SPA navigation in templates. + +--- + +## Difference Between routerLink And href? + +routerLink: + +```text +SPA Navigation +``` + +href: + +```text +Full Browser Refresh +``` + +--- + +## Programmatic Navigation? + +```typescript +router.navigate(...) +``` + +--- + +## What Is A Route Parameter? + +Used to identify a specific resource. + +Example: + +```text +/employees/101 +``` + +--- + +## What Is A Query Parameter? + +Used to modify behavior or filtering. + +Example: + +```text +?page=2 +``` + +--- + +## Difference Between Route Params And Query Params? + +```text +Route Params + ↓ +Resource Identity + +Query Params + ↓ +Display Options +``` + +--- + +## What Is A Wildcard Route? + +```typescript +path: '**' +``` + +Handles invalid URLs. + +--- + +## Why Is router-outlet Required? + +Because Angular needs a location where routed components can render. + +--- + +# Common Interview Traps + +## Trap 1 + +Using: + +```html +href +``` + +instead of: + +```html +routerLink +``` + +inside Angular applications. + +--- + +## Trap 2 + +Confusing route parameters with query parameters. + +--- + +## Trap 3 + +Forgetting: + +```html + +``` + +Without it, routing cannot render components. + +--- + +## Trap 4 + +Placing wildcard routes above normal routes. + +Incorrect: + +```typescript +{ + path:'**' +} +``` + +should usually be the **st route. + +--- + +# Senior-Level Mental Model + +Think of routing as: + +```text +URL + +↓ + +Angular Router + +↓ + +Route Configuration + +↓ + +Matched Component + +↓ + +router-outlet +``` + +Everything else: + +```text +Parameters + +Dat** +Resolvers + +Guards + +Lazy Loading +``` + +builds on top of that foundation. + +--- + +# Key Takeaways + +1. Angular Router maps URLs to components. +2. Routing enables SPA navigation without page reloads. +3. Routes define URL-to-component mappings. +4. router-outlet is where routed components render. +5. routerLink is used for template navigation. +6. router.navigate() is used for programmatic navigation. +7. Route parameters identify resources. +8. Query parameters modify behavior or filtering. +9. Nested routes support dashboard-style layouts. +10. Routing is the foundation for Guards, Resolvers, and Lazy Loading. + +--- + +# Interview Notes (Revision Version) + +## Template Navigation + +```html +routerLink +``` + +--- + +## Programmatic Navigation + +```typescript +router.navigate() +``` + +--- + +## Route Params + +```text +/employees/101 +``` + +--- + +## Query Params + +```text +?page=2 +``` + +--- + +## Router Placeholder + +```html + +``` + +--- + +## Invalid Routes + +```typescript +path: '**' +``` + +--- + +## Redirect Route + +```typescript +redirectTo +``` + +--- + +## Modern Configuration + +```typescript +provideRouter(routes) +``` + +--- + +## Key Message + +Angular Routing is the mechanism that maps URLs to components, enabling Single Page Application navigation, parameterized URLs, nested layouts, and enterprise-grade navigation patterns without full browser page reloads. + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [Standalone Components](20-standalone-components.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [Route Parameters & Query Parameters Deep Dive](22-route-parameters-and-query-parameters-deep-dive.md) + +
+ diff --git a/apps/portal/public/assets/content/05-advanced/22-route-parameters-and-query-parameters-deep-dive.md b/apps/portal/public/assets/content/05-advanced/22-route-parameters-and-query-parameters-deep-dive.md new file mode 100644 index 0000000..37bf0fd --- /dev/null +++ b/apps/portal/public/assets/content/05-advanced/22-route-parameters-and-query-parameters-deep-dive.md @@ -0,0 +1,1403 @@ +# Route Parameters & Query Parameters Deep Dive + +## Interview Priority + +**Must Know** + +## Interview Frequency + +**Extremely Common** + +## Recommended Depth + +**Expert Level Understanding** + +## Relevant For + +- CRUD Applications +- Enterprise Applications +- Dashboard Applications +- Admin Portals +- E-commerce Applications +- Senior Developer Interviews + +--- + +# First Principles + +Most applications need to answer two questions: + +```text +Which resource? + +How should it be displayed? +``` + +Angular solves these using: + +```text +Route Parameters + +Query Parameters +``` + +--- + +# Route Parameters vs Query Parameters + +This is one of the most common Angular interview topics. + +--- + +## Route Parameters + +Answer: + +```text +Which resource? +``` + +--- + +Example + +```text +/employees/101 +``` + +Meaning: + +```text +Employee ID 101 +``` + +--- + +Another Example + +```text +/projects/25 +``` + +Meaning: + +```text +Project ID 25 +``` + +--- + +## Query Parameters + +Answer: + +```text +How should it be displayed? +``` + +--- + +Example + +```text +/employees?page=2 +``` + +Meaning: + +```text +Show Page 2 +``` + +--- + +Another Example + +```text +/employees?sort=name +``` + +Meaning: + +```text +Sort By Name +``` + +--- + +# Simple Rule + +## Route Parameter + +```text +Identity +``` + +--- + +## Query Parameter + +```text +Options +``` + +--- + +# Real Enterprise Example + +```text +/employees/101 +``` + +Means: + +```text +Which Employee? +``` + +--- + +```text +/employees?page=2&sort=name +``` + +Means: + +```text +How Should Employees Be Shown? +``` + +--- + +# Route Parameters + +## Why They Exist + +Suppose an application contains: + +```text +Employee List +``` + +User clicks: + +```text +Employee 101 +``` + +Need: + +```text +Employee Details Page +``` + +--- + +Creating routes like: + +```text +/employees101 + +/employees102 + +/employees103 +``` + +would not scale. + +--- + +# Solution + +Parameterized Routes + +```typescript +{ + path: 'employees/:id', + component: EmployeeDetailComponent +} +``` + +--- + +# Understanding : + +This: + +```typescript +:id +``` + +means: + +```text +Dynamic Value +``` + +--- + +Examples + +```text +/employees/101 + +/employees/102 + +/employees/103 +``` + +All match: + +```typescript +employees/:id +``` + +--- + +# Visual + +```text +employees/:id + + ↓ + +employees/101 + +employees/102 + +employees/103 +``` + +--- + +# Multiple Route Parameters + +Possible. + +--- + +Example + +```typescript +{ + path: + 'departments/:departmentId/employees/:employeeId', + component: + EmployeeComponent +} +``` + +--- + +Example URL + +```text +/departments/10/employees/101 +``` + +--- + +Values + +```text +departmentId = 10 + +employeeId = 101 +``` + +--- + +# Enterprise Example + +E-commerce + +```text +/orders/500/items/20 +``` + +--- + +Route + +```typescript +{ + path: + 'orders/:orderId/items/:itemId' +} +``` + +--- + +Result + +```text +orderId = 500 + +itemId = 20 +``` + +--- + +# Reading Route Parameters + +Angular provides: + +```typescript +ActivatedRoute +``` + +--- + +Inject + +```typescript +constructor( + private route: + ActivatedRoute +) {} +``` + +--- + +# Approach 1 + +Snapshot + +Most common beginner approach. + +--- + +Example + +```typescript +const id = + this.route.snapshot + .paramMap + .get('id'); +``` + +--- + +URL + +```text +/employees/101 +``` + +Result + +```text +101 +``` + +--- + +# Mental Model + +Snapshot means: + +```text +Read Once +``` + +--- + +Think: + +```text +Take Picture + +Of Current URL +``` + +--- + +# When Snapshot Is Good + +```text +Initial Page Load + +Component Created Once + +Route Unlikely To Change +``` + +--- + +Example + +```text +Employee Details Page +``` + +Typical use case. + +--- + +# Limitations Of Snapshot + +Suppose user navigates: + +```text +/employees/101 + +↓ + +/employees/102 + +↓ + +/employees/103 +``` + +Component may remain alive. + +Snapshot won't automatically react. + +--- + +# Approach 2 + +Reactive Parameter Reading + +Preferred approach. + +--- + +Example + +```typescript +this.route.paramMap + .subscribe(params => { + + const id = + params.get('id'); + + }); +``` + +--- + +# Advantage + +Whenever URL changes: + +```text +101 + +↓ + +102 + +↓ + +103 +``` + +subscription receives updates. + +--- + +# Visual + +```text +Route Change + +↓ + +paramMap Emits + +↓ + +Component Updates +``` + +--- + +# Real Enterprise Example + +Master-Detail Screen + +Left: + +```text +Employees List +``` + +Right: + +```text +Employee Details +``` + +User clicks: + +```text +101 + +102 + +103 +``` + +Reactive parameters handle this naturally. + +--- + +# Route Parameters + API Calls + +Most common combination. + +--- + +URL + +```text +/employees/101 +``` + +--- + +Read Parameter + +```typescript +this.route.paramMap +``` + +--- + +Call API + +```typescript +employeeService.getById( + id +); +``` + +--- + +Visual + +```text +URL + +↓ + +Parameter + +↓ + +API Call + +↓ + +Employee Data + +↓ + +UI +``` + +--- + +# Reactive RxJS Version + +Senior-level pattern. + +--- + +```typescript +this.route.paramMap + .pipe( + + map(params => + params.get('id') + ), + + switchMap(id => + + this.employeeService + .getById(id!) + + ) + + ) + .subscribe(); +``` + +--- + +# Why Is This Better? + +Problems solved: + +```text +Route Changes + +↓ + +Automatic API Reload + +↓ + +Cancel Old Requests + +↓ + +Always Show Latest Data +``` + +--- + +# Query Parameters + +Now let's explore the second half. + +--- + +# Why Query Parameters Exist + +Imagine Employee List. + +Need: + +```text +Paging + +Sorting + +Filtering + +Searching +``` + +--- + +Route Parameter doesn't fit. + +--- + +Bad Example + +```text +/employees/page2 +``` + +--- + +Good Example + +```text +/employees?page=2 +``` + +--- + +# Query Parameter Anatomy + +Example + +```text +/employees?page=2 +``` + +--- + +Parameter + +```text +page +``` + +--- + +Value + +```text +2 +``` + +--- + +# Multiple Query Parameters + +Example + +```text +/employees + +?page=2 + +&sort=name + +&status=active + +&department=IT +``` + +--- + +Values + +```text +page=2 + +sort=name + +status=active + +department=IT +``` + +--- + +# Real Enterprise Example + +Employee Search Screen + +```text +/employees + +?page=3 + +&sort=name + +&status=active + +&department=IT +``` + +--- + +Benefits + +```text +Bookmarkable + +Sharable + +Browser Friendly + +Persistent State +``` + +--- + +# Reading Query Parameters + +Reactive Version + +```typescript +this.route.queryParamMap + .subscribe(params => { + + const page = + params.get('page'); + + }); +``` + +--- + +# Snapshot Version + +```typescript +const page = + this.route.snapshot + .queryParamMap + .get('page'); +``` + +--- + +# Query Parameter Defaults + +Very common. + +--- + +Example + +```typescript +const page = + + Number( + params.get('page') + ) || 1; +``` + +--- + +Result + +If URL: + +```text +/employees +``` + +then: + +```text +Page = 1 +``` + +--- + +# Route Params vs Query Params + +## Route Params + +```text +Required + +Identify Resource +``` + +Example + +```text +/employees/101 +``` + +--- + +## Query Params + +```text +Optional + +Modify Display +``` + +Example + +```text +?page=2 +``` + +--- + +# Real Enterprise Design Rule + +Employee Details + +```text +/employees/101 +``` + +Use: + +```text +Route Params +``` + +--- + +Employee List + +```text +/employees?page=2 +``` + +Use: + +```text +Query Params +``` + +--- + +# Updating Query Parameters + +Suppose user changes page. + +--- + +Navigate + +```typescript +this.router.navigate( + [], + { + queryParams: { + page: 2 + } + } +); +``` + +--- + +Result + +```text +?page=2 +``` + +--- + +# Preserve Existing Query Parameters + +Very common. + +--- + +Current + +```text +?page=2&sort=name +``` + +--- + +Update only page + +```typescript +this.router.navigate( + [], + { + queryParams: { + page: 3 + }, + + queryParamsHandling: + 'merge' + } +); +``` + +--- + +Result + +```text +?page=3&sort=name +``` + +--- + +# Search Screen Example + +Very common interview scenario. + +--- + +URL + +```text +/employees + +?page=1 + +&sort=name + +&search=adarsh + +&department=IT +``` + +--- + +Query Parameters control: + +```text +Pagination + +Filters + +Sorting + +Search +``` + +--- + +# Combining Route + Query Parameters + +Real-world example. + +--- + +URL + +```text +/employees/101 + +?tab=projects + +&sort=date +``` + +--- + +Route Parameter + +```text +employeeId = 101 +``` + +--- + +Query Parameters + +```text +tab = projects + +sort = date +``` + +--- + +Meaning + +```text +Show Employee 101 + +Open Projects Tab + +Sort By Date +``` + +--- + +# Parent-Child Resource Example + +```text +/departments/10/employees/101 + +?page=2 +``` + +--- + +Route Params + +```text +departmentId = 10 + +employeeId = 101 +``` + +--- + +Query Params + +```text +page = 2 +``` + +--- + +# Common Interview Questions + +## What Is A Route Parameter? + +Dynamic URL segment used to identify a resource. + +Example: + +```text +/employees/101 +``` + +--- + +## What Is A Query Parameter? + +Optional URL parameter used to modify behavior. + +Example: + +```text +?page=2 +``` + +--- + +## Difference Between ParamMap And QueryParamMap? + +```text +paramMap + +↓ + +Route Parameters +``` + +--- + +```text +queryParamMap + +↓ + +Query Parameters +``` + +--- + +## Snapshot vs Subscribe? + +Snapshot: + +```text +Read Once +``` + +--- + +Subscribe: + +```text +React To Changes +``` + +--- + +## When Should Route Parameters Be Used? + +For: + +```text +Customer ID + +Employee ID + +Order ID + +Resource Identity +``` + +--- + +## When Should Query Parameters Be Used? + +For: + +```text +Search + +Filters + +Pagination + +Sorting +``` + +--- + +# Common Interview Traps + +## Trap 1 + +Using query parameters for resource identity. + +Bad: + +```text +/employees?id=101 +``` + +Prefer: + +```text +/employees/101 +``` + +--- + +## Trap 2 + +Using route parameters for filtering. + +Bad: + +```text +/employees/active +``` + +Prefer: + +```text +/employees?status=active +``` + +--- + +## Trap 3 + +Using snapshot when route changes should be handled reactively. + +--- + +# Senior-Level Mental Model + +Think: + +```text +Route Parameters + +↓ + +What Resource? +``` + +--- + +```text +Query Parameters + +↓ + +How Should It Be Displayed? +``` + +--- + +# Decision Framework + +## Employee Details + +```text +/employees/101 +``` + +Route Parameter + +--- + +## Employee Search + +```text +/employees?search=john +``` + +Query Parameter + +--- + +## Employee Pagination + +```text +/employees?page=2 +``` + +Query Parameter + +--- + +## Employee Details + Active Tab + +```text +/employees/101?tab=projects +``` + +Both + +--- + +# Key Takeaways + +1. Route parameters identify resources. +2. Query parameters modify behavior and presentation. +3. Route parameters are defined using `:parameterName`. +4. `ActivatedRoute` is used to access route information. +5. `snapshot` reads values once. +6. `paramMap` and `queryParamMap` provide reactive updates. +7. Query parameters are ideal for search, filters, sorting, and paging. +8. Route parameters are ideal for resource identifiers. +9. RxJS operators pair naturally with route changes. +10. Most enterprise Angular applications use both route and query parameters extensively. + +--- + +# Interview Notes (Revision Version) + +## Resource Identity + +```text +/employees/101 +``` + +Use: + +```typescript +paramMap +``` + +--- + +## Search + +```text +?search=angular +``` + +Use: + +```typescript +queryParamMap +``` + +--- + +## Paging + +```text +?page=2 +``` + +Use: + +```typescript +queryParamMap +``` + +--- + +## Route Changes + +```typescript +paramMap.subscribe(...) +``` + +--- + +## One-Time Read + +```typescript +snapshot +``` + +--- + +## Senior Pattern + +```typescript +paramMap + ↓ +switchMap() + ↓ +API +``` + +--- + +## Key Message + +Route parameters answer **"Which resource?"** while query parameters answer **"How should the resource be displayed?"**. Understanding this distinction makes Angular routing design much simpler and leads to cleaner, more maintainable enterprise applications. + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [Routing & Navigation](21-routing-and-navigation.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [Route Guards](23-route-guards.md) + +
+ diff --git a/apps/portal/public/assets/content/05-advanced/23-route-guards.md b/apps/portal/public/assets/content/05-advanced/23-route-guards.md new file mode 100644 index 0000000..cf9daea --- /dev/null +++ b/apps/portal/public/assets/content/05-advanced/23-route-guards.md @@ -0,0 +1,1614 @@ +# Route Guards + +## Interview Priority + +**Must Know** + +## Interview Frequency + +**Very High** + +## Recommended Depth + +**Expert Level Understanding** + +## Relevant For + +- Authentication +- Authorization +- Enterprise Applications +- Security +- Admin Portals +- Dashboard Applications +- Senior Angular Interviews + +--- + +# First Principles + +Before understanding Route Guards, let's understand the problem they solve. + +--- + +# The Problem + +Suppose an application contains: + +```text +/login + +/dashboard + +/admin + +/reports + +/settings +``` + +Only authenticated users should access: + +```text +/ dashboard +``` + +--- + +Menu is hidden: + +```html +@if(isLoggedIn) { + + + Dashboard + + +} +``` + +Looks secure? + +No. + +--- + +User can still open browser and manually enter: + +```text +https://company.com/dashboard +``` + +Without protection: + +```text +Dashboard Loads +``` + +--- + +# Why UI Protection Is Not Enough + +Hiding menu items: + +```html +@if(role === 'Admin') +``` + +only affects the UI. + +It does not stop navigation. + +Users can: + +```text +Bookmark URLs + +Type URLs + +Refresh Pages + +Deep Link Into Screens +``` + +--- + +# Solution + +Use: + +```typescript +Route Guards +``` + +--- + +# What Is A Route Guard? + +A Route Guard is: + +> A mechanism that decides whether Angular should continue with navigation. + +--- + +# Mental Model + +```text +User Navigates + + ↓ + +Route Match + + ↓ + +Guard Executes + + ↓ + +Allowed? + + ↓ + +Yes + + ↓ + +Component Loads +``` + +--- + +Or: + +```text +User Navigates + + ↓ + +Guard Executes + + ↓ + +Denied + + ↓ + +Redirect +``` + +--- + +# Router Navigation Lifecycle + +Understanding the lifecycle helps understand where guards fit. + +```text +User Clicks Link + + ↓ + +Router Receives URL + + ↓ + +Route Matching + + ↓ + +Route Guards Execute + + ↓ + +Route Resolvers Execute + + ↓ + +Component Created + + ↓ + +router-outlet Updated +``` + +--- + +# Why Guards Are Used + +Most common enterprise requirements: + +```text +Authentication + +Authorization + +Role-Based Access + +Feature Flags + +Tenant Isolation + +Subscription Plans + +Prevent Unsaved Data Loss +``` + +--- + +# Types Of Guards + +Angular provides: + +```typescript +CanActivate + +CanActivateChild + +CanDeactivate + +CanMatch +``` + +These are the most important ones. + +--- + +# Quick Summary + +## CanActivate + +```text +Can User Enter Route? +``` + +--- + +## CanActivateChild + +```text +Can User Enter Child Routes? +``` + +--- + +## CanDeactivate + +```text +Can User Leave Current Route? +``` + +--- + +## CanMatch + +```text +Should Route Match At All? +``` + +--- + +# Functional Guards + +Modern Angular prefers: + +```typescript +Functional Guards +``` + +instead of: + +```typescript +Class-Based Guards +``` + +--- + +# Old Style + +```typescript +@Injectable() +export class AuthGuard +implements CanActivate { + +} +``` + +--- + +# Modern Style + +```typescript +export const authGuard: +CanActivateFn = () => { + + return true; + +}; +``` + +--- + +# Why Functional Guards? + +Benefits: + +```text +Less Boilerplate + +Simpler Code + +Better Tree Shaking + +Modern Angular Standard +``` + +--- + +# Dependency Injection In Functional Guards + +Use: + +```typescript +inject() +``` + +Example: + +```typescript +export const authGuard: +CanActivateFn = () => { + + const authService = + inject(AuthService); + + return authService + .isLoggedIn(); + +}; +``` + +--- + +# Guard Return Types + +Interview favorite. + +A guard can return: + +```typescript +boolean +``` + +--- + +Allow: + +```typescript +return true; +``` + +--- + +Block: + +```typescript +return false; +``` + +--- + +Observable: + +```typescript +Observable +``` + +--- + +Promise: + +```typescript +Promise +``` + +--- + +Redirect: + +```typescript +UrlTree +``` + +--- + +# Visual + +```text +true + + ↓ + +Allow Navigation +``` + +--- + +```text +false + + ↓ + +Block Navigation +``` + +--- + +```text +UrlTree + + ↓ + +Redirect User +``` + +--- + +# CanActivate + +Most commonly used guard. + +--- + +# Purpose + +Question: + +```text +Can This Route Be Activated? +``` + +--- + +# Example + +```typescript +{ + path: 'dashboard', + + component: + DashboardComponent, + + canActivate: [ + authGuard + ] +} +``` + +--- + +# Mental Model + +```text +Navigate + +↓ + +CanActivate + +↓ + +Allowed? + +↓ + +Load Component +``` + +--- + +# Authentication Example + +Auth Service: + +```typescript +@Injectable({ + providedIn: 'root' +}) +export class AuthService { + + isLoggedIn(): boolean { + + return !!localStorage + .getItem('token'); + + } + +} +``` + +--- + +Guard: + +```typescript +export const authGuard: +CanActivateFn = () => { + + const authService = + inject(AuthService); + + return authService + .isLoggedIn(); + +}; +``` + +--- + +# Flow + +```text +Dashboard Requested + +↓ + +Guard Runs + +↓ + +Token Exists? + +↓ + +Yes + +↓ + +Dashboard Loads +``` + +--- + +# Redirect To Login + +Very common. + +--- + +Guard + +```typescript +export const authGuard: +CanActivateFn = () => { + + const authService = + inject(AuthService); + + const router = + inject(Router); + + if(authService.isLoggedIn()) { + + return true; + + } + + return router.createUrlTree( + ['/login'] + ); + +}; +``` + +--- + +# Why UrlTree? + +Angular recommends: + +```typescript +return UrlTree; +``` + +instead of: + +```typescript +router.navigate(...) +``` + +inside guards. + +--- + +Benefits: + +```text +Cleaner + +Declarative + +Integrated With Navigation Pipeline +``` + +--- + +# JWT Example + +Very common enterprise scenario. + +--- + +Authentication flow: + +```text +User Logs In + +↓ + +JWT Returned + +↓ + +Store Token + +↓ + +Guard Validates Token + +↓ + +Allow Route +``` + +--- + +Auth Service + +```typescript +isLoggedIn(): boolean { + + const token = + localStorage.getItem( + 'jwt' + ); + + return !!token; + +} +``` + +--- + +Guard + +```typescript +canActivate() +``` + +validates token existence. + +--- + +# Authorization + +Senior interview favorite. + +--- + +# Difference + +Authentication: + +```text +Who Are You? +``` + +--- + +Authorization: + +```text +What Are You Allowed To Access? +``` + +--- + +# Role-Based Authorization + +Example roles: + +```text +Admin + +Manager + +Employee +``` + +--- + +# Admin Guard + +```typescript +export const adminGuard: +CanActivateFn = () => { + + const authService = + inject(AuthService); + + return authService + .role === 'Admin'; + +}; +``` + +--- + +# Route + +```typescript +{ + path:'admin', + + component: + AdminComponent, + + canActivate:[ + adminGuard + ] +} +``` + +--- + +# Real Enterprise Example + +Employee: + +```text +Dashboard ✅ + +Reports ✅ + +Admin ❌ +``` + +--- + +Admin: + +```text +Dashboard ✅ + +Reports ✅ + +Admin ✅ +``` + +--- + +# Multiple Guards + +Possible. + +--- + +Route + +```typescript +{ + path:'reports', + + canActivate:[ + + authGuard, + + roleGuard + + ] +} +``` + +--- + +Execution + +```text +authGuard + +↓ + +roleGuard + +↓ + +Load Component +``` + +--- + +# CanActivateChild + +Purpose: + +```text +Protect All Child Routes +``` + +--- + +# Problem + +Routes: + +```text +/admin + +/admin/users + +/admin/roles + +/admin/settings + +/admin/reports +``` + +--- + +Protecting each individually: + +```typescript +canActivate +``` + +becomes repetitive. + +--- + +# Solution + +```typescript +{ + path:'admin', + + canActivateChild:[ + adminGuard + ], + + children:[] +} +``` + +--- + +# Example + +```typescript +{ + path:'admin', + + component: + AdminShellComponent, + + canActivateChild:[ + adminGuard + ], + + children:[ + + { + path:'users', + component: + UsersComponent + }, + + { + path:'roles', + component: + RolesComponent + } + + ] +} +``` + +--- + +# Mental Model + +```text +Parent Protected + +↓ + +Every Child Protected +``` + +--- + +# CanDeactivate + +One of the most useful enterprise guards. + +--- + +# Problem + +User opens: + +```text +Employee Edit Form +``` + +--- + +Modifies data. + +--- + +Clicks: + +```text +Back + +Menu + +Different Page +``` + +--- + +Unsaved changes: + +```text +Lost +``` + +--- + +# Purpose + +Question: + +```text +Can User Leave Current Component? +``` + +--- + +# Common Use Cases + +```text +Employee Forms + +Settings + +Configurations + +CMS Editors + +Profile Screens +``` + +--- + +# Component Contract + +```typescript +export interface CanComponentDeactivate { + + canDeactivate(): boolean; + +} +``` + +--- + +# Component + +```typescript +export class EmployeeEditComponent +implements CanComponentDeactivate { + + hasUnsavedChanges = true; + + canDeactivate() { + + return !this + .hasUnsavedChanges; + + } + +} +``` + +--- + +# Guard + +```typescript +export const unsavedChangesGuard: +CanDeactivateFn< +EmployeeEditComponent +> = (component) => { + + return component + .canDeactivate(); + +}; +``` + +--- + +# Real Version + +```typescript +export const unsavedChangesGuard: +CanDeactivateFn< +EmployeeEditComponent +> = () => { + + return confirm( + 'Unsaved changes. Leave page?' + ); + +}; +``` + +--- + +# Navigation Flow + +```text +User Navigates Away + +↓ + +CanDeactivate + +↓ + +Unsaved Changes? + +↓ + +Show Prompt + +↓ + +User Decides +``` + +--- + +# CanMatch + +Newest and commonly asked in modern Angular interviews. + +--- + +# Purpose + +Question: + +```text +Should This Route Match? +``` + +--- + +# Difference + +CanActivate: + +```text +Route Already Matched +``` + +--- + +CanMatch: + +```text +Route Matching Itself +Can Be Prevented +``` + +--- + +# Typical Uses + +```text +Feature Flags + +A/B Testing + +Conditional Routing + +Conditional Lazy Loading +``` + +--- + +# Feature Flag Example + +Beta users: + +```text +/dashboard +``` + +↓ + +New Dashboard + +--- + +Normal users: + +```text +/dashboard +``` + +↓ + +Old Dashboard + +--- + +Guard + +```typescript +export const betaGuard: +CanMatchFn = () => { + + return true; + +}; +``` + +--- + +# Enterprise Use Cases + +```text +Premium Features + +Subscription Plans + +Tenant Routing + +Feature Rollouts + +Beta Features +``` + +--- + +# Async Guards + +Very common. + +--- + +# Why? + +Permission often comes from: + +```text +Backend + +Identity Provider + +JWT Validation + +API Call +``` + +--- + +Example: + +```typescript +export const authGuard: +CanActivateFn = () => { + + const authService = + inject(AuthService); + + return authService + .validateToken(); + +}; +``` + +--- + +Where: + +```typescript +validateToken(): +Observable +``` + +--- + +Angular waits for the result. + +--- + +# Guard Execution Order + +Interview question. + +--- + +Route: + +```typescript +canActivate:[ + authGuard, + roleGuard +] +``` + +--- + +Execution: + +```text +authGuard + +↓ + +roleGuard + +↓ + +Route Activation +``` + +--- + +If one fails: + +```text +Pipeline Stops +``` + +--- + +# Guards Do NOT Replace Security + +Extremely important. + +--- + +Incorrect understanding: + +```text +Route Guards Secure Application +``` + +--- + +Reality: + +```text +Route Guards + +↓ + +Frontend Protection + +↓ + +User Experience +``` + +--- + +Backend must still validate: + +```text +JWT + +Roles + +Permissions + +Authorization +``` + +--- + +# Common Interview Questions + +## What Is A Route Guard? + +Mechanism controlling whether Angular navigation can continue. + +--- + +## Most Common Guard? + +```typescript +CanActivate +``` + +--- + +## CanActivate vs CanActivateChild? + +CanActivate: + +```text +Single Route +``` + +--- + +CanActivateChild: + +```text +All Child Routes +``` + +--- + +## CanDeactivate? + +Protects against losing unsaved changes. + +--- + +## CanMatch? + +Controls route matching before route activation. + +--- + +## Return Types? + +```typescript +boolean + +Observable + +Promise + +UrlTree +``` + +--- + +## Functional Guards Or Class-Based Guards? + +Modern Angular prefers: + +```typescript +Functional Guards +``` + +--- + +## Do Guards Provide Security? + +No. + +Server-side validation remains mandatory. + +--- + +# Common Interview Traps + +## Trap 1 + +Using route guards as security boundaries. + +Incorrect. + +Backend security remains essential. + +--- + +## Trap 2 + +Using CanActivate for unsaved forms. + +Use: + +```typescript +CanDeactivate +``` + +--- + +## Trap 3 + +Confusing Authentication and Authorization. + +Authentication: + +```text +Who Are You? +``` + +Authorization: + +```text +What Can You Access? +``` + +--- + +## Trap 4 + +Using CanActivate on all child routes individually. + +Prefer: + +```typescript +CanActivateChild +``` + +--- + +# Decision Framework + +Need Login Validation? + +↓ + +```typescript +CanActivate +``` + +--- + +Need Role Access? + +↓ + +```typescript +CanActivate +``` + +--- + +Need Child Route Protection? + +↓ + +```typescript +CanActivateChild +``` + +--- + +Need Unsaved Form Protection? + +↓ + +```typescript +CanDeactivate +``` + +--- + +Need Feature Flags? + +↓ + +```typescript +CanMatch +``` + +--- + +# Senior-Level Mental Model + +```text +CanActivate + ↓ +Can Enter? + +CanActivateChild + ↓ +Can Enter Children? + +CanDeactivate + ↓ +Can Leave? + +CanMatch + ↓ +Can Route Match? +``` + +--- + +# Key Takeaways + +1. Route Guards control Angular navigation decisions. +2. `CanActivate` is primarily used for authentication and authorization. +3. `CanActivateChild` protects all child routes. +4. `CanDeactivate` prevents accidental data loss. +5. `CanMatch` controls route matching before activation. +6. Modern Angular prefers functional guards. +7. Guards can return booleans, Observables, Promises, or UrlTrees. +8. Multiple guards execute in sequence. +9. Guards improve navigation control and UX. +10. Guards never replace backend security. + +--- + +# Interview Notes (Revision Version) + +## Authentication + +```typescript +CanActivate +``` + +--- + +## Authorization + +```typescript +CanActivate +CanActivateChild +``` + +--- + +## Unsaved Changes + +```typescript +CanDeactivate +``` + +--- + +## Feature Flags + +```typescript +CanMatch +``` + +--- + +## Redirect + +```typescript +UrlTree +``` + +--- + +## Modern Guards + +```typescript +CanActivateFn +CanDeactivateFn +CanMatchFn +``` + +--- + +## Key Message + +Route Guards act as Angular's navigation control layer, enabling authentication, authorization, feature management, and unsaved-change protection while ensuring users navigate only to routes they are allowed to access. + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [Route Parameters & Query Parameters Deep Dive](22-route-parameters-and-query-parameters-deep-dive.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [Nested Routes](24-nested-routes.md) + +
+ diff --git a/apps/portal/public/assets/content/05-advanced/24-nested-routes.md b/apps/portal/public/assets/content/05-advanced/24-nested-routes.md new file mode 100644 index 0000000..cfcfd99 --- /dev/null +++ b/apps/portal/public/assets/content/05-advanced/24-nested-routes.md @@ -0,0 +1,598 @@ +# Nested Routes + +## Interview Priority + +**High** + +## Interview Frequency + +**Common** + +## Recommended Depth + +**Senior Level Understanding** + +## Relevant For + +- Enterprise Applications +- Dashboard Applications +- Admin Portals +- SaaS Applications +- Multi-Level Navigation +- Senior Angular Interviews + +--- + +# First Principles + +Before understanding Nested Routes, let's understand the problem they solve. + +--- + +# The Problem + +Suppose we build a dashboard application. + +Routes: + +```text +/dashboard + +/dashboard/users + +/dashboard/reports + +/dashboard/settings + +/dashboard/audit +``` + +Question: + +Should Angular completely reload the dashboard page every time a user clicks: + +```text +Users + +Reports + +Settings +``` + +? + +Ideally: + +```text +Dashboard Header +Dashboard Sidebar +Dashboard Navigation +``` + +should remain visible. + +Only: + +```text +Page Content +``` + +should change. + +--- + +# Desired UI + +```text ++----------------------------------+ +| Dashboard Header | ++----------------------------------+ +| Sidebar | Content Area | +| | | +| | Users | +| | Reports | +| | Settings | ++----------------------------------+ +``` + +When user clicks: + +```text +Reports +``` + +Only the content area changes. + +--- + +# Solution + +Use: + +```typescript +Nested Routes +``` + +--- + +# What Are Nested Routes? + +Nested Routes are routes defined inside another route using: + +```typescript +children +``` + +property. + +--- + +# Mental Model + +```text +Parent Route + ↓ +Layout/Shell + +Child Routes + ↓ +Content Area +``` + +--- + +# Parent Route + +Example: + +```typescript +{ + path: 'dashboard', + component: DashboardComponent +} +``` + +The parent component often acts as a: + +```text +Layout + +Shell + +Container +``` + +for child routes. + +--- + +# Dashboard As A Shell + +Imagine: + +```typescript +{ + path: 'dashboard', + component: DashboardComponent, + children: [] +} +``` + +DashboardComponent becomes: + +```text +Parent Layout +``` + +that remains visible while child routes change. + +--- + +# Dashboard Template + +```html +

Dashboard

+ + + + + +``` + +--- + +# Important Concept + +Notice: + +```html + +``` + +inside DashboardComponent. + +This is crucial. + +--- + +# Why Another router-outlet? + +We already have: + +```html +AppComponent + + +``` + +Why another one? + +--- + +Angular needs a place to render child routes. + +--- + +# Rendering Hierarchy + +```text +App Component + + ↓ + +router-outlet + + ↓ + +Dashboard Component + + ↓ + +router-outlet + + ↓ + +Users Component +``` + +--- + +# Route Configuration + +```typescript +{ + path: 'dashboard', + + component: DashboardComponent, + + children: [ + + { + path: 'users', + component: UsersComponent + }, + + { + path: 'reports', + component: ReportsComponent + }, + + { + path: 'settings', + component: SettingsComponent + } + + ] +} +``` + +--- + +# URL To Component Mapping + +```text +/dashboard/users + +↓ + +DashboardComponent + +↓ + +UsersComponent +``` + +--- + +```text +/dashboard/reports + +↓ + +DashboardComponent + +↓ + +ReportsComponent +``` + +--- + +```text +/dashboard/settings + +↓ + +DashboardComponent + +↓ + +SettingsComponent +``` + +--- + +# Navigation Flow + +User visits: + +```text +/dashboard/users +``` + +Angular performs: + +```text +dashboard + ↓ +DashboardComponent + ↓ +users + ↓ +UsersComponent +``` + +--- + +# Visual Rendering + +```text +AppComponent + ↓ +router-outlet + ↓ +DashboardComponent + ↓ +router-outlet + ↓ +UsersComponent +``` + +--- + +# Why Not Separate Routes? + +Without nested routes: + +```typescript +{ + path:'users', + component: UsersComponent +} + +{ + path:'reports', + component: ReportsComponent +} +``` + +Problem: + +```text +Dashboard Layout Lost + +Sidebar Lost + +Header Lost + +Repeated UI +``` + +--- + +# Nested Routes Preserve Layout + +```text +Dashboard Layout + +↓ + +Always Visible + +↓ + +Only Child Content Changes +``` + +--- + +# Enterprise Example + +Admin Portal + +```text +/admin + +/admin/users + +/admin/roles + +/admin/permissions + +/admin/audit +``` + +--- + +Configuration + +```typescript +{ + path:'admin', + + component: + AdminComponent, + + children:[ + + { + path:'users', + component: + UsersComponent + }, + + { + path:'roles', + component: + RolesComponent + }, + + { + path:'permissions', + component: + PermissionsComponent + } + + ] +} +``` + +--- + +# Child Navigation + +Inside parent component: + +```html + + Users + + + + Roles + +``` + +--- + +Notice: + +```html +users +``` + +not: + +```html +/admin/users +``` + +These are relative routes. + +--- + +# Relative Navigation + +Common in child routes. + +--- + +Current URL: + +```text +/dashboard +``` + +--- + +Navigation: + +```html + +``` + +Result: + +```text +/dashboard/users +``` + +--- + +# Absolute Navigation + +```html + +``` + +Starts from application root. + +--- + +# Relative vs Absolute + +## Relative + +```html +routerLink="users" +``` + +Current Route: + +```text +/dashboard +``` + +Result: + +```text +/dashboard/users +``` + +--- + +## Absolute + +```html +routerLink="/dashboard/users" +``` + +Always starts from root + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [Route Guards](23-route-guards.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [Route Resolvers](25-route-resolvers.md) + +
+ diff --git a/apps/portal/public/assets/content/05-advanced/25-route-resolvers.md b/apps/portal/public/assets/content/05-advanced/25-route-resolvers.md new file mode 100644 index 0000000..231c2bd --- /dev/null +++ b/apps/portal/public/assets/content/05-advanced/25-route-resolvers.md @@ -0,0 +1,1575 @@ +# Route Resolvers + +## Interview Priority + +**High** + +## Interview Frequency + +**Common** + +## Recommended Depth + +**Senior Level Understanding** + +## Relevant For + +- Enterprise Applications +- CRUD Applications +- Detail Pages +- Master-Detail Screens +- Route-Based Data Loading +- Senior Angular Interviews + +--- + +# First Principles + +Before understanding Route Resolvers, let's understand the problem they solve. + +Suppose a user navigates to: + +```text +/employees/101 +``` + +Angular loads: + +```typescript +EmployeeDetailComponent +``` + +The component requires: + +```text +Employee Name + +Email + +Department + +Projects +``` + +which must be loaded from an API. + +--- + +# Traditional Approach + +Component loads first. + +Then data is fetched. + +```typescript +ngOnInit() { + + const id = + this.route.snapshot + .paramMap + .get('id'); + + this.employeeService + .getById(id!) + .subscribe(employee => { + + this.employee = employee; + + }); + +} +``` + +--- + +# User Experience + +```text +Navigate + +↓ + +Component Created + +↓ + +Loading Spinner + +↓ + +API Call + +↓ + +Data Returns + +↓ + +UI Updates +``` + +--- + +Visual Example + +```text +Employee Details + +Loading... + +Loading... + +Loading... + +Adarsh Pawaskar +Senior Engineer +``` + +--- + +# The Problem + +The component now becomes responsible for: + +```text +Route Logic + +API Calls + +Loading State + +Error Handling + +UI Rendering +``` + +Multiple responsibilities become mixed together. + +--- + +# Resolver Approach + +Instead of: + +```text +Load Component + +↓ + +Load Data +``` + +Angular allows: + +```text +Load Data + +↓ + +Load Component +``` + +--- + +# What Is A Resolver? + +A Resolver is: + +> A mechanism that fetches route-critical data before Angular activates a route. + +--- + +# Mental Model + +Traditional + +```text +Component + +↓ + +API + +↓ + +Data +``` + +--- + +Resolver + +```text +Route + +↓ + +Resolver + +↓ + +Data + +↓ + +Component +``` + +--- + +# Angular Navigation Lifecycle + +Understanding where Resolvers execute is extremely important. + +```text +User Navigation + +↓ + +Route Match + +↓ + +Route Guards + +↓ + +Route Resolvers + +↓ + +Route Activation + +↓ + +Component Creation + +↓ + +Rendering +``` + +--- + +# Important Observation + +Resolvers execute: + +```text +After Guards + +Before Component Creation +``` + +--- + +# Modern Angular Resolver + +Angular now prefers: + +```typescript +ResolveFn +``` + +--- + +Example + +```typescript +export const employeeResolver: +ResolveFn = () => { + + return inject(EmployeeService) + .getById('101'); + +}; +``` + +--- + +# Understanding ResolveFn + +```typescript +ResolveFn +``` + +means: + +```text +Before The Route Loads + +Provide Employee Data +``` + +--- + +# Registering Resolver + +Route Configuration: + +```typescript +{ + path: 'employees/:id', + + component: + EmployeeDetailComponent, + + resolve: { + + employee: + employeeResolver + + } + +} +``` + +--- + +# Reading This Configuration + +```text +Before EmployeeDetailComponent Loads + +↓ + +Run employeeResolver + +↓ + +Store Result As + +employee +``` + +--- + +# Resolver Execution Flow + +User visits: + +```text +/employees/101 +``` + +Angular performs: + +```text +Match Route + +↓ + +Run Resolver + +↓ + +Call API + +↓ + +Receive Employee + +↓ + +Create Component + +↓ + +Render UI +``` + +--- + +# Resolver With Route Parameters + +Most common real-world scenario. + +--- + +Route + +```text +/employees/101 +``` + +Need: + +```text +Employee ID = 101 +``` + +inside the resolver. + +--- + +Resolver + +```typescript +export const employeeResolver: +ResolveFn = ( + + route + +) => { + + const id = + route.paramMap.get('id'); + + return inject( + EmployeeService + ).getById(id!); + +}; +``` + +--- + +# What Is route? + +Angular passes: + +```typescript +ActivatedRouteSnapshot +``` + +to the resolver. + +--- + +Provides access to: + +```typescript +route.paramMap + +route.queryParamMap + +route.data +``` + +--- + +# Data Flow + +```text +URL + +↓ + +Route Param + +↓ + +Resolver + +↓ + +EmployeeService + +↓ + +API + +↓ + +Employee + +↓ + +Component +``` + +--- + +# Accessing Resolved Data + +Once resolver completes: + +```typescript +EmployeeDetailComponent +``` + +receives the data. + +--- + +Component + +```typescript +constructor( + private route: + ActivatedRoute +) {} +``` + +--- + +Read Data + +```typescript +ngOnInit() { + + const employee = + + this.route.snapshot + .data['employee']; + +} +``` + +--- + +# Why No API Call? + +Because: + +```text +Resolver Already Loaded It +``` + +The component receives ready-to-use data. + +--- + +# Reactive Access + +Instead of snapshot: + +```typescript +this.route.data + .subscribe(data => { + + this.employee = + data['employee']; + + }); +``` + +--- + +# Multiple Resolvers + +Angular supports multiple resolvers for the same route. + +--- + +Example + +```typescript +{ + path:'employees/:id', + + component: + EmployeeDetailComponent, + + resolve: { + + employee: + employeeResolver, + + permissions: + permissionResolver, + + projects: + projectResolver + + } + +} +``` + +--- + +# Flow + +```text +Employee Resolver + +Projects Resolver + +Permission Resolver + +↓ + +All Complete + +↓ + +Component Loads +``` + +--- + +# Enterprise Example + +Employee Details Screen may need: + +```text +Employee Information + +Projects + +Permissions + +Reference Data +``` + +before rendering. + +--- + +# Error Handling + +Important interview topic. + +--- + +Suppose: + +```text +404 + +500 + +Network Failure +``` + +occurs. + +--- + +Without Error Handling + +Navigation can fail. + +--- + +Example + +```typescript +export const employeeResolver: +ResolveFn = ( + + route + +) => { + + return inject(EmployeeService) + .getById( + route.paramMap.get('id')! + ) + .pipe( + + catchError(() => { + + return of(null); + + }) + + ); + +}; +``` + +--- + +# Redirect On Error + +Very common. + +```typescript +catchError(() => { + + const router = + inject(Router); + + router.navigate([ + '/not-found' + ]); + + return EMPTY; + +}) +``` + +--- + +# Resolver vs Component API Calls + +One of the most common interview questions. + +--- + +# Component-Based Loading + +```typescript +ngOnInit() +``` + +loads data. + +--- + +Benefits + +```text +Simple + +Flexible + +Navigation Is Immediate +``` + +--- + +Disadvantages + +```text +Loading States + +Multiple Spinners + +Messier Components +``` + +--- + +# Resolver-Based Loading + +Benefits + +```text +Cleaner Components + +Data Ready Before Render + +Simplified Screens +``` + +--- + +Disadvantages + +```text +Navigation Waits + +Can Delay Page Transitions +``` + +--- + +# Common Misconception + +## Do Resolvers Improve Performance? + +Not usually. + +--- + +Consider: + +```text +API Takes 2 Seconds +``` + +--- + +Component Approach + +```text +Navigate + +↓ + +Component Appears + +↓ + +API Call (2 sec) + +↓ + +Data Appears +``` + +Total: + +```text +2 Seconds +``` + +--- + +Resolver Approach + +```text +Navigate + +↓ + +Resolver (2 sec) + +↓ + +Component Appears +``` + +Total: + +```text +2 Seconds +``` + +--- + +# Important Insight + +Resolvers generally improve: + +```text +User Experience + +Architecture + +Readability +``` + +not raw API performance. + +--- + +# Better UX + +Without Resolver + +```text +Page Appears + +↓ + +Loading Spinner + +↓ + +Data Appears +``` + +--- + +With Resolver + +```text +Wait + +↓ + +Fully Populated Page Appears +``` + +--- + +# Why Guards Execute Before Resolvers + +Common interview discussion. + +--- + +Suppose: + +```text +/admin/employees/101 +``` + +requires authentication. + +--- + +Potential concern: + +```text +Will API Calls Be Wasted +For Unauthorized Users? +``` + +--- + +No. + +Angular sequence is: + +```text +Route Match + +↓ + +Guards + +↓ + +Resolvers + +↓ + +Component Creation +``` + +--- + +Example + +```text +User Not Logged In + +↓ + +Auth Guard Fails + +↓ + +Redirect Login + +↓ + +Resolver Never Runs +``` + +--- + +Result + +```text +No API Call + +No Wasted Work +``` + +--- + +# Do Services Move Into Resolvers? + +Yes. + +But services do not disappear. + +--- + +# Traditional Architecture + +```text +Component + +↓ + +Service + +↓ + +API +``` + +--- + +Example + +```typescript +constructor( + private employeeService: + EmployeeService +) {} +``` + +--- + +# Resolver Architecture + +```text +Resolver + +↓ + +Service + +↓ + +API + +↓ + +Resolved Data + +↓ + +Component +``` + +--- + +Example + +```typescript +export const employeeResolver: +ResolveFn = (route) => { + + const employeeService = + inject(EmployeeService); + + return employeeService.getById( + route.paramMap.get('id')! + ); + +}; +``` + +--- + +Component + +```typescript +employee = + this.route.snapshot + .data['employee']; +``` + +--- + +# Service Responsibility Remains Same + +Services still handle: + +```text +HTTP Calls + +Business Logic + +Data Access +``` + +Only the caller changes. + +--- + +# Route Match vs Route Activation + +Very important distinction. + +--- + +# Route Match + +Question: + +```text +Which Route Definition +Matches This URL? +``` + +Example: + +```text +/employees/101 + +↓ + +employees/:id +``` + +Angular finds a match. + +--- + +No component has been created yet. + +--- + +# Route Activation + +After: + +```text +Guards Pass + +↓ + +Resolvers Complete +``` + +Angular: + +```text +Creates Component + +Injects Dependencies + +Runs Lifecycle Hooks + +Renders UI +``` + +--- + +# Simplified Definition + +Route Activation means: + +```text +Instantiate The Component + +And Render It Into + +router-outlet +``` + +--- + +# Complete Navigation Pipeline + +```text +1. User Navigates + +↓ + +2. URL Received + +↓ + +3. Route Match + +↓ + +4. Guards Execute + +↓ + +5. Resolvers Execute + +↓ + +6. Route Activation + +↓ + +7. Component Creation + +↓ + +8. Dependency Injection + +↓ + +9. Lifecycle Hooks + +↓ + +10. router-outlet Updated + +↓ + +11. User Sees Screen +``` + +--- + +# When To Use Resolvers + +Good candidates: + +```text +Employee Details + +Customer Profile + +Order Details + +User Profile + +Settings Configuration + +Reference Data +``` + +--- + +Reason: + +```text +Page Makes Little Sense +Without Data +``` + +--- + +# When NOT To Use Resolvers + +Avoid for: + +```text +Search Results + +Large Reports + +Infinite Scroll + +Huge Dashboards + +Frequently Refreshed Data +``` + +--- + +Reason: + +```text +Navigation Becomes Slower +``` + +while Angular waits. + +--- + +# Enterprise Pattern + +Most applications combine approaches. + +Example: + +```text +Guards + +↓ + +Resolvers + +↓ + +Component Created + +↓ + +Additional API Calls +``` + +--- + +Employee Screen + +```text +Resolver Loads Employee + +↓ + +Component Loads + +↓ + +Projects Loaded Later + +↓ + +Audit History Loaded Later +``` + +Only critical data is resolved upfront. + +--- + +# Common Interview Questions + +## What Is A Route Resolver? + +A mechanism that loads route-critical data before route activation. + +--- + +## When Do Resolvers Execute? + +```text +After Guards + +Before Route Activation +``` + +--- + +## What Is ResolveFn? + +Modern Angular functional resolver. + +--- + +## Can Resolvers Use Route Parameters? + +Yes. + +```typescript +route.paramMap.get(...) +``` + +--- + +## Can Multiple Resolvers Run? + +Yes. + +Using: + +```typescript +resolve:{} +``` + +with multiple entries. + +--- + +## Resolver vs ngOnInit? + +Resolver: + +```text +Data Before Component +``` + +--- + +ngOnInit: + +```text +Data After Component +``` + +--- + +## Do Resolvers Improve Performance? + +Usually no. + +They improve: + +```text +UX + +Architecture + +Readability +``` + +--- + +# Common Interview Traps + +## Trap 1 + +Thinking resolvers replace services. + +Incorrect. + +Resolvers typically consume services. + +--- + +## Trap 2 + +Using resolvers for huge datasets. + +Can slow navigation significantly. + +--- + +## Trap 3 + +Ignoring error handling. + +Failed resolvers can block route activation. + +--- + +## Trap 4 + +Thinking resolvers execute before guards. + +Actual order: + +```text +Guards + +↓ + +Resolvers + +↓ + +Route Activation +``` + +--- + +# Decision Framework + +Need Data Before Page Renders? + +↓ + +Use Resolver. + +--- + +Need Detail Screen Data? + +↓ + +Use Resolver. + +--- + +Need Search Results? + +↓ + +Usually Load In Component. + +--- + +Need Large Dynamic Data? + +↓ + +Usually Load In Component. + +--- + +# Senior-Level Mental Model + +```text +URL + +↓ + +Route Match + +↓ + +Guard + +↓ + +Resolver + +↓ + +Route Activation + +↓ + +Component + +↓ + +router-outlet +``` + +Resolvers answer: + +```text +What Data Must Exist + +Before This Route Can Render? +``` + +--- + +# Key Takeaways + +1. Resolvers load route-critical data before route activation. +2. Modern Angular uses `ResolveFn`. +3. Resolvers execute after Guards and before route activation. +4. Route parameters are commonly used within resolvers. +5. Resolved data is accessed through `route.data`. +6. Multiple resolvers can run together. +7. Resolvers simplify component responsibilities. +8. Resolvers improve UX by providing ready-to-render data. +9. Proper error handling is important. +10. Resolvers are best used when the page cannot function meaningfully without the required data. + +--- + +# Interview Notes (Revision Version) + +## Resolver Purpose + +```text +Load Data Before Route Activation +``` + +--- + +## Execution Order + +```text +Guards + +↓ + +Resolvers + +↓ + +Route Activation +``` + +--- + +## Access Data + +```typescript +route.snapshot.data +``` + +or + +```typescript +route.data +``` + +--- + +## Common Use Cases + +```text +Employee Details + +Customer Profile + +Order Details + +Configuration Pages +``` + +--- + +## Avoid For + +```text +Large Reports + +Infinite Scroll + +Massive Dashboards +``` + +--- + +## Key Message + +Route Resolvers preload route-critical data before Angular activates a route, helping create cleaner components, more predictable navigation flows, and better user experiences for detail-oriented enterprise screens. + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [Nested Routes](24-nested-routes.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [Lazy Loading](26-lazy-loading.md) + +
+ diff --git a/apps/portal/public/assets/content/05-advanced/26-lazy-loading.md b/apps/portal/public/assets/content/05-advanced/26-lazy-loading.md new file mode 100644 index 0000000..aeca3a3 --- /dev/null +++ b/apps/portal/public/assets/content/05-advanced/26-lazy-loading.md @@ -0,0 +1,1237 @@ +# Lazy Loading + +## Interview Priority + +**Must Know** + +## Interview Frequency + +**Very High** + +## Recommended Depth + +**Expert Level Understanding** + +## Relevant For + +- Enterprise Applications +- Performance Optimization +- Large Angular Applications +- SaaS Platforms +- Admin Portals +- Senior Angular Interviews + +--- + +# First Principles + +Before understanding Lazy Loading, let's understand the problem it solves. + +Imagine an Angular application contains: + +```text +Login + +Dashboard + +Employees + +Projects + +Reports + +Admin + +Analytics + +Audit +``` + +Each feature contains: + +```text +Components + +Services + +Routes + +Templates + +Styles +``` + +--- + +# The Problem + +When a user opens: + +```text +/login +``` + +should Angular download code for: + +```text +Employees + +Projects + +Reports + +Admin + +Analytics +``` + +immediately? + +Probably not. + +The user only needs: + +```text +Login Screen +``` + +at that moment. + +--- + +# Without Lazy Loading + +Angular bundles everything together. + +--- + +# Application Startup + +```text +User Opens App + +↓ + +Download Entire Application + +↓ + +Parse Entire Application + +↓ + +Initialize Entire Application + +↓ + +Show Login Screen +``` + +--- + +# Visual + +```text +App + +├── Login +├── Dashboard +├── Employees +├── Projects +├── Reports +├── Admin +└── Analytics +``` + +All downloaded immediately. + +--- + +# Problems + +```text +Large Initial Bundle + +Longer Startup Time + +More Memory Usage + +Slower First Load + +Poor User Experience +``` + +--- + +# Mental Model + +Without Lazy Loading: + +```text +Download Everything + +↓ + +Use Small Portion +``` + +--- + +# What Is Lazy Loading? + +Lazy Loading is a technique where Angular loads application features only when they are needed. + +--- + +# Mental Model + +With Lazy Loading: + +```text +Download Only What Is Needed + +When It Is Needed +``` + +--- + +# Startup Flow + +```text +User Opens Application + +↓ + +Download Login Feature + +↓ + +Show Login + +↓ + +User Opens Employees + +↓ + +Download Employees Feature + +↓ + +Render Employees +``` + +--- + +# Comparison + +## Without Lazy Loading + +```text +Application Start + +↓ + +10 MB Download + +↓ + +User Uses 1 MB +``` + +--- + +## With Lazy Loading + +```text +Application Start + +↓ + +1 MB Download + +↓ + +Load Additional Features Later +``` + +--- + +# Why Lazy Loading Exists + +Benefits: + +```text +Smaller Initial Bundle + +Faster Startup + +Reduced Memory Usage + +Better User Experience + +Improved Scalability +``` + +--- + +# What Can Be Lazy Loaded? + +Most commonly: + +```text +Feature Routes + +Feature Areas + +Standalone Components + +Standalone Route Trees +``` + +--- + +# Modern Angular Lazy Loading + +Modern Angular typically uses: + +```typescript +loadComponent() +``` + +and + +```typescript +loadChildren() +``` + +--- + +# loadComponent() + +Used for lazy loading a single standalone component. + +--- + +Example + +```typescript +{ + path: 'employees', + + loadComponent: () => + import( + './employees/employees.component' + ).then( + m => m.EmployeesComponent + ) +} +``` + +--- + +# What Happens? + +At startup: + +```text +EmployeesComponent Not Loaded +``` + +--- + +When user visits: + +```text +/employees +``` + +Angular: + +```text +Downloads Component + +Creates Component + +Renders Component +``` + +--- + +# Understanding Dynamic Imports + +Lazy Loading relies on: + +```typescript +import() +``` + +--- + +Example + +```typescript +import( + './employees.component' +) +``` + +--- + +This is called: + +```text +Dynamic Import +``` + +--- + +Meaning: + +```text +Load Later +``` + +instead of: + +```text +Load Immediately +``` + +--- + +# Static Import + +```typescript +import { + EmployeesComponent +} from './employees.component'; +``` + +--- + +Result + +```text +Included In Initial Bundle +``` + +--- + +# Dynamic Import + +```typescript +import( + './employees.component' +) +``` + +--- + +Result + +```text +Separate Bundle + +Loaded On Demand +``` + +--- + +# loadChildren() + +Most common enterprise approach. + +--- + +Used to lazy load an entire feature area. + +--- + +Example + +```typescript +{ + path: 'employees', + + loadChildren: () => + import( + './employees/routes' + ).then( + m => m.employeeRoutes + ) +} +``` + +--- + +# Difference + +## loadComponent() + +Loads: + +```text +One Component +``` + +--- + +## loadChildren() + +Loads: + +```text +Entire Route Tree + +Entire Feature Area +``` + +--- + +# Example Feature + +Employee Feature + +```text +Employee List + +Employee Details + +Employee Edit + +Employee Reports +``` + +--- + +Employee Routes + +```typescript +export const employeeRoutes: +Routes = [ + + { + path: '', + component: + EmployeeListComponent + } + +]; +``` + +--- + +Main Route + +```typescript +{ + path: 'employees', + + loadChildren: () => + import( + './employees/routes' + ) + .then( + m => m.employeeRoutes + ) +} +``` + +--- + +# Startup Behavior + +Initially: + +```text +Employee Feature Not Loaded +``` + +--- + +User Opens: + +```text +/employees +``` + +Angular: + +```text +Downloads Employee Bundle + +Loads Routes + +Loads Components +``` + +--- + +# Enterprise Route Structure + +```text +features + +├── dashboard +├── employees +├── projects +├── reports +└── admin +``` + +--- + +Routes + +```typescript +{ + path:'dashboard', + loadChildren: ... +} + +{ + path:'employees', + loadChildren: ... +} + +{ + path:'projects', + loadChildren: ... +} + +{ + path:'reports', + loadChildren: ... +} +``` + +--- + +# Build Output + +Angular creates separate bundles. + +--- + +Example + +```text +main.js + +dashboard.chunk.js + +employees.chunk.js + +projects.chunk.js + +reports.chunk.js + +admin.chunk.js +``` + +--- + +# Startup + +Only: + +```text +main.js +``` + +downloads initially. + +--- + +Other chunks download later. + +--- + +# Router Lifecycle With Lazy Loading + +When navigating to a lazy route: + +```text +User Navigates + +↓ + +Route Match + +↓ + +Download Bundle + +↓ + +Guards + +↓ + +Resolvers + +↓ + +Route Activation + +↓ + +Component Creation + +↓ + +Rendering +``` + +--- + +# Important Interview Point + +For lazy routes: + +```text +Angular Must Download +The Feature Bundle + +Before It Can Create +The Component +``` + +--- + +# Lazy Loading And Route Guards + +Very common enterprise pattern. + +--- + +Example + +```typescript +{ + path:'admin', + + canActivate:[ + authGuard + ], + + loadChildren: () => + import( + './admin/routes' + ) + .then( + m => m.routes + ) +} +``` + +--- + +# The Problem + +Even unauthorized users may trigger bundle downloads. + +--- + +# Modern Solution + +Use: + +```typescript +CanMatch +``` + +--- + +Example + +```typescript +{ + path:'admin', + + canMatch:[ + adminGuard + ], + + loadChildren: () => + import( + './admin/routes' + ) + .then( + m => m.routes + ) +} +``` + +--- + +# Why CanMatch Is Useful + +CanMatch executes during route matching. + +--- + +Result: + +```text +Unauthorized User + +↓ + +Route Not Matched + +↓ + +Bundle Never Downloaded +``` + +--- + +# CanMatch vs CanActivate + +## CanActivate + +```text +Route Already Matched +``` + +--- + +## CanMatch + +```text +Route Matching Prevented +``` + +--- + +For lazy loading: + +```text +CanMatch Is Preferred +``` + +for feature protection. + +--- + +# Lazy Loading And Nested Routes + +Very common pattern. + +--- + +Parent Route + +```typescript +{ + path:'employees', + + loadChildren: () => + import( + './employees/routes' + ) + .then( + m => m.employeeRoutes + ) +} +``` + +--- + +Employee Routes + +```typescript +[ + { + path:'', + + component: + EmployeeShellComponent, + + children:[ + + { + path:':id', + component: + EmployeeDetailComponent + } + + ] + } +] +``` + +--- + +# Lazy Loading And Resolvers + +Works naturally. + +--- + +Example + +```typescript +{ + path:':id', + + resolve:{ + employee: + employeeResolver + }, + + component: + EmployeeDetailComponent +} +``` + +--- + +Flow + +```text +Download Feature + +↓ + +Execute Resolver + +↓ + +Create Component + +↓ + +Render UI +``` + +--- + +# What Is Preloading? + +Common interview topic. + +--- + +Problem: + +```text +First Visit To Route + +↓ + +Chunk Download + +↓ + +Small Delay +``` + +--- + +Angular allows: + +```text +Preloading +``` + +--- + +# PreloadAllModules + +```typescript +provideRouter( + + routes, + + withPreloading( + PreloadAllModules + ) + +) +``` + +--- + +# Behavior + +```text +Application Starts + +↓ + +Critical Routes Loaded + +↓ + +Background Downloads Begin + +↓ + +Future Navigation Faster +``` + +--- + +# Lazy Loading vs Preloading + +## Lazy Loading + +```text +Load When Needed +``` + +--- + +## Preloading + +```text +Load In Background +After Startup +``` + +--- + +# Route-Based Code Splitting + +One of the biggest advantages of Lazy Loading. + +--- + +Without Lazy Loading + +```text +main.js +``` + +contains: + +```text +Everything +``` + +--- + +With Lazy Loading + +```text +main.js + +employees.chunk.js + +reports.chunk.js + +admin.chunk.js + +settings.chunk.js +``` + +--- + +# Why Is This Valuable? + +Benefits: + +```text +Smaller Startup Download + +Faster Initial Render + +Better Performance Scores + +Improved Scalability +``` + +--- + +# When Should Lazy Loading Be Used? + +Ideal candidates: + +```text +Admin + +Reports + +Employees + +Analytics + +Projects + +Settings +``` + +--- + +Usually: + +```text +Large Feature Areas +``` + +--- + +# When Is Lazy Loading Less Useful? + +Very small applications. + +Example: + +```text +Login + +Dashboard + +Profile +``` + +with only a few screens. + +--- + +Overhead may outweigh benefits. + +--- + +# Common Enterprise Architecture + +```text +App + +├── Core +├── Shared + +├── Dashboard +├── Employees +├── Projects +├── Reports +├── Settings +└── Admin +``` + +Each feature becomes: + +```text +Independent Bundle +``` + +loaded on demand. + +--- + +# Common Interview Questions + +## What Is Lazy Loading? + +A technique that loads code only when it is required. + +--- + +## Why Use Lazy Loading? + +```text +Reduce Bundle Size + +Improve Startup Performance + +Improve Scalability +``` + +--- + +## Most Common APIs? + +```typescript +loadComponent() + +loadChildren() +``` + +--- + +## Difference Between loadComponent And loadChildren? + +loadComponent: + +```text +Single Component +``` + +--- + +loadChildren: + +```text +Feature Area +``` + +--- + +## What Is Dynamic Import? + +```typescript +import(...) +``` + +Loads code on demand. + +--- + +## What Is Code Splitting? + +Breaking one large bundle into multiple smaller bundles. + +--- + +## Can Lazy Routes Have Guards? + +Yes. + +```typescript +CanActivate + +CanMatch + +CanDeactivate + +Resolvers +``` + +all work. + +--- + +## Why Is CanMatch Important? + +Because Angular can prevent loading an entire feature bundle for unauthorized users. + +--- + +## What Is Preloading? + +Background downloading of lazy-loaded bundles after startup. + +--- + +# Common Interview Traps + +## Trap 1 + +Thinking + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [Route Resolvers](25-route-resolvers.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [HTTP Interceptors](27-http-interceptors.md) + +
+ diff --git a/apps/portal/public/assets/content/05-advanced/27-http-interceptors.md b/apps/portal/public/assets/content/05-advanced/27-http-interceptors.md new file mode 100644 index 0000000..3008aa3 --- /dev/null +++ b/apps/portal/public/assets/content/05-advanced/27-http-interceptors.md @@ -0,0 +1,1004 @@ +# HTTP Interceptors + +## Interview Priority + +**Must Know** + +## Interview Frequency + +**Very High** + +## Recommended Depth + +**Expert Level Understanding** + +## Relevant For + +- Enterprise Applications +- Authentication +- Authorization +- HTTP Communication +- Global Error Handling +- Logging +- Observability +- Performance +- Senior Angular Interviews + +--- + +# First Principles + +Before understanding HTTP Interceptors, let's understand the problem they solve. + +Consider an application that makes many API calls: + +```typescript +this.http.get('/employees'); + +this.http.get('/projects'); + +this.http.post('/employees'); + +this.http.put('/settings'); + +this.http.delete('/reports/10'); +``` + +Every request may need: + +```text +JWT Token + +Correlation ID + +Request Logging + +Error Handling + +Loading Indicator + +API Version Header +``` + +--- + +# Traditional Approach + +Handle everything manually. + +```typescript +this.http.get( + '/employees', + { + headers: { + Authorization: + `Bearer ${token}` + } + } +); +``` + +--- + +Every request repeats the same code. + +```typescript +this.http.get(...) +this.http.post(...) +this.http.put(...) +this.http.delete(...) +``` + +--- + +# Problems + +```text +Code Duplication + +Maintenance Difficulties + +Inconsistent Header Usage + +Repeated Error Handling + +Poor Architecture +``` + +--- + +# Solution + +Use: + +```typescript +HTTP Interceptors +``` + +--- + +# What Is An HTTP Interceptor? + +An HTTP Interceptor is: + +> Middleware that intercepts outgoing HTTP requests and incoming HTTP responses. + +--- + +# Mental Model + +```text +Component + +↓ + +HttpClient + +↓ + +Interceptor + +↓ + +Backend + +↓ + +Interceptor + +↓ + +Component +``` + +--- + +Think of an Interceptor as: + +```text +Airport Security Checkpoint +``` + +Every passenger passes through security. + +Similarly: + +```text +Every HTTP Request + +↓ + +Interceptor +``` + +--- + +# HttpClient Request Lifecycle + +Understanding where interceptors fit. + +--- + +```text +Component + +↓ + +HttpClient + +↓ + +Interceptor Pipeline + +↓ + +Backend API + +↓ + +Interceptor Pipeline + +↓ + +Component +``` + +--- + +# Full Request Flow + +```text +Component + +↓ + +HttpClient + +↓ + +Auth Interceptor + +↓ + +Logging Interceptor + +↓ + +Error Interceptor + +↓ + +Backend + +↓ + +Error Interceptor + +↓ + +Logging Interceptor + +↓ + +Auth Interceptor + +↓ + +Component +``` + +--- + +# Why Interceptors Exist + +Common enterprise concerns: + +```text +Authentication + +Authorization + +Logging + +Observability + +Caching + +Error Handling + +Request Tracking + +Response Transformation + +Loading Indicators +``` + +--- + +# Modern Angular Interceptors + +Angular now prefers: + +```typescript +HttpInterceptorFn +``` + +--- + +Instead of: + +```typescript +HttpInterceptor +``` + +classes. + +--- + +# Old Style + +```typescript +@Injectable() +export class AuthInterceptor +implements HttpInterceptor { + +} +``` + +--- + +# Modern Style + +```typescript +export const authInterceptor: +HttpInterceptorFn = ( + + req, + next + +) => { + + return next(req); + +}; +``` + +--- + +# Why Functional Interceptors? + +Benefits: + +```text +Less Boilerplate + +Better Tree Shaking + +Simpler Registration + +Modern Angular Standard +``` + +--- + +# Understanding Parameters + +## req + +Represents current HTTP request. + +Type: + +```typescript +HttpRequest +``` + +--- + +Example + +```text +GET /employees +``` + +--- + +## next + +Represents: + +```text +Next Interceptor + +or + +Backend API +``` + +--- + +Calling: + +```typescript +next(req) +``` + +means: + +```text +Continue Request Processing +``` + +--- + +# Minimal Interceptor + +```typescript +export const sampleInterceptor: +HttpInterceptorFn = ( + + req, + next + +) => { + + return next(req); + +}; +``` + +--- + +No modifications occur. + +Request simply continues. + +--- + +# Registering Interceptors + +Standalone Angular: + +```typescript +bootstrapApplication( + AppComponent, + { + providers: [ + + provideHttpClient( + + withInterceptors([ + + authInterceptor + + ]) + + ) + + ] + } +); +``` + +--- + +# Interceptor Execution Order + +Very common interview topic. + +--- + +Registration + +```typescript +withInterceptors([ + + authInterceptor, + + loadingInterceptor, + + errorInterceptor + +]) +``` + +--- + +Request + +```text +Auth + +↓ + +Loading + +↓ + +Error + +↓ + +Backend +``` + +--- + +Response + +```text +Backend + +↓ + +Error + +↓ + +Loading + +↓ + +Auth +``` + +--- + +# Mental Model + +Request: + +```text +Top → Bottom +``` + +--- + +Response: + +```text +Bottom → Top +``` + +--- + +# Most Common Use Case: JWT Authentication + +Almost every enterprise application uses this. + +--- + +# Problem + +Backend expects: + +```http +Authorization: Bearer TOKEN +``` + +for every request. + +--- + +Without Interceptor + +Every request contains: + +```typescript +headers: { + Authorization: + `Bearer ${token}` +} +``` + +--- + +Repeated throughout application. + +--- + +# Authentication Interceptor + +```typescript +export const authInterceptor: +HttpInterceptorFn = ( + + req, + next + +) => { + + const token = + localStorage.getItem( + 'token' + ); + + const request = + req.clone({ + + setHeaders: { + + Authorization: + `Bearer ${token}` + + } + + }); + + return next(request); + +}; +``` + +--- + +# Why clone()? + +Extremely common interview question. + +--- + +# HttpRequest Is Immutable + +This is invalid: + +```typescript +req.headers.set(...) +``` + +--- + +Angular requests cannot be modified directly. + +--- + +Use: + +```typescript +req.clone(...) +``` + +instead. + +--- + +# Why Immutability? + +Benefits: + +```text +Predictability + +Thread Safety + +No Side Effects + +Consistent Pipeline Behavior +``` + +--- + +# Request Flow + +```text +Component + +↓ + +GET /employees + +↓ + +Auth Interceptor + +↓ + +Authorization Header Added + +↓ + +Backend API +``` + +--- + +# Loading Indicator Interceptor + +Very common enterprise requirement. + +--- + +Desired Behavior + +```text +Request Starts + +↓ + +Show Loader + +↓ + +Response Returns + +↓ + +Hide Loader +``` + +--- + +Implementation + +```typescript +export const loadingInterceptor: +HttpInterceptorFn = ( + + req, + next + +) => { + + loaderService.show(); + + return next(req).pipe( + + finalize(() => { + + loaderService.hide(); + + }) + + ); + +}; +``` + +--- + +# Benefits + +No component needs: + +```typescript +showLoader() + +hideLoader() +``` + +logic. + +--- + +Everything becomes centralized. + +--- + +# Global Error Handling + +One of the most valuable interceptor use cases. + +--- + +Without Interceptor + +Every request handles errors separately. + +```typescript +this.http.get(...) + .subscribe({ + + error: () => { + + } + + }); +``` + +--- + +Problem: + +```text +Duplicate Logic + +Inconsistent Error Handling +``` + +--- + +# Error Interceptor + +```typescript +export const errorInterceptor: +HttpInterceptorFn = ( + + req, + next + +) => { + + return next(req).pipe( + + catchError(error => { + + console.error(error); + + return throwError( + () => error + ); + + }) + + ); + +}; +``` + +--- + +# Handling 401 Unauthorized + +Very common interview scenario. + +--- + +Backend: + +```text +401 Unauthorized +``` + +--- + +Interceptor: + +```typescript +catchError(error => { + + if(error.status === 401) { + + inject(Router) + .navigate([ + '/login' + ]); + + } + + return throwError( + () => error + ); + +}); +``` + +--- + +# Flow + +```text +Token Expired + +↓ + +401 Returned + +↓ + +Interceptor Executes + +↓ + +Redirect To Login +``` + +--- + +# Request Logging + +Very common. + +--- + +Implementation + +```typescript +export const loggingInterceptor: +HttpInterceptorFn = ( + + req, + next + +) => { + + console.log( + req.method, + req.url + ); + + return next(req); + +}; +``` + +--- + +Useful for: + +```text +Debugging + +Audit Trails + +Diagnostics +``` + +--- + +# Logging Request And Response + +```typescript +export const loggingInterceptor: +HttpInterceptorFn = ( + + req, + next + +) => { + + console.log( + 'Request', + req.url + ); + + return next(req).pipe( + + tap(() => { + + console.log( + 'Response' + ); + + }) + + ); + +}; +``` + +--- + +# Response Transformation + +Interceptors can modify responses. + +--- + +Example + +Backend returns: + +```json +{ + "payload": { + "name": "John" + } +} +``` + +--- + +Interceptor can flatten: + +```json +{ + "name": "John" +} +``` + +before application receives it. + +--- + +# Observability & Correlation IDs + +Very common in microservices. + +--- + +Every request carries: + +```text +Correlation ID + +Trace ID + +Request ID +``` + +--- + +Interceptor + +```typescript +const request = req.clone({ + setHeaders: { + 'X-Correlation-ID': crypto.randomUUID() + } +}); +return next(request); +``` + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [Lazy Loading](26-lazy-loading.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [Change Detection](28-change-detection.md) + +
+ diff --git a/apps/portal/public/assets/content/05-advanced/28-change-detection.md b/apps/portal/public/assets/content/05-advanced/28-change-detection.md new file mode 100644 index 0000000..23bc586 --- /dev/null +++ b/apps/portal/public/assets/content/05-advanced/28-change-detection.md @@ -0,0 +1,1283 @@ +# Change Detection + +## Interview Priority + +**Must Know** + +## Interview Frequency + +**Very High** + +## Recommended Depth + +**Expert Level Understanding** + +## Relevant For + +- Angular Internals +- Performance Optimization +- Signals +- OnPush Strategy +- Enterprise Applications +- Senior Angular Interviews + +--- + +# First Principles + +Let's begin with the most fundamental Angular question. + +Consider: + +```typescript +@Component({ + template: ` +

{{ count }}

+ + + ` +}) +export class AppComponent { + + count = 0; + + increment() { + this.count++; + } + +} +``` + +--- + +Question: + +```typescript +this.count++; +``` + +executes. + +How does Angular know that: + +```text +DOM Needs Updating? +``` + +--- + +Answer: + +```text +Change Detection +``` + +--- + +# What Is Change Detection? + +Change Detection is: + +> Angular's mechanism for synchronizing application state with the DOM. + +--- + +# Mental Model + +```text +State Changes + +↓ + +Angular Detects Change + +↓ + +DOM Updates + +↓ + +User Sees Updated UI +``` + +--- + +# Example + +Component State + +```typescript +count = 0; +``` + +UI + +```html +0 +``` + +--- + +After: + +```typescript +count++; +``` + +State: + +```typescript +count = 1; +``` + +--- + +Angular performs: + +```text +Change Detection +``` + +and updates: + +```html +1 +``` + +--- + +# The Core Problem + +JavaScript changes variables. + +```typescript +count++; +``` + +--- + +Angular does not continuously watch every variable. + +That would be: + +```text +Extremely Expensive +``` + +--- + +Instead Angular runs: + +```text +Change Detection Cycles +``` + +at specific times. + +--- + +# Angular's Question + +Angular repeatedly asks: + +```text +Did Anything Change? +``` + +--- + +If yes: + +```text +Update DOM +``` + +--- + +If no: + +```text +Do Nothing +``` + +--- + +# What Triggers Change Detection? + +Common triggers include: + +```text +Button Click + +Input Events + +HTTP Responses + +Promises + +setTimeout() + +setInterval() + +Observable Emissions +``` + +--- + +Example + +```typescript +this.http + .get(...) + .subscribe(result => { + + this.data = result; + + }); +``` + +--- + +Flow + +```text +HTTP Response + +↓ + +Angular Notified + +↓ + +Change Detection Runs + +↓ + +UI Updates +``` + +--- + +# Zone.js + +Historically Angular relies on: + +```text +Zone.js +``` + +--- + +# What Is Zone.js? + +Zone.js monkey-patches browser APIs. + +Meaning it wraps: + +```text +Events + +Timers + +Promises + +XHR Calls + +Fetch Calls +``` + +and notifies Angular when they complete. + +--- + +# Example + +```typescript +setTimeout(() => { + + this.count++; + +}, 1000); +``` + +--- + +Flow + +```text +Timer Finished + +↓ + +Zone.js Notices + +↓ + +Angular Runs Change Detection + +↓ + +DOM Updates +``` + +--- + +# Without Zone.js + +```text +Timer Finished + +↓ + +Angular Doesn't Know +``` + +--- + +# With Zone.js + +```text +Timer Finished + +↓ + +Angular Notified + +↓ + +Change Detection Runs +``` + +--- + +# Change Detection Tree + +Angular organizes components into a tree. + +--- + +Example + +```text +AppComponent + +│ + +├── HeaderComponent + +│ + +├── DashboardComponent +│ +├── EmployeeListComponent +│ +└── ReportsComponent + +│ + +└── FooterComponent +``` + +--- + +Visual + +```text +App + +↓ + +Dashboard + +↓ + +Employees + +↓ + +Employee Card +``` + +--- + +When Change Detection executes: + +```text +Angular Traverses Tree +``` + +and checks bindings. + +--- + +# Default Change Detection Strategy + +Every Angular component uses: + +```typescript +ChangeDetectionStrategy.Default +``` + +unless specified otherwise. + +--- + +Example + +```typescript +@Component({ + changeDetection: + ChangeDetectionStrategy.Default +}) +``` + +--- + +# Behavior + +```text +Check Everything +``` + +--- + +Flow + +```text +User Action + +↓ + +Angular Starts At Root + +↓ + +Traverses Entire Tree + +↓ + +Checks Bindings + +↓ + +Updates DOM +``` + +--- + +# Why Default Strategy Works Well + +Advantages: + +```text +Very Easy To Understand + +Very Forgiving + +Works Automatically + +Few Surprises +``` + +--- + +# Object Mutation Works + +Example + +```typescript +user = { + name: 'John' +}; +``` + +--- + +Modify + +```typescript +user.name = 'Adarsh'; +``` + +--- + +Default strategy usually updates UI successfully. + +--- + +Developers often don't think about: + +```text +Immutability + +Reference Changes + +Object Replacement +``` + +--- + +# The Problem With Default Strategy + +Imagine: + +```text +App + +↓ + +Dashboard + +↓ + +100 Employee Cards + +↓ + +Nested Components +``` + +--- + +User clicks: + +```text +Employee Card #1 +``` + +--- + +Angular may run checks for: + +```text +Entire Component Tree +``` + +even though a small portion actually changed. + +--- + +# Important Clarification + +Angular does NOT rebuild the entire DOM. + +--- + +Common misconception: + +```text +Angular Re-Renders Everything +``` + +--- + +Reality: + +```text +Angular Checks Bindings + +↓ + +Updates Only Changed Bindings +``` + +--- + +Example + +Template: + +```html +{{ employee.name }} +``` + +--- + +Angular remembers: + +```text +Previous Value: +John +``` + +--- + +Current Value: + +```text +Adarsh +``` + +--- + +Difference found: + +```text +Update DOM +``` + +--- + +# OnPush Strategy + +One of the most common senior interview topics. + +--- + +Default: + +```text +Check Everything +``` + +--- + +OnPush: + +```text +Check Less Often +``` + +--- + +Example + +```typescript +@Component({ + + changeDetection: + ChangeDetectionStrategy.OnPush + +}) +export class UserComponent { + +} +``` + +--- + +# Why OnPush Exists + +Large applications may contain: + +```text +Hundreds + +Thousands + +Of Components +``` + +--- + +Default strategy checks many components. + +--- + +OnPush reduces unnecessary work. + +--- + +Result: + +```text +Potentially Better Performance +``` + +--- + +# How OnPush Works + +Instead of checking constantly, Angular checks only when specific triggers occur. + +--- + +# Trigger #1 + +Input Reference Changes + +--- + +Parent + +```typescript +user = { + name: 'John' +}; +``` + +--- + +Template + +```html + + +``` + +--- + +Child + +```typescript +OnPush +``` + +--- + +Later + +```typescript +this.user = { + name: 'Adarsh' +}; +``` + +--- + +Reference changes. + +Angular detects: + +```text +New Object Reference +``` + +--- + +Change Detection runs. + +--- + +# Trigger #2 + +Events Inside Component + +Example + +```html + + +} +``` + +--- + +Only authorized users see action. + +--- + +# Important Security Rule + +Never trust Angular authorization. + +--- + +Bad Assumption + +```text +Delete Button Hidden + +↓ + +Application Secure +``` + +--- + +Wrong. + +User can still: + +```text +Call API Directly + +Forge Requests + +Manipulate Browser +``` + +--- + +Frontend authorization provides: + +```text +User Experience +``` + +--- + +Backend authorization provides: + +```text +Actual Security +``` + +--- + +# Server-Side Authorization + +Always enforce permissions server-side. + +--- + +Frontend + +```text +Hide Unauthorized Actions +``` + +--- + +Backend + +```text +Validate Permissions + +Protect Resources + +Enforce Security +``` + +--- + +Both are required. + +--- + +# Complete Enterprise Flow + +```text +User Opens App + +↓ + +Login Screen + +↓ + +Credentials Submitted + +↓ + +Backend Validation + +↓ + +JWT Issued + +↓ + +Token Stored + +↓ + +Auth State Updated + +↓ + +Interceptor Adds Token + +↓ + +Protected API Requests + +↓ + +Guards Protect Routes + +↓ + +Authorization Rules Applied + +↓ + +Permission-Based UI + +↓ + +Backend Validation +``` + +--- + +# Logout Flow + +```text +User Clicks Logout + +↓ + +Clear Access Token + +↓ + +Clear Refresh Token + +↓ + +Clear User State + +↓ + +Redirect To Login +``` + +--- + +Example + +```typescript +logout() { + + this.authStore.clear(); + + localStorage.removeItem( + 'token' + ); + + this.router.navigate( + ['/login'] + ); + +} +``` + +--- + +# Common Interview Questions + +## Authentication vs Authorization? + +Authentication: + +```text +Who Are You? +``` + +Authorization: + +```text +What Can You Do? +``` + +--- + +## What Is JWT? + +Signed token containing identity and claims. + +--- + +## Is JWT Encrypted? + +No. + +JWT is signed. + +Not encrypted. + +--- + +## What Is A Claim? + +A piece of information stored inside a JWT payload. + +--- + +## What Is Access Token? + +Token used to access APIs. + +--- + +## What Is Refresh Token? + +Token used to obtain a new access token. + +--- + +## Why Use Interceptors? + +Automatically attach authentication information to requests. + +--- + +## Why Use Guards? + +Protect routes from unauthenticated users. + +--- + +## What Is RBAC? + +Role-Based Access Control. + +--- + +## Where Should Tokens Be Stored? + +Preferred answer: + +```text +HttpOnly Secure Cookies +``` + +when possible. + +--- + +## Should Authorization Exist Only In Angular? + +No. + +Backend must always enforce authorization. + +--- + +# Common Interview Traps + +## Trap 1 + +Confusing Authentication and Authorization. + +--- + +## Trap 2 + +Thinking JWT Is Encrypted. + +--- + +## Trap 3 + +Storing Secrets Inside JWT Payload. + +--- + +## Trap 4 + +Trusting Frontend Authorization. + +--- + +## Trap 5 + +Ignoring Refresh Token Flow. + +--- + +## Trap 6 + +Not Handling Token Expiration. + +--- + +## Trap 7 + +Keeping Authorization Logic Only In UI. + +--- + +# Senior-Level Mental Model + +Authentication answers: + +```text +Who Are You? +``` + +Authorization answers: + +```text +What Can You Do? +``` + +--- + +Enterprise Flow + +```text +Identity + +↓ + +JWT + +↓ + +Auth State + +↓ + +Interceptor + +↓ + +Protected APIs + +↓ + +Guards + +↓ + +Roles + +↓ + +Permissions + +↓ + +Authorization + +↓ + +Backend Enforcement +``` + +--- + +# Key Takeaways + +1. Authentication verifies identity. +2. Authorization verifies permissions. +3. JWT is the most common SPA authentication mechanism. +4. JWT is signed, not encrypted. +5. Claims contain user information and permissions. +6. Access tokens should be short-lived. +7. Refresh tokens provide long-lived authentication. +8. Authentication state is application state. +9. Interceptors automatically attach tokens. +10. Guards protect routes. +11. RBAC is the most common authorization model. +12. Permission-based authorization provides more flexibility. +13. Frontend authorization improves UX. +14. Backend authorization provides actual security. +15. Authentication and Authorization are foundational enterprise architecture concepts. + +--- + +# Interview Revision Sheet + +```text +Authentication + = Who Are You? + +Authorization + = What Can You Do? + +JWT + = Signed Token + +Access Token + = API Access + +Refresh Token + = Renew Access Token + +Interceptor + = Attach Token + +Guard + = Protect Route + +RBAC + = Role-Based Access + +Permissions + = Fine-Grained Access + +Backend + = Real Security Boundary +``` + +--- + +# Complete Flow To Remember + +```text +Login + +↓ + +JWT Issued + +↓ + +Token Stored + +↓ + +Interceptor Adds Token + +↓ + +API Calls + +↓ + +Guard Protects Routes + +↓ + +Role/Permission Checks + +↓ + +Backend Validates Access + +↓ + +Response Returned +``` + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [NgRx Fundamentals](31-ngrx-fundamentals.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [Error Handling Strategy](33-error-handling-strategy.md) + +
+ diff --git a/apps/portal/public/assets/content/06-senior-architecture/33-error-handling-strategy.md b/apps/portal/public/assets/content/06-senior-architecture/33-error-handling-strategy.md new file mode 100644 index 0000000..cc46b28 --- /dev/null +++ b/apps/portal/public/assets/content/06-senior-architecture/33-error-handling-strategy.md @@ -0,0 +1,1515 @@ +# Error Handling Strategy + +## Interview Priority + +**Must Know** + +## Interview Frequency + +**High** + +## Recommended Depth + +**Senior / Architect Level Understanding** + +## Relevant For + +- Enterprise Applications +- HTTP Communication +- Authentication +- Interceptors +- Observability +- Reliability Engineering +- Angular Architecture +- Senior Angular Interviews + +--- + +# First Principles + +A common misconception is: + +```text +Errors Are Exceptional +``` + +In production systems: + +```text +Errors Are Normal +``` + +Examples: + +```text +Network Failures + +Server Failures + +Expired Tokens + +Validation Failures + +Timeouts + +Database Issues + +Third-Party Service Outages +``` + +A good application is not judged by how it behaves when everything works. + +It is judged by: + +```text +How Gracefully It Handles Failure +``` + +--- + +# What Is Error Handling Strategy? + +Error Handling Strategy is: + +> A structured approach for detecting, classifying, handling, logging, monitoring, and recovering from failures. + +--- + +# Goals Of Error Handling + +```text +Protect User Experience + +Prevent Application Crashes + +Enable Recovery + +Provide Meaningful Feedback + +Support Monitoring + +Improve Debugging +``` + +--- + +# What Can Go Wrong? + +Imagine: + +```http +GET /employees +``` + +Potential failures: + +```text +No Internet + +DNS Failure + +Server Down + +Timeout + +401 Unauthorized + +403 Forbidden + +404 Not Found + +500 Internal Server Error + +Malformed JSON + +Angular Runtime Exception +``` + +--- + +# Error Classification + +One of the most important senior-level concepts. + +--- + +# Category 1: Runtime Errors + +Errors occurring inside Angular. + +--- + +Example + +```typescript +user.name.toUpperCase(); +``` + +when: + +```typescript +user === undefined +``` + +--- + +Result + +```text +TypeError +``` + +--- + +Examples + +```text +Cannot Read Property + +Null Reference + +Template Errors + +JavaScript Exceptions +``` + +--- + +# Category 2: Network Errors + +Communication never reaches backend. + +--- + +Examples + +```text +No Internet Connection + +DNS Failure + +Connection Refused + +Timeout +``` + +--- + +Angular often reports: + +```typescript +status === 0 +``` + +--- + +# Category 3: Authentication Errors + +Example + +```http +401 Unauthorized +``` + +--- + +Meaning + +```text +Identity Cannot Be Verified +``` + +--- + +Causes + +```text +Expired Token + +Invalid Token + +Missing Token +``` + +--- + +# Category 4: Authorization Errors + +Example + +```http +403 Forbidden +``` + +--- + +Meaning + +```text +Authenticated + +But Not Allowed +``` + +--- + +Examples + +```text +Insufficient Role + +Missing Permission +``` + +--- + +# Category 5: Business Validation Errors + +Business rules violated. + +--- + +Examples + +```text +Employee Already Exists + +Salary Cannot Be Negative + +Project Already Closed +``` + +--- + +Typical Status + +```http +400 Bad Request +``` + +--- + +# Category 6: Server Errors + +Backend failed unexpectedly. + +--- + +Examples + +```http +500 Internal Server Error + +502 Bad Gateway + +503 Service Unavailable +``` + +--- + +Meaning + +```text +Server Problem +``` + +--- + +# Error Handling Pyramid + +Errors should be handled at appropriate layers. + +```text +Global Error Handler + ↑ + +HTTP Interceptors + ↑ + +Services + ↑ + +Components +``` + +--- + +# Guiding Principle + +```text +Handle Errors +As High As Reasonably Possible +``` + +Avoid duplicated handling logic. + +--- + +# Poor Error Handling + +Example + +```typescript +this.employeeService + .getEmployees() + .subscribe({ + + next: result => {}, + + error: error => {} + + }); +``` + +Repeated everywhere: + +```text +Employees Component + +Projects Component + +Dashboard Component + +Settings Component +``` + +--- + +Problems + +```text +Code Duplication + +Inconsistent UX + +Maintenance Issues +``` + +--- + +# Good Error Handling + +Centralize: + +```text +Authentication Errors + +Authorization Errors + +Logging + +Monitoring + +Error Mapping +``` + +--- + +# Layer 1: Component-Level Handling + +Used for: + +```text +UI-Specific Reactions +``` + +--- + +Example + +```typescript +loadEmployees() { + + this.employeeService + .getEmployees() + .subscribe({ + + next: result => { + + this.employees = result; + + }, + + error: () => { + + this.errorMessage = + 'Unable to load employees'; + + } + + }); + +} +``` + +--- + +Use For + +```text +Validation Messages + +View-Specific Messages + +Screen-Specific UX +``` + +--- + +Avoid Using For + +```text +Token Refresh + +Authentication + +Logging + +Monitoring +``` + +--- + +# Layer 2: Service-Level Handling + +Services may transform technical errors into business errors. + +--- + +Example + +```typescript +getEmployees() { + + return this.http + .get('/employees') + .pipe( + + catchError(error => { + + return throwError( + () => + new Error( + 'Employee Service Failed' + ) + ); + + }) + + ); + +} +``` + +--- + +Benefits + +```text +Centralized Service Logic + +Reusable + +Consistent +``` + +--- + +# Layer 3: HTTP Interceptor + +One of the most important enterprise patterns. + +--- + +Why? + +Interceptor sees: + +```text +Every Request + +Every Response + +Every HTTP Error +``` + +--- + +Perfect For + +```text +401 Handling + +403 Handling + +Global Logging + +Token Refresh + +Error Mapping +``` + +--- + +# Global Error Interceptor + +```typescript +@Injectable() +export class ErrorInterceptor +implements HttpInterceptor { + + intercept(req, next) { + + return next.handle(req) + + .pipe( + + catchError(error => { + + switch(error.status) { + + case 401: + break; + + case 403: + break; + + case 500: + break; + + } + + return throwError( + () => error + ); + + }) + + ); + + } + +} +``` + +--- + +# Why Interceptors? + +Without interceptor: + +```text +401 Logic + +Repeated Everywhere +``` + +--- + +With interceptor: + +```text +Single Place +``` + +--- + +# Handling 401 Unauthorized + +Most common enterprise scenario. + +--- + +Typical Cause + +```text +Access Token Expired +``` + +--- + +Flow + +```text +API Returns 401 + +↓ + +Interceptor Detects + +↓ + +Refresh Token Request + +↓ + +New Access Token Issued + +↓ + +Original Request Retried + +↓ + +User Continues Working +``` + +--- + +User Experiences + +```text +No Interruption +``` + +--- + +# Handling 403 Forbidden + +Example + +```http +403 Forbidden +``` + +--- + +Meaning + +```text +User Is Authenticated + +But Lacks Permission +``` + +--- + +Typical UX + +```text +Access Denied Page + +Permission Warning + +Feature Hidden +``` + +--- + +# Handling 404 Not Found + +Example + +```http +404 Not Found +``` + +--- + +Example Scenario + +```text +Employee Does Not Exist +``` + +--- + +Do NOT treat every 404 as a system crash. + +--- + +Better UX + +```text +Employee Not Found +``` + +instead of: + +```text +Application Failed +``` + +--- + +# Handling 500 Internal Server Error + +Example + +```http +500 Internal Server Error +``` + +--- + +Never expose raw server details. + +--- + +Bad + +```text +Object Reference Not Set... +``` + +--- + +Good + +```text +Something Went Wrong + +Please Try Again Later +``` + +--- + +# Error Mapping + +Enterprise applications often standardize backend errors. + +--- + +Backend + +```json +{ + "code": "EMPLOYEE_EXISTS", + "message": "Employee already exists" +} +``` + +--- + +Frontend Mapping + +```typescript +{ + userMessage: + 'Employee already exists.' +} +``` + +--- + +Benefits + +```text +Consistent UX + +Standardized Error Handling + +Localization Support +``` + +--- + +# Retry Strategy + +Important senior interview topic. + +--- + +Not every error should be retried. + +--- + +Good Retry Candidates + +```text +Network Failures + +Temporary Server Errors + +Timeouts +``` + +--- + +Example + +```typescript +http.get(...) + +.pipe( + retry(3) +) +``` + +--- + +# Retry With Delay + +```typescript +retry({ + count: 3, + delay: 1000 +}) +``` + +--- + +# What Should NOT Be Retried? + +Do not automatically retry: + +```http +400 + +401 + +403 +``` + +--- + +Reason + +```text +Retrying Won't Fix The Problem +``` + +--- + +# Exponential Backoff + +Enterprise systems frequently use: + +```text +1s + +2s + +4s + +8s +``` + +between retries. + +--- + +Benefits + +```text +Reduce Server Pressure + +Avoid Retry Storms +``` + +--- + +# Angular Global ErrorHandler + +Angular provides: + +```typescript +ErrorHandler +``` + +--- + +Purpose + +```text +Catch Unhandled Runtime Errors +``` + +--- + +Example + +```typescript +@Injectable() +export class GlobalErrorHandler +implements ErrorHandler { + + handleError( + error: any + ): void { + + console.error(error); + + } + +} +``` + +--- + +Captures + +```text +Runtime Exceptions + +Template Failures + +Unexpected Application Errors +``` + +--- + +Think Of It As + +```text +Last Line Of Defense +``` + +--- + +# Global ErrorHandler Registration + +```typescript +providers: [ + { + provide: ErrorHandler, + useClass: GlobalErrorHandler + } +] +``` + +--- + +# Logging Strategy + +Senior-level discussion topic. + +--- + +Question + +```text +Where Do Errors Go? +``` + +--- + +Bad + +```text +Console.log() +``` + +only. + +--- + +Enterprise Systems Use + +```text +Azure Application Insights + +Datadog + +Splunk + +Elastic + +New Relic + +Sentry +``` + +--- + +Flow + +```text +Error + +↓ + +Global Error Handler + +↓ + +Monitoring Platform + +↓ + +Alert + +↓ + +Investigation +``` + +--- + +# User-Friendly Error Messages + +Never expose technical details. + +--- + +Bad + +```text +Cannot Read Property 'name' +of Undefined +``` + +--- + +Good + +```text +Something Went Wrong + +Please Try Again Later +``` + +--- + +# Error State Pattern + +Very common modern Angular pattern. + +--- + +Instead Of + +```typescript +employees = signal([]); +``` + +Use + +```typescript +employees = + signal([]); + +loading = + signal(false); + +error = + signal( + null + ); +``` + +--- + +Request Starts + +```typescript +loading.set(true); + +error.set(null); +``` + +--- + +Success + +```typescript +loading.set(false); +``` + +--- + +Failure + +```typescript +loading.set(false); + +error.set( + 'Unable to load employees' +); +``` + +--- + +# UI Error States + +Template + +```html +@if (loading()) { + + + +} +@else if (error()) { + + + +} +@else { + + + +} +``` + +--- + +Benefits + +```text +Clear UX + +Predictable States + +Easy Testing +``` + +--- + +# Result Pattern + +Advanced architecture approach. + +--- + +Instead of throwing exceptions everywhere: + +```typescript +throw error; +``` + +Return: + +```typescript +type Result = { + + success: boolean; + + data?: T; + + error?: string; + +}; +``` + +--- + +Example + +```typescript +{ + success: false, + error: 'Employee not found' +} +``` + +--- + +Benefits + +```text +Explicit Failures + +Predictable Behavior + +Improved Testability +``` + +--- + +# Monitoring & Observability + +Enterprise applications must answer: + +```text +What Failed? + +When Did It Fail? + +Who Did It Affect? + +How Often Does It Fail? +``` + +--- + +Error handling and observability work together. + +--- + +Examples + +```text +Application Insights + +OpenTelemetry + +Datadog + +Elastic Stack +``` + +--- + +# Common Interview Questions + +## What Layers Handle Errors? + +```text +Component + +Service + +Interceptor + +Global Error Handler +``` + +--- + +## Why Use HTTP Interceptors? + +Centralized error processing. + +--- + +## How Do You Handle 401? + +```text +Refresh Token + +Retry Request + +Redirect To Login If Refresh Fails +``` + +--- + +## Difference Between 401 And 403? + +401 + +```text +Not Authenticated +``` + +403 + +```text +Authenticated But Not Authorized +``` + +--- + +## Why Use Global ErrorHandler? + +Capture unexpected runtime errors. + +--- + +## Should Users See Technical Errors? + +No. + +Provide user-friendly messages. + +--- + +## When Should Retry Be Used? + +Transient failures only. + +--- + +# Common Interview Traps + +## Trap 1 + +Handling every error in components. + +--- + +## Trap 2 + +Swallowing errors. + +```typescript +catchError(() => EMPTY) +``` + +without logging. + +--- + +## Trap 3 + +Infinite retries. + +--- + +## Trap 4 + +Displaying stack traces to users. + +--- + +## Trap 5 + +Treating all errors equally. + +--- + +## Trap 6 + +Ignoring monitoring and observability. + +--- + +# Enterprise Error Strategy + +```text +Runtime Errors + ↓ +Global Error Handler + +HTTP Errors + ↓ +Interceptor + +Business Errors + ↓ +Service Layer + +UI Errors + ↓ +Component Layer + +Monitoring + ↓ +Observability Platform +``` + +--- + +# Senior-Level Mental Model + +Error handling is not: + +```text +try/catch +``` + +Error handling is: + +```text +Detection + +↓ + +Classification + +↓ + +Logging + +↓ + +Recovery + +↓ + +User Feedback + +↓ + +Monitoring +``` + +--- + +# Key Takeaways + +1. Failures are normal in production systems. +2. Errors should be categorized (runtime, network, auth, authorization, business, server). +3. Handle errors at the highest reasonable layer. +4. Use HTTP interceptors for centralized HTTP error handling. +5. Use Angular Global ErrorHandler for unexpected runtime failures. +6. Refresh tokens should handle most 401 responses. +7. Do not expose technical details to end users. +8. Retry only transient failures. +9. Implement structured logging and monitoring. +10. Error handling, observability, and recovery are core enterprise architecture concerns. + +--- + +# Interview Revision Sheet + +```text +401 + = Not Authenticated + +403 + = Not Authorized + +404 + = Resource Not Found + +500 + = Server Failure + +Interceptor + = Central HTTP Error Handling + +Global ErrorHandler + = Runtime Error Handling + +Retry + = Temporary Failures Only + +Monitoring + = Production Visibility + +Error Handling + = Detection + Recovery + Observability +``` + +--- + +# Complete Flow To Remember + +```text +Error Occurs + +↓ + +Classify Error + +↓ + +Handle At Appropriate Layer + +↓ + +Log Error + +↓ + +Recover If Possible + +↓ + +Show User-Friendly Message + +↓ + +Monitor & Alert + +↓ + +Improve System +``` + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [Authentication & Authorization](32-authentication-authorization.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [Enterprise Architecture](34-enterprise-architecture.md) + +
+ diff --git a/apps/portal/public/assets/content/06-senior-architecture/34-enterprise-architecture.md b/apps/portal/public/assets/content/06-senior-architecture/34-enterprise-architecture.md new file mode 100644 index 0000000..96fb555 --- /dev/null +++ b/apps/portal/public/assets/content/06-senior-architecture/34-enterprise-architecture.md @@ -0,0 +1,1678 @@ +# Enterprise Architecture + +## Interview Priority + +**Must Know For Senior Engineers** + +## Interview Frequency + +**Very High (Senior / Lead / Architect Roles)** + +## Recommended Depth + +**Architectural Understanding** + +## Relevant For + +- Enterprise Applications +- Large Teams +- Angular Architecture +- System Design Discussions +- Technical Leadership +- Senior Angular Interviews + +--- + +# First Principles + +Before discussing Angular architecture, we should answer: + +```text +Why Does Architecture Exist? +``` + +--- + +Imagine two applications. + +--- + +Application A + +```text +5 Screens + +2 Developers + +6 Months Lifetime +``` + +--- + +Application B + +```text +120 Screens + +50 Developers + +10 Teams + +5 Years Of Development + +Monthly Releases +``` + +--- + +Will both applications need the same structure? + +```text +No +``` + +--- + +Small applications optimize for: + +```text +Speed Of Development +``` + +--- + +Enterprise applications optimize for: + +```text +Scalability + +Maintainability + +Predictability + +Testability + +Team Productivity +``` + +--- + +# Definition + +Enterprise Architecture is: + +> The set of structural decisions that define how an application is organized, scaled, secured, maintained, tested, monitored, and evolved over time. + +--- + +# Architecture vs Implementation + +Junior developers often focus on: + +```typescript +How To Build A Component +``` + +--- + +Senior engineers focus on: + +```text +Where Should Components Live? + +How Should Teams Organize? + +How Will New Features Be Added? + +How Will We Prevent Architectural Decay? +``` + +--- + +# What Problems Does Enterprise Architecture Solve? + +Without architecture: + +```text +Huge Components + +Code Duplication + +Tight Coupling + +Slow Development + +Frequent Bugs + +Difficult Onboarding +``` + +--- + +Good architecture provides: + +```text +Clear Boundaries + +Consistent Structure + +Reusable Components + +Independent Teams + +Predictable Growth +``` + +--- + +# Core Architectural Principles + +Everything else builds on these principles. + +--- + +# Principle 1: Separation Of Concerns + +Most important architectural principle. + +--- + +Every layer should have a clear responsibility. + +--- + +Bad + +```typescript +EmployeeComponent +``` + +contains: + +```text +UI Logic + +API Calls + +Validation + +Business Rules + +State Management + +Error Handling +``` + +--- + +Results: + +```text +Hard To Maintain + +Hard To Test + +Hard To Reuse +``` + +--- + +Good + +```text +Component + ↓ +Store + ↓ +Service + ↓ +HTTP +``` + +--- + +Each layer owns a single concern. + +--- + +# Principle 2: Single Responsibility Principle + +Every unit should have one reason to change. + +--- + +Bad + +```typescript +AuthService +``` + +does: + +```text +Authentication + +Employee APIs + +Logging + +Notifications + +Configuration +``` + +--- + +Good + +```text +AuthService + +EmployeeService + +LoggerService + +NotificationService +``` + +--- + +Responsibilities remain focused. + +--- + +# Principle 3: High Cohesion + +Things that belong together should stay together. + +--- + +Example + +All employee-related code should live together. + +--- + +Good + +```text +employees/ + + components/ + + services/ + + models/ + + store/ +``` + +--- + +Bad + +```text +components/ + +services/ + +models/ +``` + +with employee code scattered everywhere. + +--- + +# Principle 4: Low Coupling + +Systems should depend on each other as little as possible. + +--- + +Bad + +```text +Employee Module + ↓ +Project Module + ↓ +Report Module + ↓ +Notification Module +``` + +--- + +Everything depends on everything. + +--- + +Good + +```text +Shared Contracts + +↓ + +Independent Features +``` + +--- + +Features evolve independently. + +--- + +# Angular Architecture Evolution + +Most Angular applications evolve through stages. + +--- + +Stage 1 + +```text +Everything In AppComponent +``` + +--- + +Stage 2 + +```text +Multiple Components +``` + +--- + +Stage 3 + +```text +Services +``` + +--- + +Stage 4 + +```text +Feature Modules +``` + +--- + +Stage 5 + +```text +Standalone Components +``` + +--- + +Stage 6 + +```text +Enterprise Architecture +``` + +--- + +# Modern Angular Architecture + +Today most large applications follow: + +```text +Core Layer + +↓ + +Shared Layer + +↓ + +Feature Layer +``` + +--- + +# High-Level Structure + +```text +src/ + + app/ + + core/ + + shared/ + + features/ +``` + +--- + +This is one of the most common enterprise approaches. + +--- + +# Core Layer + +Core contains: + +```text +Application-Wide Concerns +``` + +--- + +Question: + +```text +Would Multiple Features Need This? +``` + +If yes: + +```text +Core Candidate +``` + +--- + +Typical Core Contents + +```text +Authentication + +Authorization + +Interceptors + +Guards + +Logging + +Configuration + +Error Handling + +API Infrastructure +``` + +--- + +Example Structure + +```text +core/ + + auth/ + + guards/ + + interceptors/ + + services/ + + configuration/ + + logging/ +``` + +--- + +# Core Layer Rules + +Core should contain: + +✅ Application-wide services + +✅ Cross-cutting concerns + +✅ Infrastructure + +--- + +Core should NOT contain: + +❌ Employee business logic + +❌ Project business logic + +❌ Report business logic + +--- + +# Shared Layer + +Shared contains reusable UI building blocks. + +--- + +Examples + +```text +Button + +Modal + +Table + +Dropdown + +Date Pipe + +Skeleton Loader +``` + +--- + +Question: + +```text +Can This Be Reused +Across Features? +``` + +If yes: + +```text +Shared Candidate +``` + +--- + +Example Structure + +```text +shared/ + + components/ + + directives/ + + pipes/ + + ui/ + + utilities/ +``` + +--- + +# Shared Layer Rules + +Shared should contain: + +✅ Generic components + +✅ Generic directives + +✅ Generic pipes + +✅ Generic utilities + +--- + +Shared should NOT contain: + +❌ Employee Rules + +❌ Payroll Logic + +❌ Domain-Specific Behavior + +--- + +# Feature Layer + +Feature layer contains business capabilities. + +--- + +Examples + +```text +Employees + +Projects + +Reports + +Admin + +Payroll +``` + +--- + +Everything related to a business domain stays together. + +--- + +Example + +```text +features/ + + employees/ + + projects/ + + reports/ +``` + +--- + +# Employee Feature Structure + +```text +employees/ + + pages/ + + components/ + + services/ + + store/ + + models/ + + routes.ts +``` + +--- + +Benefits + +```text +Feature Isolation + +Team Ownership + +Easy Maintenance +``` + +--- + +# Feature-Based Architecture + +One of the most common interview topics. + +--- + +Bad + +```text +components/ + +services/ + +models/ + +pipes/ +``` + +--- + +Problem + +Employee code exists everywhere. + +--- + +Good + +```text +features/ + + employees/ + + projects/ + + reports/ +``` + +--- + +Everything related to a domain lives together. + +--- + +Benefits + +```text +Discoverability + +Scalability + +Team Independence +``` + +--- + +# Layered Architecture + +A common enterprise pattern. + +--- + +```text +Presentation Layer + +↓ + +State Layer + +↓ + +Service Layer + +↓ + +API Layer +``` + +--- + +Each layer has one responsibility. + +--- + +# Presentation Layer + +Contains: + +```text +Pages + +Components + +Templates +``` + +--- + +Responsibilities + +```text +Display Data + +Capture User Interaction +``` + +--- + +Should not contain: + +```text +Complex Business Rules + +Direct API Calls +``` + +--- + +# Service Layer + +Contains: + +```text +Business Logic + +API Interaction + +Workflows +``` + +--- + +Example + +```typescript +EmployeeService +``` + +--- + +Responsibilities + +```text +Load Employees + +Create Employee + +Delete Employee +``` + +--- + +# State Layer + +Contains: + +```text +Signals + +Stores + +NgRx +``` + +--- + +Responsibilities + +```text +Store State + +Expose State + +Manage Updates +``` + +--- + +Example + +```typescript +EmployeeStore +``` + +--- + +# API Layer + +Responsible for: + +```text +Backend Communication +``` + +--- + +Example + +```typescript +HttpClient +``` + +--- + +Components should rarely call APIs directly. + +--- + +Bad + +```typescript +EmployeeComponent + +↓ + +http.get(...) +``` + +--- + +Good + +```text +EmployeeComponent + +↓ + +EmployeeStore + +↓ + +EmployeeService + +↓ + +HttpClient +``` + +--- + +# Container vs Presentational Components + +Very common enterprise pattern. + +--- + +# Presentational Components + +Responsible for: + +```text +Rendering +``` + +--- + +Example + +```typescript +EmployeeCardComponent +``` + +--- + +Inputs + +```typescript +@Input() +employee +``` + +--- + +Outputs + +```typescript +@Output() +selected +``` + +--- + +Characteristics + +```text +Reusable + +Simple + +No API Calls +``` + +--- + +# Container Components + +Responsible for: + +```text +Coordination + +Workflows + +State Interaction +``` + +--- + +Example + +```typescript +EmployeePageComponent +``` + +--- + +Responsibilities + +```text +Call Store + +Load Data + +Handle Events +``` + +--- + +Pattern + +```text +Container + +↓ + +Presentational Components +``` + +--- + +# Routing Architecture + +Each feature owns its routes. + +--- + +Example + +```text +employees/routes.ts + +projects/routes.ts + +reports/routes.ts +``` + +--- + +Benefits + +```text +Feature Independence +``` + +--- + +# Lazy Loading Strategy + +Enterprise applications should lazy-load large features. + +--- + +Example + +```typescript +{ + path: 'employees', + + loadChildren: () => + import('./employees/routes') +} +``` + +--- + +Benefits + +```text +Smaller Bundles + +Faster Startup + +Better UX +``` + +--- + +# State Management Architecture + +Not all state is equal. + +--- + +# Local State + +Examples + +```text +Modal Open + +Selected Tab + +Accordion +``` + +Keep local. + +--- + +# Feature State + +Examples + +```text +Employees + +Projects + +Reports +``` + +Use: + +```text +Signals + +Service Stores +``` + +--- + +# Global State + +Examples + +```text +Current User + +Theme + +Permissions + +Language +``` + +Use: + +```text +App Store + +Signal Store + +NgRx +``` + +when complexity requires. + +--- + +# Recommended Modern Approach + +```text +Local State + +↓ + +Signals + +↓ + +Service Store Pattern + +↓ + +NgRx Only If Necessary +``` + +--- + +# Security Architecture + +Enterprise architecture includes: + +```text +Authentication + +Authorization + +Token Management + +Route Protection +``` + +--- + +Typical Components + +```text +AuthService + +AuthStore + +AuthInterceptor + +AuthGuard + +PermissionGuard +``` + +--- + +Flow + +```text +Login + +↓ + +Store User + +↓ + +Attach Token + +↓ + +Secure APIs + +↓ + +Validate Permissions +``` + +--- + +# Error Handling Architecture + +Errors should be layered. + +--- + +Architecture + +```text +Component + +↓ + +Service + +↓ + +Interceptor + +↓ + +Global Error Handler +``` + +--- + +Benefits + +```text +Centralized Logic + +Consistent UX + +Observability +``` + +--- + +# Observability Architecture + +Senior-level topic. + +--- + +Enterprise applications require: + +```text +Logging + +Metrics + +Tracing + +Monitoring +``` + +--- + +Questions Architecture Should Answer + +```text +What Failed? + +When Did It Fail? + +How Often? + +Who Was Affected? +``` + +--- + +Tools + +```text +Application Insights + +Datadog + +Sentry + +Elastic + +OpenTelemetry +``` + +--- + +# Configuration Architecture + +Never hardcode environment values. + +--- + +Bad + +```typescript +const apiUrl = + 'https://prod-api'; +``` + +--- + +Good + +```text +Configuration Service + +Environment Files + +Runtime Configuration +``` + +--- + +Benefits + +```text +Deployment Flexibility + +Environment Independence +``` + +--- + +# Testing Architecture + +Enterprise applications require multiple testing levels. + +--- + +# Unit Testing + +Test: + +```text +Components + +Services + +Stores +``` + +--- + +# Integration Testing + +Test: + +```text +Feature Interactions +``` + +--- + +# End-To-End Testing + +Test: + +```text +Entire User Journeys +``` + +Examples: + +```text +Login + +Create Employee + +Generate Report +``` + +--- + +# Dependency Injection Architecture + +DI becomes increasingly important at scale. + +--- + +Benefits + +```text +Loose Coupling + +Easy Testing + +Replaceable Implementations +``` + +--- + +Example + +```typescript +AuthService +``` + +can easily be mocked. + +--- + +# Cross-Cutting Concerns + +Very important architecture term. + +--- + +Definition + +```text +Concerns Affecting +Multiple Parts Of The Application +``` + +--- + +Examples + +```text +Authentication + +Authorization + +Logging + +Caching + +Monitoring + +Error Handling +``` + +--- + +These belong in: + +```text +Core Services + +Interceptors + +Infrastructure Layers +``` + +--- + +Not inside business features. + +--- + +# Enterprise Folder Structure + +Angular Standalone Example + +```text +src/ + + app/ + + core/ + + auth/ + + guards/ + + interceptors/ + + logging/ + + configuration/ + + shared/ + + ui/ + + pipes/ + + directives/ + + utilities/ + + features/ + + employees/ + + pages/ + + components/ + + services/ + + store/ + + models/ + + routes.ts + + projects/ + + reports/ + + admin/ +``` + +--- + +# Architecture Decision Framework + +When adding functionality ask: + +```text +Is It Reusable? + +↓ + +Shared +``` + +--- + +```text +Is It Application-Wide? + +↓ + +Core +``` + +--- + +```text +Does It Belong To A Business Domain? + +↓ + +Feature +``` + +--- + +# Common Interview Questions + +## What Is Enterprise Architecture? + +A set of structural decisions enabling long-term scalability, maintainability, and team productivity. + +--- + +## Why Organize By Feature? + +Keeps related code together and improves ownership. + +--- + +## What Belongs In Core? + +```text +Auth + +Guards + +Interceptors + +Logging + +Configuration + +Error Handling +``` + +--- + +## What Belongs In Shared? + +```text +Reusable Components + +Pipes + +Directives + +Utilities +``` + +--- + +## Why Use Lazy Loading? + +Reduce bundle size and improve startup performance. + +--- + +## Container vs Presentational Components? + +Container: + +```text +Behavior & State +``` + +Presentational: + +```text +Rendering +``` + +--- + +## Where Should API Calls Live? + +Services. + +Not components. + +--- + +## What Are Cross-Cutting Concerns? + +Concerns affecting multiple features and layers + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [Error Handling Strategy](33-error-handling-strategy.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [Micro Frontends](35-micro-frontends.md) + +
+ diff --git a/apps/portal/public/assets/content/06-senior-architecture/35-micro-frontends.md b/apps/portal/public/assets/content/06-senior-architecture/35-micro-frontends.md new file mode 100644 index 0000000..3d0be61 --- /dev/null +++ b/apps/portal/public/assets/content/06-senior-architecture/35-micro-frontends.md @@ -0,0 +1,1966 @@ +# Micro Frontends + +## Interview Priority + +**Advanced / Architect Level** + +## Interview Frequency + +**Medium to High (Senior, Lead, Architect Roles)** + +## Recommended Depth + +**Conceptual + Architectural Understanding** + +## Relevant For + +- Large Enterprise Applications +- Multi-Team Development +- Platform Engineering +- Enterprise Architecture +- Organizational Scalability +- Angular Architecture +- Technical Leadership + +--- + +# First Principles + +Before discussing Module Federation, Shell Applications, or Shared State, understand the problem. + +Imagine an Angular application: + +```text +Employee Portal +``` + +Features: + +```text +Employees + +Projects + +Reports + +Dashboard + +Administration +``` + +Everything lives inside: + +```text +One Angular Application +``` + +--- + +Initial Team + +```text +2 Developers + +10 Screens +``` + +Works perfectly. + +--- + +After several years: + +```text +50 Developers + +10 Teams + +150 Screens + +Independent Release Cycles +``` + +Problems begin appearing. + +--- + +# Problem 1: Build Size + +A small change requires: + +```text +Build Entire Application +``` + +--- + +Example + +```text +Employee Team +Changes One Component + +↓ + +Entire Application Rebuilt +``` + +--- + +As the application grows: + +```text +Build Times Increase + +Deployment Complexity Increases +``` + +--- + +# Problem 2: Deployment Coupling + +Example + +```text +Employee Feature Ready + +Reports Feature Not Ready +``` + +--- + +Question + +```text +Can Employee Team Deploy? +``` + +Sometimes: + +```text +No +``` + +because all features ship together. + +--- + +# Problem 3: Team Coupling + +Example + +```text +Team A Changes Shared Code + +↓ + +Team B Breaks + +↓ + +Team C Breaks +``` + +--- + +Productivity decreases. + +--- + +# Problem 4: Upgrade Pressure + +Example + +```text +Entire Product Uses Angular 18 +``` + +Team wants Angular 20. + +--- + +Result: + +```text +Large Coordinated Migration +``` + +--- + +# Problem 5: Ownership + +Question: + +```text +Who Owns Reports? + +Who Owns Employees? + +Who Owns Administration? +``` + +--- + +Ownership becomes unclear. + +--- + +# What Are Micro Frontends? + +Micro Frontends apply: + +```text +Microservice Principles +``` + +to: + +```text +Frontend Applications +``` + +--- + +Definition + +> An architectural style where multiple independently developed and independently deployed front-end applications are composed together to form a single user experience. + +--- + +Think: + +```text +Microservices + +For The Frontend +``` + +--- + +# Monolith vs Micro Frontend + +## Traditional Frontend Monolith + +```text +Angular App + +├─ Employees +├─ Projects +├─ Reports +├─ Dashboard +└─ Admin +``` + +Characteristics: + +```text +Single Repo + +Single Build + +Single Deployment + +Single Runtime +``` + +--- + +## Micro Frontend Architecture + +```text +Shell Application + +├─ Employees App +├─ Projects App +├─ Reports App +└─ Admin App +``` + +Characteristics: + +```text +Independent Teams + +Independent Builds + +Independent Deployments + +Independent Ownership +``` + +--- + +# Core Objective + +Users experience: + +```text +One Product +``` + +Teams build: + +```text +Many Applications +``` + +--- + +# Architectural Overview + +```text +Shell Application + + ↓ + +Employees MFE + +Projects MFE + +Reports MFE + +Admin MFE +``` + +--- + +Every Micro Frontend: + +```text +Owns Its Domain + +Owns Its Deployment + +Owns Its Complexity +``` + +--- + +# Conway's Law + +Important architect-level concept. + +--- + +Conway's Law: + +> Systems tend to mirror the communication structure of the organization. + +--- + +Example + +```text +Employee Team + +Projects Team + +Reports Team +``` + +--- + +Micro Frontends often align naturally with those team boundaries. + +--- + +# Domain-Based Ownership + +Boundaries should follow: + +```text +Business Domains +``` + +Not technical concerns. + +--- + +Good + +```text +Employees + +Projects + +Reports + +Payroll + +Administration +``` + +--- + +Bad + +```text +Buttons App + +Forms App + +Tables App +``` + +--- + +Business capabilities provide stable boundaries. + +--- + +# Shell Application + +The shell acts as: + +```text +Host Application +``` + +--- + +Responsibilities + +```text +Authentication + +Global Navigation + +Application Layout + +Loading Micro Frontends + +Global Configuration + +Shared Context +``` + +--- + +Visual + +```text +Shell + +↓ + +Loads Employees + +↓ + +Loads Reports + +↓ + +Loads Projects +``` + +--- + +Think of Shell as: + +```text +Orchestrator +``` + +--- + +# Remote Applications + +Each domain becomes an independent application. + +--- + +Examples + +```text +employees-app + +projects-app + +reports-app + +admin-app +``` + +--- + +Each team owns: + +```text +Development + +Testing + +Build Pipeline + +Deployment +``` + +--- + +# Independent Deployments + +One of the biggest business benefits. + +--- + +Without Micro Frontends + +```text +Deploy Entire Product +``` + +--- + +With Micro Frontends + +```text +Deploy Employees App Only +``` + +--- + +Benefits + +```text +Lower Risk + +Faster Releases + +Higher Team Autonomy +``` + +--- + +# Independent Technology Evolution + +Potentially: + +```text +Employees + +Angular 20 + +Projects + +Angular 19 +``` + +--- + +Because applications are isolated. + +--- + +Architectural governance is still recommended. + +--- + +# Runtime Composition + +Micro Frontends are generally assembled: + +```text +At Runtime +``` + +rather than: + +```text +At Build Time +``` + +--- + +This allows: + +```text +Independent Deployment + +Independent Versioning +``` + +--- + +# Module Federation + +Most common Angular implementation mechanism. + +--- + +Introduced through: + +```text +Webpack Module Federation +``` + +--- + +Core Idea + +```text +Application A + +↓ + +Loads Code From + +↓ + +Application B + +At Runtime +``` + +--- + +Without rebuilding Application A. + +--- + +# Traditional Import + +```typescript +import { + EmployeeModule +} from './employees'; +``` + +--- + +Dependency exists: + +```text +At Build Time +``` + +--- + +# Module Federation Import + +```typescript +loadRemoteModule(...) +``` + +--- + +Dependency resolved: + +```text +At Runtime +``` + +--- + +This is the key difference. + +--- + +# Runtime Loading Flow + +Example + +```text +shell.company.com + +employees.company.com +``` + +--- + +User navigates: + +```text +/employees +``` + +--- + +Shell detects route. + +--- + +Shell loads: + +```text +remoteEntry.js +``` + +from: + +```text +employees.company.com +``` + +--- + +Employee application gets rendered. + +--- + +Result + +```text +Independent Deployment + +Single User Experience +``` + +--- + +# How Module Federation Shares Libraries + +Without sharing: + +```text +Angular Downloaded + +Employees App + +Projects App + +Reports App +``` + +independently. + +--- + +Result: + +```text +Large Downloads + +Memory Waste +``` + +--- + +Module Federation supports: + +```typescript +shared: { + '@angular/core': { + singleton: true + } +} +``` + +--- + +Meaning: + +```text +One Angular Runtime + +Shared Across Applications +``` + +--- + +# Common Shared Dependencies + +```text +Angular Core + +Angular Router + +RxJS + +Design System +``` + +--- + +Benefits + +```text +Smaller Downloads + +Improved Performance + +Reduced Duplication +``` + +--- + +# Version Governance + +One of the biggest enterprise challenges. + +--- + +Scenario + +```text +Shell + +Angular 20 +``` + +--- + +And + +```text +Employee App + +Angular 15 +``` + +--- + +Potential: + +```text +Compatibility Problems +``` + +--- + +Large organizations frequently define: + +```text +Supported Version Policies +``` + +--- + +# Micro Frontend Communication + +A critical architectural topic. + +--- + +Question + +```text +Employee App Selected Employee + +↓ + +How Does Reports App Know? +``` + +--- + +Several patterns exist. + +--- + +# Pattern 1: Browser Events + +Simple approach. + +--- + +Producer + +```typescript +window.dispatchEvent( + new CustomEvent( + 'employee-selected', + { + detail: employee + } + ) +); +``` + +--- + +Consumer + +```typescript +window.addEventListener( + 'employee-selected', + handler +); +``` + +--- + +Flow + +```text +Publisher + +↓ + +Browser Event + +↓ + +Subscriber +``` + +--- + +Advantages + +```text +Simple + +Loose Coupling +``` + +--- + +Challenges + +```text +Not Typed + +Harder To Govern +``` + +--- + +# Pattern 2: Shared Event Bus + +Enterprise favorite. + +--- + +Example + +```typescript +eventBus.publish( + 'employee-selected', + employee +); +``` + +--- + +Consumer + +```typescript +eventBus.subscribe( + 'employee-selected' +); +``` + +--- + +Flow + +```text +App A + +↓ + +Event Bus + +↓ + +App B +``` + +--- + +Benefits + +```text +Centralized + +Testable + +Decoupled +``` + +--- + +Think: + +```text +Platform-Level RxJS +``` + +--- + +# Pattern 3: Shell Mediation + +Very common. + +--- + +Example + +```text +Employee App + +↓ + +Shell + +↓ + +Reports App +``` + +--- + +Employee App sends: + +```text +Selected Employee +``` + +--- + +Shell updates: + +```text +Shared Context +``` + +--- + +Reports reads. + +--- + +Benefits + +```text +Controlled + +Centralized + +Observable +``` + +--- + +# Pattern 4: Backend Communication + +Preferred for business workflows. + +--- + +Example + +```text +Employee Updated +``` + +--- + +Instead of direct frontend communication: + +```text +Employee App + +↓ + +API + +↓ + +Database + +↓ + +Reports App Reads +``` + +--- + +Most scalable strategy. + +--- + +# Communication Guideline + +Use: + +```text +Events +``` + +for UI collaboration. + +--- + +Use: + +```text +Backend APIs +``` + +for business data. + +--- + +Avoid: + +```text +Direct App To App Calls +``` + +--- + +Bad + +```typescript +employeeApp.callReports() +``` + +--- + +Creates tight coupling. + +--- + +# Shared State Management + +One of the most debated micro frontend topics. + +--- + +Question + +```text +Should Every MFE Use One Shared NgRx Store? +``` + +--- + +Generally: + +```text +No +``` + +--- + +Why? + +Micro Frontends exist to achieve: + +```text +Independence +``` + +--- + +Shared store introduces: + +```text +Coupling + +Coordination Costs + +Shared Ownership Problems +``` + +--- + +Eventually: + +```text +Distributed Monolith +``` + +appears. + +--- + +# State Ownership Principle + +A business domain should own its own state. + +--- + +Example + +```text +Employees App + +Owns Employees State +``` + +--- + +```text +Projects App + +Owns Projects State +``` + +--- + +```text +Reports App + +Owns Reports State +``` + +--- + +Ownership remains clear. + +--- + +# What SHOULD Be Shared? + +Only genuinely global concerns. + +--- + +# Shared User Context + +Example + +```typescript +{ + id: 1, + name: 'Adarsh', + role: 'Admin' +} +``` + +--- + +Every application needs this. + +--- + +Candidate: + +✅ Shared + +--- + +Typically owned by: + +```text +Shell +``` + +--- + +# Shared Authentication + +Examples + +```text +Access Token + +Refresh Token + +Current User + +Permissions +``` + +--- + +Usually owned by: + +```text +Shell +``` + +--- + +Micro Frontends consume. + +--- + +# Shared Theme + +Examples + +```text +Light Theme + +Dark Theme +``` + +--- + +User changes theme once. + +Entire platform updates. + +--- + +Excellent shared concern. + +--- + +# Shared Localization + +Examples + +```text +en-US + +fr-FR + +de-DE +``` + +--- + +All applications should remain consistent. + +--- + +Good candidate for shared context. + +--- + +# Shared Feature Flags + +Example + +```text +New Dashboard Enabled +``` + +--- + +Every application should evaluate the same flag values. + +--- + +Good candidate for sharing. + +--- + +# Shared Tenant Context + +Multi-tenant applications often share: + +```text +Current Tenant + +Current Organization +``` + +--- + +Useful platform-level context. + +--- + +# Recommended Shared Context Model + +```typescript +{ + currentUser, + permissions, + theme, + locale, + tenant, + featureFlags +} +``` + +--- + +Usually nothing more. + +--- + +# What Should NOT Be Shared? + +Avoid: + +```text +Employee State + +Project State + +Report State + +Business Feature Data +``` + +--- + +Example + +```text +Global Employee Store +``` + +owned by everyone. + +--- + +Problems + +```text +Coupling + +Coordination + +Loss Of Independence +``` + +--- + +# Design Systems + +Almost every successful Micro Frontend platform introduces: + +```text +Shared Design System +``` + +--- + +Examples + +```text +Buttons + +Tables + +Forms + +Typography + +Spacing + +Icons +``` + +--- + +Benefits + +```text +Consistent User Experience +``` + +while preserving: + +```text +Independent Teams +``` + +--- + +# Routing Strategy + +Usually: + +```text +Shell Owns Top-Level Routes +``` + +--- + +Examples + +```text +/employees + +/projects + +/reports + +/admin +``` + +--- + +Feature applications own local routes. + +--- + +Examples + +```text +/employees/list + +/employees/edit + +/employees/details +``` + +--- + +# Authentication Strategy + +Recommended ownership: + +```text +Shell +``` + +--- + +Shell responsibilities: + +```text +Login + +Logout + +Token Refresh + +Session Management +``` + +--- + +MFEs consume authentication state. + +--- + +Avoid + +```text +Each MFE Logs In Separately +``` + +--- + +# Observability + +Micro Frontends increase operational complexity. + +--- + +Need: + +```text +Centralized Logging + +Monitoring + +Tracing + +Analytics +``` + +--- + +Questions become: + +```text +Which MFE Failed? + +Why? + +When? + +Impact? +``` + +--- + +# Testing Strategy + +Multiple testing layers remain necessary. + +--- + +Unit Tests + +```text +Individual MFE +``` + +--- + +Integration Tests + +```text +Cross-MFE Integration +``` + +--- + +E2E Tests + +```text +Entire User Journey +``` + +--- + +Examples + +```text +Login + +Employee Search + +Report Generation +``` + +--- + +# Advantages + +## Team Autonomy + +```text +Independent Teams +``` + +--- + +## Independent Deployment + +```text +Release Faster +``` + +--- + +## Scalability + +```text +Large Organizations Scale Better +``` + +--- + +## Clear Ownership + +```text +Domain Ownership +``` + +--- + +## Technology Flexibility + +```text +Independent Upgrades +``` + +--- + +# Disadvantages + +## Operational Complexity + +```text +More Pipelines + +More Deployments + +More Infrastructure +``` + +--- + +## Communication Complexity + +```text +Harder State Coordination +``` + +--- + +## Governance Challenges + +Need standards around: + +```text +Security + +UX + +Versioning + +Monitoring +``` + +--- + +## Potential Performance Issues + +Too many MFEs may create: + +```text +Extra Requests + +Extra Downloads +``` + +--- + +# When NOT To Use Micro Frontends + +Avoid if: + +```text +Small Team + +Small Product + +Few Features + +Simple Domain +``` + +--- + +Example + +```text +10 Developers + +20 Screens +``` + +--- + +The complexity is rarely justified. + +--- + +# When Micro Frontends Make Sense + +Strong candidates: + +```text +Large Organizations + +Multiple Independent Teams + +Independent Release Cycles + +Distinct Business Domains + +Long Product Lifecycles +``` + +--- + +Examples + +```text +Banking Platforms + +Large SaaS Products + +Enterprise Portals + +Developer Platforms + +E-Commerce Ecosystems +``` + +--- + +# Micro Frontends vs Enterprise Architecture + +Enterprise Architecture: + +```text +One Application + +Many Features +``` + +--- + +Micro Frontends: + +```text +Many Applications + +One Product +``` + +--- + +Micro Frontends are essentially: + +```text +Enterprise Architecture + +At Organizational Scale +``` + +--- + +# Common Interview Questions + +## What Is A Micro Frontend? + +Independent frontend applications composed into a unified product. + +--- + +## Why Use Micro Frontends? + +Team autonomy, independent deployment, organizational scalability. + +--- + +## What Is Module Federation? + +Runtime loading and sharing of modules between independent applications. + +--- + +## What Is A Shell Application? + +Host application responsible for orchestration and shared platform concerns. + +--- + +## Should MFEs Share State? + +Only truly global concerns. + +Business state should remain local. + +--- + +## How Should MFEs Communicate? + +Events for UI interactions. + +APIs for business data. + +--- + +## What Should Be Shared? + +```text +Authentication + +User Context + +Theme + +Localization + +Feature Flags + +Tenant Context +``` + +--- + +## Biggest Benefit? + +```text +Team Independence +``` + +--- + +## Biggest Drawback? + +```text +Operational Complexity +``` + +--- + +# Architecture Blueprint + +```text +Shell + ├─ Authentication + ├─ Theme + ├─ Localization + ├─ Feature Flags + └─ Navigation + + ↓ + + Employees MFE + Projects MFE + Reports MFE + Admin MFE + + ↓ + + Backend APIs +``` + +Communication: + +```text +UI Events + ↓ +Event Bus + +Business Data + ↓ +Backend APIs + +Global Context + ↓ +Shell +``` + +--- + +# Key Takeaways + +1. Micro Frontends are primarily an organizational scaling strategy. +2. Module Federation is the most common Angular implementation mechanism. +3. The shell owns platform concerns. +4. Business domains define micro frontend boundaries. +5. Runtime loading enables independent deployment. +6. Shared state should be minimal. +7. Business state should remain owned by the responsible micro frontend. +8. Communication should be event-driven and loosely coupled. +9. Authentication, theme, locale, and feature flags are good shared concerns. +10. Team autonomy is the primary benefit; operational complexity is the primary cost. + +--- + +# Interview Revision Sheet + +```text +Micro Frontend + = Independent UI Application + +Shell + = Host / Platform Layer + +Remote + = Feature Application + +Module Federation + = Runtime Module Loading + +Event Bus + = UI Communication + +Backend APIs + = Business Communication + +Shared State + = Minimal + +State Ownership + = Per Domain + +Main Benefit + = Team Autonomy + +Main Cost + = Complexity +``` + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [Enterprise Architecture](34-enterprise-architecture.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [Angular Mock Interview Questions](../07-interview-preparation/36-mock-interview-questions.md) + +
+ diff --git a/apps/portal/public/assets/content/07-interview-preparation/36-mock-interview-questions.md b/apps/portal/public/assets/content/07-interview-preparation/36-mock-interview-questions.md new file mode 100644 index 0000000..51bafdb --- /dev/null +++ b/apps/portal/public/assets/content/07-interview-preparation/36-mock-interview-questions.md @@ -0,0 +1,619 @@ +# Angular Mock Interview Questions + +## Purpose + +This document is intended for rapid interview revision. + +Use it: + +- Before interviews +- During revision sessions +- For self-assessment +- For mock interview practice + +--- + +# Angular Fundamentals + +## Q: What is Angular? + +Angular is a TypeScript-based frontend framework used for building Single Page Applications (SPAs). + +Key characteristics: + +- Component-based architecture +- Dependency Injection +- Reactive programming support +- Routing +- Forms +- HTTP support + +--- + +## Q: SPA vs MPA? + +SPA: + +- Single HTML page +- Client-side routing +- Faster navigation + +MPA: + +- Multiple HTML pages +- Server-side navigation +- Full page reloads + +--- + +## Q: What is Bootstrapping? + +Process of loading the Angular application and creating the root component. + +--- + +# Components + +## Q: What is a Component? + +A component controls a portion of the UI. + +Contains: + +- Template +- Styles +- Logic + +--- + +## Q: Smart vs Dumb Components? + +Smart: + +- Business logic +- Service interaction +- State management + +Dumb: + +- Presentation only +- Receives Input +- Emits Output + +--- + +## Q: Lifecycle Hooks Order? + +```text +constructor + +ngOnChanges + +ngOnInit + +ngDoCheck + +ngAfterContentInit + +ngAfterContentChecked + +ngAfterViewInit + +ngAfterViewChecked + +ngOnDestroy +``` + +--- + +# Change Detection + +## Q: What is Change Detection? + +Mechanism Angular uses to synchronize data and UI. + +--- + +## Q: Default vs OnPush? + +Default: + +- Checks entire tree + +OnPush: + +- Checks only when: + - Input changes + - Event occurs + - Observable emits + - Signal updates + +--- + +## Q: Why Use OnPush? + +Improves performance by reducing unnecessary checks. + +--- + +# Dependency Injection + +## Q: What is DI? + +Design pattern for providing dependencies externally. + +Benefits: + +- Loose coupling +- Testability +- Reusability + +--- + +## Q: Singleton Service? + +Service with single instance shared across application. + +--- + +# RxJS + +## Q: Observable vs Promise? + +Observable: + +- Multiple values +- Lazy +- Cancelable + +Promise: + +- Single value +- Eager +- Not cancelable + +--- + +## Q: switchMap vs mergeMap? + +switchMap: + +- Cancels previous request + +mergeMap: + +- Runs requests concurrently + +Interview Rule: + +```text +Search Box +→ switchMap + +Parallel Requests +→ mergeMap +``` + +--- + +## Q: concatMap? + +Processes requests sequentially. + +--- + +## Q: exhaustMap? + +Ignores new requests while current request runs. + +Useful: + +```text +Login Button +Submit Button +``` + +--- + +## Q: shareReplay? + +Caches observable results. + +Prevents repeated API calls. + +--- + +# Signals + +## Q: What Problem Do Signals Solve? + +Fine-grained reactivity. + +--- + +## Q: signal vs computed vs effect? + +signal + +```text +State +``` + +computed + +```text +Derived State +``` + +effect + +```text +Side Effects +``` + +--- + +## Q: Signals vs RxJS? + +Signals: + +```text +State Management +UI Reactivity +``` + +RxJS: + +```text +Streams +Async Programming +``` + +--- + +# Routing + +## Q: What is Lazy Loading? + +Load features only when required. + +Benefits: + +- Smaller initial bundle +- Faster startup + +--- + +## Q: What Are Route Guards? + +Protect routes. + +Common types: + +```text +CanActivate + +CanDeactivate + +CanMatch +``` + +--- + +# Forms + +## Q: Template-Driven vs Reactive Forms? + +Template: + +```text +Simple Forms +``` + +Reactive: + +```text +Complex Forms +Enterprise Apps +``` + +--- + +## Q: Why Reactive Forms? + +Better: + +- Scalability +- Testability +- Dynamic forms + +--- + +# HTTP + +## Q: What Are Interceptors? + +Middleware for all HTTP requests/responses. + +Use cases: + +```text +Auth + +Logging + +Error Handling +``` + +--- + +## Q: What Is HttpClient? + +Angular service for backend communication. + +--- + +# Authentication + +## Q: Authentication vs Authorization? + +Authentication: + +```text +Who Are You? +``` + +Authorization: + +```text +What Can You Do? +``` + +--- + +## Q: JWT? + +Signed token containing identity claims. + +--- + +## Q: Access vs Refresh Token? + +Access Token: + +```text +Short-lived +``` + +Refresh Token: + +```text +Obtain New Access Tokens +``` + +--- + +# State Management + +## Q: Local vs Shared vs Global State? + +Local: + +```text +Component Only +``` + +Shared: + +```text +Feature Level +``` + +Global: + +```text +App Level +``` + +--- + +## Q: What Problem Does NgRx Solve? + +Enterprise-scale state governance. + +--- + +## Q: What Is An Action? + +An event. + +--- + +## Q: What Is dispatch()? + +Publishes event into NgRx system. + +--- + +## Q: What Is A Reducer? + +Pure function: + +```text +Old State + Action +↓ +New State +``` + +--- + +## Q: Why Are Reducers Pure? + +Predictability and testability. + +--- + +## Q: What Is An Effect? + +Handles side effects. + +Examples: + +```text +API Calls +Logging +Navigation +``` + +--- + +# Performance + +## Q: Performance Optimization Techniques? + +```text +OnPush + +trackBy + +Lazy Loading + +Signals + +Code Splitting + +Memoization +``` + +--- + +## Q: Why trackBy? + +Avoid unnecessary DOM recreation. + +--- + +# Error Handling + +## Q: 401 vs 403? + +401 + +```text +Unauthenticated +``` + +403 + +```text +Unauthorized +``` + +--- + +## Q: Why Use Global ErrorHandler? + +Capture runtime errors. + +--- + +## Q: Where Should HTTP Errors Go? + +Interceptor. + +--- + +# Enterprise Architecture + +## Q: Core vs Shared vs Feature? + +Core + +```text +Infrastructure +``` + +Shared + +```text +Reusable Building Blocks +``` + +Feature + +```text +Business Domain +``` + +--- + +## Q: Why Organize By Feature? + +Improves ownership and scalability. + +--- + +# Micro Frontends + +## Q: What Is A Micro Frontend? + +Independent frontend applications composed into one product. + +--- + +## Q: Biggest Benefit? + +Team autonomy. + +--- + +## Q: Biggest Drawback? + +Operational complexity. + +--- + +# Final Rapid Revision + +```text +Signal + = State + +Computed + = Derived State + +Interceptor + = HTTP Middleware + +Guard + = Route Protection + +Action + = Event + +Reducer + = State Transition + +Effect + = Side Effect + +JWT + = Identity Token + +Shell + = Micro Frontend Host +``` + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [Micro Frontends](../06-senior-architecture/35-micro-frontends.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [Frontend System Design](37-frontend-system-design.md) + +
+ diff --git a/apps/portal/public/assets/content/07-interview-preparation/37-frontend-system-design.md b/apps/portal/public/assets/content/07-interview-preparation/37-frontend-system-design.md new file mode 100644 index 0000000..49f27ce --- /dev/null +++ b/apps/portal/public/assets/content/07-interview-preparation/37-frontend-system-design.md @@ -0,0 +1,297 @@ +# Frontend System Design + +## Purpose + +Move from: + +```text +How Does Angular Work? +``` + +to: + +```text +How Do We Design Large Applications? +``` + +--- + +# Design Interview Mindset + +Interviewers care less about: + +```text +Syntax +``` + +and more about: + +```text +Trade-offs + +Architecture + +Scalability + +Maintainability +``` + +--- + +# Design Framework + +Whenever given a system design question: + +## Step 1 + +Understand Requirements + +Functional + +```text +What Must It Do? +``` + +Non-Functional + +```text +Performance + +Security + +Scalability +``` + +--- + +## Step 2 + +Identify Domains + +Example: + +```text +Users + +Products + +Orders + +Notifications +``` + +--- + +## Step 3 + +Define Architecture + +```text +Pages + +Routes + +Stores + +Services + +APIs +``` + +--- + +## Step 4 + +State Strategy + +Decide: + +```text +Local State + +Shared State + +Global State +``` + +--- + +## Step 5 + +Performance Strategy + +Consider: + +```text +Lazy Loading + +OnPush + +Signals + +Caching +``` + +--- + +# Design: Dashboard + +Architecture + +```text +Dashboard Page + +├─ Metrics Widget + +├─ Chart Widget + +├─ Notifications Widget +``` + +State + +```text +Signals Store +``` + +Performance + +```text +Lazy Widgets + +Caching +``` + +--- + +# Design: E-Commerce + +Domains + +```text +Products + +Cart + +Orders + +Payments + +Users +``` + +Global State + +```text +Current User + +Cart +``` + +--- + +# Design: Chat Application + +Challenges + +```text +Real-Time Updates + +Offline Support + +Large Message Lists +``` + +Architecture + +```text +WebSocket + +Store + +Message Cache + +Virtual Scrolling +``` + +--- + +# Design: Enterprise Portal + +Features + +```text +Employees + +Projects + +Reports + +Admin +``` + +Architecture + +```text +Core + +Shared + +Features +``` + +Potential Future: + +```text +Micro Frontends +``` + +--- + +# Frontend System Design Questions + +1. Design Employee Portal +2. Design E-Commerce Frontend +3. Design Notification System +4. Design Analytics Dashboard +5. Design Real-Time Chat +6. Design Large SaaS Platform +7. Design Micro Frontend Platform + +--- + +# Architecture Discussion Areas + +```text +State Management + +Authentication + +Authorization + +Caching + +Offline Mode + +Error Handling + +Performance + +Observability + +Deployment +``` + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [Angular Mock Interview Questions](36-mock-interview-questions.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [Angular Testing Deep Dive](38-angular-testing-deep-dive.md) + +
+ diff --git a/apps/portal/public/assets/content/07-interview-preparation/38-angular-testing-deep-dive.md b/apps/portal/public/assets/content/07-interview-preparation/38-angular-testing-deep-dive.md new file mode 100644 index 0000000..8363e6e --- /dev/null +++ b/apps/portal/public/assets/content/07-interview-preparation/38-angular-testing-deep-dive.md @@ -0,0 +1,391 @@ +# Angular Testing Deep Dive + +## Interview Priority + +High + +## Interview Frequency + +Medium to High + +--- + +# Why Testing? + +Testing increases: + +```text +Confidence + +Maintainability + +Refactoring Safety +``` + +--- + +# Testing Pyramid + +```text +E2E + +Integration + +Unit +``` + +--- + +# Unit Testing + +Tests small isolated units. + +Examples: + +```text +Component + +Service + +Pipe + +Guard + +Store +``` + +--- + +# TestBed + +Angular testing utility. + +```typescript +beforeEach(() => { + + TestBed.configureTestingModule({ + }); + +}); +``` + +--- + +# Component Testing + +Example + +```typescript +it('should create component'); +``` + +Typical assertions: + +```text +DOM + +Inputs + +Outputs + +Events +``` + +--- + +# Service Testing + +Example + +```typescript +EmployeeService +``` + +Mock: + +```typescript +HttpClient +``` + +Verify: + +```text +Method Calls + +Return Values + +Transformations +``` + +--- + +# Mocking Dependencies + +Example + +```typescript +spyOn(service, 'loadEmployees'); +``` + +Purpose: + +```text +Isolation +``` + +--- + +# HTTP Testing + +Use: + +```typescript +HttpTestingController +``` + +--- + +Example + +```typescript +const req = + httpMock.expectOne('/employees'); +``` + +Verify: + +```text +URL + +Method + +Payload +``` + +--- + +# Guard Testing + +Verify: + +```text +Allowed Routes + +Blocked Routes + +Navigation Rules +``` + +--- + +# Interceptor Testing + +Verify: + +```text +Headers Added + +Errors Handled + +Token Attached +``` + +--- + +# Async Testing + +Tools + +```typescript +fakeAsync() + +tick() + +waitForAsync() +``` + +--- + +# Signals Testing + +Verify: + +```text +Signal Updates + +Computed Values + +Effects +``` + +--- + +# Store Testing + +Verify: + +```text +State Changes + +Actions + +Selectors +``` + +--- + +# Integration Testing + +Multiple components working together. + +Example: + +```text +Page + +Store + +Service +``` + +Interaction. + +--- + +# E2E Testing + +Tools: + +```text +Playwright + +Cypress +``` + +--- + +Typical Scenarios + +```text +Login + +Checkout + +Create Employee + +Generate Report +``` + +--- + +# What To Test? + +Test: + +✅ Business Logic + +✅ Transformations + +✅ User Interaction + +✅ Critical Workflows + +--- + +Avoid Excessive Testing Of: + +```text +Framework Behavior +``` + +--- + +# Common Interview Questions + +## Difference Between Unit And Integration Testing? + +Unit: + +```text +Single Unit +``` + +Integration: + +```text +Multiple Units Together +``` + +--- + +## Why Mock Dependencies? + +Isolation. + +--- + +## What Is HttpTestingController? + +Utility for HTTP testing. + +--- + +## Why Use E2E? + +Validate complete user journeys. + +--- + +# Testing Strategy + +```text +Components +→ Unit + +Services +→ Unit + +Pages +→ Integration + +Critical Flows +→ E2E +``` + +--- + +# Senior-Level View + +Testing is not: + +```text +Code Coverage +``` + +Testing is: + +```text +Risk Reduction +``` + +Focus on: + +```text +Business-Critical Paths +``` + +first. + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [Frontend System Design](37-frontend-system-design.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [Behavioral Interview Preparation](39-behavioral-interview-preparation.md) + +
+ diff --git a/apps/portal/public/assets/content/07-interview-preparation/39-behavioral-interview-preparation.md b/apps/portal/public/assets/content/07-interview-preparation/39-behavioral-interview-preparation.md new file mode 100644 index 0000000..41bff59 --- /dev/null +++ b/apps/portal/public/assets/content/07-interview-preparation/39-behavioral-interview-preparation.md @@ -0,0 +1,442 @@ +# Behavioral Interview Preparation + +## Why Behavioral Interviews Matter + +Many senior candidates prepare extensively for: + +```text +Angular + +TypeScript + +RxJS + +System Design +``` + +and underestimate behavioral interviews. + +For senior and lead positions, interviewers evaluate: + +```text +Communication + +Leadership + +Collaboration + +Conflict Resolution + +Decision Making + +Ownership + +Mentoring + +Stakeholder Management +``` + +--- + +# Recommended Answer Framework + +Use: + +```text +STAR Method +``` + +--- + +S + +```text +Situation +``` + +--- + +T + +```text +Task +``` + +--- + +A + +```text +Action +``` + +--- + +R + +```text +Result +``` + +--- + +# Tell Me About Yourself + +Structure: + +```text +Current Role + +Years Of Experience + +Technical Strengths + +Leadership Experience + +Current Career Focus +``` + +--- + +Example Outline + +```text +11+ years experience + +Enterprise web applications + +.NET + Angular + +Technical leadership + +Architecture + +Cloud-native solutions +``` + +--- + +# Why Do You Want To Change? + +Focus on: + +```text +Growth + +Impact + +Learning + +Responsibilities +``` + +Avoid: + +```text +Bad Manager + +Bad Team + +Politics +``` + +--- + +# Tell Me About A Challenging Project + +Interviewer evaluates: + +```text +Problem Solving + +Leadership + +Execution +``` + +Structure: + +```text +Challenge + +Approach + +Actions + +Outcome +``` + +--- + +# Tell Me About A Technical Decision You Made + +Good examples: + +```text +Angular Migration + +Cloud Adoption + +Signals vs NgRx + +Microservices + +Authentication Strategy +``` + +Focus on: + +```text +Trade-offs + +Decision Process + +Outcome +``` + +--- + +# Describe A Production Incident + +Structure: + +```text +Issue + +Investigation + +Mitigation + +Root Cause + +Prevention +``` + +Demonstrate: + +```text +Ownership + +Calmness + +Communication +``` + +--- + +# Conflict With Team Member + +Focus on: + +```text +Problem + +Communication + +Understanding + +Resolution +``` + +Avoid: + +```text +Blame +``` + +--- + +# Mentoring Experience + +Topics: + +```text +Coaching Developers + +Code Reviews + +Knowledge Sharing + +Architecture Guidance +``` + +Demonstrate: + +```text +Influence + +Leadership + +Teaching +``` + +--- + +# Handling Tight Deadlines + +Interviewers want: + +```text +Prioritization + +Communication + +Risk Management +``` + +Not: + +```text +I Worked All Night +``` + +--- + +# Failure Question + +Example: + +```text +Tell me about a mistake. +``` + +Good structure: + +```text +Mistake + +Impact + +Learning + +Improvement +``` + +--- + +# Stakeholder Communication + +Demonstrate: + +```text +Technical Translation + +Expectation Management + +Risk Communication +``` + +Example: + +```text +Explaining technical constraints to business teams +``` + +--- + +# Leadership Questions + +Examples: + +```text +How do you influence without authority? + +How do you handle disagreements? + +How do you mentor engineers? +``` + +Key themes: + +```text +Collaboration + +Trust + +Data-Driven Decisions + +Coaching +``` + +--- + +# Expected Senior-Level Themes + +Interviewers often look for: + +```text +Ownership + +Accountability + +Communication + +Mentoring + +Architecture Thinking + +Customer Focus +``` + +--- + +# Common Behavioral Questions + +1. Tell me about yourself. +2. Why are you looking for a change? +3. Describe a challenging project. +4. Describe a production issue. +5. Describe a conflict. +6. Describe a failure. +7. Describe a leadership situation. +8. Describe a mentoring experience. +9. How do you prioritize work? +10. How do you handle ambiguity? +11. How do you influence decisions? +12. What are you most proud of? + +--- + +# Interview Revision Sheet + +```text +STAR + +Situation + +Task + +Action + +Result +``` + +Always emphasize: + +```text +Impact + +Learning + +Outcome +``` + +instead of: + +```text +Technology Only +``` + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [Angular Testing Deep Dive](38-angular-testing-deep-dive.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [Angular System Design Case Studies](40-angular-system-design-case-studies.md) + +
+ diff --git a/apps/portal/public/assets/content/07-interview-preparation/40-angular-system-design-case-studies.md b/apps/portal/public/assets/content/07-interview-preparation/40-angular-system-design-case-studies.md new file mode 100644 index 0000000..86ed0e5 --- /dev/null +++ b/apps/portal/public/assets/content/07-interview-preparation/40-angular-system-design-case-studies.md @@ -0,0 +1,650 @@ +# Angular System Design Case Studies + +## Purpose + +This guide prepares you for: + +```text +Senior Angular Interviews + +Lead Engineer Interviews + +Architecture Discussions + +Frontend System Design Rounds +``` + +--- + +# Case Study 1 + +# Design An Employee Management Portal + +--- + +## Requirements + +```text +Employee Management + +Department Management + +Authentication + +Role-Based Access + +Reporting + +Admin Dashboard +``` + +--- + +## High-Level Architecture + +```text +Core Layer + +Shared Layer + +Feature Layer +``` + +--- + +Feature Structure + +```text +Employees + +Departments + +Reports + +Admin +``` + +--- + +## State Strategy + +Global State + +```text +Current User + +Permissions + +Theme +``` + +Feature State + +```text +Employees + +Reports + +Departments +``` + +--- + +## Routing + +```text +Lazy Loaded Features +``` + +--- + +## Security + +```text +JWT + +Interceptor + +Guards + +RBAC +``` + +--- + +## Error Handling + +```text +Global Error Interceptor + +Global Error Handler +``` + +--- + +# Case Study 2 + +# Design An E-Commerce Frontend + +--- + +## Requirements + +```text +Products + +Categories + +Cart + +Checkout + +Orders + +Payments +``` + +--- + +## Domains + +```text +Catalog + +Cart + +Checkout + +Orders + +Users +``` + +--- + +## State Management + +Global + +```text +Cart + +Current User +``` + +Feature + +```text +Product Search + +Filters + +Checkout +``` + +--- + +## Performance + +```text +Lazy Loading + +Image Optimization + +Caching + +Virtual Scrolling +``` + +--- + +# Case Study 3 + +# Design A Real-Time Chat Application + +--- + +## Requirements + +```text +Messages + +Channels + +Presence + +Notifications +``` + +--- + +## Architecture + +```text +Chat Module + +Notification Module + +User Module +``` + +--- + +## Communication + +```text +WebSocket + +Signals Store + +Caching +``` + +--- + +## Performance + +```text +Virtual Scroll + +Message Chunking + +Pagination +``` + +--- + +## Challenges + +```text +Ordering + +Reconnection + +Offline Support +``` + +--- + +# Case Study 4 + +# Design Analytics Dashboard + +--- + +## Requirements + +```text +Charts + +KPIs + +Reports + +Filters + +Exports +``` + +--- + +## Performance Focus + +```text +Caching + +Lazy Widgets + +Memoization + +OnPush + +Signals +``` + +--- + +## Architecture + +```text +Dashboard Page + +↓ + +Widget Container + +↓ + +Chart Components +``` + +--- + +# Case Study 5 + +# Design Multi-Tenant SaaS Platform + +--- + +## Requirements + +```text +Multiple Organizations + +Different Permissions + +Different Branding +``` + +--- + +## Shared Context + +```text +Current User + +Current Tenant + +Permissions + +Theme +``` + +--- + +## Architecture + +```text +Tenant Context Service + +Auth Service + +Feature Modules +``` + +--- + +## Security + +```text +Tenant Isolation + +RBAC + +Backend Validation +``` + +--- + +# Case Study 6 + +# Design A Micro Frontend Platform + +--- + +## Requirements + +```text +Multiple Teams + +Independent Deployments + +Shared Authentication +``` + +--- + +## Architecture + +```text +Shell + +Employees MFE + +Projects MFE + +Reports MFE + +Admin MFE +``` + +--- + +## Shared Context + +```text +Authentication + +Theme + +Locale + +Feature Flags +``` + +--- + +## Communication + +```text +Event Bus + +Backend APIs +``` + +--- + +## Avoid Sharing + +```text +Business State +``` + +--- + +# System Design Discussion Framework + +Whenever asked to design a system: + +--- + +## Step 1 + +Requirements + +```text +Functional + +Non-Functional +``` + +--- + +## Step 2 + +Domain Identification + +```text +Users + +Products + +Orders + +Reports +``` + +--- + +## Step 3 + +Frontend Architecture + +```text +Pages + +Features + +Stores + +Services +``` + +--- + +## Step 4 + +State Strategy + +```text +Local + +Shared + +Global +``` + +--- + +## Step 5 + +Security + +```text +Authentication + +Authorization +``` + +--- + +## Step 6 + +Performance + +```text +Lazy Loading + +OnPush + +Caching + +Signals +``` + +--- + +## Step 7 + +Observability + +```text +Logging + +Monitoring + +Tracing +``` + +--- + +## Step 8 + +Deployment Strategy + +```text +Monolith + +Micro Frontends +``` + +--- + +# Common System Design Questions + +1. Design an Employee Portal. +2. Design an E-Commerce Platform. +3. Design a Real-Time Chat App. +4. Design a Dashboard Platform. +5. Design a Multi-Tenant SaaS Product. +6. Design a CRM. +7. Design a Banking Portal. +8. Design a Healthcare Platform. +9. Design a Reporting Platform. +10. Design a Micro Frontend Platform. + +--- + +# Senior Interview Mental Model + +Start thinking in: + +```text +Requirements + +↓ + +Domains + +↓ + +Architecture + +↓ + +State + +↓ + +Security + +↓ + +Performance + +↓ + +Observability + +↓ + +Deployment +``` + +instead of: + +```text +Components + +Services + +RxJS +``` + +because system design interviews evaluate: + +```text +Decision Making + +Trade-Offs + +Architecture Thinking + +Scalability +``` + +rather than Angular syntax. + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [Behavioral Interview Preparation](39-behavioral-interview-preparation.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [Quick-Fire Interview Q&A](41-quick-fire-qa.md) + +
+ diff --git a/apps/portal/public/assets/content/07-interview-preparation/41-quick-fire-qa.md b/apps/portal/public/assets/content/07-interview-preparation/41-quick-fire-qa.md new file mode 100644 index 0000000..4cc49b0 --- /dev/null +++ b/apps/portal/public/assets/content/07-interview-preparation/41-quick-fire-qa.md @@ -0,0 +1,65 @@ +# Quick-Fire Interview Q&A + +## Core Technical Q&A + +### Why does Angular use TypeScript? +Angular is designed for large enterprise applications, and TypeScript provides the foundation that makes that practical. It adds static typing, interfaces, generics, decorators, and rich tooling support. Angular relies heavily on decorators and metadata for features such as components, services, dependency injection, routing, and framework configuration. TypeScript also improves maintainability, refactoring safety, and developer productivity when working with large codebases. + +How does angular help Components? +Data Binding +→ Update Component UI +DI +→ Give Components Services +Lifecycle Hooks +→ Manage Component Lifecycle +Change Detection +→ Refresh Component UI +Signals +→ Reactive Component State +Routing +→ Display Components + + +What is the role of a Component? +Responsible for UI and user interaction. + +What is the role of a Service? +Contains reusable business logic and data access logic. + +Why does Angular use Dependency Injection? +To reduce coupling and improve testing and maintainability. + +How does Angular communicate with a backend? +Through HttpClient, typically consuming REST APIs. + +How does Angular navigate without page refresh? +Using Angular Router. + +Explain Angular Architecture Overview +Angular follows a layered architecture where Components handle presentation, Services encapsulate business logic, Dependency Injection manages object creation, Routing controls navigation, and HttpClient communicates with backend services. This separation of concerns improves maintainability, testability, and scalability for enterprise applications. + +Why does Angular use TypeScript? +Angular targets enterprise-scale applications. TypeScript provides static typing, interfaces, decorators, metadata support, generics, and excellent tooling, which improve maintainability, scalability, refactoring, and developer productivity. + +What is a Standalone Component? +A Standalone Component is a Component that can be used without being declared inside an NgModule. It reduces boilerplate, simplifies application structure, and is the recommended approach in modern Angular. + +Why must reducers be pure? +Reducers must be pure because given the same state and action they should always produce the same next state. They should not perform side effects such as API calls, navigation, logging, or asynchronous operations. This makes state transitions predictable, testable, and enables powerful debugging features such as time-travel debugging. + +How would you manage state across Micro Frontends? +I would avoid a large shared store because it creates tight coupling and reduces team independence. I would keep business state owned by the respective micro frontend and only share truly global concerns such as authentication, current user, theme, locale, and feature flags through the shell or a shared platform service. + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [Angular System Design Case Studies](40-angular-system-design-case-studies.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [Angular Cheat Sheet](../08-cheat-sheets/01-angular-cheat-sheet.md) + +
+ diff --git a/apps/portal/public/assets/content/08-cheat-sheets/01-angular-cheat-sheet.md b/apps/portal/public/assets/content/08-cheat-sheets/01-angular-cheat-sheet.md new file mode 100644 index 0000000..a058bc0 --- /dev/null +++ b/apps/portal/public/assets/content/08-cheat-sheets/01-angular-cheat-sheet.md @@ -0,0 +1,172 @@ +# Angular Cheat Sheet + +# Angular + +Angular is a TypeScript-based framework for building: + +```text +Single Page Applications (SPA) +``` + +--- + +# Angular Building Blocks + +```text +Component + = UI + +Template + = HTML View + +Directive + = DOM Behavior + +Pipe + = Data Transformation + +Service + = Business Logic + +Module (Legacy) + = Feature Grouping + +Standalone Component + = Modern Angular Building Block +``` + +--- + +# Data Binding + +```text +Interpolation + {{ value }} + +Property + [disabled]="isDisabled" + +Event + (click)="save()" + +Two-Way + [(ngModel)]="name" +``` + +--- + +# Component Communication + +Parent → Child + +```typescript +@Input() +``` + +Child → Parent + +```typescript +@Output() +``` + +--- + +# Lifecycle Hooks + +```text +constructor + +ngOnChanges + +ngOnInit + +ngDoCheck + +ngAfterContentInit + +ngAfterContentChecked + +ngAfterViewInit + +ngAfterViewChecked + +ngOnDestroy +``` + +--- + +# Dependency Injection + +Benefits: + +```text +Loose Coupling + +Reuse + +Testability +``` + +--- + +# Service Scopes + +```typescript +providedIn: 'root' +``` + +Singleton + +--- + +# Common Interview Questions + +Q: Component vs Directive? + +Component: + +```text +Template + Logic +``` + +Directive: + +```text +Behavior Only +``` + +--- + +Q: Why DI? + +```text +Loose Coupling +``` + +--- + +# Golden Rules + +```text +API Calls → Services + +UI Logic → Components + +Business Logic → Services + +State → Store/Signals +``` + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [Quick-Fire Interview Q&A](../07-interview-preparation/41-quick-fire-qa.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [RxJS Cheat Sheet](02-rxjs-cheat-sheet.md) + +
+ diff --git a/apps/portal/public/assets/content/08-cheat-sheets/02-rxjs-cheat-sheet.md b/apps/portal/public/assets/content/08-cheat-sheets/02-rxjs-cheat-sheet.md new file mode 100644 index 0000000..66c89d1 --- /dev/null +++ b/apps/portal/public/assets/content/08-cheat-sheets/02-rxjs-cheat-sheet.md @@ -0,0 +1,177 @@ +# RxJS Cheat Sheet + +# Observable vs Promise + +Observable + +```text +0 to Many Values + +Lazy + +Cancelable +``` + +Promise + +```text +Single Value + +Eager + +Not Cancelable +``` + +--- + +# Subjects + +Subject + +```text +Hot Observable +``` + +BehaviorSubject + +```text +Stores Current Value +``` + +ReplaySubject + +```text +Replays Previous Values +``` + +--- + +# Operator Decision Guide + +Search Box + +```text +switchMap +``` + +--- + +Parallel Requests + +```text +mergeMap +``` + +--- + +Sequential Requests + +```text +concatMap +``` + +--- + +Prevent Double Click + +```text +exhaustMap +``` + +--- + +# Transformation + +```typescript +map() +``` + +Transform values + +--- + +# Filtering + +```typescript +filter() + +take() + +debounceTime() + +distinctUntilChanged() +``` + +--- + +# Combination + +```typescript +combineLatest() + +forkJoin() + +zip() +``` + +--- + +# Error Handling + +```typescript +catchError() + +retry() + +retryWhen() +``` + +--- + +# Caching + +```typescript +shareReplay(1) +``` + +--- + +# Interview Traps + +switchMap: + +```text +Cancels Previous Request +``` + +mergeMap: + +```text +Does NOT Cancel +``` + +--- + +# Golden Rule + +```text +Streams + = RxJS + +State + = Signals +``` + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [Angular Cheat Sheet](01-angular-cheat-sheet.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [Signals Cheat Sheet](03-signals-cheat-sheet.md) + +
+ diff --git a/apps/portal/public/assets/content/08-cheat-sheets/03-signals-cheat-sheet.md b/apps/portal/public/assets/content/08-cheat-sheets/03-signals-cheat-sheet.md new file mode 100644 index 0000000..e8058ba --- /dev/null +++ b/apps/portal/public/assets/content/08-cheat-sheets/03-signals-cheat-sheet.md @@ -0,0 +1,134 @@ +# Signals Cheat Sheet + +## Core APIs + +```text +signal() + +computed() + +effect() +``` + +--- + +signal + +```typescript +count = signal(0); +``` + +Writable state. + +--- + +computed + +```typescript +total = computed( + () => price() * qty() +); +``` + +Derived state. + +--- + +effect + +```typescript +effect(() => { + console.log(count()); +}); +``` + +Side effects. + +--- + +# Updates + +```typescript +count.set(10); + +count.update(v => v + 1); +``` + +--- + +# Why Signals? + +```text +Fine-Grained Reactivity + +Better Performance + +Less Boilerplate +``` + +--- + +# Signals vs RxJS + +Signals + +```text +State +``` + +RxJS + +```text +Streams +``` + +--- + +# Interview Question + +Signals Replace RxJS? + +```text +No +``` + +Use: + +```text +Signals → State + +RxJS → Async Streams +``` + +--- + +# Golden Rule + +```text +State + ↓ + +signal() + + ↓ + +computed() + + ↓ + +effect() +``` + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [RxJS Cheat Sheet](02-rxjs-cheat-sheet.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [Routing Cheat Sheet](04-routing-cheat-sheet.md) + +
+ diff --git a/apps/portal/public/assets/content/08-cheat-sheets/04-routing-cheat-sheet.md b/apps/portal/public/assets/content/08-cheat-sheets/04-routing-cheat-sheet.md new file mode 100644 index 0000000..ab0da72 --- /dev/null +++ b/apps/portal/public/assets/content/08-cheat-sheets/04-routing-cheat-sheet.md @@ -0,0 +1,133 @@ +# Routing Cheat Sheet + +# Route Types + +Static + +```typescript +/users +``` + +Dynamic + +```typescript +/users/:id +``` + +--- + +# Parameters + +Route Parameter + +```typescript +/users/10 +``` + +--- + +Query Parameter + +```typescript +/users?page=2 +``` + +--- + +# Navigation + +```typescript +router.navigate(...) +``` + +--- + +# Guards + +CanActivate + +```text +Enter Route? +``` + +CanDeactivate + +```text +Leave Route? +``` + +CanMatch + +```text +Can Route Match? +``` + +--- + +# Resolvers + +Load data: + +```text +Before Navigation +``` + +--- + +# Lazy Loading + +```typescript +loadChildren(...) +``` + +Benefits: + +```text +Smaller Bundle + +Faster Startup +``` + +--- + +# Interview Questions + +401 vs Guard? + +401: + +```text +Backend +``` + +Guard: + +```text +Frontend +``` + +--- + +# Golden Rule + +```text +Authentication + = Guard + +Authorization + = Guard + Backend Validation +``` + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [Signals Cheat Sheet](03-signals-cheat-sheet.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [Forms Cheat Sheet](05-forms-cheat-sheet.md) + +
+ diff --git a/apps/portal/public/assets/content/08-cheat-sheets/05-forms-cheat-sheet.md b/apps/portal/public/assets/content/08-cheat-sheets/05-forms-cheat-sheet.md new file mode 100644 index 0000000..94f1257 --- /dev/null +++ b/apps/portal/public/assets/content/08-cheat-sheets/05-forms-cheat-sheet.md @@ -0,0 +1,101 @@ +# Forms Cheat Sheet + +# Form Types + +Template Driven + +Reactive Forms + +--- + +# Enterprise Choice + +```text +Reactive Forms +``` + +--- + +# Reactive Building Blocks + +FormControl + +FormGroup + +FormArray + +--- + +# Validation + +Required + +```typescript +Validators.required +``` + +Email + +```typescript +Validators.email +``` + +Custom + +```typescript +ValidatorFn +``` + +--- + +# Dynamic Forms + +Use: + +```typescript +FormArray +``` + +--- + +# Common Questions + +Template vs Reactive? + +Template + +```text +Simple Forms +``` + +Reactive + +```text +Complex Enterprise Forms +``` + +--- + +# Golden Rule + +```text +Small Forms + → Template + +Enterprise Forms + → Reactive +``` + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [Routing Cheat Sheet](04-routing-cheat-sheet.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [NgRx Cheat Sheet](06-ngrx-cheat-sheet.md) + +
+ diff --git a/apps/portal/public/assets/content/08-cheat-sheets/06-ngrx-cheat-sheet.md b/apps/portal/public/assets/content/08-cheat-sheets/06-ngrx-cheat-sheet.md new file mode 100644 index 0000000..d00f404 --- /dev/null +++ b/apps/portal/public/assets/content/08-cheat-sheets/06-ngrx-cheat-sheet.md @@ -0,0 +1,161 @@ +# NgRx Cheat Sheet + +# Mental Model + +```text +Action + +↓ + +Reducer + +↓ + +Store + +↓ + +Selector + +↓ + +UI +``` + +--- + +# Store + +```text +Application State +``` + +--- + +# Action + +```text +Event +``` + +Example: + +```typescript +loadEmployees +``` + +--- + +# dispatch() + +```text +Publish Event +``` + +--- + +# Reducer + +```text +Old State ++ +Action += +New State +``` + +Pure Function. + +--- + +# Effect + +```text +Side Effects +``` + +Examples: + +```text +API Calls + +Logging + +Navigation +``` + +--- + +# Selector + +```text +Read State +``` + +--- + +# Pure Reducer + +Must: + +✅ Same Input → Same Output + +✅ No Side Effects + +✅ No State Mutation + +--- + +# Do Not Put In Reducers + +```text +API Calls + +Logging + +Navigation +``` + +--- + +# Typical Pattern + +```text +loadEmployees + +↓ + +loadEmployeesSuccess + +↓ + +loadEmployeesFailure +``` + +--- + +# Golden Rule + +```text +Actions Describe + +Reducers Transform + +Effects Execute + +Selectors Read +``` + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [Forms Cheat Sheet](05-forms-cheat-sheet.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [Authentication & Authorization Cheat Sheet](07-auth-cheat-sheet.md) + +
+ diff --git a/apps/portal/public/assets/content/08-cheat-sheets/07-auth-cheat-sheet.md b/apps/portal/public/assets/content/08-cheat-sheets/07-auth-cheat-sheet.md new file mode 100644 index 0000000..32f3fbc --- /dev/null +++ b/apps/portal/public/assets/content/08-cheat-sheets/07-auth-cheat-sheet.md @@ -0,0 +1,154 @@ +# Authentication & Authorization Cheat Sheet + +# Authentication + +```text +Who Are You? +``` + +--- + +# Authorization + +```text +What Can You Do? +``` + +--- + +# JWT + +```text +Header + +Payload + +Signature +``` + +--- + +# JWT Important + +JWT is: + +✅ Signed + +❌ Encrypted + +--- + +# Access Token + +```text +API Access +``` + +--- + +# Refresh Token + +```text +Get New Access Token +``` + +--- + +# Interceptor + +Adds: + +```http +Authorization: +Bearer token +``` + +--- + +# Guards + +Protect: + +```text +Routes +``` + +--- + +# RBAC + +Role-Based Access Control. + +Examples: + +```text +Admin + +Manager + +Employee +``` + +--- + +# Permission-Based + +Examples: + +```text +employee.read + +employee.create + +employee.delete +``` + +--- + +# Important Rule + +Frontend: + +```text +User Experience +``` + +Backend: + +```text +Actual Security +``` + +--- + +# Golden Rule + +```text +Authentication + +↓ + +Identity + +↓ + +Authorization + +↓ + +Permissions +``` + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [NgRx Cheat Sheet](06-ngrx-cheat-sheet.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [Enterprise Architecture Cheat Sheet](08-architecture-cheat-sheet.md) + +
+ diff --git a/apps/portal/public/assets/content/08-cheat-sheets/08-architecture-cheat-sheet.md b/apps/portal/public/assets/content/08-cheat-sheets/08-architecture-cheat-sheet.md new file mode 100644 index 0000000..284aa72 --- /dev/null +++ b/apps/portal/public/assets/content/08-cheat-sheets/08-architecture-cheat-sheet.md @@ -0,0 +1,191 @@ +# Enterprise Architecture Cheat Sheet + +# Layers + +```text +Core + +Shared + +Feature +``` + +--- + +# Core + +Application-wide concerns. + +Examples: + +```text +Auth + +Guards + +Interceptors + +Logging +``` + +--- + +# Shared + +Reusable UI. + +Examples: + +```text +Button + +Modal + +Pipe + +Directive +``` + +--- + +# Feature + +Business Domains. + +Examples: + +```text +Employees + +Projects + +Reports +``` + +--- + +# Enterprise Flow + +```text +UI + +↓ + +Store + +↓ + +Service + +↓ + +HTTP + +↓ + +Backend +``` + +--- + +# Container Components + +```text +Behavior + +State + +Workflows +``` + +--- + +# Presentational Components + +```text +Rendering +``` + +--- + +# Cross-Cutting Concerns + +```text +Auth + +Logging + +Caching + +Monitoring + +Error Handling +``` + +--- + +# Micro Frontends + +```text +Many Apps + +One Product +``` + +--- + +# Shell Owns + +```text +Authentication + +Theme + +Localization + +Feature Flags +``` + +--- + +# Shared State + +✅ User + +✅ Theme + +✅ Locale + +✅ Permissions + +--- + +❌ Employees + +❌ Projects + +❌ Reports + +--- + +# Golden Rule + +```text +Feature Owns Its State + +Shell Owns Global Context +``` + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [Authentication & Authorization Cheat Sheet](07-auth-cheat-sheet.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** [Interview Day Quick Revision](09-interview-day-cheat-sheet.md) + +
+ diff --git a/apps/portal/public/assets/content/08-cheat-sheets/09-interview-day-cheat-sheet.md b/apps/portal/public/assets/content/08-cheat-sheets/09-interview-day-cheat-sheet.md new file mode 100644 index 0000000..98e2415 --- /dev/null +++ b/apps/portal/public/assets/content/08-cheat-sheets/09-interview-day-cheat-sheet.md @@ -0,0 +1,127 @@ +# Interview Day Quick Revision + +# Angular + +Component = UI + +Service = Logic + +DI = Dependency Management + +--- + +# RxJS + +switchMap = Cancel Previous + +mergeMap = Parallel + +concatMap = Sequential + +exhaustMap = Ignore New + +--- + +# Signals + +signal = State + +computed = Derived State + +effect = Side Effects + +--- + +# Routing + +Guard = Route Protection + +Resolver = Preload Data + +Lazy Loading = Smaller Bundle + +--- + +# Forms + +Reactive Forms = Enterprise Choice + +--- + +# Performance + +OnPush + +trackBy + +Signals + +Lazy Loading + +--- + +# NgRx + +Action = Event + +Reducer = State Transition + +Effect = Side Effect + +Selector = Read State + +--- + +# Auth + +Authentication = Who Are You? + +Authorization = What Can You Do? + +JWT = Signed Token + +--- + +# Error Handling + +401 = Unauthenticated + +403 = Unauthorized + +Interceptor = Central HTTP Handling + +--- + +# Architecture + +Core + +Shared + +Feature + +--- + +# Micro Frontends + +Shell = Host + +Remote = Feature App + +Benefit = Team Autonomy + +Cost = Complexity + + + +
+
+ +--- + +
+ +⬅️ **Previous:** [Enterprise Architecture Cheat Sheet](08-architecture-cheat-sheet.md)  |  🏠 **[Table of Contents](../README.md)**  |  ➡️ **Next:** *None (End of Guide)* + +
+ diff --git a/apps/portal/public/assets/content/web_security_guide.md b/apps/portal/public/assets/content/web_security_guide.md new file mode 100644 index 0000000..28dcd8c --- /dev/null +++ b/apps/portal/public/assets/content/web_security_guide.md @@ -0,0 +1,307 @@ +# Web Application Security Guide: Core Vulnerabilities, First Principles, and Mitigations + +This comprehensive guide breaks down the most critical web application security vulnerabilities from first principles. It provides concrete, real-world exploit examples and details exact mitigation strategies for both an **Angular frontend** and a **.NET backend**. + +--- + +## 1. Cross-Site Scripting (XSS) + +### 🧩 First Principles +A web browser treats any script inside an HTML document as executable code running within the website's security context. Browsers cannot distinguish between a script written by the original developer and a script injected by an attacker. XSS occurs when untrusted user input is treated as executable code by the browser instead of safe data. + +### 💥 Practical Scenarios & Code Examples + +#### Reflected XSS +* **Scenario:** A search page displays the user's query back to them: `"Results for: ``"`. +* **Exploit Vector:** An attacker sends a victim a link containing a script payload in the query parameter. +* **Payload:** + ```text + https://example.com/search?q= + ``` + +#### Stored XSS +* **Scenario:** A forum comment box saves user text directly into a database and renders it to subsequent visitors. +* **Payload:** + ```html +

Great post!

+ ``` + +#### DOM-Based XSS +* **Scenario:** Client-side JavaScript reads a hash fragment from the URL and writes it directly to the document object model (DOM) via an unsafe property. +* **Payload:** + ```text + https://example.com/dashboard# + ``` + +### 🛡️ Mitigation Steps + +#### Frontend (Angular) +Angular treats all values as untrusted by default. When values are inserted into the DOM via interpolation or property bindings, Angular automatically contextualizes and encodes them. +* **Do Not Bypass Protection:** Never use `DomSanitizer` methods like `bypassSecurityTrustHtml` or `bypassSecurityTrustScript` with raw user input. +* **Safe Implementation Example:** + ```typescript + // Component code + import { Component } from '@angular/core'; + + @Component({ + selector: 'app-search', + template: `
` // Bound securely by Angular + }) + export class SearchComponent { + // Angular will automatically encode HTML tags if passed into template text expressions + // Use an established client-side sanitizer library like DOMPurify if raw HTML layout must be supported + safeText: string = 'User input data goes here'; + } + ``` + +#### Backend (.NET) +* **HTML Encoding Output:** If you are building server-side views (Razor Pages / Blazor), .NET automatically HTML-encodes all outputs generated via `@Variable` syntax. +* **Content Security Policy (CSP):** Configure a strong CSP header in your middleware to prevent the execution of inline scripts and unauthorized cross-domain assets. + ```csharp + // Program.cs middleware integration + app.Use(async (context, next) => + { + context.Response.Headers.Add("Content-Security-Policy", "default-src 'self'; script-src 'self'; object-src 'none';"); + await next(); + }); + ``` +* **Cookie Protection:** Mark session cookies as `HttpOnly` so client-side JavaScript cannot read them. + ```csharp + options.Cookie.HttpOnly = true; + options.Cookie.SecurePolicy = CookieSecurePolicy.Always; + ``` + +--- + +## 2. Cross-Site Request Forgery (CSRF) + +### 🧩 First Principles +Browsers naturally include session cookies automatically whenever a web request is made to a destination domain, regardless of which website originated that request. CSRF exploits this ambient authorization trait by tricking a victim's active browser session into submitting a state-changing transaction request to a trusted application without the victim's knowledge. + +### 💥 Practical Scenarios & Code Examples +* **Scenario:** A logged-in user visits a malicious forum (`evil-site.com`). +* **Exploit Vector:** The malicious site contains a hidden form that auto-submits via JavaScript to the victim's bank website. +* **Payload:** + ```html + + + + + + ``` +* **Result:** The browser transmits the form along with the victim’s valid session cookie for `yourbank.com`. The bank processes the transfer request as valid. + +### 🛡️ Mitigation Steps + +#### Frontend (Angular) +Angular includes a built-in mechanism to handle Anti-CSRF tokens automatically. +* **HttpClient Integration:** Angular's `HttpClientModule` reads a token cookie (default name: `XSRF-TOKEN`) and appends it as an HTTP header (default name: `X-XSRF-TOKEN`) on all mutating requests (POST, PUT, DELETE). + ```typescript + // app.config.ts or AppModule + import { provideHttpClient, withXsrfConfiguration } from '@angular/common/http'; + + export const appConfig = { + providers: [ + provideHttpClient( + withXsrfConfiguration({ + cookieName: 'XSRF-TOKEN', + headerName: 'X-XSRF-TOKEN', + }) + ) + ] + }; + ``` + +#### Backend (.NET) +The backend must issue the unique cryptographic token to the client cookie and strictly validate it against incoming headers on state-changing API endpoints. +* **Configure Antiforgery Middleware:** + ```csharp + // Program.cs + builder.Services.AddAntiforgery(options => + { + options.HeaderName = "X-XSRF-TOKEN"; // Matches Angular default + }); + + var app = builder.Build(); + + // Middleware to send the token in a cookie on initial GET request + app.Use(async (context, next) => + { + var antiforgery = context.RequestServices.GetRequiredService(); + var tokens = antiforgery.GetAndStoreTokens(context); + context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken!, + new CookieOptions { HttpOnly = false, Secure = true, SameSite = SameSiteMode.Lax }); + + await next(); + }); + ``` +* **Enforce Validation on Controllers:** + ```csharp + [ApiController] + [Route("api/[controller]")] + [AutoValidateAntiforgeryToken] // Automatically validates POST/PUT/DELETE requests + public class AccountController : ControllerBase + { + [HttpPost("transfer")] + public IActionResult Transfer([FromBody] TransferModel model) + { + // Request processed safely + return Ok(); + } + } + ``` +* **SameSite Cookie Configuration:** Set the `SameSite=Lax` or `SameSite=Strict` flag on all application authentication cookies to block browsers from transmitting them during cross-site navigations. + +--- + +## 3. SQL Injection (SQLi) + +### 🧩 First Principles +Databases process strings of text as commands. SQL Injection occurs when an application takes user input and concatenates it directly into a static SQL database query string. Because the query parser cannot isolate the structural intent of the query from the embedded string data, the database interprets the user input as engine execution directives. + +### 💥 Practical Scenarios & Code Examples +* **Scenario:** A login authentication endpoint checks a username and password. +* **Vulnerable Backend Logic:** + ```csharp + string query = "SELECT * FROM Users WHERE User = '" + txtUser.Text + "' AND Pass = '" + txtPass.Text + "'"; + ``` +* **Payload:** The attacker inputs `' OR '1'='1` inside the username field. +* **Resulting Query evaluated by DB:** + ```sql + SELECT * FROM Users WHERE User = '' OR '1'='1' AND Pass = '' + ``` +* **Result:** Since `'1'='1'` is always true, the database bypasses the password condition entirely and returns the first row of the table, logging the attacker in as the administrator. + +### 🛡️ Mitigation Steps + +#### Frontend (Angular) +Frontend input validation cannot prevent SQL Injection because attackers can bypass the frontend interface and interact directly with raw API endpoints. The frontend's role is to ensure validation matches expected inputs for user experience rather than infrastructural security boundaries. + +#### Backend (.NET) +* **Use Parameterized Queries / ORMs:** Do not build SQL syntax dynamically using concatenation or interpolation. Use an Object-Relational Mapper (ORM) like **Entity Framework Core**, which implicitly implements parameterized commands under the hood. + ```csharp + // SECURE implementation using Entity Framework Core (LINQ) + public async Task AuthenticateUserAsync(string username, string password) + { + return await _context.Users + // EF Core translates this using safe db command parameterization + .FirstOrDefaultAsync(u => u.Username == username && u.PasswordHash == password); + } + ``` +* **Raw SQL Fallback Safetynet:** If you must use raw SQL strings via Dapper or ADO.NET, enforce strong explicit parameters: + ```csharp + // SECURE execution using ADO.NET Parameters + using (SqlCommand command = new SqlCommand("SELECT * FROM Users WHERE User = @user", connection)) + { + command.Parameters.Add("@user", SqlDbType.VarChar, 50).Value = username; + // Database engine safely isolates this value from execution flow + var reader = await command.ExecuteReaderAsync(); + } + ``` + +--- + +## 4. Broken Object Level Authorization (BOLA / IDOR) + +### 🧩 First Principles +Web applications often track unique internal data rows using sequential numbers or predictable identifiers (like integer primary keys or UUIDs) exposed directly in APIs. BOLA/IDOR occurs when a system relies entirely on client-provided object identifiers to fetch data without verifying if the authenticated user session possesses access rights to that specific entity instance. + +### 💥 Practical Scenarios & Code Examples +* **Scenario:** An application allows users to pull up their monthly invoice details via an API endpoint. +* **Exploit Vector:** An authenticated user notices their profile uses the following routing scheme: `https://example.com/api/invoices/10045`. +* **Payload:** The user updates the address bar or modifies the fetch parameter manually to target sequentially close resources: `https://example.com/api/invoices/10046`. +* **Result:** If the backend script checks that the requester is a *logged-in user* but fails to check *which specific user owns invoice 10046*, it serves private account data to an unauthorized third party. + +### 🛡️ Mitigation Steps + +#### Frontend (Angular) +Frontend route guards (`CanActivate`) restrict access to UI views but cannot enforce data security at the database layer. Always design the frontend to dynamically display views based on the server-authenticated session response context, and avoid caching cross-user data identifiers globally. + +#### Backend (.NET) +Every business logic layer or controller endpoint must cross-reference the user’s primary identifier (extracted securely from their access token or session claim) against the target database asset's ownership key. + +```csharp +[Authorize] +[HttpGet("api/invoices/{invoiceId}")] +public async Task GetInvoice(int invoiceId) +{ + // 1. Securely fetch the current User ID from claims principal token context + var currentUserId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; + + if (string.IsNullOrEmpty(currentUserId)) + return Unauthorized(); + + // 2. Fetch the target data entity from database + var invoice = await _context.Invoices.FindAsync(invoiceId); + + if (invoice == null) + return NotFound(); // Return NotFound rather than Forbidden to obscure existence + + // 3. SECURE STEP: Validate the data ownership mapping explicitly + if (invoice.OwnerUserId != currentUserId) + { + // Fail the request if the resource does not belong to the active user session + return NotFound(); + } + + return Ok(invoice); +} +``` + +--- + +## 5. Server-Side Request Forgery (SSRF) + +### 🧩 First Principles +Backends often occupy trusted inner networks protected by external firewall perimeters. When a web application accepts a remote network address from a client and commands the backend server to make an outbound HTTP connection request to that URL, it acts as a proxy proxy proxy. SSRF occurs when an attacker manipulates that parameter to make the hosting server send requests to unintended internal systems or private cloud infrastructure metadata interfaces. + +### 💥 Practical Scenarios & Code Examples +* **Scenario:** A profile page lets a user specify a remote image URL link (`https://assets.site.com/avatar.png`) to parse and cache a profile avatar. +* **Exploit Vector:** The attacker provides a URL targeting the hosting environment's loopback interface or cloud metadata engine endpoint. +* **Payload 1 (Internal Network Scan):** `http://127.0.0.1:8080/admin/delete-database` +* **Payload 2 (Cloud Provider Metadata Exfiltration):** `http://169.254.169.254/latest/meta-data/` +* **Result:** The backend fetches the URL from its internal location context. Since the server runs inside the secure perimeter, the cloud instance metadata provider responds to it without authorization checks, giving the attacker access to temporary security credentials, environment tokens, or database command shells. + +### 🛡️ Mitigation Steps + +#### Frontend (Angular) +Sanitize user-provided text inputs using URL parsing patterns to catch malformed structures early. Ensure you alert users if input formatting doesn't resemble a standard public web address schema. + +#### Backend (.NET) +* **Strict Whitelisting:** If your application must pull down remote assets, validate the target address against a hard whitelist of explicitly trusted domain strings. +* **Disable Input URLs Whenever Possible:** Allow users to choose from predefined keys or use secure upload interfaces instead of specifying raw execution endpoints. +* **Implement Network Level Restrictions:** Configure isolated routing rules so that your web application host environment cannot communicate with the local instance loopback interface (`127.0.0.1`) or the standard cloud metadata block (`169.254.169.254`). + +```csharp +public async Task FetchProfilePreview(string userProvidedUrl) +{ + // Validate URI structure syntax parsing + if (!Uri.TryCreate(userProvidedUrl, UriKind.Absolute, out var validatedUri)) + { + return BadRequest("Invalid URL layout format."); + } + + // Explicitly restrict requests to external public targets only + var targetHost = validatedUri.Host.ToLower(); + + // Explicitly block local or private IP patterns + if (targetHost == "localhost" || targetHost == "127.0.0.1" || targetHost.StartsWith("192.168.")) + { + return BadRequest("Unauthorized network location targeted."); + } + + // Recommended approach: Ensure domain explicitly matches a whitelisted host collection + var allowedDomains = new List { "images.unsplash.com", "res.cloudinary.com" }; + if (!allowedDomains.Contains(targetHost)) + { + return BadRequest("Target domain location is not whitelisted."); + } + + using var httpClient = new HttpClient(); + var responseData = await httpClient.GetAsync(validatedUri); + // Process response stream safely... + return Ok(); +} +``` diff --git a/apps/portal/public/favicon.ico b/apps/portal/public/favicon.ico new file mode 100644 index 0000000..57614f9 Binary files /dev/null and b/apps/portal/public/favicon.ico differ diff --git a/apps/portal/scripts/generate-manifest.mjs b/apps/portal/scripts/generate-manifest.mjs new file mode 100644 index 0000000..3f773d9 --- /dev/null +++ b/apps/portal/scripts/generate-manifest.mjs @@ -0,0 +1,819 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const ROOT_DIR = path.resolve(__dirname, '../../..'); +const PORTAL_PUBLIC_ASSETS = path.resolve(__dirname, '../public/assets'); + +const CATEGORY_META = { + '01-foundations': { title: 'Foundations & Architecture', order: 1, badge: 'Basics', icon: 'layers' }, + '02-core-angular': { title: 'Core Angular Essentials', order: 2, badge: 'Core', icon: 'cpu' }, + '03-intermediate': { title: 'Intermediate Patterns', order: 3, badge: 'Intermediate', icon: 'workflow' }, + '04-reactivity': { title: 'Reactivity, RxJS & Signals', order: 4, badge: 'Must Know', icon: 'zap' }, + '05-advanced': { title: 'Advanced & Performance', order: 5, badge: 'Advanced', icon: 'gauge' }, + '06-senior-architecture': { title: 'Senior Architecture & Enterprise', order: 6, badge: 'Architect', icon: 'shield-check' }, + '07-interview-preparation': { title: 'Interview Drills & System Design', order: 7, badge: 'Interview', icon: 'target' }, + '08-cheat-sheets': { title: 'Rapid Cheat Sheets', order: 8, badge: 'Quick Cram', icon: 'bookmark' } +}; + +const CURATED_FLASHCARDS = { + '01-why-angular': [ + { + question: 'What is Angular and what primary architectural problem does it solve?', + answer: 'Angular is a full-featured, opinionated, TypeScript-based platform maintained by Google. It provides an end-to-end architecture (DI, routing, forms, HTTP client, signals) to eliminate spaghetti code and build maintainable, testable enterprise SPAs.' + }, + { + question: 'What is the fundamental difference between Angular and jQuery?', + answer: 'jQuery relies on imperative DOM manipulation where application state is stored directly inside HTML elements. Angular uses declarative data binding where state resides in TypeScript classes/signals and the DOM is a reactive projection of that state.' + }, + { + question: 'How does Angular differ from React architecturally?', + answer: 'Angular is a complete "batteries-included" framework with built-in DI, routing, forms, and fine-grained Signals. React is a view rendering library requiring community packages for routing, forms, and state, with rendering driven by virtual DOM reconciliation.' + }, + { + question: 'What is the core difference between a Framework and a Library?', + answer: 'Inversion of Control (IoC). With a library, your application code controls the execution flow and calls the library. With a framework, the framework owns the lifecycle and architecture, calling your code into predefined hooks.' + }, + { + question: 'Why do enterprise organizations overwhelmingly prefer Angular?', + answer: 'Standardized architecture across distributed teams, automated framework migrations via ng update, first-class TypeScript type safety, integrated XSS/CSRF security, and built-in hierarchical dependency injection.' + }, + { + question: 'How has Modern Angular (v16–v19+) modernized compared to legacy Angular?', + answer: 'Introduced Standalone components by default (no NgModules), fine-grained Signals reactivity, built-in control flow (@if, @for), deferrable views (@defer), and zoneless change detection support.' + } + ], + '02-spa-concepts': [ + { + question: 'What is a Single Page Application (SPA)?', + answer: 'A web application that loads a single HTML document shell once and dynamically rewrites the UI via client-side JavaScript by manipulating the DOM and exchanging raw JSON data asynchronously over HTTP APIs without full page reloads.' + }, + { + question: 'What are the main differences between an SPA and a traditional Multi-Page Application (MPA)?', + answer: 'MPAs re-render entire HTML documents on the server for each request, causing white screen flashes and lost client state. SPAs render views in the browser, preserve client memory and state, and exchange lightweight JSON data.' + }, + { + question: 'How does client-side routing work under the hood without refreshing the browser?', + answer: 'It intercepts anchor clicks via preventDefault(), updates the URL and history stack using the HTML5 History API (history.pushState), listens for popstate events, and dynamically renders the matched component inside .' + }, + { + question: 'What are the primary challenges of SPAs and how does Angular solve them?', + answer: 'Initial load latency and SEO challenges. Angular solves these using Server-Side Rendering (@angular/ssr) with non-destructive hydration, event replay, route-level code splitting (loadComponent), and deferrable views (@defer).' + }, + { + question: 'What server configuration is required to support deep linking in an SPA?', + answer: 'The web server (Nginx, Apache, or ASP.NET Core) must be configured with a fallback rule (e.g. try_files $uri $uri/ /index.html) to serve index.html for all non-file routes so the client-side router can resolve the path.' + } + ], + '03-component-based-architecture': [ + { + question: 'What is the core philosophy of Component-Based Architecture?', + answer: 'Decomposing complex user interfaces into independent, self-contained, and reusable building blocks that encapsulate their own template, styles, and controller logic.' + }, + { + question: 'What is the difference between Smart (Container) and Dumb (Presentational) components?', + answer: 'Smart components manage state, inject services, and handle business flows. Dumb components receive data strictly via inputs, emit events via outputs, and focus purely on UI rendering with zero external dependencies.' + }, + { + question: 'How does unidirectional data flow operate between parent and child components?', + answer: 'Data flows down from parent to child via property bindings (input()), and user events flow up from child to parent via custom event emitters (output()). Children never directly mutate parent state.' + }, + { + question: 'Why did Angular introduce Signal-based component inputs and outputs?', + answer: 'Signal inputs (input(), input.required()) provide compile-time safety, integrate with computed() and effect(), eliminate ngOnChanges boilerplate, and support zoneless change detection.' + } + ], + '05-typescript-essentials-for-angular': [ + { + question: 'Why did Google choose TypeScript as the foundation for Angular?', + answer: 'TypeScript provides static type safety, refactoring support, compile-time contract enforcement, decorator metadata for framework wiring, and parameter types essential for Dependency Injection.' + }, + { + question: 'How does Angular Dependency Injection leverage TypeScript?', + answer: 'TypeScript emits type metadata for constructor parameters, allowing Angular’s injector to inspect parameter types at runtime and automatically inject the corresponding singleton service token.' + }, + { + question: 'What is the difference between interface and type in TypeScript?', + answer: 'Interfaces define extensible object and class contracts. Types are aliases that can represent primitives, unions (|), intersections (&), tuples, and complex mapped transformations.' + }, + { + question: 'What is the difference between any, unknown, and never in TypeScript?', + answer: 'any disables all type checking; unknown is a type-safe counterpart requiring narrowing before operations; never represents impossible states (e.g. exhaustive switch cases or functions that throw).' + } + ], + '24-nested-routes': [ + { + question: 'How do nested (child) routes work in Angular?', + answer: 'Configured via the children property in route definitions. When a child route activates, its component renders inside the of its parent component rather than replacing the top-level viewport.' + }, + { + question: 'How do child routes access parameters from parent routes?', + answer: 'By configuring paramsInheritanceStrategy: "always" in the router, or by traversing route.parent.paramMap to read parent path and query parameters.' + } + ], + '27-http-interceptors': [ + { + question: 'What is an HttpInterceptor in Angular?', + answer: 'A middleware pattern that inspects, transforms, or handles outgoing HttpRequest objects and incoming HttpResponse events globally (e.g. attaching auth headers, logging, caching, retry logic).' + }, + { + question: 'How are modern functional interceptors defined in Angular 15+?', + answer: 'As pure functions conforming to HttpInterceptorFn: (req, next) => next(req), configured globally via provideHttpClient(withInterceptors([authInterceptor])).' + } + ], + '28-change-detection': [ + { + question: 'What is the difference between Default and OnPush change detection strategies?', + answer: 'Default checks the entire component tree on any asynchronous event. OnPush skips component subtrees unless an @Input() reference changes, an event originated inside the component, or a Signal/AsyncPipe triggers an update.' + }, + { + question: 'How do Angular Signals improve change detection over Zone.js?', + answer: 'Signals provide fine-grained, surgical reactivity: Angular knows exactly which template node depends on which signal, enabling localized DOM updates without traversing the entire component hierarchy.' + } + ], + '38-angular-testing-deep-dive': [ + { + question: 'What is the primary role of TestBed in Angular unit testing?', + answer: 'TestBed is Angular’s test execution module that configures and initializes a mock dependency injection environment, compiling components and templates in isolation.' + }, + { + question: 'What is the difference between ComponentFixture and DebugElement?', + answer: 'ComponentFixture provides a test harness for the component instance and change detection cycle. DebugElement is an abstraction over native DOM elements with cross-platform inspection utilities.' + } + ], + '40-angular-system-design-case-studies': [ + { + question: 'How do you design an enterprise notification center with high event volume?', + answer: 'Use Server-Sent Events (SSE) or WebSockets managed by a singleton service, pipe events through an RxJS buffer/debounce, maintain an in-memory normalized queue, and virtualize the notification dropdown list.' + }, + { + question: 'How do you architect multi-tenant SaaS dashboards in Angular?', + answer: 'Dynamically load tenant configuration via APP_INITIALIZER, inject theme CSS variables at runtime, lazy load tenant-specific feature modules, and enforce tenant-scoped HTTP headers via interceptors.' + } + ], + '41-quick-fire-qa': [ + { + question: 'What is the difference between declarations, imports, and exports in NgModules?', + answer: 'declarations are components/directives/pipes belonging to this module; imports bring in external modules needed by this module; exports make internal declarations available to other modules importing this one.' + }, + { + question: 'What is tree-shaking and how does Angular achieve it?', + answer: 'Tree-shaking eliminates dead/unused code from final production bundles using ES modules static analysis and providedIn: "root" injectable metadata.' + } + ], + '02-rxjs-cheat-sheet': [ + { + question: 'What is the difference between switchMap, mergeMap, concatMap, and exhaustMap?', + answer: 'switchMap cancels previous in-flight requests (search/typeahead); mergeMap runs requests concurrently; concatMap queues requests in strict sequence; exhaustMap ignores new emissions while the current request is pending (submit buttons).' + }, + { + question: 'How do you prevent memory leaks when subscribing to Observables?', + answer: 'Use the AsyncPipe in templates, takeUntilDestroyed() in component constructors, take(1) / first() for single emissions, or manually unsubscribe in ngOnDestroy.' + } + ], + '06-ngrx-cheat-sheet': [ + { + question: 'What are the main components of NgRx architecture?', + answer: 'Store (single source of truth), Actions (dispatched event payloads), Reducers (pure functions creating new state), Selectors (memoized queries), and Effects (handling async side-effects like HTTP calls).' + } + ], + '08-architecture-cheat-sheet': [ + { + question: 'What is the difference between Core and Shared modules?', + answer: 'Core contains singleton services, interceptors, and guards loaded once at startup. Shared contains purely reusable dumb components, directives, and pipes imported by multiple feature modules.' + } + ], + '09-interview-day-cheat-sheet': [ + { + question: 'What are the top 3 architectural topics senior Angular interviewers focus on?', + answer: '1. Change Detection & Performance (Zone.js vs Signals, OnPush, @defer). 2. Reactive Architecture (RxJS stream management, state layering). 3. Scalable App Structure (modular monolith, Nx domain boundaries, lazy loading).' + } + ], + '37-frontend-system-design': [ + { + question: 'What is the primary mindset shift in frontend system design interviews?', + answer: 'Shift focus from framework syntax to large-scale architecture, scalability, maintainability, business domain boundaries, and architectural trade-offs.' + }, + { + question: 'What are the 5 core stages of the Angular system design framework?', + answer: '1. Clarify functional and non-functional requirements. 2. Identify business domains. 3. Define UI architecture & routing. 4. Determine state management strategy. 5. Establish performance, caching, and offline strategies.' + }, + { + question: 'How should state management be structured in large-scale Angular applications?', + answer: 'Categorize state into Local UI state (Signals / component state), Shared feature state (scoped services), and Global entity state (NgRx / Signals Store). Only lift state when multiple decoupled domains require access.' + }, + { + question: 'What performance strategies should be considered when designing enterprise dashboards?', + answer: 'Implement route lazy loading, OnPush change detection with Signals, deferrable views (@defer) for expensive widgets, virtual scrolling for tabular data, and API response caching via HTTP interceptors.' + }, + { + question: 'How do you architect real-time event-driven applications in Angular?', + answer: 'Abstract WebSocket connections behind singleton services, pipe incoming streams into RxJS subjects or signals, normalize entity state in memory, and implement CDK virtual scroll to handle high-frequency renders.' + } + ], + '34-enterprise-architecture': [ + { + question: 'What is the primary purpose of enterprise frontend architecture?', + answer: 'To prevent architectural decay, decouple business domains, ensure independent team velocity, and enforce strict module boundaries using patterns like Nx monorepos and clean architecture.' + }, + { + question: 'What are the three core architectural layers in a scalable Angular enterprise app?', + answer: 'Core (singleton services, auth, global error handling), Shared (dumb UI components, reusable pipes, design system tokens), and Features (domain-specific modules, smart containers, routing, local state).' + }, + { + question: 'How do you prevent architectural decay and circular dependencies across features?', + answer: 'Enforce strict unidirectional dependency rules (e.g. via Nx boundary lint rules): features can import from shared/core, but features must never import from peer features directly. Inter-feature communication occurs via URL navigation or global event buses.' + } + ], + '35-micro-frontends': [ + { + question: 'When should you choose Micro Frontends over a Modular Monolith?', + answer: 'When multiple autonomous teams require independent deployment pipelines, independent release cadences, and distinct domain ownership. For single teams or small-to-medium apps, a modular monolith with strict domain boundaries is preferred.' + }, + { + question: 'What are the architectural trade-offs of Micro Frontends in Angular?', + answer: 'Benefits include independent deployments and domain isolation. Trade-offs include increased tooling complexity, bundle overhead across remotes, version skew between shared libraries, and complex cross-MFE integration testing.' + }, + { + question: 'Should Micro Frontends share a single global NgRx store?', + answer: 'No. Sharing a single mutable global store creates tight coupling across MFEs, defeating independent deployments. Each MFE should manage its own isolated domain state, communicating across boundaries only via contracts, custom events, or URL state.' + } + ], + '30-state-management-fundamentals': [ + { + question: 'What are the three categories of state in Angular applications?', + answer: '1. Component/Local State (UI flags, forms, modal toggles). 2. Shared/Feature State (state shared among a feature’s components via services). 3. Global Application State (authenticated user, global config, cross-domain entities).' + }, + { + question: 'When is a dedicated state management library like NgRx necessary?', + answer: 'When complex state needs to be accessed by many decoupled components across different routes, requires undo/redo or time-travel debugging, has high-frequency updates, or needs strict unidirectional immutability.' + }, + { + question: 'What problem does unidirectional data flow solve in state management?', + answer: 'It eliminates race conditions, unpredictable state mutations, and hard-to-trace bugs by ensuring state changes can only be triggered via explicit actions and flows in one predictable direction to view components.' + } + ], + '31-ngrx-fundamentals': [ + { + question: 'What is the core data flow in NgRx?', + answer: 'Components dispatch Actions. Reducers take the current state and action to calculate new state immutably. Selectors allow components to query slices of state reactively. Effects handle side effects like asynchronous HTTP calls.' + }, + { + question: 'Why should NgRx state be immutable?', + answer: 'Immutability allows OnPush change detection to verify object references in O(1) time without dirty checking, guarantees predictable state transitions, and enables deterministic time-travel debugging.' + } + ], + '32-authentication-authorization': [ + { + question: 'What is the difference between Authentication and Authorization?', + answer: 'Authentication verifies WHO the user is (e.g. login credentials, JWT validation). Authorization determines WHAT resources, features, or routes that authenticated user is permitted to access (roles, permissions, scopes).' + }, + { + question: 'Where should JWT tokens be stored securely in an Angular application?', + answer: 'Ideally in httpOnly, Secure, SameSite cookies to protect against XSS attacks. If stored in memory or storage, access tokens should be short-lived with refresh tokens handled securely.' + } + ], + '33-error-handling-strategy': [ + { + question: 'How should global errors be captured and logged in Angular?', + answer: 'Implement a custom ErrorHandler class to intercept uncaught runtime exceptions globally, extract stack traces, and transmit structured telemetry to monitoring services like Sentry or Application Insights.' + }, + { + question: 'How should HTTP errors be handled gracefully across the application?', + answer: 'Use an HttpInterceptor with RxJS catchError to centralize error mapping (e.g. 401 redirect, 403 forbidden toast, 500 retry mechanism) before re-throwing or providing user-friendly fallback notifications.' + } + ], + '23-route-guards': [ + { + question: 'What is the difference between canActivate and canMatch in Angular routing?', + answer: 'canActivate runs after the route matches to check if a component can be activated; if it fails, the route still matched. canMatch runs during route matching; if it returns false, Angular continues searching for alternative routes with the same path (ideal for feature flags or MFE variants).' + }, + { + question: 'When would you use a canDeactivate guard?', + answer: 'To prevent users from accidentally navigating away from a page with unsaved form changes, prompting them with a confirmation dialog before route transition.' + } + ], + '25-route-resolvers': [ + { + question: 'What is the primary architectural purpose of a Route Resolver in Angular?', + answer: 'A resolver pre-fetches essential data before the route component activates and renders, preventing UI flickering and blank layout states while waiting for API responses.' + }, + { + question: 'What is the trade-off of using Route Resolvers?', + answer: 'Navigation is delayed until the resolver observable completes. If the API response is slow, the user stays on the previous page with no visual progress unless a global navigation loading indicator is shown.' + } + ], + '14-lifecycle-hooks': [ + { + question: 'What is the difference between ngOnInit and the constructor in Angular?', + answer: 'The constructor is standard TypeScript instantiation for dependency injection. ngOnInit is called by Angular after the component’s @Input properties are initialized and data-bound for the first time.' + }, + { + question: 'Why is ngOnDestroy critical for enterprise Angular applications?', + answer: 'It cleans up resources to prevent memory leaks, such as unsubscribing from RxJS observables, disconnecting MutationObservers or WebSockets, and clearing intervals/timeouts.' + } + ], + '16-subjects-and-behaviorsubject': [ + { + question: 'What is the difference between Subject, BehaviorSubject, and ReplaySubject?', + answer: 'Subject emits only future events. BehaviorSubject requires an initial value and emits the current/latest value to new subscribers immediately. ReplaySubject buffers a configured number of past values and replays them to new subscribers.' + }, + { + question: 'Why should you expose Subjects as Observables in services using asObservable()?', + answer: 'Encapsulation: it prevents external consumers from calling .next() or manipulating internal state directly, ensuring only the service controls mutations.' + } + ], + '01-angular-cheat-sheet': [ + { + question: 'What is the fundamental difference between Components and Directives?', + answer: 'A Component is a Directive with an associated HTML template and styles. Directives without templates attach custom behavior or styling to existing DOM elements.' + }, + { + question: 'Why does Angular use Dependency Injection?', + answer: 'DI provides loose coupling, simplifies unit testing via mock injection, promotes single responsibility, and allows flexible hierarchical scoping (root, feature, or component level).' + } + ], + '03-signals-cheat-sheet': [ + { + question: 'Do Angular Signals replace RxJS?', + answer: 'No. Signals are ideal for synchronous reactive UI state, template binding, and localized derivation. RxJS remains the best tool for complex asynchronous streams, WebSocket events, debounce/throttle, and cancellation.' + }, + { + question: 'What is the difference between computed() and effect() in Angular Signals?', + answer: 'computed() produces a read-only derived signal lazily and memoizes the result without side effects. effect() executes side effects (logging, DOM mutations, third-party libraries) whenever its dependent signals change.' + } + ], + '04-routing-cheat-sheet': [ + { + question: 'How does Angular lazy loading work with standalone components?', + answer: 'Using loadComponent: () => import("./path").then(m => m.Component) in route configuration, Angular creates a separate chunk loaded only when the route is requested.' + }, + { + question: 'What is the role of Route Resolvers?', + answer: 'Resolvers fetch required API data before the route component is rendered, ensuring the component doesn’t render an empty or partial UI while waiting for initial data.' + } + ], + '05-forms-cheat-sheet': [ + { + question: 'Why are Reactive Forms preferred over Template-Driven Forms in enterprise applications?', + answer: 'Reactive Forms are synchronous, programmatic, fully typed, easier to unit test without DOM interaction, and provide observable streams (valueChanges, statusChanges) for complex cross-field validation.' + }, + { + question: 'How do Async Validators work in Angular Reactive Forms?', + answer: 'Async Validators return an Observable or Promise that emits validation errors or null after completing asynchronous operations (such as verifying username availability via an HTTP call).' + } + ], + '07-auth-cheat-sheet': [ + { + question: 'How should HTTP requests automatically attach JWT tokens in Angular?', + answer: 'Using an HttpInterceptor (via withInterceptors([authInterceptor])), which clones outgoing HttpRequest objects and appends the Authorization: Bearer header before forwarding the request.' + }, + { + question: 'How do you handle 401 Unauthorized errors globally?', + answer: 'In an HTTP error interceptor, catch 401 responses using RxJS catchError, attempt token refresh if available, or clear session state and redirect to the login route.' + } + ] +}; + +function extractHeadings(content) { + const headingRegex = /^(#{1,3})\s+(.+)$/gm; + const headings = []; + let match; + while ((match = headingRegex.exec(content)) !== null) { + const text = match[2].trim(); + if (/^(Interview Priority|Interview Frequency|Recommended Depth|Relevant For|Table of Contents|navigation)/i.test(text)) { + continue; + } + headings.push({ + level: match[1].length, + text + }); + } + return headings; +} + +function extractFlashcards(content, topicTitle, topicId) { + // If curated flashcards exist for this topic, return them directly + if (topicId && CURATED_FLASHCARDS[topicId] && CURATED_FLASHCARDS[topicId].length > 0) { + return CURATED_FLASHCARDS[topicId].map(c => ({ + question: c.question, + answer: c.answer, + source: topicTitle + })); + } + + const cards = []; + const lines = content.split(/\r?\n/); + let inCodeBlock = false; + + // Strategy 1: Quick-fire format: "Question?" on heading / bold line, followed by text answer + for (let i = 0; i < lines.length; i++) { + const rawLine = lines[i]; + const line = rawLine.trim(); + + if (line.startsWith('```')) { + inCodeBlock = !inCodeBlock; + continue; + } + if (inCodeBlock) continue; + + // e.g. "# What Is a Service?" or "### Why does Angular use TypeScript?" or "## What is an Attribute Directive?" + const qMatch = line.match(/^(?:#{1,3})\s*(?:Q:\s*|Question:\s*)?([A-Z][^?\n\r]{8,}\?)/i) || + line.match(/^\*\*(?:Q:\s*|Question:\s*)?([A-Z][^?\n\r]{8,}\?)\*\*/i); + + if (qMatch && !line.startsWith('')) { + break; + } + const cleanLine = next.replace(/^[-*→]\s*/, '').replace(/[*_`]/g, '').trim(); + if (cleanLine.length > 5 && !cleanLine.endsWith(':')) { + ansLines.push(cleanLine); + } + if (ansLines.length >= 3) break; + } + + let answer = ansLines.join(' ').replace(/:\s*$/, '.').trim(); + if (answer.length >= 30 && !answer.includes('```') && !answer.startsWith('|') && !/^(Imagine|Think of|Consider|For example|Example)/i.test(answer)) { + cards.push({ + question, + answer, + source: topicTitle + }); + } + } + } + + // Strategy 2: "## Common Interview Questions" list + const qList = []; + let inQSection = false; + inCodeBlock = false; + for (const l of lines) { + const t = l.trim(); + if (t.startsWith('```')) { + inCodeBlock = !inCodeBlock; + continue; + } + if (inCodeBlock) continue; + + if (/^##\s+.*Interview Questions/i.test(t)) { + inQSection = true; + continue; + } + if (inQSection && /^##\s+/.test(t)) { + inQSection = false; + } + if (inQSection && (t.startsWith('-') || t.startsWith('*') || /^\d+[\.\)]/.test(t))) { + const qText = t.replace(/^[-*\d\.\)]\s+/, '').replace(/^\*\*|\*\*$/g, '').replace(/`[^`]+`/g, '').trim(); + if (qText.length > 10 && !qText.includes('```')) { + qList.push(qText.endsWith('?') ? qText : `${qText}?`); + } + } + } + + // Strategy 3: Key Takeaways list: clean items + const takeaways = []; + let inTakeaways = false; + inCodeBlock = false; + for (const l of lines) { + const t = l.trim(); + if (t.startsWith('```')) { + inCodeBlock = !inCodeBlock; + continue; + } + if (inCodeBlock) continue; + + if (/^#+\s+(Key Takeaways|Key Message|Summary|Core Architectural Principles)/i.test(t)) { + inTakeaways = true; + continue; + } + if (inTakeaways && /^#+\s+[^#]/.test(t) && !/Takeaways|Message/i.test(t)) { + inTakeaways = false; + } + if (inTakeaways) { + const numMatch = t.match(/^\d+[\.\)]\s+(.+)/); + const text = numMatch ? numMatch[1].trim() : (t.startsWith('-') || t.startsWith('*') ? t.replace(/^[-*]\s+/, '').trim() : ''); + const clean = text.replace(/[*_`]/g, '').trim(); + if (clean.length > 25 && !clean.startsWith('|') && !clean.includes('```')) { + takeaways.push(clean); + } + } + } + + // Pair questions from Strategy 2 if cards is still low + if (cards.length < 3 && qList.length > 0) { + qList.forEach((q, idx) => { + const ans = takeaways[idx] || takeaways[0] || `Key architectural interview topic in ${topicTitle}. Master the design decisions and trade-offs explained in this module.`; + cards.push({ + question: q, + answer: ans, + source: topicTitle + }); + }); + } + + // Fallback if still low + if (cards.length === 0 && takeaways.length > 0) { + takeaways.slice(0, 4).forEach((item, idx) => { + cards.push({ + question: `What is a key architectural rule regarding ${topicTitle} (Point ${idx + 1})?`, + answer: item, + source: topicTitle + }); + }); + } + + if (cards.length === 0) { + cards.push({ + question: `What is the core architectural purpose of ${topicTitle}?`, + answer: `${topicTitle} provides structured, maintainable patterns for modern Angular enterprise applications. Refer to the deep dive guide for implementation details and trade-offs.`, + source: topicTitle + }); + } + + // Deduplicate and filter any invalid cards + const seen = new Set(); + const uniqueCards = []; + for (const c of cards) { + const key = c.question.toLowerCase().trim(); + if (!seen.has(key) && c.answer.length >= 20 && !c.answer.includes('```') && !c.question.includes('```')) { + seen.add(key); + uniqueCards.push(c); + } + } + + return uniqueCards.slice(0, 8); +} + +function extractRecapNotes(content) { + // Strip any residual navigation blocks + const cleanContent = content.replace(/[\s\S]*?/gi, ''); + const lines = cleanContent.split(/\r?\n/); + const notes = []; + let inRecap = false; + + for (const l of lines) { + const t = l.trim(); + if (/^#+\s+(Key Message|Key Takeaways|Revision Notes|Summary|Key Architectural Principles|Quick Summary|Executive Summary)/i.test(t)) { + inRecap = true; + continue; + } + if (inRecap && /^#+\s+[^#]/.test(t)) { + inRecap = false; + } + // Hard stop on navigation or horizontal rules + if (inRecap && (t.startsWith('[\s\S]*?/gi, ''); + + // Title from first # Heading + const titleMatch = content.match(/^#\s+(.+)$/m); + const title = titleMatch ? titleMatch[1].trim() : file.replace('.md', ''); + + // Slug / ID + const id = file.replace('.md', ''); + const numMatch = id.match(/^(\d+[a-z]?)/); + const orderNum = numMatch ? numMatch[1] : '99'; + + // Word count & Reading Time + const wordCount = content.split(/\s+/).filter(Boolean).length; + const readingTime = Math.max(1, Math.ceil(wordCount / 200)); + + const flashcards = extractFlashcards(content, title, id); + const recapNotes = extractRecapNotes(content); + const searchTokens = extractSearchTokens(title, content, meta.title); + const headings = extractHeadings(content); + + // Summary description (skip metadata blocks and pick first substantive descriptive paragraph) + const paragraphs = content + .split(/\r?\n\r?\n/) + .map(p => p.trim()) + .filter(p => { + if (!p || p.startsWith('#') || p.startsWith(' +
+ {{ topic.categoryTitle }} +

{{ topic.title }}

+

{{ topic.summary }}

+ +
+ ⏱️ {{ topic.readingTime }} min read + 🎴 {{ topic.flashcards.length }} Flashcards +
+
+ + +
+ Key Architectural Rules +
+
+ @for (note of topic.recapNotes; track $index) { +
+ {{ $index + 1 }} +

{{ note }}

+
+ } @empty { +

Review full Deep Dive module for exhaustive notes.

+ } +
+ + +
+ + +
+ } @else { +

Select a topic from the curriculum to view executive takeaways.

+ } +
+ + } + `, + styles: [` + .drawer-backdrop { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.55); + backdrop-filter: blur(4px); + z-index: 200; + } + + .drawer-container { + position: fixed; + top: 0; + right: 0; + width: var(--drawer-width); + max-width: 90vw; + height: 100vh; + z-index: 201; + display: flex; + flex-direction: column; + border-radius: 1.25rem 0 0 1.25rem; + box-shadow: -8px 0 32px rgba(0, 0, 0, 0.6); + animation: slideIn 250ms cubic-bezier(0.16, 1, 0.3, 1); + overflow: hidden; + } + + @keyframes slideIn { + from { transform: translateX(100%); } + to { transform: translateX(0); } + } + + .drawer-header { + padding: 1.25rem 1.5rem; + border-bottom: 1px solid rgba(255, 255, 255, 0.08); + display: flex; + align-items: center; + justify-content: space-between; + } + + .drawer-title-wrap { + display: flex; + align-items: center; + gap: 0.75rem; + } + + .drawer-icon { + font-size: 1.5rem; + } + + .drawer-title { + font-size: 1rem; + font-weight: 800; + color: var(--text-primary); + } + + .drawer-sub { + font-size: 0.75rem; + color: var(--text-muted); + } + + .close-btn { + width: 32px; + height: 32px; + border-radius: 0.5rem; + background: rgba(255, 255, 255, 0.05); + border: 1px solid rgba(255, 255, 255, 0.1); + color: var(--text-secondary); + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: all var(--transition-fast); + } + + .close-btn:hover { + color: var(--accent-cyan); + border-color: var(--accent-cyan); + } + + .drawer-content { + flex: 1; + overflow-y: auto; + padding: 1.25rem 1.5rem; + display: flex; + flex-direction: column; + gap: 1rem; + } + + .drawer-card { + padding: 1rem; + } + + .card-badge { + font-size: 0.68rem; + text-transform: uppercase; + font-weight: 700; + color: var(--accent-cyan); + letter-spacing: 0.05em; + } + + .topic-title { + font-size: 1.15rem; + font-weight: 700; + margin: 0.35rem 0; + color: var(--text-primary); + } + + .topic-summary { + font-size: 0.85rem; + color: var(--text-secondary); + line-height: 1.5; + } + + .meta-row { + display: flex; + gap: 1rem; + margin-top: 0.75rem; + font-size: 0.75rem; + color: var(--text-muted); + font-weight: 600; + } + + .section-title { + font-size: 0.85rem; + font-weight: 800; + text-transform: uppercase; + letter-spacing: 0.05em; + color: var(--text-secondary); + margin-top: 0.5rem; + } + + .takeaways-list { + display: flex; + flex-direction: column; + gap: 0.65rem; + } + + .takeaway-card { + padding: 0.75rem 1rem; + display: flex; + align-items: flex-start; + gap: 0.75rem; + } + + .takeaway-num { + width: 22px; + height: 22px; + border-radius: 50%; + background: rgba(0, 243, 255, 0.15); + border: 1px solid rgba(0, 243, 255, 0.3); + color: var(--accent-cyan); + font-size: 0.72rem; + font-weight: 800; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + } + + .takeaway-text { + font-size: 0.85rem; + color: var(--text-secondary); + line-height: 1.45; + } + + .quick-actions { + display: flex; + flex-direction: column; + gap: 0.5rem; + margin-top: auto; + padding-top: 1rem; + } + + .empty-text { + font-size: 0.85rem; + color: var(--text-muted); + text-align: center; + padding: 2rem 0; + } + `] +}) +export class QuickRecapDrawerComponent { + readonly facade = inject(ReaderFacade); +} diff --git a/apps/portal/src/app/core/layout/header/header.component.ts b/apps/portal/src/app/core/layout/header/header.component.ts new file mode 100644 index 0000000..ce9dd2d --- /dev/null +++ b/apps/portal/src/app/core/layout/header/header.component.ts @@ -0,0 +1,304 @@ +import { Component, inject, HostListener } from '@angular/core'; +import { RouterLink } from '@angular/router'; +import { ThemeService } from '../../services/theme.service'; +import { ReaderFacade, TrackFilter } from '../../state/reader.facade'; + +@Component({ + selector: 'app-header', + standalone: true, + imports: [RouterLink], + template: ` +
+
+ +
+ + + + + +
+
+ Angular Mastery + Architect Edition +
+
+ + +
+ + + + +
+
+ +
+ + + + +
+ + {{ facade.progressPercent() }}% Mastered +
+ + + + + + +
+
+ `, + styles: [` + :host { + display: block; + position: sticky; + top: 0; + z-index: 1000; + width: 100%; + } + + .app-header { + height: var(--header-height); + margin: 0.5rem 1rem 0.75rem 1rem; + padding: 0 1.25rem; + display: flex; + align-items: center; + justify-content: space-between; + backdrop-filter: var(--glass-blur-heavy); + -webkit-backdrop-filter: var(--glass-blur-heavy); + border: var(--glass-border); + background: var(--glass-bg); + border-radius: 1rem; + box-shadow: var(--glass-shadow); + } + + .header-left, .header-right { + display: flex; + align-items: center; + gap: 1rem; + } + + .brand-link { + display: flex; + align-items: center; + gap: 0.75rem; + text-decoration: none; + color: inherit; + } + + .logo-icon { + width: 38px; + height: 38px; + border-radius: 0.6rem; + background: linear-gradient(135deg, rgba(0, 243, 255, 0.2) 0%, rgba(56, 189, 248, 0.1) 100%); + border: 1px solid var(--accent-cyan); + display: flex; + align-items: center; + justify-content: center; + color: var(--accent-cyan); + box-shadow: 0 0 12px var(--accent-cyan-glow); + } + + .brand-text { + display: flex; + flex-direction: column; + } + + .brand-title { + font-weight: 800; + font-size: 1.05rem; + letter-spacing: -0.02em; + background: linear-gradient(135deg, var(--text-primary) 0%, var(--accent-cyan) 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + } + + .brand-badge { + font-size: 0.65rem; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--accent-cyan); + font-weight: 700; + } + + .track-selector { + display: flex; + align-items: center; + background: rgba(0, 0, 0, 0.18); + padding: 0.2rem; + border-radius: 0.6rem; + border: 1px solid rgba(255, 255, 255, 0.06); + margin-left: 0.5rem; + } + + .track-btn { + background: transparent; + border: none; + color: var(--text-secondary); + font-size: 0.75rem; + font-weight: 600; + padding: 0.35rem 0.65rem; + border-radius: 0.45rem; + cursor: pointer; + transition: all var(--transition-fast); + font-family: inherit; + } + + .track-btn:hover { + color: var(--text-primary); + } + + .track-btn.active { + background: rgba(0, 243, 255, 0.15); + color: var(--accent-cyan); + border: 1px solid rgba(0, 243, 255, 0.3); + box-shadow: 0 0 10px rgba(0, 243, 255, 0.2); + } + + .search-trigger-btn { + display: flex; + align-items: center; + gap: 0.6rem; + padding: 0.45rem 0.85rem; + background: rgba(0, 0, 0, 0.2); + border: 1px solid rgba(255, 255, 255, 0.08); + border-radius: 0.6rem; + color: var(--text-muted); + cursor: pointer; + font-family: inherit; + font-size: 0.82rem; + transition: all var(--transition-fast); + } + + .search-trigger-btn:hover { + border-color: var(--accent-cyan); + color: var(--text-primary); + box-shadow: 0 0 12px var(--accent-cyan-glow); + } + + .search-kbd { + font-family: var(--font-mono); + font-size: 0.7rem; + padding: 0.15rem 0.4rem; + border-radius: 0.3rem; + background: rgba(255, 255, 255, 0.08); + border: 1px solid rgba(255, 255, 255, 0.15); + color: var(--text-secondary); + } + + .progress-pill { + display: flex; + align-items: center; + gap: 0.45rem; + padding: 0.35rem 0.75rem; + border-radius: 9999px; + background: rgba(52, 211, 153, 0.1); + border: 1px solid rgba(52, 211, 153, 0.3); + font-size: 0.75rem; + font-weight: 700; + color: var(--accent-emerald); + } + + .progress-dot { + width: 6px; + height: 6px; + border-radius: 50%; + background: var(--accent-emerald); + box-shadow: 0 0 6px var(--accent-emerald); + } + + .icon-btn { + width: 36px; + height: 36px; + border-radius: 0.6rem; + display: flex; + align-items: center; + justify-content: center; + background: rgba(255, 255, 255, 0.05); + border: 1px solid rgba(255, 255, 255, 0.1); + color: var(--text-secondary); + cursor: pointer; + transition: all var(--transition-fast); + } + + .icon-btn:hover { + color: var(--accent-cyan); + border-color: var(--accent-cyan); + box-shadow: 0 0 12px var(--accent-cyan-glow); + } + + @media (max-width: 900px) { + .track-selector { + display: none; + } + .search-label, .search-kbd { + display: none; + } + } + `] +}) +export class HeaderComponent { + readonly themeService = inject(ThemeService); + readonly facade = inject(ReaderFacade); + + @HostListener('window:keydown', ['$event']) + onKeyDown(e: KeyboardEvent): void { + if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'k') { + e.preventDefault(); + this.facade.toggleSearch(); + } + } +} diff --git a/apps/portal/src/app/core/layout/scroll-to-top/scroll-to-top.component.ts b/apps/portal/src/app/core/layout/scroll-to-top/scroll-to-top.component.ts new file mode 100644 index 0000000..d34922b --- /dev/null +++ b/apps/portal/src/app/core/layout/scroll-to-top/scroll-to-top.component.ts @@ -0,0 +1,151 @@ +import { Component, OnInit, OnDestroy, signal } from '@angular/core'; + +@Component({ + selector: 'app-scroll-to-top', + standalone: true, + template: ` + + `, + styles: [` + :host { + position: fixed; + bottom: 2.25rem; + right: 2.25rem; + z-index: 99999; + pointer-events: none; + } + + .scroll-top-btn { + pointer-events: auto; + display: inline-flex; + align-items: center; + gap: 0.4rem; + padding: 0.6rem 1rem 0.6rem 0.8rem; + border-radius: 9999px; + background: var(--bg-surface-elevated); + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); + border: 1.5px solid var(--accent-cyan); + color: var(--accent-cyan); + font-family: var(--font-sans); + font-size: 0.8rem; + font-weight: 800; + letter-spacing: 0.05em; + text-transform: uppercase; + cursor: pointer; + box-shadow: 0 8px 25px rgba(0, 0, 0, 0.35), 0 0 16px var(--accent-cyan-glow); + opacity: 0; + visibility: hidden; + transform: translateY(20px) scale(0.9); + transition: opacity 200ms ease, transform 240ms cubic-bezier(0.34, 1.56, 0.64, 1), background 150ms ease, border-color 150ms ease, box-shadow 150ms ease; + } + + .scroll-top-btn.visible { + opacity: 1; + visibility: visible; + transform: translateY(0) scale(1); + } + + .scroll-top-btn:hover { + background: var(--accent-cyan); + color: #030712; + border-color: var(--accent-cyan); + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5), 0 0 24px var(--accent-cyan); + transform: translateY(-3px) scale(1.05); + } + + .scroll-top-btn:active { + transform: translateY(0) scale(0.96); + } + + .arrow-icon { + width: 16px; + height: 16px; + transition: transform 150ms ease; + } + + .scroll-top-btn:hover .arrow-icon { + transform: translateY(-2px); + } + + .btn-label { + line-height: 1; + } + + @media (max-width: 768px) { + :host { + bottom: 1.25rem; + right: 1.25rem; + } + .scroll-top-btn { + padding: 0.5rem 0.8rem 0.5rem 0.65rem; + font-size: 0.75rem; + } + } + `] +}) +export class ScrollToTopComponent implements OnInit, OnDestroy { + readonly isVisible = signal(false); + private cleanupListener?: () => void; + + ngOnInit(): void { + const handleScroll = () => { + const winScroll = window.pageYOffset || window.scrollY || 0; + const docScroll = document.documentElement?.scrollTop || 0; + const bodyScroll = document.body?.scrollTop || 0; + const scrollPos = Math.max(winScroll, docScroll, bodyScroll); + this.isVisible.set(scrollPos > 100); + }; + + // Capture phase listener ensures scroll events on window, html, body or any container are captured + const captureListener = (event: Event) => { + let scrollPos = window.pageYOffset || window.scrollY || 0; + scrollPos = Math.max(scrollPos, document.documentElement?.scrollTop || 0, document.body?.scrollTop || 0); + const target = event.target as HTMLElement; + if (target && typeof target.scrollTop === 'number') { + scrollPos = Math.max(scrollPos, target.scrollTop); + } + this.isVisible.set(scrollPos > 100); + }; + + window.addEventListener('scroll', captureListener, { capture: true, passive: true }); + document.addEventListener('scroll', captureListener, { capture: true, passive: true }); + + this.cleanupListener = () => { + window.removeEventListener('scroll', captureListener, true); + document.removeEventListener('scroll', captureListener, true); + }; + + handleScroll(); + } + + ngOnDestroy(): void { + this.cleanupListener?.(); + } + + scrollToTop(): void { + window.scrollTo({ top: 0, left: 0, behavior: 'smooth' }); + document.documentElement?.scrollTo({ top: 0, left: 0, behavior: 'smooth' }); + document.body?.scrollTo({ top: 0, left: 0, behavior: 'smooth' }); + const main = document.querySelector('.main-viewport'); + if (main) main.scrollTo({ top: 0, left: 0, behavior: 'smooth' }); + } +} diff --git a/apps/portal/src/app/core/layout/search-modal/search-modal.component.ts b/apps/portal/src/app/core/layout/search-modal/search-modal.component.ts new file mode 100644 index 0000000..7dcfc40 --- /dev/null +++ b/apps/portal/src/app/core/layout/search-modal/search-modal.component.ts @@ -0,0 +1,306 @@ +import { Component, inject, signal } from '@angular/core'; +import { Router } from '@angular/router'; +import { FormsModule } from '@angular/forms'; +import { SearchService } from '../../services/search.service'; +import { ReaderFacade } from '../../state/reader.facade'; +import { SearchResult } from '../../models/content.model'; + +@Component({ + selector: 'app-search-modal', + standalone: true, + imports: [FormsModule], + template: ` + @if (facade.isSearchOpen()) { +
+
+
+
+ + + + + +
+ + +
+ +
+ @for (result of results(); track result.topic.id) { + + } @empty { +
+

No topics matching "{{ searchQuery }}"

+ Try searching for "Signals", "Change Detection", "RxJS", or "DI" +
+ } +
+
+ } + `, + styles: [` + .search-backdrop { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.65); + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); + z-index: 2000; + } + + .search-dialog { + position: fixed; + top: 12%; + left: 50%; + transform: translateX(-50%); + width: 680px; + max-width: 92vw; + max-height: 75vh; + z-index: 2001; + display: flex; + flex-direction: column; + border-radius: 1.25rem; + background: var(--bg-surface-elevated); + border: 1px solid var(--glass-border-cyan); + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4), 0 0 30px var(--accent-cyan-glow); + backdrop-filter: blur(24px); + -webkit-backdrop-filter: blur(24px); + animation: popIn 180ms cubic-bezier(0.16, 1, 0.3, 1); + overflow: hidden; + } + + [data-theme='light'] .search-dialog { + background: #ffffff; + border: 1px solid rgba(2, 132, 199, 0.35); + box-shadow: 0 24px 60px rgba(15, 23, 42, 0.18), 0 0 20px rgba(2, 132, 199, 0.15); + } + + [data-theme='dark'] .search-dialog { + background: rgba(11, 17, 32, 0.95); + border: 1px solid rgba(0, 243, 255, 0.35); + } + + @keyframes popIn { + from { transform: translateX(-50%) scale(0.96); opacity: 0; } + to { transform: translateX(-50%) scale(1); opacity: 1; } + } + + .search-header { + display: flex; + align-items: center; + gap: 0.75rem; + padding: 1.15rem 1.25rem; + border-bottom: 1px solid var(--glass-border); + } + + [data-theme='light'] .search-header { + border-bottom: 1px solid rgba(226, 232, 240, 0.9); + } + + [data-theme='dark'] .search-header { + border-bottom: 1px solid rgba(255, 255, 255, 0.1); + } + + .search-input-box { + flex: 1; + display: flex; + align-items: center; + gap: 0.75rem; + padding: 0.55rem 0.85rem; + border-radius: 0.75rem; + background: rgba(148, 163, 184, 0.08); + border: 1px solid rgba(148, 163, 184, 0.2); + } + + .search-icon { + color: var(--accent-cyan); + flex-shrink: 0; + } + + .search-input { + flex: 1; + background: transparent; + border: none; + outline: none; + font-family: inherit; + font-size: 1rem; + color: var(--text-primary); + font-weight: 500; + } + + .search-input::placeholder { + color: var(--text-muted); + font-size: 0.92rem; + } + + .search-close-btn { + display: inline-flex; + align-items: center; + gap: 0.4rem; + padding: 0.5rem 0.75rem; + border-radius: 0.65rem; + background: rgba(148, 163, 184, 0.1); + border: 1px solid rgba(148, 163, 184, 0.2); + color: var(--text-secondary); + cursor: pointer; + font-family: inherit; + transition: all var(--transition-fast); + } + + .search-close-btn:hover { + background: rgba(244, 63, 94, 0.12); + border-color: rgba(244, 63, 94, 0.35); + color: #f43f5e; + } + + .esc-kbd { + font-family: var(--font-mono); + font-size: 0.68rem; + font-weight: 700; + padding: 0.1rem 0.35rem; + border-radius: 0.3rem; + background: rgba(0, 0, 0, 0.1); + border: 1px solid rgba(148, 163, 184, 0.25); + } + + .search-results { + flex: 1; + overflow-y: auto; + padding: 0.85rem 1rem; + display: flex; + flex-direction: column; + gap: 0.5rem; + } + + .result-item { + width: 100%; + text-align: left; + padding: 0.95rem 1.15rem; + border-radius: 0.75rem; + background: rgba(148, 163, 184, 0.05); + border: 1px solid rgba(148, 163, 184, 0.12); + cursor: pointer; + transition: all var(--transition-fast); + font-family: inherit; + } + + .result-item:hover, .result-item:focus { + background: rgba(0, 243, 255, 0.08); + border-color: var(--accent-cyan); + transform: translateX(3px); + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08); + } + + [data-theme='light'] .result-item:hover { + background: rgba(2, 132, 199, 0.08); + border-color: rgba(2, 132, 199, 0.4); + } + + .result-top { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 0.3rem; + } + + .result-category { + font-size: 0.72rem; + font-weight: 800; + text-transform: uppercase; + color: var(--accent-cyan); + letter-spacing: 0.05em; + } + + .result-match-type { + font-size: 0.65rem; + padding: 0.1rem 0.4rem; + border-radius: 0.3rem; + background: rgba(148, 163, 184, 0.12); + color: var(--text-muted); + text-transform: uppercase; + font-weight: 700; + letter-spacing: 0.04em; + } + + .result-title { + font-size: 1.05rem; + font-weight: 800; + color: var(--text-primary); + margin-bottom: 0.3rem; + } + + .result-snippet { + font-size: 0.85rem; + color: var(--text-secondary); + line-height: 1.45; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + } + + .no-results { + padding: 3rem 1rem; + text-align: center; + } + + .no-results-title { + font-size: 1rem; + font-weight: 600; + color: var(--text-primary); + } + + .hint { + display: block; + margin-top: 0.5rem; + font-size: 0.85rem; + color: var(--accent-cyan); + font-weight: 600; + } + `] +}) +export class SearchModalComponent { + private readonly searchService = inject(SearchService); + readonly facade = inject(ReaderFacade); + private readonly router = inject(Router); + + searchQuery = ''; + readonly results = signal([]); + + constructor() { + this.onSearchChange(''); + } + + onSearchChange(query: string): void { + this.searchService.search(query).subscribe(res => { + this.results.set(res); + }); + } + + selectResult(result: SearchResult): void { + this.facade.setSearchOpen(false); + this.facade.setActiveTopicById(result.topic.id); + this.router.navigate(['/topic', result.topic.id]); + } +} diff --git a/apps/portal/src/app/core/layout/shell/shell.component.ts b/apps/portal/src/app/core/layout/shell/shell.component.ts new file mode 100644 index 0000000..3fef740 --- /dev/null +++ b/apps/portal/src/app/core/layout/shell/shell.component.ts @@ -0,0 +1,64 @@ +import { Component } from '@angular/core'; +import { RouterOutlet } from '@angular/router'; +import { HeaderComponent } from '../header/header.component'; +import { SidebarComponent } from '../sidebar/sidebar.component'; +import { QuickRecapDrawerComponent } from '../drawer/quick-recap-drawer.component'; +import { SearchModalComponent } from '../search-modal/search-modal.component'; +import { ScrollToTopComponent } from '../scroll-to-top/scroll-to-top.component'; + +@Component({ + selector: 'app-shell', + standalone: true, + imports: [ + RouterOutlet, + HeaderComponent, + SidebarComponent, + QuickRecapDrawerComponent, + SearchModalComponent, + ScrollToTopComponent + ], + template: ` +
+ +
+ +
+ +
+
+ + + +
+ `, + styles: [` + .shell-root { + display: flex; + flex-direction: column; + min-height: 100vh; + position: relative; + } + + .shell-body { + display: flex; + flex: 1; + max-width: 1600px; + margin: 0 auto; + width: 100%; + padding-bottom: 2rem; + } + + .main-viewport { + flex: 1; + min-width: 0; /* Prevents overflow blowout */ + padding: 0 1.5rem; + } + + @media (max-width: 900px) { + .main-viewport { + padding: 0 1rem; + } + } + `] +}) +export class ShellComponent {} diff --git a/apps/portal/src/app/core/layout/sidebar/sidebar.component.ts b/apps/portal/src/app/core/layout/sidebar/sidebar.component.ts new file mode 100644 index 0000000..251f920 --- /dev/null +++ b/apps/portal/src/app/core/layout/sidebar/sidebar.component.ts @@ -0,0 +1,305 @@ +import { Component, inject, signal } from '@angular/core'; +import { RouterLink, RouterLinkActive } from '@angular/router'; +import { ReaderFacade } from '../../state/reader.facade'; +import { CategoryGroup, TopicManifest } from '../../models/content.model'; + +@Component({ + selector: 'app-sidebar', + standalone: true, + imports: [RouterLink, RouterLinkActive], + template: ` + + `, + styles: [` + .sidebar { + width: var(--sidebar-width); + height: calc(100vh - var(--header-height) - 2.5rem); + position: sticky; + top: var(--sticky-offset); + margin-left: 1rem; + display: flex; + flex-direction: column; + overflow: hidden; + border-radius: 1rem; + } + + .sidebar-header { + padding: 1rem 1.25rem; + border-bottom: 1px solid rgba(255, 255, 255, 0.07); + } + + .sidebar-title { + display: flex; + align-items: center; + justify-content: space-between; + font-size: 0.85rem; + font-weight: 700; + color: var(--text-secondary); + text-transform: uppercase; + letter-spacing: 0.05em; + } + + .count-pill { + font-size: 0.7rem; + background: rgba(0, 243, 255, 0.1); + color: var(--accent-cyan); + border: 1px solid rgba(0, 243, 255, 0.25); + padding: 0.15rem 0.5rem; + border-radius: 9999px; + font-weight: 700; + } + + .category-list { + flex: 1; + overflow-y: auto; + padding: 0.75rem 0.5rem 1.5rem 0.5rem; + } + + .category-group { + margin-bottom: 0.5rem; + } + + .category-header-btn { + width: 100%; + display: flex; + align-items: center; + justify-content: space-between; + background: transparent; + border: none; + padding: 0.55rem 0.65rem; + border-radius: 0.5rem; + color: var(--text-primary); + cursor: pointer; + font-family: inherit; + font-size: 0.82rem; + font-weight: 700; + transition: all var(--transition-fast); + text-align: left; + } + + .category-header-btn:hover { + background: var(--sidebar-item-hover-bg); + color: var(--sidebar-item-hover-text); + } + + .cat-title-wrap { + display: flex; + align-items: center; + gap: 0.5rem; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .cat-icon { + font-size: 1rem; + } + + .cat-title { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .cat-meta { + display: flex; + align-items: center; + gap: 0.4rem; + } + + .cat-badge { + font-size: 0.65rem; + background: rgba(255, 255, 255, 0.07); + padding: 0.1rem 0.4rem; + border-radius: 0.35rem; + color: var(--text-muted); + font-weight: 600; + } + + .chevron-icon { + transition: transform var(--transition-fast); + color: var(--text-muted); + } + + .chevron-icon.rotated { + transform: rotate(-90deg); + } + + .topic-items { + margin-top: 0.2rem; + margin-left: 0.5rem; + padding-left: 0.5rem; + border-left: 1px solid rgba(255, 255, 255, 0.08); + display: flex; + flex-direction: column; + gap: 0.15rem; + } + + .topic-link { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.45rem 0.65rem; + border-radius: 0.5rem; + font-size: 0.8rem; + color: var(--text-secondary); + text-decoration: none; + transition: all var(--transition-fast); + border: 1px solid transparent; + } + + .topic-link:hover { + background: var(--sidebar-item-hover-bg); + color: var(--sidebar-item-hover-text); + border-color: var(--sidebar-item-hover-border); + transform: translateX(3px); + } + + .topic-link.active { + background: linear-gradient(135deg, rgba(var(--accent-cyan-rgb), 0.14) 0%, rgba(56, 189, 248, 0.06) 100%); + color: var(--accent-cyan); + border-color: var(--sidebar-item-hover-border); + font-weight: 700; + box-shadow: 0 0 12px rgba(var(--accent-cyan-rgb), 0.12); + } + + .topic-link-left { + display: flex; + align-items: center; + gap: 0.5rem; + overflow: hidden; + } + + .status-dot { + width: 14px; + height: 14px; + border-radius: 50%; + border: 1.5px solid rgba(148, 163, 184, 0.4); + background: transparent; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + padding: 0; + color: #030712; + flex-shrink: 0; + transition: all var(--transition-fast); + } + + .status-dot.completed { + background: var(--accent-emerald); + border-color: var(--accent-emerald); + color: #030712; + box-shadow: 0 0 8px rgba(52, 211, 153, 0.5); + } + + .topic-name { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .read-time { + font-size: 0.7rem; + color: var(--text-muted); + font-family: var(--font-mono); + flex-shrink: 0; + } + + @media (max-width: 900px) { + .sidebar { + display: none; + } + } + `] +}) +export class SidebarComponent { + readonly facade = inject(ReaderFacade); + private collapsedCategories = signal>(new Set()); + + toggleCategory(key: string): void { + this.collapsedCategories.update(set => { + const next = new Set(set); + if (next.has(key)) next.delete(key); + else next.add(key); + return next; + }); + } + + isCollapsed(key: string): boolean { + return this.collapsedCategories().has(key); + } + + getCategoryTopics(categoryKey: string): TopicManifest[] { + return this.facade.filteredTopics().filter(t => t.category === categoryKey); + } + + getCategoryIcon(key: string): string { + const map: Record = { + '01-foundations': '🧱', + '02-core-angular': '⚙️', + '03-intermediate': '🔀', + '04-reactivity': '⚡', + '05-advanced': '🚀', + '06-senior-architecture': '🛡️', + '07-interview-preparation': '🎯', + '08-cheat-sheets': '📑' + }; + return map[key] || '📁'; + } +} diff --git a/apps/portal/src/app/core/models/content.model.ts b/apps/portal/src/app/core/models/content.model.ts new file mode 100644 index 0000000..dab2f02 --- /dev/null +++ b/apps/portal/src/app/core/models/content.model.ts @@ -0,0 +1,67 @@ +export type ViewMode = 'deep-dive' | 'recap' | 'flashcards'; + +export type AppTheme = 'dark' | 'light'; + +export interface HeadingItem { + level: number; + text: string; +} + +export interface Flashcard { + question: string; + answer: string; + source: string; +} + +export interface TopicManifest { + id: string; + slug: string; + title: string; + summary: string; + category: string; + categoryTitle: string; + categoryBadge: string; + categoryIcon: string; + categoryOrder: number; + order: string; + fileName: string; + contentUrl: string; + wordCount: number; + readingTime: number; + headings: HeadingItem[]; + flashcards: Flashcard[]; + recapNotes: string[]; + searchTokens: string[]; +} + +export interface CategorySummaryTopic { + id: string; + title: string; + readingTime: number; + flashcardCount: number; +} + +export interface CategoryGroup { + key: string; + title: string; + badge: string; + icon: string; + order: number; + topicCount: number; + topics: CategorySummaryTopic[]; +} + +export interface ContentManifest { + generatedAt: string; + totalTopics: number; + totalFlashcards: number; + categories: CategoryGroup[]; + topics: TopicManifest[]; +} + +export interface SearchResult { + topic: TopicManifest; + matchedOn: 'title' | 'summary' | 'keyword' | 'flashcard'; + matchedSnippet: string; + score: number; +} diff --git a/apps/portal/src/app/core/services/content.service.ts b/apps/portal/src/app/core/services/content.service.ts new file mode 100644 index 0000000..d953638 --- /dev/null +++ b/apps/portal/src/app/core/services/content.service.ts @@ -0,0 +1,47 @@ +import { Injectable, inject } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Observable, shareReplay, map, catchError, of } from 'rxjs'; +import { ContentManifest, TopicManifest } from '../models/content.model'; + +@Injectable({ + providedIn: 'root' +}) +export class ContentService { + private readonly http = inject(HttpClient); + private manifestCache$?: Observable; + + getManifest(): Observable { + if (!this.manifestCache$) { + this.manifestCache$ = this.http.get('assets/content-manifest.json').pipe( + catchError(err => { + console.warn('Failed to load content manifest from relative assets/, falling back to /assets/', err); + return this.http.get('/assets/content-manifest.json'); + }), + shareReplay(1) + ); + } + return this.manifestCache$; + } + + getTopic(id: string): Observable { + return this.getManifest().pipe( + map(manifest => manifest.topics.find(t => t.id === id || t.slug === id)) + ); + } + + getTopicMarkdown(contentUrl: string): Observable { + // Ensure relative path starts clean + const cleanUrl = contentUrl.startsWith('/') ? contentUrl.slice(1) : contentUrl; + return this.http.get(cleanUrl, { responseType: 'text' }).pipe( + catchError(err => { + console.warn(`Could not load markdown from ${cleanUrl}, attempting with leading slash`, err); + return this.http.get(`/${cleanUrl}`, { responseType: 'text' }).pipe( + catchError(innerErr => { + console.error(`Failed to fetch markdown content from ${cleanUrl}:`, innerErr); + return of(`# Content Not Found\n\nUnable to stream markdown for: \`${cleanUrl}\`. Please verify asset routing.`); + }) + ); + }) + ); + } +} diff --git a/apps/portal/src/app/core/services/markdown-sanitizer.service.spec.ts b/apps/portal/src/app/core/services/markdown-sanitizer.service.spec.ts new file mode 100644 index 0000000..dafb726 --- /dev/null +++ b/apps/portal/src/app/core/services/markdown-sanitizer.service.spec.ts @@ -0,0 +1,44 @@ +import { TestBed } from '@angular/core/testing'; +import { MarkdownSanitizerService } from './markdown-sanitizer.service'; + +describe('MarkdownSanitizerService', () => { + let service: MarkdownSanitizerService; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [MarkdownSanitizerService] + }); + service = TestBed.inject(MarkdownSanitizerService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('should rewrite README.md links to /topic/01-why-angular', () => { + const markdown = 'Check the [Table of Contents](../README.md) here.'; + const result = service.renderMarkdown(markdown) as any; + const htmlString = result.changingThisBreaksApplicationSecurity || ''; + expect(htmlString).toContain('href="/topic/01-why-angular"'); + expect(htmlString).toContain('data-topic-id="01-why-angular"'); + }); + + it('should rewrite relative topic .md links to SPA routes', () => { + const markdown = 'Go to [SPA Concepts](02-spa-concepts.md).'; + const result = service.renderMarkdown(markdown) as any; + const htmlString = result.changingThisBreaksApplicationSecurity || ''; + expect(htmlString).toContain('href="/topic/02-spa-concepts"'); + expect(htmlString).toContain('data-topic-id="02-spa-concepts"'); + }); + + it('should format code blocks with IDE window header, language tag, and copy button', () => { + const markdown = '```typescript\nconst x = 10;\n```'; + const result = service.renderMarkdown(markdown) as any; + const htmlString = result.changingThisBreaksApplicationSecurity || ''; + expect(htmlString).toContain('code-block-wrapper'); + expect(htmlString).toContain('code-block-header'); + expect(htmlString).toContain('code-window-dots'); + expect(htmlString).toContain('code-copy-btn'); + expect(htmlString).toContain('TYPESCRIPT'); + }); +}); diff --git a/apps/portal/src/app/core/services/markdown-sanitizer.service.ts b/apps/portal/src/app/core/services/markdown-sanitizer.service.ts new file mode 100644 index 0000000..5c0f9ed --- /dev/null +++ b/apps/portal/src/app/core/services/markdown-sanitizer.service.ts @@ -0,0 +1,164 @@ +import { Injectable, inject, SecurityContext } from '@angular/core'; +import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; +import { marked, Renderer } from 'marked'; +import DOMPurify from 'dompurify'; +import Prism from 'prismjs'; + +// Load Prism grammar components if available +// @ts-ignore +import 'prismjs/components/prism-typescript'; +// @ts-ignore +import 'prismjs/components/prism-javascript'; +// @ts-ignore +import 'prismjs/components/prism-json'; +// @ts-ignore +import 'prismjs/components/prism-bash'; +// @ts-ignore +import 'prismjs/components/prism-css'; +// @ts-ignore +import 'prismjs/components/prism-markup'; + +@Injectable({ + providedIn: 'root' +}) +export class MarkdownSanitizerService { + private readonly sanitizer = inject(DomSanitizer); + + constructor() { + this.configureMarked(); + } + + private configureMarked(): void { + const renderer = new Renderer(); + + // Link re-writer to handle relative markdown links + const originalLink = renderer.link.bind(renderer); + renderer.link = (token: any) => { + let finalHref = token.href; + const text = token.tokens ? token.tokens.map((t: any) => ('text' in t ? t.text : '')).join('') : (token.text || ''); + + // Check if this is a Table of Contents link pointing to README.md + if (token.href && /readme\.md/i.test(token.href)) { + finalHref = `/topic/01-why-angular`; + return `${text}`; + } + + // Check if this is an internal link to another markdown file (e.g. `02-spa-concepts.md` or `../02-core-angular/06-xxx.md`) + if (token.href && !token.href.startsWith('http://') && !token.href.startsWith('https://') && !token.href.startsWith('#')) { + const match = token.href.match(/([0-9]{2}[a-z]?-[a-zA-Z0-9_-]+)\.md/); + if (match) { + finalHref = `/topic/${match[1]}`; + return `${text}`; + } + } + + const linkHtml = (originalLink as any)({ ...token, href: finalHref }); + // External links open in new tab + if (finalHref.startsWith('http://') || finalHref.startsWith('https://')) { + return linkHtml.replace(' { + const language = lang && Prism.languages[lang] ? lang : 'typescript'; + const grammar = Prism.languages[language] || Prism.languages['typescript'] || Prism.languages['javascript']; + let highlighted = text; + try { + if (grammar) { + highlighted = Prism.highlight(text, grammar, language); + } + } catch (err) { + console.warn('Prism highlight failed for lang:', lang, err); + } + + const displayLang = (lang || 'code').toUpperCase(); + return `
${displayLang}
${highlighted}
`; + }; + + marked.setOptions({ + renderer, + gfm: true, + breaks: false + }); + } + + private preprocessMarkdown(markdown: string): string { + if (!markdown) return ''; + + // 1. Strip raw navigation blocks: ... + let clean = markdown.replace(/[\s\S]*?/gi, ''); + + // Strip standalone navigation lines at bottom if any + clean = clean.replace(/\n\s*⬅️[\s\S]*?➡️[^\n]*/gi, ''); + clean = clean.replace(/\n\s*⬅️[\s\S]*?🏠[^\n]*/gi, ''); + + // 2. Transform the initial Interview Priority block into an Interview Intelligence Card + const metaRegex = /##\s*Interview Priority\s+([^\r\n#]+)\s+##\s*Interview Frequency\s+([^\r\n#]+)\s+##\s*Recommended Depth\s+([^\r\n#]+)\s+##\s*Relevant For\s+([\s\S]*?)(?=\r?\n\s*---\s*|\r?\n\s*#+|$)/i; + + clean = clean.replace(metaRegex, (_match, priorityRaw, freqRaw, depthRaw, rolesRaw) => { + const cleanVal = (s: string) => s.replace(/^\*+|\*+$/g, '').trim(); + const priority = cleanVal(priorityRaw); + const frequency = cleanVal(freqRaw); + const depth = cleanVal(depthRaw); + + const roles = rolesRaw + .split(/\r?\n/) + .map((r: string) => r.replace(/^[-*]\s+/, '').trim()) + .filter((r: string) => r.length > 0 && !r.startsWith('#') && !r.startsWith('-')); + + const roleBadges = roles + .map((r: string) => `${r}`) + .join(''); + + return ` +
+
+ ⚡ Interview Intelligence Matrix + Readiness Target +
+
+
+ Interview Priority + ${priority} +
+
+ Interview Frequency + ${frequency} +
+
+ Recommended Depth + ${depth} +
+
+ Relevant For +
${roleBadges}
+
+
+
+`; + }); + + // Strip immediate trailing separator if left by metadata replacement + clean = clean.replace(/(<\/div>\s*)\n\s*---\s*\n/g, '$1\n\n'); + + return clean; + } + + renderMarkdown(markdown: string): SafeHtml { + if (!markdown) return ''; + try { + const processed = this.preprocessMarkdown(markdown); + const rawHtml = marked.parse(processed) as string; + const cleanHtml = DOMPurify.sanitize(rawHtml, { + ADD_ATTR: ['target', 'rel', 'data-topic-id', 'class'], + ADD_TAGS: ['button', 'div', 'span'] + }); + return this.sanitizer.bypassSecurityTrustHtml(cleanHtml); + } catch (err) { + console.error('Error rendering markdown:', err); + return `

Failed to parse markdown content.

`; + } + } +} diff --git a/apps/portal/src/app/core/services/search.service.ts b/apps/portal/src/app/core/services/search.service.ts new file mode 100644 index 0000000..f98ded1 --- /dev/null +++ b/apps/portal/src/app/core/services/search.service.ts @@ -0,0 +1,96 @@ +import { Injectable, inject } from '@angular/core'; +import { ContentService } from './content.service'; +import { TopicManifest, SearchResult } from '../models/content.model'; +import { Observable, map } from 'rxjs'; + +@Injectable({ + providedIn: 'root' +}) +export class SearchService { + private readonly contentService = inject(ContentService); + + search(query: string): Observable { + const q = query.trim().toLowerCase(); + if (!q) { + return this.contentService.getManifest().pipe( + map(manifest => + manifest.topics.slice(0, 8).map(topic => ({ + topic, + matchedOn: 'title', + matchedSnippet: topic.summary, + score: 1 + })) + ) + ); + } + + const queryTerms = q.split(/\s+/).filter(Boolean); + + return this.contentService.getManifest().pipe( + map(manifest => { + const results: SearchResult[] = []; + + for (const topic of manifest.topics) { + let score = 0; + let matchedOn: SearchResult['matchedOn'] = 'keyword'; + let matchedSnippet = topic.summary; + + const titleLower = topic.title.toLowerCase(); + const summaryLower = topic.summary.toLowerCase(); + const catLower = topic.categoryTitle.toLowerCase(); + + // Title exact match bonus + if (titleLower.includes(q)) { + score += 100; + matchedOn = 'title'; + matchedSnippet = topic.title; + } + + // Category match + if (catLower.includes(q)) { + score += 40; + } + + // Term matching in title & summary + for (const term of queryTerms) { + if (titleLower.includes(term)) score += 30; + if (summaryLower.includes(term)) { + score += 15; + if (matchedOn !== 'title') { + matchedOn = 'summary'; + matchedSnippet = topic.summary; + } + } + } + + // Search tokens matching + for (const token of topic.searchTokens) { + const tokenLower = token.toLowerCase(); + for (const term of queryTerms) { + if (tokenLower === term) score += 20; + else if (tokenLower.includes(term)) score += 10; + } + } + + // Flashcard question matches + for (const card of topic.flashcards) { + const cardQLower = card.question.toLowerCase(); + if (cardQLower.includes(q)) { + score += 35; + matchedOn = 'flashcard'; + matchedSnippet = `Q: ${card.question}`; + break; + } + } + + if (score > 0) { + results.push({ topic, matchedOn, matchedSnippet, score }); + } + } + + // Sort descending by relevance score + return results.sort((a, b) => b.score - a.score).slice(0, 15); + }) + ); + } +} diff --git a/apps/portal/src/app/core/services/theme.service.ts b/apps/portal/src/app/core/services/theme.service.ts new file mode 100644 index 0000000..f48552a --- /dev/null +++ b/apps/portal/src/app/core/services/theme.service.ts @@ -0,0 +1,46 @@ +import { Injectable, signal, effect, inject, PLATFORM_ID } from '@angular/core'; +import { isPlatformBrowser } from '@angular/common'; +import { AppTheme } from '../models/content.model'; + +@Injectable({ + providedIn: 'root' +}) +export class ThemeService { + private readonly platformId = inject(PLATFORM_ID); + private readonly STORAGE_KEY = 'aip_portal_theme'; + + readonly theme = signal(this.getInitialTheme()); + + constructor() { + effect(() => { + const currentTheme = this.theme(); + if (isPlatformBrowser(this.platformId)) { + document.documentElement.setAttribute('data-theme', currentTheme); + localStorage.setItem(this.STORAGE_KEY, currentTheme); + } + }); + } + + toggleTheme(): void { + this.theme.update(t => (t === 'dark' ? 'light' : 'dark')); + } + + setTheme(newTheme: AppTheme): void { + this.theme.set(newTheme); + } + + private getInitialTheme(): AppTheme { + if (!isPlatformBrowser(this.platformId)) { + return 'dark'; + } + const saved = localStorage.getItem(this.STORAGE_KEY) as AppTheme | null; + if (saved === 'light' || saved === 'dark') { + return saved; + } + // Check system preference + if (window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches) { + return 'light'; + } + return 'dark'; + } +} diff --git a/apps/portal/src/app/core/state/reader.facade.ts b/apps/portal/src/app/core/state/reader.facade.ts new file mode 100644 index 0000000..9112cb8 --- /dev/null +++ b/apps/portal/src/app/core/state/reader.facade.ts @@ -0,0 +1,141 @@ +import { Injectable, inject, signal, computed, PLATFORM_ID } from '@angular/core'; +import { isPlatformBrowser } from '@angular/common'; +import { ContentService } from '../services/content.service'; +import { ContentManifest, TopicManifest, ViewMode } from '../models/content.model'; + +export type TrackFilter = 'all' | 'architect' | 'cram' | 'reactivity'; + +@Injectable({ + providedIn: 'root' +}) +export class ReaderFacade { + private readonly platformId = inject(PLATFORM_ID); + private readonly contentService = inject(ContentService); + private readonly COMPLETED_STORAGE_KEY = 'aip_portal_completed_topics'; + + // Signals state + readonly manifest = signal(null); + readonly activeTopic = signal(null); + readonly viewMode = signal('deep-dive'); + readonly isDrawerOpen = signal(false); + readonly isSearchOpen = signal(false); + readonly activeTrack = signal('all'); + readonly completedTopicIds = signal>(this.loadCompletedTopics()); + + // Computed signals + readonly totalTopicsCount = computed(() => this.manifest()?.totalTopics ?? 0); + readonly completedCount = computed(() => this.completedTopicIds().size); + readonly progressPercent = computed(() => { + const total = this.totalTopicsCount(); + if (total === 0) return 0; + return Math.min(100, Math.round((this.completedCount() / total) * 100)); + }); + + readonly filteredTopics = computed(() => { + const m = this.manifest(); + if (!m) return []; + const track = this.activeTrack(); + if (track === 'all') return m.topics; + if (track === 'architect') { + return m.topics.filter(t => t.category === '06-senior-architecture' || t.categoryBadge === 'Architect'); + } + if (track === 'cram') { + return m.topics.filter(t => t.category === '08-cheat-sheets' || t.category === '07-interview-preparation'); + } + if (track === 'reactivity') { + return m.topics.filter(t => t.category === '04-reactivity'); + } + return m.topics; + }); + + constructor() { + this.loadManifest(); + } + + loadManifest(): void { + this.contentService.getManifest().subscribe({ + next: data => { + this.manifest.set(data); + // If no active topic, default to the first one + if (!this.activeTopic() && data.topics.length > 0) { + this.activeTopic.set(data.topics[0]); + } + }, + error: err => console.error('ReaderFacade failed to load manifest:', err) + }); + } + + setActiveTopicById(id: string): void { + const m = this.manifest(); + if (!m) return; + const found = m.topics.find(t => t.id === id || t.slug === id); + if (found) { + this.activeTopic.set(found); + } + } + + setViewMode(mode: ViewMode): void { + this.viewMode.set(mode); + } + + toggleDrawer(): void { + this.isDrawerOpen.update(v => !v); + } + + setDrawerOpen(isOpen: boolean): void { + this.isDrawerOpen.set(isOpen); + } + + toggleSearch(): void { + this.isSearchOpen.update(v => !v); + } + + setSearchOpen(isOpen: boolean): void { + this.isSearchOpen.set(isOpen); + } + + setActiveTrack(track: TrackFilter): void { + this.activeTrack.set(track); + } + + toggleTopicCompleted(topicId: string): void { + this.completedTopicIds.update(prev => { + const next = new Set(prev); + if (next.has(topicId)) { + next.delete(topicId); + } else { + next.add(topicId); + } + this.saveCompletedTopics(next); + return next; + }); + } + + isTopicCompleted(topicId: string): boolean { + return this.completedTopicIds().has(topicId); + } + + private loadCompletedTopics(): Set { + if (!isPlatformBrowser(this.platformId)) { + return new Set(); + } + try { + const raw = localStorage.getItem(this.COMPLETED_STORAGE_KEY); + if (raw) { + return new Set(JSON.parse(raw)); + } + } catch { + // ignore + } + return new Set(); + } + + private saveCompletedTopics(set: Set): void { + if (!isPlatformBrowser(this.platformId)) return; + try { + localStorage.setItem(this.COMPLETED_STORAGE_KEY, JSON.stringify(Array.from(set))); + } catch { + // ignore + } + } +} diff --git a/apps/portal/src/app/features/topic-reader/components/flashcard-deck.component.ts b/apps/portal/src/app/features/topic-reader/components/flashcard-deck.component.ts new file mode 100644 index 0000000..87bf663 --- /dev/null +++ b/apps/portal/src/app/features/topic-reader/components/flashcard-deck.component.ts @@ -0,0 +1,336 @@ +import { Component, input, signal, computed, effect } from '@angular/core'; +import { Flashcard } from '../../../core/models/content.model'; + +@Component({ + selector: 'app-flashcard-deck', + standalone: true, + template: ` +
+
+
+ Card {{ currentDisplayIndex() }} of {{ cards().length }} +
+
+
+
+ +
+ + @if (currentCard(); as card) { +
+
+
+ {{ card.source }} + {{ isRevealed() ? 'Tap to hide answer' : 'Tap to reveal answer' }} +
+ +
+ Q +

{{ card.question }}

+
+ + @if (isRevealed()) { +
+
+
+ A +

{{ card.answer }}

+
+
+ } @else { +
+ Click anywhere on the card to reveal the answer +
+ } +
+
+ +
+ + + + + +
+ } @else { +
+

No flashcards available for this topic yet.

+
+ } +
+ `, + styles: [` + .deck-container { + display: flex; + flex-direction: column; + gap: 1.5rem; + max-width: 760px; + margin: 1rem auto; + } + + .deck-header { + display: flex; + align-items: center; + justify-content: space-between; + } + + .deck-counter { + display: flex; + flex-direction: column; + gap: 0.35rem; + } + + .card-num { + font-size: 0.82rem; + font-weight: 700; + color: var(--text-secondary); + font-family: var(--font-mono); + } + + .counter-bar { + width: 180px; + height: 4px; + background: rgba(255, 255, 255, 0.1); + border-radius: 9999px; + overflow: hidden; + } + + .counter-fill { + height: 100%; + background: linear-gradient(90deg, #00f3ff, #818cf8); + transition: width var(--transition-smooth); + } + + .shuffle-btn { + background: transparent; + border: none; + color: var(--accent-cyan); + font-size: 0.8rem; + font-weight: 600; + cursor: pointer; + } + + .card-stage { + cursor: pointer; + user-select: none; + perspective: 1000px; + } + + .flashcard { + min-height: 280px; + padding: 2rem; + display: flex; + flex-direction: column; + justify-content: space-between; + border-radius: 1.25rem; + transition: all var(--transition-smooth); + } + + .flashcard:hover { + transform: translateY(-2px); + box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5), 0 0 20px var(--accent-cyan-glow); + } + + .flashcard.revealed { + border-color: rgba(0, 243, 255, 0.4); + background: var(--glass-bg-accent); + } + + .card-badge { + display: flex; + align-items: center; + justify-content: space-between; + font-size: 0.72rem; + font-weight: 700; + color: var(--accent-cyan); + text-transform: uppercase; + letter-spacing: 0.05em; + } + + .click-hint { + color: var(--text-muted); + font-size: 0.7rem; + text-transform: none; + font-weight: 500; + } + + .card-question { + display: flex; + align-items: flex-start; + gap: 1rem; + margin: 1.5rem 0; + } + + .q-icon { + width: 32px; + height: 32px; + border-radius: 0.5rem; + background: rgba(0, 243, 255, 0.15); + border: 1px solid rgba(0, 243, 255, 0.35); + color: var(--accent-cyan); + font-weight: 800; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + } + + .q-text { + font-size: 1.35rem; + font-weight: 700; + color: var(--text-primary); + line-height: 1.4; + } + + .divider { + height: 1px; + background: rgba(255, 255, 255, 0.1); + margin: 1.25rem 0; + } + + .card-answer-wrap { + animation: fadeIn 200ms ease-in-out; + } + + @keyframes fadeIn { + from { opacity: 0; transform: translateY(6px); } + to { opacity: 1; transform: translateY(0); } + } + + .card-answer { + display: flex; + align-items: flex-start; + gap: 1rem; + } + + .a-icon { + width: 32px; + height: 32px; + border-radius: 0.5rem; + background: rgba(52, 211, 153, 0.15); + border: 1px solid rgba(52, 211, 153, 0.35); + color: var(--accent-emerald); + font-weight: 800; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + } + + .a-text { + font-size: 1.05rem; + color: var(--text-primary); + line-height: 1.6; + } + + .reveal-placeholder { + padding: 1.5rem 0; + text-align: center; + color: var(--text-muted); + font-size: 0.85rem; + font-style: italic; + } + + .deck-controls { + display: flex; + align-items: center; + justify-content: space-between; + gap: 1rem; + } + + .nav-btn { + flex: 1; + } + + .nav-btn:disabled { + opacity: 0.4; + cursor: not-allowed; + } + + .reveal-toggle-btn { + flex: 1.5; + color: var(--accent-cyan); + border-color: rgba(0, 243, 255, 0.3); + } + + .empty-deck { + padding: 3rem; + text-align: center; + color: var(--text-muted); + } + `] +}) +export class FlashcardDeckComponent { + cards = input.required(); + + readonly currentIndex = signal(0); + readonly isRevealed = signal(false); + + constructor() { + effect(() => { + // Whenever cards input changes, reset state to card 1 with answer hidden + this.cards(); + this.currentIndex.set(0); + this.isRevealed.set(false); + }); + } + + readonly currentDisplayIndex = computed(() => { + const total = this.cards().length; + if (total === 0) return 0; + return Math.min(this.currentIndex() + 1, total); + }); + + readonly currentCard = computed(() => { + const list = this.cards(); + if (!list || list.length === 0) return null; + const idx = Math.min(Math.max(0, this.currentIndex()), list.length - 1); + return list[idx] || null; + }); + + readonly progressPercent = computed(() => { + const total = this.cards().length; + if (total === 0) return 0; + const idx = Math.min(Math.max(0, this.currentIndex()), total - 1); + return Math.round(((idx + 1) / total) * 100); + }); + + toggleReveal(): void { + this.isRevealed.update(r => !r); + } + + nextCard(): void { + if (this.currentIndex() < this.cards().length - 1) { + this.currentIndex.update(i => i + 1); + this.isRevealed.set(false); + } + } + + prevCard(): void { + if (this.currentIndex() > 0) { + this.currentIndex.update(i => i - 1); + this.isRevealed.set(false); + } + } + + resetDeck(): void { + this.currentIndex.set(0); + this.isRevealed.set(false); + } +} diff --git a/apps/portal/src/app/features/topic-reader/components/recap-summary.component.ts b/apps/portal/src/app/features/topic-reader/components/recap-summary.component.ts new file mode 100644 index 0000000..984486b --- /dev/null +++ b/apps/portal/src/app/features/topic-reader/components/recap-summary.component.ts @@ -0,0 +1,201 @@ +import { Component, input } from '@angular/core'; +import { TopicManifest } from '../../../core/models/content.model'; + +@Component({ + selector: 'app-recap-summary', + standalone: true, + template: ` +
+
+ + + +
+ + +
+

+ 💎 + Core Principles & Interview Answers +

+
+ @for (note of topic().recapNotes; track $index) { +
+
+ Rule {{ $index + 1 }} +
+

{{ note }}

+
+ } @empty { +

Detailed notes available in the Deep Dive tab.

+ } +
+
+ + +
+

+ ⚠️ + Enterprise Traps & Probing Questions +

+
+
+ What interviewers listen for: + Clear distinction between synchronous UI reactivity (Signals) and asynchronous streaming pipelines (RxJS). Avoid dogmatic statements like "RxJS is obsolete". +
+
+ System trade-off: + Evaluating zone.js overhead vs zoneless change detection scheduling; OnPush immutability requirements; memory leak prevention. +
+
+ Enterprise architectural standard: + Strict separation between Presentational (Dumb) components and Facade/Store Smart containers. +
+
+
+
+ `, + styles: [` + .recap-container { + display: flex; + flex-direction: column; + gap: 2rem; + max-width: 900px; + margin: 1rem auto; + } + + .recap-banner { + padding: 2rem; + border-radius: 1.25rem; + } + + .banner-top { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 0.75rem; + } + + .banner-badge { + font-size: 0.75rem; + font-weight: 700; + color: var(--accent-cyan); + text-transform: uppercase; + letter-spacing: 0.05em; + } + + .read-speed { + font-size: 0.75rem; + color: var(--accent-amber); + font-weight: 600; + } + + .banner-title { + font-size: 1.85rem; + font-weight: 800; + color: var(--text-primary); + margin-bottom: 0.5rem; + } + + .banner-summary { + font-size: 1rem; + color: var(--text-secondary); + line-height: 1.6; + } + + .recap-section { + display: flex; + flex-direction: column; + gap: 1rem; + } + + .section-heading { + font-size: 1.15rem; + font-weight: 700; + color: var(--text-primary); + display: flex; + align-items: center; + gap: 0.6rem; + } + + .heading-icon { + font-size: 1.25rem; + } + + .rules-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); + gap: 1rem; + } + + .rule-card { + padding: 1.25rem; + display: flex; + flex-direction: column; + gap: 0.5rem; + } + + .rule-header { + display: flex; + align-items: center; + justify-content: space-between; + } + + .rule-tag { + font-size: 0.7rem; + font-weight: 800; + text-transform: uppercase; + color: var(--accent-cyan); + letter-spacing: 0.05em; + } + + .rule-body { + font-size: 0.92rem; + color: var(--text-secondary); + line-height: 1.5; + } + + .pitfalls-list { + padding: 1.25rem 1.5rem; + display: flex; + flex-direction: column; + gap: 1rem; + } + + .pitfall-item { + display: flex; + flex-direction: column; + gap: 0.25rem; + padding-bottom: 0.75rem; + border-bottom: 1px solid rgba(255, 255, 255, 0.06); + } + + .pitfall-item:last-child { + border-bottom: none; + padding-bottom: 0; + } + + .pitfall-q { + font-size: 0.85rem; + font-weight: 700; + color: var(--accent-cyan); + } + + .pitfall-a { + font-size: 0.92rem; + color: var(--text-secondary); + line-height: 1.5; + } + + .empty-note { + color: var(--text-muted); + font-size: 0.9rem; + } + `] +}) +export class RecapSummaryComponent { + topic = input.required(); +} diff --git a/apps/portal/src/app/features/topic-reader/topic-reader.component.ts b/apps/portal/src/app/features/topic-reader/topic-reader.component.ts new file mode 100644 index 0000000..172c652 --- /dev/null +++ b/apps/portal/src/app/features/topic-reader/topic-reader.component.ts @@ -0,0 +1,477 @@ +import { Component, inject, signal, effect, input, HostListener } from '@angular/core'; +import { ActivatedRoute, Router, RouterLink } from '@angular/router'; +import { SafeHtml } from '@angular/platform-browser'; +import { ContentService } from '../../core/services/content.service'; +import { MarkdownSanitizerService } from '../../core/services/markdown-sanitizer.service'; +import { ReaderFacade } from '../../core/state/reader.facade'; +import { TopicManifest, ViewMode } from '../../core/models/content.model'; +import { FlashcardDeckComponent } from './components/flashcard-deck.component'; +import { RecapSummaryComponent } from './components/recap-summary.component'; +import { SignalsGraphVisualizerComponent } from '../visualizers/signals-graph-visualizer.component'; +import { ChangeDetectionTreeVisualizerComponent } from '../visualizers/change-detection-tree-visualizer.component'; + +@Component({ + selector: 'app-topic-reader', + standalone: true, + imports: [ + RouterLink, + FlashcardDeckComponent, + RecapSummaryComponent, + SignalsGraphVisualizerComponent, + ChangeDetectionTreeVisualizerComponent + ], + template: ` + @if (topic(); as t) { +
+ +
+
+ {{ t.categoryTitle }} + / + {{ t.id }} +
+ +
+

{{ t.title }}

+
+ + +
+
+ +

{{ t.summary }}

+ + +
+ ⏱️ {{ t.readingTime }} Min Read + 🎴 {{ t.flashcards.length }} Flashcards + 🏷️ {{ t.categoryBadge }} + 📊 {{ t.wordCount }} Words +
+ + + +
+ + +
+ @if (isVisualizerActive()) { +
+ @if (t.id.includes('signal') || t.category === '04-reactivity') { + + } + +
+ } @else if (facade.viewMode() === 'recap') { + + } @else if (facade.viewMode() === 'flashcards') { + + } @else { + +
+ @if (isLoading()) { +
+
+

Streaming architectural guide...

+
+ } @else { +
+
+ } +
+ } +
+ + +
+
+ } @else { +
+
+

Loading curriculum index...

+
+ } + `, + styles: [` + .reader-container { + display: flex; + flex-direction: column; + gap: 1.5rem; + max-width: var(--max-content-width); + margin: 0 auto; + padding-bottom: 3rem; + } + + .reader-header { + padding: 1.75rem 2rem; + border-radius: 1.25rem; + display: flex; + flex-direction: column; + gap: 1rem; + } + + .header-breadcrumb { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.78rem; + } + + .cat-badge { + font-weight: 700; + color: var(--accent-cyan); + text-transform: uppercase; + letter-spacing: 0.05em; + } + + .sep { + color: var(--text-muted); + } + + .topic-slug { + font-family: var(--font-mono); + color: var(--text-muted); + } + + .header-main-row { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 1.5rem; + flex-wrap: wrap; + } + + .topic-title { + font-size: 2.25rem; + font-weight: 800; + letter-spacing: -0.03em; + color: var(--text-primary); + flex: 1; + min-width: 280px; + } + + .action-buttons { + display: flex; + gap: 0.75rem; + } + + .complete-btn.completed { + background: rgba(52, 211, 153, 0.15); + border-color: rgba(52, 211, 153, 0.4); + color: var(--accent-emerald); + } + + .topic-lead { + font-size: 1.05rem; + color: var(--text-secondary); + line-height: 1.6; + } + + .meta-pills { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + } + + .pill { + font-size: 0.75rem; + padding: 0.25rem 0.65rem; + border-radius: 9999px; + background: rgba(255, 255, 255, 0.05); + border: 1px solid rgba(255, 255, 255, 0.1); + color: var(--text-secondary); + font-weight: 600; + } + + .view-tabs { + display: flex; + gap: 0.5rem; + border-top: 1px solid rgba(255, 255, 255, 0.08); + padding-top: 1rem; + margin-top: 0.5rem; + overflow-x: auto; + } + + .tab-btn { + background: transparent; + border: 1px solid transparent; + padding: 0.6rem 1.15rem; + border-radius: 0.75rem; + font-family: inherit; + font-size: 0.88rem; + font-weight: 700; + color: var(--text-secondary); + cursor: pointer; + transition: all var(--transition-fast); + white-space: nowrap; + } + + .tab-btn:hover { + color: var(--text-primary); + background: rgba(255, 255, 255, 0.04); + } + + .tab-btn.active { + background: rgba(0, 243, 255, 0.12); + border-color: rgba(0, 243, 255, 0.35); + color: var(--accent-cyan); + box-shadow: 0 0 16px rgba(0, 243, 255, 0.15); + } + + .highlight-tab { + color: var(--accent-cyan); + border-color: rgba(0, 243, 255, 0.2); + } + + .reader-body { + min-height: 400px; + } + + .markdown-body { + padding: 2.5rem; + border-radius: 1.25rem; + background: var(--glass-bg); + } + + .loading-state { + padding: 4rem 2rem; + text-align: center; + display: flex; + flex-direction: column; + align-items: center; + gap: 1rem; + border-radius: 1.25rem; + color: var(--text-muted); + } + + .spinner { + width: 36px; + height: 36px; + border: 3px solid rgba(0, 243, 255, 0.2); + border-top-color: var(--accent-cyan); + border-radius: 50%; + animation: spin 800ms linear infinite; + } + + @keyframes spin { + to { transform: rotate(360deg); } + } + + .reader-footer { + padding: 1.5rem; + border-radius: 1.25rem; + display: flex; + align-items: center; + justify-content: space-between; + gap: 1rem; + flex-wrap: wrap; + } + + .nav-link-btn { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 0.2rem; + padding: 0.75rem 1.25rem; + min-width: 220px; + } + + .next-btn { + align-items: flex-end; + margin-left: auto; + } + + .nav-dir { + font-size: 0.7rem; + text-transform: uppercase; + letter-spacing: 0.05em; + opacity: 0.8; + } + + .nav-title { + font-size: 0.92rem; + font-weight: 700; + } + + @media (max-width: 768px) { + .markdown-body { + padding: 1.5rem; + } + .reader-header { + padding: 1.25rem; + } + .topic-title { + font-size: 1.75rem; + } + .nav-link-btn { + width: 100%; + } + } + `] +}) +export class TopicReaderComponent { + private readonly route = inject(ActivatedRoute); + private readonly router = inject(Router); + private readonly contentService = inject(ContentService); + private readonly markdownSanitizer = inject(MarkdownSanitizerService); + readonly facade = inject(ReaderFacade); + + // Inputs & Signals + id = input(); + readonly topic = signal(null); + readonly renderedHtml = signal(''); + readonly isLoading = signal(false); + readonly isVisualizerActive = signal(false); + + // Prev / Next Topics + readonly prevTopic = signal(null); + readonly nextTopic = signal(null); + + constructor() { + this.route.paramMap.subscribe(params => { + const topicId = params.get('id') || '01-why-angular'; + this.loadTopic(topicId); + }); + + effect(() => { + // Whenever view mode tab changes (deep-dive, cram, flashcards), reset scroll to top + this.facade.viewMode(); + this.resetScrollToTop(); + }); + } + + private resetScrollToTop(): void { + window.scrollTo({ top: 0, left: 0, behavior: 'instant' }); + if (document.documentElement) document.documentElement.scrollTop = 0; + if (document.body) document.body.scrollTop = 0; + const main = document.querySelector('.main-viewport'); + if (main) main.scrollTop = 0; + } + + private loadTopic(topicId: string): void { + this.resetScrollToTop(); + this.isLoading.set(true); + this.isVisualizerActive.set(false); + + this.contentService.getTopic(topicId).subscribe(manifestTopic => { + if (manifestTopic) { + this.topic.set(manifestTopic); + this.facade.setActiveTopicById(manifestTopic.id); + this.computePrevNext(manifestTopic.id); + + // Fetch raw markdown on demand + this.contentService.getTopicMarkdown(manifestTopic.contentUrl).subscribe({ + next: md => { + const html = this.markdownSanitizer.renderMarkdown(md); + this.renderedHtml.set(html); + this.isLoading.set(false); + // Ensure scroll resets after DOM insertion and paint + requestAnimationFrame(() => { + this.resetScrollToTop(); + setTimeout(() => this.resetScrollToTop(), 25); + }); + }, + error: () => this.isLoading.set(false) + }); + } else { + // Topic not found in manifest + this.isLoading.set(false); + } + }); + } + + private computePrevNext(currentId: string): void { + const list = this.facade.filteredTopics(); + const idx = list.findIndex(t => t.id === currentId); + if (idx >= 0) { + this.prevTopic.set(idx > 0 ? list[idx - 1] : null); + this.nextTopic.set(idx < list.length - 1 ? list[idx + 1] : null); + } + } + + hasVisualizers(topicId: string): boolean { + return ( + topicId.includes('signal') || + topicId.includes('change-detection') || + topicId.includes('rxjs') || + topicId.includes('subject') + ); + } + + toggleVisualizer(): void { + this.isVisualizerActive.update(v => !v); + } + + onContentClick(event: MouseEvent): void { + const target = event.target as HTMLElement; + const link = target.closest('a'); + if (link) { + const topicId = link.getAttribute('data-topic-id'); + const href = link.getAttribute('href'); + if (topicId) { + event.preventDefault(); + this.router.navigate(['/topic', topicId]); + } else if (href && (/readme\.md/i.test(href) || href === '/')) { + event.preventDefault(); + this.router.navigate(['/topic', '01-why-angular']); + } + } + } +} diff --git a/apps/portal/src/app/features/visualizers/change-detection-tree-visualizer.component.ts b/apps/portal/src/app/features/visualizers/change-detection-tree-visualizer.component.ts new file mode 100644 index 0000000..c1b064d --- /dev/null +++ b/apps/portal/src/app/features/visualizers/change-detection-tree-visualizer.component.ts @@ -0,0 +1,315 @@ +import { Component, signal } from '@angular/core'; + +type CDStrategy = 'Default' | 'OnPush' | 'Zoneless'; + +@Component({ + selector: 'app-cd-tree-visualizer', + standalone: true, + template: ` +
+
+
+ Performance Architecture +

Angular Change Detection Engine Visualizer

+
+
+ + + +
+
+ +
+ + {{ statusMessage() }} +
+ + +
+ +
+ APP ROOT + AppComponent + {{ strategy() }} +
+ +
+
+
+
+ + +
+
+ PARENT 1 + HeaderComponent + {{ strategy() === 'Default' ? 'Default' : 'OnPush' }} +
+ +
+ PARENT 2 + DashboardComponent + {{ strategy() === 'Default' ? 'Default' : 'OnPush' }} +
+
+ + +
+
+
+
+ +
+
+ LEAF + UserBadge +
+
+ LEAF + MetricCard +
+
+ TARGET + Node C2 (Clicked) +
+
+
+ + +
+
+ {{ nodesCheckedCount() }} / 6 + Components Dirty Checked +
+
+ {{ strategy() === 'Default' ? '60-120ms' : strategy() === 'OnPush' ? '12ms' : '< 1ms' }} + Typical Frame Latency +
+
+ {{ strategy() === 'Zoneless' ? 'No Zone.js' : 'Zone.js monkey-patches' }} + Runtime Mechanism +
+
+
+ `, + styles: [` + .visualizer-root { + padding: 1.75rem; + border-radius: 1.25rem; + margin: 1.5rem 0; + display: flex; + flex-direction: column; + gap: 1.5rem; + } + + .viz-header { + display: flex; + align-items: center; + justify-content: space-between; + flex-wrap: wrap; + gap: 1rem; + border-bottom: 1px solid rgba(255, 255, 255, 0.08); + padding-bottom: 1rem; + } + + .viz-badge { + font-size: 0.72rem; + font-weight: 800; + text-transform: uppercase; + color: var(--accent-cyan); + letter-spacing: 0.05em; + } + + .viz-title { + font-size: 1.25rem; + font-weight: 800; + color: var(--text-primary); + margin-top: 0.2rem; + } + + .strategy-picker { + display: flex; + background: rgba(0, 0, 0, 0.2); + padding: 0.25rem; + border-radius: 0.6rem; + gap: 0.25rem; + } + + .mode-btn { + background: transparent; + border: none; + padding: 0.4rem 0.75rem; + border-radius: 0.45rem; + font-size: 0.78rem; + font-weight: 600; + color: var(--text-secondary); + cursor: pointer; + font-family: inherit; + transition: all var(--transition-fast); + } + + .mode-btn:hover { + color: var(--text-primary); + } + + .mode-btn.active { + background: rgba(0, 243, 255, 0.15); + color: var(--accent-cyan); + border: 1px solid rgba(0, 243, 255, 0.3); + box-shadow: 0 0 10px rgba(0, 243, 255, 0.15); + } + + .action-bar { + display: flex; + align-items: center; + gap: 1rem; + flex-wrap: wrap; + } + + .action-status { + font-size: 0.85rem; + color: var(--accent-cyan); + font-weight: 600; + } + + .tree-stage { + display: flex; + flex-direction: column; + align-items: center; + gap: 0.5rem; + padding: 1rem 0; + } + + .tree-node { + padding: 0.75rem 1.25rem; + border-radius: 0.75rem; + background: rgba(15, 23, 42, 0.7); + border: 1px solid rgba(255, 255, 255, 0.1); + display: flex; + flex-direction: column; + align-items: center; + min-width: 140px; + transition: all 300ms ease; + } + + .tree-node.checked { + border-color: #f43f5e; + background: rgba(244, 63, 94, 0.15); + box-shadow: 0 0 16px rgba(244, 63, 94, 0.4); + transform: scale(1.04); + } + + .target-node { + border-color: var(--accent-cyan); + } + + .node-tag { + font-size: 0.65rem; + font-weight: 800; + color: var(--text-muted); + letter-spacing: 0.05em; + } + + .target-tag { + color: var(--accent-cyan); + } + + .node-label { + font-size: 0.85rem; + font-weight: 700; + color: var(--text-primary); + margin: 0.15rem 0; + } + + .node-cd { + font-size: 0.68rem; + color: var(--accent-cyan); + font-family: var(--font-mono); + } + + .tree-tier { + display: flex; + gap: 2rem; + justify-content: center; + } + + .tree-tier.leaves { + gap: 1rem; + } + + .comparison-summary { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1rem; + padding: 1.25rem; + text-align: center; + } + + .stat-val { + display: block; + font-size: 1.25rem; + font-weight: 800; + color: var(--accent-cyan); + font-family: var(--font-mono); + } + + .stat-lbl { + font-size: 0.75rem; + color: var(--text-muted); + font-weight: 600; + } + `] +}) +export class ChangeDetectionTreeVisualizerComponent { + strategy = signal('Default'); + checkedNodes = signal>(new Set()); + statusMessage = signal('Select a strategy and click the leaf button to simulate change detection traversal.'); + + setStrategy(s: CDStrategy): void { + this.strategy.set(s); + this.checkedNodes.set(new Set()); + this.statusMessage.set(`Switched to ${s} mode. Ready to simulate.`); + } + + isNodeChecked(nodeId: string): boolean { + return this.checkedNodes().has(nodeId); + } + + nodesCheckedCount(): number { + return this.checkedNodes().size; + } + + triggerEventOnLeaf(): void { + const s = this.strategy(); + if (s === 'Default') { + // Default: dirty-checks EVERY component in the entire tree + this.checkedNodes.set(new Set(['AppRoot', 'Header', 'Dashboard', 'Profile', 'MetricCard', 'WidgetC2'])); + this.statusMessage.set('Default: Zone.js intercepted event and re-checked all 6 components down the tree!'); + } else if (s === 'OnPush') { + // OnPush: Skips unchanged subtrees (Header, Profile, MetricCard are skipped!) + this.checkedNodes.set(new Set(['AppRoot', 'Dashboard', 'WidgetC2'])); + this.statusMessage.set('OnPush: Skipped HeaderComponent and untouched leaves! Only 3 nodes checked.'); + } else { + // Zoneless: Direct pinpoint notification to the targeted node without top-down tree walk! + this.checkedNodes.set(new Set(['WidgetC2'])); + this.statusMessage.set('Zoneless: Direct signal notification! Only 1 node updated. Zero top-down tree check.'); + } + } +} diff --git a/apps/portal/src/app/features/visualizers/signals-graph-visualizer.component.ts b/apps/portal/src/app/features/visualizers/signals-graph-visualizer.component.ts new file mode 100644 index 0000000..8399002 --- /dev/null +++ b/apps/portal/src/app/features/visualizers/signals-graph-visualizer.component.ts @@ -0,0 +1,275 @@ +import { Component, signal, computed, effect } from '@angular/core'; + +@Component({ + selector: 'app-signals-graph-visualizer', + standalone: true, + template: ` +
+
+
+ Interactive Runtime Model +

Signals Reactive Graph & Glitch-Free Propagation

+
+
+ + + +
+
+ + +
+ +
+
+ ROOT SIGNAL +
count = signal({{ count() }})
+
Value: {{ count() }}
+
+
+ ↓ notifies +
+
+ + +
+
+
+ COMPUTED (DERIVED) +
double = computed(() => count() * 2)
+
Value: {{ double() }}
+
+
+ ↓ notifies +
+
+ +
+
+ COMPUTED (DERIVED) +
isEven = computed(() => count() % 2 === 0)
+
Value: {{ isEven() ? 'TRUE' : 'FALSE' }}
+
+
+ ↓ notifies +
+
+
+ + +
+
+
+ EFFECT (SIDE-EFFECT SINK) +
effect(() => syncDOM(double(), isEven()))
+
Last Synced Frame: double={{ double() }}, isEven={{ isEven() }} (Executions: {{ effectRuns() }})
+
+
+
+
+ + +
+
+ ⚡ Push-Pull Algorithm +

Dirty flag is pushed down the tree in O(1); derived value is pulled lazily only when read. Zero glitch / diamond problem.

+
+
+ 🛡️ Memory Safety +

Unlike RxJS BehaviorSubject, signals require no unsubscribe(), no takeUntilDestroyed(), and clean up automatically.

+
+
+
+ `, + styles: [` + .visualizer-root { + padding: 1.75rem; + border-radius: 1.25rem; + margin: 1.5rem 0; + display: flex; + flex-direction: column; + gap: 1.5rem; + } + + .viz-header { + display: flex; + align-items: center; + justify-content: space-between; + flex-wrap: wrap; + gap: 1rem; + border-bottom: 1px solid rgba(255, 255, 255, 0.08); + padding-bottom: 1rem; + } + + .viz-badge { + font-size: 0.72rem; + font-weight: 800; + text-transform: uppercase; + color: var(--accent-cyan); + letter-spacing: 0.05em; + } + + .viz-title { + font-size: 1.25rem; + font-weight: 800; + color: var(--text-primary); + margin-top: 0.2rem; + } + + .viz-controls { + display: flex; + gap: 0.5rem; + } + + .graph-stage { + display: flex; + flex-direction: column; + align-items: center; + gap: 1.25rem; + padding: 1rem 0; + } + + .node-wrapper { + display: flex; + flex-direction: column; + align-items: center; + } + + .node-wrapper.full-width { + width: 100%; + max-width: 600px; + } + + .graph-node { + padding: 1rem 1.5rem; + border-radius: 0.85rem; + border: 1px solid rgba(255, 255, 255, 0.1); + background: rgba(15, 23, 42, 0.6); + backdrop-filter: blur(12px); + text-align: center; + min-width: 250px; + transition: all var(--transition-smooth); + } + + .signal-node { + border-color: rgba(0, 243, 255, 0.5); + box-shadow: 0 0 20px rgba(0, 243, 255, 0.2); + } + + .computed-node { + border-color: rgba(129, 140, 248, 0.4); + box-shadow: 0 0 16px rgba(129, 140, 248, 0.15); + } + + .effect-node { + border-color: rgba(52, 211, 153, 0.4); + box-shadow: 0 0 16px rgba(52, 211, 153, 0.15); + width: 100%; + } + + .node-type { + font-size: 0.68rem; + font-weight: 800; + letter-spacing: 0.08em; + display: block; + margin-bottom: 0.25rem; + color: var(--text-muted); + } + + .signal-node .node-type { color: var(--accent-cyan); } + .computed-node .node-type { color: var(--accent-violet); } + .effect-node .node-type { color: var(--accent-emerald); } + + .node-code { + font-family: var(--font-mono); + font-size: 0.85rem; + color: var(--text-primary); + margin-bottom: 0.4rem; + } + + .node-val { + font-size: 0.85rem; + color: var(--text-secondary); + } + + .node-val strong { + color: var(--text-primary); + font-weight: 800; + font-family: var(--font-mono); + } + + .edge-down { + height: 24px; + display: flex; + align-items: center; + justify-content: center; + } + + .edge-arrow { + font-size: 0.72rem; + color: var(--accent-cyan); + font-family: var(--font-mono); + } + + .computed-layer { + display: flex; + gap: 1.5rem; + justify-content: center; + flex-wrap: wrap; + } + + .viz-notes { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); + gap: 1rem; + border-top: 1px solid rgba(255, 255, 255, 0.08); + padding-top: 1.25rem; + } + + .note-box { + padding: 1rem; + } + + .note-title { + font-size: 0.85rem; + font-weight: 700; + color: var(--text-primary); + display: block; + margin-bottom: 0.25rem; + } + + .note-desc { + font-size: 0.8rem; + color: var(--text-secondary); + line-height: 1.45; + } + `] +}) +export class SignalsGraphVisualizerComponent { + count = signal(2); + double = computed(() => this.count() * 2); + isEven = computed(() => this.count() % 2 === 0); + effectRuns = signal(1); + + constructor() { + effect(() => { + // track dependencies + this.double(); + this.isEven(); + this.effectRuns.update(r => r + 1); + }); + } + + increment(): void { + this.count.update(c => c + 1); + } + + decrement(): void { + this.count.update(c => Math.max(0, c - 1)); + } + + reset(): void { + this.count.set(1); + this.effectRuns.set(1); + } +} diff --git a/apps/portal/src/index.html b/apps/portal/src/index.html new file mode 100644 index 0000000..cf683ca --- /dev/null +++ b/apps/portal/src/index.html @@ -0,0 +1,18 @@ + + + + + Angular Architect & Interview Mastery Portal + + + + + + + + + + + + + diff --git a/apps/portal/src/main.ts b/apps/portal/src/main.ts new file mode 100644 index 0000000..75287dc --- /dev/null +++ b/apps/portal/src/main.ts @@ -0,0 +1,10 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { appConfig } from './app/app.config'; +import { App } from './app/app'; + +if (typeof window !== 'undefined' && 'scrollRestoration' in history) { + history.scrollRestoration = 'manual'; +} + +bootstrapApplication(App, appConfig) + .catch((err) => console.error(err)); diff --git a/apps/portal/src/styles.css b/apps/portal/src/styles.css new file mode 100644 index 0000000..de4d00a --- /dev/null +++ b/apps/portal/src/styles.css @@ -0,0 +1,596 @@ +/* ========================================================================== + Liquid Glass Design System — Core Tokens & Global Themes + ========================================================================== */ + +:root { + /* Dimensions & Layout */ + --header-height: 72px; + --sidebar-width: 310px; + --drawer-width: 420px; + --max-content-width: 1100px; + --sticky-offset: calc(var(--header-height) + 1rem); + + /* Typography */ + --font-sans: 'Plus Jakarta Sans', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + --font-mono: 'JetBrains Mono', 'Fira Code', ui-monospace, SFMono-Regular, monospace; + + /* Animation Timings */ + --transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1); + --transition-smooth: 250ms cubic-bezier(0.16, 1, 0.3, 1); + --transition-bounce: 350ms cubic-bezier(0.34, 1.56, 0.64, 1); + + /* Shared Accents */ + --accent-cyan: #00f3ff; + --accent-cyan-rgb: 0, 243, 255; + --accent-blue: #38bdf8; + --accent-violet: #818cf8; + --accent-emerald: #34d399; + --accent-amber: #fbbf24; + --accent-rose: #f43f5e; +} + +/* ========================================================================== + Dark Theme (Liquid Glass Default) + ========================================================================== */ +[data-theme='dark'] { + --bg-base: #070b14; + --bg-gradient: radial-gradient(circle at 10% 10%, rgba(14, 165, 233, 0.08) 0%, transparent 40%), + radial-gradient(circle at 90% 80%, rgba(129, 140, 248, 0.07) 0%, transparent 45%), + radial-gradient(circle at 50% 50%, #0b1120 0%, #030712 100%); + --bg-surface: rgba(15, 23, 42, 0.65); + --bg-surface-elevated: rgba(30, 41, 59, 0.7); + + --text-primary: #f8fafc; + --text-secondary: #94a3b8; + --text-muted: #64748b; + --text-inverse: #0f172a; + + /* Glassmorphism Specs */ + --glass-bg: linear-gradient(135deg, rgba(255, 255, 255, 0.07) 0%, rgba(255, 255, 255, 0.02) 100%), rgba(15, 23, 42, 0.6); + --glass-bg-accent: linear-gradient(135deg, rgba(0, 243, 255, 0.08) 0%, rgba(15, 23, 42, 0.7) 100%); + --glass-border: 1px solid rgba(255, 255, 255, 0.09); + --glass-border-hover: 1px solid rgba(0, 243, 255, 0.4); + --glass-border-cyan: 1px solid rgba(0, 243, 255, 0.35); + --glass-blur: blur(16px); + --glass-blur-heavy: blur(24px); + + --glass-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.5), inset 0 1px 1px 0 rgba(255, 255, 255, 0.1); + --glass-shadow-glow: 0 8px 32px 0 rgba(0, 0, 0, 0.5), 0 0 24px rgba(0, 243, 255, 0.2), inset 0 1px 1px 0 rgba(255, 255, 255, 0.2); + --accent-cyan-glow: rgba(0, 243, 255, 0.25); + --code-bg: rgba(3, 7, 18, 0.85); + --table-border: rgba(255, 255, 255, 0.08); + + /* Sidebar Tokens */ + --sidebar-item-hover-bg: rgba(255, 255, 255, 0.06); + --sidebar-item-hover-border: rgba(0, 243, 255, 0.25); + --sidebar-item-hover-text: #ffffff; +} + +/* ========================================================================== + Light Theme + ========================================================================== */ +[data-theme='light'] { + --bg-base: #f8fafc; + --bg-gradient: radial-gradient(circle at 10% 10%, rgba(14, 165, 233, 0.06) 0%, transparent 40%), + radial-gradient(circle at 90% 80%, rgba(99, 102, 241, 0.05) 0%, transparent 45%), + radial-gradient(circle at 50% 50%, #f1f5f9 0%, #f8fafc 100%); + --bg-surface: rgba(255, 255, 255, 0.82); + --bg-surface-elevated: #ffffff; + + --text-primary: #0f172a; + --text-secondary: #475569; + --text-muted: #94a3b8; + --text-inverse: #ffffff; + + /* Glassmorphism Specs */ + --glass-bg: linear-gradient(135deg, rgba(255, 255, 255, 0.9) 0%, rgba(255, 255, 255, 0.7) 100%); + --glass-bg-accent: linear-gradient(135deg, rgba(2, 132, 199, 0.08) 0%, rgba(255, 255, 255, 0.9) 100%); + --glass-border: 1px solid rgba(226, 232, 240, 0.9); + --glass-border-hover: 1px solid rgba(2, 132, 199, 0.45); + --glass-border-cyan: 1px solid rgba(2, 132, 199, 0.4); + --glass-blur: blur(16px); + --glass-blur-heavy: blur(24px); + + --glass-shadow: 0 8px 30px 0 rgba(15, 23, 42, 0.06), inset 0 1px 1px 0 rgba(255, 255, 255, 0.9); + --glass-shadow-glow: 0 8px 30px 0 rgba(15, 23, 42, 0.08), 0 0 20px rgba(2, 132, 199, 0.15), inset 0 1px 1px 0 rgba(255, 255, 255, 0.9); + --accent-cyan: #0284c7; + --accent-cyan-glow: rgba(2, 132, 199, 0.15); + --code-bg: #1e293b; + --table-border: rgba(226, 232, 240, 0.9); + + /* Sidebar Tokens */ + --sidebar-item-hover-bg: rgba(2, 132, 199, 0.12); + --sidebar-item-hover-border: rgba(2, 132, 199, 0.4); + --sidebar-item-hover-text: #0369a1; +} + +/* ========================================================================== + CSS Reset & Base Styles + ========================================================================== */ +*, *::before, *::after { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +html, body { + height: 100%; + font-family: var(--font-sans); + background-color: var(--bg-base); + background-image: var(--bg-gradient); + background-attachment: fixed; + color: var(--text-primary); + line-height: 1.6; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + overflow-x: hidden; +} + +/* Custom Smooth Scrollbar */ +::-webkit-scrollbar { + width: 7px; + height: 7px; +} +::-webkit-scrollbar-track { + background: transparent; +} +::-webkit-scrollbar-thumb { + background: rgba(148, 163, 184, 0.25); + border-radius: 9999px; +} +::-webkit-scrollbar-thumb:hover { + background: rgba(0, 243, 255, 0.45); +} + +/* ========================================================================== + Utility Classes & Components + ========================================================================== */ +.glass-panel { + background: var(--glass-bg); + backdrop-filter: var(--glass-blur); + -webkit-backdrop-filter: var(--glass-blur); + border: var(--glass-border); + box-shadow: var(--glass-shadow); + border-radius: 1rem; + transition: all var(--transition-smooth); +} + +.glass-panel:hover { + border: var(--glass-border-hover); +} + +.glass-panel-glow { + background: var(--glass-bg-accent); + backdrop-filter: var(--glass-blur); + -webkit-backdrop-filter: var(--glass-blur); + border: var(--glass-border-cyan); + box-shadow: var(--glass-shadow-glow); + border-radius: 1rem; +} + +.glass-pill { + display: inline-flex; + align-items: center; + gap: 0.4rem; + padding: 0.25rem 0.75rem; + font-size: 0.75rem; + font-weight: 600; + border-radius: 9999px; + background: rgba(0, 243, 255, 0.08); + border: 1px solid rgba(0, 243, 255, 0.25); + color: var(--accent-cyan); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.btn-glass { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + padding: 0.6rem 1.25rem; + border-radius: 0.75rem; + font-family: var(--font-sans); + font-weight: 600; + font-size: 0.875rem; + cursor: pointer; + background: var(--glass-bg); + border: var(--glass-border); + color: var(--text-primary); + backdrop-filter: var(--glass-blur); + transition: all var(--transition-fast); + text-decoration: none; +} + +.btn-glass:hover { + border-color: var(--accent-cyan); + color: var(--accent-cyan); + box-shadow: 0 0 16px var(--accent-cyan-glow); + transform: translateY(-1px); +} + +.btn-cyan { + background: linear-gradient(135deg, #00f3ff 0%, #0284c7 100%); + color: #030712; + border: none; + font-weight: 700; + box-shadow: 0 4px 16px rgba(0, 243, 255, 0.3); +} + +.btn-cyan:hover { + filter: brightness(1.1); + box-shadow: 0 6px 24px rgba(0, 243, 255, 0.5); + transform: translateY(-1px); + color: #030712; +} + +/* ========================================================================== + Rendered Markdown Prose Styling + ========================================================================== */ +.prose-content { + color: var(--text-primary); + font-size: 1.05rem; + line-height: 1.75; +} + +.prose-content h1 { + font-size: 2.25rem; + font-weight: 800; + letter-spacing: -0.025em; + margin-bottom: 1.5rem; + background: linear-gradient(135deg, var(--text-primary) 30%, var(--accent-cyan) 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} + +.prose-content h2 { + font-size: 1.5rem; + font-weight: 700; + margin-top: 2.5rem; + margin-bottom: 1rem; + padding-bottom: 0.5rem; + border-bottom: 1px solid rgba(148, 163, 184, 0.15); + color: var(--text-primary); + display: flex; + align-items: center; + gap: 0.5rem; +} + +.prose-content h3 { + font-size: 1.2rem; + font-weight: 600; + margin-top: 1.75rem; + margin-bottom: 0.75rem; + color: var(--text-primary); +} + +.prose-content p { + margin-bottom: 1.25rem; + color: var(--text-secondary); +} + +.prose-content ul, .prose-content ol { + margin-bottom: 1.5rem; + padding-left: 1.5rem; + color: var(--text-secondary); +} + +.prose-content li { + margin-bottom: 0.5rem; +} + +.prose-content strong { + color: var(--text-primary); + font-weight: 700; +} + +.prose-content code:not(pre code) { + font-family: var(--font-mono); + font-size: 0.88em; + padding: 0.2rem 0.45rem; + border-radius: 0.375rem; + background: rgba(0, 243, 255, 0.08); + border: 1px solid rgba(0, 243, 255, 0.2); + color: var(--accent-cyan); +} + +/* Integrated IDE Code Block & Header */ +.code-block-wrapper { + margin: 1.75rem 0; + border-radius: 0.85rem; + overflow: hidden; + border: 1px solid rgba(255, 255, 255, 0.12); + background: var(--code-bg); + box-shadow: 0 8px 30px rgba(0, 0, 0, 0.35); + transition: border-color var(--transition-fast); +} + +[data-theme='light'] .code-block-wrapper { + border-color: rgba(226, 232, 240, 0.9); + box-shadow: 0 8px 24px rgba(15, 23, 42, 0.08); +} + +.code-block-wrapper:hover { + border-color: rgba(0, 243, 255, 0.35); +} + +[data-theme='light'] .code-block-wrapper:hover { + border-color: rgba(2, 132, 199, 0.4); +} + +.code-block-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.55rem 1rem; + background: #0f172a; + border-bottom: 1px solid rgba(255, 255, 255, 0.08); +} + +.code-header-left { + display: flex; + align-items: center; + gap: 0.75rem; +} + +.code-window-dots { + display: inline-flex; + align-items: center; + gap: 0.35rem; +} + +.code-window-dots .dot { + width: 9px; + height: 9px; + border-radius: 50%; + display: inline-block; +} + +.dot-red { background: #ff5f56; } +.dot-yellow { background: #ffbd2e; } +.dot-green { background: #27c93f; } + +.code-lang-tag { + font-family: var(--font-mono); + font-size: 0.72rem; + font-weight: 700; + color: var(--accent-cyan); + letter-spacing: 0.06em; +} + +.code-copy-btn { + display: inline-flex; + align-items: center; + gap: 0.4rem; + padding: 0.25rem 0.65rem; + border-radius: 0.45rem; + background: rgba(255, 255, 255, 0.08); + border: 1px solid rgba(255, 255, 255, 0.15); + color: #94a3b8; + font-family: var(--font-sans); + font-size: 0.75rem; + font-weight: 600; + cursor: pointer; + transition: all var(--transition-fast); +} + +.code-copy-btn:hover { + background: rgba(0, 243, 255, 0.15); + border-color: rgba(0, 243, 255, 0.4); + color: #00f3ff; + box-shadow: 0 0 10px rgba(0, 243, 255, 0.2); +} + +.code-copy-btn.copied { + background: rgba(52, 211, 153, 0.2); + border-color: rgba(52, 211, 153, 0.5); + color: #34d399; +} + +.copy-svg { + flex-shrink: 0; +} + +.prose-content pre { + margin: 0; + padding: 1.25rem; + border-radius: 0; + background: var(--code-bg); + border: none; + overflow-x: auto; + font-family: var(--font-mono); + font-size: 0.9rem; + line-height: 1.6; + box-shadow: none; +} + +.prose-content pre code { + font-family: inherit; + color: #e2e8f0; +} + +.prose-content blockquote { + margin: 1.5rem 0; + padding: 1rem 1.25rem; + border-left: 4px solid var(--accent-cyan); + background: rgba(0, 243, 255, 0.05); + border-radius: 0 0.75rem 0.75rem 0; + color: var(--text-secondary); + font-style: normal; +} + +.prose-content table { + width: 100%; + border-collapse: separate; + border-spacing: 0; + margin: 1.5rem 0; + border-radius: 0.75rem; + overflow: hidden; + border: 1px solid var(--table-border); +} + +.prose-content th { + background: rgba(15, 23, 42, 0.4); + color: var(--text-primary); + font-weight: 700; + text-align: left; + padding: 0.875rem 1rem; + border-bottom: 1px solid var(--table-border); + font-size: 0.9rem; +} + +.prose-content td { + padding: 0.75rem 1rem; + border-bottom: 1px solid var(--table-border); + color: var(--text-secondary); + font-size: 0.9rem; +} + +.prose-content tr:last-child td { + border-bottom: none; +} + +.prose-content tr:hover td { + background: rgba(255, 255, 255, 0.02); +} + +.prose-content hr { + margin: 2.5rem 0; + border: none; + border-top: 1px solid rgba(148, 163, 184, 0.15); +} + +.prose-content a { + color: var(--accent-cyan); + text-decoration: none; + border-bottom: 1px dashed rgba(0, 243, 255, 0.5); + transition: border-color var(--transition-fast); +} + +.prose-content a:hover { + border-bottom-style: solid; +} + +/* ========================================================================== + Interview Intelligence Matrix Card + ========================================================================== */ +.interview-intel-card { + margin: 0.5rem 0 2rem 0; + padding: 1.25rem 1.5rem; + background: var(--glass-bg-accent); + border: var(--glass-border-cyan); + border-radius: 1rem; + backdrop-filter: var(--glass-blur); + -webkit-backdrop-filter: var(--glass-blur); + box-shadow: var(--glass-shadow); +} + +.intel-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 1rem; + padding-bottom: 0.75rem; + border-bottom: 1px solid rgba(255, 255, 255, 0.08); +} + +[data-theme='light'] .intel-header { + border-bottom-color: rgba(15, 23, 42, 0.08); +} + +.intel-title { + font-size: 0.82rem; + font-weight: 800; + text-transform: uppercase; + letter-spacing: 0.06em; + color: var(--accent-cyan); +} + +.intel-badge { + font-size: 0.7rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.05em; + padding: 0.2rem 0.6rem; + border-radius: 9999px; + background: rgba(var(--accent-cyan-rgb), 0.12); + color: var(--accent-cyan); + border: 1px solid rgba(var(--accent-cyan-rgb), 0.25); +} + +.intel-grid { + display: grid; + grid-template-columns: repeat(3, minmax(130px, 1fr)) 1.5fr; + gap: 1.25rem; + align-items: start; +} + +@media (max-width: 960px) { + .intel-grid { + grid-template-columns: 1fr 1fr; + gap: 1rem; + } +} + +@media (max-width: 600px) { + .intel-grid { + grid-template-columns: 1fr; + } +} + +.intel-metric { + display: flex; + flex-direction: column; + gap: 0.35rem; +} + +.metric-label { + font-size: 0.72rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.05em; + color: var(--text-muted); +} + +.metric-val { + font-size: 0.92rem; + font-weight: 700; + color: var(--text-primary); + line-height: 1.3; +} + +.priority-val { + color: var(--accent-emerald); +} + +.freq-val { + color: var(--accent-cyan); +} + +.depth-val { + color: var(--accent-violet); +} + +.roles-metric .roles-wrapper { + display: flex; + flex-wrap: wrap; + gap: 0.35rem; + margin-top: 0.15rem; +} + +.role-pill { + font-size: 0.72rem; + font-weight: 600; + padding: 0.2rem 0.55rem; + border-radius: 0.45rem; + background: rgba(255, 255, 255, 0.06); + border: 1px solid rgba(255, 255, 255, 0.1); + color: var(--text-secondary); + white-space: nowrap; +} + +[data-theme='light'] .role-pill { + background: rgba(15, 23, 42, 0.05); + border-color: rgba(15, 23, 42, 0.1); + color: var(--text-secondary); +} diff --git a/apps/portal/src/typings.d.ts b/apps/portal/src/typings.d.ts new file mode 100644 index 0000000..7b46cc4 --- /dev/null +++ b/apps/portal/src/typings.d.ts @@ -0,0 +1 @@ +declare module 'prismjs/components/*'; diff --git a/apps/portal/tsconfig.app.json b/apps/portal/tsconfig.app.json new file mode 100644 index 0000000..cb151e1 --- /dev/null +++ b/apps/portal/tsconfig.app.json @@ -0,0 +1,14 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "types": [] + }, + "include": [ + "src/**/*.ts" + ], + "exclude": [ + "src/**/*.spec.ts" + ] +} diff --git a/apps/portal/tsconfig.json b/apps/portal/tsconfig.json new file mode 100644 index 0000000..0e5102b --- /dev/null +++ b/apps/portal/tsconfig.json @@ -0,0 +1,41 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ +{ + "compileOnSave": false, + "compilerOptions": { + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "skipLibCheck": true, + "isolatedModules": true, + "experimentalDecorators": true, + "importHelpers": true, + "target": "ES2022", + "module": "preserve", + "baseUrl": ".", + "paths": { + "@core/*": ["src/app/core/*"], + "@shared/*": ["src/app/shared/*"], + "@features/*": ["src/app/features/*"], + "@env/*": ["src/environments/*"] + }, + "strict": true, + "ignoreDeprecations": "6.0" + }, + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + }, + "files": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/apps/portal/tsconfig.spec.json b/apps/portal/tsconfig.spec.json new file mode 100644 index 0000000..d383706 --- /dev/null +++ b/apps/portal/tsconfig.spec.json @@ -0,0 +1,15 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/spec", + "types": [ + "vitest/globals" + ] + }, + "include": [ + "src/**/*.d.ts", + "src/**/*.spec.ts" + ] +} diff --git a/docs/update-navigation.ps1 b/docs/update-navigation.ps1 new file mode 100644 index 0000000..3dac4a1 --- /dev/null +++ b/docs/update-navigation.ps1 @@ -0,0 +1,122 @@ +# update-navigation.ps1 - Generates clean Prev/TOC/Next footers for all topics with comfortable visual spacing + +$orderedFiles = @( + "01-foundations/01-why-angular.md", + "01-foundations/02-spa-concepts.md", + "01-foundations/03-component-based-architecture.md", + "01-foundations/04-angular-architecture-overview.md", + "01-foundations/05-typescript-essentials-for-angular.md", + "02-core-angular/06-angular-components.md", + "02-core-angular/07-templates-and-data-binding.md", + "02-core-angular/08-directives.md", + "02-core-angular/09-services.md", + "02-core-angular/10-dependency-injection.md", + "02-core-angular/11-http-client.md", + "03-intermediate/12-rxjs-and-observables-fundamentals.md", + "03-intermediate/13-component-communication.md", + "03-intermediate/14-lifecycle-hooks.md", + "03-intermediate/15-forms.md", + "04-reactivity/16-subjects-and-behaviorsubject.md", + "04-reactivity/17-rxjs-operators-deep-dive.md", + "04-reactivity/17a-common-enterprise-rxjs-scenarios.md", + "04-reactivity/17b-async-pipe-and-rxjs-template-patterns.md", + "04-reactivity/18-signals.md", + "04-reactivity/18a-signal-based-component-apis.md", + "05-advanced/19-ngmodules-fundamentals.md", + "05-advanced/20-standalone-components.md", + "05-advanced/21-routing-and-navigation.md", + "05-advanced/22-route-parameters-and-query-parameters-deep-dive.md", + "05-advanced/23-route-guards.md", + "05-advanced/24-nested-routes.md", + "05-advanced/25-route-resolvers.md", + "05-advanced/26-lazy-loading.md", + "05-advanced/27-http-interceptors.md", + "05-advanced/28-change-detection.md", + "05-advanced/29-performance-optimization.md", + "06-senior-architecture/30-state-management-fundamentals.md", + "06-senior-architecture/31-ngrx-fundamentals.md", + "06-senior-architecture/32-authentication-authorization.md", + "06-senior-architecture/33-error-handling-strategy.md", + "06-senior-architecture/34-enterprise-architecture.md", + "06-senior-architecture/35-micro-frontends.md", + "07-interview-preparation/36-mock-interview-questions.md", + "07-interview-preparation/37-frontend-system-design.md", + "07-interview-preparation/38-angular-testing-deep-dive.md", + "07-interview-preparation/39-behavioral-interview-preparation.md", + "07-interview-preparation/40-angular-system-design-case-studies.md", + "07-interview-preparation/41-quick-fire-qa.md", + "08-cheat-sheets/01-angular-cheat-sheet.md", + "08-cheat-sheets/02-rxjs-cheat-sheet.md", + "08-cheat-sheets/03-signals-cheat-sheet.md", + "08-cheat-sheets/04-routing-cheat-sheet.md", + "08-cheat-sheets/05-forms-cheat-sheet.md", + "08-cheat-sheets/06-ngrx-cheat-sheet.md", + "08-cheat-sheets/07-auth-cheat-sheet.md", + "08-cheat-sheets/08-architecture-cheat-sheet.md", + "08-cheat-sheets/09-interview-day-cheat-sheet.md" +) + +function Get-RelativePath($fromPath, $toPath) { + $fromDir = [System.IO.Path]::GetDirectoryName($fromPath).Replace("\", "/") + $toDir = [System.IO.Path]::GetDirectoryName($toPath).Replace("\", "/") + $toName = [System.IO.Path]::GetFileName($toPath) + if ($fromDir -eq $toDir) { + return $toName + } else { + return "../$toPath" + } +} + +function Get-TopicTitle($path) { + $lines = Get-Content $path -TotalCount 8 + foreach ($line in $lines) { + if ($line -match '^#\s+(.+)$') { + return $matches[1].Trim() + } + } + return [System.IO.Path]::GetFileNameWithoutExtension($path) +} + +$updatedCount = 0 + +for ($i = 0; $i -lt $orderedFiles.Count; $i++) { + $curr = $orderedFiles[$i] + if (-not (Test-Path $curr)) { + Write-Warning "File not found: $curr" + continue + } + + $prev = if ($i -gt 0) { $orderedFiles[$i - 1] } else { $null } + $next = if ($i -lt $orderedFiles.Count - 1) { $orderedFiles[$i + 1] } else { $null } + + $prevText = if ($prev) { "⬅️ **Previous:** [" + (Get-TopicTitle $prev) + "](" + (Get-RelativePath $curr $prev) + ")" } else { "⬅️ **Previous:** *None (First Topic)*" } + $nextText = if ($next) { "➡️ **Next:** [" + (Get-TopicTitle $next) + "](" + (Get-RelativePath $curr $next) + ")" } else { "➡️ **Next:** *None (End of Guide)*" } + + $navBlock = @" + + +
+
+ +--- + +
+ +$prevText  |  🏠 **[Table of Contents](../README.md)**  |  $nextText + +
+ +"@ + + $content = Get-Content $curr -Raw -Encoding utf8 + if ($content -match '(?s).*?') { + $newContent = [regex]::Replace($content, '(?s).*?', $navBlock.Trim()) + } else { + $newContent = $content.TrimEnd() + "`r`n`r`n" + $navBlock.Trim() + "`r`n" + } + + [System.IO.File]::WriteAllText((Resolve-Path $curr).Path, $newContent, (New-Object System.Text.UTF8Encoding($false))) + $updatedCount++ +} + +Write-Output "Successfully updated navigation spacing across $updatedCount files!" diff --git a/web_security_guide.md b/web_security_guide.md index a25aea8..28dcd8c 100644 --- a/web_security_guide.md +++ b/web_security_guide.md @@ -12,7 +12,7 @@ A web browser treats any script inside an HTML document as executable code runni ### 💥 Practical Scenarios & Code Examples #### Reflected XSS -* **Scenario:** A search page displays the user's query back to them: `"Results for: "`. +* **Scenario:** A search page displays the user's query back to them: `"Results for: ``"`. * **Exploit Vector:** An attacker sends a victim a link containing a script payload in the query parameter. * **Payload:** ```text